kaafil-react-uikit 0.1.0-beta.1

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/README.md +187 -0
  4. package/dist/OfflineFallbackScreen-B5dfaJBN.d.ts +37 -0
  5. package/dist/OfflineFallbackScreen-Ctm7nBa-.d.cts +37 -0
  6. package/dist/SessionExpiredScreen-CytQNuak.d.cts +50 -0
  7. package/dist/SessionExpiredScreen-D8GHyZc3.d.ts +50 -0
  8. package/dist/admin/index.cjs +23779 -0
  9. package/dist/admin/index.cjs.map +1 -0
  10. package/dist/admin/index.d.cts +1157 -0
  11. package/dist/admin/index.d.ts +1157 -0
  12. package/dist/admin/index.js +23724 -0
  13. package/dist/admin/index.js.map +1 -0
  14. package/dist/chunk-6FPAGGFL.js +6028 -0
  15. package/dist/chunk-6FPAGGFL.js.map +1 -0
  16. package/dist/chunk-ACVU55PZ.cjs +294 -0
  17. package/dist/chunk-ACVU55PZ.cjs.map +1 -0
  18. package/dist/chunk-EM7JV3D7.cjs +6051 -0
  19. package/dist/chunk-EM7JV3D7.cjs.map +1 -0
  20. package/dist/chunk-IIPQ4JNC.js +287 -0
  21. package/dist/chunk-IIPQ4JNC.js.map +1 -0
  22. package/dist/chunk-LL243STJ.cjs +2195 -0
  23. package/dist/chunk-LL243STJ.cjs.map +1 -0
  24. package/dist/chunk-PJRZCOFF.cjs +958 -0
  25. package/dist/chunk-PJRZCOFF.cjs.map +1 -0
  26. package/dist/chunk-PTQ2CJKE.cjs +40 -0
  27. package/dist/chunk-PTQ2CJKE.cjs.map +1 -0
  28. package/dist/chunk-PZKXLQSU.js +10 -0
  29. package/dist/chunk-PZKXLQSU.js.map +1 -0
  30. package/dist/chunk-QHUDOVKW.cjs +111 -0
  31. package/dist/chunk-QHUDOVKW.cjs.map +1 -0
  32. package/dist/chunk-TOBZFADB.cjs +12 -0
  33. package/dist/chunk-TOBZFADB.cjs.map +1 -0
  34. package/dist/chunk-UUGMNP27.js +936 -0
  35. package/dist/chunk-UUGMNP27.js.map +1 -0
  36. package/dist/chunk-UYJ6LIWP.js +2154 -0
  37. package/dist/chunk-UYJ6LIWP.js.map +1 -0
  38. package/dist/chunk-VHTSTUCJ.cjs +346 -0
  39. package/dist/chunk-VHTSTUCJ.cjs.map +1 -0
  40. package/dist/chunk-WUAFL77X.js +340 -0
  41. package/dist/chunk-WUAFL77X.js.map +1 -0
  42. package/dist/chunk-XI7MK4Y7.js +38 -0
  43. package/dist/chunk-XI7MK4Y7.js.map +1 -0
  44. package/dist/chunk-YDGR7JSY.js +107 -0
  45. package/dist/chunk-YDGR7JSY.js.map +1 -0
  46. package/dist/core/index.cjs +1254 -0
  47. package/dist/core/index.cjs.map +1 -0
  48. package/dist/core/index.d.cts +1267 -0
  49. package/dist/core/index.d.ts +1267 -0
  50. package/dist/core/index.js +995 -0
  51. package/dist/core/index.js.map +1 -0
  52. package/dist/index.css +1 -0
  53. package/dist/manager/index.cjs +13562 -0
  54. package/dist/manager/index.cjs.map +1 -0
  55. package/dist/manager/index.d.cts +1054 -0
  56. package/dist/manager/index.d.ts +1054 -0
  57. package/dist/manager/index.js +13518 -0
  58. package/dist/manager/index.js.map +1 -0
  59. package/dist/testing/index.cjs +221 -0
  60. package/dist/testing/index.cjs.map +1 -0
  61. package/dist/testing/index.d.cts +269 -0
  62. package/dist/testing/index.d.ts +269 -0
  63. package/dist/testing/index.js +208 -0
  64. package/dist/testing/index.js.map +1 -0
  65. package/dist/tokenStyle-CToG5oOS.d.cts +3 -0
  66. package/dist/tokenStyle-CToG5oOS.d.ts +3 -0
  67. package/dist/traveller/index.cjs +1799 -0
  68. package/dist/traveller/index.cjs.map +1 -0
  69. package/dist/traveller/index.d.cts +58 -0
  70. package/dist/traveller/index.d.ts +58 -0
  71. package/dist/traveller/index.js +1789 -0
  72. package/dist/traveller/index.js.map +1 -0
  73. package/dist/useAgencyTrip-BUYSIaC2.d.cts +454 -0
  74. package/dist/useAgencyTrip-BzyJB1D9.d.ts +454 -0
  75. package/dist/useNotifications-BZW1Z_ur.d.cts +356 -0
  76. package/dist/useNotifications-D262i5BQ.d.ts +356 -0
  77. package/dist/useVendors-DXOuZWhZ.d.cts +784 -0
  78. package/dist/useVendors-DXOuZWhZ.d.ts +784 -0
  79. package/package.json +95 -0
