kaafil-react-uikit 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/admin/index.cjs +342 -342
  2. package/dist/admin/index.js +5 -5
  3. package/dist/{chunk-YHOQRKDJ.cjs → chunk-57SUK3GD.cjs} +7 -7
  4. package/dist/{chunk-YHOQRKDJ.cjs.map → chunk-57SUK3GD.cjs.map} +1 -1
  5. package/dist/{chunk-2SRBJ4H7.js → chunk-A4Z2YBEF.js} +19 -5
  6. package/dist/chunk-A4Z2YBEF.js.map +1 -0
  7. package/dist/{chunk-2MWTLYVP.cjs → chunk-BBC5EX5O.cjs} +17 -17
  8. package/dist/{chunk-2MWTLYVP.cjs.map → chunk-BBC5EX5O.cjs.map} +1 -1
  9. package/dist/{chunk-W5PG3IUJ.cjs → chunk-ECCQW5LN.cjs} +7 -4
  10. package/dist/chunk-ECCQW5LN.cjs.map +1 -0
  11. package/dist/{chunk-M2D6CZK2.cjs → chunk-FVK7HMQ5.cjs} +37 -37
  12. package/dist/{chunk-M2D6CZK2.cjs.map → chunk-FVK7HMQ5.cjs.map} +1 -1
  13. package/dist/{chunk-PYETPKTT.js → chunk-GJ2J3PYN.js} +3 -3
  14. package/dist/{chunk-PYETPKTT.js.map → chunk-GJ2J3PYN.js.map} +1 -1
  15. package/dist/{chunk-TF3GCIDK.cjs → chunk-HACH2NUM.cjs} +24 -13
  16. package/dist/chunk-HACH2NUM.cjs.map +1 -0
  17. package/dist/{chunk-PDQVVJCS.js → chunk-KW5LH6KV.js} +3 -3
  18. package/dist/{chunk-PDQVVJCS.js.map → chunk-KW5LH6KV.js.map} +1 -1
  19. package/dist/{chunk-WS2DKREI.cjs → chunk-MNUCVVIE.cjs} +168 -97
  20. package/dist/chunk-MNUCVVIE.cjs.map +1 -0
  21. package/dist/{chunk-CURTZGHY.js → chunk-RWLO6IFX.js} +19 -8
  22. package/dist/chunk-RWLO6IFX.js.map +1 -0
  23. package/dist/{chunk-V36WWMTF.cjs → chunk-UALMCHNW.cjs} +19 -5
  24. package/dist/chunk-UALMCHNW.cjs.map +1 -0
  25. package/dist/{chunk-JMWXQTE3.js → chunk-VLP323AD.js} +6 -3
  26. package/dist/chunk-VLP323AD.js.map +1 -0
  27. package/dist/{chunk-7WXE5SUS.js → chunk-WTTGPWMR.js} +92 -21
  28. package/dist/chunk-WTTGPWMR.js.map +1 -0
  29. package/dist/{chunk-PCDEJHWL.cjs → chunk-YNHOPYBK.cjs} +12 -12
  30. package/dist/{chunk-PCDEJHWL.cjs.map → chunk-YNHOPYBK.cjs.map} +1 -1
  31. package/dist/{chunk-FFXGZZGJ.js → chunk-YTSYRLGS.js} +5 -5
  32. package/dist/{chunk-FFXGZZGJ.js.map → chunk-YTSYRLGS.js.map} +1 -1
  33. package/dist/{chunk-E42K6UMM.js → chunk-YU2242AR.js} +5 -5
  34. package/dist/{chunk-E42K6UMM.js.map → chunk-YU2242AR.js.map} +1 -1
  35. package/dist/core/index.cjs +113 -113
  36. package/dist/core/index.js +11 -11
  37. package/dist/manager/index.cjs +239 -239
  38. package/dist/manager/index.js +6 -6
  39. package/dist/traveller/index.cjs +35 -35
  40. package/dist/traveller/index.js +5 -5
  41. package/package.json +2 -2
  42. package/dist/chunk-2SRBJ4H7.js.map +0 -1
  43. package/dist/chunk-7WXE5SUS.js.map +0 -1
  44. package/dist/chunk-CURTZGHY.js.map +0 -1
  45. package/dist/chunk-JMWXQTE3.js.map +0 -1
  46. package/dist/chunk-TF3GCIDK.cjs.map +0 -1
  47. package/dist/chunk-V36WWMTF.cjs.map +0 -1
  48. package/dist/chunk-W5PG3IUJ.cjs.map +0 -1
  49. package/dist/chunk-WS2DKREI.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { OfflineEngineContext } from './chunk-2SRBJ4H7.js';
