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.
- package/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/dist/OfflineFallbackScreen-B5dfaJBN.d.ts +37 -0
- package/dist/OfflineFallbackScreen-Ctm7nBa-.d.cts +37 -0
- package/dist/SessionExpiredScreen-CytQNuak.d.cts +50 -0
- package/dist/SessionExpiredScreen-D8GHyZc3.d.ts +50 -0
- package/dist/admin/index.cjs +23779 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +1157 -0
- package/dist/admin/index.d.ts +1157 -0
- package/dist/admin/index.js +23724 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/chunk-6FPAGGFL.js +6028 -0
- package/dist/chunk-6FPAGGFL.js.map +1 -0
- package/dist/chunk-ACVU55PZ.cjs +294 -0
- package/dist/chunk-ACVU55PZ.cjs.map +1 -0
- package/dist/chunk-EM7JV3D7.cjs +6051 -0
- package/dist/chunk-EM7JV3D7.cjs.map +1 -0
- package/dist/chunk-IIPQ4JNC.js +287 -0
- package/dist/chunk-IIPQ4JNC.js.map +1 -0
- package/dist/chunk-LL243STJ.cjs +2195 -0
- package/dist/chunk-LL243STJ.cjs.map +1 -0
- package/dist/chunk-PJRZCOFF.cjs +958 -0
- package/dist/chunk-PJRZCOFF.cjs.map +1 -0
- package/dist/chunk-PTQ2CJKE.cjs +40 -0
- package/dist/chunk-PTQ2CJKE.cjs.map +1 -0
- package/dist/chunk-PZKXLQSU.js +10 -0
- package/dist/chunk-PZKXLQSU.js.map +1 -0
- package/dist/chunk-QHUDOVKW.cjs +111 -0
- package/dist/chunk-QHUDOVKW.cjs.map +1 -0
- package/dist/chunk-TOBZFADB.cjs +12 -0
- package/dist/chunk-TOBZFADB.cjs.map +1 -0
- package/dist/chunk-UUGMNP27.js +936 -0
- package/dist/chunk-UUGMNP27.js.map +1 -0
- package/dist/chunk-UYJ6LIWP.js +2154 -0
- package/dist/chunk-UYJ6LIWP.js.map +1 -0
- package/dist/chunk-VHTSTUCJ.cjs +346 -0
- package/dist/chunk-VHTSTUCJ.cjs.map +1 -0
- package/dist/chunk-WUAFL77X.js +340 -0
- package/dist/chunk-WUAFL77X.js.map +1 -0
- package/dist/chunk-XI7MK4Y7.js +38 -0
- package/dist/chunk-XI7MK4Y7.js.map +1 -0
- package/dist/chunk-YDGR7JSY.js +107 -0
- package/dist/chunk-YDGR7JSY.js.map +1 -0
- package/dist/core/index.cjs +1254 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +1267 -0
- package/dist/core/index.d.ts +1267 -0
- package/dist/core/index.js +995 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.css +1 -0
- package/dist/manager/index.cjs +13562 -0
- package/dist/manager/index.cjs.map +1 -0
- package/dist/manager/index.d.cts +1054 -0
- package/dist/manager/index.d.ts +1054 -0
- package/dist/manager/index.js +13518 -0
- package/dist/manager/index.js.map +1 -0
- package/dist/testing/index.cjs +221 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +269 -0
- package/dist/testing/index.d.ts +269 -0
- package/dist/testing/index.js +208 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokenStyle-CToG5oOS.d.cts +3 -0
- package/dist/tokenStyle-CToG5oOS.d.ts +3 -0
- package/dist/traveller/index.cjs +1799 -0
- package/dist/traveller/index.cjs.map +1 -0
- package/dist/traveller/index.d.cts +58 -0
- package/dist/traveller/index.d.ts +58 -0
- package/dist/traveller/index.js +1789 -0
- package/dist/traveller/index.js.map +1 -0
- package/dist/useAgencyTrip-BUYSIaC2.d.cts +454 -0
- package/dist/useAgencyTrip-BzyJB1D9.d.ts +454 -0
- package/dist/useNotifications-BZW1Z_ur.d.cts +356 -0
- package/dist/useNotifications-D262i5BQ.d.ts +356 -0
- package/dist/useVendors-DXOuZWhZ.d.cts +784 -0
- package/dist/useVendors-DXOuZWhZ.d.ts +784 -0
- package/package.json +95 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkTOBZFADB_cjs = require('./chunk-TOBZFADB.cjs');
|
|
4
|
+
var chunkPTQ2CJKE_cjs = require('./chunk-PTQ2CJKE.cjs');
|
|
5
|
+
var chunkEM7JV3D7_cjs = require('./chunk-EM7JV3D7.cjs');
|
|
6
|
+
var client = require('kaafil-js/client');
|
|
7
|
+
var react = require('react');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function deriveListStatus(forms, listFailed) {
|
|
11
|
+
if (forms === void 0) {
|
|
12
|
+
return listFailed ? "not-found" : "loading";
|
|
13
|
+
}
|
|
14
|
+
return forms.length === 0 ? "list-empty" : "list-populated";
|
|
15
|
+
}
|
|
16
|
+
function deriveWriteStatus(revoked, saveOp, submitOp, isOnline) {
|
|
17
|
+
if (revoked) {
|
|
18
|
+
return { status: "dropped-on-revoke", op: submitOp ?? saveOp };
|
|
19
|
+
}
|
|
20
|
+
const active = submitOp !== void 0 ? { op: submitOp, kind: "submit" } : void 0;
|
|
21
|
+
const fallback = active ?? (saveOp !== void 0 ? { op: saveOp, kind: "save" } : void 0);
|
|
22
|
+
if (fallback === void 0) {
|
|
23
|
+
return { status: "idle", op: void 0 };
|
|
24
|
+
}
|
|
25
|
+
const { op, kind } = fallback;
|
|
26
|
+
if (op.status === "parked") {
|
|
27
|
+
if (op.parkReason?.code === "CONFLICT_VERSION") {
|
|
28
|
+
return { status: "conflicted", op };
|
|
29
|
+
}
|
|
30
|
+
if (op.parkReason?.code === "SUBJECT_REQUIRED") {
|
|
31
|
+
return { status: "subject-required", op };
|
|
32
|
+
}
|
|
33
|
+
return { status: "queued-offline", op };
|
|
34
|
+
}
|
|
35
|
+
if (op.status === "done") {
|
|
36
|
+
return { status: kind === "submit" ? "submitted" : "idle", op };
|
|
37
|
+
}
|
|
38
|
+
if (!isOnline) {
|
|
39
|
+
return { status: "queued-offline", op };
|
|
40
|
+
}
|
|
41
|
+
return { status: kind === "submit" ? "submitting" : "saving", op };
|
|
42
|
+
}
|
|
43
|
+
function useShareForms(token) {
|
|
44
|
+
const client$1 = chunkEM7JV3D7_cjs.useKaafilClient();
|
|
45
|
+
const persona = chunkEM7JV3D7_cjs.usePersona();
|
|
46
|
+
const engine = chunkTOBZFADB_cjs.useOfflineEngine();
|
|
47
|
+
const { now, sinceLastSync } = chunkPTQ2CJKE_cjs.useServerTime();
|
|
48
|
+
const serverTimeKnown = sinceLastSync !== void 0;
|
|
49
|
+
const [forms, setForms] = react.useState(void 0);
|
|
50
|
+
const [serverTime, setServerTime] = react.useState(void 0);
|
|
51
|
+
const [listFailed, setListFailed] = react.useState(false);
|
|
52
|
+
const [revoked, setRevoked] = react.useState(false);
|
|
53
|
+
const [isOnline, setIsOnline] = react.useState(
|
|
54
|
+
() => typeof navigator === "undefined" || navigator.onLine
|
|
55
|
+
);
|
|
56
|
+
const fetchList = react.useCallback(async () => {
|
|
57
|
+
try {
|
|
58
|
+
const response = await client$1.share.forms.list({ token });
|
|
59
|
+
setForms(response.items);
|
|
60
|
+
setServerTime(response.meta.serverTime);
|
|
61
|
+
setListFailed(false);
|
|
62
|
+
} catch {
|
|
63
|
+
setListFailed(true);
|
|
64
|
+
}
|
|
65
|
+
}, [client$1, token]);
|
|
66
|
+
react.useEffect(() => {
|
|
67
|
+
if (persona !== "share") {
|
|
68
|
+
if (chunkEM7JV3D7_cjs.isDevelopment()) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"useShareForms() was called from a manager/agencyAdmin session \u2014 this hook is `share`-only (families/traveller/02-forms.md, `ShareFormsSection`)."
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
void fetchList();
|
|
76
|
+
}, [persona, fetchList]);
|
|
77
|
+
react.useEffect(() => {
|
|
78
|
+
if (typeof window === "undefined") {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const handleOnline = () => setIsOnline(true);
|
|
82
|
+
const handleOffline = () => setIsOnline(false);
|
|
83
|
+
window.addEventListener("online", handleOnline);
|
|
84
|
+
window.addEventListener("offline", handleOffline);
|
|
85
|
+
return () => {
|
|
86
|
+
window.removeEventListener("online", handleOnline);
|
|
87
|
+
window.removeEventListener("offline", handleOffline);
|
|
88
|
+
};
|
|
89
|
+
}, []);
|
|
90
|
+
chunkEM7JV3D7_cjs.useOfflineEvent("share.revoked", () => {
|
|
91
|
+
setRevoked(true);
|
|
92
|
+
for (const op of engine.outbox.all()) {
|
|
93
|
+
if (op.tripRef === token && (op.status === "pending" || op.status === "parked")) {
|
|
94
|
+
void engine.outbox.drop(op.id);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const saveMutation = chunkEM7JV3D7_cjs.useOfflineMutation({
|
|
99
|
+
tripRef: token,
|
|
100
|
+
enqueue: (mutationEngine, input) => {
|
|
101
|
+
const query = input.travellerRef !== void 0 ? `?travellerRef=${encodeURIComponent(input.travellerRef)}` : "";
|
|
102
|
+
return mutationEngine.enqueue({
|
|
103
|
+
tripRef: token,
|
|
104
|
+
method: client.SyncOpMethod.Post,
|
|
105
|
+
path: `/api/v1/share/${token}/forms/${input.formId}/save${query}`,
|
|
106
|
+
// Hardcoded literal, not imported: `OperationId` is not exported
|
|
107
|
+
// from `kaafil-js/client`'s public surface (same reasoning
|
|
108
|
+
// `useRooming.ts` documents). Value verified against
|
|
109
|
+
// `kaafil-js/src/generated/security.ts` this session:
|
|
110
|
+
// `OperationId.ShareSaveForm === 'shareSaveForm'`.
|
|
111
|
+
operationId: "shareSaveForm",
|
|
112
|
+
body: {
|
|
113
|
+
answers: input.answers,
|
|
114
|
+
clientToken: input.clientToken,
|
|
115
|
+
...input.startNew !== void 0 ? { startNew: input.startNew } : {},
|
|
116
|
+
...input.isAnonymous !== void 0 ? { isAnonymous: input.isAnonymous } : {}
|
|
117
|
+
},
|
|
118
|
+
// The server-anchored "now" this write was created at — never
|
|
119
|
+
// `Date.now()`/`new Date()` (hard rule #6) — `EnqueueWriteOptions
|
|
120
|
+
// .enqueuedAt`'s own doc comment: "client-attested and only ever
|
|
121
|
+
// used by the server to REFUSE" (the 7-day post-expiry grace
|
|
122
|
+
// check). Optional on the wire, and genuinely omitted here on a
|
|
123
|
+
// cold session: `useServerTime().now()` throws before this
|
|
124
|
+
// session's very first `snapshot.updated` event has landed (its
|
|
125
|
+
// own documented behaviour, guarded at the call site exactly as
|
|
126
|
+
// its header instructs) — a share write attempted in that narrow
|
|
127
|
+
// window still enqueues durably, just without a client-attested
|
|
128
|
+
// stamp for the server's grace check to read.
|
|
129
|
+
...serverTimeKnown ? { enqueuedAt: now() } : {}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
const submitMutation = chunkEM7JV3D7_cjs.useOfflineMutation({
|
|
134
|
+
tripRef: token,
|
|
135
|
+
enqueue: (mutationEngine, input) => {
|
|
136
|
+
const query = input.travellerRef !== void 0 ? `?travellerRef=${encodeURIComponent(input.travellerRef)}` : "";
|
|
137
|
+
return mutationEngine.enqueue({
|
|
138
|
+
tripRef: token,
|
|
139
|
+
method: client.SyncOpMethod.Post,
|
|
140
|
+
path: `/api/v1/share/${token}/forms/${input.formId}/submit${query}`,
|
|
141
|
+
operationId: "shareSubmitForm",
|
|
142
|
+
body: {
|
|
143
|
+
answers: input.answers,
|
|
144
|
+
clientToken: input.clientToken,
|
|
145
|
+
...input.startNew !== void 0 ? { startNew: input.startNew } : {},
|
|
146
|
+
...input.isAnonymous !== void 0 ? { isAnonymous: input.isAnonymous } : {}
|
|
147
|
+
},
|
|
148
|
+
// See the identical `save` enqueue above for why this is guarded.
|
|
149
|
+
...serverTimeKnown ? { enqueuedAt: now() } : {}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const renderForm = react.useCallback(
|
|
154
|
+
async (formId, options) => {
|
|
155
|
+
const response = await client$1.share.forms.render({
|
|
156
|
+
token,
|
|
157
|
+
formId,
|
|
158
|
+
...options?.travellerRef !== void 0 ? { travellerRef: options.travellerRef } : {}
|
|
159
|
+
});
|
|
160
|
+
return response;
|
|
161
|
+
},
|
|
162
|
+
[client$1, token]
|
|
163
|
+
);
|
|
164
|
+
const { status: writeStatus, op: writeOp } = deriveWriteStatus(
|
|
165
|
+
revoked,
|
|
166
|
+
saveMutation.op,
|
|
167
|
+
submitMutation.op,
|
|
168
|
+
isOnline
|
|
169
|
+
);
|
|
170
|
+
return {
|
|
171
|
+
status: deriveListStatus(forms, listFailed),
|
|
172
|
+
forms: forms ?? [],
|
|
173
|
+
serverTime,
|
|
174
|
+
refresh: fetchList,
|
|
175
|
+
renderForm,
|
|
176
|
+
save: saveMutation.mutate,
|
|
177
|
+
submit: submitMutation.mutate,
|
|
178
|
+
writeStatus,
|
|
179
|
+
writeOp
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
var NON_SECTION_KEYS = /* @__PURE__ */ new Set(["trip", "meta"]);
|
|
183
|
+
function useShareSnapshot(token, options) {
|
|
184
|
+
const client = chunkEM7JV3D7_cjs.useKaafilClient();
|
|
185
|
+
const persona = chunkEM7JV3D7_cjs.usePersona();
|
|
186
|
+
const engine = react.useContext(chunkEM7JV3D7_cjs.OfflineEngineContext);
|
|
187
|
+
const [sections, setSections] = react.useState(void 0);
|
|
188
|
+
const [trip, setTrip] = react.useState(void 0);
|
|
189
|
+
const [serverTime, setServerTime] = react.useState(void 0);
|
|
190
|
+
const [terminal, setTerminal] = react.useState(
|
|
191
|
+
void 0
|
|
192
|
+
);
|
|
193
|
+
react.useEffect(() => {
|
|
194
|
+
if (persona !== "share") {
|
|
195
|
+
if (chunkEM7JV3D7_cjs.isDevelopment()) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
"useShareSnapshot() was called from a manager/agencyAdmin session \u2014 this hook is `share`-only (08-core-hooks.md \xA73.1)."
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
let cancelled = false;
|
|
203
|
+
async function load() {
|
|
204
|
+
try {
|
|
205
|
+
const response = await client.share.snapshot({ token });
|
|
206
|
+
if (cancelled) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const map = /* @__PURE__ */ new Map();
|
|
210
|
+
for (const [key, value] of Object.entries(response)) {
|
|
211
|
+
if (!NON_SECTION_KEYS.has(key)) {
|
|
212
|
+
map.set(key, value);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
setSections(map);
|
|
216
|
+
setTrip(response.trip);
|
|
217
|
+
setServerTime(response.meta.serverTime);
|
|
218
|
+
} catch {
|
|
219
|
+
if (!cancelled) {
|
|
220
|
+
setTerminal("not-found");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
void load();
|
|
225
|
+
if (engine === null) {
|
|
226
|
+
return () => {
|
|
227
|
+
cancelled = true;
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const unsubscribeExpired = engine.events.on("share.expired", () => setTerminal("expired"));
|
|
231
|
+
const unsubscribeRevoked = engine.events.on("share.revoked", () => setTerminal("revoked"));
|
|
232
|
+
return () => {
|
|
233
|
+
cancelled = true;
|
|
234
|
+
unsubscribeExpired();
|
|
235
|
+
unsubscribeRevoked();
|
|
236
|
+
};
|
|
237
|
+
}, [client, persona, token, engine]);
|
|
238
|
+
if (terminal !== void 0) {
|
|
239
|
+
return { status: terminal, sections: sections ?? /* @__PURE__ */ new Map(), trip, serverTime };
|
|
240
|
+
}
|
|
241
|
+
if (sections === void 0) {
|
|
242
|
+
return { status: "loading", sections: /* @__PURE__ */ new Map(), trip: void 0, serverTime: void 0 };
|
|
243
|
+
}
|
|
244
|
+
return { status: "ready", sections, trip, serverTime };
|
|
245
|
+
}
|
|
246
|
+
var BrandAssetsContext = react.createContext(null);
|
|
247
|
+
function useKaafilBrand() {
|
|
248
|
+
return react.useContext(BrandAssetsContext) ?? {};
|
|
249
|
+
}
|
|
250
|
+
function resolveShareMeta(assets) {
|
|
251
|
+
return {
|
|
252
|
+
title: assets.appName,
|
|
253
|
+
favicon: assets.favicon,
|
|
254
|
+
ogTitle: assets.appName,
|
|
255
|
+
ogDescription: void 0,
|
|
256
|
+
ogImage: void 0,
|
|
257
|
+
// Share meta defaults to noindex — a share link is private-by-obscurity;
|
|
258
|
+
// indexing it is a disclosure bug, never the shipped default
|
|
259
|
+
// (`00-decisions.md` U-005).
|
|
260
|
+
robots: "noindex"
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function KaafilDocumentHead({ meta }) {
|
|
264
|
+
react.useEffect(() => {
|
|
265
|
+
if (meta.title !== void 0) {
|
|
266
|
+
document.title = meta.title;
|
|
267
|
+
}
|
|
268
|
+
if (meta.favicon !== void 0) {
|
|
269
|
+
let link = document.querySelector('link[rel="icon"][data-kf-brand]');
|
|
270
|
+
if (link === null) {
|
|
271
|
+
link = document.createElement("link");
|
|
272
|
+
link.rel = "icon";
|
|
273
|
+
link.setAttribute("data-kf-brand", "");
|
|
274
|
+
document.head.appendChild(link);
|
|
275
|
+
}
|
|
276
|
+
link.href = meta.favicon;
|
|
277
|
+
}
|
|
278
|
+
const setMeta = (name, content, attr = "name") => {
|
|
279
|
+
if (content === void 0) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
let tag = document.querySelector(`meta[${attr}="${name}"][data-kf-brand]`);
|
|
283
|
+
if (tag === null) {
|
|
284
|
+
tag = document.createElement("meta");
|
|
285
|
+
tag.setAttribute(attr, name);
|
|
286
|
+
tag.setAttribute("data-kf-brand", "");
|
|
287
|
+
document.head.appendChild(tag);
|
|
288
|
+
}
|
|
289
|
+
tag.content = content;
|
|
290
|
+
};
|
|
291
|
+
setMeta("og:title", meta.ogTitle, "property");
|
|
292
|
+
setMeta("og:description", meta.ogDescription, "property");
|
|
293
|
+
setMeta("og:image", meta.ogImage, "property");
|
|
294
|
+
setMeta("robots", meta.robots);
|
|
295
|
+
}, [meta]);
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
function BrandShell({
|
|
299
|
+
appName,
|
|
300
|
+
logo,
|
|
301
|
+
logoMark,
|
|
302
|
+
favicon,
|
|
303
|
+
headStrategy = "none",
|
|
304
|
+
children
|
|
305
|
+
}) {
|
|
306
|
+
const persona = chunkEM7JV3D7_cjs.usePersona();
|
|
307
|
+
const assets = react.useMemo(
|
|
308
|
+
() => ({ appName, logo, logoMark, favicon }),
|
|
309
|
+
[appName, logo, logoMark, favicon]
|
|
310
|
+
);
|
|
311
|
+
const rejectedOwnPage = headStrategy === "own-page" && persona !== "share";
|
|
312
|
+
react.useEffect(() => {
|
|
313
|
+
if (headStrategy === "own-page" && persona !== "share" && chunkEM7JV3D7_cjs.isDevelopment()) {
|
|
314
|
+
throw new Error(
|
|
315
|
+
`BrandShell: headStrategy="own-page" was requested under a non-share persona ("${persona}") \u2014 only the traveller/share surface may own document.head (00-decisions.md U-005). Falls back to "none" in production.`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
if (headStrategy === "none" && // `favicon` ONLY. `appName` is not a head-only prop: it is the last rung
|
|
319
|
+
// of the in-page brand fallback chain (partner logo -> neutral mark ->
|
|
320
|
+
// text-only appName, 05-theming-and-branding.md §8), so it does real
|
|
321
|
+
// work under every `headStrategy` and warning about it told integrators
|
|
322
|
+
// their correct code was wrong. `favicon` has no in-page use at all, so
|
|
323
|
+
// passing one under `'none'` genuinely is a no-op worth flagging.
|
|
324
|
+
favicon !== void 0 && chunkEM7JV3D7_cjs.isDevelopment()) {
|
|
325
|
+
console.warn(
|
|
326
|
+
'BrandShell: headStrategy="none" (the default) is a hard no-op \u2014 "favicon" was passed but document.head is never touched under "none" (00-decisions.md U-005). "appName" is unaffected: it still feeds the in-page brand fallback.'
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}, [headStrategy, persona, favicon]);
|
|
330
|
+
const shouldOwnPage = headStrategy === "own-page" && persona === "share" && !rejectedOwnPage;
|
|
331
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(BrandAssetsContext.Provider, { value: assets, children: [
|
|
332
|
+
shouldOwnPage ? /* @__PURE__ */ jsxRuntime.jsx(KaafilDocumentHead, { meta: resolveShareMeta(assets) }) : null,
|
|
333
|
+
children
|
|
334
|
+
] });
|
|
335
|
+
}
|
|
336
|
+
function useKaafilShareMeta(assets) {
|
|
337
|
+
return react.useMemo(() => resolveShareMeta(assets), [assets]);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
exports.BrandShell = BrandShell;
|
|
341
|
+
exports.useKaafilBrand = useKaafilBrand;
|
|
342
|
+
exports.useKaafilShareMeta = useKaafilShareMeta;
|
|
343
|
+
exports.useShareForms = useShareForms;
|
|
344
|
+
exports.useShareSnapshot = useShareSnapshot;
|
|
345
|
+
//# sourceMappingURL=chunk-VHTSTUCJ.cjs.map
|
|
346
|
+
//# sourceMappingURL=chunk-VHTSTUCJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/hooks/useShareForms.ts","../src/core/hooks/useShareSnapshot.ts","../src/core/shells/BrandShell.tsx"],"names":["client","useKaafilClient","usePersona","useOfflineEngine","useServerTime","useState","useCallback","useEffect","isDevelopment","useOfflineEvent","useOfflineMutation","SyncOpMethod","useContext","OfflineEngineContext","createContext","useMemo","jsxs"],"mappings":"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","file":"chunk-VHTSTUCJ.cjs","sourcesContent":["'use client';\n\n// `08-core-hooks.md §3` (new row — `ShareFormsSection`'s `Backed by`,\n// `ui-kit/families/traveller/02-forms.md`, cited): the write-back half of\n// the traveller share surface. `forms` is the ONLY write-back section on\n// the whole share surface; every other section is read-only through\n// `useShareSnapshot()` alone. This hook wraps the four dedicated\n// `shareAuth`-only routes `share.forms.{list,render,save,submit}`\n// (`kaafil-js/src/resources/share.ts`, verified this session).\n//\n// ── WHY THIS HOOK DOES NOT ALSO CALL `useShareSnapshot()` ──────────────────\n//\n// `02-forms.md`'s `Backed by` field says the section's own `/core` hook\n// \"composes both\" — the snapshot (for `forms` section presence) and the\n// dedicated routes (for the actual list/render/save/submit). This file\n// deliberately does NOT reproduce the snapshot fetch: `ShareFormsSection`\n// (future work, not this file) is only ever mounted once its own call to\n// `useShareSnapshot(token)` has already confirmed `sections.has('forms')`\n// (`01-share-sections.md`'s registry-driven mount rule) — duplicating that\n// fetch here would be a second, redundant read of the same snapshot for no\n// new information, and this hook has nothing to add to a presence check it\n// does not own. What this file DOES independently reproduce is\n// `useShareSnapshot()`'s own persona gate and its `share.expired`/\n// `share.revoked` subscriptions (`08-core-hooks.md §0`: \"every domain hook\n// is capability-gated internally\" — the share-surface's equivalent gate is\n// the persona check, applied the same way every other hook in this catalog\n// applies its own).\n//\n// ── SPEC TENSION FLAGGED: \"cache-first\" vs. \"the dedicated routes\" ─────────\n//\n// `02-forms.md`'s `Offline` field says the list \"rides the scoped share\n// snapshot cache-first, same as every other section\", but its own `Backed\n// by` field says in the same breath that \"the actual form list/render/\n// save/submit calls ride the dedicated forms-share routes\" — and\n// `ShareSnapshotResponse`'s per-section shape (verified,\n// `useShareSnapshot.ts`) carries no registered `forms` array with\n// `fillState`/`opensAt`/`closesAt` fields; only `ShareFormsListResponse`\n// (`GET /api/v1/share/{token}/forms`) does. This file follows the more\n// specific, SDK-verified statement — a LIVE `share.forms.list()` call,\n// refreshable on demand — the same \"own live-fetch sub-state, not a\n// `snapshot.updated` subscription\" pattern `useRooming.ts`'s\n// `unassignedPool` already uses for an analogous non-delta read. Flagged\n// for the doc owner to resolve; not silently picked one way with no trace.\n//\n// ── `clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY ──────────────────────\n//\n// `ShareSaveFormRequest`/`ShareSubmitFormRequest` (verified,\n// `kaafil-js/openapi/openapi.json`) require a body-level `clientToken`\n// (16-255 chars) — the SDK's own comment on `share.ts` calls it \"this\n// route's own de-dupe key, distinct from the automatic `Idempotency-Key`\n// header\", and `modules/forms/FRD.md §4.1/§8` calls it \"a fixed\n// `clientToken` generated once at enqueue and reused on every retry\" —\n// i.e. exactly the write-once, replay-unchanged lifecycle hard rule #4\n// reserves for `OfflineEngine.enqueue()`'s own internal idempotency-key\n// minting. This hook does NOT invent a value for it (that would be exactly\n// the \"hook mints its own idempotency key\" anti-pattern hard rule #4\n// forbids, even though `clientToken` is a different field from the\n// transport `Idempotency-Key` header `enqueue()` mints separately and\n// automatically for every op, share writes included). Instead\n// `ShareFormWriteInput.clientToken` is a REQUIRED field the CALLER\n// supplies — a future `ShareFormsSection`/`ShareFormFillScreen` generates\n// one value per form-fill session (e.g. once, the first time a field is\n// touched) and passes the SAME value to every `save()`/`submit()` call for\n// that session, which is what makes retries of the SAME logical write\n// dedupe wire-side. This hook only ever forwards it into the body verbatim.\n//\n// ── REVOCATION DROPS QUEUED WRITES — A REAL SDK GAP, WORKED AROUND HERE ────\n//\n// `02-forms.md`'s `Offline` field: \"Revocation is different and has no\n// grace: a `REVOKED` token drops queued writes unsent and purges the local\n// copy immediately.\" Verified against `kaafil-js/src/offline/share.ts`\n// this session: `ShareGuard.invalidate()` treats `'expired'` and\n// `'revoked'` IDENTICALLY — it purges the read snapshot in both cases and\n// only PRESERVES (never drops) the outbox either way; nothing in\n// `kaafil-js` currently distinguishes \"grace-covered\" from \"unsent, drop\n// it\" at the outbox level. This is a genuine SDK/spec disagreement — see\n// this file's final report note. Rather than leave the spec's hard\n// requirement unmet, this hook enforces it locally on `share.revoked`,\n// riding the outbox's own existing `drop()` primitive (the identical call\n// `useConflicts()`'s `'keep-current'` resolution already makes) — this is\n// composing an existing primitive in reaction to an event, not a second\n// sync engine (hard rule #3): no retry ladder, no cache, no idempotency\n// logic is reimplemented here, only \"remove this op from the outbox\".\n\nimport type { KaafilClient, OutboxOp } from 'kaafil-js/client';\nimport { SyncOpMethod } from 'kaafil-js/client';\nimport { useCallback, useEffect, useState } from 'react';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { useOfflineEngine } from './useOfflineEngine';\nimport { useOfflineEvent } from './useOfflineEvent';\nimport { useOfflineMutation } from './useOfflineMutation';\nimport { usePersona } from './usePersona';\nimport { useServerTime } from './useServerTime';\n\ntype ShareFormsClient = Pick<KaafilClient, 'share'>;\n\n/** One row of `GET /api/v1/share/{token}/forms` — `ShareFormsListResponse`\n * (verified, `kaafil-js/openapi/openapi.json`). Metadata only: no prefill,\n * no field list, no answers (`FRD:traveller-share §4.8`) — that detail\n * arrives only from `renderForm()` below. */\nexport interface ShareFormListItem {\n readonly formId: string;\n readonly key: string;\n readonly title: string;\n readonly phase: string;\n readonly required: boolean;\n readonly fillState: string;\n readonly opensAt: string | null;\n readonly closesAt: string | null;\n readonly responseStatus: string | null;\n readonly submittedAt: string | null;\n}\n\n/** One answer in a `save`/`submit` body — `ShareSaveFormRequest.answers[]`\n * / `ShareSubmitFormRequest.answers[]` (verified). */\nexport interface ShareFormAnswer {\n readonly fieldKey: string;\n readonly value: unknown;\n}\n\nexport interface ShareFormWriteInput {\n readonly formId: string;\n readonly answers: readonly ShareFormAnswer[];\n /** This route's own de-dupe key — REQUIRED by the wire schema. See this\n * file's header (\"`clientToken` IS NOT A HOOK-MINTED IDEMPOTENCY KEY\") —\n * the caller mints this once per form-fill session and reuses it across\n * every save/submit/retry of that session; this hook never generates it. */\n readonly clientToken: string;\n /** Group fill within the same `TripTraveller.partyId` — omit when the\n * token itself already scopes to a single traveller. */\n readonly travellerRef?: string;\n readonly startNew?: boolean;\n readonly isAnonymous?: boolean;\n}\n\nexport type ShareFormsListStatus = 'loading' | 'list-empty' | 'list-populated' | 'not-found';\n\n/**\n * The write-side status a `save()`/`submit()` call's tracked `OutboxOp`\n * resolves to. Deliberately excludes `'filling'` (`02-forms.md`'s own\n * `States` list also names it): `'filling'` is \"the traveller is editing\n * fields, no request in flight yet\" — a pure local UI mode with no\n * server/outbox signal behind it, so it belongs to the composite that owns\n * the form's field state, never to this hook, which only ever reflects\n * something async.\n */\nexport type ShareFormsWriteStatus =\n | 'idle'\n | 'saving'\n | 'submitting'\n | 'submitted'\n | 'conflicted'\n | 'subject-required'\n | 'queued-offline'\n | 'dropped-on-revoke';\n\nexport interface ShareFormsResult {\n readonly status: ShareFormsListStatus;\n /** `[]` in both the `'loading'` and the `'list-empty'` states — a\n * consumer distinguishes them via `status`, never via array emptiness\n * alone (`02-forms.md`'s `Gating`: an empty list is a NORMAL steady\n * state, never an error, never a placeholder). */\n readonly forms: readonly ShareFormListItem[];\n readonly serverTime: string | undefined;\n /** Re-runs `share.forms.list()`. A composite calls this after a\n * `submit()` lands, since submitting can change another form's\n * `fillState` (e.g. an any-phase trigger opening a follow-up form). */\n readonly refresh: () => Promise<void>;\n /** `GET /api/v1/share/{token}/forms/{formId}` — the fully-resolved,\n * ready-to-paint form (definition merged with any in-progress answers).\n * An open shape (`ShareRenderResponse` has no closed schema yet,\n * verified) — a live call, not cached, matching `save`/`submit`'s own\n * un-cached write path. */\n readonly renderForm: (\n formId: string,\n options?: { readonly travellerRef?: string },\n ) => Promise<Record<string, unknown>>;\n /** An in-progress save — no required-field validation server-side. */\n readonly save: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** Finalizes the response — full validation, one transaction. */\n readonly submit: (input: ShareFormWriteInput) => Promise<OutboxOp>;\n /** The combined save/submit write status — see `ShareFormsWriteStatus`. */\n readonly writeStatus: ShareFormsWriteStatus;\n /** The most recently tracked save/submit `OutboxOp`, or `undefined`\n * before either has ever been called. */\n readonly writeOp: OutboxOp | undefined;\n}\n\nfunction deriveListStatus(\n forms: readonly ShareFormListItem[] | undefined,\n listFailed: boolean,\n): ShareFormsListStatus {\n if (forms === undefined) {\n return listFailed ? 'not-found' : 'loading';\n }\n return forms.length === 0 ? 'list-empty' : 'list-populated';\n}\n\nfunction deriveWriteStatus(\n revoked: boolean,\n saveOp: OutboxOp | undefined,\n submitOp: OutboxOp | undefined,\n isOnline: boolean,\n): { readonly status: ShareFormsWriteStatus; readonly op: OutboxOp | undefined } {\n if (revoked) {\n return { status: 'dropped-on-revoke', op: submitOp ?? saveOp };\n }\n // `submit` always takes precedence when both are in play — finishing a\n // submission is the more advanced, more important-to-surface state of\n // the two for the same form.\n const active = submitOp !== undefined ? { op: submitOp, kind: 'submit' as const } : undefined;\n const fallback =\n active ?? (saveOp !== undefined ? { op: saveOp, kind: 'save' as const } : undefined);\n if (fallback === undefined) {\n return { status: 'idle', op: undefined };\n }\n const { op, kind } = fallback;\n if (op.status === 'parked') {\n if (op.parkReason?.code === 'CONFLICT_VERSION') {\n return { status: 'conflicted', op };\n }\n if (op.parkReason?.code === 'SUBJECT_REQUIRED') {\n return { status: 'subject-required', op };\n }\n // Any other park reason (e.g. `ShareWriteGraceExpired` —\n // `kaafil-js/src/offline/share.ts`, a grace window that closed before\n // this op ever synced) reports the same as a still-queued offline\n // write: the honest \"still catching up\" framing until it is provably\n // gone (`02-forms.md`'s `Offline` field). A distinct terminal wording\n // for that specific park reason is `ShareFormsSection`'s own\n // presentation concern, not this hook's.\n return { status: 'queued-offline', op };\n }\n if (op.status === 'done') {\n return { status: kind === 'submit' ? 'submitted' : 'idle', op };\n }\n // pending or inflight.\n if (!isOnline) {\n return { status: 'queued-offline', op };\n }\n return { status: kind === 'submit' ? 'submitting' : 'saving', op };\n}\n\nexport function useShareForms(token: string): ShareFormsResult {\n const client = useKaafilClient() as ShareFormsClient;\n const persona = usePersona();\n const engine = useOfflineEngine();\n const { now, sinceLastSync } = useServerTime();\n // `now()` throws before this session's first `snapshot.updated` event —\n // see `useServerTime.ts`'s own \"CALLERS: guard the call site\" note. This\n // hook's writes stay legal without a stamp (see the `enqueuedAt` call\n // sites below), so the guard is \"can I use it\", never \"must I have it\".\n const serverTimeKnown = sinceLastSync !== undefined;\n\n const [forms, setForms] = useState<readonly ShareFormListItem[] | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [listFailed, setListFailed] = useState(false);\n const [revoked, setRevoked] = useState(false);\n const [isOnline, setIsOnline] = useState(\n () => typeof navigator === 'undefined' || navigator.onLine,\n );\n\n const fetchList = useCallback(async (): Promise<void> => {\n try {\n const response = await client.share.forms.list({ token });\n setForms(response.items);\n setServerTime(response.meta.serverTime);\n setListFailed(false);\n } catch {\n // `GET .../forms` only ever fails `RESOURCE_NOT_FOUND` (`FRD:\n // traveller-share §4.1`'s route table, verified) — the share 404\n // carve-out means dark/cross-scope/genuinely-gone collapse to one\n // shape, same reasoning `useShareSnapshot()` applies to its own read.\n setListFailed(true);\n }\n }, [client, token]);\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareForms() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (families/traveller/02-forms.md, `ShareFormsSection`).',\n );\n }\n return;\n }\n void fetchList();\n }, [persona, fetchList]);\n\n // A browser-level connectivity signal, same source `useOutboxStatus()`\n // reads (`kaafil-js`'s `OfflineEngine` exposes no queryable connectivity\n // flag of its own — see that hook's own header, verified again here).\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n const handleOnline = (): void => setIsOnline(true);\n const handleOffline = (): void => setIsOnline(false);\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n }, []);\n\n useOfflineEvent('share.revoked', () => {\n setRevoked(true);\n // See this file's header (\"REVOCATION DROPS QUEUED WRITES\"). Only this\n // token's own lane, and only ops still in play (a `'done'` op already\n // landed and has nothing left to drop).\n for (const op of engine.outbox.all()) {\n if (op.tripRef === token && (op.status === 'pending' || op.status === 'parked')) {\n void engine.outbox.drop(op.id);\n }\n }\n });\n\n const saveMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/save${query}`,\n // Hardcoded literal, not imported: `OperationId` is not exported\n // from `kaafil-js/client`'s public surface (same reasoning\n // `useRooming.ts` documents). Value verified against\n // `kaafil-js/src/generated/security.ts` this session:\n // `OperationId.ShareSaveForm === 'shareSaveForm'`.\n operationId: 'shareSaveForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // The server-anchored \"now\" this write was created at — never\n // `Date.now()`/`new Date()` (hard rule #6) — `EnqueueWriteOptions\n // .enqueuedAt`'s own doc comment: \"client-attested and only ever\n // used by the server to REFUSE\" (the 7-day post-expiry grace\n // check). Optional on the wire, and genuinely omitted here on a\n // cold session: `useServerTime().now()` throws before this\n // session's very first `snapshot.updated` event has landed (its\n // own documented behaviour, guarded at the call site exactly as\n // its header instructs) — a share write attempted in that narrow\n // window still enqueues durably, just without a client-attested\n // stamp for the server's grace check to read.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const submitMutation = useOfflineMutation<ShareFormWriteInput>({\n tripRef: token,\n enqueue: (mutationEngine, input) => {\n const query =\n input.travellerRef !== undefined\n ? `?travellerRef=${encodeURIComponent(input.travellerRef)}`\n : '';\n return mutationEngine.enqueue({\n tripRef: token,\n method: SyncOpMethod.Post,\n path: `/api/v1/share/${token}/forms/${input.formId}/submit${query}`,\n operationId: 'shareSubmitForm' as never,\n body: {\n answers: input.answers,\n clientToken: input.clientToken,\n ...(input.startNew !== undefined ? { startNew: input.startNew } : {}),\n ...(input.isAnonymous !== undefined ? { isAnonymous: input.isAnonymous } : {}),\n },\n // See the identical `save` enqueue above for why this is guarded.\n ...(serverTimeKnown ? { enqueuedAt: now() } : {}),\n });\n },\n });\n\n const renderForm = useCallback(\n async (\n formId: string,\n options?: { readonly travellerRef?: string },\n ): Promise<Record<string, unknown>> => {\n const response = await client.share.forms.render({\n token,\n formId,\n ...(options?.travellerRef !== undefined ? { travellerRef: options.travellerRef } : {}),\n });\n return response;\n },\n [client, token],\n );\n\n const { status: writeStatus, op: writeOp } = deriveWriteStatus(\n revoked,\n saveMutation.op,\n submitMutation.op,\n isOnline,\n );\n\n return {\n status: deriveListStatus(forms, listFailed),\n forms: forms ?? [],\n serverTime,\n refresh: fetchList,\n renderForm,\n save: saveMutation.mutate,\n submit: submitMutation.mutate,\n writeStatus,\n writeOp,\n };\n}\n","'use client';\n\n// `08-core-hooks.md §3.1`. The single assembled read behind the whole\n// traveller share surface. Wraps `share.snapshot()` (verified `shareAuth`\n// -only in `security.ts` — no `apiKeyAuth` alternative exists for any\n// `share.*` operation), read cache-first from the snapshot once\n// `OfflineEngine`'s `share` option is configured.\n//\n// VERIFIED SHAPE (`openapi/openapi.json`'s `ShareSnapshotResponse`, this\n// session): a flat object with one property PER section key, each present\n// only when the link exposes it and data exists — never `null`, never an\n// empty placeholder — plus a `trip` echo (`name`/`startDate`/`endDate`/\n// `timezone`/`phase`) that is NOT one of the thirteen section flags\n// (`09-registries.md §5`). This hook builds `sections` by taking every own\n// key off the response except `trip` and `meta` — additive-safe against a\n// future 14th section key without needing this file to enumerate all\n// thirteen by name.\n//\n// `trip` and the server clock are surfaced as their OWN top-level fields on\n// `ShareSnapshotResult`, never folded into `sections`: every consumer of\n// `sections` (starting with `KaafilShareView`, which walks the map and\n// renders one composite per key) treats \"is this key present\" as \"should I\n// render a section for it\" — letting `trip`/`meta` leak into that map would\n// make the view try to mount a `<ShareTripSection>`/`<ShareMetaSection>`\n// that has never existed. But every traveller composite still needs BOTH:\n// `trip.timezone` is the one zone `DateDisplay`/`TimeDisplay` may legally\n// anchor to on this surface (never the device zone, never a fabricated\n// `'UTC'`), `trip.phase` lets a composite react to PRE_DEPARTURE/ON_TRIP/\n// POST_TRIP, and `meta.serverTime` is the only trusted \"now\" a share\n// composite may read without going through `useServerTime()` (hard rule\n// #6 — no `Date.now()`/`new Date()` in `src/core`). Hence: two extra return\n// fields, kept structurally apart from the section map they must never\n// pollute.\n\nimport type { KaafilClient } from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { isDevelopment } from '../internal/isDevelopment';\nimport { useKaafilClient } from './useKaafilClient';\nimport { usePersona } from './usePersona';\n\nexport interface ShareSnapshotOptions {\n readonly travellerRef?: string;\n}\n\n// Derived from the SDK's own return type rather than hand-copied — this\n// stays correct for free if `kaafil-js` grows another `trip` field, instead\n// of silently drifting the way the old `'UTC'` constants did.\ntype ShareSnapshotResponse = Awaited<ReturnType<KaafilClient['share']['snapshot']>>;\n\n/** The trip echo every snapshot response carries — `name`/`startDate`/\n * `endDate` plus the two fields this hook used to throw away:\n * `timezone` (the IANA zone every traveller-facing render must anchor to)\n * and `phase` (server-computed `PRE_DEPARTURE`/`ON_TRIP`/`POST_TRIP`). */\nexport type ShareSnapshotTrip = ShareSnapshotResponse['trip'];\n\nexport interface ShareSnapshotResult {\n readonly status: 'loading' | 'ready' | 'expired' | 'revoked' | 'not-found';\n readonly sections: ReadonlyMap<string, unknown>;\n /** The snapshot's `trip` echo, or `undefined` before any response has\n * landed (mirrors `sections`' own \"empty until ready\" behaviour). NEVER a\n * section key — see this file's header. */\n readonly trip: ShareSnapshotTrip | undefined;\n /** `meta.serverTime` off the most recently landed snapshot response — the\n * only trusted clock a share composite may read (hard rule #6). Kept\n * separate from `useServerTime()`'s own stream (which advances off\n * `OfflineEngine`'s `snapshot.updated` event, not this hook's own fetch)\n * so a composite that only ever calls `useShareSnapshot()` still has a\n * real server stamp to compare against without also wiring the offline\n * engine's event stream. `undefined` before any response has landed. */\n readonly serverTime: string | undefined;\n}\n\nconst NON_SECTION_KEYS = new Set(['trip', 'meta']);\n\nexport function useShareSnapshot(\n token: string,\n options?: ShareSnapshotOptions,\n): ShareSnapshotResult {\n void options; // `travellerRef` narrows which subject-token rows the server\n // itself returns for a `travellerRef`-scoped share link — this hook has no\n // client-side use for it beyond passing the token through; the server does\n // the scoping (`09-registries.md §5`, `families/traveller/01-share-sections.md`, cited).\n\n const client = useKaafilClient();\n const persona = usePersona();\n const engine = useContext(OfflineEngineContext);\n const [sections, setSections] = useState<ReadonlyMap<string, unknown> | undefined>(undefined);\n const [trip, setTrip] = useState<ShareSnapshotTrip | undefined>(undefined);\n const [serverTime, setServerTime] = useState<string | undefined>(undefined);\n const [terminal, setTerminal] = useState<'expired' | 'revoked' | 'not-found' | undefined>(\n undefined,\n );\n\n useEffect(() => {\n if (persona !== 'share') {\n if (isDevelopment()) {\n throw new Error(\n 'useShareSnapshot() was called from a manager/agencyAdmin session — ' +\n 'this hook is `share`-only (08-core-hooks.md §3.1).',\n );\n }\n return;\n }\n\n let cancelled = false;\n async function load(): Promise<void> {\n try {\n const response = await client.share.snapshot({ token });\n if (cancelled) {\n return;\n }\n const map = new Map<string, unknown>();\n for (const [key, value] of Object.entries(response)) {\n if (!NON_SECTION_KEYS.has(key)) {\n map.set(key, value);\n }\n }\n setSections(map);\n setTrip(response.trip);\n setServerTime(response.meta.serverTime);\n } catch {\n // `06-capability-and-personas.md §9`: dark, cross-scope, and\n // genuinely-gone all collapse to the identical shape here — this\n // hook cannot and must not try to distinguish them, so any failure\n // to read the snapshot at all reports the same `'not-found'`.\n if (!cancelled) {\n setTerminal('not-found');\n }\n }\n }\n void load();\n\n if (engine === null) {\n return () => {\n cancelled = true;\n };\n }\n const unsubscribeExpired = engine.events.on('share.expired', () => setTerminal('expired'));\n const unsubscribeRevoked = engine.events.on('share.revoked', () => setTerminal('revoked'));\n return () => {\n cancelled = true;\n unsubscribeExpired();\n unsubscribeRevoked();\n };\n }, [client, persona, token, engine]);\n\n if (terminal !== undefined) {\n return { status: terminal, sections: sections ?? new Map(), trip, serverTime };\n }\n if (sections === undefined) {\n return { status: 'loading', sections: new Map(), trip: undefined, serverTime: undefined };\n }\n return { status: 'ready', sections, trip, serverTime };\n}\n","'use client';\n\n// `shells/06-brand-shell.md`. Eighth in the locked mount order\n// (`shells/README.md §2` row 8) — supplies host-side brand assets\n// (fallback chain: partner logo → neutral mark → text-only appName,\n// `05-theming-and-branding.md §8`) and, opt-in and persona-gated, controls\n// `document.head` for the traveller/share surface only. Fetches nothing\n// from the engine (`00-decisions.md` U-007 — per-agency brand assets are a\n// named non-goal for the backend).\n\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useEffect, useMemo } from 'react';\nimport { usePersona } from '../hooks/usePersona';\nimport { isDevelopment } from '../internal/isDevelopment';\n\nexport interface KaafilBrandAssets {\n readonly appName?: string;\n readonly logo?: string | ReactNode;\n readonly logoMark?: string | ReactNode;\n readonly favicon?: string;\n}\n\nexport interface BrandShellProps extends KaafilBrandAssets {\n readonly headStrategy?: 'none' | 'own-page';\n readonly children?: ReactNode;\n}\n\n/** Shell-owned, not a `08-core-hooks.md` catalog entry (component authoring\n * — this context's eventual consumer, a logo/header component reading the\n * fallback chain — is out of scope for this phase). */\nconst BrandAssetsContext = createContext<KaafilBrandAssets | null>(null);\n\n/** Reads the brand assets `BrandShell` forwarded, or `{}` when mounted\n * without one (never throws — brand is presentational, not load-bearing the\n * way a session read is). */\nexport function useKaafilBrand(): KaafilBrandAssets {\n return useContext(BrandAssetsContext) ?? {};\n}\n\nexport interface ShareMeta {\n readonly title: string | undefined;\n readonly favicon: string | undefined;\n readonly ogTitle: string | undefined;\n readonly ogDescription: string | undefined;\n readonly ogImage: string | undefined;\n readonly robots: 'noindex' | 'index';\n}\n\nfunction resolveShareMeta(assets: KaafilBrandAssets): ShareMeta {\n return {\n title: assets.appName,\n favicon: assets.favicon,\n ogTitle: assets.appName,\n ogDescription: undefined,\n ogImage: undefined,\n // Share meta defaults to noindex — a share link is private-by-obscurity;\n // indexing it is a disclosure bug, never the shipped default\n // (`00-decisions.md` U-005).\n robots: 'noindex',\n };\n}\n\n/** Only the traveller/share surface may write `document.head` — mounted\n * exclusively by `BrandShell` under a persona-permitted `'own-page'`\n * request. Renders nothing into the page body: title/meta writes are not\n * \"visual output\" in the sense `shells/README.md §1` means the phrase. */\nfunction KaafilDocumentHead({ meta }: { readonly meta: ShareMeta }): null {\n useEffect(() => {\n if (meta.title !== undefined) {\n document.title = meta.title;\n }\n if (meta.favicon !== undefined) {\n let link = document.querySelector<HTMLLinkElement>('link[rel=\"icon\"][data-kf-brand]');\n if (link === null) {\n link = document.createElement('link');\n link.rel = 'icon';\n link.setAttribute('data-kf-brand', '');\n document.head.appendChild(link);\n }\n link.href = meta.favicon;\n }\n const setMeta = (\n name: string,\n content: string | undefined,\n attr: 'name' | 'property' = 'name',\n ): void => {\n if (content === undefined) {\n return;\n }\n let tag = document.querySelector<HTMLMetaElement>(`meta[${attr}=\"${name}\"][data-kf-brand]`);\n if (tag === null) {\n tag = document.createElement('meta');\n tag.setAttribute(attr, name);\n tag.setAttribute('data-kf-brand', '');\n document.head.appendChild(tag);\n }\n tag.content = content;\n };\n setMeta('og:title', meta.ogTitle, 'property');\n setMeta('og:description', meta.ogDescription, 'property');\n setMeta('og:image', meta.ogImage, 'property');\n setMeta('robots', meta.robots);\n }, [meta]);\n\n return null;\n}\n\n/**\n * Forwards brand assets unconditionally; enforces `headStrategy` before\n * ever mounting `<KaafilDocumentHead>`. `'none'` is a HARD no-op — no\n * branch below it ever touches `document.head`, even when `favicon`/\n * `appName` are supplied. `'own-page'` is runtime-rejected under\n * `agencyAdmin`/`manager` (dev throw, prod warning + silent `'none'`\n * fallback — the same shape `useCapability()` uses for a wrong-persona call,\n * `08-core-hooks.md §1.4`).\n */\nexport function BrandShell({\n appName,\n logo,\n logoMark,\n favicon,\n headStrategy = 'none',\n children,\n}: BrandShellProps): ReactNode {\n const persona = usePersona();\n const assets = useMemo(\n () => ({ appName, logo, logoMark, favicon }),\n [appName, logo, logoMark, favicon],\n );\n\n const rejectedOwnPage = headStrategy === 'own-page' && persona !== 'share';\n\n useEffect(() => {\n if (headStrategy === 'own-page' && persona !== 'share' && isDevelopment()) {\n throw new Error(\n 'BrandShell: headStrategy=\"own-page\" was requested under a non-share persona ' +\n `(\"${persona}\") — only the traveller/share surface may own document.head ` +\n '(00-decisions.md U-005). Falls back to \"none\" in production.',\n );\n }\n if (\n headStrategy === 'none' &&\n // `favicon` ONLY. `appName` is not a head-only prop: it is the last rung\n // of the in-page brand fallback chain (partner logo -> neutral mark ->\n // text-only appName, 05-theming-and-branding.md §8), so it does real\n // work under every `headStrategy` and warning about it told integrators\n // their correct code was wrong. `favicon` has no in-page use at all, so\n // passing one under `'none'` genuinely is a no-op worth flagging.\n favicon !== undefined &&\n isDevelopment()\n ) {\n // biome-ignore lint/suspicious/noConsole: dev-only, gated above by isDevelopment()\n console.warn(\n 'BrandShell: headStrategy=\"none\" (the default) is a hard no-op — \"favicon\" was passed ' +\n 'but document.head is never touched under \"none\" (00-decisions.md U-005). ' +\n '\"appName\" is unaffected: it still feeds the in-page brand fallback.',\n );\n }\n }, [headStrategy, persona, favicon]);\n\n const shouldOwnPage = headStrategy === 'own-page' && persona === 'share' && !rejectedOwnPage;\n\n return (\n <BrandAssetsContext.Provider value={assets}>\n {shouldOwnPage ? <KaafilDocumentHead meta={resolveShareMeta(assets)} /> : null}\n {children}\n </BrandAssetsContext.Provider>\n );\n}\n\n/**\n * Shell-owned read-only alternative to `<KaafilDocumentHead>` — for a host\n * (e.g. Next.js App Router) that already owns `<head>` via its own\n * `metadata`/`generateMetadata` export and must keep `headStrategy=\"none\"`\n * here. Never writes the head itself; exactly one of the two paths is\n * active per mount (`shells/06-brand-shell.md`).\n */\nexport function useKaafilShareMeta(assets: KaafilBrandAssets): ShareMeta {\n return useMemo(() => resolveShareMeta(assets), [assets]);\n}\n"]}
|