@@ -0,0 +1,784 @@
1
+ import { ChecklistItemDeltaRow, ChecklistsResource, AddChecklistItemOptions, OutboxOp, PatchChecklistItemOptions, DeleteChecklistItemOptions, ToggleChecklistItemOptions, ListChecklistTemplatesOptions, ChecklistTemplatesResource, PullChecklistTemplateOptions, KaafilClient, FilesResource, ExpensesResource, LogExpenseOptions, LinkExpenseReceiptOptions, VoidExpenseOptions, SubmitExpenseClaimOptions, WithdrawExpenseClaimOptions, FloatResource, IssueFloatOptions, ReturnFloatOptions, AdjustFloatOptions, FormsResource, ManagerCreateFormResponseOptions, DispatchFormOptions, ExportFormResponsesOptions, KaafilBinaryResponse, ReadFormConsentReceiptOptions, BoundCreateFormOptions, PatchFormOptions, CloneFormOptions, ReorderFormOptions, CreateFormSectionOptions, PatchFormSectionOptions, DeleteFormSectionOptions, CreateFormFieldOptions, PatchFormFieldOptions, DeleteFormFieldOptions, ItineraryResource, ListItineraryChangeLogOptions, PatchItineraryDayOptions, AddItineraryItemOptions, PatchItineraryItemOptions, DeleteItineraryItemOptions, ReorderItineraryItemOptions, RoomingResource, RoomingRoomDeltaRow, RoomingOccupant, AssignRoomingBedOptions, AutoAssignRoomingOptions, CreateRoomingStayWindowOptions, PatchRoomingStayWindowOptions, DeleteRoomingStayWindowOptions, CreateRoomingRoomOptions, PatchRoomingRoomOptions, DeleteRoomingRoomOptions, VendorsResource, CreateVendorOptions, UpsertVendorOptions, DeleteVendorOptions } from 'kaafil-js/client';
2
+
3
+ /** The four handling paths a `/core` hook ever takes for a classified error. */
4
+ type ErrorHandlingPath = 'retryable' | 'parked' | 'surface-to-user' | 're-auth';
5
+ /**
6
+ * Exactly the 24 codes `architecture/10-conventions.md §7` lists, each
7
+ * mapped to exactly one `ErrorHandlingPath`:
8
+ *
9
+ * - `re-auth` (1 code): `UNAUTHENTICATED` — the SDK's own credential
10
+ * resolver already attempted one silent refresh before this ever reaches
11
+ * `/core` (`10 §7`'s "*SDK attempts one token refresh first"); a hook
12
+ * seeing this classification is seeing a refresh that already failed, so
13
+ * the one correct action left is surfacing `session.expired`.
14
+ * - `retryable` (3 codes): the three the offline table (`06 §7`) marks
15
+ * TRANSIENT — `RATE_LIMITED`, `INTERNAL_ERROR`, `TENANT_SCOPE_MISSING`.
16
+ * - `parked` (3 codes): the closed three-code subset `07-offline-and-sync.md
17
+ * §10` names explicitly as "never retried" — `PLAN_FEATURE_DISABLED`,
18
+ * `FILE_PURGED`, `LOCKED`. (`UnsatisfiableSchemeError` — not a wire code,
19
+ * thrown locally by `kaafil-js`'s credential resolver before any request —
20
+ * is the fourth member of that same closed set; `./classify.ts` handles it
21
+ * by `instanceof`, not through this table, because it never carries a
22
+ * `KaafilErrorCode`.)
23
+ * - `surface-to-user` (the remaining 17 codes): every code that is neither
24
+ * retryable, parked, nor a re-auth signal — a human needs to see it and,
25
+ * in most cases, act on it (fix a validation error, resolve a conflict via
26
+ * `useConflicts()`, request a fresh share link). `CONFLICT_VERSION` and
27
+ * `SECRET_NOT_REPLAYABLE` land here too: both ARE surfaced to a human, just
28
+ * through the one shared `ConflictResolver` shape (`07-offline-and-sync.md
29
+ * §6`) rather than a generic toast — that richer shape is `useConflicts()`'s
30
+ * job, not this classifier's.
31
+ */
32
+ declare const ERROR_HANDLING_PATH: Readonly<Record<string, ErrorHandlingPath>>;
33
+
34
+ /** The normalised shape every `/core` hook's `'error'` branch carries. */
35
+ interface ErrorClassification {
36
+ readonly path: ErrorHandlingPath;
37
+ /** The catalog code, when the failure reached a real response
38
+ * (`kind: 'api'`). `undefined` for a transport failure (network, timeout,
39
+ * abort) or for `UnsatisfiableSchemeError`, neither of which ever carries
40
+ * one (`kaafil-js/src/http/errors.ts`, verified: `KaafilTransportError`
41
+ * fixes `code: undefined`). */
42
+ readonly code: string | undefined;
43
+ readonly status: number | undefined;
44
+ /** `true` when the SDK's own credential resolver refused the call locally,
45
+ * before any request was built — `UnsatisfiableSchemeError`
46
+ * (`kaafil-js/src/auth/credentials.ts`, verified). A hook surfacing this
47
+ * to a composite renders it as a structural "this persona cannot do that",
48
+ * never a transient failure worth retrying. */
49
+ readonly unsatisfiableScheme: boolean;
50
+ /** The original error, for a caller that needs more than the
51
+ * classification (support escalation, a `details` field a specific hook
52
+ * knows how to read). */
53
+ readonly cause: unknown;
54
+ }
55
+ /**
56
+ * Turns any caught value into the shape every `/core` hook's error branch
57
+ * needs. Never throws — an unrecognised shape (a bare `Error`, a rejected
58
+ * promise from something outside the SDK) still classifies, conservatively,
59
+ * as `surface-to-user` with no code and no status, rather than propagating
60
+ * an uncaught exception up through a hook that promised a `{ status:
61
+ * 'error' }` branch instead.
62
+ */
63
+ declare function classifyError(error: unknown): ErrorClassification;
64
+
65
+ /**
66
+ * Which of the three gates failed, in the priority order the three
67
+ * darkness treatments are picked in (`06-capability-and-personas.md §2`).
68
+ * `'mode'` wins over `'data'` wins over `'flag'` is NOT the rule here — the
69
+ * wire's own gate order is flag-before-data (`06 §1`, "why 402 wins"), but
70
+ * `DarkReason` names which single boolean a *component* should read to pick
71
+ * its treatment, and mode-dark is checked first because it is the one
72
+ * treatment that renders nothing at all (`06 §2`'s table, row 1).
73
+ */
74
+ type DarkReason = 'mode' | 'data' | 'flag';
75
+ /**
76
+ * The wire shape of one row of `journey.capabilities()`
77
+ * (`06-capability-and-personas.md §6`, verified against
78
+ * `kaafil-js/src/resources/journey.ts`'s `JourneyCapabilitiesResponse`).
79
+ * `capability` is an open string set (§6 point 5) — never exhaustively
80
+ * switched on.
81
+ */
82
+ interface CapabilityRow {
83
+ readonly capability: string;
84
+ readonly modeOk: boolean;
85
+ readonly dataOk: boolean;
86
+ readonly flagOk: boolean;
87
+ readonly enabled: boolean;
88
+ }
89
+ /**
90
+ * `useCapability()`'s per-key return shape (`08-core-hooks.md §1.4`).
91
+ * `status` is derived client-side from the triple, once, here — never
92
+ * re-derived by a composite (§6 point 2).
93
+ */
94
+ interface CapabilityTriple {
95
+ readonly enabled: boolean;
96
+ readonly modeOk: boolean;
97
+ readonly dataOk: boolean;
98
+ readonly flagOk: boolean;
99
+ readonly status: 'lit' | 'mode-dark' | 'data-dark' | 'flag-dark';
100
+ }
101
+
102
+ /**
103
+ * `TReady` is merged into the `'ready'` branch (never nested under a `data`
104
+ * key) so a composite destructures `{ status, rows, mutate }` directly once
105
+ * it has narrowed on `status === 'ready'`, with no extra unwrap.
106
+ */
107
+ type DomainHookResult<TReady extends object> = {
108
+ readonly status: 'loading';
109
+ } | {
110
+ readonly status: 'dark';
111
+ readonly reason: DarkReason;
112
+ } | ({
113
+ readonly status: 'ready';
114
+ } & TReady) | {
115
+ readonly status: 'error';
116
+ readonly error: ErrorClassification;
117
+ };
118
+
119
+ /** `Awaited<ReturnType<...>>` off the resource methods, never imported by
120
+ * name — `ChecklistAggregateResponse`/`ChecklistItemResponse`/
121
+ * `ToggleResponse`/`TemplatesListResponse`/`DeleteItemResponse` are not
122
+ * individually re-exported from `kaafil-js/client`'s public surface
123
+ * (verified — only `ChecklistItemDeltaRow` and the `*Options` input types
124
+ * are), the identical gap `usePickups.ts`'s own header documents for its
125
+ * resource's response shapes. */
126
+ type ChecklistReadResult = Awaited<ReturnType<ChecklistsResource['read']>>;
127
+ type ChecklistTemplatesListResult = Awaited<ReturnType<ChecklistTemplatesResource['list']>>;
128
+ /** One row of `ChecklistReadResult['sections']` — always the full set,
129
+ * never narrowed by `?since=` (this file's header). */
130
+ type ChecklistSectionRow = ChecklistReadResult['sections'][number];
131
+ /** One row of `ChecklistReadResult['availableTemplates']` — the agency's
132
+ * templates this trip hasn't pulled yet, as returned inline on the
133
+ * aggregate (a smaller shape than `listTemplates()`'s own
134
+ * `TemplatesListResponse`, which additionally carries `itemCount`/
135
+ * `lastUsedAt`). */
136
+ type ChecklistAvailableTemplateRow = ChecklistReadResult['availableTemplates'][number];
137
+ type AddChecklistItemInput = Omit<AddChecklistItemOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
138
+ type PatchChecklistItemInput = Omit<PatchChecklistItemOptions, 'tripRef' | 'signal'>;
139
+ type RemoveChecklistItemInput = Omit<DeleteChecklistItemOptions, 'tripRef' | 'signal'>;
140
+ type ToggleChecklistItemInput = Omit<ToggleChecklistItemOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
141
+ type ListChecklistTemplatesInput = Omit<ListChecklistTemplatesOptions, 'tripRef' | 'signal'>;
142
+ type PullChecklistTemplateInput = Omit<PullChecklistTemplateOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
143
+ type BoundAgencyChecklistTemplates = KaafilClient['checklists']['agencyTemplates'];
144
+ type ListAgencyChecklistTemplatesInput = Parameters<BoundAgencyChecklistTemplates['list']>[0];
145
+ type CreateAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['create']>[0];
146
+ type PatchAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['patch']>[0];
147
+ type RemoveAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['remove']>[0];
148
+ type PublishAgencyChecklistTemplateInput = Parameters<BoundAgencyChecklistTemplates['publish']>[0];
149
+ /**
150
+ * The live-fetched remainder of `ChecklistAggregateResponse` — everything
151
+ * that isn't `items[]` (this file's header explains the split). Same
152
+ * `loaded`/`lastFetchFailed`/`fetchedAt`/`refresh` contract
153
+ * `RoomingUnassignedPool`/`FloatManagerSummaryState` already establish.
154
+ */
155
+ interface ChecklistAggregateState {
156
+ readonly title: string | undefined;
157
+ readonly subtitle: string | undefined;
158
+ readonly progress: {
159
+ readonly total: number;
160
+ readonly complete: number;
161
+ } | undefined;
162
+ readonly hasOpenMandatoryByPhase: {
163
+ readonly PRE_DEPARTURE: boolean;
164
+ readonly IN_TRIP: boolean;
165
+ readonly POST_TRIP: boolean;
166
+ } | undefined;
167
+ readonly sections: readonly ChecklistSectionRow[];
168
+ /** The aggregate's OWN `items[]`, in the identical live-row-or-tombstone
169
+ * union the snapshot list holds. Kept because a desk console mounts no
170
+ * `OfflineShell`, so the snapshot list is permanently empty there and
171
+ * this fetch is the only place a desk's rows come from — see
172
+ * `ChecklistsReady.items`. */
173
+ readonly items: readonly ChecklistItemDeltaRow[];
174
+ readonly availableTemplates: readonly ChecklistAvailableTemplateRow[];
175
+ /** `true` once at least one `client.checklists.read()` call has settled
176
+ * (success or failure) — distinguishes "never fetched yet" from "fetched
177
+ * and genuinely empty." */
178
+ readonly loaded: boolean;
179
+ /** `true` when the MOST RECENT fetch threw. */
180
+ readonly lastFetchFailed: boolean;
181
+ readonly fetchedAt: string | undefined;
182
+ readonly refresh: () => Promise<void>;
183
+ }
184
+ interface ChecklistsReady {
185
+ /** Cache-first — `SnapshotStore`'s `'checklist'` list. */
186
+ readonly items: readonly ChecklistItemDeltaRow[];
187
+ readonly syncedAt: string | undefined;
188
+ readonly aggregate: ChecklistAggregateState;
189
+ /** `null` rather than an `OutboxOp` when the write took the DESK lane —
190
+ * a direct call has no outbox row to hand back. A field surface with an
191
+ * `OfflineShell` still gets its op. */
192
+ readonly addItem: (input: AddChecklistItemInput) => Promise<OutboxOp | null>;
193
+ readonly patchItem: (input: PatchChecklistItemInput) => Promise<OutboxOp | null>;
194
+ readonly removeItem: (input: RemoveChecklistItemInput) => Promise<OutboxOp | null>;
195
+ readonly toggleItem: (input: ToggleChecklistItemInput) => Promise<OutboxOp | null>;
196
+ /** Live, on-demand — see this file's header for why this is a plain
197
+ * function rather than auto-fetched hook state. */
198
+ readonly listTemplates: (input?: ListChecklistTemplatesInput) => Promise<ChecklistTemplatesListResult>;
199
+ readonly pullTemplate: (input: PullChecklistTemplateInput) => Promise<OutboxOp | null>;
200
+ /** Out of THIS family's own scope (this file's header) — wired for the
201
+ * future admin-family template-authoring composite. */
202
+ readonly listAgencyTemplates: (input: ListAgencyChecklistTemplatesInput) => ReturnType<BoundAgencyChecklistTemplates['list']>;
203
+ readonly createAgencyTemplate: (input: CreateAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['create']>;
204
+ readonly patchAgencyTemplate: (input: PatchAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['patch']>;
205
+ readonly removeAgencyTemplate: (input: RemoveAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['remove']>;
206
+ readonly publishAgencyTemplate: (input: PublishAgencyChecklistTemplateInput) => ReturnType<BoundAgencyChecklistTemplates['publish']>;
207
+ }
208
+ /**
209
+ * PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
210
+ * fetch when the `'checklists'` capability triple isn't `lit`
211
+ * (`06-capability-and-personas.md §3`).
212
+ */
213
+ declare function useChecklists(tripRef: string): DomainHookResult<ChecklistsReady>;
214
+
215
+ interface FilesContext {
216
+ readonly tripRef: string;
217
+ }
218
+ type RequestOptions = Omit<Parameters<FilesResource['request']>[0], 'tripRef'>;
219
+ type ConfirmOptions = Omit<Parameters<FilesResource['confirm']>[0], 'signal'>;
220
+ type ListOptions = Omit<Parameters<FilesResource['list']>[0], 'tripRef'>;
221
+ type FileMeta = Awaited<ReturnType<FilesResource['meta']>>;
222
+ type FileUrl = Awaited<ReturnType<FilesResource['url']>>;
223
+ type FileListItem = Awaited<ReturnType<FilesResource['list']>>['items'][number];
224
+ type ManagerFileView = Awaited<ReturnType<FilesResource['managerView']>>;
225
+ type FilePurpose = FileMeta['purpose'];
226
+ interface FilesReady {
227
+ /** `POST /api/v1/files` — mints a `pending` row + a presigned `PUT`. */
228
+ readonly request: (input: RequestOptions) => ReturnType<FilesResource['request']>;
229
+ /** `POST /api/v1/files/{id}/confirm` — verifies what landed, flips
230
+ * `pending` → `ready`. `422 UPLOAD_MISMATCH` leaves the file `pending`;
231
+ * the caller re-`PUT`s and re-confirms (`FileAttachmentFlow` does this
232
+ * itself, see its own file). */
233
+ readonly confirm: (input: ConfirmOptions) => ReturnType<FilesResource['confirm']>;
234
+ /** `GET /api/v1/files/{id}` — the metadata skeleton; `200` even once
235
+ * `purged` — the one read that never 404s on a gone file. */
236
+ readonly meta: (fileId: string) => Promise<FileMeta>;
237
+ /** `GET /api/v1/files/{id}/url` — a fresh 5-minute signed GET, minted on
238
+ * every call, never cached across calls. `410 FILE_PURGED` once the
239
+ * bytes are gone. */
240
+ readonly url: (fileId: string) => Promise<FileUrl>;
241
+ /** `GET /api/v1/files?tripRef=…` — every file for this trip, newest
242
+ * first, optionally narrowed by `purpose`/`status`. Needs no `fileId`. */
243
+ readonly list: (query?: ListOptions) => Promise<readonly FileListItem[]>;
244
+ /** `GET /api/v1/files/{id}/manager-view` — `meta()` + `url()` composed
245
+ * server-side into one manager-doc-viewer-shaped response. Never refuses
246
+ * on a non-`ready` file the way `url()` does — `url`/`urlExpiresAt` come
247
+ * back `null` instead. */
248
+ readonly managerView: (fileId: string) => Promise<ManagerFileView>;
249
+ /**
250
+ * The full presign → direct-`PUT` → confirm sequence for one local
251
+ * `File`, run in the foreground. On a `422 UPLOAD_MISMATCH` OR a presign
252
+ * window that lapsed mid-`PUT` (the underlying `PUT`/`confirm` answering
253
+ * a plain network failure once the 15-minute signature has expired) this
254
+ * re-requests a fresh slot and retries silently, up to `maxAttempts` —
255
+ * `07-files.md`'s own "must never surface as an error" contract for the
256
+ * routine case. Bytes never pass through this SDK or the Kaafil API
257
+ * itself; only the presign/confirm calls do.
258
+ */
259
+ readonly upload: (input: {
260
+ readonly file: File;
261
+ readonly purpose: FilePurpose;
262
+ /** REQUIRED for `trip_document`, refused (422) for every other purpose —
263
+ * nothing else names that file. */
264
+ readonly title?: string;
265
+ /** Only for `trip_document`. Omit and the document belongs to the whole
266
+ * trip; name a traveller and only they see it on their own share link.
267
+ * It also decides retention (scoped → `TRAVELLER_PII`). */
268
+ readonly travellerRef?: string;
269
+ readonly maxAttempts?: number;
270
+ readonly signal?: AbortSignal;
271
+ }) => Promise<FileMeta>;
272
+ }
273
+ declare function useFiles(context: FilesContext): FilesReady;
274
+
275
+ /** One row of `ExpensesReady['expenses']` — a live expense (the full
276
+ * `ExpenseResponse` shape) or the tombstone for one voided-and-purged since
277
+ * the last cursor. Structurally identical split to `RoomingRoomDeltaRow`
278
+ * (`useRooming.ts`) — `ExpenseListResponse.items[]` is `anyOf` the two,
279
+ * verified against `kaafil-js/openapi/openapi.json`. */
280
+ type ExpenseDeltaRow = Awaited<ReturnType<ExpensesResource['list']>>['items'][number];
281
+ /** `ExpenseDeltaRow`, narrowed to the live shape — a voided-and-purged row
282
+ * carries no `category`/`paymentMode`/etc. at all. Composites built on this
283
+ * hook (`manager/composite/expenses/*`) filter to this narrowed type before
284
+ * rendering, the same `isLive*` pattern `useRooming.ts`'s consumers use. */
285
+ type ExpenseLiveRow = Exclude<ExpenseDeltaRow, {
286
+ readonly _tombstone: true;
287
+ }>;
288
+ type LogExpenseInput = Omit<LogExpenseOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
289
+ type LinkExpenseReceiptInput = Omit<LinkExpenseReceiptOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
290
+ type VoidExpenseInput = Omit<VoidExpenseOptions, 'tripRef' | 'signal'>;
291
+ type SubmitExpenseClaimInput = Omit<SubmitExpenseClaimOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
292
+ type WithdrawExpenseClaimInput = Omit<WithdrawExpenseClaimOptions, 'tripRef' | 'signal'>;
293
+ type LogInput = LogExpenseInput;
294
+ type LinkReceiptInput = LinkExpenseReceiptInput;
295
+ type VoidInput = VoidExpenseInput;
296
+ type SubmitClaimInput = SubmitExpenseClaimInput;
297
+ type WithdrawClaimInput = WithdrawExpenseClaimInput;
298
+ interface ExpensesReady {
299
+ readonly expenses: readonly ExpenseDeltaRow[];
300
+ readonly syncedAt: string | undefined;
301
+ /** Re-reads the desk lane after a write. A NO-OP on a field device, where
302
+ * the outbox drain emits `snapshot.updated` and the cache-first read
303
+ * re-renders itself — a composite need not know which lane it is on. */
304
+ readonly refreshLive: () => Promise<void>;
305
+ /** Live read (`client.expenses.read()`), not cache-first — for a detail
306
+ * screen that wants the freshest single row rather than whatever the last
307
+ * pull cached. The list above stays the cache-first source for every list
308
+ * view. */
309
+ readonly read: (expenseId: string) => ReturnType<ExpensesResource['read']>;
310
+ readonly log: (input: LogInput) => Promise<OutboxOp | null>;
311
+ /** FIELD-ONLY — see the mutation's own note: its route requires a
312
+ * client-minted idempotency key, which hard rule #4 forbids this layer
313
+ * from supplying, so there is no desk lane and a desk must not offer it. */
314
+ readonly linkReceipt: (input: LinkReceiptInput) => Promise<OutboxOp>;
315
+ readonly void: (input: VoidInput) => Promise<OutboxOp | null>;
316
+ readonly submitClaim: (input: SubmitClaimInput) => Promise<OutboxOp>;
317
+ readonly withdrawClaim: (input: WithdrawClaimInput) => Promise<OutboxOp>;
318
+ /**
319
+ * The SERVER's own spend rollup for this trip — `spendTotalMinor` and the
320
+ * per-category breakdown, straight off `expenses.list()`'s envelope.
321
+ *
322
+ * Its own live state rather than a field on the snapshot list, for the
323
+ * reason `useFloat.summary` and `useCollections.eligible` are: these are
324
+ * DERIVED figures with no `id`/`version`, so there is nothing for
325
+ * `SnapshotStore` to key on, and `useSnapshotList`'s `live` contract
326
+ * returns `{rows, syncedAt}` with no slot for an envelope.
327
+ *
328
+ * Exposed because summing the rows in the browser is the wrong answer
329
+ * twice over: it would miss anything outside the page and it would put a
330
+ * money figure's correctness in the client. `deriveOverview.ts` wanted
331
+ * exactly this and said so ("wants a server-side rollup … until it lands
332
+ * this card states the two numbers it can stand behind").
333
+ */
334
+ readonly totals: ExpenseTotalsState;
335
+ }
336
+ /** The server's spend rollup, and whether it could be read at all. */
337
+ interface ExpenseTotalsState {
338
+ /** `undefined` until a read lands — NEVER 0. A money figure the surface
339
+ * could not read must not be drawn as zero. */
340
+ readonly spendTotalMinor: number | undefined;
341
+ readonly categoryTotals: Readonly<Record<string, number>> | undefined;
342
+ readonly loaded: boolean;
343
+ readonly lastFetchFailed: boolean;
344
+ readonly refresh: () => Promise<void>;
345
+ }
346
+ /**
347
+ * PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
348
+ * fetch when the `'expenses'` capability triple isn't `lit`
349
+ * (`06-capability-and-personas.md §3`).
350
+ */
351
+ declare function useExpenses(tripRef: string): DomainHookResult<ExpensesReady>;
352
+
353
+ /** One row of the calling manager's own float ledger — verified against
354
+ * `FloatLedgerResponse.data[number]` (`generated/schema.d.ts`). See this
355
+ * file's header for why no tombstone variant is modelled. */
356
+ type FloatMovementRow = Awaited<ReturnType<FloatResource['readLedger']>>['data'][number];
357
+ /** One row of `FloatSummaryResponse.data[]` — entirely derived, never
358
+ * delta-mergeable. See this file's header. */
359
+ type FloatSummaryRow = Awaited<ReturnType<FloatResource['readSummary']>>['data'][number];
360
+ type IssueFloatInput = Omit<IssueFloatOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
361
+ type ReturnFloatInput = Omit<ReturnFloatOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
362
+ type AdjustFloatInput = Omit<AdjustFloatOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
363
+ /**
364
+ * The live-fetched balance/breakdown state — `readSummary()`'s own rows,
365
+ * refreshed explicitly rather than subscribed to `snapshot.updated`. Carries
366
+ * every manager row the calling credential is entitled to see (a manager
367
+ * session is answered its own row only, per `float.ts`'s own header — this
368
+ * hook does not re-filter that, since it has no independent way to verify
369
+ * which row is "self" beyond what the server already scoped the response
370
+ * to); a composite that needs "my own row" specifically matches on the
371
+ * `managerRef`/`managerId` it was itself handed as a prop.
372
+ */
373
+ interface FloatManagerSummaryState {
374
+ readonly rows: readonly FloatSummaryRow[];
375
+ /** `true` once at least one `client.float.readSummary()` call has settled
376
+ * (success or failure) — distinguishes "never fetched yet" from "fetched
377
+ * and genuinely empty," the same contract `RoomingUnassignedPool.loaded`
378
+ * documents. */
379
+ readonly loaded: boolean;
380
+ /** `true` when the MOST RECENT fetch threw. */
381
+ readonly lastFetchFailed: boolean;
382
+ /** The server time (`meta.serverTime`) of the last successful fetch. */
383
+ readonly fetchedAt: string | undefined;
384
+ readonly refresh: () => Promise<void>;
385
+ }
386
+ interface FloatReady {
387
+ /** The calling manager's own movement history, cache-first — see this
388
+ * file's header for why this is the one cache-first field on this hook. */
389
+ readonly movements: readonly FloatMovementRow[];
390
+ readonly syncedAt: string | undefined;
391
+ readonly summary: FloatManagerSummaryState;
392
+ /**
393
+ * ONE NAMED MANAGER's movement history — `GET .../float/{managerId}/ledger`.
394
+ *
395
+ * Distinct from `movements` above, and the distinction matters on a desk:
396
+ * `movements` is the CALLING MANAGER's own ledger, read from the `'float'`
397
+ * pull section, which is scoped that way. An agency admin has no `Manager`
398
+ * row, so there is no "own ledger" for them to read and no live lane that
399
+ * could sensibly fill one — a desk asks "show me THIS manager's
400
+ * movements", which is what this is for. `summary` names every manager who
401
+ * has ever had a movement on the trip, so a caller always has an id to
402
+ * pass.
403
+ */
404
+ readonly readLedger: (managerId: string) => ReturnType<FloatResource['readLedger']>;
405
+ /** `POST .../float/issue` — `type=ISSUE`, `direction=IN`, never
406
+ * negative-float guarded. */
407
+ readonly issue: (input: IssueFloatInput) => Promise<OutboxOp | null>;
408
+ /** `POST .../float/return` — `type=RETURN`, `direction=OUT`,
409
+ * negative-float guarded. A manager, an API key OR an agency admin may
410
+ * call it: this comment previously claimed `managerAuth`-only and that an
411
+ * admin "gets refused by the server", which is wrong —
412
+ * `float.routes.ts` declares the admin triple on it and its route
413
+ * description calls the parity a deliberate owner decision. An office
414
+ * reconciling a returned cash box is the case it exists for. */
415
+ readonly return: (input: ReturnFloatInput) => Promise<OutboxOp | null>;
416
+ /** `POST .../float/adjust` — `type=ADJUSTMENT`; `direction: 'OUT'` is
417
+ * negative-float guarded identically to `return`, `direction: 'IN'`
418
+ * never is. `note` is required by the wire request type itself. */
419
+ readonly adjust: (input: AdjustFloatInput) => Promise<OutboxOp | null>;
420
+ }
421
+ /**
422
+ * PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
423
+ * fetch when the `'float'` capability triple isn't `lit`
424
+ * (`06-capability-and-personas.md §3`).
425
+ */
426
+ declare function useFloat(tripRef: string): DomainHookResult<FloatReady>;
427
+
428
+ /** One row of `GET /api/v1/trips/{ref}/forms` — `TripFormsListResponse.items[]`
429
+ * (verified, `kaafil-js/src/generated/schema.d.ts`). Carries `id`/`version`
430
+ * like a delta row, but is NOT one — `SYNC_PULL_SECTIONS` has no `forms`
431
+ * entry (this file's own header, corrected this pass), so this is a live
432
+ * fetch (`forms.trip.list`), never a snapshot-list read. */
433
+ type TripFormRow = Awaited<ReturnType<FormsResource['trip']['list']>>['items'][number];
434
+ /** One row of `GET /api/v1/trips/{ref}/forms/completion` — PER FORM, not
435
+ * per traveller. See `completion()`'s own note. */
436
+ type FormCompletionRow = Awaited<ReturnType<FormsResource['trip']['completion']>>['items'][number];
437
+ type DispatchFormInput = Omit<DispatchFormOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
438
+ type ExportFormResponsesInput = Omit<ExportFormResponsesOptions, 'signal'>;
439
+ type ConsentReceiptInput = Omit<ReadFormConsentReceiptOptions, 'signal'>;
440
+ /** `GET /api/v1/forms/{formId}` — `FormDetailResponse`: sections + fields +
441
+ * resolved options, the definition `FormFillRenderer` renders. A live read
442
+ * (mirrors `useShareForms.renderForm`'s own "live call, not cached" note) —
443
+ * there is no per-form definition slot in the trip snapshot's `'forms'`
444
+ * section, only the summary row list. */
445
+ type FormDefinitionDetail = Awaited<ReturnType<FormsResource['get']>>;
446
+ /** `GET /api/v1/forms/{formId}/responses/{responseId}` —
447
+ * `ResponseDetailResponse`: the full answer set plus staleness flags against
448
+ * the current form version. */
449
+ type FormResponseDetail = Awaited<ReturnType<FormsResource['responses']['get']>>;
450
+ /** One row of `GET /api/v1/trips/{ref}/travellers/{travellerRef}/forms/
451
+ * responses` — this traveller's `FormResponse` rows across every form on the
452
+ * trip (the per-traveller cross-form read `TripFormResponsesResource.list`,
453
+ * scoped to one `formId`, cannot give). Used to resolve "has this filler
454
+ * already got a response for this form, and what's its id/status" without a
455
+ * second per-form list call per row. */
456
+ type TravellerFormResponseRow = Awaited<ReturnType<FormsResource['trip']['travellerResponses']['list']>>['items'][number];
457
+ /** One row of `GET /api/v1/trips/{ref}/forms/{formId}/responses` — EVERY
458
+ * respondent's row for one form (the per-form cross-traveller read
459
+ * `TripFormResponsesResource.list`, the other axis from
460
+ * `travellerResponses` above — same underlying `ResponsesListResponse`
461
+ * shape, verified identical, just a different URL/fixed axis). `managerAuth`
462
+ * is allowed (`generated/security.ts`, verified) — this is what a manager
463
+ * needs to browse "who has answered this form" for a `TRAVELLER`-audience
464
+ * form, never a browsable-all-travellers index of its own (that stays out
465
+ * of scope, `manager/05-forms.md`'s own boundary). */
466
+ type TripFormResponseRow = Awaited<ReturnType<FormsResource['trip']['responses']['list']>>['items'][number];
467
+ /** `POST /api/v1/trips/{ref}/forms/{formId}/responses` body — a manager
468
+ * self-fill (`travellerRef` omitted) or on-behalf capture
469
+ * (`travellerRef` set), `submit: false` for a draft save and `true` for a
470
+ * final submit. No `clientToken` on this route (unlike the share-surface's
471
+ * `save`/`submit`, `useShareForms.ts`'s own header note) — the standard
472
+ * `Idempotency-Key` header `OfflineEngine.enqueue()` mints once per op is
473
+ * this route's only de-dupe key. */
474
+ type SubmitFormResponseInput = Omit<ManagerCreateFormResponseOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
475
+ /** `GET /api/v1/agencies/{ref}/forms` row — `FormListResponse.items[]`, the
476
+ * agency's whole catalog (every locale/status/scope), `FormsCatalogScreen`'s
477
+ * own read. Structurally identical to `FormDefinitionResponse`; named
478
+ * separately here because it is reached through a different call
479
+ * (`forms.list`, not `forms.get`). */
480
+ type FormAgencyRow = Awaited<ReturnType<FormsResource['list']>>['items'][number];
481
+ /** The patch-transition family's own return shape — `forms.patch`/
482
+ * `.publish`/`.close`/`.reopen`/`.archive`/`.unarchive`/`.clone` all answer
483
+ * `FormDefinitionResponse`, the metadata-only row (no `sections`/`fields`) —
484
+ * distinct from `FormDefinitionDetail`, which only `.get`/`.create` return. */
485
+ type FormDefinitionSummary = Awaited<ReturnType<FormsResource['patch']>>;
486
+ type FormSectionDetail = FormDefinitionDetail['sections'][number];
487
+ type FormFieldDetail = FormDefinitionDetail['fields'][number];
488
+ /** `GET /api/v1/forms/bindings` row — the closed 16-key binding registry
489
+ * `BindingCatalogBrowser` browses. */
490
+ type BindingEntry = Awaited<ReturnType<FormsResource['bindings']['list']>>['items'][number];
491
+ /** `forms.create`'s own body, `agencyRef` dropped (browser-bound, see this
492
+ * file's own header) and the request-plumbing fields excluded — the exact
493
+ * shape `FormBuilder` composes from its metadata/section/field draft. */
494
+ type CreateFormInput = Omit<BoundCreateFormOptions, 'idempotencyKey' | 'signal'>;
495
+ type PatchFormInput = Omit<PatchFormOptions, 'signal'>;
496
+ type CloneFormInput = Omit<CloneFormOptions, 'idempotencyKey' | 'signal'>;
497
+ type ReorderFormInput = Omit<ReorderFormOptions, 'signal'>;
498
+ type CreateFormSectionInput = Omit<CreateFormSectionOptions, 'signal'>;
499
+ type PatchFormSectionInput = Omit<PatchFormSectionOptions, 'signal'>;
500
+ type DeleteFormSectionInput = Omit<DeleteFormSectionOptions, 'signal'>;
501
+ type CreateFormFieldInput = Omit<CreateFormFieldOptions, 'signal'>;
502
+ type PatchFormFieldInput = Omit<PatchFormFieldOptions, 'signal'>;
503
+ type DeleteFormFieldInput = Omit<DeleteFormFieldOptions, 'signal'>;
504
+ interface FormsReady {
505
+ /** This trip's applicable forms, each with its resolved `window` and
506
+ * `dispatched`/`submitted` counts — `TripFormsPanel`'s own read. Live-
507
+ * fetched and explicitly refreshed (this file's header, corrected this
508
+ * pass) — NOT a cache-first snapshot-list read. */
509
+ readonly forms: readonly TripFormRow[];
510
+ /** The last successful live fetch's own `meta.serverTime` — `undefined`
511
+ * before the first fetch settles. */
512
+ readonly syncedAt: string | undefined;
513
+ /** Live read of one form's definition (sections + fields). */
514
+ readonly getDefinition: (formId: string) => Promise<FormDefinitionDetail>;
515
+ /** Live read of one response's full answer set + staleness flags. */
516
+ readonly getResponse: (formId: string, responseId: string) => Promise<FormResponseDetail>;
517
+ /** Live read of one traveller's responses across every form on the trip —
518
+ * how `FormFillFlow` resolves "already submitted" / prefill without a
519
+ * per-form response list call. */
520
+ readonly travellerResponses: (travellerRef: string) => Promise<readonly TravellerFormResponseRow[]>;
521
+ /** Live read of EVERY respondent's row for one form — how a manager
522
+ * browses "who has answered this `TRAVELLER`-audience form" (the other
523
+ * axis from `travellerResponses`). */
524
+ readonly formResponses: (formId: string) => Promise<readonly TripFormResponseRow[]>;
525
+ /** Save (`submit: false`) or finalize (`submit: true`) a response —
526
+ * self-fill or on-behalf, same endpoint, same outbox-backed write. */
527
+ /** `null` rather than an `OutboxOp` when the write took the DESK lane —
528
+ * a direct call has no outbox row to hand back. */
529
+ readonly submitResponse: (input: SubmitFormResponseInput) => Promise<OutboxOp | null>;
530
+ /** `forms.list` — the agency's whole catalog, browser-bound (no
531
+ * `agencyRef` to supply). */
532
+ /** `getFormsCompletionMatrix` — one row PER FORM, not a traveller grid.
533
+ * See the callback's own note. */
534
+ /** Re-reads the live trip forms list. Exposed because a composite
535
+ * showing a failed read needs a retry, and `forms`/`syncedAt` are
536
+ * fetched, not subscribed. */
537
+ readonly refresh: () => Promise<void>;
538
+ readonly completion: () => Promise<readonly FormCompletionRow[]>;
539
+ /** `dispatchForm` — sends a form to travellers and refreshes the list,
540
+ * since it moves `dispatched`. */
541
+ readonly dispatch: (input: DispatchFormInput) => Promise<void>;
542
+ readonly exportResponses: (input: ExportFormResponsesInput) => Promise<KaafilBinaryResponse>;
543
+ readonly consentReceipt: (input: ConsentReceiptInput) => Promise<KaafilBinaryResponse>;
544
+ readonly listAgencyForms: () => Promise<readonly FormAgencyRow[]>;
545
+ /** `forms.create` — authors a fresh `DRAFT`, browser-bound. */
546
+ readonly createForm: (input: CreateFormInput) => Promise<FormDefinitionDetail>;
547
+ readonly patchForm: (input: PatchFormInput) => Promise<FormDefinitionSummary>;
548
+ readonly publishForm: (formId: string) => Promise<FormDefinitionSummary>;
549
+ readonly closeForm: (formId: string) => Promise<FormDefinitionSummary>;
550
+ readonly reopenForm: (formId: string) => Promise<FormDefinitionSummary>;
551
+ readonly archiveForm: (formId: string) => Promise<FormDefinitionSummary>;
552
+ readonly unarchiveForm: (formId: string) => Promise<FormDefinitionSummary>;
553
+ readonly cloneForm: (input: CloneFormInput) => Promise<FormDefinitionSummary>;
554
+ /** `forms.reorder` — one idempotent transaction over every section's and
555
+ * field's `sortOrder`, never one call per row (this spec entry's own
556
+ * `Does NOT`). */
557
+ readonly reorderForm: (input: ReorderFormInput) => Promise<void>;
558
+ readonly createSection: (input: CreateFormSectionInput) => Promise<FormSectionDetail>;
559
+ readonly patchSection: (input: PatchFormSectionInput) => Promise<FormSectionDetail>;
560
+ readonly deleteSection: (input: DeleteFormSectionInput) => Promise<void>;
561
+ readonly createField: (input: CreateFormFieldInput) => Promise<FormFieldDetail>;
562
+ readonly patchField: (input: PatchFormFieldInput) => Promise<FormFieldDetail>;
563
+ readonly deleteField: (input: DeleteFormFieldInput) => Promise<void>;
564
+ /** `forms.bindings.list` — the closed 16-key registry; `apiKeyAuth`/
565
+ * `agencyAdminAuth`/`managerAuth` all accepted (`generated/security.ts`). */
566
+ readonly listBindings: () => Promise<readonly BindingEntry[]>;
567
+ }
568
+ /**
569
+ * Not mode-dark (`manager/05-forms.md`'s own framing note) — a dark result
570
+ * from this trip's `'forms'` capability row is always data- or flag-shaped
571
+ * in practice. Returns `{ status: 'dark', reason }` before any fetch when
572
+ * that row isn't `lit`.
573
+ */
574
+ declare function useForms(tripRef: string): DomainHookResult<FormsReady>;
575
+
576
+ type ItineraryReadResult = Awaited<ReturnType<ItineraryResource['read']>>;
577
+ /** One row of `ItineraryReadResponse.days[]` — no tombstone branch (this
578
+ * section is never delta-pulled; see this file's header). */
579
+ type ItineraryDayRow = ItineraryReadResult['days'][number];
580
+ /** `ItineraryReadResponse.items[]`'s generated shape is `anyOf` a live item
581
+ * or a tombstone — the same shared shape the delta (`?since=`) mode uses,
582
+ * even though a full/no-`since` read (the only mode this hook ever calls)
583
+ * has nothing to report a tombstone against. This hook filters the
584
+ * tombstone variant out in `refresh()` below rather than exposing the raw
585
+ * union, so every `ItineraryItemRow` a composite reads is unconditionally
586
+ * live — the same `isLiveRoom`/`Exclude<..., {_tombstone:true}>` pattern
587
+ * `useRooming.ts`'s own consumers use, applied inside the hook here since
588
+ * this hook, unlike `useRooming()`, has no separate delta-row type the spec
589
+ * asks composites to narrow themselves. */
590
+ type ItineraryItemUnion = ItineraryReadResult['items'][number];
591
+ type ItineraryItemRow = Exclude<ItineraryItemUnion, {
592
+ readonly _tombstone: true;
593
+ }>;
594
+ type ItineraryTripSummary = ItineraryReadResult['trip'];
595
+ type ItineraryChangeLogRow = Awaited<ReturnType<ItineraryResource['changeLog']['list']>>[number];
596
+ type AddItineraryItemInput = Omit<AddItineraryItemOptions, 'tripRef' | 'signal'>;
597
+ type PatchItineraryItemInput = Omit<PatchItineraryItemOptions, 'tripRef' | 'signal'>;
598
+ type RemoveItineraryItemInput = Omit<DeleteItineraryItemOptions, 'tripRef' | 'signal'>;
599
+ type ReorderItineraryItemInput = Omit<ReorderItineraryItemOptions, 'tripRef' | 'signal'>;
600
+ type PatchItineraryDayInput = Omit<PatchItineraryDayOptions, 'tripRef' | 'signal'>;
601
+ type LoadItineraryChangeLogInput = Omit<ListItineraryChangeLogOptions, 'tripRef' | 'signal'>;
602
+ interface ItineraryReady {
603
+ readonly trip: ItineraryTripSummary | undefined;
604
+ /** Server's own pick of which day the itinerary screen should land on
605
+ * (today → first future day → Day 1, `FRD:itinerary §5`) — never
606
+ * computed client-side. */
607
+ readonly initialDayIso: string | undefined;
608
+ readonly canAddItems: boolean;
609
+ readonly canAddItemsReason: string | null;
610
+ readonly days: readonly ItineraryDayRow[];
611
+ readonly items: readonly ItineraryItemRow[];
612
+ /** `true` once the first `itinerary.read()` has settled (success or
613
+ * failure) — distinguishes "never fetched yet" from "fetched, trip has
614
+ * zero days," the same discipline `RoomingReady.unassignedPool.loaded`
615
+ * documents. */
616
+ readonly loaded: boolean;
617
+ readonly lastFetchFailed: boolean;
618
+ readonly syncedAt: string | undefined;
619
+ readonly refresh: () => Promise<void>;
620
+ readonly changeLog: readonly ItineraryChangeLogRow[];
621
+ readonly changeLogLoaded: boolean;
622
+ readonly changeLogFetchFailed: boolean;
623
+ readonly loadChangeLog: (input?: LoadItineraryChangeLogInput) => Promise<void>;
624
+ readonly patchDay: (input: PatchItineraryDayInput) => Promise<OutboxOp | null>;
625
+ readonly addItem: (input: AddItineraryItemInput) => Promise<OutboxOp | null>;
626
+ readonly patchItem: (input: PatchItineraryItemInput) => Promise<OutboxOp | null>;
627
+ readonly removeItem: (input: RemoveItineraryItemInput) => Promise<OutboxOp | null>;
628
+ readonly reorderItem: (input: ReorderItineraryItemInput) => Promise<OutboxOp | null>;
629
+ }
630
+ type ItineraryResult = {
631
+ readonly status: 'loading';
632
+ } | ({
633
+ readonly status: 'ready';
634
+ } & ItineraryReady) | {
635
+ readonly status: 'error';
636
+ };
637
+ /**
638
+ * Ungated (see this file's header) — never returns `{status:'dark'}`.
639
+ * `manager`/`agencyAdmin` both call this same hook (`FRD:itinerary`'s
640
+ * `COORDINATOR`-role read-only resolution happens on the OPEN SESSION,
641
+ * server-side, never as a UIKit prop or a second hook).
642
+ */
643
+ declare function useItinerary(tripRef: string): ItineraryResult;
644
+
645
+ type RoomingStayWindowRow = Awaited<ReturnType<RoomingResource['stayWindows']['list']>>[number];
646
+ type AssignBedInput = Omit<AssignRoomingBedOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
647
+ type AutoAssignInput = Omit<AutoAssignRoomingOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
648
+ type CreateWindowInput = Omit<CreateRoomingStayWindowOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
649
+ type PatchWindowInput = Omit<PatchRoomingStayWindowOptions, 'tripRef' | 'signal'>;
650
+ type RemoveWindowInput = Omit<DeleteRoomingStayWindowOptions, 'tripRef' | 'signal'>;
651
+ type CreateRoomInput = Omit<CreateRoomingRoomOptions, 'tripRef' | 'idempotencyKey' | 'signal'>;
652
+ type PatchRoomInput = Omit<PatchRoomingRoomOptions, 'tripRef' | 'signal'>;
653
+ type RemoveRoomInput = Omit<DeleteRoomingRoomOptions, 'tripRef' | 'signal'>;
654
+ /**
655
+ * The unassigned-traveller roster — `RoomingBoardResponse.unassigned[]`
656
+ * (`kaafil-js/openapi/openapi.json`, verified). Its own state, deliberately
657
+ * NOT folded into `rooms`/`stayWindows`'s cache-first shape, so a consumer
658
+ * can never mistake one for the other.
659
+ */
660
+ interface RoomingUnassignedPool {
661
+ readonly travellers: readonly RoomingOccupant[];
662
+ /** `true` once at least one `client.rooming.read()` call has settled
663
+ * (success or failure) — distinguishes "never fetched yet" from "fetched
664
+ * and genuinely empty." */
665
+ readonly loaded: boolean;
666
+ /** `true` when the MOST RECENT fetch threw (offline, network, server
667
+ * error) — a consumer must render this differently from `travellers: []`
668
+ * with `loaded: true`; those two states are never the same thing. */
669
+ readonly lastFetchFailed: boolean;
670
+ /** The server time (`meta.serverTime`) of the last successful fetch —
671
+ * `undefined` before any fetch has ever succeeded. Never a locally-minted
672
+ * timestamp (hard rule #6). */
673
+ readonly fetchedAt: string | undefined;
674
+ /** Re-runs the live read, optionally scoped to one stay window.
675
+ *
676
+ * `stayWindowId` matters: the engine's own `readBoard` (`kaafil-engine`
677
+ * `rooming.service.ts#unassignedCountForWindow`'s doc comment) computes
678
+ * "unassigned" PER WINDOW deliberately — "a trip-wide unassigned count
679
+ * would have to decide whether a traveller with a bed on night 1 and none
680
+ * on night 2 is assigned, and there is no answer to that a subscriber
681
+ * could use." Passing the board's currently-active window here is what
682
+ * makes the pool answer "who still needs a bed IN THIS WINDOW," which is
683
+ * the only question a manager looking at one window's board is actually
684
+ * asking. Omitting it (this hook's own initial mount-time fetch, before
685
+ * any window is known) falls back to the engine's unscoped trip-wide
686
+ * read — genuinely ambiguous, per the same comment, so a caller that
687
+ * knows the active window should always pass it.
688
+ *
689
+ * `assign` already calls this on its own success (a bed assignment
690
+ * changes who is unassigned); a composite calls it again after any OTHER
691
+ * action that can move someone in or out of the pool. */
692
+ readonly refresh: (stayWindowId?: string) => Promise<void>;
693
+ }
694
+ interface RoomingReady {
695
+ readonly rooms: readonly RoomingRoomDeltaRow[];
696
+ readonly stayWindows: readonly RoomingStayWindowRow[];
697
+ readonly syncedAt: string | undefined;
698
+ /**
699
+ * LIVE-READ, NOT cache-first — deliberately. `RoomingBoardResponse`
700
+ * (`openapi/openapi.json`) carries three arrays; only `rooms[]` is
701
+ * delta-shaped (`anyOf` a live row or a `_tombstone`, with `id`/
702
+ * `version`). `unassigned[]` is a plain object keyed on `travellerId`
703
+ * with no tombstone branch and no version — a DERIVED projection the
704
+ * engine recomputes per read, not a durable delta feed
705
+ * (`kaafil-js/src/offline/pull.ts`'s `extractSectionRows` docstring,
706
+ * verified this session). Caching it as a delta row would be a real bug:
707
+ * no tombstone can ever arrive for a traveller who GETS a bed, and
708
+ * `SnapshotStore.applyPull` rebases by id rather than replacing, so a
709
+ * cached pool would grow monotonically and never shrink. This field is
710
+ * therefore sourced from its own `client.rooming.read()` call, refreshed
711
+ * explicitly rather than subscribed to `snapshot.updated`.
712
+ */
713
+ readonly unassignedPool: RoomingUnassignedPool;
714
+ /** Re-reads both desk lanes after a write. A NO-OP on a field device,
715
+ * where the outbox drain emits `snapshot.updated` and the cache-first
716
+ * reads re-render themselves — a composite need not know which lane it is
717
+ * on. */
718
+ readonly refreshLive: () => Promise<void>;
719
+ readonly assign: (input: AssignBedInput) => Promise<OutboxOp | null>;
720
+ readonly autoAssign: (input: AutoAssignInput) => ReturnType<RoomingResource['autoAssign']>;
721
+ readonly createStayWindow: (input: CreateWindowInput) => Promise<OutboxOp | null>;
722
+ readonly patchStayWindow: (input: PatchWindowInput) => Promise<OutboxOp | null>;
723
+ readonly removeStayWindow: (input: RemoveWindowInput) => Promise<OutboxOp | null>;
724
+ readonly createRoom: (input: CreateRoomInput) => Promise<OutboxOp | null>;
725
+ readonly patchRoom: (input: PatchRoomInput) => Promise<OutboxOp | null>;
726
+ readonly removeRoom: (input: RemoveRoomInput) => Promise<OutboxOp | null>;
727
+ }
728
+
729
+ /**
730
+ * PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
731
+ * fetch when the `'rooming'` capability triple isn't `lit`
732
+ * (`06-capability-and-personas.md §3`).
733
+ */
734
+ declare function useRooming(tripRef: string): DomainHookResult<RoomingReady>;
735
+
736
+ /** One row of `vendors.list()`'s array — `VendorSummaryResponse`
737
+ * (`kaafil-js/src/generated/schema.d.ts`, verified): `id`/`name`/`category`/
738
+ * `phone`/`city`. `phone`/`city` are nullable — absent until the CRM feeds
739
+ * one. Still no `status`, no `expenseTotalMinor` on the wire — a consumer
740
+ * that needs either is reading a field this response does not send.
741
+ * `KaafilResponse<readonly VendorSummaryResponse[]>` is `T & { meta }`
742
+ * (`kaafil-js/src/types/meta.ts`'s own intersection, verified) — the
743
+ * response IS the array, `meta` attached, never a nested `.data` field. */
744
+ type TripVendorRow = Awaited<ReturnType<VendorsResource['list']>>[number];
745
+ type UpsertVendorInput = Omit<UpsertVendorOptions, 'agencyRef' | 'idempotencyKey' | 'signal'>;
746
+ type RemoveVendorInput = Omit<DeleteVendorOptions, 'agencyRef' | 'idempotencyKey' | 'signal'>;
747
+ /** `create` takes neither an external id nor a `sourceUpdatedAt` — the
748
+ * server mints the first and stamps the second. See `VendorsReady.create`. */
749
+ type CreateVendorInput = Omit<CreateVendorOptions, 'agencyRef' | 'idempotencyKey' | 'signal'>;
750
+ interface VendorsReady {
751
+ readonly vendors: readonly TripVendorRow[];
752
+ /** `true` once at least one `client.vendors.list()` call has settled
753
+ * (success or failure) — same `loaded` contract `TrekBoardState`/
754
+ * `RoomingUnassignedPool` document. */
755
+ readonly loaded: boolean;
756
+ /** `true` when the MOST RECENT fetch threw — distinct from an honest
757
+ * empty directory (`loaded: true`, `vendors: []`). */
758
+ readonly lastFetchFailed: boolean;
759
+ /** The server time (`meta.serverTime`) of the last successful fetch. */
760
+ readonly syncedAt: string | undefined;
761
+ readonly refresh: () => Promise<void>;
762
+ /**
763
+ * Agency-scoped create for a vendor the AGENCY authored, not one a CRM
764
+ * pushed (`POST /agencies/:ref/vendors`).
765
+ *
766
+ * Takes NO external id and NO `sourceUpdatedAt`: `Vendor.externalId` is
767
+ * `NOT NULL`, so before this route existed the only create path was
768
+ * `upsert`, which is addressed by the caller's own CRM id — something an
769
+ * operator adding a supplier by hand does not have and must not be asked
770
+ * to invent. The server mints it (prefixed `kf-`) and stamps the write
771
+ * with its own clock.
772
+ */
773
+ readonly create: (input: CreateVendorInput) => ReturnType<VendorsResource['create']>;
774
+ /** Agency-scoped CRM upsert — see this file's header. Doubles as the EDIT
775
+ * of an existing vendor: `sourceUpdatedAt` is optional on the wire now, and
776
+ * omitting it has the server stamp the edit rather than letting it lose an
777
+ * LWW comparison against the very row the operator is looking at. */
778
+ readonly upsert: (input: UpsertVendorInput) => ReturnType<VendorsResource['upsert']>;
779
+ /** Agency-scoped CRM soft-delete — see this file's header. */
780
+ readonly remove: (input: RemoveVendorInput) => ReturnType<VendorsResource['remove']>;
781
+ }
782
+ declare function useVendors(tripRef: string, agencyRef: string): DomainHookResult<VendorsReady>;
783
+
784
+ export { useItinerary as A, useRooming as B, type ChecklistSectionRow as C, type DomainHookResult as D, type ExpenseDeltaRow as E, type FileListItem as F, useVendors as G, type CreateAgencyChecklistTemplateInput as H, type ItineraryDayRow as I, type RemoveAgencyChecklistTemplateInput as J, type PublishAgencyChecklistTemplateInput as K, type FormDefinitionDetail as L, type FormResponseDetail as M, type TravellerFormResponseRow as N, type ExpenseLiveRow as O, type PatchAgencyChecklistTemplateInput as P, type ChecklistAvailableTemplateRow as Q, type RoomingStayWindowRow as R, type FilePurpose as S, type TripFormRow as T, type ManagerFileView as U, type VendorsReady as V, type ChecklistsReady as a, type FloatMovementRow as b, type TripFormResponseRow as c, type ItineraryItemRow as d, type TripVendorRow as e, type FloatSummaryRow as f, type CapabilityTriple as g, type ErrorClassification as h, type CapabilityRow as i, type DarkReason as j, ERROR_HANDLING_PATH as k, type ErrorHandlingPath as l, type ExpensesReady as m, type FilesContext as n, type FilesReady as o, type FloatReady as p, type FormsReady as q, type ItineraryReady as r, type RoomingReady as s, type RoomingUnassignedPool as t, classifyError as u, useChecklists as v, useExpenses as w, useFiles as x, useFloat as y, useForms as z };