1
+ import { OfflineEngineContext } from './chunk-A4Z2YBEF.js';
2
2
  import { useContext, useState, useRef, useEffect } from 'react';
3
3
 
4
4
  var TICK_INTERVAL_MS = 1e3;
@@ -12,6 +12,9 @@ function useServerTime() {
12
12
  return;
13
13
  }
14
14
  const unsubscribe = engine.events.on("snapshot.updated", (event) => {
15
+ if (event.serverTime === void 0) {
16
+ return;
17
+ }
15
18
  observedAtPerfRef.current = performance.now();
16
19
  setLastServerTime(event.serverTime);
17
20
  });
@@ -34,5 +37,5 @@ function useServerTime() {
34
37
  }
35
38
 
36
39
  export { useServerTime };
37
- //# sourceMappingURL=chunk-JMWXQTE3.js.map
38
- //# sourceMappingURL=chunk-JMWXQTE3.js.map
40
+ //# sourceMappingURL=chunk-VLP323AD.js.map
41
+ //# sourceMappingURL=chunk-VLP323AD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/hooks/useServerTime.ts"],"names":[],"mappings":";;;AA8BA,IAAM,gBAAA,GAAmB,GAAA;AAElB,SAAS,aAAA,GAA+B;AAC7C,EAAA,MAAM,MAAA,GAAS,WAAW,oBAAoB,CAAA;AAC9C,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,SAA6B,MAAS,CAAA;AAClF,EAAA,MAAM,iBAAA,GAAoB,OAA2B,MAAS,CAAA;AAC9D,EAAA,MAAM,GAAG,SAAS,CAAA,GAAI,SAAS,CAAC,CAAA;AAEhC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,WAAW,IAAA,EAAM;AACnB,MAAA;AAAA,IACF;AAKA,IAAA,MAAM,cAAc,MAAA,CAAO,MAAA,CAAO,EAAA,CAAG,kBAAA,EAAoB,CAAC,KAAA,KAAU;AASlE,MAAA,IAAI,KAAA,CAAM,eAAe,MAAA,EAAW;AAClC,QAAA;AAAA,MACF;AACA,MAAA,iBAAA,CAAkB,OAAA,GAAU,YAAY,GAAA,EAAI;AAC5C,MAAA,iBAAA,CAAkB,MAAM,UAAU,CAAA;AAAA,IACpC,CAAC,CAAA;AACD,IAAA,OAAO,WAAA;AAAA,EACT,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,EAAA,GAAK,YAAY,MAAM,SAAA,CAAU,CAAC,IAAA,KAAS,IAAA,GAAO,CAAC,CAAA,EAAG,gBAAgB,CAAA;AAC5E,IAAA,OAAO,MAAM,cAAc,EAAE,CAAA;AAAA,EAC/B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,SAAS,GAAA,GAAc;AACrB,IAAA,IAAI,cAAA,KAAmB,MAAA,IAAa,iBAAA,CAAkB,OAAA,KAAY,MAAA,EAAW;AAY3E,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AACA,IAAA,MAAM,SAAA,GAAY,WAAA,CAAY,GAAA,EAAI,GAAI,iBAAA,CAAkB,OAAA;AACxD,IAAA,OAAO,IAAI,KAAK,IAAA,CAAK,KAAA,CAAM,cAAc,CAAA,GAAI,SAAS,EAAE,WAAA,EAAY;AAAA,EACtE;AAEA,EAAA,OAAO,EAAE,GAAA,EAAK,aAAA,EAAe,cAAA,EAAe;AAC9C","file":"chunk-VLP323AD.js","sourcesContent":["'use client';\n\n// `08-core-hooks.md §1.6`. The single place that turns the stream of\n// `meta.serverTime` stamps into a usable clock, so no composite reads\n// `meta.serverTime` off an individual response by hand.\n//\n// Hard rule #6 (\"Zero `Date.now()`/`new Date()` local getters in\n// `src/core`\") bans exactly the two calls that fabricate \"now\" from the\n// local wall clock: `Date.now()` and no-argument `new Date()`. This file\n// never calls either. Advancing a SERVER-supplied timestamp by an elapsed\n// delta necessarily needs SOME measure of \"how much local time passed\" —\n// this file measures that with `performance.now()`, a monotonic clock with\n// no relationship to wall-clock \"now\" (it cannot be read as a date, only as\n// a duration), and applies the delta with `new Date(<epoch-ms>)` /\n// `Date.parse(<iso-string>)` — both single-argument forms that operate on a\n// value already anchored to `meta.serverTime`, never on the local clock.\n\nimport { useContext, useEffect, useRef, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\n\nexport interface ServerTimeApi {\n now: () => string;\n sinceLastSync: string | undefined;\n}\n\n/** Re-renders any hook consumer often enough for a mounted countdown to look\n * live, without pretending the underlying value moved forward on its own\n * merits (`08-core-hooks.md §1.6`'s sandbox time-travel caveat: this timer\n * forces a re-render, it never advances the stamp past what the last real\n * response justified). */\nconst TICK_INTERVAL_MS = 1000;\n\nexport function useServerTime(): ServerTimeApi {\n const engine = useContext(OfflineEngineContext);\n const [lastServerTime, setLastServerTime] = useState<string | undefined>(undefined);\n const observedAtPerfRef = useRef<number | undefined>(undefined);\n const [, forceTick] = useState(0);\n\n useEffect(() => {\n if (engine === null) {\n return;\n }\n // `snapshot.updated` is the one event in `OfflineEventMap` that carries\n // `serverTime` (`kaafil-js/src/offline/events.ts`, verified this\n // session) — the engine's other five events do not, so this is the only\n // subscription this hook can build without inventing a stamp.\n const unsubscribe = engine.events.on('snapshot.updated', (event) => {\n // `serverTime` is ABSENT on a change that never involved the server —\n // an optimistic create painted while offline, or an op the host\n // discarded (`kaafil-js/src/offline/events.ts`). Those are real snapshot\n // changes and a list must repaint for them, but they carry no reading,\n // and taking `undefined` here would erase the last real one this\n // session had: `now()` would start throwing again mid-session, on a\n // device that HAS heard from the server, because the manager ticked\n // something off out of signal.\n if (event.serverTime === undefined) {\n return;\n }\n observedAtPerfRef.current = performance.now();\n setLastServerTime(event.serverTime);\n });\n return unsubscribe;\n }, [engine]);\n\n useEffect(() => {\n const id = setInterval(() => forceTick((tick) => tick + 1), TICK_INTERVAL_MS);\n return () => clearInterval(id);\n }, []);\n\n function now(): string {\n if (lastServerTime === undefined || observedAtPerfRef.current === undefined) {\n // No response has ever landed in this session yet. Fabricating a\n // value from the local wall clock is exactly what hard rule #6\n // forbids — this is the one case this hook cannot silently satisfy\n // its own non-optional return type, so it says so loudly instead.\n // CALLERS: this is a real, expected window — React runs an effect\n // (which is how this hook's own subscription above starts) strictly\n // AFTER the render that needs `now()`'s result has already committed,\n // so a component's very FIRST render can hit this throw even when\n // wired up correctly. Guard the call site, matching\n // `useStaleness.ts`'s own precedent, rather than reaching for a\n // device-clock fallback here (hard rule #6 forbids exactly that).\n throw new Error(\n 'useServerTime().now() was called before any response carrying meta.serverTime ' +\n 'had been observed — there is no server timestamp yet to advance from.',\n );\n }\n const elapsedMs = performance.now() - observedAtPerfRef.current;\n return new Date(Date.parse(lastServerTime) + elapsedMs).toISOString();\n }\n\n return { now, sinceLastSync: lastServerTime };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { OfflineEngineContext, KaafilClientContext, useCapabilityRows, UNKNOWN_CAPABILITY_TRIPLE, deriveCapabilityTriple, darkReasonFor, useKaafilClient, useOfflineMutation, usePersona, isDevelopment, useOfflineEvent } from './chunk-2SRBJ4H7.js';
1
+ import { OfflineEngineContext, KaafilClientContext, useCapabilityRows, UNKNOWN_CAPABILITY_TRIPLE, deriveCapabilityTriple, darkReasonFor, useKaafilClient, useOfflineMutation, usePersona, isDevelopment, useOfflineEvent } from './chunk-A4Z2YBEF.js';
2
2
  import { UnsatisfiableSchemeError, isKaafilError, SyncOpMethod } from 'kaafil-js/client';
