export interface CandidateAdoptionOwner { apiContext: string; storeContext: string; signedIn: string; actedAs: string; organizationId: string; } export interface CandidateAdoptionIntent { intentId: string; payloadDigest: string; } export type CandidateAdoptionHold = { version: 1; owner: CandidateAdoptionOwner; intentId: string; revision: number; } & ( | { state: 'reserved' | 'sent'; payloadDigest: string; patientId?: never } | { state: 'linked'; payloadDigest: string; patientId: string } | { state: 'opened'; payloadDigest?: never; patientId?: never } ); export interface CandidateAdoptionLease { onSend(): void; knownLinked(patientId: string): void; opened(): void; releaseUnsent(): void; } interface AdoptionLocks { request(name: string, callback: () => Promise): Promise; } interface AdoptionStorage { getItem(key: string): string | null; setItem(key: string, value: string): void; removeItem(key: string): void; } interface HoldDependencies { storage?: AdoptionStorage; locks?: AdoptionLocks; } const OWNER_FIELDS = ['apiContext', 'storeContext', 'signedIn', 'actedAs', 'organizationId'] as const; const text = (value: unknown): value is string => typeof value === 'string' && value.length > 0; const object = (value: unknown): value is Record => typeof value === 'object' && value !== null && !Array.isArray(value); const keysAre = (value: Record, allowed: readonly string[]): boolean => Object.keys(value).every((key) => allowed.includes(key)); const ownerFacts = (owner: CandidateAdoptionOwner): CandidateAdoptionOwner => { if (!object(owner) || !OWNER_FIELDS.every((field) => text(owner[field]))) throw new Error('Adoption ownership is unavailable.'); return { apiContext: owner.apiContext, storeContext: owner.storeContext, signedIn: owner.signedIn, actedAs: owner.actedAs, organizationId: owner.organizationId, }; }; const laneKey = (owner: CandidateAdoptionOwner): string => `akashic:patient-adoption:v1:${encodeURIComponent(JSON.stringify(OWNER_FIELDS.map((field) => owner[field])))}`; const parseHold = (raw: string, owner: CandidateAdoptionOwner): CandidateAdoptionHold => { const value: unknown = JSON.parse(raw); if (!object(value) || !keysAre(value, ['version', 'owner', 'intentId', 'revision', 'payloadDigest', 'state', 'patientId']) || value.version !== 1 || !text(value.intentId) || !Number.isSafeInteger(value.revision) || (value.revision as number) < 1 || (value.state === 'opened' ? 'payloadDigest' in value : !text(value.payloadDigest)) || !object(value.owner) || !keysAre(value.owner, OWNER_FIELDS) || !OWNER_FIELDS.every((field) => value.owner && (value.owner as Record)[field] === owner[field]) || !['reserved', 'sent', 'linked', 'opened'].includes(value.state as string) || (value.state === 'linked' ? !text(value.patientId) : 'patientId' in value)) { throw new Error('The previous adoption could not be read safely.'); } return value as unknown as CandidateAdoptionHold; }; const browserDependencies = (): HoldDependencies => { let storage: Storage | undefined; try { storage = globalThis.localStorage; } catch { /* Storage access can be refused before any operation starts. */ } return { storage, locks: typeof navigator === 'undefined' ? undefined : navigator.locks }; }; /** Only an owned successful handoff makes a settled slot replaceable. */ export const createCandidateAdoptionHoldStore = (dependencies: HoldDependencies = browserDependencies()) => { const storage = (): AdoptionStorage => { if (!dependencies.storage) throw new Error('Adoption storage is unavailable.'); return dependencies.storage; }; const read = (input: CandidateAdoptionOwner): CandidateAdoptionHold | undefined => { const owner = ownerFacts(input); const raw = storage().getItem(laneKey(owner)); return raw === null ? undefined : parseHold(raw, owner); }; const terminal = (held: CandidateAdoptionHold): CandidateAdoptionHold => ({ version: 1, owner: held.owner, intentId: held.intentId, revision: held.revision, state: 'opened', }); const withReservation = async (input: CandidateAdoptionOwner, intent: CandidateAdoptionIntent, execute: (lease: CandidateAdoptionLease) => Promise): Promise => { const owner = ownerFacts(input); if (!text(intent.intentId) || !text(intent.payloadDigest)) throw new Error('Adoption intent is unavailable.'); const submitted = { intentId: intent.intentId, payloadDigest: intent.payloadDigest }; const key = laneKey(owner); const backend = storage(); if (!dependencies.locks) throw new Error('Adoption serialization is unavailable.'); return dependencies.locks.request(key, async () => { const previousRaw = backend.getItem(key); const previous = previousRaw === null ? undefined : parseHold(previousRaw, owner); if (previous && (previous.state !== 'opened' || previous.intentId === intent.intentId)) { throw new Error('An earlier adoption is still held for this practice.'); } const revision = previous ? previous.revision + 1 : 1; if (!Number.isSafeInteger(revision)) throw new Error('Adoption revision is unavailable.'); let held: CandidateAdoptionHold = { version: 1, owner, ...submitted, revision, state: 'reserved' }; let expected = JSON.stringify(held); if (backend.getItem(key) !== previousRaw) throw new Error('The adoption lane changed before reservation.'); backend.setItem(key, expected); if (backend.getItem(key) !== expected) throw new Error('Adoption reservation could not be confirmed.'); let active = true; let released = false; const own = () => { if (!active || released || backend.getItem(key) !== expected) throw new Error('This adoption executor no longer owns its reservation.'); }; const persist = (next: CandidateAdoptionHold) => { own(); const serialized = JSON.stringify(next); backend.setItem(key, serialized); if (backend.getItem(key) !== serialized) throw new Error('Adoption state could not be confirmed.'); held = next; expected = serialized; }; const lease: CandidateAdoptionLease = { onSend: () => { own(); if (held.state !== 'reserved') throw new Error('This adoption was already dispatched.'); // HttpService calls this synchronously immediately before fetch. persist({ ...held, state: 'sent' }); }, knownLinked: (patientId) => { own(); if (held.state !== 'sent' || !text(patientId)) throw new Error('The linked adoption answer is not valid for this dispatch.'); persist({ ...held, state: 'linked', patientId }); }, opened: () => { own(); if (held.state !== 'linked') throw new Error('Only a known linked adoption can complete its handoff.'); persist(terminal(held)); }, releaseUnsent: () => { own(); if (held.state !== 'reserved') throw new Error('A dispatched adoption cannot be released as unsent.'); backend.removeItem(key); if (backend.getItem(key) !== null) throw new Error('The unsent adoption reservation could not be released.'); released = true; }, }; try { return await execute(lease); } finally { active = false; } }); }; const withLinkedHandoff = async ( input: CandidateAdoptionOwner, snapshot: CandidateAdoptionHold, execute: () => Promise, ): Promise => { const owner = ownerFacts(input); const backend = storage(); const key = laneKey(owner); if (!dependencies.locks) throw new Error('Adoption serialization is unavailable.'); await dependencies.locks.request(key, async () => { const expected = backend.getItem(key); if (expected === null) throw new Error('The linked adoption receipt is unavailable.'); const held = parseHold(expected, owner); if (held.state !== 'linked' || JSON.stringify(held) !== JSON.stringify(snapshot)) { throw new Error('This linked adoption is no longer current.'); } if (!await execute()) return; if (backend.getItem(key) !== expected) throw new Error('The linked adoption changed before handoff settlement.'); const opened = JSON.stringify(terminal(held)); backend.setItem(key, opened); if (backend.getItem(key) !== opened) throw new Error('The completed handoff could not be confirmed.'); }); }; const dispatchCreate = async ( input: CandidateAdoptionOwner, prepare: () => Promise<() => Promise>, ): Promise => { const owner = ownerFacts(input); if (!dependencies.locks) throw new Error('Adoption serialization is unavailable.'); const started = await dependencies.locks.request(laneKey(owner), async () => { const send = await prepare(); let held: CandidateAdoptionHold | undefined; try { held = read(owner); } catch { throw new Error('The earlier add-to-practice result could not be read safely. Registration is held in this practice.'); } if (held && held.state !== 'opened') { throw new Error('An earlier add-to-practice result is still held. Check that original result before starting another registration.'); } // Release admission after synchronous dispatch, rather than holding it through an unknown HTTP answer. return { answer: send() }; }); return started.answer; }; return { read, withReservation, withLinkedHandoff, dispatchCreate }; };