Files
HealthLog/src/lib/query-keys/sharing.ts
T
Marc-André Bombeck 3fd6bd6081 test(claim): drive the single-sign-on claim against a mock identity provider
Check the link before the code is redeemed and refuse a live session first,
so a refused claim spends nothing at the provider.
2026-10-15 23:18:24 +02:00

105 lines
4.7 KiB
TypeScript

/**
* Query keys — account sharing (v1.36.0).
* Part of the centralized factory; aggregated in `./index.ts`.
*
* Note what these keys deliberately do NOT carry: the acting account. That
* dimension lives on the QueryClient's `queryKeyHashFn`
* (`./record-scope.ts`), which folds it into every entry in the app at once
* — including these. Adding it here as well would partition the same cache
* twice under two rules, which is the two-deciders shape in miniature.
*
* The reads below are all non-delegable by construction: `/api/account/grants`
* and the record-activity feed both resolve through a bare `requireAuth()` and
* refuse while a switch is on. They are still record-scoped like everything
* else, so a session that switches out and back never reads a grant list
* cached under the wrong scope.
*/
export const sharingKeys = {
/**
* Grants in both directions (`GET /api/account/grants`). One key for one
* endpoint: the route answers "who can see my record, and whose can I see"
* in a single payload precisely so no client renders half of it.
*/
accountGrants: () => ["account", "grants"] as const,
/**
* The owner's record-activity feed (`GET /api/account/activity`) — who
* opened their record, and when.
*/
accountActivity: () => ["account", "activity"] as const,
/**
* v1.37.0 — the managed-profile family, as one prefix.
*
* Present so a grant transition can invalidate the whole family at once:
* accepting a Guardian invitation is an ordinary `POST
* /api/account/grants/{id}/accept`, and it moves a row on a roster that
* endpoint knows nothing about. Listing this prefix in `grantDependentKeys`
* is what keeps the two in step without the accept path having to know a
* managed profile exists.
*/
managedProfiles: () => ["managed-profiles"] as const,
/**
* Who looks after one managed profile (`GET
* /api/managed-profiles/{id}/guardians`). Keyed by profile because a
* Guardian may look after more than one, and a single key for all of them
* would be the same-key-different-shape collision the factory exists to
* prevent.
*/
managedProfileGuardians: (profileId: string) =>
["managed-profiles", profileId, "guardians"] as const,
/**
* One managed record's identity (`GET /api/managed-profiles/{id}`). Keyed by
* profile for the same reason the roster is, and under the same prefix so a
* grant transition invalidates it with the rest of the family.
*/
managedProfile: (profileId: string) =>
["managed-profiles", profileId, "profile"] as const,
/**
* v1.42 (#959) — whether a managed profile has an open handover link (`GET
* /api/managed-profiles/{id}/handover`). Under the family prefix so a grant
* transition refreshes it with the rest.
*/
managedProfileHandover: (profileId: string) =>
["managed-profiles", profileId, "handover"] as const,
/**
* v1.42 (#959) — the decision a new owner owes about their former
* Guardians (`GET /api/account/handover-decision`). Under `account` like
* the grant list it changes.
*/
handoverDecision: () => ["account", "handover-decision"] as const,
/**
* v1.42 (#959) — the anonymous preview of a handover link. The token is
* deliberately NOT part of the key: a cache key is a place a credential
* should never be written to, and one claim page reads one link.
*/
profileClaimPreview: () => ["profile-claim", "preview"] as const,
/** Mutation keys — kept in the factory so no bare array reaches a call site. */
accountGrantInvite: () => ["account", "grants", "invite"] as const,
accountGrantAccept: () => ["account", "grants", "accept"] as const,
accountGrantRevoke: () => ["account", "grants", "revoke"] as const,
accountGrantRenounce: () => ["account", "grants", "renounce"] as const,
accountSwitchMutation: () => ["account", "switch"] as const,
managedProfileCreate: () => ["managed-profiles", "create"] as const,
managedProfileUpdate: () => ["managed-profiles", "update"] as const,
managedProfileDelete: () => ["managed-profiles", "delete"] as const,
managedProfileGuardianInvite: () =>
["managed-profiles", "guardian", "invite"] as const,
managedProfileGuardianRemove: () =>
["managed-profiles", "guardian", "remove"] as const,
managedProfileHandoverCreate: () =>
["managed-profiles", "handover", "create"] as const,
managedProfileHandoverRevoke: () =>
["managed-profiles", "handover", "revoke"] as const,
handoverDecide: () => ["account", "handover-decision", "decide"] as const,
profileClaim: () => ["profile-claim", "submit"] as const,
/** v1.43 (#959) — starting the claim through single sign-on. */
profileClaimSso: () => ["profile-claim", "sso"] as const,
};