kaafil-react-uikit 0.3.1 → 0.4.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 (62) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/dist/admin/index.cjs +565 -387
  3. package/dist/admin/index.cjs.map +1 -1
  4. package/dist/admin/index.d.cts +2 -2
  5. package/dist/admin/index.d.ts +2 -2
  6. package/dist/admin/index.js +231 -53
  7. package/dist/admin/index.js.map +1 -1
  8. package/dist/{chunk-IU2UM5JP.cjs → chunk-2D4Y4LOT.cjs} +88 -48
  9. package/dist/chunk-2D4Y4LOT.cjs.map +1 -0
  10. package/dist/{chunk-KW5LH6KV.js → chunk-2JABR4FF.js} +3 -3
  11. package/dist/{chunk-KW5LH6KV.js.map → chunk-2JABR4FF.js.map} +1 -1
  12. package/dist/{chunk-YNHOPYBK.cjs → chunk-4LZXDRAD.cjs} +12 -12
  13. package/dist/{chunk-YNHOPYBK.cjs.map → chunk-4LZXDRAD.cjs.map} +1 -1
  14. package/dist/{chunk-YU2242AR.js → chunk-5DVTAEQC.js} +5 -5
  15. package/dist/{chunk-YU2242AR.js.map → chunk-5DVTAEQC.js.map} +1 -1
  16. package/dist/{chunk-A4Z2YBEF.js → chunk-ABWNV4N4.js} +86 -38
  17. package/dist/chunk-ABWNV4N4.js.map +1 -0
  18. package/dist/{chunk-GJ2J3PYN.js → chunk-BUE2GJDN.js} +3 -3
  19. package/dist/{chunk-GJ2J3PYN.js.map → chunk-BUE2GJDN.js.map} +1 -1
  20. package/dist/{chunk-UALMCHNW.cjs → chunk-CCJ5XNWY.cjs} +86 -38
  21. package/dist/chunk-CCJ5XNWY.cjs.map +1 -0
  22. package/dist/{chunk-VLP323AD.js → chunk-DABF4PUI.js} +3 -3
  23. package/dist/{chunk-VLP323AD.js.map → chunk-DABF4PUI.js.map} +1 -1
  24. package/dist/{chunk-HACH2NUM.cjs → chunk-DDSNXKFN.cjs} +9 -9
  25. package/dist/{chunk-HACH2NUM.cjs.map → chunk-DDSNXKFN.cjs.map} +1 -1
  26. package/dist/{chunk-ECCQW5LN.cjs → chunk-DUZG6A22.cjs} +4 -4
  27. package/dist/{chunk-ECCQW5LN.cjs.map → chunk-DUZG6A22.cjs.map} +1 -1
  28. package/dist/{chunk-JHMN6Q5R.js → chunk-EP4VCID7.js} +92 -47
  29. package/dist/chunk-EP4VCID7.js.map +1 -0
  30. package/dist/{chunk-57SUK3GD.cjs → chunk-KDGBJPPZ.cjs} +7 -7
  31. package/dist/{chunk-57SUK3GD.cjs.map → chunk-KDGBJPPZ.cjs.map} +1 -1
  32. package/dist/{chunk-BBC5EX5O.cjs → chunk-TPBKRXEB.cjs} +17 -17
  33. package/dist/{chunk-BBC5EX5O.cjs.map → chunk-TPBKRXEB.cjs.map} +1 -1
  34. package/dist/{chunk-RWLO6IFX.js → chunk-TV5QTT44.js} +3 -3
  35. package/dist/{chunk-RWLO6IFX.js.map → chunk-TV5QTT44.js.map} +1 -1
  36. package/dist/{chunk-RO2RNHQP.js → chunk-VAJ4D7EF.js} +56 -17
  37. package/dist/chunk-VAJ4D7EF.js.map +1 -0
  38. package/dist/{chunk-6TI6YQPE.cjs → chunk-XBYLSJOS.cjs} +168 -123
  39. package/dist/chunk-XBYLSJOS.cjs.map +1 -0
  40. package/dist/core/index.cjs +124 -121
  41. package/dist/core/index.cjs.map +1 -1
  42. package/dist/core/index.d.cts +10 -1
  43. package/dist/core/index.d.ts +10 -1
  44. package/dist/core/index.js +20 -19
  45. package/dist/core/index.js.map +1 -1
  46. package/dist/index.css +1 -1
  47. package/dist/manager/index.cjs +239 -239
  48. package/dist/manager/index.js +6 -6
  49. package/dist/tokens.json +5013 -0
  50. package/dist/traveller/index.cjs +46 -40
  51. package/dist/traveller/index.cjs.map +1 -1
  52. package/dist/traveller/index.js +16 -10
  53. package/dist/traveller/index.js.map +1 -1
  54. package/dist/{useAgencyTrip-BUYSIaC2.d.cts → useAgencyTrip-C1YmfuQq.d.cts} +35 -1
  55. package/dist/{useAgencyTrip-BzyJB1D9.d.ts → useAgencyTrip-gZB0NILH.d.ts} +35 -1
  56. package/package.json +4 -3
  57. package/dist/chunk-6TI6YQPE.cjs.map +0 -1
  58. package/dist/chunk-A4Z2YBEF.js.map +0 -1
  59. package/dist/chunk-IU2UM5JP.cjs.map +0 -1
  60. package/dist/chunk-JHMN6Q5R.js.map +0 -1
  61. package/dist/chunk-RO2RNHQP.js.map +0 -1
  62. package/dist/chunk-UALMCHNW.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { OfflineEngineContext } from './chunk-A4Z2YBEF.js';
1
+ import { OfflineEngineContext } from './chunk-ABWNV4N4.js';
2
2
  import { useContext, useState, useRef, useEffect } from 'react';
3
3
 
4
4
  var TICK_INTERVAL_MS = 1e3;
@@ -37,5 +37,5 @@ function useServerTime() {
37
37
  }
38
38
 
39
39
  export { useServerTime };
