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,1267 @@
1
+ import { D as DomainHookResult, g as CapabilityTriple, h as ErrorClassification } from '../useVendors-DXOuZWhZ.js';
2
+ export { i as CapabilityRow, a as ChecklistsReady, j as DarkReason, k as ERROR_HANDLING_PATH, l as ErrorHandlingPath, m as ExpensesReady, n as FilesContext, o as FilesReady, p as FloatReady, q as FormsReady, r as ItineraryReady, s as RoomingReady, t as RoomingUnassignedPool, V as VendorsReady, u as classifyError, v as useChecklists, w as useExpenses, x as useFiles, y as useFloat, z as useForms, A as useItinerary, B as useRooming, G as useVendors } from '../useVendors-DXOuZWhZ.js';
3
+ import { ManagerAgencyProfileResponse, BookingListRow, AgencyAdminMeResponse, OfflineEngine, OutboxOp, KaafilClient, FeedbackNpsResource, DispatchFormOptions, JourneyResource, BoundJourneyResource, TripManagersResource, OfflineEventName, OfflineEventMap, ShareTokensResource, FormsResource, TravellersResource, TreksResource, CreateTrekWalkInOptions, PostponeTrekOptions, Environment, KaafilStorageAdapter, BlobUploader, BlobSource, ConflictResolver, EntityReader } from 'kaafil-js/client';
4
+ export { AgencyTripFilters, AgencyTripSegmentCounts, ManagerAgencyProfileResponse, TripManifestFacets, TripManifestFilters, UnsatisfiableSchemeError } from 'kaafil-js/client';
5
+ export { i as AgencyChecklistTemplatesReady, j as AgencyFormsReady, k as AgencyJourneyTriggersReady, g as AgencySettingsDocument, l as AgencySettingsReady, m as AgencyTripManifestResult, f as AgencyTripRead, n as AgencyTripResult, A as AgencyTripRow, o as AgencyTripsResult, B as BindingRow, a as ChecklistTemplateItemRow, C as ChecklistTemplateRow, p as CloneFormInput, q as CollectionsReady, r as CreateFormFieldInput, s as CreateFormInput, t as CreateFormSectionInput, D as DeleteFormFieldInput, u as DeleteFormInput, v as DeleteFormSectionInput, w as DirectoriesReady, F as FormDetailRow, b as FormFieldRow, c as FormRow, h as FormSectionRow, x as FormSummaryRow, J as JourneyTriggerRow, P as PaginatedListFetch, y as PaginatedListFetchResult, z as PaginatedListOptions, E as PaginatedListResult, G as PatchAgencySettingsInput, H as PatchFormFieldInput, I as PatchFormInput, K as PatchFormSectionInput, L as PatchJourneyTriggerInput, R as ReorderFormInput, d as TripManifestRow, N as useAgencyChecklistTemplates, O as useAgencyForms, Q as useAgencyJourneyTriggers, S as useAgencySettings, U as useAgencyTrip, V as useAgencyTripManifest, W as useAgencyTrips, X as useCollections, Y as useDirectories, Z as useKaafilClient, _ as usePaginatedList } from '../useAgencyTrip-BzyJB1D9.js';
6
+ import { C as ConflictRecord, S as StalenessState } from '../useNotifications-D262i5BQ.js';
7
+ export { a as CloseoutReady, M as ManagerMeReady, b as ManagerMeResult, c as ManagerNotification, d as ManagerTodayReady, e as ManagerTodayResult, N as NotificationsReady, f as NotificationsResult, O as OutboxState, P as PartiesReady, g as PickupsReady, h as SeatingReady, i as SyncEventKind, u as useCloseout, j as useManagerMe, k as useManagerToday, l as useNotifications, m as useParties, n as usePickups, o as useSeating } from '../useNotifications-D262i5BQ.js';
8
+ import { ReactNode, Component } from 'react';
9
+ import { K as KaafilTokenOverrides } from '../tokenStyle-CToG5oOS.js';
10
+
11
+ type ManagerAgencyProfileRead = ManagerAgencyProfileResponse;
12
+ interface AgencyManagerProfileResult {
13
+ readonly read: ManagerAgencyProfileRead | undefined;
14
+ readonly readFailed: boolean;
15
+ readonly refresh: () => Promise<void>;
16
+ }
17
+ declare function useAgencyManagerProfile(managerRef: string): AgencyManagerProfileResult;
18
+
19
+ /**
20
+ * The FULL replacement set for one booking's vouchers.
21
+ *
22
+ * `voucherFileKeys` REPLACES the stored array wholesale (`bookings FRD` R6) —
23
+ * there is no add-one call. A caller that sends only its new key detaches
24
+ * every voucher the CRM already attached, so a UI must read the booking's
25
+ * current list and send it back plus the addition. Named `…FileKeys` rather
26
+ * than `…FileIds` because that is the wire field; the values are file ids.
27
+ */
28
+ interface ReplaceBookingVouchersInput {
29
+ readonly bookingRef: string;
30
+ readonly voucherFileKeys: readonly string[];
31
+ }
32
+ interface BookingsReady {
33
+ readonly bookings: readonly BookingListRow[];
34
+ readonly syncedAt: string | undefined;
35
+ /** Re-read the live list — used after a voucher write so the booking's own
36
+ * `voucherFileKeys` stops being stale. */
37
+ readonly refreshLive: () => Promise<void>;
38
+ /**
39
+ * `PATCH /trips/{ref}/bookings/{bookingRef}/vouchers`. The ONE write this
40
+ * hook has, and the only booking field a browser may change.
41
+ *
42
+ * `ReplaceBookingVouchersOptions` declares NO `idempotencyKey`, so nothing
43
+ * here mints or accepts one (hard rule #4) and the desk lane is legal.
44
+ */
45
+ readonly replaceVouchers: (input: ReplaceBookingVouchersInput) => Promise<void>;
46
+ }
47
+ declare function useBookings(tripRef: string): DomainHookResult<BookingsReady>;
48
+
49
+ /**
50
+ * Thrown by a §3 domain hook that has no implementation yet in this build.
51
+ *
52
+ * Deliberately NOT the same class as `kaafil-js`'s own `KaafilNotImplementedError`
53
+ * (re-exported by `./classify.ts`'s callers via `kaafil-js/client` where
54
+ * needed) — that one models the wire's `501 NOT_IMPLEMENTED` catalog code (a
55
+ * reserved server route, `architecture/10-conventions.md §7`), a fact about
56
+ * the API. This one is a fact about THIS PACKAGE's own build: the hook
57
+ * exists, type-checks, and is importable, but its body has not been
58
+ * authored yet. Naming it differently keeps a composite's `catch` block from
59
+ * ever conflating "the server hasn't shipped this route" with "the UIKit
60
+ * hasn't shipped this hook" — the two need different user-facing copy and
61
+ * the two need different fixes.
62
+ */
63
+ declare class NotImplementedError extends Error {
64
+ readonly hookName: string;
65
+ readonly specSection: string;
66
+ constructor(hookName: string, specSection: string);
67
+ }
68
+
69
+ interface AgencyAdminMeLoading {
70
+ readonly status: 'loading';
71
+ }
72
+ interface AgencyAdminMeError {
73
+ readonly status: 'error';
74
+ }
75
+ interface AgencyAdminMeReady {
76
+ readonly status: 'ready';
77
+ readonly agencyAdmin: AgencyAdminMeResponse['agencyAdmin'];
78
+ }
79
+ type AgencyAdminMeResult = AgencyAdminMeLoading | AgencyAdminMeError | AgencyAdminMeReady;
80
+ /**
81
+ * `client.agencyAdminMe.read()` behind a plain fetch-on-mount effect — this
82
+ * read takes no parameter (it resolves entirely off the authenticated
83
+ * principal, `agency-admin-me.routes.ts`'s own header, cited), so it never
84
+ * re-fetches on a prop change. A caller wanting "refresh now" re-mounts this
85
+ * hook's owning component.
86
+ */
87
+ declare function useAgencyAdminMe(): AgencyAdminMeResult;
88
+
89
+ interface BlobStatusResult {
90
+ readonly status: 'pending' | 'uploading' | 'ready' | 'orphaned' | 'purged';
91
+ readonly progress: number | undefined;
92
+ }
93
+ declare function useBlobStatus(blobId: string): BlobStatusResult;
94
+
95
+ /** `08-core-hooks.md §1.4`. Persona restriction: unavailable to `share`. */
96
+ declare function useCapability(key: string): CapabilityTriple;
97
+ /** `08-core-hooks.md §1.4`. Omitting `keys` returns every key the last read
98
+ * carried. Persona restriction: unavailable to `share`. */
99
+ declare function useCapabilities(keys?: readonly string[]): ReadonlyMap<string, CapabilityTriple>;
100
+
101
+ interface CapabilityStatusesResult {
102
+ /** Capability key → the derived status, for every key the last read
103
+ * carried. A key that is ABSENT from this map is absent from the read —
104
+ * callers apply their own open-set fallback (`06 §6` point 5), which this
105
+ * hook deliberately does not choose for them. */
106
+ readonly statuses: ReadonlyMap<string, CapabilityTriple['status']>;
107
+ /** `false` until the first `journey.capabilities()` response for this trip
108
+ * has landed. A `share` session is immediately `true` with an empty map
109
+ * (`06 §9` — no `share.*` operation exposes this read). */
110
+ readonly loaded: boolean;
111
+ }
112
+ declare function useCapabilityStatuses(tripRef: string): CapabilityStatusesResult;
113
+
114
+ interface ConflictsResult {
115
+ readonly conflicts: readonly ConflictRecord[];
116
+ readonly resolve: (opId: string, choice: 'keep-queued' | 'keep-current') => Promise<void>;
117
+ }
118
+ declare function useConflicts(tripRef: string): ConflictsResult;
119
+
120
+ type OfflineMutationStatus = 'idle' | 'queued' | 'syncing' | 'synced' | 'conflicted' | 'parked';
121
+ interface OfflineMutationConfig<TOptions> {
122
+ readonly tripRef: string;
123
+ readonly enqueue: (engine: OfflineEngine, options: TOptions) => Promise<OutboxOp>;
124
+ /**
125
+ * The same write, awaited straight against the SDK, for a surface with no
126
+ * `OfflineShell` — see this file's header. Omit it and `mutate` keeps its
127
+ * original throw on such a surface, which is right for a domain that only
128
+ * a field persona ever writes.
129
+ *
130
+ * Its resolved value is deliberately unused: the caller re-reads after the
131
+ * write (every domain hook here already does), so a returned row would be a
132
+ * second, staler copy of what the refresh is about to fetch.
133
+ */
134
+ readonly direct?: (client: KaafilClient, options: TOptions) => Promise<unknown>;
135
+ }
136
+ /** A config that HAS a direct lane — the overload below keys on this. */
137
+ interface OfflineMutationConfigWithDirect<TOptions> extends OfflineMutationConfig<TOptions> {
138
+ readonly direct: (client: KaafilClient, options: TOptions) => Promise<unknown>;
139
+ }
140
+ /**
141
+ * `TQueued` is `OutboxOp` for a config with no direct lane and
142
+ * `OutboxOp | null` for one that has it. Parameterised rather than widened
143
+ * unconditionally: a hook that supplies no `direct` can never take the direct
144
+ * branch, so telling its callers the result might be `null` would be a lie
145
+ * that every one of the fourteen existing domain hooks would have to absorb
146
+ * into its own published `Ready` interface.
147
+ */
148
+ interface OfflineMutationResult<TOptions, TQueued = OutboxOp> {
149
+ /** `null` on the direct lane — committed, with no outbox row to track. */
150
+ readonly mutate: (options: TOptions) => Promise<TQueued>;
151
+ readonly status: OfflineMutationStatus;
152
+ readonly op: OutboxOp | undefined;
153
+ }
154
+ declare function useOfflineMutation<TOptions>(config: OfflineMutationConfigWithDirect<TOptions>): OfflineMutationResult<TOptions, OutboxOp | null>;
155
+ declare function useOfflineMutation<TOptions>(config: OfflineMutationConfig<TOptions>): OfflineMutationResult<TOptions, OutboxOp>;
156
+
157
+ type FeedbackNpsScope = {
158
+ readonly kind: 'trip';
159
+ readonly tripRef: string;
160
+ } | {
161
+ readonly kind: 'agency';
162
+ readonly agencyRef: string;
163
+ };
164
+ type TripFeedbackSummaryData = Awaited<ReturnType<FeedbackNpsResource['trip']>>['data'];
165
+ type AgencyFeedbackSummaryData = Awaited<ReturnType<FeedbackNpsResource['agency']>>['data'];
166
+ /** One row of the `agency` read's `definitions[]` — the agency-wide rollup
167
+ * `manager/06-feedback-nps.md` explicitly leaves to the admin family; kept
168
+ * here only so this one shared hook serves both scopes uniformly. */
169
+ type FeedbackNpsAgencyDefinitionRow = AgencyFeedbackSummaryData['definitions'][number];
170
+ type DispatchFeedbackChaseInput = Omit<DispatchFormOptions, 'tripRef' | 'formId' | 'idempotencyKey' | 'signal'>;
171
+ interface FeedbackNpsReady {
172
+ readonly kind: FeedbackNpsScope['kind'];
173
+ /** `trip` scope only — the discriminated union verbatim off the wire.
174
+ * `undefined` on `agency` scope, or before the first `trip` fetch has
175
+ * settled (`loaded` distinguishes the two). */
176
+ readonly summary: TripFeedbackSummaryData | undefined;
177
+ /** `agency` scope only — always `[]` on `trip` scope. */
178
+ readonly definitions: readonly FeedbackNpsAgencyDefinitionRow[];
179
+ /** `true` once the first read has settled (success or failure) —
180
+ * distinguishes "never fetched yet" from "fetched, and this trip
181
+ * genuinely has no feedback form configured" (`CloseoutReady.loaded`'s
182
+ * own contract, reused). */
183
+ readonly loaded: boolean;
184
+ /** `true` when the MOST RECENT fetch threw. `loaded`/`summary`/
185
+ * `definitions` stay whatever they already were. */
186
+ readonly lastFetchFailed: boolean;
187
+ readonly syncedAt: string | undefined;
188
+ readonly refresh: () => Promise<void>;
189
+ /** `trip` scope only. Throws if called before a `configured: true`
190
+ * summary has resolved a `formId` to target — a caller gates the
191
+ * dispatch control on `summary?.configured === true` first exactly as
192
+ * `manager/06-feedback-nps.md`'s own `FeedbackDispatchControls` does. */
193
+ readonly dispatch: (input?: DispatchFeedbackChaseInput) => Promise<OutboxOp>;
194
+ /** The dispatch op's OWN outbox lifecycle (`useOfflineMutation`'s own
195
+ * `status`) — `'parked'`/`'conflicted'` is how a `423`
196
+ * ("this trip has been closed out") or a `402` plan-lock surfaces, via
197
+ * `dispatchOp?.parkReason?.userMessage` (`06-feedback-nps.md`'s own
198
+ * `locked (423)`/`parked (402)` States rows). */
199
+ readonly dispatchStatus: OfflineMutationStatus;
200
+ readonly dispatchOp: OutboxOp | undefined;
201
+ }
202
+ /**
203
+ * Two scope members, different credential sets, one shared read/refresh
204
+ * shape — see this file's header for why there is no `createDomainHook()`
205
+ * gate. `manager`'s own `FeedbackNpsFlow` only ever calls this with
206
+ * `{ kind: 'trip', tripRef }`; the `agency` branch exists for the admin
207
+ * family's own agency-wide rollup, reusing this same hook rather than a
208
+ * second `useAgencyFeedbackNps()` (hard rule #2).
209
+ */
210
+ declare function useFeedbackNps(scope: FeedbackNpsScope): FeedbackNpsReady;
211
+
212
+ type JourneyReadResponse = Awaited<ReturnType<JourneyResource['get']>>;
213
+ interface JourneyResult {
214
+ readonly read: JourneyReadResponse | undefined;
215
+ readonly capabilities: ReadonlyMap<string, CapabilityTriple>;
216
+ readonly refreshCapabilities: () => Promise<void>;
217
+ readonly triggers: {
218
+ list: BoundJourneyResource['triggers']['list'];
219
+ patch: BoundJourneyResource['triggers']['patch'];
220
+ };
221
+ }
222
+ declare function useJourney(tripRef: string): JourneyResult;
223
+
224
+ /** One row of `trips.managers.list`'s result — derived structurally off the
225
+ * resource's own return type (`useJourney.ts`'s identical precedent for
226
+ * `JourneyReadResponse`), so this can never drift from what the SDK
227
+ * actually returns. */
228
+ type TripManagerRow = Awaited<ReturnType<TripManagersResource['list']>>[number];
229
+ type ManagersResult = {
230
+ readonly status: 'loading';
231
+ } | {
232
+ readonly status: 'ready';
233
+ readonly managers: readonly TripManagerRow[];
234
+ } | {
235
+ readonly status: 'error';
236
+ readonly error: ErrorClassification;
237
+ };
238
+ /**
239
+ * A trip's manager roster — every live manager assigned to the trip,
240
+ * lead first (`trips.managers.list`'s own doc comment). `manager`/
241
+ * `agencyAdmin` only, matching `useJourney()`'s identical persona
242
+ * restriction and identical dev-only throw on misuse.
243
+ */
244
+ declare function useManagers(tripRef: string): ManagersResult;
245
+
246
+ /** Returns the engine instance `OfflineShell` opened via
247
+ * `client.openOffline(options)`. Throws outside the provider tree. */
248
+ declare function useOfflineEngine(): OfflineEngine;
249
+
250
+ /**
251
+ * Subscribes `handler` to `name` for as long as the calling component is
252
+ * mounted. `session.expired`/`share.expired`/`share.revoked` firing for the
253
+ * "wrong" persona's session is inert, not an error (`08-core-hooks.md
254
+ * §2.6`'s persona-restriction note) — this hook does not filter by persona
255
+ * itself, since the engine only ever emits the events its own persona can
256
+ * produce.
257
+ */
258
+ declare function useOfflineEvent<TName extends OfflineEventName>(name: TName, handler: (event: OfflineEventMap[TName]) => void): void;
259
+
260
+ interface OutboxStatusOptions {
261
+ readonly tripRef?: string;
262
+ }
263
+ interface OutboxStatusResult {
264
+ readonly pending: number;
265
+ readonly parked: number;
266
+ readonly lanes: readonly string[];
267
+ readonly isOnline: boolean;
268
+ }
269
+ /**
270
+ * `isOnline` reads `navigator.onLine` where available (every browser target
271
+ * this package ships to) and defaults to `true` on a runtime without a
272
+ * `navigator` (SSR) — the honest default for a signal that only degrades
273
+ * gracefully, never one this hook can source from the SDK itself: `06
274
+ * -offline-first.md` and `08-core-hooks.md §2.9`'s own "Wraps" line name
275
+ * "the transport's own online/offline signal" but `kaafil-js`'s
276
+ * `OfflineEngine` does not expose a queryable connectivity flag of its own
277
+ * (verified — `Outbox`/`Drainer` react to `online`/`offline` DOM events
278
+ * internally via `bindOpportunisticTriggers()`, but neither surfaces the
279
+ * CURRENT state as a readable property). This hook therefore reads the
280
+ * browser signal directly and refreshes it on the same events the engine
281
+ * itself listens for, rather than duplicating a connectivity tracker inside
282
+ * the SDK it is meant to be a thin subscription over.
283
+ */
284
+ declare function useOutboxStatus(options?: OutboxStatusOptions): OutboxStatusResult;
285
+
286
+ /**
287
+ * The three credential kinds a `KaafilClient` can be opened with, collapsed
288
+ * to the label every component downstream of the session adapter is allowed
289
+ * to see (`06-capability-and-personas.md §7`). Never a JWT, never which of
290
+ * `session.open`/`admin.open`/`share.open` produced it beyond this label.
291
+ */
292
+ type Persona = 'manager' | 'agencyAdmin' | 'share';
293
+ /**
294
+ * `useSession()`'s return shape (`08-core-hooks.md §1.2`). `agencyRef` is
295
+ * `undefined` only for a `share` session — a share token carries no agency
296
+ * scope of its own (`client-entry.ts`'s share-session header, verified).
297
+ */
298
+ interface SessionState {
299
+ readonly status: 'authenticated' | 'expired' | 'opening';
300
+ readonly persona: Persona;
301
+ readonly agencyRef: string | undefined;
302
+ readonly environment: 'live' | 'test';
303
+ }
304
+
305
+ declare function usePersona(): Persona;
306
+
307
+ /**
308
+ * Returns the resolved value of `key` (`module.group.key`), or `undefined`
309
+ * when no rung supplied one and there is no frozen default to fall back to.
310
+ * There is no default parameter and none may ever be added (hard rule #5) —
311
+ * a component that needs to render something while this is `undefined`
312
+ * renders a loading/unknown state, never a guessed value.
313
+ */
314
+ declare function useResolvedSetting<T>(key: `${string}.${string}.${string}`): T | undefined;
315
+
316
+ interface ServerTimeApi {
317
+ now: () => string;
318
+ sinceLastSync: string | undefined;
319
+ }
320
+ declare function useServerTime(): ServerTimeApi;
321
+
322
+ /**
323
+ * Returns the open session's derived state. Reads the base value
324
+ * `SessionShell` provides, then layers the `session.expired` offline event
325
+ * (`08-core-hooks.md §1.2`'s "Wraps" paragraph) on top locally, so a
326
+ * `manager`/`agencyAdmin` session that expires mid-mount transitions to
327
+ * `status: 'expired'` without waiting on `SessionShell` to re-render this
328
+ * subtree itself. `OfflineShell` may not have mounted yet (or ever, for a
329
+ * consumer that only needs `useKaafilClient()`) — the listener attaches
330
+ * only when an `OfflineEngine` is actually in context, and is a no-op
331
+ * otherwise; this hook still throws if `SessionShell` itself is absent,
332
+ * since a session-derived read with no session is meaningless.
333
+ */
334
+ declare function useSession(): SessionState;
335
+
336
+ /** One row of `GET /api/v1/share/{token}/forms` — `ShareFormsListResponse`
337
+ * (verified, `kaafil-js/openapi/openapi.json`). Metadata only: no prefill,
338
+ * no field list, no answers (`FRD:traveller-share §4.8`) — that detail
339
+ * arrives only from `renderForm()` below. */
340
+ interface ShareFormListItem {
341
+ readonly formId: string;
342
+ readonly key: string;
343
+ readonly title: string;
344
+ readonly phase: string;
345
+ readonly required: boolean;
346
+ readonly fillState: string;
347
+ readonly opensAt: string | null;
348
+ readonly closesAt: string | null;
349
+ readonly responseStatus: string | null;
350
+ readonly submittedAt: string | null;
351
+ }
352
+ /** One answer in a `save`/`submit` body — `ShareSaveFormRequest.answers[]`
353
+ * / `ShareSubmitFormRequest.answers[]` (verified). */
354
+ interface ShareFormAnswer {
355
+ readonly fieldKey: string;
356
+ readonly value: unknown;
357
+ }
358
+ interface ShareFormWriteInput {
359
+ readonly formId: string;
360
+ readonly answers: readonly ShareFormAnswer[];
361
+ /** This route's own de-dupe key — REQUIRED by the wire schema. See this
362
+ * file's header ("`clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY") —
363
+ * the caller mints this once per form-fill session and reuses it across
364
+ * every save/submit/retry of that session; this hook never generates it. */
365
+ readonly clientToken: string;
366
+ /** Group fill within the same `TripTraveller.partyId` — omit when the
367
+ * token itself already scopes to a single traveller. */
368
+ readonly travellerRef?: string;
369
+ readonly startNew?: boolean;
370
+ readonly isAnonymous?: boolean;
371
+ }
372
+ type ShareFormsListStatus = 'loading' | 'list-empty' | 'list-populated' | 'not-found';
373
+ /**
374
+ * The write-side status a `save()`/`submit()` call's tracked `OutboxOp`
375
+ * resolves to. Deliberately excludes `'filling'` (`02-forms.md`'s own
376
+ * `States` list also names it): `'filling'` is "the traveller is editing
377
+ * fields, no request in flight yet" — a pure local UI mode with no
378
+ * server/outbox signal behind it, so it belongs to the composite that owns
379
+ * the form's field state, never to this hook, which only ever reflects
380
+ * something async.
381
+ */
382
+ type ShareFormsWriteStatus = 'idle' | 'saving' | 'submitting' | 'submitted' | 'conflicted' | 'subject-required' | 'queued-offline' | 'dropped-on-revoke';
383
+ interface ShareFormsResult {
384
+ readonly status: ShareFormsListStatus;
385
+ /** `[]` in both the `'loading'` and the `'list-empty'` states — a
386
+ * consumer distinguishes them via `status`, never via array emptiness
387
+ * alone (`02-forms.md`'s `Gating`: an empty list is a NORMAL steady
388
+ * state, never an error, never a placeholder). */
389
+ readonly forms: readonly ShareFormListItem[];
390
+ readonly serverTime: string | undefined;
391
+ /** Re-runs `share.forms.list()`. A composite calls this after a
392
+ * `submit()` lands, since submitting can change another form's
393
+ * `fillState` (e.g. an any-phase trigger opening a follow-up form). */
394
+ readonly refresh: () => Promise<void>;
395
+ /** `GET /api/v1/share/{token}/forms/{formId}` — the fully-resolved,
396
+ * ready-to-paint form (definition merged with any in-progress answers).
397
+ * An open shape (`ShareRenderResponse` has no closed schema yet,
398
+ * verified) — a live call, not cached, matching `save`/`submit`'s own
399
+ * un-cached write path. */
400
+ readonly renderForm: (formId: string, options?: {
401
+ readonly travellerRef?: string;
402
+ }) => Promise<Record<string, unknown>>;
403
+ /** An in-progress save — no required-field validation server-side. */
404
+ readonly save: (input: ShareFormWriteInput) => Promise<OutboxOp>;
405
+ /** Finalizes the response — full validation, one transaction. */
406
+ readonly submit: (input: ShareFormWriteInput) => Promise<OutboxOp>;
407
+ /** The combined save/submit write status — see `ShareFormsWriteStatus`. */
408
+ readonly writeStatus: ShareFormsWriteStatus;
409
+ /** The most recently tracked save/submit `OutboxOp`, or `undefined`
410
+ * before either has ever been called. */
411
+ readonly writeOp: OutboxOp | undefined;
412
+ }
413
+ declare function useShareForms(token: string): ShareFormsResult;
414
+
415
+ /** Metadata for one link. Never carries a plaintext token. */
416
+ type ShareLinkRow = Awaited<ReturnType<ShareTokensResource['list']>>[number];
417
+ /** A freshly minted link — the ONE shape that carries `token`. */
418
+ type MintedShareLink = Awaited<ReturnType<ShareTokensResource['create']>>;
419
+ /**
420
+ * The 14 sections a link can expose, in the order the engine's own
421
+ * `SHARE_SECTION_KEYS` declares them.
422
+ *
423
+ * Derived from the SDK's own request type rather than hand-listed, so a
424
+ * section added engine-side becomes a compile error here instead of a
425
+ * silently-missing checkbox.
426
+ */
427
+ type ShareSectionKey = keyof NonNullable<NonNullable<Parameters<ShareTokensResource['create']>[0]['config']>['sections']>;
428
+ type ShareSections = Readonly<Record<ShareSectionKey, boolean>>;
429
+ interface MintShareLinkInput {
430
+ /** Omit for the whole-trip FAMILY link. */
431
+ readonly travellerRef?: string;
432
+ /**
433
+ * Omit entirely for the agency's own mode-based default. Supplying it turns
434
+ * every UNSET key OFF — a partial config does NOT inherit the defaults, so
435
+ * a caller that supplies this must name every section it wants visible.
436
+ * That is the wire's rule, not this hook's.
437
+ */
438
+ readonly sections?: ShareSections;
439
+ readonly ttlDays?: number;
440
+ readonly expiresAt?: string;
441
+ }
442
+ interface PatchShareLinkInput {
443
+ readonly id: string;
444
+ /** The token row's own version. A stale one answers `409 CONFLICT_VERSION`. */
445
+ readonly version: number;
446
+ readonly sections?: ShareSections;
447
+ readonly expiresAt?: string;
448
+ }
449
+ interface ShareLinksResult {
450
+ /** Live links on this trip — `undefined` until the first read lands. */
451
+ readonly links: readonly ShareLinkRow[] | undefined;
452
+ readonly readFailed: boolean;
453
+ readonly refresh: () => Promise<void>;
454
+ readonly mint: (input: MintShareLinkInput) => Promise<MintedShareLink>;
455
+ readonly revoke: (id: string) => Promise<void>;
456
+ readonly patch: (input: PatchShareLinkInput) => Promise<void>;
457
+ readonly regenerate: (id: string) => Promise<MintedShareLink>;
458
+ }
459
+ /**
460
+ * `travellerRef` narrows the LIST to one subject; pass `null` for every link
461
+ * on the trip, family link included. Minting takes its own `travellerRef`
462
+ * independently, so one hook instance can list a traveller's links and mint
463
+ * a family link.
464
+ */
465
+ declare function useShareLinks(tripRef: string, travellerRef: string | null): ShareLinksResult;
466
+
467
+ interface ShareSnapshotOptions {
468
+ readonly travellerRef?: string;
469
+ }
470
+ type ShareSnapshotResponse = Awaited<ReturnType<KaafilClient['share']['snapshot']>>;
471
+ /** The trip echo every snapshot response carries — `name`/`startDate`/
472
+ * `endDate` plus the two fields this hook used to throw away:
473
+ * `timezone` (the IANA zone every traveller-facing render must anchor to)
474
+ * and `phase` (server-computed `PRE_DEPARTURE`/`ON_TRIP`/`POST_TRIP`). */
475
+ type ShareSnapshotTrip = ShareSnapshotResponse['trip'];
476
+ interface ShareSnapshotResult {
477
+ readonly status: 'loading' | 'ready' | 'expired' | 'revoked' | 'not-found';
478
+ readonly sections: ReadonlyMap<string, unknown>;
479
+ /** The snapshot's `trip` echo, or `undefined` before any response has
480
+ * landed (mirrors `sections`' own "empty until ready" behaviour). NEVER a
481
+ * section key — see this file's header. */
482
+ readonly trip: ShareSnapshotTrip | undefined;
483
+ /** `meta.serverTime` off the most recently landed snapshot response — the
484
+ * only trusted clock a share composite may read (hard rule #6). Kept
485
+ * separate from `useServerTime()`'s own stream (which advances off
486
+ * `OfflineEngine`'s `snapshot.updated` event, not this hook's own fetch)
487
+ * so a composite that only ever calls `useShareSnapshot()` still has a
488
+ * real server stamp to compare against without also wiring the offline
489
+ * engine's event stream. `undefined` before any response has landed. */
490
+ readonly serverTime: string | undefined;
491
+ }
492
+ declare function useShareSnapshot(token: string, options?: ShareSnapshotOptions): ShareSnapshotResult;
493
+
494
+ interface SnapshotEntityResult<TRow> {
495
+ readonly row: TRow | undefined;
496
+ readonly syncedAt: string | undefined;
497
+ }
498
+ /**
499
+ * `row` is `undefined` both when the id genuinely doesn't exist and when
500
+ * the list hasn't synced yet — a caller distinguishes those with
501
+ * `syncedAt`: absent entirely means "never pulled"; present with `row:
502
+ * undefined` means "pulled, and this id isn't in it" (`08-core-hooks.md
503
+ * §2.3`).
504
+ */
505
+ declare function useSnapshotEntity<TRow extends Record<string, unknown>>(tripRef: string, listName: string, id: string): SnapshotEntityResult<TRow>;
506
+
507
+ interface SnapshotListOptions<TRow> {
508
+ /**
509
+ * The same list, read live, for a surface with no `OfflineShell`. Resolve
510
+ * the rows AND the server's own time — `syncedAt` means the same thing on
511
+ * both lanes ("what this reading is as of"), and deriving it from a device
512
+ * clock would make the two disagree.
513
+ */
514
+ readonly live?: (client: KaafilClient, tripRef: string) => Promise<{
515
+ readonly rows: readonly TRow[];
516
+ readonly syncedAt: string | undefined;
517
+ }>;
518
+ }
519
+ interface SnapshotListResult<TRow> {
520
+ readonly rows: readonly TRow[];
521
+ readonly syncedAt: string | undefined;
522
+ /**
523
+ * Re-reads the live lane. A NO-OP on the snapshot lane, where the store's
524
+ * own `snapshot.updated` event already drives re-render and a manual
525
+ * refetch would be a second sync path.
526
+ */
527
+ readonly refreshLive: () => Promise<void>;
528
+ }
529
+ /**
530
+ * At-least-once delivery is the CALLER's problem to absorb structurally
531
+ * (`07-offline-and-sync.md §4`) — this hook returns exactly what
532
+ * `SnapshotStore.list()` holds and does not de-duplicate beyond what the
533
+ * store itself already guarantees.
534
+ */
535
+ declare function useSnapshotList<TRow extends Record<string, unknown>>(tripRef: string, listName: string, options?: SnapshotListOptions<TRow>): SnapshotListResult<TRow>;
536
+
537
+ /**
538
+ * DECISION: the catalog names "a threshold `ConfigShell` supplies" without
539
+ * naming the resolved-setting key. `stale.thresholdSeconds` is this hook's
540
+ * own choice of key, under the `stale` module namespace — the closest fit to
541
+ * `useResolvedSetting`'s `module.group.key` shape (`08-core-hooks.md §1.5`)
542
+ * for a value that isn't owned by any one of the eighteen domain modules.
543
+ * `isStale` is conservatively `false` (never a guessed threshold) when
544
+ * nothing resolved one — the honest reading of "no threshold configured"
545
+ * given `useResolvedSetting()` may never supply its own default (hard rule
546
+ * #5).
547
+ */
548
+ declare function useStaleness(tripRef: string, listName: string): StalenessState;
549
+
550
+ type TravellerFormResponseRow = Awaited<ReturnType<FormsResource['trip']['travellerResponses']['list']>>['items'][number];
551
+ interface TravellerFormResponsesResult {
552
+ readonly responses: readonly TravellerFormResponseRow[] | undefined;
553
+ readonly readFailed: boolean;
554
+ readonly refresh: () => Promise<void>;
555
+ }
556
+ /**
557
+ * `travellerRef` is nullable so a caller can mount this unconditionally while
558
+ * its own panel is closed — Rules of Hooks forbid calling it conditionally,
559
+ * and `null` reads as "nothing open yet" rather than firing a request for a
560
+ * traveller nobody asked for.
561
+ */
562
+ declare function useTravellerFormResponses(tripRef: string, travellerRef: string | null): TravellerFormResponsesResult;
563
+
564
+ type TravellerProfileRead = Awaited<ReturnType<TravellersResource['getDetail']>>;
565
+ interface AddTravellerNoteInput {
566
+ readonly body: string;
567
+ }
568
+ interface TravellerProfileResult {
569
+ readonly read: TravellerProfileRead | undefined;
570
+ readonly readFailed: boolean;
571
+ readonly refresh: () => Promise<void>;
572
+ readonly addNote: (input: AddTravellerNoteInput) => Promise<OutboxOp>;
573
+ }
574
+ declare function useTravellerProfile(tripRef: string, travellerRef: string): TravellerProfileResult;
575
+
576
+ /** `TrekBoardResponse` — verified against `kaafil-js/src/generated/
577
+ * schema.d.ts`. `TreksResource['board']` resolves to `KaafilResponse<
578
+ * TrekBoardResponse>` = `TrekBoardResponse & { meta }` (`kaafil-js/src/
579
+ * types/meta.ts`'s own intersection, not a nested `data` field — unlike
580
+ * `FloatSummaryResponse`/`FloatLedgerResponse`, `TrekBoardResponse` carries
581
+ * `phase`/`stops`/`runningHeadCount`/`emptyState` at its own top level).
582
+ * `phase`/`externalTripId` are `null` only when `emptyState` is set; every
583
+ * other field still comes back with its empty/zero identity rather than
584
+ * being omitted (the schema's own doc comment). */
585
+ type TrekBoardData = Awaited<ReturnType<TreksResource['board']>>;
586
+ /** One row of `treks.walkIns.listPage()`'s page — `KaafilPagedResponse<
587
+ * readonly WalkInListItemResponse[]>` = the array itself, `meta` attached
588
+ * (`kaafil-js/src/types/meta.ts`), never a nested `.data`. */
589
+ type TrekWalkInRow = Awaited<ReturnType<TreksResource['walkIns']['listPage']>>[number];
590
+ /** `WalkInMetaResponse` — the pickup options + field-requirement hints a
591
+ * walk-in intake form reads, verified against the generated schema; same
592
+ * flat (non-`data`-nested) shape as `TrekBoardData`. */
593
+ type TrekWalkInMetaData = Awaited<ReturnType<TreksResource['walkIns']['meta']>>;
594
+ type CreateTrekWalkInInput = Omit<CreateTrekWalkInOptions, 'trekRef' | 'idempotencyKey' | 'signal'>;
595
+ type PostponeTrekInput = Omit<PostponeTrekOptions, 'trekRef' | 'idempotencyKey' | 'signal'>;
596
+ /** `ReconcileTrekWalkInOptions` — verified real (`treks.ts`'s own
597
+ * `walkIns.reconcile`) but NOT re-exported from `kaafil-js/client`'s public
598
+ * surface (verified: `client-entry.ts`'s `export type { ... } from
599
+ * './resources/treks'` list omits it, unlike its five siblings). Derived
600
+ * structurally off `TreksResource['walkIns']['reconcile']`'s own parameter
601
+ * rather than importing a name that does not exist on the package's public
602
+ * barrel — see this worker's final report for the exact export gap. */
603
+ type ReconcileTrekWalkInInput = Omit<Parameters<TreksResource['walkIns']['reconcile']>[0], 'trekRef' | 'walkInId' | 'idempotencyKey' | 'signal'>;
604
+ /**
605
+ * The phase-aware boarding board — a live-fetched, explicitly-refreshed
606
+ * state, never a snapshot subscription. See this file's header for why.
607
+ */
608
+ interface TrekBoardState {
609
+ readonly data: TrekBoardData | undefined;
610
+ /** `true` once at least one `client.treks.board()` call has settled
611
+ * (success or failure) — same `loaded` contract `RoomingUnassignedPool`/
612
+ * `useFloat().summary` document. */
613
+ readonly loaded: boolean;
614
+ /** `true` when the MOST RECENT fetch threw — a genuine transport/server
615
+ * failure, distinct from the honest `emptyState` "no trek assigned"
616
+ * `200`. */
617
+ readonly lastFetchFailed: boolean;
618
+ readonly fetchedAt: string | undefined;
619
+ readonly refresh: () => Promise<void>;
620
+ }
621
+ /** The walk-in roster — live-fetched via `listPage`, one page at a time
622
+ * (`08-treks.md §TrekWalkInRoster`'s own prop table cites `.list`/
623
+ * `.listPage`, never a delta feed). `loadMore` advances the cursor; calling
624
+ * `refresh` resets back to the first page. */
625
+ interface TrekWalkInsState {
626
+ readonly rows: readonly TrekWalkInRow[];
627
+ readonly loaded: boolean;
628
+ readonly lastFetchFailed: boolean;
629
+ readonly fetchedAt: string | undefined;
630
+ readonly hasMore: boolean;
631
+ readonly refresh: () => Promise<void>;
632
+ readonly loadMore: () => Promise<void>;
633
+ }
634
+ interface TrekWalkInMetaState {
635
+ readonly data: TrekWalkInMetaData | undefined;
636
+ readonly loaded: boolean;
637
+ readonly lastFetchFailed: boolean;
638
+ readonly refresh: () => Promise<void>;
639
+ }
640
+ interface TreksReady {
641
+ readonly board: TrekBoardState;
642
+ readonly walkIns: TrekWalkInsState;
643
+ readonly walkInMeta: TrekWalkInMetaState;
644
+ /** `POST .../walk-ins` — lands `BOARDED` directly for a manager submitter
645
+ * (`D-211`). */
646
+ readonly createWalkIn: (input: CreateTrekWalkInInput) => Promise<OutboxOp>;
647
+ /** `POST .../postpone` — shifts itinerary/stay-window dates server-side;
648
+ * never shifts pickup `scheduledTime`s (`08-treks.md`'s own "Does NOT"). */
649
+ readonly postpone: (input: PostponeTrekInput) => Promise<OutboxOp>;
650
+ /** `POST .../walk-ins/{walkInId}/reconcile` — the human fallback for a
651
+ * name-only walk-in the automatic CRM-phone match can't clear on its
652
+ * own. See this file's header for why this is exposed beyond
653
+ * `08-treks.md`'s own prop tables. */
654
+ readonly reconcileWalkIn: (walkInId: string, input: ReconcileTrekWalkInInput) => Promise<OutboxOp>;
655
+ }
656
+ /**
657
+ * PERSONALIZED mode-dark. Returns `{ status: 'dark', reason }` before any
658
+ * fetch when the `'treks'` capability triple isn't `lit`
659
+ * (`06-capability-and-personas.md §3`).
660
+ */
661
+ declare function useTreks(tripRef: string): DomainHookResult<TreksReady>;
662
+
663
+ interface KaafilBrandAssets {
664
+ readonly appName?: string;
665
+ readonly logo?: string | ReactNode;
666
+ readonly logoMark?: string | ReactNode;
667
+ readonly favicon?: string;
668
+ }
669
+ interface BrandShellProps extends KaafilBrandAssets {
670
+ readonly headStrategy?: 'none' | 'own-page';
671
+ readonly children?: ReactNode;
672
+ }
673
+ /** Reads the brand assets `BrandShell` forwarded, or `{}` when mounted
674
+ * without one (never throws — brand is presentational, not load-bearing the
675
+ * way a session read is). */
676
+ declare function useKaafilBrand(): KaafilBrandAssets;
677
+ interface ShareMeta {
678
+ readonly title: string | undefined;
679
+ readonly favicon: string | undefined;
680
+ readonly ogTitle: string | undefined;
681
+ readonly ogDescription: string | undefined;
682
+ readonly ogImage: string | undefined;
683
+ readonly robots: 'noindex' | 'index';
684
+ }
685
+ /**
686
+ * Forwards brand assets unconditionally; enforces `headStrategy` before
687
+ * ever mounting `<KaafilDocumentHead>`. `'none'` is a HARD no-op — no
688
+ * branch below it ever touches `document.head`, even when `favicon`/
689
+ * `appName` are supplied. `'own-page'` is runtime-rejected under
690
+ * `agencyAdmin`/`manager` (dev throw, prod warning + silent `'none'`
691
+ * fallback — the same shape `useCapability()` uses for a wrong-persona call,
692
+ * `08-core-hooks.md §1.4`).
693
+ */
694
+ declare function BrandShell({ appName, logo, logoMark, favicon, headStrategy, children, }: BrandShellProps): ReactNode;
695
+ /**
696
+ * Shell-owned read-only alternative to `<KaafilDocumentHead>` — for a host
697
+ * (e.g. Next.js App Router) that already owns `<head>` via its own
698
+ * `metadata`/`generateMetadata` export and must keep `headStrategy="none"`
699
+ * here. Never writes the head itself; exactly one of the two paths is
700
+ * active per mount (`shells/06-brand-shell.md`).
701
+ */
702
+ declare function useKaafilShareMeta(assets: KaafilBrandAssets): ShareMeta;
703
+
704
+ /** A `module.group.key`-keyed document — the shape `seedConfig.tenant`/
705
+ * `seedConfig.agency` each carry (`shells/04-config-shell.md`'s `seedConfig`
706
+ * prop). Not exported from `../types` — this is `ConfigShell`'s own prop
707
+ * shape, not a `08-core-hooks.md`-catalogued type. */
708
+ type ConfigDocument = Readonly<Record<string, unknown>>;
709
+ interface ConfigShellProps {
710
+ readonly seedConfig?: {
711
+ readonly tenant?: ConfigDocument;
712
+ readonly agency?: ConfigDocument;
713
+ };
714
+ /** Accepted, not yet load-bearing — see this file's header DISCOVERY note. */
715
+ readonly refreshOn?: ReadonlyArray<'trip-load' | 'refocus' | 'snapshot-update'>;
716
+ readonly children?: ReactNode;
717
+ }
718
+ /**
719
+ * Builds `resolveSetting` over the two rungs of the precedence ladder this
720
+ * package can actually reach today (`shells/04-config-shell.md §"Agency
721
+ * settings are unreachable"`) — Agency narrows Tenant, Tenant is the seed's
722
+ * outer rung; neither Kaafil-default nor Partner has a browser-reachable
723
+ * read at all (`⛔ GAP-002`), so this shell does not fabricate either.
724
+ */
725
+ declare function ConfigShell({ seedConfig, children }: ConfigShellProps): ReactNode;
726
+
727
+ /** Discriminated per `00-decisions.md` U-014/U-003 — passing a `shareToken`
728
+ * alongside `agencyRef` is a compile error, never a shape this shell has to
729
+ * defensively validate at runtime. */
730
+ type SessionShellCredential = {
731
+ readonly accessToken: string;
732
+ readonly refreshToken: string;
733
+ readonly agencyRef: string;
734
+ readonly shareToken?: undefined;
735
+ readonly credentialResolver?: undefined;
736
+ } | {
737
+ readonly shareToken: string;
738
+ readonly accessToken?: undefined;
739
+ readonly refreshToken?: undefined;
740
+ readonly agencyRef?: undefined;
741
+ readonly credentialResolver?: undefined;
742
+ } | {
743
+ readonly credentialResolver: () => Promise<{
744
+ accessToken: string;
745
+ refreshToken: string;
746
+ agencyRef: string;
747
+ } | {
748
+ shareToken: string;
749
+ }>;
750
+ readonly accessToken?: undefined;
751
+ readonly refreshToken?: undefined;
752
+ readonly agencyRef?: undefined;
753
+ readonly shareToken?: undefined;
754
+ };
755
+ type SessionShellProps = SessionShellCredential & {
756
+ readonly environment?: Environment;
757
+ /** Forwarded into `session.open`'s/`admin.open`'s own `onRefresh` field —
758
+ * no-ops silently for a `share` credential, which has no refresh shape
759
+ * (`shells/02-session-shell.md`). */
760
+ readonly onRefresh?: (result: unknown) => void | Promise<void>;
761
+ readonly onSessionExpired?: (credentialKind: Persona) => void;
762
+ readonly children?: ReactNode;
763
+ };
764
+ /**
765
+ * Opens the credential the resolved value props imply, exposes
766
+ * `KaafilClientContext`/`SessionContext`, and latches `onSessionExpired` to
767
+ * fire exactly once per terminal expiry — see this file's header for why the
768
+ * actual event subscription happens one shell further in
769
+ * (`./internal/sessionExpiryBridge.ts`). `client.close()` runs on unmount and
770
+ * before any re-open — `KaafilClient` throws `KaafilClientAlreadyOpenError`
771
+ * on a second `open()` without an intervening `close()` (verified,
772
+ * `client-entry.ts`).
773
+ */
774
+ declare function SessionShell(props: SessionShellProps): ReactNode;
775
+
776
+ /**
777
+ * The discriminated union `09-telemetry-shell.md §"Event taxonomy"` owns —
778
+ * every field is an opaque id, an enum, or a count, never PII, never a money
779
+ * amount. This is the type's one home; a new event type is added to this
780
+ * union and to the table in that doc, nowhere else.
781
+ */
782
+ type TelemetryEvent = {
783
+ readonly type: 'session.opened';
784
+ readonly timestamp: string;
785
+ readonly persona: string;
786
+ } | {
787
+ readonly type: 'session.expired';
788
+ readonly timestamp: string;
789
+ readonly persona: string;
790
+ } | {
791
+ readonly type: 'capability.darkened';
792
+ readonly timestamp: string;
793
+ readonly capability: string;
794
+ readonly reason: 'mode' | 'data' | 'flag';
795
+ } | {
796
+ readonly type: 'sync.drained';
797
+ readonly timestamp: string;
798
+ readonly tripRef: string;
799
+ readonly pending: number;
800
+ } | {
801
+ readonly type: 'sync.error';
802
+ readonly timestamp: string;
803
+ readonly tripRef: string;
804
+ readonly pending: number;
805
+ } | {
806
+ readonly type: 'outbox.parked';
807
+ readonly timestamp: string;
808
+ readonly opId: string;
809
+ readonly code: string;
810
+ } | {
811
+ readonly type: 'conflict.surfaced';
812
+ readonly timestamp: string;
813
+ readonly opId: string;
814
+ } | {
815
+ readonly type: 'conflict.resolved';
816
+ readonly timestamp: string;
817
+ readonly opId: string;
818
+ readonly choice?: 'keep-queued' | 'keep-current';
819
+ } | {
820
+ readonly type: 'error.caught';
821
+ readonly timestamp: string;
822
+ readonly classification: 'retryable' | 'parked' | 'isolation';
823
+ readonly code: string | undefined;
824
+ } | {
825
+ readonly type: 'brand.headStrategyRejected';
826
+ readonly timestamp: string;
827
+ readonly persona: string;
828
+ } | {
829
+ readonly type: 'frame.resolved';
830
+ readonly timestamp: string;
831
+ readonly frame: string;
832
+ readonly overridden: boolean;
833
+ };
834
+ interface TelemetryShellProps {
835
+ readonly onEvent?: (event: TelemetryEvent) => void;
836
+ readonly children?: ReactNode;
837
+ }
838
+ /**
839
+ * Wires the taxonomy's `session.opened`/`session.expired` rows off
840
+ * `useSession()`'s own status transitions — `useSession()` (hooks phase)
841
+ * already folds the underlying `session.expired`/`share.expired`/
842
+ * `share.revoked` offline events into its returned `status`
843
+ * (`../hooks/useSession.ts`), so watching `status` here is the one signal
844
+ * this shell needs, without a second, competing subscription to the same
845
+ * underlying event. The remaining taxonomy rows
846
+ * (`capability.darkened`/`sync.*`/`outbox.parked`/`conflict.*`/
847
+ * `error.caught`/`brand.headStrategyRejected`/`frame.resolved`) are defined
848
+ * in the `TelemetryEvent` union above (their one home) for the
849
+ * component-authoring phase's composites to emit into this same sink —
850
+ * this shell does not synthesize them itself before the events they
851
+ * describe exist to observe.
852
+ */
853
+ declare function TelemetryShell({ onEvent, children }: TelemetryShellProps): ReactNode;
854
+
855
+ type KaafilUIKitProviderCredential = SessionShellCredential;
856
+ /** `defaultCapabilityViews` (`shells/00-kaafil-uikit-provider.md`'s props
857
+ * table) — a tree-wide DEFAULT for the three capability-dark view overrides
858
+ * `04-customization-ladder.md §5.1` defines. No composite exists yet to
859
+ * consume this (component authoring is out of scope for this phase), so
860
+ * this context is the provider's own seam for one to read later; it never
861
+ * changes which of the three treatments applies, only what a composite
862
+ * falls back to rendering when it hasn't set its own same-named prop. */
863
+ interface DefaultCapabilityViews {
864
+ readonly modeDarkView?: ReactNode | ((ctx: unknown) => ReactNode);
865
+ readonly dataDarkView?: ReactNode | ((ctx: unknown) => ReactNode);
866
+ readonly flagDarkView?: ReactNode | ((ctx: unknown) => ReactNode);
867
+ }
868
+ /** Reads the tree-wide default set by `<KaafilUIKitProvider
869
+ * defaultCapabilityViews>` — `{}` when the provider didn't set one, never a
870
+ * required context (a missing default simply means every composite falls
871
+ * back to its own built-in dark treatment). */
872
+ declare function useDefaultCapabilityViews(): DefaultCapabilityViews;
873
+ interface KaafilUIKitProviderProps {
874
+ readonly environment?: Environment;
875
+ readonly onSessionExpired?: (credentialKind: Persona) => void;
876
+ readonly density?: 'compact' | 'default' | 'comfortable';
877
+ readonly theme?: 'light' | 'dark' | 'system';
878
+ readonly brand?: KaafilBrandAssets & {
879
+ readonly headStrategy?: 'none' | 'own-page';
880
+ };
881
+ readonly locale?: string;
882
+ readonly isolation?: 'scoped' | 'shadow';
883
+ readonly onEvent?: (event: TelemetryEvent) => void;
884
+ readonly storage?: KaafilStorageAdapter;
885
+ readonly blob?: {
886
+ readonly uploader: BlobUploader;
887
+ readonly source: BlobSource;
888
+ };
889
+ readonly conflictResolver?: ConflictResolver;
890
+ readonly readEntity?: EntityReader;
891
+ readonly offlineScope?: string;
892
+ readonly seedConfig?: {
893
+ readonly tenant?: ConfigDocument;
894
+ readonly agency?: ConfigDocument;
895
+ };
896
+ readonly defaultCapabilityViews?: DefaultCapabilityViews;
897
+ readonly children?: ReactNode;
898
+ }
899
+ type KaafilUIKitProviderCredentialProps = KaafilUIKitProviderCredential & KaafilUIKitProviderProps;
900
+ /**
901
+ * Composes `ErrorShell → ThemeShell → I18nShell → AppShell → SessionShell →
902
+ * OfflineShell → ConfigShell → BrandShell → TelemetryShell → {children}`,
903
+ * the locked order (`shells/README.md §2`). No `persona` prop, no `mode`
904
+ * prop, no `sessionEndpoint` — the credential union above is the only way
905
+ * in, as VALUES (`00-decisions.md` U-003, U-014).
906
+ */
907
+ declare function KaafilUIKitProvider(props: KaafilUIKitProviderCredentialProps): ReactNode;
908
+
909
+ /**
910
+ * A type-keyed registry: `register`/`resolve`, resolved by lookup, never a
911
+ * `switch`/`if-else` chain (`09-registries.md §1` rule 1). `resolve` is
912
+ * documented as "internal; called by the composite that owns the registry
913
+ * per row/item/kind it renders" (§1's registration-signature note) — this
914
+ * factory does not itself enforce that a consumer never calls `resolve`
915
+ * from outside a composite, because there is no way to do so at the type
916
+ * level without either a second module boundary per registry or a runtime
917
+ * check with no way to tell "composite" from "integrator code" apart; the
918
+ * convention is enforced by which files ever call it, not by this factory.
919
+ */
920
+ interface Registry<TKey extends string, THandler> {
921
+ /**
922
+ * Registers `handler` under `key`. Rule 2 (`09-registries.md §1`): the
923
+ * LAST registration for a given key wins — a custom registration
924
+ * overwrites a built-in one, and a re-registration of an already-custom
925
+ * key overwrites that too. There is no "first registration wins" mode and
926
+ * no error on re-registering an existing key, because tier-4/5
927
+ * customization (`04-customization-ladder.md`) depends on exactly this
928
+ * being silent and total.
929
+ */
930
+ register(key: TKey, handler: THandler): void;
931
+ /**
932
+ * Resolves `key` to its registered handler, or the `default` fallback
933
+ * (rule 3) when `key` has no registration — because the engine shipped a
934
+ * new kind this build predates, or an integrator's custom kind rode an
935
+ * key nothing here anticipated. Never throws and never returns
936
+ * `undefined` — the fallback registration (seeded at `createRegistry()`
937
+ * time) guarantees a resolve is always total.
938
+ */
939
+ resolve(key: TKey): THandler;
940
+ /**
941
+ * Every key currently registered, fallback excluded — for a composite
942
+ * that needs to enumerate what's registered (a settings UI listing
943
+ * overridable kinds), never for resolving one row's dispatch (that is
944
+ * always `resolve`, so a row from an unregistered kind still renders).
945
+ */
946
+ registeredKeys(): readonly TKey[];
947
+ }
948
+ interface CreateRegistryOptions<THandler> {
949
+ /**
950
+ * The `default` registration (`09-registries.md §1` rule 3: "the fallback
951
+ * is itself a registration under a reserved `default` key, so it inherits
952
+ * the same override mechanism as every other entry rather than being
953
+ * special-cased in the resolver"). A consumer MAY later call
954
+ * `register('default' as TKey, …)` to replace it wholesale, the same way
955
+ * any other key can be overridden — this factory does not special-case
956
+ * that call either.
957
+ */
958
+ readonly fallback: THandler;
959
+ }
960
+ /**
961
+ * Builds one registry instance. `TKey` is left to the caller as a string
962
+ * literal union (`FormFieldKind`, `BlockerKey`, …) or a plain `string` for a
963
+ * genuinely open set — this factory does not require the union to be
964
+ * closed, which is exactly what lets a consumer "register a new key without
965
+ * a PR" against this package.
966
+ */
967
+ declare function createRegistry<TKey extends string, THandler>(options: CreateRegistryOptions<THandler>): Registry<TKey, THandler>;
968
+
969
+ /** A desk/field screen scoped to one trip. `tripRef` is the host's own
970
+ * external id OR a Kaafil id — every `:ref` route accepts either, and the
971
+ * UIKit never asks a host to resolve one into the other first. */
972
+ interface TripScopedParams {
973
+ readonly tripRef: string;
974
+ /** One of the trip workspace's own tab keys. Optional: absent means the
975
+ * surface picks its own landing tab. */
976
+ readonly initialTab?: KaafilTripTabKey;
977
+ }
978
+ /** An agency-wide desk screen. There is deliberately no `agencyRef`: scope
979
+ * comes from the open credential, never a route param (`00-decisions.md`
980
+ * U-003). */
981
+ interface AgencyScopedParams {
982
+ readonly initialPane?: string;
983
+ }
984
+ /** The public share surface. `token` is the opaque value from the share
985
+ * link's own URL — never a traveller id, never a trip ref. */
986
+ interface ShareParams {
987
+ readonly token: string;
988
+ }
989
+ /** The two props introduced by the deep-link table itself. Both are bare
990
+ * props on the receiving screen — they carry scope and behaviour, not
991
+ * presentation, so neither is a `components`/`*Slot` key. */
992
+ interface DeepLinkParams {
993
+ /** The row the destination must scroll to and focus on arrival. */
994
+ readonly highlightId?: string;
995
+ /** A named filter preset the destination flow already exposes. The screen
996
+ * passes it straight through; the UIKit has no filter language of its
997
+ * own. */
998
+ readonly presetFilter?: string;
999
+ }
1000
+ type KaafilTripTabKey = 'itinerary' | 'pickups' | 'rooming' | 'vehicles' | 'vendors' | 'payments' | 'expenses' | 'float' | 'checklist' | 'forms';
1001
+ /**
1002
+ * The engine's own route hints (`closing-day.constants.ts#
1003
+ * CLOSEOUT_BLOCKER_ACTION_KEYS`), and the value a caller routes on.
1004
+ *
1005
+ * ROUTE ON `actionKey`, NEVER ON `key` OR THE COPY. The engine says so
1006
+ * outright — "the route hint the FE navigates on, so it never parses copy"
1007
+ * — and the titles are explicitly free to be reworded.
1008
+ */
1009
+ type CloseoutActionKey = 'open_checklist' | 'open_collections' | 'open_expenses' | 'open_float' | 'open_forms' | 'open_itinerary' | 'open_mandatory_checklist' | 'open_past_itinerary' | 'open_pickups' | 'open_rooming' | 'open_seating' | 'open_vendors';
1010
+ /** The blocker keys the engine can emit (`CloseoutBlockerKey`). Accepted as
1011
+ * an optional refinement below — see `resolveBlockerDeepLink`. */
1012
+ type CloseoutBlockerKey = 'balance_due' | 'missing_receipt' | 'float_not_returned' | 'open_mandatory_checklist' | 'required_form_gap' | 'unassigned_rooming' | 'unseated_travellers' | 'pickup_no_show' | 'reimbursements_pending' | 'unrated_vendors' | 'open_past_itinerary';
1013
+ /**
1014
+ * `actionKey` → the tab that clears the row.
1015
+ *
1016
+ * A type-keyed record (hard rule #8), total over the union so a new engine
1017
+ * action key is a compile error here rather than a row whose jump silently
1018
+ * does nothing.
1019
+ */
1020
+ declare const CLOSEOUT_ACTION_TAB: Readonly<Record<CloseoutActionKey, KaafilTripTabKey>>;
1021
+ /** What a caller should do with a blocker row's action. */
1022
+ interface BlockerDeepLink extends DeepLinkParams {
1023
+ readonly tab: KaafilTripTabKey;
1024
+ }
1025
+ interface ResolveBlockerInput {
1026
+ /** The engine's route hint. The one required field. */
1027
+ readonly actionKey: string;
1028
+ /** The blocker's own key, when the caller has the whole row. Refines the
1029
+ * filter only; it never changes the tab. */
1030
+ readonly key?: string;
1031
+ /** Passed through untouched when the caller knows which row to focus —
1032
+ * typically only when the blocker's `count` is 1. */
1033
+ readonly highlightId?: string;
1034
+ }
1035
+ /**
1036
+ * Resolve one blocker row into a destination, or `null` for a row that has
1037
+ * nowhere to send anyone.
1038
+ *
1039
+ * `null` IS A REAL ANSWER AND MUST BE HANDLED — render the row's own
1040
+ * statement with no jump, never a control that goes nowhere. It happens for
1041
+ * an `actionKey` this build has no tab for (a later engine adding one), and
1042
+ * for a tab this trip does not have.
1043
+ *
1044
+ * ── TWO ROWS THE SPEC AND THE ENGINE DISAGREE ABOUT ──────────────────────
1045
+ *
1046
+ * `shared/00-routing-and-deeplinks.md §3` says `reimbursements_pending`
1047
+ * "never navigates" (resolved by CRM back-office, out of band) and that
1048
+ * `unrated_vendors` is unbuildable (`GAP-005`). The ENGINE assigns both an
1049
+ * `actionKey` anyway — `open_expenses` and `open_vendors` — on its own
1050
+ * stated principle that "a row pointing at NO screen is a row a manager
1051
+ * cannot act on".
1052
+ *
1053
+ * This follows the engine, because the engine is what actually ships the
1054
+ * row, and both destinations are defensible: the expenses tab shows claim
1055
+ * status read-only, which IS where you look to see where a reimbursement
1056
+ * got to, and the vendors tab is where a rating would live. In practice
1057
+ * neither costs anything — `unrated_vendors` is never emitted at all (the
1058
+ * engine has no vendor-rating source to probe), and `reimbursements_pending`
1059
+ * lands unfiltered by the note in `BLOCKER_PRESET_FILTER`.
1060
+ */
1061
+ declare function resolveBlockerDeepLink(input: ResolveBlockerInput): BlockerDeepLink | null;
1062
+ /**
1063
+ * A deep link → query params, for a host that wants to put one in a URL.
1064
+ *
1065
+ * Returns a FRESH `URLSearchParams` carrying only this link's own keys — it
1066
+ * never mutates an input, so a caller merges it into their own query string
1067
+ * on their own terms rather than having this decide what else survives.
1068
+ */
1069
+ declare function toSearchParams(link: Partial<BlockerDeepLink>): URLSearchParams;
1070
+ /**
1071
+ * Query params → the props a surface takes, or `null` when the query carries
1072
+ * no Kaafil deep link at all.
1073
+ *
1074
+ * `null` rather than an empty object, so `if (link)` is enough to tell "this
1075
+ * URL addresses a Kaafil surface" from "it does not" — a host's own router
1076
+ * will hand this every query string it sees, most of which are none of our
1077
+ * business.
1078
+ *
1079
+ * AN UNKNOWN TAB IS DROPPED, not passed through. The value came from a URL,
1080
+ * which is user-editable; handing an arbitrary string to a surface as a tab
1081
+ * key would put unvalidated input into a registry lookup. `highlightId` and
1082
+ * `presetFilter` ARE passed through as-is — they are opaque by contract, and
1083
+ * the destination flow is what decides whether either matches anything.
1084
+ */
1085
+ declare function fromSearchParams(params: URLSearchParams): (DeepLinkParams & {
1086
+ readonly initialTab?: KaafilTripTabKey;
1087
+ }) | null;
1088
+ /** Narrows a string off a URL to the shared tab vocabulary. Exported because
1089
+ * a host validating its own route params wants the same check. */
1090
+ declare function isTripTabKey(value: string | null | undefined): value is KaafilTripTabKey;
1091
+
1092
+ type AppShellFamily = 'admin' | 'manager' | 'traveller';
1093
+ type AppShellFrame = 'desk' | 'field' | 'document';
1094
+ interface AppShellProps {
1095
+ readonly frame?: AppShellFrame;
1096
+ readonly family?: AppShellFamily;
1097
+ readonly isolation?: 'scoped' | 'shadow';
1098
+ readonly navSlot?: ReactNode;
1099
+ readonly actionBarSlot?: ReactNode;
1100
+ readonly connectivitySlot?: ReactNode;
1101
+ readonly children?: ReactNode;
1102
+ /**
1103
+ * Per-instance `--kf-*` token overrides (`00-decisions.md` U-019). Layer-3
1104
+ * tokens are per component TYPE; this is how one INSTANCE differs from
1105
+ * another without restyling every sibling. On a shell this is also the
1106
+ * per-SUBTREE scope: custom properties inherit, so tokens set here reach
1107
+ * every descendant — including across the shadow-DOM boundary in
1108
+ * `isolation="shadow"` mode, since custom-property inheritance follows
1109
+ * the flat tree from a shadow host into its shadow root (unlike ordinary
1110
+ * selector-based CSS, which does not cross that boundary). `.kf-root`
1111
+ * here is either the shadow host itself, or a light-DOM ancestor of it —
1112
+ * both cases inherit normally, so `style` lands once, on this outermost
1113
+ * node, never needing to be re-applied inside `ShadowIsolationBoundary`.
1114
+ */
1115
+ readonly style?: KaafilTokenOverrides;
1116
+ }
1117
+ /**
1118
+ * Establishes `.kf-root`, the resolved `frame` shape, the CSS-isolation
1119
+ * rung, and the container-query root. Renders unconditionally — never
1120
+ * withholds `{children}` behind a loading gate (`shells/README.md §1`).
1121
+ */
1122
+ declare function AppShell({ frame, family, isolation, navSlot, actionBarSlot, connectivitySlot, children, style, }: AppShellProps): ReactNode;
1123
+
1124
+ type ErrorShellClassification = 'retryable' | 'parked' | 'isolation';
1125
+ interface ErrorShellFallbackContext {
1126
+ readonly classification: ErrorShellClassification;
1127
+ readonly code: string | undefined;
1128
+ }
1129
+ interface ErrorShellProps {
1130
+ readonly fallbackSlot?: (ctx: ErrorShellFallbackContext) => ReactNode;
1131
+ readonly children?: ReactNode;
1132
+ }
1133
+ interface ErrorShellState {
1134
+ readonly caught: {
1135
+ readonly error: unknown;
1136
+ } | null;
1137
+ readonly retryKey: number;
1138
+ }
1139
+ /**
1140
+ * Catches a thrown render error from anything beneath it. Never consumes
1141
+ * `ThemeShell`/`I18nShell` context in its own fallback, never retries a
1142
+ * `parked`/`isolation` classification, and never offers an admin override
1143
+ * for a classified `423`/`402`/`422`.
1144
+ */
1145
+ declare class ErrorShell extends Component<ErrorShellProps, ErrorShellState> {
1146
+ state: ErrorShellState;
1147
+ static getDerivedStateFromError(error: unknown): Partial<ErrorShellState>;
1148
+ private handleRetry;
1149
+ render(): ReactNode;
1150
+ }
1151
+
1152
+ type HostCssHazard = 'global-box-sizing-reset' | 'global-button-input-unset' | 'tailwind-preflight-after-ours' | 'global-margin-padding-reset' | 'conflicting-layer-order' | 'global-img-max-width' | 'host-css-reset-library' | 'global-focus-outline-removed';
1153
+ interface HostCssCompatibilityProbeProps {
1154
+ /**
1155
+ * Per-instance `--kf-*` token overrides (`00-decisions.md` U-019). Layer-3
1156
+ * tokens are per component TYPE; this is how one INSTANCE differs from
1157
+ * another without restyling every sibling.
1158
+ */
1159
+ readonly style?: KaafilTokenOverrides;
1160
+ }
1161
+ /** Dev-only per `05-theming-and-branding.md §7` — a no-op in production,
1162
+ * with a dev-only warning at the ONE other place `isDevelopment()` reads
1163
+ * false-negatively (a production build accidentally shipping this probe). */
1164
+ declare function HostCssCompatibilityProbe({ style, }?: HostCssCompatibilityProbeProps): ReactNode;
1165
+
1166
+ type ChromeLocale = 'en' | 'hi';
1167
+
1168
+ interface I18nContextValue {
1169
+ readonly locale: ChromeLocale;
1170
+ /** The host's own, possibly-unrecognised BCP-47 tag — kept alongside the
1171
+ * resolved `ChromeLocale` so a consumer can tell "we're rendering en
1172
+ * because the host asked for en" apart from "we're rendering en because
1173
+ * the host's tag has no shipped sibling catalog" without this context
1174
+ * lying about which locale is actually active. */
1175
+ readonly requestedLocale: string;
1176
+ readonly t: (key: string) => string;
1177
+ }
1178
+
1179
+ interface I18nShellProps {
1180
+ /** BCP-47 tag. No `Accept-Language` sniff, no `Traveller.locale` read —
1181
+ * the host is the one party that already knows which locale its user or
1182
+ * traveller wants (`13-i18n-and-formatting.md §3.3`). */
1183
+ readonly locale?: string;
1184
+ readonly children?: ReactNode;
1185
+ }
1186
+ /**
1187
+ * Resolves the active locale's chrome catalog synchronously — the catalog
1188
+ * ships inside the package bundle (`shells/07-i18n-shell.md`'s own "Offline"
1189
+ * field), so there is no loading state for a descendant to observe.
1190
+ */
1191
+ declare function I18nShell({ locale, children }: I18nShellProps): ReactNode;
1192
+ /**
1193
+ * `I18nShell`'s own reader — shell-owned, not a `08-core-hooks.md` catalog
1194
+ * entry (no chrome-translation hook is named there; component authoring,
1195
+ * this hook's eventual consumer, is out of scope for this phase). Mirrors
1196
+ * `BrandShell`'s `useKaafilShareMeta()` in kind: a read-only hook a shell
1197
+ * exposes alongside itself. Throws outside the provider tree, the same
1198
+ * "no provider, no default" contract every `/core` context follows.
1199
+ */
1200
+ declare function useKaafilChrome(): I18nContextValue;
1201
+
1202
+ interface KaafilSessionProbeProps {
1203
+ /** The host's own intended environment — cross-checked against the
1204
+ * opened credential's actual `environment` to catch "wrong environment"/
1205
+ * "test key on live" (`shells/00-kaafil-uikit-provider.md`). */
1206
+ readonly expectedEnvironment?: 'live' | 'test';
1207
+ /**
1208
+ * Per-instance `--kf-*` token overrides (`00-decisions.md` U-019). Layer-3
1209
+ * tokens are per component TYPE; this is how one INSTANCE differs from
1210
+ * another without restyling every sibling.
1211
+ */
1212
+ readonly style?: KaafilTokenOverrides;
1213
+ }
1214
+ /**
1215
+ * When green: a card naming credential kind, environment, and status. When
1216
+ * red: names exactly ONE of the five documented failure modes this probe
1217
+ * exists to distinguish — this build can only detect the two that are
1218
+ * directly readable off `useSession()`'s derived shape (an expired/rejected
1219
+ * credential, and an environment mismatch); "clock skew" and "CORS" need a
1220
+ * live network probe this component does not attempt on its own, so they
1221
+ * are named here as documented, undetected-by-this-build failure modes
1222
+ * rather than silently omitted from the contract.
1223
+ */
1224
+ declare function KaafilSessionProbe({ expectedEnvironment, style, }: KaafilSessionProbeProps): ReactNode;
1225
+
1226
+ interface OfflineShellProps {
1227
+ readonly storage: KaafilStorageAdapter;
1228
+ readonly blob?: {
1229
+ readonly uploader: BlobUploader;
1230
+ readonly source: BlobSource;
1231
+ };
1232
+ readonly conflictResolver?: ConflictResolver;
1233
+ readonly readEntity?: EntityReader;
1234
+ /** Escalation-tier override — see `shells/03-offline-shell.md`'s own
1235
+ * warning about two concurrently-open credentials sharing one browser's
1236
+ * storage. Defaults to a scope derived from persona + agencyRef, never
1237
+ * the credential secret itself. */
1238
+ readonly scope?: string;
1239
+ readonly children?: ReactNode;
1240
+ }
1241
+ /**
1242
+ * Opens the one `OfflineEngine` for the open session's credential, once
1243
+ * `SessionShell`'s credential exists — `OpenOfflineOptions` cannot be shaped
1244
+ * before that (`shells/README.md §2` row 6's rationale). Closes the previous
1245
+ * engine and opens a fresh one on a genuine credential change (a different
1246
+ * agency, a re-minted share token); an ordinary silent token rotation never
1247
+ * reaches this effect at all (`shells/03-offline-shell.md`'s "Credential
1248
+ * binding and credential change" section).
1249
+ */
1250
+ declare function OfflineShell({ storage, blob, conflictResolver, readEntity, scope, children, }: OfflineShellProps): ReactNode;
1251
+
1252
+ interface ThemeShellProps {
1253
+ readonly theme?: 'light' | 'dark' | 'system';
1254
+ readonly density?: 'compact' | 'default' | 'comfortable';
1255
+ readonly children?: ReactNode;
1256
+ }
1257
+ /**
1258
+ * Resolves theme/density synchronously, during render — not behind a
1259
+ * `useEffect`, so there is no theme-loading flash for a descendant to
1260
+ * observe (`05-theme-shell.md`'s "no theme-loading state" contract). Only
1261
+ * `theme="system"` needs a live subscription, to follow the OS preference
1262
+ * across a change while mounted; an explicit `"light"`/`"dark"` is used
1263
+ * immediately and never touches `matchMedia` at all.
1264
+ */
1265
+ declare function ThemeShell({ theme, density, children }: ThemeShellProps): ReactNode;
1266
+
1267
+ export { type AddTravellerNoteInput, type AgencyAdminMeReady, type AgencyAdminMeResult, type AgencyManagerProfileResult, type AgencyScopedParams, AppShell, type AppShellFamily, type AppShellFrame, type AppShellProps, type BlobStatusResult, type BlockerDeepLink, type BookingsReady, BrandShell, type BrandShellProps, CLOSEOUT_ACTION_TAB, type CapabilityStatusesResult, CapabilityTriple, type CloseoutActionKey, type CloseoutBlockerKey, type ConfigDocument, ConfigShell, type ConfigShellProps, ConflictRecord, type ConflictsResult, type CreateRegistryOptions, type DeepLinkParams, type DefaultCapabilityViews, DomainHookResult, ErrorClassification, ErrorShell, type ErrorShellClassification, type ErrorShellFallbackContext, type ErrorShellProps, type FeedbackNpsReady, type FeedbackNpsScope, HostCssCompatibilityProbe, type HostCssHazard, I18nShell, type I18nShellProps, type JourneyResult, type KaafilBrandAssets, KaafilSessionProbe, type KaafilSessionProbeProps, type KaafilTripTabKey, KaafilUIKitProvider, type KaafilUIKitProviderCredential, type KaafilUIKitProviderCredentialProps, type KaafilUIKitProviderProps, type ManagerAgencyProfileRead, type ManagersResult, NotImplementedError, type OfflineMutationConfig, type OfflineMutationResult, type OfflineMutationStatus, OfflineShell, type OfflineShellProps, type OutboxStatusOptions, type OutboxStatusResult, type Persona, type Registry, type ResolveBlockerInput, type ServerTimeApi, SessionShell, type SessionShellCredential, type SessionShellProps, type SessionState, type ShareFormAnswer, type ShareFormListItem, type ShareFormWriteInput, type ShareFormsListStatus, type ShareFormsResult, type ShareFormsWriteStatus, type ShareLinkRow, type ShareLinksResult, type ShareMeta, type ShareParams, type ShareSectionKey, type ShareSections, type ShareSnapshotOptions, type ShareSnapshotResult, type ShareSnapshotTrip, type SnapshotEntityResult, type SnapshotListResult, StalenessState, type TelemetryEvent, TelemetryShell, type TelemetryShellProps, ThemeShell, type ThemeShellProps, type TravellerFormResponseRow, type TravellerProfileRead, type TravellerProfileResult, type TreksReady, type TripManagerRow, type TripScopedParams, createRegistry, fromSearchParams, isTripTabKey, resolveBlockerDeepLink, toSearchParams, useAgencyAdminMe, useAgencyManagerProfile, useBlobStatus, useBookings, useCapabilities, useCapability, useCapabilityStatuses, useConflicts, useDefaultCapabilityViews, useFeedbackNps, useJourney, useKaafilBrand, useKaafilChrome, useKaafilShareMeta, useManagers, useOfflineEngine, useOfflineEvent, useOfflineMutation, useOutboxStatus, usePersona, useResolvedSetting, useServerTime, useSession, useShareForms, useShareLinks, useShareSnapshot, useSnapshotEntity, useSnapshotList, useStaleness, useTravellerFormResponses, useTravellerProfile, useTreks };