3
3
  export { FALLBACK_GLYPH, SHADES_PER_FAMILY, TONE_FAMILIES, parseRoomingTone, roomingHeuristicSeverity } from 'kaafil-js/client';
4
4
  import { useContext, useState, useRef, useCallback, useEffect } from 'react';
@@ -176,6 +176,14 @@ function createDomainHook(moduleKey, build) {
176
176
  }
177
177
 
178
178
  // src/core/hooks/useChecklists.ts
179
+ function appendPendingRows(fromAggregate, fromSnapshot) {
180
+ if (fromSnapshot.length === 0) {
181
+ return fromAggregate;
182
+ }
183
+ const known = new Set(fromAggregate.map((row) => row.id));
184
+ const pending = fromSnapshot.filter((row) => !known.has(row.id));
185
+ return pending.length === 0 ? fromAggregate : [...fromAggregate, ...pending];
186
+ }
179
187
  var useChecklistsInternal = createDomainHook("checklists", (tripRef) => {
180
188
  const client = useKaafilClient();
181
189
  const engine = useContext(OfflineEngineContext);
@@ -183,22 +191,31 @@ var useChecklistsInternal = createDomainHook("checklists", (tripRef) => {
183
191
  tripRef,
184
192
  "checklist"
185
193
  );
186
- const [aggregate, setAggregate] = useState({
187
- title: void 0,
188
- subtitle: void 0,
189
- progress: void 0,
190
- hasOpenMandatoryByPhase: void 0,
191
- sections: [],
192
- items: [],
193
- availableTemplates: [],
194
- loaded: false,
195
- lastFetchFailed: false,
196
- fetchedAt: void 0
194
+ const aggregateCacheKey = `checklists.read:${tripRef}`;
195
+ const [aggregate, setAggregate] = useState(() => {
196
+ const cached = engine?.reads.get(
197
+ aggregateCacheKey
198
+ );
199
+ if (cached !== void 0) {
200
+ return { ...cached.value, loaded: true, lastFetchFailed: false };
201
+ }
202
+ return {
203
+ title: void 0,
204
+ subtitle: void 0,
205
+ progress: void 0,
206
+ hasOpenMandatoryByPhase: void 0,
207
+ sections: [],
208
+ items: [],
209
+ availableTemplates: [],
210
+ loaded: false,
211
+ lastFetchFailed: false,
212
+ fetchedAt: void 0
213
+ };
197
214
  });
198
215
  const refreshAggregate = useCallback(async () => {
199
216
  try {
200
217
  const response = await client.checklists.read({ tripRef });
201
- setAggregate({
218
+ const value = {
202
219
  title: response.title,
203
220
  subtitle: response.subtitle,
204
221
  progress: response.progress,
@@ -206,14 +223,21 @@ var useChecklistsInternal = createDomainHook("checklists", (tripRef) => {
206
223
  sections: response.sections,
207
224
  items: response.items,
208
225
  availableTemplates: response.availableTemplates,
209
- loaded: true,
210
- lastFetchFailed: false,
211
226
  fetchedAt: response.meta.serverTime
227
+ };
228
+ void engine?.reads.set(aggregateCacheKey, value, response.meta.serverTime).catch(() => {
212
229
  });
230
+ setAggregate({ ...value, loaded: true, lastFetchFailed: false });
213
231
  } catch {
214
- setAggregate((previous) => ({ ...previous, lastFetchFailed: true }));
232
+ setAggregate((previous) => {
233
+ if (previous.loaded) {
234
+ return { ...previous, lastFetchFailed: true };
235
+ }
236
+ const cached = engine?.reads.get(aggregateCacheKey);
237
+ return cached === void 0 ? { ...previous, lastFetchFailed: true } : { ...cached.value, loaded: true, lastFetchFailed: true };
238
+ });
215
239
  }
216
- }, [client, tripRef]);
240
+ }, [client, tripRef, engine, aggregateCacheKey]);
217
241
  useEffect(() => {
218
242
  void refreshAggregate();
219
243
  }, [refreshAggregate]);
@@ -233,7 +257,54 @@ var useChecklistsInternal = createDomainHook("checklists", (tripRef) => {
233
257
  method: SyncOpMethod.Post,
234
258
  path: `/api/v1/trips/${tripRef}/checklist/items`,
235
259
  operationId: "addChecklistItem",
236
- body: input
260
+ body: input,
261
+ // ── THE OPTIMISTIC CREATE ──────────────────────────────────────
262
+ // Without this the item a manager adds out of signal does not
263
+ // appear until it syncs. It was queued, durable and landed on
264
+ // reconnect the whole time — and from where the manager is
265
+ // standing, work that does not show up is work that was lost, so
266
+ // they add it again.
267
+ //
268
+ // `creates` makes the ENGINE mint the row's temporary id (this
269
+ // hook never invents identifiers) and swap it for the server's
270
+ // canonical row the moment the write lands, in one
271
+ // `snapshot.updated`, so the item is never rendered twice.
272
+ creates: true,
273
+ entity: { listName: "checklist" },
274
+ // Shaped as a real `ChecklistItemDeltaRow` so the board renders it
275
+ // through the same path as every other row rather than a special
276
+ // "pending" branch. `id` is a placeholder the engine replaces with
277
+ // the id it mints; `version: 0` cannot beat any server row in the
278
+ // snapshot's version-guarded merge.
279
+ //
280
+ // The write addresses a section by its `sectionKey`, but the board
281
+ // groups rows by `sectionId`, so the id is resolved from the sections
282
+ // this hook already holds. Adding to a section that does not exist
283
+ // yet resolves to `''` and the row sits ungrouped until the write
284
+ // lands — correct rather than clever: inventing a section id would
285
+ // put the item under a heading that does not exist.
286
+ optimistic: {
287
+ id: "pending",
288
+ version: 0,
289
+ sectionId: aggregate.sections.find((section) => section.key === input.sectionKey)?.id ?? "",
290
+ key: input.key ?? null,
291
+ title: input.title,
292
+ subLine: null,
293
+ status: "OPEN",
294
+ // `=== true`, never `?? false` (hard rule #5): an omitted
295
+ // `mandatory` is the SERVER's default to apply, not this hook's to
296
+ // guess. The optimistic row shows it unstarred for the moment it
297
+ // takes the write to land, and the canonical row replaces it.
298
+ isMandatory: input.mandatory === true,
299
+ gate: null,
300
+ dayOffset: null,
301
+ sortOrder: Number.MAX_SAFE_INTEGER,
302
+ completedByManagerId: null,
303
+ performedByKind: null,
304
+ performedById: null,
305
+ completedAt: null,
306
+ updatedAt: null
307
+ }
237
308
  }),
238
309
  // ── THE DESK LANE ─────────────────────────────────────────────────
239
310
  // All five checklist writes take the admin triple, and a desk console
@@ -375,7 +446,7 @@ var useChecklistsInternal = createDomainHook("checklists", (tripRef) => {
375
446
  (input) => client.checklists.agencyTemplates.publish(input),
376
447
  [client]
377
448
  );
378
- const items = syncedAt === void 0 ? aggregate.items : snapshotItems;
449
+ const items = syncedAt === void 0 ? appendPendingRows(aggregate.items, snapshotItems) : snapshotItems;
379
450
  return {
380
451
  items,
381
452
  syncedAt,
@@ -2150,5 +2221,5 @@ function useVendors(tripRef, agencyRef) {
2150
2221
  }
2151
2222
 
2152
2223
  export { ERROR_HANDLING_PATH, FORM_FIELD_KIND_SINGLE_BOOLEAN, classifyError, createDomainHook, useChecklists, useCloseout, useCollections, useExpenses, useFiles, useFloat, useForms, useItinerary, useManagerToday, useManagers, useParties, usePickups, useRooming, useSeating, useSnapshotList, useTravellerProfile, useVendors };
2153
- //# sourceMappingURL=chunk-7WXE5SUS.js.map
2154
- //# sourceMappingURL=chunk-7WXE5SUS.js.map
2224
+ //# sourceMappingURL=chunk-WTTGPWMR.js.map
2225
+ //# sourceMappingURL=chunk-WTTGPWMR.js.map