kaafil-react-uikit 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/dist/admin/index.cjs +565 -387
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.d.cts +2 -2
- package/dist/admin/index.d.ts +2 -2
- package/dist/admin/index.js +231 -53
- package/dist/admin/index.js.map +1 -1
- package/dist/{chunk-YTSYRLGS.js → chunk-2LMFSKZU.js} +56 -17
- package/dist/chunk-2LMFSKZU.js.map +1 -0
- package/dist/{chunk-BBC5EX5O.cjs → chunk-2XW5EIMS.cjs} +17 -17
- package/dist/{chunk-BBC5EX5O.cjs.map → chunk-2XW5EIMS.cjs.map} +1 -1
- package/dist/{chunk-YU2242AR.js → chunk-3BEQYIFU.js} +5 -5
- package/dist/{chunk-YU2242AR.js.map → chunk-3BEQYIFU.js.map} +1 -1
- package/dist/{chunk-UALMCHNW.cjs → chunk-3N2EQ2KC.cjs} +84 -36
- package/dist/chunk-3N2EQ2KC.cjs.map +1 -0
- package/dist/{chunk-57SUK3GD.cjs → chunk-4QYI7TOF.cjs} +7 -7
- package/dist/{chunk-57SUK3GD.cjs.map → chunk-4QYI7TOF.cjs.map} +1 -1
- package/dist/{chunk-ECCQW5LN.cjs → chunk-6BY2NPBZ.cjs} +4 -4
- package/dist/{chunk-ECCQW5LN.cjs.map → chunk-6BY2NPBZ.cjs.map} +1 -1
- package/dist/{chunk-YNHOPYBK.cjs → chunk-7356B6YS.cjs} +12 -12
- package/dist/{chunk-YNHOPYBK.cjs.map → chunk-7356B6YS.cjs.map} +1 -1
- package/dist/{chunk-A4Z2YBEF.js → chunk-7G6IENKN.js} +84 -36
- package/dist/chunk-7G6IENKN.js.map +1 -0
- package/dist/{chunk-MNUCVVIE.cjs → chunk-A7C74SJT.cjs} +302 -148
- package/dist/chunk-A7C74SJT.cjs.map +1 -0
- package/dist/{chunk-KW5LH6KV.js → chunk-CLHAJBJS.js} +3 -3
- package/dist/{chunk-KW5LH6KV.js.map → chunk-CLHAJBJS.js.map} +1 -1
- package/dist/{chunk-GJ2J3PYN.js → chunk-E6ER25BG.js} +3 -3
- package/dist/{chunk-GJ2J3PYN.js.map → chunk-E6ER25BG.js.map} +1 -1
- package/dist/{chunk-HACH2NUM.cjs → chunk-KETEED2T.cjs} +9 -9
- package/dist/{chunk-HACH2NUM.cjs.map → chunk-KETEED2T.cjs.map} +1 -1
- package/dist/{chunk-RWLO6IFX.js → chunk-KTEXDGAA.js} +3 -3
- package/dist/{chunk-RWLO6IFX.js.map → chunk-KTEXDGAA.js.map} +1 -1
- package/dist/{chunk-VLP323AD.js → chunk-RBIEMGHT.js} +3 -3
- package/dist/{chunk-VLP323AD.js.map → chunk-RBIEMGHT.js.map} +1 -1
- package/dist/{chunk-WTTGPWMR.js → chunk-WPQB7JWS.js} +227 -73
- package/dist/chunk-WPQB7JWS.js.map +1 -0
- package/dist/{chunk-FVK7HMQ5.cjs → chunk-XBLAVNP3.cjs} +88 -48
- package/dist/chunk-XBLAVNP3.cjs.map +1 -0
- package/dist/core/index.cjs +124 -121
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +10 -1
- package/dist/core/index.d.ts +10 -1
- package/dist/core/index.js +20 -19
- package/dist/core/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/manager/index.cjs +239 -239
- package/dist/manager/index.js +6 -6
- package/dist/tokens.json +5013 -0
- package/dist/traveller/index.cjs +46 -40
- package/dist/traveller/index.cjs.map +1 -1
- package/dist/traveller/index.js +16 -10
- package/dist/traveller/index.js.map +1 -1
- package/dist/{useAgencyTrip-BUYSIaC2.d.cts → useAgencyTrip-C1YmfuQq.d.cts} +35 -1
- package/dist/{useAgencyTrip-BzyJB1D9.d.ts → useAgencyTrip-gZB0NILH.d.ts} +35 -1
- package/package.json +4 -3
- package/dist/chunk-A4Z2YBEF.js.map +0 -1
- package/dist/chunk-FVK7HMQ5.cjs.map +0 -1
- package/dist/chunk-MNUCVVIE.cjs.map +0 -1
- package/dist/chunk-UALMCHNW.cjs.map +0 -1
- package/dist/chunk-WTTGPWMR.js.map +0 -1
- package/dist/chunk-YTSYRLGS.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/hooks/useShareForms.ts","../src/core/hooks/useShareSnapshot.ts"],"names":["client","useKaafilClient","usePersona","useOfflineEngine","useServerTime","useState","useCallback","useEffect","isDevelopment","useOfflineEvent","useOfflineMutation","SyncOpMethod","useContext","OfflineEngineContext"],"mappings":"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","file":"chunk-BBC5EX5O.cjs","sourcesContent":["'use client';\n\n// `08-core-hooks.md §3` (new row — `ShareFormsSection`'s `Backed by`,\n// `ui-kit/families/traveller/02-forms.md`, cited): the write-back half of\n// the traveller share surface. `forms` is the ONLY write-back section on\n// the whole share surface; every other section is read-only through\n// `useShareSnapshot()` alone. This hook wraps the four dedicated\n// `shareAuth`-only routes `share.forms.{list,render,save,submit}`\n// (`kaafil-js/src/resources/share.ts`, verified this session).\n//\n// ── WHY THIS HOOK DOES NOT ALSO CALL `useShareSnapshot()` ──────────────────\n//\n// `02-forms.md`'s `Backed by` field says the section's own `/core` hook\n// \"composes both\" — the snapshot (for `forms` section presence) and the\n// dedicated routes (for the actual list/render/save/submit). This file\n// deliberately does NOT reproduce the snapshot fetch: `ShareFormsSection`\n// (future work, not this file) is only ever mounted once its own call to\n// `useShareSnapshot(token)` has already confirmed `sections.has('forms')`\n// (`01-share-sections.md`'s registry-driven mount rule) — duplicating that\n// fetch here would be a second, redundant read of the same snapshot for no\n// new information, and this hook has nothing to add to a presence check it\n// does not own. What this file DOES independently reproduce is\n// `useShareSnapshot()`'s own persona gate and its `share.expired`/\n// `share.revoked` subscriptions (`08-core-hooks.md §0`: \"every domain hook\n// is capability-gated internally\" — the share-surface's equivalent gate is\n// the persona check, applied the same way every other hook in this catalog\n// applies its own).\n//\n// ── SPEC TENSION FLAGGED: \"cache-first\" vs. \"the dedicated routes\" ─────────\n//\n// `02-forms.md`'s `Offline` field says the list \"rides the scoped share\n// snapshot cache-first, same as every other section\", but its own `Backed\n// by` field says in the same breath that \"the actual form list/render/\n// save/submit calls ride the dedicated forms-share routes\" — and\n// `ShareSnapshotResponse`'s per-section shape (verified,\n// `useShareSnapshot.ts`) carries no registered `forms` array with\n// `fillState`/`opensAt`/`closesAt` fields; only `ShareFormsListResponse`\n// (`GET /api/v1/share/{token}/forms`) does. This file follows the more\n// specific, SDK-verified statement — a LIVE `share.forms.list()` call,\n// refreshable on demand — the same \"own live-fetch sub-state, not a\n// `snapshot.updated` subscription\" pattern `useRooming.ts`'s\n// `unassignedPool` already uses for an analogous non-delta read. Flagged\n// for the doc owner to resolve; not silently picked one way with no trace.\n//\n// ── `clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY ──────────────────────\n//\n// `ShareSaveFormRequest`/`ShareSubmitFormRequest` (verified,\n// `kaafil-js/openapi/openapi.json`) require a body-level `clientToken`\n// (16-255 chars) — the SDK's own comment on `share.ts` calls it \"this\n// route's own de-dupe key, distinct from the automatic `Idempotency-Key`\n// header\", and `modules/forms/FRD.md §4.1/§8` calls it \"a fixed\n// `clientToken` generated once at enqueue and reused on every retry\" —\n// i.e. exactly the write-once, replay-unchanged lifecycle hard rule #4\n// reserves for `OfflineEngine.enqueue()`'s own internal idempotency-key\n// minting. This hook does NOT invent a value for it (that would be exactly\n// the \"hook mints its own idempotency key\" anti-pattern hard rule #4\n// forbids, even though `clientToken` is a different field from the\n// transport `Idempotency-Key` header `enqueue()` mints separately and\n// automatically for every op, share writes included). Instead\n// `ShareFormWriteInput.clientToken` is a REQUIRED field the CALLER\n// supplies — a future `ShareFormsSection`/`ShareFormFillScreen` generates\n// one value per form-fill session (e.g. once, the first time a field is\n// touched) and passes the SAME value to every `save()`/`submit()` call for\n// that session, which is what makes retries of the SAME logical write\n// dedupe wire-side. This hook only ever forwards it into the body verbatim.\n//\n// ── REVOCATION DROPS QUEUED WRITES — A REAL SDK GAP, WORKED AROUND HERE ────\n//\n// `02-forms.md`'s `Offline` field: \"Revocation is different and has no\n// grace: a `REVOKED` token drops queued writes unsent and purges the local\n// copy immediately.\" Verified against `kaafil-js/src/offline/share.ts`\n// this session: `ShareGuard.invalidate()` treats `'expired'` and\n// `'revoked'` IDENTICALLY — it purges the read snapshot in both cases and\n// only PRESERVES (never drops) the outbox either way; nothing in\n// `kaafil-js` currently distinguishes \"grace-covered\" from \"unsent, drop\n// it\" at the outbox level. This is a genuine SDK/spec disagreement — see\n// this file's final report note. Rather than leave the spec's hard\n// requirement unmet, this hook enforces it locally on `share.revoked`,\n// riding the outbox's own existing `drop()` primitive (the identical call\n// `useConflicts()`'s `'keep-current'` resolution already makes) — this is\n// composing an existing primitive in reaction to an event, not a second\n// sync engine (hard rule #3): no retry ladder, no cache, no idempotency\n// logic is reimplemented here, only \"remove this op from the outbox\".\n\nimport type { KaafilClient, OutboxOp } from 'kaafil-js/client';\nimport { SyncOpMethod } from 'kaafil-js/client';\nimport { useCallback, useEffect, useState } from 'react';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { useOfflineEngine } from './useOfflineEngine';\nimport { useOfflineEvent } from './useOfflineEvent';\nimport { useOfflineMutation } from './useOfflineMutation';\nimport { usePersona } from './usePersona';\nimport { useServerTime } from './useServerTime';\n\ntype ShareFormsClient = Pick<KaafilClient, 'share'>;\n\n/** One row of `GET /api/v1/share/{token}/forms` — `ShareFormsListResponse`\n * (verified, `kaafil-js/openapi/openapi.json`). Metadata only: no prefill,\n * no field list, no answers (`FRD:traveller-share §4.8`) — that detail\n * arrives only from `renderForm()` below. */\nexport interface ShareFormListItem {\n readonly formId: string;\n readonly key: string;\n readonly title: string;\n readonly phase: string;\n readonly required: boolean;\n readonly fillState: string;\n readonly opensAt: string | null;\n readonly closesAt: string | null;\n readonly responseStatus: string | null;\n readonly submittedAt: string | null;\n}\n\n/** One answer in a `save`/`submit` body — `ShareSaveFormRequest.answers[]`\n * / `ShareSubmitFormRequest.answers[]` (verified). */\nexport interface ShareFormAnswer {\n readonly fieldKey: string;\n readonly value: unknown;\n}\n\nexport interface ShareFormWriteInput {\n readonly formId: string;\n readonly answers: readonly ShareFormAnswer[];\n /** This route's own de-dupe key — REQUIRED by the wire schema. See this\n * file's header (\"`clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY\") —\n * the caller mints this once per form-fill session and reuses it across\n * every save/submit/retry of that session; this hook never generates it. */\n readonly clientToken: string;\n /** Group fill within the same `TripTraveller.partyId` — omit when the\n * token itself already scopes to a single traveller. */\n readonly travellerRef?: string;\n readonly startNew?: boolean;\n readonly isAnonymous?: boolean;\n}\n\nexport type ShareFormsListStatus = 'loading' | 'list-empty' | 'list-populated' | 'not-found';\n\n/**\n * The write-side status a `save()`/`submit()` call's tracked `OutboxOp`\n * resolves to. Deliberately excludes `'filling'` (`02-forms.md`'s own\n * `States` list also names it): `'filling'` is \"the traveller is editing\n * fields, no request in flight yet\" — a pure local UI mode with no\n * server/outbox signal behind it, so it belongs to the composite that owns\n * the form's field state, never to this hook, which only ever reflects\n * something async.\n */\nexport type ShareFormsWriteStatus =\n | 'idle'\n | 'saving'\n | 'submitting'\n | 'submitted'\n | 'conflicted'\n | 'subject-required'\n | 'queued-offline'\n | 'dropped-on-revoke';\n\nexport interface ShareFormsResult {\n readonly status: ShareFormsListStatus;\n /** `[]` in both the `'loading'` and the `'list-empty'` states — a\n * consumer distinguishes them via `status`, never via array emptiness\n * alone (`02-forms.md`'s `Gating`: an empty list is a NORMAL steady\n * state, never an error, never a placeholder). */\n readonly forms: readonly ShareFormListItem[];\n readonly serverTime: string | undefined;\n /** Re-runs `share.forms.list()`. A composite calls this after a\n * `submit()` lands, since submitting can change another form's\n * `fillState` (e.g. an any-phase trigger opening a follow-up form). */\n readonly refresh: () => Promise<void>;\n /** `GET /api/v1/share/{token}/forms/{formId}` — the fully-resolved,\n * ready-to-paint form (definition merged with any in-progress answers).\n * An open shape (`ShareRenderResponse` has no closed schema yet,\n * verified) — a live call, not cached, matching `save`/`submit`'s own\n * un-cached write path. */\n readonly renderForm: (\n formId: string,\n options?: { readonly travellerRef?: string },\n ) => Promise<Record<string, unknown>>;\n /** An in-progress save — no required-field validation server-side. */\n readonly save: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** Finalizes the response — full validation, one transaction. */\n readonly submit: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** The combined save/submit write status — see `ShareFormsWriteStatus`. */\n readonly writeStatus: ShareFormsWriteStatus;\n /** The most recently tracked save/submit `OutboxOp`, or `undefined`\n * before either has ever been called. */\n readonly writeOp: OutboxOp | undefined;\n}\n\nfunction deriveListStatus(\n forms: readonly ShareFormListItem[] | undefined,\n listFailed: boolean,\n): ShareFormsListStatus {\n if (forms === undefined) {\n return listFailed ? 'not-found' : 'loading';\n }\n return forms.length === 0 ? 'list-empty' : 'list-populated';\n}\n\nfunction deriveWriteStatus(\n revoked: boolean,\n saveOp: OutboxOp | undefined,\n submitOp: OutboxOp | undefined,\n isOnline: boolean,\n): { readonly status: ShareFormsWriteStatus; readonly op: OutboxOp | undefined } {\n if (revoked) {\n return { status: 'dropped-on-revoke', op: submitOp ?? saveOp };\n }\n // `submit` always takes precedence when both are in play — finishing a\n // submission is the more advanced, more important-to-surface state of\n // the two for the same form.\n const active = submitOp !== undefined ? { op: submitOp, kind: 'submit' as const } : undefined;\n const fallback =\n active ?? (saveOp !== undefined ? { op: saveOp, kind: 'save' as const } : undefined);\n if (fallback === undefined) {\n return { status: 'idle', op: undefined };\n }\n const { op, kind } = fallback;\n if (op.status === 'parked') {\n if (op.parkReason?.code === 'CONFLICT_VERSION') {\n return { status: 'conflicted', op };\n }\n if (op.parkReason?.code === 'SUBJECT_REQUIRED') {\n return { status: 'subject-required', op };\n }\n // Any other park reason (e.g. `ShareWriteGraceExpired` —\n // `kaafil-js/src/offline/share.ts`, a grace window that closed before\n // this op ever synced) reports the same as a still-queued offline\n // write: the honest \"still catching up\" framing until it is provably\n // gone (`02-forms.md`'s `Offline` field). A distinct terminal wording\n // for that specific park reason is `ShareFormsSection`'s own\n // presentation concern, not this hook's.\n return { status: 'queued-offline', op };\n }\n if (op.status === 'done') {\n return { status: kind === 'submit' ? 'submitted' : 'idle', op };\n }\n // pending or inflight.\n if (!isOnline) {\n return { status: 'queued-offline', op };\n }\n return { status: kind === 'submit' ? 'submitting' : 'saving', op };\n}\n\nexport function useShareForms(token: string): ShareFormsResult {\n const client = useKaafilClient() as ShareFormsClient;\n const persona = usePersona();\n const engine = useOfflineEngine();\n const { now, sinceLastSync } = useServerTime();\n // `now()` throws before this session's first `snapshot.updated` event —\n // see `useServerTime.ts`'s own \"CALLERS: guard the call site\" note. This\n // hook's writes stay legal without a stamp (see the `enqueuedAt` call\n // sites below), so the guard is \"can I use it\", never \"must I have it\".\n const serverTimeKnown = sinceLastSync !== undefined;\n\n const [forms, setForms] = useState<readonly ShareFormListItem[] | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [listFailed, setListFailed] = useState(false);\n const [revoked, setRevoked] = useState(false);\n const [isOnline, setIsOnline] = useState(\n () => typeof navigator === 'undefined' || navigator.onLine,\n );\n\n const fetchList = useCallback(async (): Promise<void> => {\n try {\n const response = await client.share.forms.list({ token });\n setForms(response.items);\n setServerTime(response.meta.serverTime);\n setListFailed(false);\n } catch {\n // `GET .../forms` only ever fails `RESOURCE_NOT_FOUND` (`FRD:\n // traveller-share §4.1`'s route table, verified) — the share 404\n // carve-out means dark/cross-scope/genuinely-gone collapse to one\n // shape, same reasoning `useShareSnapshot()` applies to its own read.\n setListFailed(true);\n }\n }, [client, token]);\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareForms() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (families/traveller/02-forms.md, `ShareFormsSection`).',\n );\n }\n return;\n }\n void fetchList();\n }, [persona, fetchList]);\n\n // A browser-level connectivity signal, same source `useOutboxStatus()`\n // reads (`kaafil-js`'s `OfflineEngine` exposes no queryable connectivity\n // flag of its own — see that hook's own header, verified again here).\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n const handleOnline = (): void => setIsOnline(true);\n const handleOffline = (): void => setIsOnline(false);\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n useOfflineEvent('share.revoked', () => {\n setRevoked(true);\n // See this file's header (\"REVOCATION DROPS QUEUED WRITES\"). Only this\n // token's own lane, and only ops still in play (a `'done'` op already\n // landed and has nothing left to drop).\n for (const op of engine.outbox.all()) {\n if (op.tripRef === token && (op.status === 'pending' || op.status === 'parked')) {\n void engine.outbox.drop(op.id);\n }\n }\n });\n\n const saveMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/save${query}`,\n // Hardcoded literal, not imported: `OperationId` is not exported\n // from `kaafil-js/client`'s public surface (same reasoning\n // `useRooming.ts` documents). Value verified against\n // `kaafil-js/src/generated/security.ts` this session:\n // `OperationId.ShareSaveForm === 'shareSaveForm'`.\n operationId: 'shareSaveForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // The server-anchored \"now\" this write was created at — never\n // `Date.now()`/`new Date()` (hard rule #6) — `EnqueueWriteOptions\n // .enqueuedAt`'s own doc comment: \"client-attested and only ever\n // used by the server to REFUSE\" (the 7-day post-expiry grace\n // check). Optional on the wire, and genuinely omitted here on a\n // cold session: `useServerTime().now()` throws before this\n // session's very first `snapshot.updated` event has landed (its\n // own documented behaviour, guarded at the call site exactly as\n // its header instructs) — a share write attempted in that narrow\n // window still enqueues durably, just without a client-attested\n // stamp for the server's grace check to read.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const submitMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/submit${query}`,\n operationId: 'shareSubmitForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // See the identical `save` enqueue above for why this is guarded.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const renderForm = useCallback(\n async (\n formId: string,\n options?: { readonly travellerRef?: string },\n ): Promise<Record<string, unknown>> => {\n const response = await client.share.forms.render({\n token,\n formId,\n ...(options?.travellerRef !== undefined ? { travellerRef: options.travellerRef } : {}),\n });\n return response;\n },\n [client, token],\n );\n\n const { status: writeStatus, op: writeOp } = deriveWriteStatus(\n revoked,\n saveMutation.op,\n submitMutation.op,\n isOnline,\n );\n\n return {\n status: deriveListStatus(forms, listFailed),\n forms: forms ?? [],\n serverTime,\n refresh: fetchList,\n renderForm,\n save: saveMutation.mutate,\n submit: submitMutation.mutate,\n writeStatus,\n writeOp,\n };\n}\n","'use client';\n\n// `08-core-hooks.md §3.1`. The single assembled read behind the whole\n// traveller share surface. Wraps `share.snapshot()` (verified `shareAuth`\n// -only in `security.ts` — no `apiKeyAuth` alternative exists for any\n// `share.*` operation), read cache-first from the snapshot once\n// `OfflineEngine`'s `share` option is configured.\n//\n// VERIFIED SHAPE (`openapi/openapi.json`'s `ShareSnapshotResponse`, this\n// session): a flat object with one property PER section key, each present\n// only when the link exposes it and data exists — never `null`, never an\n// empty placeholder — plus a `trip` echo (`name`/`startDate`/`endDate`/\n// `timezone`/`phase`) that is NOT one of the thirteen section flags\n// (`09-registries.md §5`). This hook builds `sections` by taking every own\n// key off the response except `trip` and `meta` — additive-safe against a\n// future 14th section key without needing this file to enumerate all\n// thirteen by name.\n//\n// `trip` and the server clock are surfaced as their OWN top-level fields on\n// `ShareSnapshotResult`, never folded into `sections`: every consumer of\n// `sections` (starting with `KaafilShareView`, which walks the map and\n// renders one composite per key) treats \"is this key present\" as \"should I\n// render a section for it\" — letting `trip`/`meta` leak into that map would\n// make the view try to mount a `<ShareTripSection>`/`<ShareMetaSection>`\n// that has never existed. But every traveller composite still needs BOTH:\n// `trip.timezone` is the one zone `DateDisplay`/`TimeDisplay` may legally\n// anchor to on this surface (never the device zone, never a fabricated\n// `'UTC'`), `trip.phase` lets a composite react to PRE_DEPARTURE/ON_TRIP/\n// POST_TRIP, and `meta.serverTime` is the only trusted \"now\" a share\n// composite may read without going through `useServerTime()` (hard rule\n// #6 — no `Date.now()`/`new Date()` in `src/core`). Hence: two extra return\n// fields, kept structurally apart from the section map they must never\n// pollute.\n\nimport type { KaafilClient } from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { usePersona } from './usePersona';\n\nexport interface ShareSnapshotOptions {\n readonly travellerRef?: string;\n}\n\n// Derived from the SDK's own return type rather than hand-copied — this\n// stays correct for free if `kaafil-js` grows another `trip` field, instead\n// of silently drifting the way the old `'UTC'` constants did.\ntype ShareSnapshotResponse = Awaited<ReturnType<KaafilClient['share']['snapshot']>>;\n\n/** The trip echo every snapshot response carries — `name`/`startDate`/\n * `endDate` plus the two fields this hook used to throw away:\n * `timezone` (the IANA zone every traveller-facing render must anchor to)\n * and `phase` (server-computed `PRE_DEPARTURE`/`ON_TRIP`/`POST_TRIP`). */\nexport type ShareSnapshotTrip = ShareSnapshotResponse['trip'];\n\nexport interface ShareSnapshotResult {\n readonly status: 'loading' | 'ready' | 'expired' | 'revoked' | 'not-found';\n readonly sections: ReadonlyMap<string, unknown>;\n /** The snapshot's `trip` echo, or `undefined` before any response has\n * landed (mirrors `sections`' own \"empty until ready\" behaviour). NEVER a\n * section key — see this file's header. */\n readonly trip: ShareSnapshotTrip | undefined;\n /** `meta.serverTime` off the most recently landed snapshot response — the\n * only trusted clock a share composite may read (hard rule #6). Kept\n * separate from `useServerTime()`'s own stream (which advances off\n * `OfflineEngine`'s `snapshot.updated` event, not this hook's own fetch)\n * so a composite that only ever calls `useShareSnapshot()` still has a\n * real server stamp to compare against without also wiring the offline\n * engine's event stream. `undefined` before any response has landed. */\n readonly serverTime: string | undefined;\n}\n\nconst NON_SECTION_KEYS = new Set(['trip', 'meta']);\n\nexport function useShareSnapshot(\n token: string,\n options?: ShareSnapshotOptions,\n): ShareSnapshotResult {\n void options; // `travellerRef` narrows which subject-token rows the server\n // itself returns for a `travellerRef`-scoped share link — this hook has no\n // client-side use for it beyond passing the token through; the server does\n // the scoping (`09-registries.md §5`, `families/traveller/01-share-sections.md`, cited).\n\n const client = useKaafilClient();\n const persona = usePersona();\n const engine = useContext(OfflineEngineContext);\n const [sections, setSections] = useState<ReadonlyMap<string, unknown> | undefined>(undefined);\n const [trip, setTrip] = useState<ShareSnapshotTrip | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [terminal, setTerminal] = useState<'expired' | 'revoked' | 'not-found' | undefined>(\n undefined,\n );\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareSnapshot() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (08-core-hooks.md §3.1).',\n );\n }\n return;\n }\n\n let cancelled = false;\n async function load(): Promise<void> {\n try {\n const response = await client.share.snapshot({ token });\n if (cancelled) {\n return;\n }\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(response)) {\n if (!NON_SECTION_KEYS.has(key)) {\n map.set(key, value);\n }\n }\n setSections(map);\n setTrip(response.trip);\n setServerTime(response.meta.serverTime);\n } catch {\n // `06-capability-and-personas.md §9`: dark, cross-scope, and\n // genuinely-gone all collapse to the identical shape here — this\n // hook cannot and must not try to distinguish them, so any failure\n // to read the snapshot at all reports the same `'not-found'`.\n if (!cancelled) {\n setTerminal('not-found');\n }\n }\n }\n void load();\n\n if (engine === null) {\n return () => {\n cancelled = true;\n };\n }\n const unsubscribeExpired = engine.events.on('share.expired', () => setTerminal('expired'));\n const unsubscribeRevoked = engine.events.on('share.revoked', () => setTerminal('revoked'));\n return () => {\n cancelled = true;\n unsubscribeExpired();\n unsubscribeRevoked();\n };\n }, [client, persona, token, engine]);\n\n if (terminal !== undefined) {\n return { status: terminal, sections: sections ?? new Map(), trip, serverTime };\n }\n if (sections === undefined) {\n return { status: 'loading', sections: new Map(), trip: undefined, serverTime: undefined };\n }\n return { status: 'ready', sections, trip, serverTime };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/hooks/useShareForms.ts","../src/core/hooks/useShareSnapshot.ts"],"names":["client","useKaafilClient","usePersona","useOfflineEngine","useServerTime","useState","useCallback","useEffect","isDevelopment","useOfflineEvent","useOfflineMutation","SyncOpMethod","useContext","OfflineEngineContext"],"mappings":";;;;;;;;AA6LA,SAAS,gBAAA,CACP,OACA,UAAA,EACsB;AACtB,EAAA,IAAI,UAAU,MAAA,EAAW;AACvB,IAAA,OAAO,aAAa,WAAA,GAAc,SAAA;AAAA,EACpC;AACA,EAAA,OAAO,KAAA,CAAM,MAAA,KAAW,CAAA,GAAI,YAAA,GAAe,gBAAA;AAC7C;AAEA,SAAS,iBAAA,CACP,OAAA,EACA,MAAA,EACA,QAAA,EACA,QAAA,EAC+E;AAC/E,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO,EAAE,MAAA,EAAQ,mBAAA,EAAqB,EAAA,EAAI,YAAY,MAAA,EAAO;AAAA,EAC/D;AAIA,EAAA,MAAM,MAAA,GAAS,aAAa,MAAA,GAAY,EAAE,IAAI,QAAA,EAAU,IAAA,EAAM,UAAkB,GAAI,MAAA;AACpF,EAAA,MAAM,QAAA,GACJ,WAAW,MAAA,KAAW,MAAA,GAAY,EAAE,EAAA,EAAI,MAAA,EAAQ,IAAA,EAAM,MAAA,EAAgB,GAAI,MAAA,CAAA;AAC5E,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,EAAA,EAAI,MAAA,EAAU;AAAA,EACzC;AACA,EAAA,MAAM,EAAE,EAAA,EAAI,IAAA,EAAK,GAAI,QAAA;AACrB,EAAA,IAAI,EAAA,CAAG,WAAW,QAAA,EAAU;AAC1B,IAAA,IAAI,EAAA,CAAG,UAAA,EAAY,IAAA,KAAS,kBAAA,EAAoB;AAC9C,MAAA,OAAO,EAAE,MAAA,EAAQ,YAAA,EAAc,EAAA,EAAG;AAAA,IACpC;AACA,IAAA,IAAI,EAAA,CAAG,UAAA,EAAY,IAAA,KAAS,kBAAA,EAAoB;AAC9C,MAAA,OAAO,EAAE,MAAA,EAAQ,kBAAA,EAAoB,EAAA,EAAG;AAAA,IAC1C;AAQA,IAAA,OAAO,EAAE,MAAA,EAAQ,gBAAA,EAAkB,EAAA,EAAG;AAAA,EACxC;AACA,EAAA,IAAI,EAAA,CAAG,WAAW,MAAA,EAAQ;AACxB,IAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,KAAS,QAAA,GAAW,WAAA,GAAc,QAAQ,EAAA,EAAG;AAAA,EAChE;AAEA,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,EAAE,MAAA,EAAQ,gBAAA,EAAkB,EAAA,EAAG;AAAA,EACxC;AACA,EAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,KAAS,QAAA,GAAW,YAAA,GAAe,UAAU,EAAA,EAAG;AACnE;AAEO,SAAS,cAAc,KAAA,EAAiC;AAC7D,EAAA,MAAMA,WAASC,iCAAA,EAAgB;AAC/B,EAAA,MAAM,UAAUC,4BAAA,EAAW;AAC3B,EAAA,MAAM,SAASC,kCAAA,EAAiB;AAChC,EAAA,MAAM,EAAE,GAAA,EAAK,aAAA,EAAc,GAAIC,+BAAA,EAAc;AAK7C,EAAA,MAAM,kBAAkB,aAAA,KAAkB,MAAA;AAE1C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIC,eAAmD,MAAS,CAAA;AACtF,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,eAA6B,MAAS,CAAA;AAC1E,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,eAAS,KAAK,CAAA;AAClD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,cAAA;AAAA,IAC9B,MAAM,OAAO,SAAA,KAAc,WAAA,IAAe,SAAA,CAAU;AAAA,GACtD;AAEA,EAAA,MAAM,SAAA,GAAYC,kBAAY,YAA2B;AACvD,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAMN,QAAA,CAAO,KAAA,CAAM,MAAM,IAAA,CAAK,EAAE,OAAO,CAAA;AACxD,MAAA,QAAA,CAAS,SAAS,KAAK,CAAA;AACvB,MAAA,aAAA,CAAc,QAAA,CAAS,KAAK,UAAU,CAAA;AACtC,MAAA,aAAA,CAAc,KAAK,CAAA;AAAA,IACrB,CAAA,CAAA,MAAQ;AAKN,MAAA,aAAA,CAAc,IAAI,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG,CAACA,QAAA,EAAQ,KAAK,CAAC,CAAA;AAElB,EAAAO,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,IAAIC,iCAAc,EAAG;AACnB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SAEF;AAAA,MACF;AACA,MAAA;AAAA,IACF;AACA,IAAA,KAAK,SAAA,EAAU;AAAA,EACjB,CAAA,EAAG,CAAC,OAAA,EAAS,SAAS,CAAC,CAAA;AAKvB,EAAAD,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA;AAAA,IACF;AACA,IAAA,MAAM,YAAA,GAAe,MAAY,WAAA,CAAY,IAAI,CAAA;AACjD,IAAA,MAAM,aAAA,GAAgB,MAAY,WAAA,CAAY,KAAK,CAAA;AACnD,IAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,YAAY,CAAA;AAC9C,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAChD,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,YAAY,CAAA;AACjD,MAAA,MAAA,CAAO,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,IACrD,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAAE,iCAAA,CAAgB,iBAAiB,MAAM;AACrC,IAAA,UAAA,CAAW,IAAI,CAAA;AAIf,IAAA,KAAA,MAAW,EAAA,IAAM,MAAA,CAAO,MAAA,CAAO,GAAA,EAAI,EAAG;AACpC,MAAA,IAAI,EAAA,CAAG,YAAY,KAAA,KAAU,EAAA,CAAG,WAAW,SAAA,IAAa,EAAA,CAAG,WAAW,QAAA,CAAA,EAAW;AAC/E,QAAA,KAAK,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,EAAA,CAAG,EAAE,CAAA;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,eAAeC,oCAAA,CAAwC;AAAA,IAC3D,OAAA,EAAS,KAAA;AAAA,IACT,OAAA,EAAS,CAAC,cAAA,EAAgB,KAAA,KAAU;AAClC,MAAA,MAAM,KAAA,GACJ,MAAM,YAAA,KAAiB,MAAA,GACnB,iBAAiB,kBAAA,CAAmB,KAAA,CAAM,YAAY,CAAC,CAAA,CAAA,GACvD,EAAA;AACN,MAAA,OAAO,eAAe,OAAA,CAAQ;AAAA,QAC5B,OAAA,EAAS,KAAA;AAAA,QACT,QAAQC,mBAAA,CAAa,IAAA;AAAA,QACrB,MAAM,CAAA,cAAA,EAAiB,KAAK,UAAU,KAAA,CAAM,MAAM,QAAQ,KAAK,CAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAM/D,WAAA,EAAa,eAAA;AAAA,QACb,IAAA,EAAM;AAAA,UACJ,SAAS,KAAA,CAAM,OAAA;AAAA,UACf,aAAa,KAAA,CAAM,WAAA;AAAA,UACnB,GAAI,MAAM,QAAA,KAAa,MAAA,GAAY,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS,GAAI,EAAC;AAAA,UACnE,GAAI,MAAM,WAAA,KAAgB,MAAA,GAAY,EAAE,WAAA,EAAa,KAAA,CAAM,WAAA,EAAY,GAAI;AAAC,SAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAYA,GAAI,eAAA,GAAkB,EAAE,YAAY,GAAA,EAAI,KAAM;AAAC,OAChD,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AAED,EAAA,MAAM,iBAAiBD,oCAAA,CAAwC;AAAA,IAC7D,OAAA,EAAS,KAAA;AAAA,IACT,OAAA,EAAS,CAAC,cAAA,EAAgB,KAAA,KAAU;AAClC,MAAA,MAAM,KAAA,GACJ,MAAM,YAAA,KAAiB,MAAA,GACnB,iBAAiB,kBAAA,CAAmB,KAAA,CAAM,YAAY,CAAC,CAAA,CAAA,GACvD,EAAA;AACN,MAAA,OAAO,eAAe,OAAA,CAAQ;AAAA,QAC5B,OAAA,EAAS,KAAA;AAAA,QACT,QAAQC,mBAAA,CAAa,IAAA;AAAA,QACrB,MAAM,CAAA,cAAA,EAAiB,KAAK,UAAU,KAAA,CAAM,MAAM,UAAU,KAAK,CAAA,CAAA;AAAA,QACjE,WAAA,EAAa,iBAAA;AAAA,QACb,IAAA,EAAM;AAAA,UACJ,SAAS,KAAA,CAAM,OAAA;AAAA,UACf,aAAa,KAAA,CAAM,WAAA;AAAA,UACnB,GAAI,MAAM,QAAA,KAAa,MAAA,GAAY,EAAE,QAAA,EAAU,KAAA,CAAM,QAAA,EAAS,GAAI,EAAC;AAAA,UACnE,GAAI,MAAM,WAAA,KAAgB,MAAA,GAAY,EAAE,WAAA,EAAa,KAAA,CAAM,WAAA,EAAY,GAAI;AAAC,SAC9E;AAAA;AAAA,QAEA,GAAI,eAAA,GAAkB,EAAE,YAAY,GAAA,EAAI,KAAM;AAAC,OAChD,CAAA;AAAA,IACH;AAAA,GACD,CAAA;AAED,EAAA,MAAM,UAAA,GAAaL,iBAAA;AAAA,IACjB,OACE,QACA,OAAA,KACqC;AACrC,MAAA,MAAM,QAAA,GAAW,MAAMN,QAAA,CAAO,KAAA,CAAM,MAAM,MAAA,CAAO;AAAA,QAC/C,KAAA;AAAA,QACA,MAAA;AAAA,QACA,GAAI,SAAS,YAAA,KAAiB,MAAA,GAAY,EAAE,YAAA,EAAc,OAAA,CAAQ,YAAA,EAAa,GAAI;AAAC,OACrF,CAAA;AACD,MAAA,OAAO,QAAA;AAAA,IACT,CAAA;AAAA,IACA,CAACA,UAAQ,KAAK;AAAA,GAChB;AAEA,EAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAa,EAAA,EAAI,SAAQ,GAAI,iBAAA;AAAA,IAC3C,OAAA;AAAA,IACA,YAAA,CAAa,EAAA;AAAA,IACb,cAAA,CAAe,EAAA;AAAA,IACf;AAAA,GACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,gBAAA,CAAiB,KAAA,EAAO,UAAU,CAAA;AAAA,IAC1C,KAAA,EAAO,SAAS,EAAC;AAAA,IACjB,UAAA;AAAA,IACA,OAAA,EAAS,SAAA;AAAA,IACT,UAAA;AAAA,IACA,MAAM,YAAA,CAAa,MAAA;AAAA,IACnB,QAAQ,cAAA,CAAe,MAAA;AAAA,IACvB,WAAA;AAAA,IACA;AAAA,GACF;AACF;ACvVA,IAAM,mCAAmB,IAAI,GAAA,CAAI,CAAC,MAAA,EAAQ,MAAM,CAAC,CAAA;AAE1C,SAAS,gBAAA,CACd,OACA,OAAA,EACqB;AAMrB,EAAA,MAAM,SAASC,iCAAA,EAAgB;AAC/B,EAAA,MAAM,UAAUC,4BAAA,EAAW;AAC3B,EAAA,MAAM,MAAA,GAASU,iBAAWC,sCAAoB,CAAA;AAC9C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIR,eAAmD,MAAS,CAAA;AAC5F,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIA,eAAwC,MAAS,CAAA;AACzE,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,eAA6B,MAAS,CAAA;AAC1E,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,cAAAA;AAAA,IAC9B;AAAA,GACF;AAEA,EAAAE,gBAAU,MAAM;AACd,IAAA,IAAI,YAAY,OAAA,EAAS;AACvB,MAAA,IAAIC,iCAAc,EAAG;AACnB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SAEF;AAAA,MACF;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,eAAe,IAAA,GAAsB;AACnC,MAAA,IAAI;AACF,QAAA,MAAM,WAAW,MAAM,MAAA,CAAO,MAAM,QAAA,CAAS,EAAE,OAAO,CAAA;AACtD,QAAA,IAAI,SAAA,EAAW;AACb,UAAA;AAAA,QACF;AACA,QAAA,MAAM,GAAA,uBAAU,GAAA,EAAqB;AACrC,QAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA,EAAG;AACnD,UAAA,IAAI,CAAC,gBAAA,CAAiB,GAAA,CAAI,GAAG,CAAA,EAAG;AAC9B,YAAA,GAAA,CAAI,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,UACpB;AAAA,QACF;AACA,QAAA,WAAA,CAAY,GAAG,CAAA;AACf,QAAA,OAAA,CAAQ,SAAS,IAAI,CAAA;AACrB,QAAA,aAAA,CAAc,QAAA,CAAS,KAAK,UAAU,CAAA;AAAA,MACxC,CAAA,CAAA,MAAQ;AAKN,QAAA,IAAI,CAAC,SAAA,EAAW;AACd,UAAA,WAAA,CAAY,WAAW,CAAA;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AACA,IAAA,KAAK,IAAA,EAAK;AAEV,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA,OAAO,MAAM;AACX,QAAA,SAAA,GAAY,IAAA;AAAA,MACd,CAAA;AAAA,IACF;AACA,IAAA,MAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO,EAAA,CAAG,iBAAiB,MAAM,WAAA,CAAY,SAAS,CAAC,CAAA;AACzF,IAAA,MAAM,kBAAA,GAAqB,OAAO,MAAA,CAAO,EAAA,CAAG,iBAAiB,MAAM,WAAA,CAAY,SAAS,CAAC,CAAA;AACzF,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,kBAAA,EAAmB;AACnB,MAAA,kBAAA,EAAmB;AAAA,IACrB,CAAA;AAAA,EACF,GAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,MAAM,CAAC,CAAA;AAEnC,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,OAAO,EAAE,QAAQ,QAAA,EAAU,QAAA,EAAU,4BAAY,IAAI,GAAA,EAAI,EAAG,IAAA,EAAM,UAAA,EAAW;AAAA,EAC/E;AACA,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,OAAO,EAAE,MAAA,EAAQ,SAAA,EAAW,QAAA,kBAAU,IAAI,KAAI,EAAG,IAAA,EAAM,MAAA,EAAW,UAAA,EAAY,MAAA,EAAU;AAAA,EAC1F;AACA,EAAA,OAAO,EAAE,MAAA,EAAQ,OAAA,EAAS,QAAA,EAAU,MAAM,UAAA,EAAW;AACvD","file":"chunk-2XW5EIMS.cjs","sourcesContent":["'use client';\n\n// `08-core-hooks.md §3` (new row — `ShareFormsSection`'s `Backed by`,\n// `ui-kit/families/traveller/02-forms.md`, cited): the write-back half of\n// the traveller share surface. `forms` is the ONLY write-back section on\n// the whole share surface; every other section is read-only through\n// `useShareSnapshot()` alone. This hook wraps the four dedicated\n// `shareAuth`-only routes `share.forms.{list,render,save,submit}`\n// (`kaafil-js/src/resources/share.ts`, verified this session).\n//\n// ── WHY THIS HOOK DOES NOT ALSO CALL `useShareSnapshot()` ──────────────────\n//\n// `02-forms.md`'s `Backed by` field says the section's own `/core` hook\n// \"composes both\" — the snapshot (for `forms` section presence) and the\n// dedicated routes (for the actual list/render/save/submit). This file\n// deliberately does NOT reproduce the snapshot fetch: `ShareFormsSection`\n// (future work, not this file) is only ever mounted once its own call to\n// `useShareSnapshot(token)` has already confirmed `sections.has('forms')`\n// (`01-share-sections.md`'s registry-driven mount rule) — duplicating that\n// fetch here would be a second, redundant read of the same snapshot for no\n// new information, and this hook has nothing to add to a presence check it\n// does not own. What this file DOES independently reproduce is\n// `useShareSnapshot()`'s own persona gate and its `share.expired`/\n// `share.revoked` subscriptions (`08-core-hooks.md §0`: \"every domain hook\n// is capability-gated internally\" — the share-surface's equivalent gate is\n// the persona check, applied the same way every other hook in this catalog\n// applies its own).\n//\n// ── SPEC TENSION FLAGGED: \"cache-first\" vs. \"the dedicated routes\" ─────────\n//\n// `02-forms.md`'s `Offline` field says the list \"rides the scoped share\n// snapshot cache-first, same as every other section\", but its own `Backed\n// by` field says in the same breath that \"the actual form list/render/\n// save/submit calls ride the dedicated forms-share routes\" — and\n// `ShareSnapshotResponse`'s per-section shape (verified,\n// `useShareSnapshot.ts`) carries no registered `forms` array with\n// `fillState`/`opensAt`/`closesAt` fields; only `ShareFormsListResponse`\n// (`GET /api/v1/share/{token}/forms`) does. This file follows the more\n// specific, SDK-verified statement — a LIVE `share.forms.list()` call,\n// refreshable on demand — the same \"own live-fetch sub-state, not a\n// `snapshot.updated` subscription\" pattern `useRooming.ts`'s\n// `unassignedPool` already uses for an analogous non-delta read. Flagged\n// for the doc owner to resolve; not silently picked one way with no trace.\n//\n// ── `clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY ──────────────────────\n//\n// `ShareSaveFormRequest`/`ShareSubmitFormRequest` (verified,\n// `kaafil-js/openapi/openapi.json`) require a body-level `clientToken`\n// (16-255 chars) — the SDK's own comment on `share.ts` calls it \"this\n// route's own de-dupe key, distinct from the automatic `Idempotency-Key`\n// header\", and `modules/forms/FRD.md §4.1/§8` calls it \"a fixed\n// `clientToken` generated once at enqueue and reused on every retry\" —\n// i.e. exactly the write-once, replay-unchanged lifecycle hard rule #4\n// reserves for `OfflineEngine.enqueue()`'s own internal idempotency-key\n// minting. This hook does NOT invent a value for it (that would be exactly\n// the \"hook mints its own idempotency key\" anti-pattern hard rule #4\n// forbids, even though `clientToken` is a different field from the\n// transport `Idempotency-Key` header `enqueue()` mints separately and\n// automatically for every op, share writes included). Instead\n// `ShareFormWriteInput.clientToken` is a REQUIRED field the CALLER\n// supplies — a future `ShareFormsSection`/`ShareFormFillScreen` generates\n// one value per form-fill session (e.g. once, the first time a field is\n// touched) and passes the SAME value to every `save()`/`submit()` call for\n// that session, which is what makes retries of the SAME logical write\n// dedupe wire-side. This hook only ever forwards it into the body verbatim.\n//\n// ── REVOCATION DROPS QUEUED WRITES — A REAL SDK GAP, WORKED AROUND HERE ────\n//\n// `02-forms.md`'s `Offline` field: \"Revocation is different and has no\n// grace: a `REVOKED` token drops queued writes unsent and purges the local\n// copy immediately.\" Verified against `kaafil-js/src/offline/share.ts`\n// this session: `ShareGuard.invalidate()` treats `'expired'` and\n// `'revoked'` IDENTICALLY — it purges the read snapshot in both cases and\n// only PRESERVES (never drops) the outbox either way; nothing in\n// `kaafil-js` currently distinguishes \"grace-covered\" from \"unsent, drop\n// it\" at the outbox level. This is a genuine SDK/spec disagreement — see\n// this file's final report note. Rather than leave the spec's hard\n// requirement unmet, this hook enforces it locally on `share.revoked`,\n// riding the outbox's own existing `drop()` primitive (the identical call\n// `useConflicts()`'s `'keep-current'` resolution already makes) — this is\n// composing an existing primitive in reaction to an event, not a second\n// sync engine (hard rule #3): no retry ladder, no cache, no idempotency\n// logic is reimplemented here, only \"remove this op from the outbox\".\n\nimport type { KaafilClient, OutboxOp } from 'kaafil-js/client';\nimport { SyncOpMethod } from 'kaafil-js/client';\nimport { useCallback, useEffect, useState } from 'react';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { useOfflineEngine } from './useOfflineEngine';\nimport { useOfflineEvent } from './useOfflineEvent';\nimport { useOfflineMutation } from './useOfflineMutation';\nimport { usePersona } from './usePersona';\nimport { useServerTime } from './useServerTime';\n\ntype ShareFormsClient = Pick<KaafilClient, 'share'>;\n\n/** One row of `GET /api/v1/share/{token}/forms` — `ShareFormsListResponse`\n * (verified, `kaafil-js/openapi/openapi.json`). Metadata only: no prefill,\n * no field list, no answers (`FRD:traveller-share §4.8`) — that detail\n * arrives only from `renderForm()` below. */\nexport interface ShareFormListItem {\n readonly formId: string;\n readonly key: string;\n readonly title: string;\n readonly phase: string;\n readonly required: boolean;\n readonly fillState: string;\n readonly opensAt: string | null;\n readonly closesAt: string | null;\n readonly responseStatus: string | null;\n readonly submittedAt: string | null;\n}\n\n/** One answer in a `save`/`submit` body — `ShareSaveFormRequest.answers[]`\n * / `ShareSubmitFormRequest.answers[]` (verified). */\nexport interface ShareFormAnswer {\n readonly fieldKey: string;\n readonly value: unknown;\n}\n\nexport interface ShareFormWriteInput {\n readonly formId: string;\n readonly answers: readonly ShareFormAnswer[];\n /** This route's own de-dupe key — REQUIRED by the wire schema. See this\n * file's header (\"`clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY\") —\n * the caller mints this once per form-fill session and reuses it across\n * every save/submit/retry of that session; this hook never generates it. */\n readonly clientToken: string;\n /** Group fill within the same `TripTraveller.partyId` — omit when the\n * token itself already scopes to a single traveller. */\n readonly travellerRef?: string;\n readonly startNew?: boolean;\n readonly isAnonymous?: boolean;\n}\n\nexport type ShareFormsListStatus = 'loading' | 'list-empty' | 'list-populated' | 'not-found';\n\n/**\n * The write-side status a `save()`/`submit()` call's tracked `OutboxOp`\n * resolves to. Deliberately excludes `'filling'` (`02-forms.md`'s own\n * `States` list also names it): `'filling'` is \"the traveller is editing\n * fields, no request in flight yet\" — a pure local UI mode with no\n * server/outbox signal behind it, so it belongs to the composite that owns\n * the form's field state, never to this hook, which only ever reflects\n * something async.\n */\nexport type ShareFormsWriteStatus =\n | 'idle'\n | 'saving'\n | 'submitting'\n | 'submitted'\n | 'conflicted'\n | 'subject-required'\n | 'queued-offline'\n | 'dropped-on-revoke';\n\nexport interface ShareFormsResult {\n readonly status: ShareFormsListStatus;\n /** `[]` in both the `'loading'` and the `'list-empty'` states — a\n * consumer distinguishes them via `status`, never via array emptiness\n * alone (`02-forms.md`'s `Gating`: an empty list is a NORMAL steady\n * state, never an error, never a placeholder). */\n readonly forms: readonly ShareFormListItem[];\n readonly serverTime: string | undefined;\n /** Re-runs `share.forms.list()`. A composite calls this after a\n * `submit()` lands, since submitting can change another form's\n * `fillState` (e.g. an any-phase trigger opening a follow-up form). */\n readonly refresh: () => Promise<void>;\n /** `GET /api/v1/share/{token}/forms/{formId}` — the fully-resolved,\n * ready-to-paint form (definition merged with any in-progress answers).\n * An open shape (`ShareRenderResponse` has no closed schema yet,\n * verified) — a live call, not cached, matching `save`/`submit`'s own\n * un-cached write path. */\n readonly renderForm: (\n formId: string,\n options?: { readonly travellerRef?: string },\n ) => Promise<Record<string, unknown>>;\n /** An in-progress save — no required-field validation server-side. */\n readonly save: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** Finalizes the response — full validation, one transaction. */\n readonly submit: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** The combined save/submit write status — see `ShareFormsWriteStatus`. */\n readonly writeStatus: ShareFormsWriteStatus;\n /** The most recently tracked save/submit `OutboxOp`, or `undefined`\n * before either has ever been called. */\n readonly writeOp: OutboxOp | undefined;\n}\n\nfunction deriveListStatus(\n forms: readonly ShareFormListItem[] | undefined,\n listFailed: boolean,\n): ShareFormsListStatus {\n if (forms === undefined) {\n return listFailed ? 'not-found' : 'loading';\n }\n return forms.length === 0 ? 'list-empty' : 'list-populated';\n}\n\nfunction deriveWriteStatus(\n revoked: boolean,\n saveOp: OutboxOp | undefined,\n submitOp: OutboxOp | undefined,\n isOnline: boolean,\n): { readonly status: ShareFormsWriteStatus; readonly op: OutboxOp | undefined } {\n if (revoked) {\n return { status: 'dropped-on-revoke', op: submitOp ?? saveOp };\n }\n // `submit` always takes precedence when both are in play — finishing a\n // submission is the more advanced, more important-to-surface state of\n // the two for the same form.\n const active = submitOp !== undefined ? { op: submitOp, kind: 'submit' as const } : undefined;\n const fallback =\n active ?? (saveOp !== undefined ? { op: saveOp, kind: 'save' as const } : undefined);\n if (fallback === undefined) {\n return { status: 'idle', op: undefined };\n }\n const { op, kind } = fallback;\n if (op.status === 'parked') {\n if (op.parkReason?.code === 'CONFLICT_VERSION') {\n return { status: 'conflicted', op };\n }\n if (op.parkReason?.code === 'SUBJECT_REQUIRED') {\n return { status: 'subject-required', op };\n }\n // Any other park reason (e.g. `ShareWriteGraceExpired` —\n // `kaafil-js/src/offline/share.ts`, a grace window that closed before\n // this op ever synced) reports the same as a still-queued offline\n // write: the honest \"still catching up\" framing until it is provably\n // gone (`02-forms.md`'s `Offline` field). A distinct terminal wording\n // for that specific park reason is `ShareFormsSection`'s own\n // presentation concern, not this hook's.\n return { status: 'queued-offline', op };\n }\n if (op.status === 'done') {\n return { status: kind === 'submit' ? 'submitted' : 'idle', op };\n }\n // pending or inflight.\n if (!isOnline) {\n return { status: 'queued-offline', op };\n }\n return { status: kind === 'submit' ? 'submitting' : 'saving', op };\n}\n\nexport function useShareForms(token: string): ShareFormsResult {\n const client = useKaafilClient() as ShareFormsClient;\n const persona = usePersona();\n const engine = useOfflineEngine();\n const { now, sinceLastSync } = useServerTime();\n // `now()` throws before this session's first `snapshot.updated` event —\n // see `useServerTime.ts`'s own \"CALLERS: guard the call site\" note. This\n // hook's writes stay legal without a stamp (see the `enqueuedAt` call\n // sites below), so the guard is \"can I use it\", never \"must I have it\".\n const serverTimeKnown = sinceLastSync !== undefined;\n\n const [forms, setForms] = useState<readonly ShareFormListItem[] | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [listFailed, setListFailed] = useState(false);\n const [revoked, setRevoked] = useState(false);\n const [isOnline, setIsOnline] = useState(\n () => typeof navigator === 'undefined' || navigator.onLine,\n );\n\n const fetchList = useCallback(async (): Promise<void> => {\n try {\n const response = await client.share.forms.list({ token });\n setForms(response.items);\n setServerTime(response.meta.serverTime);\n setListFailed(false);\n } catch {\n // `GET .../forms` only ever fails `RESOURCE_NOT_FOUND` (`FRD:\n // traveller-share §4.1`'s route table, verified) — the share 404\n // carve-out means dark/cross-scope/genuinely-gone collapse to one\n // shape, same reasoning `useShareSnapshot()` applies to its own read.\n setListFailed(true);\n }\n }, [client, token]);\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareForms() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (families/traveller/02-forms.md, `ShareFormsSection`).',\n );\n }\n return;\n }\n void fetchList();\n }, [persona, fetchList]);\n\n // A browser-level connectivity signal, same source `useOutboxStatus()`\n // reads (`kaafil-js`'s `OfflineEngine` exposes no queryable connectivity\n // flag of its own — see that hook's own header, verified again here).\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n const handleOnline = (): void => setIsOnline(true);\n const handleOffline = (): void => setIsOnline(false);\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n useOfflineEvent('share.revoked', () => {\n setRevoked(true);\n // See this file's header (\"REVOCATION DROPS QUEUED WRITES\"). Only this\n // token's own lane, and only ops still in play (a `'done'` op already\n // landed and has nothing left to drop).\n for (const op of engine.outbox.all()) {\n if (op.tripRef === token && (op.status === 'pending' || op.status === 'parked')) {\n void engine.outbox.drop(op.id);\n }\n }\n });\n\n const saveMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/save${query}`,\n // Hardcoded literal, not imported: `OperationId` is not exported\n // from `kaafil-js/client`'s public surface (same reasoning\n // `useRooming.ts` documents). Value verified against\n // `kaafil-js/src/generated/security.ts` this session:\n // `OperationId.ShareSaveForm === 'shareSaveForm'`.\n operationId: 'shareSaveForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // The server-anchored \"now\" this write was created at — never\n // `Date.now()`/`new Date()` (hard rule #6) — `EnqueueWriteOptions\n // .enqueuedAt`'s own doc comment: \"client-attested and only ever\n // used by the server to REFUSE\" (the 7-day post-expiry grace\n // check). Optional on the wire, and genuinely omitted here on a\n // cold session: `useServerTime().now()` throws before this\n // session's very first `snapshot.updated` event has landed (its\n // own documented behaviour, guarded at the call site exactly as\n // its header instructs) — a share write attempted in that narrow\n // window still enqueues durably, just without a client-attested\n // stamp for the server's grace check to read.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const submitMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/submit${query}`,\n operationId: 'shareSubmitForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // See the identical `save` enqueue above for why this is guarded.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const renderForm = useCallback(\n async (\n formId: string,\n options?: { readonly travellerRef?: string },\n ): Promise<Record<string, unknown>> => {\n const response = await client.share.forms.render({\n token,\n formId,\n ...(options?.travellerRef !== undefined ? { travellerRef: options.travellerRef } : {}),\n });\n return response;\n },\n [client, token],\n );\n\n const { status: writeStatus, op: writeOp } = deriveWriteStatus(\n revoked,\n saveMutation.op,\n submitMutation.op,\n isOnline,\n );\n\n return {\n status: deriveListStatus(forms, listFailed),\n forms: forms ?? [],\n serverTime,\n refresh: fetchList,\n renderForm,\n save: saveMutation.mutate,\n submit: submitMutation.mutate,\n writeStatus,\n writeOp,\n };\n}\n","'use client';\n\n// `08-core-hooks.md §3.1`. The single assembled read behind the whole\n// traveller share surface. Wraps `share.snapshot()` (verified `shareAuth`\n// -only in `security.ts` — no `apiKeyAuth` alternative exists for any\n// `share.*` operation), read cache-first from the snapshot once\n// `OfflineEngine`'s `share` option is configured.\n//\n// VERIFIED SHAPE (`openapi/openapi.json`'s `ShareSnapshotResponse`, this\n// session): a flat object with one property PER section key, each present\n// only when the link exposes it and data exists — never `null`, never an\n// empty placeholder — plus a `trip` echo (`name`/`startDate`/`endDate`/\n// `timezone`/`phase`) that is NOT one of the thirteen section flags\n// (`09-registries.md §5`). This hook builds `sections` by taking every own\n// key off the response except `trip` and `meta` — additive-safe against a\n// future 14th section key without needing this file to enumerate all\n// thirteen by name.\n//\n// `trip` and the server clock are surfaced as their OWN top-level fields on\n// `ShareSnapshotResult`, never folded into `sections`: every consumer of\n// `sections` (starting with `KaafilShareView`, which walks the map and\n// renders one composite per key) treats \"is this key present\" as \"should I\n// render a section for it\" — letting `trip`/`meta` leak into that map would\n// make the view try to mount a `<ShareTripSection>`/`<ShareMetaSection>`\n// that has never existed. But every traveller composite still needs BOTH:\n// `trip.timezone` is the one zone `DateDisplay`/`TimeDisplay` may legally\n// anchor to on this surface (never the device zone, never a fabricated\n// `'UTC'`), `trip.phase` lets a composite react to PRE_DEPARTURE/ON_TRIP/\n// POST_TRIP, and `meta.serverTime` is the only trusted \"now\" a share\n// composite may read without going through `useServerTime()` (hard rule\n// #6 — no `Date.now()`/`new Date()` in `src/core`). Hence: two extra return\n// fields, kept structurally apart from the section map they must never\n// pollute.\n\nimport type { KaafilClient } from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { usePersona } from './usePersona';\n\nexport interface ShareSnapshotOptions {\n readonly travellerRef?: string;\n}\n\n// Derived from the SDK's own return type rather than hand-copied — this\n// stays correct for free if `kaafil-js` grows another `trip` field, instead\n// of silently drifting the way the old `'UTC'` constants did.\ntype ShareSnapshotResponse = Awaited<ReturnType<KaafilClient['share']['snapshot']>>;\n\n/** The trip echo every snapshot response carries — `name`/`startDate`/\n * `endDate` plus the two fields this hook used to throw away:\n * `timezone` (the IANA zone every traveller-facing render must anchor to)\n * and `phase` (server-computed `PRE_DEPARTURE`/`ON_TRIP`/`POST_TRIP`). */\nexport type ShareSnapshotTrip = ShareSnapshotResponse['trip'];\n\nexport interface ShareSnapshotResult {\n readonly status: 'loading' | 'ready' | 'expired' | 'revoked' | 'not-found';\n readonly sections: ReadonlyMap<string, unknown>;\n /** The snapshot's `trip` echo, or `undefined` before any response has\n * landed (mirrors `sections`' own \"empty until ready\" behaviour). NEVER a\n * section key — see this file's header. */\n readonly trip: ShareSnapshotTrip | undefined;\n /** `meta.serverTime` off the most recently landed snapshot response — the\n * only trusted clock a share composite may read (hard rule #6). Kept\n * separate from `useServerTime()`'s own stream (which advances off\n * `OfflineEngine`'s `snapshot.updated` event, not this hook's own fetch)\n * so a composite that only ever calls `useShareSnapshot()` still has a\n * real server stamp to compare against without also wiring the offline\n * engine's event stream. `undefined` before any response has landed. */\n readonly serverTime: string | undefined;\n}\n\nconst NON_SECTION_KEYS = new Set(['trip', 'meta']);\n\nexport function useShareSnapshot(\n token: string,\n options?: ShareSnapshotOptions,\n): ShareSnapshotResult {\n void options; // `travellerRef` narrows which subject-token rows the server\n // itself returns for a `travellerRef`-scoped share link — this hook has no\n // client-side use for it beyond passing the token through; the server does\n // the scoping (`09-registries.md §5`, `families/traveller/01-share-sections.md`, cited).\n\n const client = useKaafilClient();\n const persona = usePersona();\n const engine = useContext(OfflineEngineContext);\n const [sections, setSections] = useState<ReadonlyMap<string, unknown> | undefined>(undefined);\n const [trip, setTrip] = useState<ShareSnapshotTrip | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [terminal, setTerminal] = useState<'expired' | 'revoked' | 'not-found' | undefined>(\n undefined,\n );\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareSnapshot() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (08-core-hooks.md §3.1).',\n );\n }\n return;\n }\n\n let cancelled = false;\n async function load(): Promise<void> {\n try {\n const response = await client.share.snapshot({ token });\n if (cancelled) {\n return;\n }\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(response)) {\n if (!NON_SECTION_KEYS.has(key)) {\n map.set(key, value);\n }\n }\n setSections(map);\n setTrip(response.trip);\n setServerTime(response.meta.serverTime);\n } catch {\n // `06-capability-and-personas.md §9`: dark, cross-scope, and\n // genuinely-gone all collapse to the identical shape here — this\n // hook cannot and must not try to distinguish them, so any failure\n // to read the snapshot at all reports the same `'not-found'`.\n if (!cancelled) {\n setTerminal('not-found');\n }\n }\n }\n void load();\n\n if (engine === null) {\n return () => {\n cancelled = true;\n };\n }\n const unsubscribeExpired = engine.events.on('share.expired', () => setTerminal('expired'));\n const unsubscribeRevoked = engine.events.on('share.revoked', () => setTerminal('revoked'));\n return () => {\n cancelled = true;\n unsubscribeExpired();\n unsubscribeRevoked();\n };\n }, [client, persona, token, engine]);\n\n if (terminal !== undefined) {\n return { status: terminal, sections: sections ?? new Map(), trip, serverTime };\n }\n if (sections === undefined) {\n return { status: 'loading', sections: new Map(), trip: undefined, serverTime: undefined };\n }\n return { status: 'ready', sections, trip, serverTime };\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useOfflineEngine } from './chunk-
|
|
2
|
-
import { useServerTime } from './chunk-
|
|
3
|
-
import { useKaafilClient, usePersona, isDevelopment, useOfflineEvent, useOfflineMutation, OfflineEngineContext } from './chunk-
|
|
1
|
+
import { useOfflineEngine } from './chunk-CLHAJBJS.js';
|
|
2
|
+
import { useServerTime } from './chunk-RBIEMGHT.js';
|
|
3
|
+
import { useKaafilClient, usePersona, isDevelopment, useOfflineEvent, useOfflineMutation, OfflineEngineContext } from './chunk-7G6IENKN.js';
|
|
4
4
|
import { SyncOpMethod } from 'kaafil-js/client';
|
|
5
5
|
import { useState, useCallback, useEffect, useContext } from 'react';
|
|
6
6
|
|
|
@@ -242,5 +242,5 @@ function useShareSnapshot(token, options) {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
export { useShareForms, useShareSnapshot };
|
|
245
|
-
//# sourceMappingURL=chunk-
|
|
246
|
-
//# sourceMappingURL=chunk-
|
|
245
|
+
//# sourceMappingURL=chunk-3BEQYIFU.js.map
|
|
246
|
+
//# sourceMappingURL=chunk-3BEQYIFU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/hooks/useShareForms.ts","../src/core/hooks/useShareSnapshot.ts"],"names":["useState","useEffect"],"mappings":"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","file":"chunk-YU2242AR.js","sourcesContent":["'use client';\n\n// `08-core-hooks.md §3` (new row — `ShareFormsSection`'s `Backed by`,\n// `ui-kit/families/traveller/02-forms.md`, cited): the write-back half of\n// the traveller share surface. `forms` is the ONLY write-back section on\n// the whole share surface; every other section is read-only through\n// `useShareSnapshot()` alone. This hook wraps the four dedicated\n// `shareAuth`-only routes `share.forms.{list,render,save,submit}`\n// (`kaafil-js/src/resources/share.ts`, verified this session).\n//\n// ── WHY THIS HOOK DOES NOT ALSO CALL `useShareSnapshot()` ──────────────────\n//\n// `02-forms.md`'s `Backed by` field says the section's own `/core` hook\n// \"composes both\" — the snapshot (for `forms` section presence) and the\n// dedicated routes (for the actual list/render/save/submit). This file\n// deliberately does NOT reproduce the snapshot fetch: `ShareFormsSection`\n// (future work, not this file) is only ever mounted once its own call to\n// `useShareSnapshot(token)` has already confirmed `sections.has('forms')`\n// (`01-share-sections.md`'s registry-driven mount rule) — duplicating that\n// fetch here would be a second, redundant read of the same snapshot for no\n// new information, and this hook has nothing to add to a presence check it\n// does not own. What this file DOES independently reproduce is\n// `useShareSnapshot()`'s own persona gate and its `share.expired`/\n// `share.revoked` subscriptions (`08-core-hooks.md §0`: \"every domain hook\n// is capability-gated internally\" — the share-surface's equivalent gate is\n// the persona check, applied the same way every other hook in this catalog\n// applies its own).\n//\n// ── SPEC TENSION FLAGGED: \"cache-first\" vs. \"the dedicated routes\" ─────────\n//\n// `02-forms.md`'s `Offline` field says the list \"rides the scoped share\n// snapshot cache-first, same as every other section\", but its own `Backed\n// by` field says in the same breath that \"the actual form list/render/\n// save/submit calls ride the dedicated forms-share routes\" — and\n// `ShareSnapshotResponse`'s per-section shape (verified,\n// `useShareSnapshot.ts`) carries no registered `forms` array with\n// `fillState`/`opensAt`/`closesAt` fields; only `ShareFormsListResponse`\n// (`GET /api/v1/share/{token}/forms`) does. This file follows the more\n// specific, SDK-verified statement — a LIVE `share.forms.list()` call,\n// refreshable on demand — the same \"own live-fetch sub-state, not a\n// `snapshot.updated` subscription\" pattern `useRooming.ts`'s\n// `unassignedPool` already uses for an analogous non-delta read. Flagged\n// for the doc owner to resolve; not silently picked one way with no trace.\n//\n// ── `clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY ──────────────────────\n//\n// `ShareSaveFormRequest`/`ShareSubmitFormRequest` (verified,\n// `kaafil-js/openapi/openapi.json`) require a body-level `clientToken`\n// (16-255 chars) — the SDK's own comment on `share.ts` calls it \"this\n// route's own de-dupe key, distinct from the automatic `Idempotency-Key`\n// header\", and `modules/forms/FRD.md §4.1/§8` calls it \"a fixed\n// `clientToken` generated once at enqueue and reused on every retry\" —\n// i.e. exactly the write-once, replay-unchanged lifecycle hard rule #4\n// reserves for `OfflineEngine.enqueue()`'s own internal idempotency-key\n// minting. This hook does NOT invent a value for it (that would be exactly\n// the \"hook mints its own idempotency key\" anti-pattern hard rule #4\n// forbids, even though `clientToken` is a different field from the\n// transport `Idempotency-Key` header `enqueue()` mints separately and\n// automatically for every op, share writes included). Instead\n// `ShareFormWriteInput.clientToken` is a REQUIRED field the CALLER\n// supplies — a future `ShareFormsSection`/`ShareFormFillScreen` generates\n// one value per form-fill session (e.g. once, the first time a field is\n// touched) and passes the SAME value to every `save()`/`submit()` call for\n// that session, which is what makes retries of the SAME logical write\n// dedupe wire-side. This hook only ever forwards it into the body verbatim.\n//\n// ── REVOCATION DROPS QUEUED WRITES — A REAL SDK GAP, WORKED AROUND HERE ────\n//\n// `02-forms.md`'s `Offline` field: \"Revocation is different and has no\n// grace: a `REVOKED` token drops queued writes unsent and purges the local\n// copy immediately.\" Verified against `kaafil-js/src/offline/share.ts`\n// this session: `ShareGuard.invalidate()` treats `'expired'` and\n// `'revoked'` IDENTICALLY — it purges the read snapshot in both cases and\n// only PRESERVES (never drops) the outbox either way; nothing in\n// `kaafil-js` currently distinguishes \"grace-covered\" from \"unsent, drop\n// it\" at the outbox level. This is a genuine SDK/spec disagreement — see\n// this file's final report note. Rather than leave the spec's hard\n// requirement unmet, this hook enforces it locally on `share.revoked`,\n// riding the outbox's own existing `drop()` primitive (the identical call\n// `useConflicts()`'s `'keep-current'` resolution already makes) — this is\n// composing an existing primitive in reaction to an event, not a second\n// sync engine (hard rule #3): no retry ladder, no cache, no idempotency\n// logic is reimplemented here, only \"remove this op from the outbox\".\n\nimport type { KaafilClient, OutboxOp } from 'kaafil-js/client';\nimport { SyncOpMethod } from 'kaafil-js/client';\nimport { useCallback, useEffect, useState } from 'react';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { useOfflineEngine } from './useOfflineEngine';\nimport { useOfflineEvent } from './useOfflineEvent';\nimport { useOfflineMutation } from './useOfflineMutation';\nimport { usePersona } from './usePersona';\nimport { useServerTime } from './useServerTime';\n\ntype ShareFormsClient = Pick<KaafilClient, 'share'>;\n\n/** One row of `GET /api/v1/share/{token}/forms` — `ShareFormsListResponse`\n * (verified, `kaafil-js/openapi/openapi.json`). Metadata only: no prefill,\n * no field list, no answers (`FRD:traveller-share §4.8`) — that detail\n * arrives only from `renderForm()` below. */\nexport interface ShareFormListItem {\n readonly formId: string;\n readonly key: string;\n readonly title: string;\n readonly phase: string;\n readonly required: boolean;\n readonly fillState: string;\n readonly opensAt: string | null;\n readonly closesAt: string | null;\n readonly responseStatus: string | null;\n readonly submittedAt: string | null;\n}\n\n/** One answer in a `save`/`submit` body — `ShareSaveFormRequest.answers[]`\n * / `ShareSubmitFormRequest.answers[]` (verified). */\nexport interface ShareFormAnswer {\n readonly fieldKey: string;\n readonly value: unknown;\n}\n\nexport interface ShareFormWriteInput {\n readonly formId: string;\n readonly answers: readonly ShareFormAnswer[];\n /** This route's own de-dupe key — REQUIRED by the wire schema. See this\n * file's header (\"`clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY\") —\n * the caller mints this once per form-fill session and reuses it across\n * every save/submit/retry of that session; this hook never generates it. */\n readonly clientToken: string;\n /** Group fill within the same `TripTraveller.partyId` — omit when the\n * token itself already scopes to a single traveller. */\n readonly travellerRef?: string;\n readonly startNew?: boolean;\n readonly isAnonymous?: boolean;\n}\n\nexport type ShareFormsListStatus = 'loading' | 'list-empty' | 'list-populated' | 'not-found';\n\n/**\n * The write-side status a `save()`/`submit()` call's tracked `OutboxOp`\n * resolves to. Deliberately excludes `'filling'` (`02-forms.md`'s own\n * `States` list also names it): `'filling'` is \"the traveller is editing\n * fields, no request in flight yet\" — a pure local UI mode with no\n * server/outbox signal behind it, so it belongs to the composite that owns\n * the form's field state, never to this hook, which only ever reflects\n * something async.\n */\nexport type ShareFormsWriteStatus =\n | 'idle'\n | 'saving'\n | 'submitting'\n | 'submitted'\n | 'conflicted'\n | 'subject-required'\n | 'queued-offline'\n | 'dropped-on-revoke';\n\nexport interface ShareFormsResult {\n readonly status: ShareFormsListStatus;\n /** `[]` in both the `'loading'` and the `'list-empty'` states — a\n * consumer distinguishes them via `status`, never via array emptiness\n * alone (`02-forms.md`'s `Gating`: an empty list is a NORMAL steady\n * state, never an error, never a placeholder). */\n readonly forms: readonly ShareFormListItem[];\n readonly serverTime: string | undefined;\n /** Re-runs `share.forms.list()`. A composite calls this after a\n * `submit()` lands, since submitting can change another form's\n * `fillState` (e.g. an any-phase trigger opening a follow-up form). */\n readonly refresh: () => Promise<void>;\n /** `GET /api/v1/share/{token}/forms/{formId}` — the fully-resolved,\n * ready-to-paint form (definition merged with any in-progress answers).\n * An open shape (`ShareRenderResponse` has no closed schema yet,\n * verified) — a live call, not cached, matching `save`/`submit`'s own\n * un-cached write path. */\n readonly renderForm: (\n formId: string,\n options?: { readonly travellerRef?: string },\n ) => Promise<Record<string, unknown>>;\n /** An in-progress save — no required-field validation server-side. */\n readonly save: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** Finalizes the response — full validation, one transaction. */\n readonly submit: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** The combined save/submit write status — see `ShareFormsWriteStatus`. */\n readonly writeStatus: ShareFormsWriteStatus;\n /** The most recently tracked save/submit `OutboxOp`, or `undefined`\n * before either has ever been called. */\n readonly writeOp: OutboxOp | undefined;\n}\n\nfunction deriveListStatus(\n forms: readonly ShareFormListItem[] | undefined,\n listFailed: boolean,\n): ShareFormsListStatus {\n if (forms === undefined) {\n return listFailed ? 'not-found' : 'loading';\n }\n return forms.length === 0 ? 'list-empty' : 'list-populated';\n}\n\nfunction deriveWriteStatus(\n revoked: boolean,\n saveOp: OutboxOp | undefined,\n submitOp: OutboxOp | undefined,\n isOnline: boolean,\n): { readonly status: ShareFormsWriteStatus; readonly op: OutboxOp | undefined } {\n if (revoked) {\n return { status: 'dropped-on-revoke', op: submitOp ?? saveOp };\n }\n // `submit` always takes precedence when both are in play — finishing a\n // submission is the more advanced, more important-to-surface state of\n // the two for the same form.\n const active = submitOp !== undefined ? { op: submitOp, kind: 'submit' as const } : undefined;\n const fallback =\n active ?? (saveOp !== undefined ? { op: saveOp, kind: 'save' as const } : undefined);\n if (fallback === undefined) {\n return { status: 'idle', op: undefined };\n }\n const { op, kind } = fallback;\n if (op.status === 'parked') {\n if (op.parkReason?.code === 'CONFLICT_VERSION') {\n return { status: 'conflicted', op };\n }\n if (op.parkReason?.code === 'SUBJECT_REQUIRED') {\n return { status: 'subject-required', op };\n }\n // Any other park reason (e.g. `ShareWriteGraceExpired` —\n // `kaafil-js/src/offline/share.ts`, a grace window that closed before\n // this op ever synced) reports the same as a still-queued offline\n // write: the honest \"still catching up\" framing until it is provably\n // gone (`02-forms.md`'s `Offline` field). A distinct terminal wording\n // for that specific park reason is `ShareFormsSection`'s own\n // presentation concern, not this hook's.\n return { status: 'queued-offline', op };\n }\n if (op.status === 'done') {\n return { status: kind === 'submit' ? 'submitted' : 'idle', op };\n }\n // pending or inflight.\n if (!isOnline) {\n return { status: 'queued-offline', op };\n }\n return { status: kind === 'submit' ? 'submitting' : 'saving', op };\n}\n\nexport function useShareForms(token: string): ShareFormsResult {\n const client = useKaafilClient() as ShareFormsClient;\n const persona = usePersona();\n const engine = useOfflineEngine();\n const { now, sinceLastSync } = useServerTime();\n // `now()` throws before this session's first `snapshot.updated` event —\n // see `useServerTime.ts`'s own \"CALLERS: guard the call site\" note. This\n // hook's writes stay legal without a stamp (see the `enqueuedAt` call\n // sites below), so the guard is \"can I use it\", never \"must I have it\".\n const serverTimeKnown = sinceLastSync !== undefined;\n\n const [forms, setForms] = useState<readonly ShareFormListItem[] | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [listFailed, setListFailed] = useState(false);\n const [revoked, setRevoked] = useState(false);\n const [isOnline, setIsOnline] = useState(\n () => typeof navigator === 'undefined' || navigator.onLine,\n );\n\n const fetchList = useCallback(async (): Promise<void> => {\n try {\n const response = await client.share.forms.list({ token });\n setForms(response.items);\n setServerTime(response.meta.serverTime);\n setListFailed(false);\n } catch {\n // `GET .../forms` only ever fails `RESOURCE_NOT_FOUND` (`FRD:\n // traveller-share §4.1`'s route table, verified) — the share 404\n // carve-out means dark/cross-scope/genuinely-gone collapse to one\n // shape, same reasoning `useShareSnapshot()` applies to its own read.\n setListFailed(true);\n }\n }, [client, token]);\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareForms() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (families/traveller/02-forms.md, `ShareFormsSection`).',\n );\n }\n return;\n }\n void fetchList();\n }, [persona, fetchList]);\n\n // A browser-level connectivity signal, same source `useOutboxStatus()`\n // reads (`kaafil-js`'s `OfflineEngine` exposes no queryable connectivity\n // flag of its own — see that hook's own header, verified again here).\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n const handleOnline = (): void => setIsOnline(true);\n const handleOffline = (): void => setIsOnline(false);\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n useOfflineEvent('share.revoked', () => {\n setRevoked(true);\n // See this file's header (\"REVOCATION DROPS QUEUED WRITES\"). Only this\n // token's own lane, and only ops still in play (a `'done'` op already\n // landed and has nothing left to drop).\n for (const op of engine.outbox.all()) {\n if (op.tripRef === token && (op.status === 'pending' || op.status === 'parked')) {\n void engine.outbox.drop(op.id);\n }\n }\n });\n\n const saveMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/save${query}`,\n // Hardcoded literal, not imported: `OperationId` is not exported\n // from `kaafil-js/client`'s public surface (same reasoning\n // `useRooming.ts` documents). Value verified against\n // `kaafil-js/src/generated/security.ts` this session:\n // `OperationId.ShareSaveForm === 'shareSaveForm'`.\n operationId: 'shareSaveForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // The server-anchored \"now\" this write was created at — never\n // `Date.now()`/`new Date()` (hard rule #6) — `EnqueueWriteOptions\n // .enqueuedAt`'s own doc comment: \"client-attested and only ever\n // used by the server to REFUSE\" (the 7-day post-expiry grace\n // check). Optional on the wire, and genuinely omitted here on a\n // cold session: `useServerTime().now()` throws before this\n // session's very first `snapshot.updated` event has landed (its\n // own documented behaviour, guarded at the call site exactly as\n // its header instructs) — a share write attempted in that narrow\n // window still enqueues durably, just without a client-attested\n // stamp for the server's grace check to read.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const submitMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/submit${query}`,\n operationId: 'shareSubmitForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // See the identical `save` enqueue above for why this is guarded.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const renderForm = useCallback(\n async (\n formId: string,\n options?: { readonly travellerRef?: string },\n ): Promise<Record<string, unknown>> => {\n const response = await client.share.forms.render({\n token,\n formId,\n ...(options?.travellerRef !== undefined ? { travellerRef: options.travellerRef } : {}),\n });\n return response;\n },\n [client, token],\n );\n\n const { status: writeStatus, op: writeOp } = deriveWriteStatus(\n revoked,\n saveMutation.op,\n submitMutation.op,\n isOnline,\n );\n\n return {\n status: deriveListStatus(forms, listFailed),\n forms: forms ?? [],\n serverTime,\n refresh: fetchList,\n renderForm,\n save: saveMutation.mutate,\n submit: submitMutation.mutate,\n writeStatus,\n writeOp,\n };\n}\n","'use client';\n\n// `08-core-hooks.md §3.1`. The single assembled read behind the whole\n// traveller share surface. Wraps `share.snapshot()` (verified `shareAuth`\n// -only in `security.ts` — no `apiKeyAuth` alternative exists for any\n// `share.*` operation), read cache-first from the snapshot once\n// `OfflineEngine`'s `share` option is configured.\n//\n// VERIFIED SHAPE (`openapi/openapi.json`'s `ShareSnapshotResponse`, this\n// session): a flat object with one property PER section key, each present\n// only when the link exposes it and data exists — never `null`, never an\n// empty placeholder — plus a `trip` echo (`name`/`startDate`/`endDate`/\n// `timezone`/`phase`) that is NOT one of the thirteen section flags\n// (`09-registries.md §5`). This hook builds `sections` by taking every own\n// key off the response except `trip` and `meta` — additive-safe against a\n// future 14th section key without needing this file to enumerate all\n// thirteen by name.\n//\n// `trip` and the server clock are surfaced as their OWN top-level fields on\n// `ShareSnapshotResult`, never folded into `sections`: every consumer of\n// `sections` (starting with `KaafilShareView`, which walks the map and\n// renders one composite per key) treats \"is this key present\" as \"should I\n// render a section for it\" — letting `trip`/`meta` leak into that map would\n// make the view try to mount a `<ShareTripSection>`/`<ShareMetaSection>`\n// that has never existed. But every traveller composite still needs BOTH:\n// `trip.timezone` is the one zone `DateDisplay`/`TimeDisplay` may legally\n// anchor to on this surface (never the device zone, never a fabricated\n// `'UTC'`), `trip.phase` lets a composite react to PRE_DEPARTURE/ON_TRIP/\n// POST_TRIP, and `meta.serverTime` is the only trusted \"now\" a share\n// composite may read without going through `useServerTime()` (hard rule\n// #6 — no `Date.now()`/`new Date()` in `src/core`). Hence: two extra return\n// fields, kept structurally apart from the section map they must never\n// pollute.\n\nimport type { KaafilClient } from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { usePersona } from './usePersona';\n\nexport interface ShareSnapshotOptions {\n readonly travellerRef?: string;\n}\n\n// Derived from the SDK's own return type rather than hand-copied — this\n// stays correct for free if `kaafil-js` grows another `trip` field, instead\n// of silently drifting the way the old `'UTC'` constants did.\ntype ShareSnapshotResponse = Awaited<ReturnType<KaafilClient['share']['snapshot']>>;\n\n/** The trip echo every snapshot response carries — `name`/`startDate`/\n * `endDate` plus the two fields this hook used to throw away:\n * `timezone` (the IANA zone every traveller-facing render must anchor to)\n * and `phase` (server-computed `PRE_DEPARTURE`/`ON_TRIP`/`POST_TRIP`). */\nexport type ShareSnapshotTrip = ShareSnapshotResponse['trip'];\n\nexport interface ShareSnapshotResult {\n readonly status: 'loading' | 'ready' | 'expired' | 'revoked' | 'not-found';\n readonly sections: ReadonlyMap<string, unknown>;\n /** The snapshot's `trip` echo, or `undefined` before any response has\n * landed (mirrors `sections`' own \"empty until ready\" behaviour). NEVER a\n * section key — see this file's header. */\n readonly trip: ShareSnapshotTrip | undefined;\n /** `meta.serverTime` off the most recently landed snapshot response — the\n * only trusted clock a share composite may read (hard rule #6). Kept\n * separate from `useServerTime()`'s own stream (which advances off\n * `OfflineEngine`'s `snapshot.updated` event, not this hook's own fetch)\n * so a composite that only ever calls `useShareSnapshot()` still has a\n * real server stamp to compare against without also wiring the offline\n * engine's event stream. `undefined` before any response has landed. */\n readonly serverTime: string | undefined;\n}\n\nconst NON_SECTION_KEYS = new Set(['trip', 'meta']);\n\nexport function useShareSnapshot(\n token: string,\n options?: ShareSnapshotOptions,\n): ShareSnapshotResult {\n void options; // `travellerRef` narrows which subject-token rows the server\n // itself returns for a `travellerRef`-scoped share link — this hook has no\n // client-side use for it beyond passing the token through; the server does\n // the scoping (`09-registries.md §5`, `families/traveller/01-share-sections.md`, cited).\n\n const client = useKaafilClient();\n const persona = usePersona();\n const engine = useContext(OfflineEngineContext);\n const [sections, setSections] = useState<ReadonlyMap<string, unknown> | undefined>(undefined);\n const [trip, setTrip] = useState<ShareSnapshotTrip | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [terminal, setTerminal] = useState<'expired' | 'revoked' | 'not-found' | undefined>(\n undefined,\n );\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareSnapshot() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (08-core-hooks.md §3.1).',\n );\n }\n return;\n }\n\n let cancelled = false;\n async function load(): Promise<void> {\n try {\n const response = await client.share.snapshot({ token });\n if (cancelled) {\n return;\n }\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(response)) {\n if (!NON_SECTION_KEYS.has(key)) {\n map.set(key, value);\n }\n }\n setSections(map);\n setTrip(response.trip);\n setServerTime(response.meta.serverTime);\n } catch {\n // `06-capability-and-personas.md §9`: dark, cross-scope, and\n // genuinely-gone all collapse to the identical shape here — this\n // hook cannot and must not try to distinguish them, so any failure\n // to read the snapshot at all reports the same `'not-found'`.\n if (!cancelled) {\n setTerminal('not-found');\n }\n }\n }\n void load();\n\n if (engine === null) {\n return () => {\n cancelled = true;\n };\n }\n const unsubscribeExpired = engine.events.on('share.expired', () => setTerminal('expired'));\n const unsubscribeRevoked = engine.events.on('share.revoked', () => setTerminal('revoked'));\n return () => {\n cancelled = true;\n unsubscribeExpired();\n unsubscribeRevoked();\n };\n }, [client, persona, token, engine]);\n\n if (terminal !== undefined) {\n return { status: terminal, sections: sections ?? new Map(), trip, serverTime };\n }\n if (sections === undefined) {\n return { status: 'loading', sections: new Map(), trip: undefined, serverTime: undefined };\n }\n return { status: 'ready', sections, trip, serverTime };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/hooks/useShareForms.ts","../src/core/hooks/useShareSnapshot.ts"],"names":["useState","useEffect"],"mappings":"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","file":"chunk-3BEQYIFU.js","sourcesContent":["'use client';\n\n// `08-core-hooks.md §3` (new row — `ShareFormsSection`'s `Backed by`,\n// `ui-kit/families/traveller/02-forms.md`, cited): the write-back half of\n// the traveller share surface. `forms` is the ONLY write-back section on\n// the whole share surface; every other section is read-only through\n// `useShareSnapshot()` alone. This hook wraps the four dedicated\n// `shareAuth`-only routes `share.forms.{list,render,save,submit}`\n// (`kaafil-js/src/resources/share.ts`, verified this session).\n//\n// ── WHY THIS HOOK DOES NOT ALSO CALL `useShareSnapshot()` ──────────────────\n//\n// `02-forms.md`'s `Backed by` field says the section's own `/core` hook\n// \"composes both\" — the snapshot (for `forms` section presence) and the\n// dedicated routes (for the actual list/render/save/submit). This file\n// deliberately does NOT reproduce the snapshot fetch: `ShareFormsSection`\n// (future work, not this file) is only ever mounted once its own call to\n// `useShareSnapshot(token)` has already confirmed `sections.has('forms')`\n// (`01-share-sections.md`'s registry-driven mount rule) — duplicating that\n// fetch here would be a second, redundant read of the same snapshot for no\n// new information, and this hook has nothing to add to a presence check it\n// does not own. What this file DOES independently reproduce is\n// `useShareSnapshot()`'s own persona gate and its `share.expired`/\n// `share.revoked` subscriptions (`08-core-hooks.md §0`: \"every domain hook\n// is capability-gated internally\" — the share-surface's equivalent gate is\n// the persona check, applied the same way every other hook in this catalog\n// applies its own).\n//\n// ── SPEC TENSION FLAGGED: \"cache-first\" vs. \"the dedicated routes\" ─────────\n//\n// `02-forms.md`'s `Offline` field says the list \"rides the scoped share\n// snapshot cache-first, same as every other section\", but its own `Backed\n// by` field says in the same breath that \"the actual form list/render/\n// save/submit calls ride the dedicated forms-share routes\" — and\n// `ShareSnapshotResponse`'s per-section shape (verified,\n// `useShareSnapshot.ts`) carries no registered `forms` array with\n// `fillState`/`opensAt`/`closesAt` fields; only `ShareFormsListResponse`\n// (`GET /api/v1/share/{token}/forms`) does. This file follows the more\n// specific, SDK-verified statement — a LIVE `share.forms.list()` call,\n// refreshable on demand — the same \"own live-fetch sub-state, not a\n// `snapshot.updated` subscription\" pattern `useRooming.ts`'s\n// `unassignedPool` already uses for an analogous non-delta read. Flagged\n// for the doc owner to resolve; not silently picked one way with no trace.\n//\n// ── `clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY ──────────────────────\n//\n// `ShareSaveFormRequest`/`ShareSubmitFormRequest` (verified,\n// `kaafil-js/openapi/openapi.json`) require a body-level `clientToken`\n// (16-255 chars) — the SDK's own comment on `share.ts` calls it \"this\n// route's own de-dupe key, distinct from the automatic `Idempotency-Key`\n// header\", and `modules/forms/FRD.md §4.1/§8` calls it \"a fixed\n// `clientToken` generated once at enqueue and reused on every retry\" —\n// i.e. exactly the write-once, replay-unchanged lifecycle hard rule #4\n// reserves for `OfflineEngine.enqueue()`'s own internal idempotency-key\n// minting. This hook does NOT invent a value for it (that would be exactly\n// the \"hook mints its own idempotency key\" anti-pattern hard rule #4\n// forbids, even though `clientToken` is a different field from the\n// transport `Idempotency-Key` header `enqueue()` mints separately and\n// automatically for every op, share writes included). Instead\n// `ShareFormWriteInput.clientToken` is a REQUIRED field the CALLER\n// supplies — a future `ShareFormsSection`/`ShareFormFillScreen` generates\n// one value per form-fill session (e.g. once, the first time a field is\n// touched) and passes the SAME value to every `save()`/`submit()` call for\n// that session, which is what makes retries of the SAME logical write\n// dedupe wire-side. This hook only ever forwards it into the body verbatim.\n//\n// ── REVOCATION DROPS QUEUED WRITES — A REAL SDK GAP, WORKED AROUND HERE ────\n//\n// `02-forms.md`'s `Offline` field: \"Revocation is different and has no\n// grace: a `REVOKED` token drops queued writes unsent and purges the local\n// copy immediately.\" Verified against `kaafil-js/src/offline/share.ts`\n// this session: `ShareGuard.invalidate()` treats `'expired'` and\n// `'revoked'` IDENTICALLY — it purges the read snapshot in both cases and\n// only PRESERVES (never drops) the outbox either way; nothing in\n// `kaafil-js` currently distinguishes \"grace-covered\" from \"unsent, drop\n// it\" at the outbox level. This is a genuine SDK/spec disagreement — see\n// this file's final report note. Rather than leave the spec's hard\n// requirement unmet, this hook enforces it locally on `share.revoked`,\n// riding the outbox's own existing `drop()` primitive (the identical call\n// `useConflicts()`'s `'keep-current'` resolution already makes) — this is\n// composing an existing primitive in reaction to an event, not a second\n// sync engine (hard rule #3): no retry ladder, no cache, no idempotency\n// logic is reimplemented here, only \"remove this op from the outbox\".\n\nimport type { KaafilClient, OutboxOp } from 'kaafil-js/client';\nimport { SyncOpMethod } from 'kaafil-js/client';\nimport { useCallback, useEffect, useState } from 'react';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { useOfflineEngine } from './useOfflineEngine';\nimport { useOfflineEvent } from './useOfflineEvent';\nimport { useOfflineMutation } from './useOfflineMutation';\nimport { usePersona } from './usePersona';\nimport { useServerTime } from './useServerTime';\n\ntype ShareFormsClient = Pick<KaafilClient, 'share'>;\n\n/** One row of `GET /api/v1/share/{token}/forms` — `ShareFormsListResponse`\n * (verified, `kaafil-js/openapi/openapi.json`). Metadata only: no prefill,\n * no field list, no answers (`FRD:traveller-share §4.8`) — that detail\n * arrives only from `renderForm()` below. */\nexport interface ShareFormListItem {\n readonly formId: string;\n readonly key: string;\n readonly title: string;\n readonly phase: string;\n readonly required: boolean;\n readonly fillState: string;\n readonly opensAt: string | null;\n readonly closesAt: string | null;\n readonly responseStatus: string | null;\n readonly submittedAt: string | null;\n}\n\n/** One answer in a `save`/`submit` body — `ShareSaveFormRequest.answers[]`\n * / `ShareSubmitFormRequest.answers[]` (verified). */\nexport interface ShareFormAnswer {\n readonly fieldKey: string;\n readonly value: unknown;\n}\n\nexport interface ShareFormWriteInput {\n readonly formId: string;\n readonly answers: readonly ShareFormAnswer[];\n /** This route's own de-dupe key — REQUIRED by the wire schema. See this\n * file's header (\"`clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY\") —\n * the caller mints this once per form-fill session and reuses it across\n * every save/submit/retry of that session; this hook never generates it. */\n readonly clientToken: string;\n /** Group fill within the same `TripTraveller.partyId` — omit when the\n * token itself already scopes to a single traveller. */\n readonly travellerRef?: string;\n readonly startNew?: boolean;\n readonly isAnonymous?: boolean;\n}\n\nexport type ShareFormsListStatus = 'loading' | 'list-empty' | 'list-populated' | 'not-found';\n\n/**\n * The write-side status a `save()`/`submit()` call's tracked `OutboxOp`\n * resolves to. Deliberately excludes `'filling'` (`02-forms.md`'s own\n * `States` list also names it): `'filling'` is \"the traveller is editing\n * fields, no request in flight yet\" — a pure local UI mode with no\n * server/outbox signal behind it, so it belongs to the composite that owns\n * the form's field state, never to this hook, which only ever reflects\n * something async.\n */\nexport type ShareFormsWriteStatus =\n | 'idle'\n | 'saving'\n | 'submitting'\n | 'submitted'\n | 'conflicted'\n | 'subject-required'\n | 'queued-offline'\n | 'dropped-on-revoke';\n\nexport interface ShareFormsResult {\n readonly status: ShareFormsListStatus;\n /** `[]` in both the `'loading'` and the `'list-empty'` states — a\n * consumer distinguishes them via `status`, never via array emptiness\n * alone (`02-forms.md`'s `Gating`: an empty list is a NORMAL steady\n * state, never an error, never a placeholder). */\n readonly forms: readonly ShareFormListItem[];\n readonly serverTime: string | undefined;\n /** Re-runs `share.forms.list()`. A composite calls this after a\n * `submit()` lands, since submitting can change another form's\n * `fillState` (e.g. an any-phase trigger opening a follow-up form). */\n readonly refresh: () => Promise<void>;\n /** `GET /api/v1/share/{token}/forms/{formId}` — the fully-resolved,\n * ready-to-paint form (definition merged with any in-progress answers).\n * An open shape (`ShareRenderResponse` has no closed schema yet,\n * verified) — a live call, not cached, matching `save`/`submit`'s own\n * un-cached write path. */\n readonly renderForm: (\n formId: string,\n options?: { readonly travellerRef?: string },\n ) => Promise<Record<string, unknown>>;\n /** An in-progress save — no required-field validation server-side. */\n readonly save: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** Finalizes the response — full validation, one transaction. */\n readonly submit: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** The combined save/submit write status — see `ShareFormsWriteStatus`. */\n readonly writeStatus: ShareFormsWriteStatus;\n /** The most recently tracked save/submit `OutboxOp`, or `undefined`\n * before either has ever been called. */\n readonly writeOp: OutboxOp | undefined;\n}\n\nfunction deriveListStatus(\n forms: readonly ShareFormListItem[] | undefined,\n listFailed: boolean,\n): ShareFormsListStatus {\n if (forms === undefined) {\n return listFailed ? 'not-found' : 'loading';\n }\n return forms.length === 0 ? 'list-empty' : 'list-populated';\n}\n\nfunction deriveWriteStatus(\n revoked: boolean,\n saveOp: OutboxOp | undefined,\n submitOp: OutboxOp | undefined,\n isOnline: boolean,\n): { readonly status: ShareFormsWriteStatus; readonly op: OutboxOp | undefined } {\n if (revoked) {\n return { status: 'dropped-on-revoke', op: submitOp ?? saveOp };\n }\n // `submit` always takes precedence when both are in play — finishing a\n // submission is the more advanced, more important-to-surface state of\n // the two for the same form.\n const active = submitOp !== undefined ? { op: submitOp, kind: 'submit' as const } : undefined;\n const fallback =\n active ?? (saveOp !== undefined ? { op: saveOp, kind: 'save' as const } : undefined);\n if (fallback === undefined) {\n return { status: 'idle', op: undefined };\n }\n const { op, kind } = fallback;\n if (op.status === 'parked') {\n if (op.parkReason?.code === 'CONFLICT_VERSION') {\n return { status: 'conflicted', op };\n }\n if (op.parkReason?.code === 'SUBJECT_REQUIRED') {\n return { status: 'subject-required', op };\n }\n // Any other park reason (e.g. `ShareWriteGraceExpired` —\n // `kaafil-js/src/offline/share.ts`, a grace window that closed before\n // this op ever synced) reports the same as a still-queued offline\n // write: the honest \"still catching up\" framing until it is provably\n // gone (`02-forms.md`'s `Offline` field). A distinct terminal wording\n // for that specific park reason is `ShareFormsSection`'s own\n // presentation concern, not this hook's.\n return { status: 'queued-offline', op };\n }\n if (op.status === 'done') {\n return { status: kind === 'submit' ? 'submitted' : 'idle', op };\n }\n // pending or inflight.\n if (!isOnline) {\n return { status: 'queued-offline', op };\n }\n return { status: kind === 'submit' ? 'submitting' : 'saving', op };\n}\n\nexport function useShareForms(token: string): ShareFormsResult {\n const client = useKaafilClient() as ShareFormsClient;\n const persona = usePersona();\n const engine = useOfflineEngine();\n const { now, sinceLastSync } = useServerTime();\n // `now()` throws before this session's first `snapshot.updated` event —\n // see `useServerTime.ts`'s own \"CALLERS: guard the call site\" note. This\n // hook's writes stay legal without a stamp (see the `enqueuedAt` call\n // sites below), so the guard is \"can I use it\", never \"must I have it\".\n const serverTimeKnown = sinceLastSync !== undefined;\n\n const [forms, setForms] = useState<readonly ShareFormListItem[] | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [listFailed, setListFailed] = useState(false);\n const [revoked, setRevoked] = useState(false);\n const [isOnline, setIsOnline] = useState(\n () => typeof navigator === 'undefined' || navigator.onLine,\n );\n\n const fetchList = useCallback(async (): Promise<void> => {\n try {\n const response = await client.share.forms.list({ token });\n setForms(response.items);\n setServerTime(response.meta.serverTime);\n setListFailed(false);\n } catch {\n // `GET .../forms` only ever fails `RESOURCE_NOT_FOUND` (`FRD:\n // traveller-share §4.1`'s route table, verified) — the share 404\n // carve-out means dark/cross-scope/genuinely-gone collapse to one\n // shape, same reasoning `useShareSnapshot()` applies to its own read.\n setListFailed(true);\n }\n }, [client, token]);\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareForms() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (families/traveller/02-forms.md, `ShareFormsSection`).',\n );\n }\n return;\n }\n void fetchList();\n }, [persona, fetchList]);\n\n // A browser-level connectivity signal, same source `useOutboxStatus()`\n // reads (`kaafil-js`'s `OfflineEngine` exposes no queryable connectivity\n // flag of its own — see that hook's own header, verified again here).\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n const handleOnline = (): void => setIsOnline(true);\n const handleOffline = (): void => setIsOnline(false);\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n useOfflineEvent('share.revoked', () => {\n setRevoked(true);\n // See this file's header (\"REVOCATION DROPS QUEUED WRITES\"). Only this\n // token's own lane, and only ops still in play (a `'done'` op already\n // landed and has nothing left to drop).\n for (const op of engine.outbox.all()) {\n if (op.tripRef === token && (op.status === 'pending' || op.status === 'parked')) {\n void engine.outbox.drop(op.id);\n }\n }\n });\n\n const saveMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/save${query}`,\n // Hardcoded literal, not imported: `OperationId` is not exported\n // from `kaafil-js/client`'s public surface (same reasoning\n // `useRooming.ts` documents). Value verified against\n // `kaafil-js/src/generated/security.ts` this session:\n // `OperationId.ShareSaveForm === 'shareSaveForm'`.\n operationId: 'shareSaveForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // The server-anchored \"now\" this write was created at — never\n // `Date.now()`/`new Date()` (hard rule #6) — `EnqueueWriteOptions\n // .enqueuedAt`'s own doc comment: \"client-attested and only ever\n // used by the server to REFUSE\" (the 7-day post-expiry grace\n // check). Optional on the wire, and genuinely omitted here on a\n // cold session: `useServerTime().now()` throws before this\n // session's very first `snapshot.updated` event has landed (its\n // own documented behaviour, guarded at the call site exactly as\n // its header instructs) — a share write attempted in that narrow\n // window still enqueues durably, just without a client-attested\n // stamp for the server's grace check to read.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const submitMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/submit${query}`,\n operationId: 'shareSubmitForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // See the identical `save` enqueue above for why this is guarded.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const renderForm = useCallback(\n async (\n formId: string,\n options?: { readonly travellerRef?: string },\n ): Promise<Record<string, unknown>> => {\n const response = await client.share.forms.render({\n token,\n formId,\n ...(options?.travellerRef !== undefined ? { travellerRef: options.travellerRef } : {}),\n });\n return response;\n },\n [client, token],\n );\n\n const { status: writeStatus, op: writeOp } = deriveWriteStatus(\n revoked,\n saveMutation.op,\n submitMutation.op,\n isOnline,\n );\n\n return {\n status: deriveListStatus(forms, listFailed),\n forms: forms ?? [],\n serverTime,\n refresh: fetchList,\n renderForm,\n save: saveMutation.mutate,\n submit: submitMutation.mutate,\n writeStatus,\n writeOp,\n };\n}\n","'use client';\n\n// `08-core-hooks.md §3.1`. The single assembled read behind the whole\n// traveller share surface. Wraps `share.snapshot()` (verified `shareAuth`\n// -only in `security.ts` — no `apiKeyAuth` alternative exists for any\n// `share.*` operation), read cache-first from the snapshot once\n// `OfflineEngine`'s `share` option is configured.\n//\n// VERIFIED SHAPE (`openapi/openapi.json`'s `ShareSnapshotResponse`, this\n// session): a flat object with one property PER section key, each present\n// only when the link exposes it and data exists — never `null`, never an\n// empty placeholder — plus a `trip` echo (`name`/`startDate`/`endDate`/\n// `timezone`/`phase`) that is NOT one of the thirteen section flags\n// (`09-registries.md §5`). This hook builds `sections` by taking every own\n// key off the response except `trip` and `meta` — additive-safe against a\n// future 14th section key without needing this file to enumerate all\n// thirteen by name.\n//\n// `trip` and the server clock are surfaced as their OWN top-level fields on\n// `ShareSnapshotResult`, never folded into `sections`: every consumer of\n// `sections` (starting with `KaafilShareView`, which walks the map and\n// renders one composite per key) treats \"is this key present\" as \"should I\n// render a section for it\" — letting `trip`/`meta` leak into that map would\n// make the view try to mount a `<ShareTripSection>`/`<ShareMetaSection>`\n// that has never existed. But every traveller composite still needs BOTH:\n// `trip.timezone` is the one zone `DateDisplay`/`TimeDisplay` may legally\n// anchor to on this surface (never the device zone, never a fabricated\n// `'UTC'`), `trip.phase` lets a composite react to PRE_DEPARTURE/ON_TRIP/\n// POST_TRIP, and `meta.serverTime` is the only trusted \"now\" a share\n// composite may read without going through `useServerTime()` (hard rule\n// #6 — no `Date.now()`/`new Date()` in `src/core`). Hence: two extra return\n// fields, kept structurally apart from the section map they must never\n// pollute.\n\nimport type { KaafilClient } from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { usePersona } from './usePersona';\n\nexport interface ShareSnapshotOptions {\n readonly travellerRef?: string;\n}\n\n// Derived from the SDK's own return type rather than hand-copied — this\n// stays correct for free if `kaafil-js` grows another `trip` field, instead\n// of silently drifting the way the old `'UTC'` constants did.\ntype ShareSnapshotResponse = Awaited<ReturnType<KaafilClient['share']['snapshot']>>;\n\n/** The trip echo every snapshot response carries — `name`/`startDate`/\n * `endDate` plus the two fields this hook used to throw away:\n * `timezone` (the IANA zone every traveller-facing render must anchor to)\n * and `phase` (server-computed `PRE_DEPARTURE`/`ON_TRIP`/`POST_TRIP`). */\nexport type ShareSnapshotTrip = ShareSnapshotResponse['trip'];\n\nexport interface ShareSnapshotResult {\n readonly status: 'loading' | 'ready' | 'expired' | 'revoked' | 'not-found';\n readonly sections: ReadonlyMap<string, unknown>;\n /** The snapshot's `trip` echo, or `undefined` before any response has\n * landed (mirrors `sections`' own \"empty until ready\" behaviour). NEVER a\n * section key — see this file's header. */\n readonly trip: ShareSnapshotTrip | undefined;\n /** `meta.serverTime` off the most recently landed snapshot response — the\n * only trusted clock a share composite may read (hard rule #6). Kept\n * separate from `useServerTime()`'s own stream (which advances off\n * `OfflineEngine`'s `snapshot.updated` event, not this hook's own fetch)\n * so a composite that only ever calls `useShareSnapshot()` still has a\n * real server stamp to compare against without also wiring the offline\n * engine's event stream. `undefined` before any response has landed. */\n readonly serverTime: string | undefined;\n}\n\nconst NON_SECTION_KEYS = new Set(['trip', 'meta']);\n\nexport function useShareSnapshot(\n token: string,\n options?: ShareSnapshotOptions,\n): ShareSnapshotResult {\n void options; // `travellerRef` narrows which subject-token rows the server\n // itself returns for a `travellerRef`-scoped share link — this hook has no\n // client-side use for it beyond passing the token through; the server does\n // the scoping (`09-registries.md §5`, `families/traveller/01-share-sections.md`, cited).\n\n const client = useKaafilClient();\n const persona = usePersona();\n const engine = useContext(OfflineEngineContext);\n const [sections, setSections] = useState<ReadonlyMap<string, unknown> | undefined>(undefined);\n const [trip, setTrip] = useState<ShareSnapshotTrip | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [terminal, setTerminal] = useState<'expired' | 'revoked' | 'not-found' | undefined>(\n undefined,\n );\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareSnapshot() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (08-core-hooks.md §3.1).',\n );\n }\n return;\n }\n\n let cancelled = false;\n async function load(): Promise<void> {\n try {\n const response = await client.share.snapshot({ token });\n if (cancelled) {\n return;\n }\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(response)) {\n if (!NON_SECTION_KEYS.has(key)) {\n map.set(key, value);\n }\n }\n setSections(map);\n setTrip(response.trip);\n setServerTime(response.meta.serverTime);\n } catch {\n // `06-capability-and-personas.md §9`: dark, cross-scope, and\n // genuinely-gone all collapse to the identical shape here — this\n // hook cannot and must not try to distinguish them, so any failure\n // to read the snapshot at all reports the same `'not-found'`.\n if (!cancelled) {\n setTerminal('not-found');\n }\n }\n }\n void load();\n\n if (engine === null) {\n return () => {\n cancelled = true;\n };\n }\n const unsubscribeExpired = engine.events.on('share.expired', () => setTerminal('expired'));\n const unsubscribeRevoked = engine.events.on('share.revoked', () => setTerminal('revoked'));\n return () => {\n cancelled = true;\n unsubscribeExpired();\n unsubscribeRevoked();\n };\n }, [client, persona, token, engine]);\n\n if (terminal !== undefined) {\n return { status: terminal, sections: sections ?? new Map(), trip, serverTime };\n }\n if (sections === undefined) {\n return { status: 'loading', sections: new Map(), trip: undefined, serverTime: undefined };\n }\n return { status: 'ready', sections, trip, serverTime };\n}\n"]}
|