40
- //# sourceMappingURL=chunk-VLP323AD.js.map
41
- //# sourceMappingURL=chunk-VLP323AD.js.map
40
+ //# sourceMappingURL=chunk-DABF4PUI.js.map
41
+ //# sourceMappingURL=chunk-DABF4PUI.js.map
@@ -1 +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
+ {"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-DABF4PUI.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,16 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var chunkUALMCHNW_cjs = require('./chunk-UALMCHNW.cjs');
3
+ var chunkCCJ5XNWY_cjs = require('./chunk-CCJ5XNWY.cjs');
4
4
  var react = require('react');
5
5
 
6
6
  function isVersionConflict(op) {
7
7
  return op.status === "parked" && op.parkReason?.code === "CONFLICT_VERSION";
8
8
  }
9
9
  function useConflicts(tripRef) {
10
- const engine = react.useContext(chunkUALMCHNW_cjs.OfflineEngineContext);
10
+ const engine = react.useContext(chunkCCJ5XNWY_cjs.OfflineEngineContext);
11
11
  const [tick, forceRefresh] = react.useState(0);
12
- chunkUALMCHNW_cjs.useOfflineEvent("sync.error", () => forceRefresh((current) => current + 1));
13
- chunkUALMCHNW_cjs.useOfflineEvent("sync.drained", () => forceRefresh((current) => current + 1));
12
+ chunkCCJ5XNWY_cjs.useOfflineEvent("sync.error", () => forceRefresh((current) => current + 1));
13
+ chunkCCJ5XNWY_cjs.useOfflineEvent("sync.drained", () => forceRefresh((current) => current + 1));
14
14
  const conflictOps = react.useMemo(
15
15
  () => engine === null ? [] : engine.outbox.all().filter((op) => op.tripRef === tripRef && isVersionConflict(op)),
16
16
  // eslint-disable-next-line react-hooks/exhaustive-deps -- same reasoning, see the biome-ignore comment above.
@@ -47,8 +47,8 @@ function useConflicts(tripRef) {
47
47
  var lastKnown = /* @__PURE__ */ new WeakMap();
48
48
  var READ_CACHE_KEY = "manager.me.trips";
49
49
  function useManagerMe() {
50
- const client = chunkUALMCHNW_cjs.useKaafilClient();
51
- const engine = react.useContext(chunkUALMCHNW_cjs.OfflineEngineContext);
50
+ const client = chunkCCJ5XNWY_cjs.useKaafilClient();
51
+ const engine = react.useContext(chunkCCJ5XNWY_cjs.OfflineEngineContext);
52
52
  const [result, setResult] = react.useState(
53
53
  () => lastKnown.get(client) ?? readCached(engine) ?? { status: "loading" }
54
54
  );
@@ -87,7 +87,7 @@ function isTombstone(item) {
87
87
  return "_tombstone" in item && item._tombstone === true;
88
88
  }
89
89
  function useNotifications() {
90
- const client = chunkUALMCHNW_cjs.useKaafilClient();
90
+ const client = chunkCCJ5XNWY_cjs.useKaafilClient();
91
91
  const [result, setResult] = react.useState({ status: "loading" });
92
92
  react.useEffect(() => {
93
93
  let cancelled = false;
@@ -123,5 +123,5 @@ function useNotifications() {
123
123
  exports.useConflicts = useConflicts;
124
124
  exports.useManagerMe = useManagerMe;
125
125
  exports.useNotifications = useNotifications;
126
- //# sourceMappingURL=chunk-HACH2NUM.cjs.map
127
- //# sourceMappingURL=chunk-HACH2NUM.cjs.map
126
+ //# sourceMappingURL=chunk-DDSNXKFN.cjs.map
127
+ //# sourceMappingURL=chunk-DDSNXKFN.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/hooks/useConflicts.ts","../src/core/hooks/useManagerMe.ts","../src/core/hooks/useNotifications.ts"],"names":["useContext","OfflineEngineContext","useState","useOfflineEvent","useMemo","useKaafilClient","useEffect"],"mappings":"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","file":"chunk-HACH2NUM.cjs","sourcesContent":["'use client';\n\n// `08-core-hooks.md §2.7`. Every `409 CONFLICT_VERSION` op currently parked\n// and awaiting a human choice for this trip — entity-agnostic on purpose, so\n// the one shared `ConflictResolver` UI covers rooming/seating/checklist/\n// every versioned entity (`07-offline-and-sync.md §6`).\n//\n// VERIFIED SHAPE (`kaafil-js/src/offline/{outbox,reconcile}.ts`, this\n// session): a \"surface\" conflict reaches `Outbox.park()` with only\n// `{ status, code: 'CONFLICT_VERSION', userMessage }` — no row of either\n// side. The fresh server row, however, was already written into\n// `SnapshotStore` by `reconcileConflict()` before the park (its own \"why the\n// entity is re-pulled first\" header) whenever `op.entity` was set and the\n// re-read succeeded. `currentValue` below is read from there — the snapshot\n// — rather than invented on this hook's own read. `queuedValue` is the\n// parked op's own `body`, the intent that was never applied.\n\nimport type { OutboxOp } from 'kaafil-js/client';\nimport { useContext, useMemo, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport type { ConflictRecord } from '../types/offline.types';\nimport { useOfflineEvent } from './useOfflineEvent';\n\nexport interface ConflictsResult {\n readonly conflicts: readonly ConflictRecord[];\n readonly resolve: (opId: string, choice: 'keep-queued' | 'keep-current') => Promise<void>;\n}\n\nfunction isVersionConflict(op: OutboxOp): boolean {\n return op.status === 'parked' && op.parkReason?.code === 'CONFLICT_VERSION';\n}\n\nexport function useConflicts(tripRef: string): ConflictsResult {\n // NOT `useOfflineEngine()` (its throwing form) — called from inside a\n // domain hook's unconditional `build()` (`createDomainHook.ts`), including\n // the render(s) before `OfflineShell` has opened an engine. No engine yet\n // means no ops to have conflicted yet — `conflicts: []`, never a crash.\n const engine = useContext(OfflineEngineContext);\n // `tick` itself is never read below beyond being a `useMemo` dependency —\n // `engine.outbox.all()` is a plain synchronous read with no reactive\n // identity of its own, so bumping this on every `sync.error`/`sync.drained`\n // is what makes the memo below re-run on the events that can change what\n // it returns.\n const [tick, forceRefresh] = useState(0);\n\n useOfflineEvent('sync.error', () => forceRefresh((current) => current + 1));\n useOfflineEvent('sync.drained', () => forceRefresh((current) => current + 1));\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: tick is a deliberate side-channel dep, forcing a re-run against the outbox's own non-reactive state on every sync event (see its declaration above).\n const conflictOps = useMemo(\n () =>\n engine === null\n ? []\n : engine.outbox.all().filter((op) => op.tripRef === tripRef && isVersionConflict(op)),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- same reasoning, see the biome-ignore comment above.\n [engine, tripRef, tick],\n );\n\n const conflicts: readonly ConflictRecord[] =\n engine === null\n ? []\n : conflictOps.map((op) => {\n const currentValue =\n op.entity !== undefined\n ? engine.snapshot\n .list(op.tripRef, op.entity.listName)\n .find((row) => row.id === op.entity?.id)\n : undefined;\n return {\n opId: op.id,\n entity: op.entity?.listName ?? op.path,\n queuedValue: op.body,\n currentValue,\n };\n });\n\n async function resolve(opId: string, choice: 'keep-queued' | 'keep-current'): Promise<void> {\n if (engine === null) {\n return;\n }\n const op = engine.outbox.get(opId);\n if (op === undefined) {\n return;\n }\n if (choice === 'keep-current') {\n // `engine.discard()`, NOT `outbox.drop()`. The queue knows nothing about\n // the snapshot, so dropping a CREATE through it leaves the row the\n // manager just chose to discard on screen permanently: it is keyed on an\n // id only this device ever knew, so no pull, no delta and no tombstone\n // can ever remove it. `discard()` takes the optimistic row with the op.\n await engine.discard(opId);\n return;\n }\n // `keep-queued`: re-issue with the fresh version. The fresh row's own\n // `version` field is what `07-offline-and-sync.md §6` calls\n // `details.currentVersion`; reading it off the snapshot row this hook\n // already fetched above is the same value, sourced from the same\n // re-read `reconcileConflict()` already performed.\n const freshRow =\n op.entity !== undefined\n ? engine.snapshot\n .list(op.tripRef, op.entity.listName)\n .find((row) => row.id === op.entity?.id)\n : undefined;\n const freshVersion = freshRow?.version;\n await engine.outbox.reissue(opId, freshVersion !== undefined ? { ifMatch: freshVersion } : {});\n engine.drainer.kick();\n }\n\n return { conflicts, resolve };\n}\n","'use client';\n\n// `families/manager/01-surface.md`'s own \"trip-discovery gap\" note is now\n// stale — verified against the real engine: `GET /manager/me/trips`\n// (`client.managerMe.listTrips()`) resolves a manager's own live trip\n// assignments plus a sensible `defaultTripId`, and now also the manager's\n// own `fullName` (this build's own small engine addition —\n// `manager-me.dto.ts`'s `manager` field). This session's own product\n// decision overrides that spec note for `ManagerNowSection` and any future\n// manager composite that needs \"which trip(s) does THIS manager see\" —\n// never a host-supplied `assignedTrips` prop for those composites (that\n// prop still exists for `KaafilManagerApp`/`MyTripsScreen` themselves, per\n// spec, since a Surface's own trip-list PANE is a separate, larger\n// decision this hook does not revisit).\n//\n// Same deliberately minimal shape as `useManagerToday.ts`: a plain\n// `client.managerMe.listTrips()` call behind `useEffect`/`useState`, no\n// offline/outbox machinery (this is a live read with no mutation of its\n// own to queue), no capability gate (which trips a manager is assigned to\n// is an ingest fact, not a capability — `01-surface.md`'s own \"Gating\"\n// field, cited).\n\nimport type {\n KaafilClient,\n ManagerOwnTrip,\n ManagerOwnTripsResponse,\n OfflineEngine,\n} from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { useKaafilClient } from './useKaafilClient';\n\n// Re-exported so a composite (`ManagerTripSection.types.ts`, `TripHero.tsx`)\n// can name one trip row's shape without importing `kaafil-js/client`\n// directly — hard rule #1.\nexport type { ManagerOwnTrip };\n\nexport interface ManagerMeLoading {\n readonly status: 'loading';\n}\n\nexport interface ManagerMeError {\n readonly status: 'error';\n}\n\nexport interface ManagerMeReady extends ManagerOwnTripsResponse {\n readonly status: 'ready';\n /** The server timestamp this read landed with (`meta.serverTime`) — never\n * a local clock read (hard rule #6). */\n readonly syncedAt: string;\n}\n\nexport type ManagerMeResult = ManagerMeLoading | ManagerMeError | ManagerMeReady;\n\n/**\n * The last answer this read actually got, per open client — the same\n * module-level, instance-keyed `WeakMap` shape `../internal/ensureTripPulled.ts`\n * already uses, and for the same reason: five composites\n * (`KaafilManagerApp`, `ManagerNowSection`, `ManagerTripSection`,\n * `ManagerChecklistSection`, `ManagerMeSection`) call this hook, and the\n * field surface mounts and unmounts them as the manager moves between tabs.\n *\n * Without this, EVERY tab switch was a fresh network read that the field\n * persona is by definition not guaranteed to be able to make. On a manager's\n * phone with no signal the read rejected, the hook reported `'error'`, and\n * every one of those composites renders nothing at all for that status — so\n * losing the network and then touching a tab emptied the trip screen, with\n * no way back short of a page reload (`architecture/06-offline-first.md`:\n * the field half must serve what it last knew rather than blank).\n *\n * It is not a cache in front of `kaafil-js` (hard rule #3): nothing is\n * reconciled, no cursor is kept, no event is subscribed to, and a live read\n * still fires on every mount so \"re-mount to refresh\" keeps working. It only\n * stops a failed read from erasing an answer the session already has.\n *\n * It dies with the tab, which is the whole reason for the durable lane below.\n */\nconst lastKnown = new WeakMap<KaafilClient, ManagerMeReady>();\n\n/**\n * The key this hook's answer is cached under in the engine's durable read\n * cache (`kaafil-js`'s `engine.reads`, credential-scoped by the engine).\n *\n * The `WeakMap` above survives a tab switch; it does not survive a RELOAD, and\n * a reload is the field surface's normal case rather than an edge one — a\n * manager's phone discards a backgrounded tab on its own. So an offline\n * manager who reloaded got `'error'` from the very first read, and every\n * composite that reads this hook renders nothing for that status: a blank\n * trip list, with their queued writes sitting intact in IndexedDB behind a\n * screen with no way into them. Durable writes the manager could not reach\n * are indistinguishable, from where they are standing, from lost ones.\n *\n * This response cannot live in the snapshot store: its rows carry a\n * `tripRef`, a name and a status but no `id` and no `version`, so there is\n * nothing for a version-guarded merge to guard (`kaafil-js/src/offline/\n * reads.ts` explains the split at length). It is a whole answer or nothing,\n * which is exactly the promise `engine.reads` makes.\n */\nconst READ_CACHE_KEY = 'manager.me.trips';\n\n/** What is actually persisted — the response plus the `meta.serverTime` it\n * landed with. The store keeps `syncedAt` itself, so only the body goes in. */\ntype CachedManagerMe = ManagerOwnTripsResponse;\n\n/**\n * `client.managerMe.listTrips()` behind a plain fetch-on-mount effect —\n * this read takes no parameter (it resolves entirely off the authenticated\n * principal, `manager-me.routes.ts`'s own header, cited), so it never\n * re-fetches on a prop change the way `useManagerToday(tripRef)` does.\n * A caller wanting \"refresh now\" re-mounts this hook's owning component.\n */\nexport function useManagerMe(): ManagerMeResult {\n const client = useKaafilClient();\n // The raw context, not `useOfflineEngine()`'s throwing form: this hook is\n // read by desk composites too, and a desk mount has no `OfflineShell` and\n // therefore no engine. With none, everything below degrades to exactly the\n // in-memory behaviour this hook had before — which is right for a surface\n // that is online by definition.\n const engine = useContext(OfflineEngineContext);\n\n // Seeded from what is already known, in order: this session's own answer,\n // then the durable one from a previous session. Both beat a skeleton, and\n // the durable one is the only one that exists after a reload.\n const [result, setResult] = useState<ManagerMeResult>(\n () => lastKnown.get(client) ?? readCached(engine) ?? { status: 'loading' },\n );\n\n useEffect(() => {\n let cancelled = false;\n setResult(lastKnown.get(client) ?? readCached(engine) ?? { status: 'loading' });\n client.managerMe\n .listTrips()\n .then((response) => {\n const { meta, ...data } = response;\n const ready: ManagerMeReady = { status: 'ready', ...data, syncedAt: meta.serverTime };\n // Recorded before the cancellation check: a response that landed is\n // true regardless of whether THIS component is still mounted to\n // render it, and the next mount should not have to re-ask.\n lastKnown.set(client, ready);\n // Fire-and-forget, and deliberately not awaited before the state is\n // set: painting the fresh answer must not wait on a disk write, and a\n // failed write costs the NEXT reload's first paint rather than this\n // one's correctness. `meta.serverTime`, never a device clock — the\n // store has no setter that would accept one (hard rule #6).\n void engine?.reads.set(READ_CACHE_KEY, data, meta.serverTime).catch(() => {\n // A full or unavailable store is not this read's problem to report.\n // The live answer above is already rendered and already correct.\n });\n if (!cancelled) {\n setResult(ready);\n }\n })\n .catch(() => {\n if (cancelled) {\n return;\n }\n // `'error'` ONLY when NOTHING is known — not this session, not the\n // last one. A read that failed after one succeeded means the device\n // is offline or the engine is unreachable, not that the manager\n // stopped being assigned to their trips; reporting `'error'` there is\n // what blanked the field surface, and after a reload it blanked it\n // from the very first read. `syncedAt` on the held value already\n // tells a caller how old the reading is.\n setResult(lastKnown.get(client) ?? readCached(engine) ?? { status: 'error' });\n });\n return () => {\n cancelled = true;\n };\n }, [client, engine]);\n\n return result;\n}\n\n/**\n * The last answer a PREVIOUS session got, if this surface has a durable store\n * at all. Returns `undefined` rather than an `'error'` so every call site can\n * fall through to the next-best source in one expression.\n */\nfunction readCached(engine: OfflineEngine | null): ManagerMeReady | undefined {\n const cached = engine?.reads.get<CachedManagerMe>(READ_CACHE_KEY);\n if (cached === undefined) {\n return undefined;\n }\n return { status: 'ready', ...cached.value, syncedAt: cached.syncedAt };\n}\n","'use client';\n\n// `08-core-hooks.md §3` table, row `useNotifications()`. Primary SDK surface\n// (verified, `kaafil-js/src/resources/notifications.ts`):\n// `notifications.list` (`managerAuth`-only), `notifications.markRead`\n// (likewise `managerAuth`-only). NOT trip-scoped — the only hook in this\n// table taking no `tripRef`. `managerAuth` is the ONLY scheme either\n// operation accepts, so this hook is structurally unreachable from the\n// `admin`/`traveller` families. `list` takes an optional `?since=` delta\n// cursor passed straight through, never a UIKit-invented filter language.\n//\n// Deliberately the same minimal shape as `useManagerToday.ts`/\n// `useManagerMe.ts`: a plain fetch-on-mount effect, no offline/outbox\n// machinery. `families/manager/11-offline-sync-and-notifications.md\n// §NotificationsFeed`'s own \"write: markRead enqueues through the outbox\n// with a fixed idempotency key\" contract is that COMPOSITE's job once it\n// is built — this hook exists now only so `ManagerNowSection`'s header\n// bell can show a real (honestly zero, until something writes a\n// notification) unread count, which needs `list()` alone. `markRead` is\n// still exposed as a direct SDK call (never mints its own idempotency key,\n// hard rule #4 — mark-read is naturally idempotent server-side per the\n// SDK's own doc comment, so no key is needed here at all) so an early\n// caller isn't blocked, but a future `NotificationsFeed` composite should\n// route its OWN write through the outbox instead of reusing this hook's\n// `markRead` as-is.\n//\n// The wire's `items` array is a discriminated union (a live row, or a\n// `{ _tombstone: true; id; version; deletedAt }` delete marker) — this hook\n// filters tombstones out of `notifications` (a plain unread-count/list\n// consumer has no use for a deleted row) rather than exposing the union to\n// every caller.\n\nimport type { NotificationsResource } from 'kaafil-js/client';\nimport { useEffect, useState } from 'react';\nimport { useKaafilClient } from './useKaafilClient';\n\n// `notifications.ts`'s resource module exports no named response type of its\n// own (unlike `manager-me.ts`'s `ManagerOwnTripsResponse`) — derived off the\n// resource's own method signature instead, `useFiles.ts`/`useExpenses.ts`'s\n// established precedent for this exact situation.\ntype NotificationListResponse = Awaited<ReturnType<NotificationsResource['list']>>;\ntype NotificationListItem = NotificationListResponse['items'][number];\n\n/** The live-row half of `NotificationListItem`'s union — a tombstone never\n * reaches a caller of this hook (see this file's own header). */\nexport type ManagerNotification = Exclude<NotificationListItem, { _tombstone: true }>;\n\nexport interface NotificationsLoading {\n readonly status: 'loading';\n}\n\nexport interface NotificationsError {\n readonly status: 'error';\n}\n\nexport interface NotificationsReady {\n readonly status: 'ready';\n readonly notifications: readonly ManagerNotification[];\n /** `notifications.filter((n) => n.readAt === null).length` — the wire\n * carries no numeric unread field of its own (verified against\n * `NotificationListResponse`), so this is a real derivation off real\n * rows, never a fabricated count. */\n readonly unreadCount: number;\n readonly markRead: (id: string) => Promise<void>;\n /** The server timestamp this read landed with (`meta.serverTime`) —\n * never a local clock read (hard rule #6). */\n readonly syncedAt: string;\n}\n\nexport type NotificationsResult = NotificationsLoading | NotificationsError | NotificationsReady;\n\nfunction isTombstone(\n item: NotificationListItem,\n): item is Extract<NotificationListItem, { _tombstone: true }> {\n return '_tombstone' in item && item._tombstone === true;\n}\n\nexport function useNotifications(): NotificationsResult {\n const client = useKaafilClient();\n const [result, setResult] = useState<NotificationsResult>({ status: 'loading' });\n\n useEffect(() => {\n let cancelled = false;\n setResult({ status: 'loading' });\n client.notifications\n .list()\n .then((response) => {\n if (cancelled) {\n return;\n }\n const notifications = response.items.filter(\n (item): item is ManagerNotification => !isTombstone(item),\n );\n setResult({\n status: 'ready',\n notifications,\n unreadCount: notifications.filter((n) => n.readAt === null).length,\n markRead: async (id: string) => {\n await client.notifications.markRead({ id });\n },\n syncedAt: response.meta.serverTime,\n });\n })\n .catch(() => {\n if (!cancelled) {\n setResult({ status: 'error' });\n }\n });\n return () => {\n cancelled = true;\n };\n }, [client]);\n\n return result;\n}\n"]}
1
+ {"version":3,"sources":["../src/core/hooks/useConflicts.ts","../src/core/hooks/useManagerMe.ts","../src/core/hooks/useNotifications.ts"],"names":["useContext","OfflineEngineContext","useState","useOfflineEvent","useMemo","useKaafilClient","useEffect"],"mappings":"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","file":"chunk-DDSNXKFN.cjs","sourcesContent":["'use client';\n\n// `08-core-hooks.md §2.7`. Every `409 CONFLICT_VERSION` op currently parked\n// and awaiting a human choice for this trip — entity-agnostic on purpose, so\n// the one shared `ConflictResolver` UI covers rooming/seating/checklist/\n// every versioned entity (`07-offline-and-sync.md §6`).\n//\n// VERIFIED SHAPE (`kaafil-js/src/offline/{outbox,reconcile}.ts`, this\n// session): a \"surface\" conflict reaches `Outbox.park()` with only\n// `{ status, code: 'CONFLICT_VERSION', userMessage }` — no row of either\n// side. The fresh server row, however, was already written into\n// `SnapshotStore` by `reconcileConflict()` before the park (its own \"why the\n// entity is re-pulled first\" header) whenever `op.entity` was set and the\n// re-read succeeded. `currentValue` below is read from there — the snapshot\n// — rather than invented on this hook's own read. `queuedValue` is the\n// parked op's own `body`, the intent that was never applied.\n\nimport type { OutboxOp } from 'kaafil-js/client';\nimport { useContext, useMemo, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport type { ConflictRecord } from '../types/offline.types';\nimport { useOfflineEvent } from './useOfflineEvent';\n\nexport interface ConflictsResult {\n readonly conflicts: readonly ConflictRecord[];\n readonly resolve: (opId: string, choice: 'keep-queued' | 'keep-current') => Promise<void>;\n}\n\nfunction isVersionConflict(op: OutboxOp): boolean {\n return op.status === 'parked' && op.parkReason?.code === 'CONFLICT_VERSION';\n}\n\nexport function useConflicts(tripRef: string): ConflictsResult {\n // NOT `useOfflineEngine()` (its throwing form) — called from inside a\n // domain hook's unconditional `build()` (`createDomainHook.ts`), including\n // the render(s) before `OfflineShell` has opened an engine. No engine yet\n // means no ops to have conflicted yet — `conflicts: []`, never a crash.\n const engine = useContext(OfflineEngineContext);\n // `tick` itself is never read below beyond being a `useMemo` dependency —\n // `engine.outbox.all()` is a plain synchronous read with no reactive\n // identity of its own, so bumping this on every `sync.error`/`sync.drained`\n // is what makes the memo below re-run on the events that can change what\n // it returns.\n const [tick, forceRefresh] = useState(0);\n\n useOfflineEvent('sync.error', () => forceRefresh((current) => current + 1));\n useOfflineEvent('sync.drained', () => forceRefresh((current) => current + 1));\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: tick is a deliberate side-channel dep, forcing a re-run against the outbox's own non-reactive state on every sync event (see its declaration above).\n const conflictOps = useMemo(\n () =>\n engine === null\n ? []\n : engine.outbox.all().filter((op) => op.tripRef === tripRef && isVersionConflict(op)),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- same reasoning, see the biome-ignore comment above.\n [engine, tripRef, tick],\n );\n\n const conflicts: readonly ConflictRecord[] =\n engine === null\n ? []\n : conflictOps.map((op) => {\n const currentValue =\n op.entity !== undefined\n ? engine.snapshot\n .list(op.tripRef, op.entity.listName)\n .find((row) => row.id === op.entity?.id)\n : undefined;\n return {\n opId: op.id,\n entity: op.entity?.listName ?? op.path,\n queuedValue: op.body,\n currentValue,\n };\n });\n\n async function resolve(opId: string, choice: 'keep-queued' | 'keep-current'): Promise<void> {\n if (engine === null) {\n return;\n }\n const op = engine.outbox.get(opId);\n if (op === undefined) {\n return;\n }\n if (choice === 'keep-current') {\n // `engine.discard()`, NOT `outbox.drop()`. The queue knows nothing about\n // the snapshot, so dropping a CREATE through it leaves the row the\n // manager just chose to discard on screen permanently: it is keyed on an\n // id only this device ever knew, so no pull, no delta and no tombstone\n // can ever remove it. `discard()` takes the optimistic row with the op.\n await engine.discard(opId);\n return;\n }\n // `keep-queued`: re-issue with the fresh version. The fresh row's own\n // `version` field is what `07-offline-and-sync.md §6` calls\n // `details.currentVersion`; reading it off the snapshot row this hook\n // already fetched above is the same value, sourced from the same\n // re-read `reconcileConflict()` already performed.\n const freshRow =\n op.entity !== undefined\n ? engine.snapshot\n .list(op.tripRef, op.entity.listName)\n .find((row) => row.id === op.entity?.id)\n : undefined;\n const freshVersion = freshRow?.version;\n await engine.outbox.reissue(opId, freshVersion !== undefined ? { ifMatch: freshVersion } : {});\n engine.drainer.kick();\n }\n\n return { conflicts, resolve };\n}\n","'use client';\n\n// `families/manager/01-surface.md`'s own \"trip-discovery gap\" note is now\n// stale — verified against the real engine: `GET /manager/me/trips`\n// (`client.managerMe.listTrips()`) resolves a manager's own live trip\n// assignments plus a sensible `defaultTripId`, and now also the manager's\n// own `fullName` (this build's own small engine addition —\n// `manager-me.dto.ts`'s `manager` field). This session's own product\n// decision overrides that spec note for `ManagerNowSection` and any future\n// manager composite that needs \"which trip(s) does THIS manager see\" —\n// never a host-supplied `assignedTrips` prop for those composites (that\n// prop still exists for `KaafilManagerApp`/`MyTripsScreen` themselves, per\n// spec, since a Surface's own trip-list PANE is a separate, larger\n// decision this hook does not revisit).\n//\n// Same deliberately minimal shape as `useManagerToday.ts`: a plain\n// `client.managerMe.listTrips()` call behind `useEffect`/`useState`, no\n// offline/outbox machinery (this is a live read with no mutation of its\n// own to queue), no capability gate (which trips a manager is assigned to\n// is an ingest fact, not a capability — `01-surface.md`'s own \"Gating\"\n// field, cited).\n\nimport type {\n KaafilClient,\n ManagerOwnTrip,\n ManagerOwnTripsResponse,\n OfflineEngine,\n} from 'kaafil-js/client';\nimport { useContext, useEffect, useState } from 'react';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { useKaafilClient } from './useKaafilClient';\n\n// Re-exported so a composite (`ManagerTripSection.types.ts`, `TripHero.tsx`)\n// can name one trip row's shape without importing `kaafil-js/client`\n// directly — hard rule #1.\nexport type { ManagerOwnTrip };\n\nexport interface ManagerMeLoading {\n readonly status: 'loading';\n}\n\nexport interface ManagerMeError {\n readonly status: 'error';\n}\n\nexport interface ManagerMeReady extends ManagerOwnTripsResponse {\n readonly status: 'ready';\n /** The server timestamp this read landed with (`meta.serverTime`) — never\n * a local clock read (hard rule #6). */\n readonly syncedAt: string;\n}\n\nexport type ManagerMeResult = ManagerMeLoading | ManagerMeError | ManagerMeReady;\n\n/**\n * The last answer this read actually got, per open client — the same\n * module-level, instance-keyed `WeakMap` shape `../internal/ensureTripPulled.ts`\n * already uses, and for the same reason: five composites\n * (`KaafilManagerApp`, `ManagerNowSection`, `ManagerTripSection`,\n * `ManagerChecklistSection`, `ManagerMeSection`) call this hook, and the\n * field surface mounts and unmounts them as the manager moves between tabs.\n *\n * Without this, EVERY tab switch was a fresh network read that the field\n * persona is by definition not guaranteed to be able to make. On a manager's\n * phone with no signal the read rejected, the hook reported `'error'`, and\n * every one of those composites renders nothing at all for that status — so\n * losing the network and then touching a tab emptied the trip screen, with\n * no way back short of a page reload (`architecture/06-offline-first.md`:\n * the field half must serve what it last knew rather than blank).\n *\n * It is not a cache in front of `kaafil-js` (hard rule #3): nothing is\n * reconciled, no cursor is kept, no event is subscribed to, and a live read\n * still fires on every mount so \"re-mount to refresh\" keeps working. It only\n * stops a failed read from erasing an answer the session already has.\n *\n * It dies with the tab, which is the whole reason for the durable lane below.\n */\nconst lastKnown = new WeakMap<KaafilClient, ManagerMeReady>();\n\n/**\n * The key this hook's answer is cached under in the engine's durable read\n * cache (`kaafil-js`'s `engine.reads`, credential-scoped by the engine).\n *\n * The `WeakMap` above survives a tab switch; it does not survive a RELOAD, and\n * a reload is the field surface's normal case rather than an edge one — a\n * manager's phone discards a backgrounded tab on its own. So an offline\n * manager who reloaded got `'error'` from the very first read, and every\n * composite that reads this hook renders nothing for that status: a blank\n * trip list, with their queued writes sitting intact in IndexedDB behind a\n * screen with no way into them. Durable writes the manager could not reach\n * are indistinguishable, from where they are standing, from lost ones.\n *\n * This response cannot live in the snapshot store: its rows carry a\n * `tripRef`, a name and a status but no `id` and no `version`, so there is\n * nothing for a version-guarded merge to guard (`kaafil-js/src/offline/\n * reads.ts` explains the split at length). It is a whole answer or nothing,\n * which is exactly the promise `engine.reads` makes.\n */\nconst READ_CACHE_KEY = 'manager.me.trips';\n\n/** What is actually persisted — the response plus the `meta.serverTime` it\n * landed with. The store keeps `syncedAt` itself, so only the body goes in. */\ntype CachedManagerMe = ManagerOwnTripsResponse;\n\n/**\n * `client.managerMe.listTrips()` behind a plain fetch-on-mount effect —\n * this read takes no parameter (it resolves entirely off the authenticated\n * principal, `manager-me.routes.ts`'s own header, cited), so it never\n * re-fetches on a prop change the way `useManagerToday(tripRef)` does.\n * A caller wanting \"refresh now\" re-mounts this hook's owning component.\n */\nexport function useManagerMe(): ManagerMeResult {\n const client = useKaafilClient();\n // The raw context, not `useOfflineEngine()`'s throwing form: this hook is\n // read by desk composites too, and a desk mount has no `OfflineShell` and\n // therefore no engine. With none, everything below degrades to exactly the\n // in-memory behaviour this hook had before — which is right for a surface\n // that is online by definition.\n const engine = useContext(OfflineEngineContext);\n\n // Seeded from what is already known, in order: this session's own answer,\n // then the durable one from a previous session. Both beat a skeleton, and\n // the durable one is the only one that exists after a reload.\n const [result, setResult] = useState<ManagerMeResult>(\n () => lastKnown.get(client) ?? readCached(engine) ?? { status: 'loading' },\n );\n\n useEffect(() => {\n let cancelled = false;\n setResult(lastKnown.get(client) ?? readCached(engine) ?? { status: 'loading' });\n client.managerMe\n .listTrips()\n .then((response) => {\n const { meta, ...data } = response;\n const ready: ManagerMeReady = { status: 'ready', ...data, syncedAt: meta.serverTime };\n // Recorded before the cancellation check: a response that landed is\n // true regardless of whether THIS component is still mounted to\n // render it, and the next mount should not have to re-ask.\n lastKnown.set(client, ready);\n // Fire-and-forget, and deliberately not awaited before the state is\n // set: painting the fresh answer must not wait on a disk write, and a\n // failed write costs the NEXT reload's first paint rather than this\n // one's correctness. `meta.serverTime`, never a device clock — the\n // store has no setter that would accept one (hard rule #6).\n void engine?.reads.set(READ_CACHE_KEY, data, meta.serverTime).catch(() => {\n // A full or unavailable store is not this read's problem to report.\n // The live answer above is already rendered and already correct.\n });\n if (!cancelled) {\n setResult(ready);\n }\n })\n .catch(() => {\n if (cancelled) {\n return;\n }\n // `'error'` ONLY when NOTHING is known — not this session, not the\n // last one. A read that failed after one succeeded means the device\n // is offline or the engine is unreachable, not that the manager\n // stopped being assigned to their trips; reporting `'error'` there is\n // what blanked the field surface, and after a reload it blanked it\n // from the very first read. `syncedAt` on the held value already\n // tells a caller how old the reading is.\n setResult(lastKnown.get(client) ?? readCached(engine) ?? { status: 'error' });\n });\n return () => {\n cancelled = true;\n };\n }, [client, engine]);\n\n return result;\n}\n\n/**\n * The last answer a PREVIOUS session got, if this surface has a durable store\n * at all. Returns `undefined` rather than an `'error'` so every call site can\n * fall through to the next-best source in one expression.\n */\nfunction readCached(engine: OfflineEngine | null): ManagerMeReady | undefined {\n const cached = engine?.reads.get<CachedManagerMe>(READ_CACHE_KEY);\n if (cached === undefined) {\n return undefined;\n }\n return { status: 'ready', ...cached.value, syncedAt: cached.syncedAt };\n}\n","'use client';\n\n// `08-core-hooks.md §3` table, row `useNotifications()`. Primary SDK surface\n// (verified, `kaafil-js/src/resources/notifications.ts`):\n// `notifications.list` (`managerAuth`-only), `notifications.markRead`\n// (likewise `managerAuth`-only). NOT trip-scoped — the only hook in this\n// table taking no `tripRef`. `managerAuth` is the ONLY scheme either\n// operation accepts, so this hook is structurally unreachable from the\n// `admin`/`traveller` families. `list` takes an optional `?since=` delta\n// cursor passed straight through, never a UIKit-invented filter language.\n//\n// Deliberately the same minimal shape as `useManagerToday.ts`/\n// `useManagerMe.ts`: a plain fetch-on-mount effect, no offline/outbox\n// machinery. `families/manager/11-offline-sync-and-notifications.md\n// §NotificationsFeed`'s own \"write: markRead enqueues through the outbox\n// with a fixed idempotency key\" contract is that COMPOSITE's job once it\n// is built — this hook exists now only so `ManagerNowSection`'s header\n// bell can show a real (honestly zero, until something writes a\n// notification) unread count, which needs `list()` alone. `markRead` is\n// still exposed as a direct SDK call (never mints its own idempotency key,\n// hard rule #4 — mark-read is naturally idempotent server-side per the\n// SDK's own doc comment, so no key is needed here at all) so an early\n// caller isn't blocked, but a future `NotificationsFeed` composite should\n// route its OWN write through the outbox instead of reusing this hook's\n// `markRead` as-is.\n//\n// The wire's `items` array is a discriminated union (a live row, or a\n// `{ _tombstone: true; id; version; deletedAt }` delete marker) — this hook\n// filters tombstones out of `notifications` (a plain unread-count/list\n// consumer has no use for a deleted row) rather than exposing the union to\n// every caller.\n\nimport type { NotificationsResource } from 'kaafil-js/client';\nimport { useEffect, useState } from 'react';\nimport { useKaafilClient } from './useKaafilClient';\n\n// `notifications.ts`'s resource module exports no named response type of its\n// own (unlike `manager-me.ts`'s `ManagerOwnTripsResponse`) — derived off the\n// resource's own method signature instead, `useFiles.ts`/`useExpenses.ts`'s\n// established precedent for this exact situation.\ntype NotificationListResponse = Awaited<ReturnType<NotificationsResource['list']>>;\ntype NotificationListItem = NotificationListResponse['items'][number];\n\n/** The live-row half of `NotificationListItem`'s union — a tombstone never\n * reaches a caller of this hook (see this file's own header). */\nexport type ManagerNotification = Exclude<NotificationListItem, { _tombstone: true }>;\n\nexport interface NotificationsLoading {\n readonly status: 'loading';\n}\n\nexport interface NotificationsError {\n readonly status: 'error';\n}\n\nexport interface NotificationsReady {\n readonly status: 'ready';\n readonly notifications: readonly ManagerNotification[];\n /** `notifications.filter((n) => n.readAt === null).length` — the wire\n * carries no numeric unread field of its own (verified against\n * `NotificationListResponse`), so this is a real derivation off real\n * rows, never a fabricated count. */\n readonly unreadCount: number;\n readonly markRead: (id: string) => Promise<void>;\n /** The server timestamp this read landed with (`meta.serverTime`) —\n * never a local clock read (hard rule #6). */\n readonly syncedAt: string;\n}\n\nexport type NotificationsResult = NotificationsLoading | NotificationsError | NotificationsReady;\n\nfunction isTombstone(\n item: NotificationListItem,\n): item is Extract<NotificationListItem, { _tombstone: true }> {\n return '_tombstone' in item && item._tombstone === true;\n}\n\nexport function useNotifications(): NotificationsResult {\n const client = useKaafilClient();\n const [result, setResult] = useState<NotificationsResult>({ status: 'loading' });\n\n useEffect(() => {\n let cancelled = false;\n setResult({ status: 'loading' });\n client.notifications\n .list()\n .then((response) => {\n if (cancelled) {\n return;\n }\n const notifications = response.items.filter(\n (item): item is ManagerNotification => !isTombstone(item),\n );\n setResult({\n status: 'ready',\n notifications,\n unreadCount: notifications.filter((n) => n.readAt === null).length,\n markRead: async (id: string) => {\n await client.notifications.markRead({ id });\n },\n syncedAt: response.meta.serverTime,\n });\n })\n .catch(() => {\n if (!cancelled) {\n setResult({ status: 'error' });\n }\n });\n return () => {\n cancelled = true;\n };\n }, [client]);\n\n return result;\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkUALMCHNW_cjs = require('./chunk-UALMCHNW.cjs');
3
+ var chunkCCJ5XNWY_cjs = require('./chunk-CCJ5XNWY.cjs');
4
4
  var react = require('react');
5
5
 
6
6
  var TICK_INTERVAL_MS = 1e3;
7
7
  function useServerTime() {
8
- const engine = react.useContext(chunkUALMCHNW_cjs.OfflineEngineContext);
8
+ const engine = react.useContext(chunkCCJ5XNWY_cjs.OfflineEngineContext);
9
9
  const [lastServerTime, setLastServerTime] = react.useState(void 0);
10
10
  const observedAtPerfRef = react.useRef(void 0);
11
11
  const [, forceTick] = react.useState(0);
@@ -39,5 +39,5 @@ function useServerTime() {
39
39
  }
40
40
 
41
41
  exports.useServerTime = useServerTime;
42
- //# sourceMappingURL=chunk-ECCQW5LN.cjs.map
43
- //# sourceMappingURL=chunk-ECCQW5LN.cjs.map
42
+ //# sourceMappingURL=chunk-DUZG6A22.cjs.map
43
+ //# sourceMappingURL=chunk-DUZG6A22.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/hooks/useServerTime.ts"],"names":["useContext","OfflineEngineContext","useState","useRef","useEffect"],"mappings":";;;;;AA8BA,IAAM,gBAAA,GAAmB,GAAA;AAElB,SAAS,aAAA,GAA+B;AAC7C,EAAA,MAAM,MAAA,GAASA,iBAAWC,sCAAoB,CAAA;AAC9C,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIC,eAA6B,MAAS,CAAA;AAClF,EAAA,MAAM,iBAAA,GAAoBC,aAA2B,MAAS,CAAA;AAC9D,EAAA,MAAM,GAAG,SAAS,CAAA,GAAID,eAAS,CAAC,CAAA;AAEhC,EAAAE,eAAA,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,EAAAA,eAAA,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-ECCQW5LN.cjs","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
+ {"version":3,"sources":["../src/core/hooks/useServerTime.ts"],"names":["useContext","OfflineEngineContext","useState","useRef","useEffect"],"mappings":";;;;;AA8BA,IAAM,gBAAA,GAAmB,GAAA;AAElB,SAAS,aAAA,GAA+B;AAC7C,EAAA,MAAM,MAAA,GAASA,iBAAWC,sCAAoB,CAAA;AAC9C,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIC,eAA6B,MAAS,CAAA;AAClF,EAAA,MAAM,iBAAA,GAAoBC,aAA2B,MAAS,CAAA;AAC9D,EAAA,MAAM,GAAG,SAAS,CAAA,GAAID,eAAS,CAAC,CAAA;AAEhC,EAAAE,eAAA,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,EAAAA,eAAA,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-DUZG6A22.cjs","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-A4Z2YBEF.js';
1
+ import { OfflineEngineContext, KaafilClientContext, useCapabilityRows, UNKNOWN_CAPABILITY_TRIPLE, deriveCapabilityTriple, darkReasonFor, useKaafilClient, useOfflineMutation, usePersona, isDevelopment, useOfflineEvent } from './chunk-ABWNV4N4.js';
2
2
  import { UnsatisfiableSchemeError, isKaafilError, SyncOpMethod, normalizeDateTimeInput } 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';
@@ -726,9 +726,19 @@ var useExpensesInternal = createDomainHook("expenses", (tripRef) => {
726
726
  useEffect(() => {
727
727
  void refreshTotals();
728
728
  }, [refreshTotals]);
729
+ const engine = useContext(OfflineEngineContext);
730
+ useEffect(() => {
731
+ if (engine === null) {
732
+ return;
733
+ }
734
+ const unsubscribe = engine.events.on("sync.drained", () => {
735
+ void refreshTotals();
736
+ });
737
+ return unsubscribe;
738
+ }, [engine, refreshTotals]);
729
739
  const logMutation = useOfflineMutation({
730
740
  tripRef,
731
- enqueue: (engine, input) => engine.enqueue({
741
+ enqueue: (engine2, input) => engine2.enqueue({
732
742
  tripRef,
733
743
  method: SyncOpMethod.Post,
734
744
  path: `/api/v1/trips/${tripRef}/expenses`,
@@ -749,9 +759,9 @@ var useExpensesInternal = createDomainHook("expenses", (tripRef) => {
749
759
  });
750
760
  const linkReceiptMutation = useOfflineMutation({
751
761
  tripRef,
752
- enqueue: (engine, input) => {
762
+ enqueue: (engine2, input) => {
753
763
  const { expenseId, ...body } = input;
754
- return engine.enqueue({
764
+ return engine2.enqueue({
755
765
  tripRef,
756
766
  method: SyncOpMethod.Patch,
757
767
  path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/receipt`,
@@ -772,9 +782,9 @@ var useExpensesInternal = createDomainHook("expenses", (tripRef) => {
772
782
  });
773
783
  const voidMutation = useOfflineMutation({
774
784
  tripRef,
775
- enqueue: (engine, input) => {
785
+ enqueue: (engine2, input) => {
776
786
  const { expenseId, version, ...body } = input;
777
- return engine.enqueue({
787
+ return engine2.enqueue({
778
788
  tripRef,
779
789
  method: SyncOpMethod.Delete,
780
790
  path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/void`,
@@ -787,9 +797,9 @@ var useExpensesInternal = createDomainHook("expenses", (tripRef) => {
787
797
  });
788
798
  const submitClaimMutation = useOfflineMutation({
789
799
  tripRef,
790
- enqueue: (engine, input) => {
800
+ enqueue: (engine2, input) => {
791
801
  const { expenseId } = input;
792
- return engine.enqueue({
802
+ return engine2.enqueue({
793
803
  tripRef,
794
804
  method: SyncOpMethod.Post,
795
805
  path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/claim`,
@@ -800,9 +810,9 @@ var useExpensesInternal = createDomainHook("expenses", (tripRef) => {
800
810
  });
801
811
  const withdrawClaimMutation = useOfflineMutation({
802
812
  tripRef,
803
- enqueue: (engine, input) => {
813
+ enqueue: (engine2, input) => {
804
814
  const { expenseId, version } = input;
805
- return engine.enqueue({
815
+ return engine2.enqueue({
806
816
  tripRef,
807
817
  method: SyncOpMethod.Delete,
808
818
  path: `/api/v1/trips/${tripRef}/expenses/${expenseId}/claim/withdraw`,
@@ -1269,6 +1279,16 @@ function useItinerary(tripRef) {
1269
1279
  useEffect(() => {
1270
1280
  void refresh();
1271
1281
  }, [refresh]);
1282
+ const engine = useContext(OfflineEngineContext);
1283
+ const refreshAfterDrain = useCallback(() => {
1284
+ if (engine === null) {
1285
+ return;
1286
+ }
1287
+ const unsubscribe = engine.events.on("sync.drained", () => {
1288
+ unsubscribe();
1289
+ void refresh();
1290
+ });
1291
+ }, [engine, refresh]);
1272
1292
  const [changeLog, setChangeLog] = useState([]);
1273
1293
  const [changeLogLoaded, setChangeLogLoaded] = useState(false);
1274
1294
  const [changeLogFetchFailed, setChangeLogFetchFailed] = useState(false);
@@ -1287,9 +1307,9 @@ function useItinerary(tripRef) {
1287
1307
  );
1288
1308
  const patchDayMutation = useOfflineMutation({
1289
1309
  tripRef,
1290
- enqueue: (engine, input) => {
1310
+ enqueue: (engine2, input) => {
1291
1311
  const { dayIndex, version, ...body } = input;
1292
- return engine.enqueue({
1312
+ return engine2.enqueue({
1293
1313
  tripRef,
1294
1314
  method: SyncOpMethod.Patch,
1295
1315
  path: `/api/v1/trips/${tripRef}/itinerary/days/${dayIndex}`,
@@ -1305,13 +1325,14 @@ function useItinerary(tripRef) {
1305
1325
  async (input) => {
1306
1326
  const op = await patchDayMutate(input);
1307
1327
  void refresh();
1328
+ refreshAfterDrain();
1308
1329
  return op;
1309
1330
  },
1310
- [patchDayMutate, refresh]
1331
+ [patchDayMutate, refresh, refreshAfterDrain]
1311
1332
  );
1312
1333
  const addItemMutation = useOfflineMutation({
1313
1334
  tripRef,
1314
- enqueue: (engine, input) => engine.enqueue({
1335
+ enqueue: (engine2, input) => engine2.enqueue({
1315
1336
  tripRef,
1316
1337
  method: SyncOpMethod.Post,
1317
1338
  path: `/api/v1/trips/${tripRef}/itinerary/items`,
@@ -1325,15 +1346,16 @@ function useItinerary(tripRef) {
1325
1346
  async (input) => {
1326
1347
  const op = await addItemMutate(input);
1327
1348
  void refresh();
1349
+ refreshAfterDrain();
1328
1350
  return op;
1329
1351
  },
1330
- [addItemMutate, refresh]
1352
+ [addItemMutate, refresh, refreshAfterDrain]
1331
1353
  );
1332
1354
  const patchItemMutation = useOfflineMutation({
1333
1355
  tripRef,
1334
- enqueue: (engine, input) => {
1356
+ enqueue: (engine2, input) => {
1335
1357
  const { itemId, version, ...body } = input;
1336
- return engine.enqueue({
1358
+ return engine2.enqueue({
1337
1359
  tripRef,
1338
1360
  method: SyncOpMethod.Patch,
1339
1361
  path: `/api/v1/trips/${tripRef}/itinerary/items/${itemId}`,
@@ -1349,15 +1371,16 @@ function useItinerary(tripRef) {
1349
1371
  async (input) => {
1350
1372
  const op = await patchItemMutate(input);
1351
1373
  void refresh();
1374
+ refreshAfterDrain();
1352
1375
  return op;
1353
1376
  },
1354
- [patchItemMutate, refresh]
1377
+ [patchItemMutate, refresh, refreshAfterDrain]
1355
1378
  );
1356
1379
  const removeItemMutation = useOfflineMutation({
1357
1380
  tripRef,
1358
- enqueue: (engine, input) => {
1381
+ enqueue: (engine2, input) => {
1359
1382
  const { itemId, version } = input;
1360
- return engine.enqueue({
1383
+ return engine2.enqueue({
1361
1384
  tripRef,
1362
1385
  method: SyncOpMethod.Delete,
1363
1386
  path: `/api/v1/trips/${tripRef}/itinerary/items/${itemId}`,
@@ -1372,15 +1395,16 @@ function useItinerary(tripRef) {
1372
1395
  async (input) => {
1373
1396
  const op = await removeItemMutate(input);
1374
1397
  void refresh();
1398
+ refreshAfterDrain();
1375
1399
  return op;
1376
1400
  },
1377
- [removeItemMutate, refresh]
1401
+ [removeItemMutate, refresh, refreshAfterDrain]
1378
1402
  );
1379
1403
  const reorderItemMutation = useOfflineMutation({
1380
1404
  tripRef,
1381
- enqueue: (engine, input) => {
1405
+ enqueue: (engine2, input) => {
1382
1406
  const { itemId, ...body } = input;
1383
- return engine.enqueue({
1407
+ return engine2.enqueue({
1384
1408
  tripRef,
1385
1409
  method: SyncOpMethod.Post,
1386
1410
  path: `/api/v1/trips/${tripRef}/itinerary/items/${itemId}/reorder`,
@@ -1395,9 +1419,10 @@ function useItinerary(tripRef) {
1395
1419
  async (input) => {
1396
1420
  const op = await reorderItemMutate(input);
1397
1421
  void refresh();
1422
+ refreshAfterDrain();
1398
1423
  return op;
1399
1424
  },
1400
- [reorderItemMutate, refresh]
1425
+ [reorderItemMutate, refresh, refreshAfterDrain]
1401
1426
  );
1402
1427
  if (!loaded && !lastFetchFailed) {
1403
1428
  return { status: "loading" };
@@ -1874,7 +1899,7 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1874
1899
  }, [refreshRoomsLive, refreshWindowsLive]);
1875
1900
  const assignMutation = useOfflineMutation({
1876
1901
  tripRef,
1877
- enqueue: (engine, input) => engine.enqueue({
1902
+ enqueue: (engine2, input) => engine2.enqueue({
1878
1903
  tripRef,
1879
1904
  method: SyncOpMethod.Post,
1880
1905
  path: `/api/v1/trips/${tripRef}/rooming/assign`,
@@ -1918,6 +1943,16 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1918
1943
  useEffect(() => {
1919
1944
  void refreshUnassignedPool();
1920
1945
  }, [refreshUnassignedPool]);
1946
+ const engine = useContext(OfflineEngineContext);
1947
+ useEffect(() => {
1948
+ if (engine === null) {
1949
+ return;
1950
+ }
1951
+ const unsubscribe = engine.events.on("sync.drained", () => {
1952
+ void refreshUnassignedPool(lastRequestedStayWindowIdRef.current);
1953
+ });
1954
+ return unsubscribe;
1955
+ }, [engine, refreshUnassignedPool]);
1921
1956
  const assignMutate = assignMutation.mutate;
1922
1957
  const assign = useCallback(
1923
1958
  async (input) => {
@@ -1929,7 +1964,7 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1929
1964
  );
1930
1965
  const createWindowMutation = useOfflineMutation({
1931
1966
  tripRef,
1932
- enqueue: (engine, input) => engine.enqueue({
1967
+ enqueue: (engine2, input) => engine2.enqueue({
1933
1968
  tripRef,
1934
1969
  method: SyncOpMethod.Post,
1935
1970
  path: `/api/v1/trips/${tripRef}/rooming/stay-windows`,
@@ -1947,9 +1982,9 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1947
1982
  });
1948
1983
  const patchWindowMutation = useOfflineMutation({
1949
1984
  tripRef,
1950
- enqueue: (engine, input) => {
1985
+ enqueue: (engine2, input) => {
1951
1986
  const { windowId, version, ...body } = input;
1952
- return engine.enqueue({
1987
+ return engine2.enqueue({
1953
1988
  tripRef,
1954
1989
  method: SyncOpMethod.Patch,
1955
1990
  path: `/api/v1/trips/${tripRef}/rooming/stay-windows/${windowId}`,
@@ -1962,10 +1997,10 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1962
1997
  });
1963
1998
  const removeWindowMutation = useOfflineMutation({
1964
1999
  tripRef,
1965
- enqueue: (engine, input) => {
2000
+ enqueue: (engine2, input) => {
1966
2001
  const { windowId, version, force } = input;
1967
2002
  const query = force === true ? "?force=true" : "";
1968
- return engine.enqueue({
2003
+ return engine2.enqueue({
1969
2004
  tripRef,
1970
2005
  method: SyncOpMethod.Delete,
1971
2006
  path: `/api/v1/trips/${tripRef}/rooming/stay-windows/${windowId}${query}`,
@@ -1977,7 +2012,7 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1977
2012
  });
1978
2013
  const createRoomMutation = useOfflineMutation({
1979
2014
  tripRef,
1980
- enqueue: (engine, input) => engine.enqueue({
2015
+ enqueue: (engine2, input) => engine2.enqueue({
1981
2016
  tripRef,
1982
2017
  method: SyncOpMethod.Post,
1983
2018
  path: `/api/v1/trips/${tripRef}/rooming/rooms`,
@@ -1988,9 +2023,9 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
1988
2023
  });
1989
2024
  const patchRoomMutation = useOfflineMutation({
1990
2025
  tripRef,
1991
- enqueue: (engine, input) => {
2026
+ enqueue: (engine2, input) => {
1992
2027
  const { roomId, version, ...body } = input;
1993
- return engine.enqueue({
2028
+ return engine2.enqueue({
1994
2029
  tripRef,
1995
2030
  method: SyncOpMethod.Patch,
1996
2031
  path: `/api/v1/trips/${tripRef}/rooming/rooms/${roomId}`,
@@ -2003,10 +2038,10 @@ var useRoomingInternal = createDomainHook("rooming", (tripRef) => {
2003
2038
  });
2004
2039
  const removeRoomMutation = useOfflineMutation({
2005
2040
  tripRef,
2006
- enqueue: (engine, input) => {
2041
+ enqueue: (engine2, input) => {
2007
2042
  const { roomId, version, force } = input;
2008
2043
  const query = force === true ? "?force=true" : "";
2009
- return engine.enqueue({
2044
+ return engine2.enqueue({
2010
2045
  tripRef,
2011
2046
  method: SyncOpMethod.Delete,
2012
2047
  path: `/api/v1/trips/${tripRef}/rooming/rooms/${roomId}${query}`,
@@ -2060,7 +2095,7 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2060
2095
  });
2061
2096
  const assignMutation = useOfflineMutation({
2062
2097
  tripRef,
2063
- enqueue: (engine, input) => engine.enqueue({
2098
+ enqueue: (engine2, input) => engine2.enqueue({
2064
2099
  tripRef,
2065
2100
  method: SyncOpMethod.Post,
2066
2101
  path: `/api/v1/trips/${tripRef}/seating/assign`,
@@ -2097,6 +2132,16 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2097
2132
  useEffect(() => {
2098
2133
  void refreshUnassignedPool();
2099
2134
  }, [refreshUnassignedPool]);
2135
+ const engine = useContext(OfflineEngineContext);
2136
+ useEffect(() => {
2137
+ if (engine === null) {
2138
+ return;
2139
+ }
2140
+ const unsubscribe = engine.events.on("sync.drained", () => {
2141
+ void refreshUnassignedPool();
2142
+ });
2143
+ return unsubscribe;
2144
+ }, [engine, refreshUnassignedPool]);
2100
2145
  const assignMutate = assignMutation.mutate;
2101
2146
  const assign = useCallback(
2102
2147
  async (input) => {
@@ -2108,7 +2153,7 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2108
2153
  );
2109
2154
  const createVehicleMutation = useOfflineMutation({
2110
2155
  tripRef,
2111
- enqueue: (engine, input) => engine.enqueue({
2156
+ enqueue: (engine2, input) => engine2.enqueue({
2112
2157
  tripRef,
2113
2158
  method: SyncOpMethod.Post,
2114
2159
  path: `/api/v1/trips/${tripRef}/seating/vehicles`,
@@ -2119,9 +2164,9 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2119
2164
  });
2120
2165
  const patchVehicleMutation = useOfflineMutation({
2121
2166
  tripRef,
2122
- enqueue: (engine, input) => {
2167
+ enqueue: (engine2, input) => {
2123
2168
  const { vehicleId, version, ...body } = input;
2124
- return engine.enqueue({
2169
+ return engine2.enqueue({
2125
2170
  tripRef,
2126
2171
  method: SyncOpMethod.Patch,
2127
2172
  path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}`,
@@ -2134,9 +2179,9 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2134
2179
  });
2135
2180
  const removeVehicleMutation = useOfflineMutation({
2136
2181
  tripRef,
2137
- enqueue: (engine, input) => {
2182
+ enqueue: (engine2, input) => {
2138
2183
  const { vehicleId, version } = input;
2139
- return engine.enqueue({
2184
+ return engine2.enqueue({
2140
2185
  tripRef,
2141
2186
  method: SyncOpMethod.Delete,
2142
2187
  path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}`,
@@ -2176,9 +2221,9 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2176
2221
  );
2177
2222
  const linkManagerMutation = useOfflineMutation({
2178
2223
  tripRef,
2179
- enqueue: (engine, input) => {
2224
+ enqueue: (engine2, input) => {
2180
2225
  const { vehicleId, ...body } = input;
2181
- return engine.enqueue({
2226
+ return engine2.enqueue({
2182
2227
  tripRef,
2183
2228
  method: SyncOpMethod.Post,
2184
2229
  path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}/manager`,
@@ -2190,9 +2235,9 @@ var useSeatingInternal = createDomainHook("transport-seating", (tripRef) => {
2190
2235
  });
2191
2236
  const unlinkManagerMutation = useOfflineMutation({
2192
2237
  tripRef,
2193
- enqueue: (engine, input) => {
2238
+ enqueue: (engine2, input) => {
2194
2239
  const { vehicleId, version } = input;
2195
- return engine.enqueue({
2240
+ return engine2.enqueue({
2196
2241
  tripRef,
2197
2242
  method: SyncOpMethod.Delete,
2198
2243
  path: `/api/v1/trips/${tripRef}/seating/vehicles/${vehicleId}/manager`,
@@ -2330,5 +2375,5 @@ function useVendors(tripRef, agencyRef) {
2330
2375
  }
2331
2376
 
2332
2377
  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 };
2333
- //# sourceMappingURL=chunk-JHMN6Q5R.js.map
2334
- //# sourceMappingURL=chunk-JHMN6Q5R.js.map
2378
+ //# sourceMappingURL=chunk-EP4VCID7.js.map
2379
+ //# sourceMappingURL=chunk-EP4VCID7.js.map