kaafil-react-uikit 0.3.1 → 0.4.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 (62) hide show
  1. package/CHANGELOG.md +54 -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-RO2RNHQP.js → chunk-2LMFSKZU.js} +56 -17
  9. package/dist/chunk-2LMFSKZU.js.map +1 -0
  10. package/dist/{chunk-BBC5EX5O.cjs → chunk-2XW5EIMS.cjs} +17 -17
  11. package/dist/{chunk-BBC5EX5O.cjs.map → chunk-2XW5EIMS.cjs.map} +1 -1
  12. package/dist/{chunk-YU2242AR.js → chunk-3BEQYIFU.js} +5 -5
  13. package/dist/{chunk-YU2242AR.js.map → chunk-3BEQYIFU.js.map} +1 -1
  14. package/dist/{chunk-UALMCHNW.cjs → chunk-3N2EQ2KC.cjs} +84 -36
  15. package/dist/chunk-3N2EQ2KC.cjs.map +1 -0
  16. package/dist/{chunk-57SUK3GD.cjs → chunk-4QYI7TOF.cjs} +7 -7
  17. package/dist/{chunk-57SUK3GD.cjs.map → chunk-4QYI7TOF.cjs.map} +1 -1
  18. package/dist/{chunk-ECCQW5LN.cjs → chunk-6BY2NPBZ.cjs} +4 -4
  19. package/dist/{chunk-ECCQW5LN.cjs.map → chunk-6BY2NPBZ.cjs.map} +1 -1
  20. package/dist/{chunk-YNHOPYBK.cjs → chunk-7356B6YS.cjs} +12 -12
  21. package/dist/{chunk-YNHOPYBK.cjs.map → chunk-7356B6YS.cjs.map} +1 -1
  22. package/dist/{chunk-A4Z2YBEF.js → chunk-7G6IENKN.js} +84 -36
  23. package/dist/chunk-7G6IENKN.js.map +1 -0
  24. package/dist/{chunk-6TI6YQPE.cjs → chunk-A7C74SJT.cjs} +168 -123
  25. package/dist/chunk-A7C74SJT.cjs.map +1 -0
  26. package/dist/{chunk-KW5LH6KV.js → chunk-CLHAJBJS.js} +3 -3
  27. package/dist/{chunk-KW5LH6KV.js.map → chunk-CLHAJBJS.js.map} +1 -1
  28. package/dist/{chunk-GJ2J3PYN.js → chunk-E6ER25BG.js} +3 -3
  29. package/dist/{chunk-GJ2J3PYN.js.map → chunk-E6ER25BG.js.map} +1 -1
  30. package/dist/{chunk-HACH2NUM.cjs → chunk-KETEED2T.cjs} +9 -9
  31. package/dist/{chunk-HACH2NUM.cjs.map → chunk-KETEED2T.cjs.map} +1 -1
  32. package/dist/{chunk-RWLO6IFX.js → chunk-KTEXDGAA.js} +3 -3
  33. package/dist/{chunk-RWLO6IFX.js.map → chunk-KTEXDGAA.js.map} +1 -1
  34. package/dist/{chunk-VLP323AD.js → chunk-RBIEMGHT.js} +3 -3
  35. package/dist/{chunk-VLP323AD.js.map → chunk-RBIEMGHT.js.map} +1 -1
  36. package/dist/{chunk-JHMN6Q5R.js → chunk-WPQB7JWS.js} +92 -47
  37. package/dist/chunk-WPQB7JWS.js.map +1 -0
  38. package/dist/{chunk-IU2UM5JP.cjs → chunk-XBLAVNP3.cjs} +88 -48
  39. package/dist/chunk-XBLAVNP3.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 { useRequiredContext, OfflineEngineContext, usePersona, isDevelopment } from './chunk-A4Z2YBEF.js';
1
+ import { useRequiredContext, OfflineEngineContext, usePersona, isDevelopment } from './chunk-7G6IENKN.js';
2
2
  import { createContext, useContext, useMemo, useEffect } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -101,5 +101,5 @@ function useKaafilShareMeta(assets) {
101
101
  }
102
102
 
103
103
  export { BrandShell, useKaafilBrand, useKaafilShareMeta, useOfflineEngine };
104
- //# sourceMappingURL=chunk-KW5LH6KV.js.map
105
- //# sourceMappingURL=chunk-KW5LH6KV.js.map
104
+ //# sourceMappingURL=chunk-CLHAJBJS.js.map
105
+ //# sourceMappingURL=chunk-CLHAJBJS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/hooks/useOfflineEngine.ts","../src/core/shells/BrandShell.tsx"],"names":[],"mappings":"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","file":"chunk-KW5LH6KV.js","sourcesContent":["'use client';\n\n// `08-core-hooks.md §2.1`. The escape hatch underneath every hook in §2 — a\n// composite should reach for a narrower hook before this one.\n\nimport type { OfflineEngine, OutboxOp } from 'kaafil-js/client';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { useRequiredContext } from '../internal/useRequiredContext';\n\n// Re-exported so a composite (`ManagerSyncCenterSection.types.ts`) can name\n// one outbox op's shape without importing `kaafil-js/client` directly —\n// hard rule #1.\nexport type { OutboxOp };\n\n/** Returns the engine instance `OfflineShell` opened via\n * `client.openOffline(options)`. Throws outside the provider tree. */\nexport function useOfflineEngine(): OfflineEngine {\n return useRequiredContext(OfflineEngineContext, 'useOfflineEngine', 'OfflineShell');\n}\n","'use client';\n\n// `shells/06-brand-shell.md`. Eighth in the locked mount order\n// (`shells/README.md §2` row 8) — supplies host-side brand assets\n// (fallback chain: partner logo → neutral mark → text-only appName,\n// `05-theming-and-branding.md §8`) and, opt-in and persona-gated, controls\n// `document.head` for the traveller/share surface only. Fetches nothing\n// from the engine (`00-decisions.md` U-007 — per-agency brand assets are a\n// named non-goal for the backend).\n\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useEffect, useMemo } from 'react';\nimport { usePersona } from '../hooks/usePersona';\nimport { isDevelopment } from '../internal/isDevelopment';\n\nexport interface KaafilBrandAssets {\n readonly appName?: string;\n readonly logo?: string | ReactNode;\n readonly logoMark?: string | ReactNode;\n readonly favicon?: string;\n}\n\nexport interface BrandShellProps extends KaafilBrandAssets {\n readonly headStrategy?: 'none' | 'own-page';\n readonly children?: ReactNode;\n}\n\n/** Shell-owned, not a `08-core-hooks.md` catalog entry (component authoring\n * — this context's eventual consumer, a logo/header component reading the\n * fallback chain — is out of scope for this phase). */\nconst BrandAssetsContext = createContext<KaafilBrandAssets | null>(null);\n\n/** Reads the brand assets `BrandShell` forwarded, or `{}` when mounted\n * without one (never throws — brand is presentational, not load-bearing the\n * way a session read is). */\nexport function useKaafilBrand(): KaafilBrandAssets {\n return useContext(BrandAssetsContext) ?? {};\n}\n\nexport interface ShareMeta {\n readonly title: string | undefined;\n readonly favicon: string | undefined;\n readonly ogTitle: string | undefined;\n readonly ogDescription: string | undefined;\n readonly ogImage: string | undefined;\n readonly robots: 'noindex' | 'index';\n}\n\nfunction resolveShareMeta(assets: KaafilBrandAssets): ShareMeta {\n return {\n title: assets.appName,\n favicon: assets.favicon,\n ogTitle: assets.appName,\n ogDescription: undefined,\n ogImage: undefined,\n // Share meta defaults to noindex — a share link is private-by-obscurity;\n // indexing it is a disclosure bug, never the shipped default\n // (`00-decisions.md` U-005).\n robots: 'noindex',\n };\n}\n\n/** Only the traveller/share surface may write `document.head` — mounted\n * exclusively by `BrandShell` under a persona-permitted `'own-page'`\n * request. Renders nothing into the page body: title/meta writes are not\n * \"visual output\" in the sense `shells/README.md §1` means the phrase. */\nfunction KaafilDocumentHead({ meta }: { readonly meta: ShareMeta }): null {\n useEffect(() => {\n if (meta.title !== undefined) {\n document.title = meta.title;\n }\n if (meta.favicon !== undefined) {\n let link = document.querySelector<HTMLLinkElement>('link[rel=\"icon\"][data-kf-brand]');\n if (link === null) {\n link = document.createElement('link');\n link.rel = 'icon';\n link.setAttribute('data-kf-brand', '');\n document.head.appendChild(link);\n }\n link.href = meta.favicon;\n }\n const setMeta = (\n name: string,\n content: string | undefined,\n attr: 'name' | 'property' = 'name',\n ): void => {\n if (content === undefined) {\n return;\n }\n let tag = document.querySelector<HTMLMetaElement>(`meta[${attr}=\"${name}\"][data-kf-brand]`);\n if (tag === null) {\n tag = document.createElement('meta');\n tag.setAttribute(attr, name);\n tag.setAttribute('data-kf-brand', '');\n document.head.appendChild(tag);\n }\n tag.content = content;\n };\n setMeta('og:title', meta.ogTitle, 'property');\n setMeta('og:description', meta.ogDescription, 'property');\n setMeta('og:image', meta.ogImage, 'property');\n setMeta('robots', meta.robots);\n }, [meta]);\n\n return null;\n}\n\n/**\n * Forwards brand assets unconditionally; enforces `headStrategy` before\n * ever mounting `<KaafilDocumentHead>`. `'none'` is a HARD no-op — no\n * branch below it ever touches `document.head`, even when `favicon`/\n * `appName` are supplied. `'own-page'` is runtime-rejected under\n * `agencyAdmin`/`manager` (dev throw, prod warning + silent `'none'`\n * fallback — the same shape `useCapability()` uses for a wrong-persona call,\n * `08-core-hooks.md §1.4`).\n */\nexport function BrandShell({\n appName,\n logo,\n logoMark,\n favicon,\n headStrategy = 'none',\n children,\n}: BrandShellProps): ReactNode {\n const persona = usePersona();\n const assets = useMemo(\n () => ({ appName, logo, logoMark, favicon }),\n [appName, logo, logoMark, favicon],\n );\n\n const rejectedOwnPage = headStrategy === 'own-page' && persona !== 'share';\n\n useEffect(() => {\n if (headStrategy === 'own-page' && persona !== 'share' && isDevelopment()) {\n throw new Error(\n 'BrandShell: headStrategy=\"own-page\" was requested under a non-share persona ' +\n `(\"${persona}\") — only the traveller/share surface may own document.head ` +\n '(00-decisions.md U-005). Falls back to \"none\" in production.',\n );\n }\n if (\n headStrategy === 'none' &&\n // `favicon` ONLY. `appName` is not a head-only prop: it is the last rung\n // of the in-page brand fallback chain (partner logo -> neutral mark ->\n // text-only appName, 05-theming-and-branding.md §8), so it does real\n // work under every `headStrategy` and warning about it told integrators\n // their correct code was wrong. `favicon` has no in-page use at all, so\n // passing one under `'none'` genuinely is a no-op worth flagging.\n favicon !== undefined &&\n isDevelopment()\n ) {\n // biome-ignore lint/suspicious/noConsole: dev-only, gated above by isDevelopment()\n console.warn(\n 'BrandShell: headStrategy=\"none\" (the default) is a hard no-op — \"favicon\" was passed ' +\n 'but document.head is never touched under \"none\" (00-decisions.md U-005). ' +\n '\"appName\" is unaffected: it still feeds the in-page brand fallback.',\n );\n }\n }, [headStrategy, persona, favicon]);\n\n const shouldOwnPage = headStrategy === 'own-page' && persona === 'share' && !rejectedOwnPage;\n\n return (\n <BrandAssetsContext.Provider value={assets}>\n {shouldOwnPage ? <KaafilDocumentHead meta={resolveShareMeta(assets)} /> : null}\n {children}\n </BrandAssetsContext.Provider>\n );\n}\n\n/**\n * Shell-owned read-only alternative to `<KaafilDocumentHead>` — for a host\n * (e.g. Next.js App Router) that already owns `<head>` via its own\n * `metadata`/`generateMetadata` export and must keep `headStrategy=\"none\"`\n * here. Never writes the head itself; exactly one of the two paths is\n * active per mount (`shells/06-brand-shell.md`).\n */\nexport function useKaafilShareMeta(assets: KaafilBrandAssets): ShareMeta {\n return useMemo(() => resolveShareMeta(assets), [assets]);\n}\n"]}
1
+ {"version":3,"sources":["../src/core/hooks/useOfflineEngine.ts","../src/core/shells/BrandShell.tsx"],"names":[],"mappings":";;;;;AAgBO,SAAS,gBAAA,GAAkC;AAChD,EAAA,OAAO,kBAAA,CAAmB,oBAAA,EAAsB,kBAAA,EAAoB,cAAc,CAAA;AACpF;ACYA,IAAM,kBAAA,GAAqB,cAAwC,IAAI,CAAA;AAKhE,SAAS,cAAA,GAAoC;AAClD,EAAA,OAAO,UAAA,CAAW,kBAAkB,CAAA,IAAK,EAAC;AAC5C;AAWA,SAAS,iBAAiB,MAAA,EAAsC;AAC9D,EAAA,OAAO;AAAA,IACL,OAAO,MAAA,CAAO,OAAA;AAAA,IACd,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,SAAS,MAAA,CAAO,OAAA;AAAA,IAChB,aAAA,EAAe,MAAA;AAAA,IACf,OAAA,EAAS,MAAA;AAAA;AAAA;AAAA;AAAA,IAIT,MAAA,EAAQ;AAAA,GACV;AACF;AAMA,SAAS,kBAAA,CAAmB,EAAE,IAAA,EAAK,EAAuC;AACxE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,IAAA,CAAK,UAAU,MAAA,EAAW;AAC5B,MAAA,QAAA,CAAS,QAAQ,IAAA,CAAK,KAAA;AAAA,IACxB;AACA,IAAA,IAAI,IAAA,CAAK,YAAY,MAAA,EAAW;AAC9B,MAAA,IAAI,IAAA,GAAO,QAAA,CAAS,aAAA,CAA+B,iCAAiC,CAAA;AACpF,MAAA,IAAI,SAAS,IAAA,EAAM;AACjB,QAAA,IAAA,GAAO,QAAA,CAAS,cAAc,MAAM,CAAA;AACpC,QAAA,IAAA,CAAK,GAAA,GAAM,MAAA;AACX,QAAA,IAAA,CAAK,YAAA,CAAa,iBAAiB,EAAE,CAAA;AACrC,QAAA,QAAA,CAAS,IAAA,CAAK,YAAY,IAAI,CAAA;AAAA,MAChC;AACA,MAAA,IAAA,CAAK,OAAO,IAAA,CAAK,OAAA;AAAA,IACnB;AACA,IAAA,MAAM,OAAA,GAAU,CACd,IAAA,EACA,OAAA,EACA,OAA4B,MAAA,KACnB;AACT,MAAA,IAAI,YAAY,MAAA,EAAW;AACzB,QAAA;AAAA,MACF;AACA,MAAA,IAAI,MAAM,QAAA,CAAS,aAAA,CAA+B,QAAQ,IAAI,CAAA,EAAA,EAAK,IAAI,CAAA,iBAAA,CAAmB,CAAA;AAC1F,MAAA,IAAI,QAAQ,IAAA,EAAM;AAChB,QAAA,GAAA,GAAM,QAAA,CAAS,cAAc,MAAM,CAAA;AACnC,QAAA,GAAA,CAAI,YAAA,CAAa,MAAM,IAAI,CAAA;AAC3B,QAAA,GAAA,CAAI,YAAA,CAAa,iBAAiB,EAAE,CAAA;AACpC,QAAA,QAAA,CAAS,IAAA,CAAK,YAAY,GAAG,CAAA;AAAA,MAC/B;AACA,MAAA,GAAA,CAAI,OAAA,GAAU,OAAA;AAAA,IAChB,CAAA;AACA,IAAA,OAAA,CAAQ,UAAA,EAAY,IAAA,CAAK,OAAA,EAAS,UAAU,CAAA;AAC5C,IAAA,OAAA,CAAQ,gBAAA,EAAkB,IAAA,CAAK,aAAA,EAAe,UAAU,CAAA;AACxD,IAAA,OAAA,CAAQ,UAAA,EAAY,IAAA,CAAK,OAAA,EAAS,UAAU,CAAA;AAC5C,IAAA,OAAA,CAAQ,QAAA,EAAU,KAAK,MAAM,CAAA;AAAA,EAC/B,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,OAAO,IAAA;AACT;AAWO,SAAS,UAAA,CAAW;AAAA,EACzB,OAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,YAAA,GAAe,MAAA;AAAA,EACf;AACF,CAAA,EAA+B;AAC7B,EAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,EAAA,MAAM,MAAA,GAAS,OAAA;AAAA,IACb,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,UAAU,OAAA,EAAQ,CAAA;AAAA,IAC1C,CAAC,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,OAAO;AAAA,GACnC;AAEA,EAAA,MAAM,eAAA,GAAkB,YAAA,KAAiB,UAAA,IAAc,OAAA,KAAY,OAAA;AAEnE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,YAAA,KAAiB,UAAA,IAAc,OAAA,KAAY,OAAA,IAAW,eAAc,EAAG;AACzE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,iFACO,OAAO,CAAA,6HAAA;AAAA,OAEhB;AAAA,IACF;AACA,IAAA,IACE,YAAA,KAAiB,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOjB,OAAA,KAAY,MAAA,IACZ,aAAA,EAAc,EACd;AAEA,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,OAGF;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,EAAc,OAAA,EAAS,OAAO,CAAC,CAAA;AAEnC,EAAA,MAAM,aAAA,GAAgB,YAAA,KAAiB,UAAA,IAAc,OAAA,KAAY,WAAW,CAAC,eAAA;AAE7E,EAAA,uBACE,IAAA,CAAC,kBAAA,CAAmB,QAAA,EAAnB,EAA4B,OAAO,MAAA,EACjC,QAAA,EAAA;AAAA,IAAA,aAAA,uBAAiB,kBAAA,EAAA,EAAmB,IAAA,EAAM,gBAAA,CAAiB,MAAM,GAAG,CAAA,GAAK,IAAA;AAAA,IACzE;AAAA,GAAA,EACH,CAAA;AAEJ;AASO,SAAS,mBAAmB,MAAA,EAAsC;AACvE,EAAA,OAAO,QAAQ,MAAM,gBAAA,CAAiB,MAAM,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AACzD","file":"chunk-CLHAJBJS.js","sourcesContent":["'use client';\n\n// `08-core-hooks.md §2.1`. The escape hatch underneath every hook in §2 — a\n// composite should reach for a narrower hook before this one.\n\nimport type { OfflineEngine, OutboxOp } from 'kaafil-js/client';\nimport { OfflineEngineContext } from '../internal/contexts';\nimport { useRequiredContext } from '../internal/useRequiredContext';\n\n// Re-exported so a composite (`ManagerSyncCenterSection.types.ts`) can name\n// one outbox op's shape without importing `kaafil-js/client` directly —\n// hard rule #1.\nexport type { OutboxOp };\n\n/** Returns the engine instance `OfflineShell` opened via\n * `client.openOffline(options)`. Throws outside the provider tree. */\nexport function useOfflineEngine(): OfflineEngine {\n return useRequiredContext(OfflineEngineContext, 'useOfflineEngine', 'OfflineShell');\n}\n","'use client';\n\n// `shells/06-brand-shell.md`. Eighth in the locked mount order\n// (`shells/README.md §2` row 8) — supplies host-side brand assets\n// (fallback chain: partner logo → neutral mark → text-only appName,\n// `05-theming-and-branding.md §8`) and, opt-in and persona-gated, controls\n// `document.head` for the traveller/share surface only. Fetches nothing\n// from the engine (`00-decisions.md` U-007 — per-agency brand assets are a\n// named non-goal for the backend).\n\nimport type { ReactNode } from 'react';\nimport { createContext, useContext, useEffect, useMemo } from 'react';\nimport { usePersona } from '../hooks/usePersona';\nimport { isDevelopment } from '../internal/isDevelopment';\n\nexport interface KaafilBrandAssets {\n readonly appName?: string;\n readonly logo?: string | ReactNode;\n readonly logoMark?: string | ReactNode;\n readonly favicon?: string;\n}\n\nexport interface BrandShellProps extends KaafilBrandAssets {\n readonly headStrategy?: 'none' | 'own-page';\n readonly children?: ReactNode;\n}\n\n/** Shell-owned, not a `08-core-hooks.md` catalog entry (component authoring\n * — this context's eventual consumer, a logo/header component reading the\n * fallback chain — is out of scope for this phase). */\nconst BrandAssetsContext = createContext<KaafilBrandAssets | null>(null);\n\n/** Reads the brand assets `BrandShell` forwarded, or `{}` when mounted\n * without one (never throws — brand is presentational, not load-bearing the\n * way a session read is). */\nexport function useKaafilBrand(): KaafilBrandAssets {\n return useContext(BrandAssetsContext) ?? {};\n}\n\nexport interface ShareMeta {\n readonly title: string | undefined;\n readonly favicon: string | undefined;\n readonly ogTitle: string | undefined;\n readonly ogDescription: string | undefined;\n readonly ogImage: string | undefined;\n readonly robots: 'noindex' | 'index';\n}\n\nfunction resolveShareMeta(assets: KaafilBrandAssets): ShareMeta {\n return {\n title: assets.appName,\n favicon: assets.favicon,\n ogTitle: assets.appName,\n ogDescription: undefined,\n ogImage: undefined,\n // Share meta defaults to noindex — a share link is private-by-obscurity;\n // indexing it is a disclosure bug, never the shipped default\n // (`00-decisions.md` U-005).\n robots: 'noindex',\n };\n}\n\n/** Only the traveller/share surface may write `document.head` — mounted\n * exclusively by `BrandShell` under a persona-permitted `'own-page'`\n * request. Renders nothing into the page body: title/meta writes are not\n * \"visual output\" in the sense `shells/README.md §1` means the phrase. */\nfunction KaafilDocumentHead({ meta }: { readonly meta: ShareMeta }): null {\n useEffect(() => {\n if (meta.title !== undefined) {\n document.title = meta.title;\n }\n if (meta.favicon !== undefined) {\n let link = document.querySelector<HTMLLinkElement>('link[rel=\"icon\"][data-kf-brand]');\n if (link === null) {\n link = document.createElement('link');\n link.rel = 'icon';\n link.setAttribute('data-kf-brand', '');\n document.head.appendChild(link);\n }\n link.href = meta.favicon;\n }\n const setMeta = (\n name: string,\n content: string | undefined,\n attr: 'name' | 'property' = 'name',\n ): void => {\n if (content === undefined) {\n return;\n }\n let tag = document.querySelector<HTMLMetaElement>(`meta[${attr}=\"${name}\"][data-kf-brand]`);\n if (tag === null) {\n tag = document.createElement('meta');\n tag.setAttribute(attr, name);\n tag.setAttribute('data-kf-brand', '');\n document.head.appendChild(tag);\n }\n tag.content = content;\n };\n setMeta('og:title', meta.ogTitle, 'property');\n setMeta('og:description', meta.ogDescription, 'property');\n setMeta('og:image', meta.ogImage, 'property');\n setMeta('robots', meta.robots);\n }, [meta]);\n\n return null;\n}\n\n/**\n * Forwards brand assets unconditionally; enforces `headStrategy` before\n * ever mounting `<KaafilDocumentHead>`. `'none'` is a HARD no-op — no\n * branch below it ever touches `document.head`, even when `favicon`/\n * `appName` are supplied. `'own-page'` is runtime-rejected under\n * `agencyAdmin`/`manager` (dev throw, prod warning + silent `'none'`\n * fallback — the same shape `useCapability()` uses for a wrong-persona call,\n * `08-core-hooks.md §1.4`).\n */\nexport function BrandShell({\n appName,\n logo,\n logoMark,\n favicon,\n headStrategy = 'none',\n children,\n}: BrandShellProps): ReactNode {\n const persona = usePersona();\n const assets = useMemo(\n () => ({ appName, logo, logoMark, favicon }),\n [appName, logo, logoMark, favicon],\n );\n\n const rejectedOwnPage = headStrategy === 'own-page' && persona !== 'share';\n\n useEffect(() => {\n if (headStrategy === 'own-page' && persona !== 'share' && isDevelopment()) {\n throw new Error(\n 'BrandShell: headStrategy=\"own-page\" was requested under a non-share persona ' +\n `(\"${persona}\") — only the traveller/share surface may own document.head ` +\n '(00-decisions.md U-005). Falls back to \"none\" in production.',\n );\n }\n if (\n headStrategy === 'none' &&\n // `favicon` ONLY. `appName` is not a head-only prop: it is the last rung\n // of the in-page brand fallback chain (partner logo -> neutral mark ->\n // text-only appName, 05-theming-and-branding.md §8), so it does real\n // work under every `headStrategy` and warning about it told integrators\n // their correct code was wrong. `favicon` has no in-page use at all, so\n // passing one under `'none'` genuinely is a no-op worth flagging.\n favicon !== undefined &&\n isDevelopment()\n ) {\n // biome-ignore lint/suspicious/noConsole: dev-only, gated above by isDevelopment()\n console.warn(\n 'BrandShell: headStrategy=\"none\" (the default) is a hard no-op — \"favicon\" was passed ' +\n 'but document.head is never touched under \"none\" (00-decisions.md U-005). ' +\n '\"appName\" is unaffected: it still feeds the in-page brand fallback.',\n );\n }\n }, [headStrategy, persona, favicon]);\n\n const shouldOwnPage = headStrategy === 'own-page' && persona === 'share' && !rejectedOwnPage;\n\n return (\n <BrandAssetsContext.Provider value={assets}>\n {shouldOwnPage ? <KaafilDocumentHead meta={resolveShareMeta(assets)} /> : null}\n {children}\n </BrandAssetsContext.Provider>\n );\n}\n\n/**\n * Shell-owned read-only alternative to `<KaafilDocumentHead>` — for a host\n * (e.g. Next.js App Router) that already owns `<head>` via its own\n * `metadata`/`generateMetadata` export and must keep `headStrategy=\"none\"`\n * here. Never writes the head itself; exactly one of the two paths is\n * active per mount (`shells/06-brand-shell.md`).\n */\nexport function useKaafilShareMeta(assets: KaafilBrandAssets): ShareMeta {\n return useMemo(() => resolveShareMeta(assets), [assets]);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { useKaafilChrome, useOutboxStatus, useCapabilities, usePersona, tokenStyle } from './chunk-A4Z2YBEF.js';
1
+ import { useKaafilChrome, useOutboxStatus, useCapabilities, usePersona, tokenStyle } from './chunk-7G6IENKN.js';
2
2
  import { useState, useRef, useId, useEffect } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
@@ -283,5 +283,5 @@ function SessionExpiredScreen({
283
283
  }
284
284
 
285
285
  export { BootErrorScreen, LockedTripScreen, NotFoundScreen, OfflineFallbackScreen, PlanLockedScreen, SessionExpiredScreen };
286
- //# sourceMappingURL=chunk-GJ2J3PYN.js.map
287
- //# sourceMappingURL=chunk-GJ2J3PYN.js.map
286
+ //# sourceMappingURL=chunk-E6ER25BG.js.map
287
+ //# sourceMappingURL=chunk-E6ER25BG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/system-screens/BootErrorScreen.tsx","../src/shared/system-screens/internal/SystemScreenFrame.tsx","../src/shared/system-screens/LockedTripScreen.tsx","../src/shared/system-screens/NotFoundScreen.tsx","../src/shared/system-screens/OfflineFallbackScreen.tsx","../src/shared/system-screens/PlanLockedScreen.tsx","../src/shared/system-screens/SessionExpiredScreen.tsx"],"names":["jsx","jsxs","Fragment","useState"],"mappings":"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","file":"chunk-GJ2J3PYN.js","sourcesContent":["'use client';\n\n// `shared/01-system-screens.md §BootErrorScreen`.\n//\n// ── THE ONE SCREEN THAT CANNOT ASSUME ANYTHING ───────────────────────────\n//\n// It renders when `<KaafilUIKitProvider>` itself never opened, mounted by\n// the integrator's own error boundary around it. That means:\n//\n// · NO `I18nShell` → no `t()`. The copy below is ENGLISH ONLY, and that\n// is the spec's own DELTA, not an oversight.\n// · NO `ThemeShell` → no resolved tokens.\n// · NO `usePersona` → the credential never opened, so there is no\n// resolved persona to fork a presentation on. This is\n// the ONE screen in the tier that ships a single\n// rendering for every mounting context, including the\n// share surface — `public`'s richer reading layout\n// depends on shells that did not run.\n// · NO hook of any kind. There is no context above it to read.\n//\n// ── WHY IT CARRIES INLINE STYLES, WHICH NOTHING ELSE HERE DOES ───────────\n//\n// The stylesheet is OPT-IN (`import \"kaafil-react-uikit/styles\"`), so this\n// screen may render with no CSS whatsoever — and it is the one screen whose\n// entire job is explaining a total failure. Unstyled black-on-white text is\n// a poor way to do that, and a token reference resolves to nothing when the\n// ladder never loaded.\n//\n// So every node carries BOTH: an inline style that works with zero CSS, and\n// a `.kf-boot-error-screen__*` class so a host that DID load the stylesheet\n// can restyle it. The literals below are deliberate and must not be\n// \"corrected\" into `var(--kf-*)` — doing so silently breaks the only case\n// this screen exists for. dx-05 governs `src/styles/components/**` and does\n// not reach here, which is consistent rather than a loophole: the rule\n// exists so a stylesheet cannot shadow a token, and this file is not one.\n\nimport type { CSSProperties, ReactNode } from 'react';\nimport { useState } from 'react';\n\n/** The same failure modes `<KaafilSessionProbe/>` distinguishes, plus the\n * honest sixth for anything unclassifiable. */\nexport type BootFailureMode =\n | 'endpoint-401'\n | 'wrong-environment'\n | 'test-key-on-live'\n | 'clock-skew'\n | 'cors'\n | 'unknown';\n\nexport interface BootErrorScreenProps {\n readonly failureMode?: BootFailureMode;\n /** The underlying error's message. Rendered behind a disclosure, never\n * inline in the primary copy — it is developer-facing. */\n readonly detail?: string;\n /** Remounts the entire provider tree; some boot failures (a `kf_test_`\n * key shipped to production) cannot be fixed by retrying at all, which\n * is why this is optional. */\n readonly onRetry?: () => void;\n readonly components?: { readonly BootErrorScreenUI?: () => ReactNode };\n}\n\n/** English-only by contract — see this file's header. Each line says what\n * an integrator can actually DO, because the audience for a boot failure is\n * whoever wired the provider, not an end user. */\nconst COPY: Readonly<Record<BootFailureMode, string>> = {\n 'endpoint-401':\n 'The API key or session token was rejected. Check the credential the provider was given.',\n 'wrong-environment':\n 'This credential belongs to a different environment than the endpoint it was sent to.',\n 'test-key-on-live': 'A test key was used against live. Retrying will not help — swap the key.',\n 'clock-skew': \"This device's clock is too far from the server's for a token to validate.\",\n cors: 'The browser blocked the request before it was sent. This origin is not allowlisted on the API.',\n unknown: 'Kaafil could not start. The detail below may say why.',\n};\n\nconst PANEL: CSSProperties = {\n boxSizing: 'border-box',\n maxInlineSize: '32rem',\n marginInline: 'auto',\n marginBlock: '3rem',\n padding: '1.5rem',\n border: '1px solid #e3ded7',\n borderRadius: '10px',\n background: '#ffffff',\n color: '#1c1917',\n fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif',\n fontSize: '0.875rem',\n lineHeight: 1.5,\n};\n\nconst TITLE: CSSProperties = { margin: '0 0 0.5rem', fontSize: '1rem', fontWeight: 600 };\nconst BODY: CSSProperties = { margin: '0 0 1rem', color: '#57534e' };\nconst CODE: CSSProperties = {\n display: 'block',\n overflowX: 'auto',\n marginBlockStart: '0.5rem',\n padding: '0.75rem',\n borderRadius: '6px',\n background: '#f5f5f4',\n color: '#44403c',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: '0.75rem',\n whiteSpace: 'pre-wrap',\n};\nconst BUTTON: CSSProperties = {\n padding: '0.5rem 1rem',\n border: '0',\n borderRadius: '8px',\n background: '#1c1917',\n color: '#ffffff',\n fontSize: '0.8125rem',\n fontWeight: 500,\n cursor: 'pointer',\n};\nconst TOGGLE: CSSProperties = {\n marginBlockStart: '1rem',\n color: '#78716c',\n fontSize: '0.75rem',\n cursor: 'pointer',\n};\n\nexport function BootErrorScreen({\n failureMode = 'unknown',\n detail,\n onRetry,\n components,\n}: BootErrorScreenProps): ReactNode {\n const [showDetail, setShowDetail] = useState(false);\n const Override = components?.BootErrorScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <div className=\"kf-boot-error-screen\" style={PANEL} role=\"alert\">\n <h1 className=\"kf-boot-error-screen__title\" style={TITLE}>\n Kaafil could not start\n </h1>\n <p className=\"kf-boot-error-screen__body\" style={BODY}>\n {COPY[failureMode]}\n </p>\n\n {onRetry === undefined ? null : (\n <button\n type=\"button\"\n className=\"kf-boot-error-screen__retry\"\n style={BUTTON}\n onClick={onRetry}\n >\n Try again\n </button>\n )}\n\n {detail === undefined || detail === '' ? null : (\n <div className=\"kf-boot-error-screen__detail\">\n {/* A disclosure rather than always-on: the message is the\n * underlying error's, which can be long and is not the first\n * thing a reader needs. */}\n <button\n type=\"button\"\n className=\"kf-boot-error-screen__detail-toggle\"\n style={{ ...TOGGLE, background: 'none', border: 0, padding: 0 }}\n aria-expanded={showDetail}\n onClick={() => setShowDetail((open) => !open)}\n >\n {showDetail ? 'Hide technical detail' : 'Show technical detail'}\n </button>\n {showDetail ? (\n <code className=\"kf-boot-error-screen__detail-text\" style={CODE}>\n {detail}\n </code>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n","'use client';\n\n// The one frame the five context-reading system screens share.\n//\n// ── THE PRESENTATION FORK IS REAL, AND IT IS NOT A PROP ───────────────────\n//\n// `shared/01-system-screens.md` records a `Presentation notes by mounting\n// context` field on every screen: an `admin`-family mount is a CONTAINED\n// CARD inside the host's own CRM page, a `manager`-family mount is a\n// FULL-VIEWPORT takeover with its primary action reachable without\n// scrolling. The spec is explicit that this is a genuine layout-model fork\n// (`03-layer-contract.md §5` level 3), not a spacing difference — and\n// equally explicit that it is ONE component, never two named variants.\n//\n// It is derived from `usePersona()`, never from a prop. Hard rule #7: no\n// `persona` prop and no `mode` prop exists anywhere, and dx-07 fails the\n// build on either. The credential decides.\n//\n// ── WHY THIS IS A FRAME AND NOT AN ATOM ──────────────────────────────────\n//\n// The spec's `Composes` rows name `EmptyState`/`Button`/`Banner` atoms from\n// `atomic/*`. Those do not exist: the v2 rebuild ships no shared\n// `/primitives` layer, by decision — each family owns whatever atoms it\n// needs. So these five screens share a FRAME (the layout fork, the focus\n// move, the announcement) and own their own content, rather than depending\n// on a primitives package this package deliberately does not have.\n\nimport type { ReactNode } from 'react';\nimport { useEffect, useId, useRef } from 'react';\nimport { usePersona } from '../../../core/hooks/usePersona.js';\nimport type { KaafilTokenOverrides } from '../../../primitives/internal/tokenStyle.js';\nimport { tokenStyle } from '../../../primitives/internal/tokenStyle.js';\n\nexport interface SystemScreenFrameProps {\n /** BEM block name — each screen owns its own, so a host can target one\n * without catching the rest (`.kf-session-expired-screen__*`). */\n readonly block: string;\n readonly title: string;\n readonly body: ReactNode;\n /** Rendered below the body. On a field mount this is what the sticky\n * action region holds. */\n readonly actions?: ReactNode;\n /** An error/context line above the title. */\n readonly banner?: ReactNode;\n readonly icon?: ReactNode;\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function SystemScreenFrame({\n block,\n title,\n body,\n actions,\n banner,\n icon,\n style,\n}: SystemScreenFrameProps): ReactNode {\n const persona = usePersona();\n const headingRef = useRef<HTMLHeadingElement>(null);\n const headingId = useId();\n\n // A field mount owns the whole screen; a desk mount is embedded beside the\n // host's own nav and must not consume the viewport (briefing §27.7).\n // `traveller` takes the contained treatment too — only `OfflineFallback`\n // and `BootError` reach the public surface, and neither should black out\n // a document a host has embedded.\n const layout = persona === 'manager' ? 'takeover' : 'contained';\n\n // FOCUS MOVES TO THE HEADING on mount. This is a full state replacement,\n // not an overlay — there is no opener to return focus to, so the\n // overlay-focus contract (`12-accessibility.md §3`) does not apply and the\n // heading is the correct landing point. Without this a keyboard or screen\n // reader user gets no signal that the page became something else and Tab\n // keeps walking whatever is still behind it.\n useEffect(() => {\n headingRef.current?.focus();\n }, []);\n\n return (\n <div\n className=\"kf-system-screen\"\n data-block={block}\n data-layout={layout}\n style={tokenStyle(style)}\n // A significant, infrequent state change — announced politely rather\n // than assertively, since the focus move above already carries the\n // urgency and `assertive` would interrupt whatever is mid-sentence.\n role=\"status\"\n aria-live=\"polite\"\n aria-labelledby={headingId}\n >\n <div className={`kf-system-screen__panel ${block}`}>\n {icon === undefined ? null : (\n <span className=\"kf-system-screen__glyph\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n {banner === undefined ? null : <div className=\"kf-system-screen__banner\">{banner}</div>}\n {/* `tabIndex={-1}` so the effect above can focus it — a heading is\n * not focusable by default and focusing the panel instead would\n * draw the accent ring around the entire card. */}\n <h1 ref={headingRef} id={headingId} tabIndex={-1} className=\"kf-system-screen__title\">\n {title}\n </h1>\n <div className=\"kf-system-screen__body\">{body}</div>\n {actions === undefined ? null : <div className=\"kf-system-screen__actions\">{actions}</div>}\n </div>\n </div>\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §LockedTripScreen`.\n//\n// The destination for a WRITE-ONLY deep link into a closed-out trip — a\n// \"record this collection\" push sent before the trip closed, a bookmarked\n// mutation route. Mounting the flow it points at would show a form whose\n// one action can only ever answer `423`.\n//\n// BACKED BY NOTHING, and that is the discipline rather than a gap. This\n// screen never calls `closeout.get`/`useCloseout` to learn the trip is\n// locked: the caller already knows (its own write would 423, or the link's\n// own metadata carried the LOCKED stage). Checking again here would be a\n// second gate — \"check the resolved state before render, never\n// render-then-catch\", applied to lock state instead of capability darkness.\n//\n// LINKS to Closing Day, never mounts it. No unlock control appears here or\n// anywhere else at any layer (hard rule #21): the desk locks, the CRM\n// reopens.\n\nimport type { ReactNode } from 'react';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface LockedTripScreenProps {\n /** For context and for building the link's destination — never used to\n * fetch anything. */\n readonly tripRef: string;\n /** What the dead-end link would have done (\"Record a collection\"), from\n * whatever metadata came with it. Absent means the generic sentence: this\n * screen invents no explanation of its own. */\n readonly intendedAction?: string;\n /** The host's own navigation to wherever it mounts Closing Day. A screen\n * never navigates itself. */\n readonly onGoToClosingDay: (tripRef: string) => void;\n readonly components?: { readonly LockedTripScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function LockedTripScreen({\n tripRef,\n intendedAction,\n onGoToClosingDay,\n components,\n style,\n}: LockedTripScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const Override = components?.LockedTripScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <SystemScreenFrame\n block=\"kf-locked-trip-screen\"\n title={t('chrome.systemScreens.lockedTrip.title')}\n banner={\n intendedAction === undefined\n ? t('chrome.systemScreens.lockedTrip.genericAction')\n : t('chrome.systemScreens.lockedTrip.namedAction').replace('{action}', intendedAction)\n }\n body={<p>{t('chrome.systemScreens.lockedTrip.body')}</p>}\n {...(style !== undefined ? { style } : {})}\n actions={\n <button\n type=\"button\"\n className=\"kf-system-screen__primary\"\n onClick={() => onGoToClosingDay(tripRef)}\n >\n {t('chrome.systemScreens.lockedTrip.action')}\n </button>\n }\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §NotFoundScreen`.\n//\n// A route-level 404 for a `manager`/`agencyAdmin` mount: a bookmarked\n// `tripRef` from another agency, a deep link to a deleted entity, a stale\n// notification pointing at a now-cross-tenant resource.\n//\n// NOT THE TRAVELLER'S 404, and this is load-bearing rather than an\n// oversight. A dead share token is `ShareEndedScreen`'s `not-found`\n// variant, which must stay 404-shaped — no heading, no divider, no\n// distinguishing DOM signal (hard rule #20, enforced by dx-15). Rendering\n// THIS screen on the share surface would leak that the token was once real.\n// It is therefore never re-exported from `/traveller`.\n//\n// BACKED BY NOTHING, by design. It renders on a 404 that some mounted\n// flow's own hook already surfaced; it performs no second fetch to\n// \"double-check\" what the flow already learned.\n\nimport type { ReactNode } from 'react';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface NotFoundScreenProps {\n /** The host's own \"back\" navigation. Optional — a host that reached this\n * with no history to return to simply gets no button, which is more\n * honest than one that goes nowhere. */\n readonly onGoBack?: () => void;\n readonly components?: { readonly NotFoundScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function NotFoundScreen({ onGoBack, components, style }: NotFoundScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const Override = components?.NotFoundScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <SystemScreenFrame\n block=\"kf-not-found-screen\"\n title={t('chrome.systemScreens.notFound.title')}\n // Deliberately does not speculate about WHY. \"Deleted, or belongs to\n // another agency, or you mistyped it\" are three different sentences\n // and this screen cannot tell which is true — naming one would be a\n // guess presented as a diagnosis.\n body={<p>{t('chrome.systemScreens.notFound.body')}</p>}\n {...(style !== undefined ? { style } : {})}\n {...(onGoBack !== undefined\n ? {\n actions: (\n <button type=\"button\" className=\"kf-system-screen__primary\" onClick={onGoBack}>\n {t('chrome.systemScreens.notFound.action')}\n </button>\n ),\n }\n : {})}\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §OfflineFallbackScreen`.\n//\n// The device has nothing cached AND no connectivity — a manager opening a\n// trip for the first time at a trailhead before ever syncing, a desk\n// session losing the network before its first pull, a share link opened\n// with zero signal before the first snapshot.\n//\n// THE ONLY ONE OF THE SIX THE TRAVELLER SURFACE SHARES besides\n// `BootErrorScreen`, and it is safe there precisely because it discloses\n// nothing: \"there is no connection\" is true of any URL and leaks nothing\n// about whether this token was ever real. That is what separates it from\n// `ShareEndedScreen`'s dead-token cases, which are about a token's\n// VALIDITY and must stay 404-shaped (hard rule #20).\n//\n// SESSION STATE OUTRANKS THIS. An offline device whose credential has also\n// expired shows `SessionExpiredScreen`, not this — re-authentication is the\n// blocking concern regardless of connectivity, and a caller composing both\n// should check session first.\n\nimport type { ReactNode } from 'react';\nimport { useOutboxStatus } from '../../core/hooks/useOutboxStatus.js';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface OfflineFallbackScreenProps {\n /** Present on a trip-scoped mount; absent on the share surface. Context\n * only — this screen fetches nothing. */\n readonly tripRef?: string;\n /** Present on a share mount instead of `tripRef`. */\n readonly token?: string;\n /** The host's own retry — typically remounting whatever failed. */\n readonly onRetry?: () => void;\n readonly components?: { readonly OfflineFallbackScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function OfflineFallbackScreen({\n onRetry,\n components,\n style,\n}: OfflineFallbackScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const { isOnline } = useOutboxStatus();\n const Override = components?.OfflineFallbackScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <SystemScreenFrame\n block=\"kf-offline-fallback-screen\"\n title={t('chrome.systemScreens.offlineFallback.title')}\n body={\n <>\n <p>{t('chrome.systemScreens.offlineFallback.body')}</p>\n {/* Connectivity came back while this was on screen. Said rather\n * than acted on: this screen does not refetch or navigate on its\n * own, because whatever failed is the caller's to retry — but\n * leaving a stale \"you are offline\" up while the bars are back is\n * the thing that makes an app feel broken. */}\n {isOnline ? (\n <p data-tone=\"ok\">{t('chrome.systemScreens.offlineFallback.backOnline')}</p>\n ) : null}\n </>\n }\n {...(style !== undefined ? { style } : {})}\n {...(onRetry !== undefined\n ? {\n actions: (\n <button type=\"button\" className=\"kf-system-screen__primary\" onClick={onRetry}>\n {t('chrome.systemScreens.offlineFallback.action')}\n </button>\n ),\n }\n : {})}\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §PlanLockedScreen`.\n//\n// The takeover a STANDALONE route (no surrounding tab bar) renders when its\n// destination module is flag-dark. Admin-only: entitlement is a desk\n// concern, a manager's flows are always mounted inside a tab set where a\n// flag-dark tab renders its own inline view, and the share surface has no\n// upsell copy at all (its flag-dark case is 404-shaped).\n//\n// ── A SPEC FIELD THAT HAS NO WIRE SOURCE ─────────────────────────────────\n//\n// The spec splits the CTA by `details.reason` into `'agency'` (the agency's\n// own plan) vs `'partner-ceiling'` (the partner's tier caps it), so the\n// right person is sent to the right queue. `CapabilityRow` carries no such\n// field — `06-capability-and-personas.md §6` point 2 fixes it as \"four\n// booleans, no `reason` string\", and `capability.types.ts` says so too.\n//\n// So the reason is an OPTIONAL PROP, supplied by whatever the caller saw\n// (a refusal's own `details`), and `onUpgradeRequest` is called with\n// `undefined` when nobody knows. Defaulting it to `'agency'` would have\n// been a guess routed to a real support queue — the host can decide what to\n// do with \"unknown\" far better than this screen can invent an answer.\n\nimport type { ReactNode } from 'react';\nimport { useCapabilities } from '../../core/hooks/useCapabilities.js';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport type PlanLockedReason = 'agency' | 'partner-ceiling';\n\nexport interface PlanLockedScreenProps {\n /** The capability this route was headed for. Read for its own triple\n * only — never exhaustively switched on (`§6` point 5's open-set rule). */\n readonly capabilityKey: string;\n /** The host's own upsell/support flow — Kaafil has no in-product upgrade\n * path to hand off to. `undefined` means the wire did not say which\n * queue this belongs in; see this file's header. */\n readonly onUpgradeRequest: (reason: PlanLockedReason | undefined) => void;\n /** Supplied by the caller when it saw a refusal that named one. */\n readonly reason?: PlanLockedReason;\n readonly components?: { readonly PlanLockedScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function PlanLockedScreen({\n capabilityKey,\n onUpgradeRequest,\n reason,\n components,\n style,\n}: PlanLockedScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const capabilities = useCapabilities([capabilityKey]);\n const Override = components?.PlanLockedScreenUI;\n if (Override !== undefined) return <Override />;\n\n // Read, never re-derived: `status` is resolved once in `/core` and this\n // screen reads it rather than recombining the booleans itself\n // (`06-capability-and-personas.md §6` point 2).\n const status = capabilities.get(capabilityKey)?.status;\n\n return (\n <SystemScreenFrame\n block=\"kf-plan-locked-screen\"\n title={t('chrome.systemScreens.planLocked.title')}\n body={\n <>\n <p>\n {t(\n reason === 'partner-ceiling'\n ? 'chrome.systemScreens.planLocked.bodyPartnerCeiling'\n : 'chrome.systemScreens.planLocked.body',\n )}\n </p>\n {/* A route that reached this screen for a capability that is NOT\n * flag-dark is a caller bug, and saying so beats rendering upsell\n * copy over a module that is merely data-dark or simply lit. */}\n {status === 'flag-dark' ? null : (\n <p data-tone=\"warn\">{t('chrome.systemScreens.planLocked.notFlagDark')}</p>\n )}\n </>\n }\n {...(style !== undefined ? { style } : {})}\n actions={\n <button\n type=\"button\"\n className=\"kf-system-screen__primary\"\n onClick={() => onUpgradeRequest(reason)}\n >\n {t('chrome.systemScreens.planLocked.action')}\n </button>\n }\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §SessionExpiredScreen`.\n//\n// Renders exactly when `useSession().status === 'expired'` — the open\n// credential's silent refresh has exhausted itself. Re-authentication is\n// routed through the HOST's own login; Kaafil has no login page to redirect\n// to, which is why `onReauthenticate` is required and has no default.\n//\n// DOES NOT retry any token call itself. A broken credential means the\n// host's own user is not authenticated and must re-authenticate through the\n// host's own flow — obtaining fresh tokens is `SessionShell`'s concern on\n// the NEXT successful mount, not this screen's on the current broken one.\n//\n// DOES NOT inspect which credential kind expired beyond the resolved\n// persona (`00-decisions.md` U-003 forecloses reading a JWT at any layer\n// above `SessionShell`).\n\nimport type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface SessionExpiredScreenProps {\n /** The host's own re-auth entry point. Required: there is no default\n * action, because Kaafil has no login page of its own. */\n readonly onReauthenticate: (returnTo?: string) => void | Promise<void>;\n /** Threaded back to `onReauthenticate` UNEXAMINED. This screen never\n * interprets it, builds a URL from it, or hands it to a router — it\n * exists purely so the host's own flow can restore the right destination\n * afterwards. */\n readonly returnTo?: string;\n readonly components?: { readonly SessionExpiredScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function SessionExpiredScreen({\n onReauthenticate,\n returnTo,\n components,\n style,\n}: SessionExpiredScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const [pending, setPending] = useState(false);\n // The host's own auth failure reason is not something Kaafil can\n // classify, so the rejection's own message is shown verbatim.\n const [failure, setFailure] = useState<string | undefined>(undefined);\n\n const Override = components?.SessionExpiredScreenUI;\n if (Override !== undefined) return <Override />;\n\n async function reauthenticate(): Promise<void> {\n setPending(true);\n setFailure(undefined);\n try {\n await onReauthenticate(returnTo);\n } catch (error) {\n setFailure(error instanceof Error ? error.message : String(error));\n } finally {\n setPending(false);\n }\n }\n\n return (\n <SystemScreenFrame\n block=\"kf-session-expired-screen\"\n title={t('chrome.systemScreens.sessionExpired.title')}\n body={<p>{t('chrome.systemScreens.sessionExpired.body')}</p>}\n {...(failure !== undefined ? { banner: <span data-tone=\"warn\">{failure}</span> } : {})}\n {...(style !== undefined ? { style } : {})}\n actions={\n <button\n type=\"button\"\n className=\"kf-system-screen__primary\"\n disabled={pending}\n onClick={() => void reauthenticate()}\n >\n {t(\n pending\n ? 'chrome.systemScreens.sessionExpired.busy'\n : 'chrome.systemScreens.sessionExpired.action',\n )}\n </button>\n }\n />\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/shared/system-screens/BootErrorScreen.tsx","../src/shared/system-screens/internal/SystemScreenFrame.tsx","../src/shared/system-screens/LockedTripScreen.tsx","../src/shared/system-screens/NotFoundScreen.tsx","../src/shared/system-screens/OfflineFallbackScreen.tsx","../src/shared/system-screens/PlanLockedScreen.tsx","../src/shared/system-screens/SessionExpiredScreen.tsx"],"names":["jsx","jsxs","Fragment","useState"],"mappings":"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","file":"chunk-E6ER25BG.js","sourcesContent":["'use client';\n\n// `shared/01-system-screens.md §BootErrorScreen`.\n//\n// ── THE ONE SCREEN THAT CANNOT ASSUME ANYTHING ───────────────────────────\n//\n// It renders when `<KaafilUIKitProvider>` itself never opened, mounted by\n// the integrator's own error boundary around it. That means:\n//\n// · NO `I18nShell` → no `t()`. The copy below is ENGLISH ONLY, and that\n// is the spec's own DELTA, not an oversight.\n// · NO `ThemeShell` → no resolved tokens.\n// · NO `usePersona` → the credential never opened, so there is no\n// resolved persona to fork a presentation on. This is\n// the ONE screen in the tier that ships a single\n// rendering for every mounting context, including the\n// share surface — `public`'s richer reading layout\n// depends on shells that did not run.\n// · NO hook of any kind. There is no context above it to read.\n//\n// ── WHY IT CARRIES INLINE STYLES, WHICH NOTHING ELSE HERE DOES ───────────\n//\n// The stylesheet is OPT-IN (`import \"kaafil-react-uikit/styles\"`), so this\n// screen may render with no CSS whatsoever — and it is the one screen whose\n// entire job is explaining a total failure. Unstyled black-on-white text is\n// a poor way to do that, and a token reference resolves to nothing when the\n// ladder never loaded.\n//\n// So every node carries BOTH: an inline style that works with zero CSS, and\n// a `.kf-boot-error-screen__*` class so a host that DID load the stylesheet\n// can restyle it. The literals below are deliberate and must not be\n// \"corrected\" into `var(--kf-*)` — doing so silently breaks the only case\n// this screen exists for. dx-05 governs `src/styles/components/**` and does\n// not reach here, which is consistent rather than a loophole: the rule\n// exists so a stylesheet cannot shadow a token, and this file is not one.\n\nimport type { CSSProperties, ReactNode } from 'react';\nimport { useState } from 'react';\n\n/** The same failure modes `<KaafilSessionProbe/>` distinguishes, plus the\n * honest sixth for anything unclassifiable. */\nexport type BootFailureMode =\n | 'endpoint-401'\n | 'wrong-environment'\n | 'test-key-on-live'\n | 'clock-skew'\n | 'cors'\n | 'unknown';\n\nexport interface BootErrorScreenProps {\n readonly failureMode?: BootFailureMode;\n /** The underlying error's message. Rendered behind a disclosure, never\n * inline in the primary copy — it is developer-facing. */\n readonly detail?: string;\n /** Remounts the entire provider tree; some boot failures (a `kf_test_`\n * key shipped to production) cannot be fixed by retrying at all, which\n * is why this is optional. */\n readonly onRetry?: () => void;\n readonly components?: { readonly BootErrorScreenUI?: () => ReactNode };\n}\n\n/** English-only by contract — see this file's header. Each line says what\n * an integrator can actually DO, because the audience for a boot failure is\n * whoever wired the provider, not an end user. */\nconst COPY: Readonly<Record<BootFailureMode, string>> = {\n 'endpoint-401':\n 'The API key or session token was rejected. Check the credential the provider was given.',\n 'wrong-environment':\n 'This credential belongs to a different environment than the endpoint it was sent to.',\n 'test-key-on-live': 'A test key was used against live. Retrying will not help — swap the key.',\n 'clock-skew': \"This device's clock is too far from the server's for a token to validate.\",\n cors: 'The browser blocked the request before it was sent. This origin is not allowlisted on the API.',\n unknown: 'Kaafil could not start. The detail below may say why.',\n};\n\nconst PANEL: CSSProperties = {\n boxSizing: 'border-box',\n maxInlineSize: '32rem',\n marginInline: 'auto',\n marginBlock: '3rem',\n padding: '1.5rem',\n border: '1px solid #e3ded7',\n borderRadius: '10px',\n background: '#ffffff',\n color: '#1c1917',\n fontFamily: 'system-ui, -apple-system, Segoe UI, Roboto, sans-serif',\n fontSize: '0.875rem',\n lineHeight: 1.5,\n};\n\nconst TITLE: CSSProperties = { margin: '0 0 0.5rem', fontSize: '1rem', fontWeight: 600 };\nconst BODY: CSSProperties = { margin: '0 0 1rem', color: '#57534e' };\nconst CODE: CSSProperties = {\n display: 'block',\n overflowX: 'auto',\n marginBlockStart: '0.5rem',\n padding: '0.75rem',\n borderRadius: '6px',\n background: '#f5f5f4',\n color: '#44403c',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: '0.75rem',\n whiteSpace: 'pre-wrap',\n};\nconst BUTTON: CSSProperties = {\n padding: '0.5rem 1rem',\n border: '0',\n borderRadius: '8px',\n background: '#1c1917',\n color: '#ffffff',\n fontSize: '0.8125rem',\n fontWeight: 500,\n cursor: 'pointer',\n};\nconst TOGGLE: CSSProperties = {\n marginBlockStart: '1rem',\n color: '#78716c',\n fontSize: '0.75rem',\n cursor: 'pointer',\n};\n\nexport function BootErrorScreen({\n failureMode = 'unknown',\n detail,\n onRetry,\n components,\n}: BootErrorScreenProps): ReactNode {\n const [showDetail, setShowDetail] = useState(false);\n const Override = components?.BootErrorScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <div className=\"kf-boot-error-screen\" style={PANEL} role=\"alert\">\n <h1 className=\"kf-boot-error-screen__title\" style={TITLE}>\n Kaafil could not start\n </h1>\n <p className=\"kf-boot-error-screen__body\" style={BODY}>\n {COPY[failureMode]}\n </p>\n\n {onRetry === undefined ? null : (\n <button\n type=\"button\"\n className=\"kf-boot-error-screen__retry\"\n style={BUTTON}\n onClick={onRetry}\n >\n Try again\n </button>\n )}\n\n {detail === undefined || detail === '' ? null : (\n <div className=\"kf-boot-error-screen__detail\">\n {/* A disclosure rather than always-on: the message is the\n * underlying error's, which can be long and is not the first\n * thing a reader needs. */}\n <button\n type=\"button\"\n className=\"kf-boot-error-screen__detail-toggle\"\n style={{ ...TOGGLE, background: 'none', border: 0, padding: 0 }}\n aria-expanded={showDetail}\n onClick={() => setShowDetail((open) => !open)}\n >\n {showDetail ? 'Hide technical detail' : 'Show technical detail'}\n </button>\n {showDetail ? (\n <code className=\"kf-boot-error-screen__detail-text\" style={CODE}>\n {detail}\n </code>\n ) : null}\n </div>\n )}\n </div>\n );\n}\n","'use client';\n\n// The one frame the five context-reading system screens share.\n//\n// ── THE PRESENTATION FORK IS REAL, AND IT IS NOT A PROP ───────────────────\n//\n// `shared/01-system-screens.md` records a `Presentation notes by mounting\n// context` field on every screen: an `admin`-family mount is a CONTAINED\n// CARD inside the host's own CRM page, a `manager`-family mount is a\n// FULL-VIEWPORT takeover with its primary action reachable without\n// scrolling. The spec is explicit that this is a genuine layout-model fork\n// (`03-layer-contract.md §5` level 3), not a spacing difference — and\n// equally explicit that it is ONE component, never two named variants.\n//\n// It is derived from `usePersona()`, never from a prop. Hard rule #7: no\n// `persona` prop and no `mode` prop exists anywhere, and dx-07 fails the\n// build on either. The credential decides.\n//\n// ── WHY THIS IS A FRAME AND NOT AN ATOM ──────────────────────────────────\n//\n// The spec's `Composes` rows name `EmptyState`/`Button`/`Banner` atoms from\n// `atomic/*`. Those do not exist: the v2 rebuild ships no shared\n// `/primitives` layer, by decision — each family owns whatever atoms it\n// needs. So these five screens share a FRAME (the layout fork, the focus\n// move, the announcement) and own their own content, rather than depending\n// on a primitives package this package deliberately does not have.\n\nimport type { ReactNode } from 'react';\nimport { useEffect, useId, useRef } from 'react';\nimport { usePersona } from '../../../core/hooks/usePersona.js';\nimport type { KaafilTokenOverrides } from '../../../primitives/internal/tokenStyle.js';\nimport { tokenStyle } from '../../../primitives/internal/tokenStyle.js';\n\nexport interface SystemScreenFrameProps {\n /** BEM block name — each screen owns its own, so a host can target one\n * without catching the rest (`.kf-session-expired-screen__*`). */\n readonly block: string;\n readonly title: string;\n readonly body: ReactNode;\n /** Rendered below the body. On a field mount this is what the sticky\n * action region holds. */\n readonly actions?: ReactNode;\n /** An error/context line above the title. */\n readonly banner?: ReactNode;\n readonly icon?: ReactNode;\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function SystemScreenFrame({\n block,\n title,\n body,\n actions,\n banner,\n icon,\n style,\n}: SystemScreenFrameProps): ReactNode {\n const persona = usePersona();\n const headingRef = useRef<HTMLHeadingElement>(null);\n const headingId = useId();\n\n // A field mount owns the whole screen; a desk mount is embedded beside the\n // host's own nav and must not consume the viewport (briefing §27.7).\n // `traveller` takes the contained treatment too — only `OfflineFallback`\n // and `BootError` reach the public surface, and neither should black out\n // a document a host has embedded.\n const layout = persona === 'manager' ? 'takeover' : 'contained';\n\n // FOCUS MOVES TO THE HEADING on mount. This is a full state replacement,\n // not an overlay — there is no opener to return focus to, so the\n // overlay-focus contract (`12-accessibility.md §3`) does not apply and the\n // heading is the correct landing point. Without this a keyboard or screen\n // reader user gets no signal that the page became something else and Tab\n // keeps walking whatever is still behind it.\n useEffect(() => {\n headingRef.current?.focus();\n }, []);\n\n return (\n <div\n className=\"kf-system-screen\"\n data-block={block}\n data-layout={layout}\n style={tokenStyle(style)}\n // A significant, infrequent state change — announced politely rather\n // than assertively, since the focus move above already carries the\n // urgency and `assertive` would interrupt whatever is mid-sentence.\n role=\"status\"\n aria-live=\"polite\"\n aria-labelledby={headingId}\n >\n <div className={`kf-system-screen__panel ${block}`}>\n {icon === undefined ? null : (\n <span className=\"kf-system-screen__glyph\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n {banner === undefined ? null : <div className=\"kf-system-screen__banner\">{banner}</div>}\n {/* `tabIndex={-1}` so the effect above can focus it — a heading is\n * not focusable by default and focusing the panel instead would\n * draw the accent ring around the entire card. */}\n <h1 ref={headingRef} id={headingId} tabIndex={-1} className=\"kf-system-screen__title\">\n {title}\n </h1>\n <div className=\"kf-system-screen__body\">{body}</div>\n {actions === undefined ? null : <div className=\"kf-system-screen__actions\">{actions}</div>}\n </div>\n </div>\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §LockedTripScreen`.\n//\n// The destination for a WRITE-ONLY deep link into a closed-out trip — a\n// \"record this collection\" push sent before the trip closed, a bookmarked\n// mutation route. Mounting the flow it points at would show a form whose\n// one action can only ever answer `423`.\n//\n// BACKED BY NOTHING, and that is the discipline rather than a gap. This\n// screen never calls `closeout.get`/`useCloseout` to learn the trip is\n// locked: the caller already knows (its own write would 423, or the link's\n// own metadata carried the LOCKED stage). Checking again here would be a\n// second gate — \"check the resolved state before render, never\n// render-then-catch\", applied to lock state instead of capability darkness.\n//\n// LINKS to Closing Day, never mounts it. No unlock control appears here or\n// anywhere else at any layer (hard rule #21): the desk locks, the CRM\n// reopens.\n\nimport type { ReactNode } from 'react';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface LockedTripScreenProps {\n /** For context and for building the link's destination — never used to\n * fetch anything. */\n readonly tripRef: string;\n /** What the dead-end link would have done (\"Record a collection\"), from\n * whatever metadata came with it. Absent means the generic sentence: this\n * screen invents no explanation of its own. */\n readonly intendedAction?: string;\n /** The host's own navigation to wherever it mounts Closing Day. A screen\n * never navigates itself. */\n readonly onGoToClosingDay: (tripRef: string) => void;\n readonly components?: { readonly LockedTripScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function LockedTripScreen({\n tripRef,\n intendedAction,\n onGoToClosingDay,\n components,\n style,\n}: LockedTripScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const Override = components?.LockedTripScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <SystemScreenFrame\n block=\"kf-locked-trip-screen\"\n title={t('chrome.systemScreens.lockedTrip.title')}\n banner={\n intendedAction === undefined\n ? t('chrome.systemScreens.lockedTrip.genericAction')\n : t('chrome.systemScreens.lockedTrip.namedAction').replace('{action}', intendedAction)\n }\n body={<p>{t('chrome.systemScreens.lockedTrip.body')}</p>}\n {...(style !== undefined ? { style } : {})}\n actions={\n <button\n type=\"button\"\n className=\"kf-system-screen__primary\"\n onClick={() => onGoToClosingDay(tripRef)}\n >\n {t('chrome.systemScreens.lockedTrip.action')}\n </button>\n }\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §NotFoundScreen`.\n//\n// A route-level 404 for a `manager`/`agencyAdmin` mount: a bookmarked\n// `tripRef` from another agency, a deep link to a deleted entity, a stale\n// notification pointing at a now-cross-tenant resource.\n//\n// NOT THE TRAVELLER'S 404, and this is load-bearing rather than an\n// oversight. A dead share token is `ShareEndedScreen`'s `not-found`\n// variant, which must stay 404-shaped — no heading, no divider, no\n// distinguishing DOM signal (hard rule #20, enforced by dx-15). Rendering\n// THIS screen on the share surface would leak that the token was once real.\n// It is therefore never re-exported from `/traveller`.\n//\n// BACKED BY NOTHING, by design. It renders on a 404 that some mounted\n// flow's own hook already surfaced; it performs no second fetch to\n// \"double-check\" what the flow already learned.\n\nimport type { ReactNode } from 'react';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface NotFoundScreenProps {\n /** The host's own \"back\" navigation. Optional — a host that reached this\n * with no history to return to simply gets no button, which is more\n * honest than one that goes nowhere. */\n readonly onGoBack?: () => void;\n readonly components?: { readonly NotFoundScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function NotFoundScreen({ onGoBack, components, style }: NotFoundScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const Override = components?.NotFoundScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <SystemScreenFrame\n block=\"kf-not-found-screen\"\n title={t('chrome.systemScreens.notFound.title')}\n // Deliberately does not speculate about WHY. \"Deleted, or belongs to\n // another agency, or you mistyped it\" are three different sentences\n // and this screen cannot tell which is true — naming one would be a\n // guess presented as a diagnosis.\n body={<p>{t('chrome.systemScreens.notFound.body')}</p>}\n {...(style !== undefined ? { style } : {})}\n {...(onGoBack !== undefined\n ? {\n actions: (\n <button type=\"button\" className=\"kf-system-screen__primary\" onClick={onGoBack}>\n {t('chrome.systemScreens.notFound.action')}\n </button>\n ),\n }\n : {})}\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §OfflineFallbackScreen`.\n//\n// The device has nothing cached AND no connectivity — a manager opening a\n// trip for the first time at a trailhead before ever syncing, a desk\n// session losing the network before its first pull, a share link opened\n// with zero signal before the first snapshot.\n//\n// THE ONLY ONE OF THE SIX THE TRAVELLER SURFACE SHARES besides\n// `BootErrorScreen`, and it is safe there precisely because it discloses\n// nothing: \"there is no connection\" is true of any URL and leaks nothing\n// about whether this token was ever real. That is what separates it from\n// `ShareEndedScreen`'s dead-token cases, which are about a token's\n// VALIDITY and must stay 404-shaped (hard rule #20).\n//\n// SESSION STATE OUTRANKS THIS. An offline device whose credential has also\n// expired shows `SessionExpiredScreen`, not this — re-authentication is the\n// blocking concern regardless of connectivity, and a caller composing both\n// should check session first.\n\nimport type { ReactNode } from 'react';\nimport { useOutboxStatus } from '../../core/hooks/useOutboxStatus.js';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface OfflineFallbackScreenProps {\n /** Present on a trip-scoped mount; absent on the share surface. Context\n * only — this screen fetches nothing. */\n readonly tripRef?: string;\n /** Present on a share mount instead of `tripRef`. */\n readonly token?: string;\n /** The host's own retry — typically remounting whatever failed. */\n readonly onRetry?: () => void;\n readonly components?: { readonly OfflineFallbackScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function OfflineFallbackScreen({\n onRetry,\n components,\n style,\n}: OfflineFallbackScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const { isOnline } = useOutboxStatus();\n const Override = components?.OfflineFallbackScreenUI;\n if (Override !== undefined) return <Override />;\n\n return (\n <SystemScreenFrame\n block=\"kf-offline-fallback-screen\"\n title={t('chrome.systemScreens.offlineFallback.title')}\n body={\n <>\n <p>{t('chrome.systemScreens.offlineFallback.body')}</p>\n {/* Connectivity came back while this was on screen. Said rather\n * than acted on: this screen does not refetch or navigate on its\n * own, because whatever failed is the caller's to retry — but\n * leaving a stale \"you are offline\" up while the bars are back is\n * the thing that makes an app feel broken. */}\n {isOnline ? (\n <p data-tone=\"ok\">{t('chrome.systemScreens.offlineFallback.backOnline')}</p>\n ) : null}\n </>\n }\n {...(style !== undefined ? { style } : {})}\n {...(onRetry !== undefined\n ? {\n actions: (\n <button type=\"button\" className=\"kf-system-screen__primary\" onClick={onRetry}>\n {t('chrome.systemScreens.offlineFallback.action')}\n </button>\n ),\n }\n : {})}\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §PlanLockedScreen`.\n//\n// The takeover a STANDALONE route (no surrounding tab bar) renders when its\n// destination module is flag-dark. Admin-only: entitlement is a desk\n// concern, a manager's flows are always mounted inside a tab set where a\n// flag-dark tab renders its own inline view, and the share surface has no\n// upsell copy at all (its flag-dark case is 404-shaped).\n//\n// ── A SPEC FIELD THAT HAS NO WIRE SOURCE ─────────────────────────────────\n//\n// The spec splits the CTA by `details.reason` into `'agency'` (the agency's\n// own plan) vs `'partner-ceiling'` (the partner's tier caps it), so the\n// right person is sent to the right queue. `CapabilityRow` carries no such\n// field — `06-capability-and-personas.md §6` point 2 fixes it as \"four\n// booleans, no `reason` string\", and `capability.types.ts` says so too.\n//\n// So the reason is an OPTIONAL PROP, supplied by whatever the caller saw\n// (a refusal's own `details`), and `onUpgradeRequest` is called with\n// `undefined` when nobody knows. Defaulting it to `'agency'` would have\n// been a guess routed to a real support queue — the host can decide what to\n// do with \"unknown\" far better than this screen can invent an answer.\n\nimport type { ReactNode } from 'react';\nimport { useCapabilities } from '../../core/hooks/useCapabilities.js';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport type PlanLockedReason = 'agency' | 'partner-ceiling';\n\nexport interface PlanLockedScreenProps {\n /** The capability this route was headed for. Read for its own triple\n * only — never exhaustively switched on (`§6` point 5's open-set rule). */\n readonly capabilityKey: string;\n /** The host's own upsell/support flow — Kaafil has no in-product upgrade\n * path to hand off to. `undefined` means the wire did not say which\n * queue this belongs in; see this file's header. */\n readonly onUpgradeRequest: (reason: PlanLockedReason | undefined) => void;\n /** Supplied by the caller when it saw a refusal that named one. */\n readonly reason?: PlanLockedReason;\n readonly components?: { readonly PlanLockedScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function PlanLockedScreen({\n capabilityKey,\n onUpgradeRequest,\n reason,\n components,\n style,\n}: PlanLockedScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const capabilities = useCapabilities([capabilityKey]);\n const Override = components?.PlanLockedScreenUI;\n if (Override !== undefined) return <Override />;\n\n // Read, never re-derived: `status` is resolved once in `/core` and this\n // screen reads it rather than recombining the booleans itself\n // (`06-capability-and-personas.md §6` point 2).\n const status = capabilities.get(capabilityKey)?.status;\n\n return (\n <SystemScreenFrame\n block=\"kf-plan-locked-screen\"\n title={t('chrome.systemScreens.planLocked.title')}\n body={\n <>\n <p>\n {t(\n reason === 'partner-ceiling'\n ? 'chrome.systemScreens.planLocked.bodyPartnerCeiling'\n : 'chrome.systemScreens.planLocked.body',\n )}\n </p>\n {/* A route that reached this screen for a capability that is NOT\n * flag-dark is a caller bug, and saying so beats rendering upsell\n * copy over a module that is merely data-dark or simply lit. */}\n {status === 'flag-dark' ? null : (\n <p data-tone=\"warn\">{t('chrome.systemScreens.planLocked.notFlagDark')}</p>\n )}\n </>\n }\n {...(style !== undefined ? { style } : {})}\n actions={\n <button\n type=\"button\"\n className=\"kf-system-screen__primary\"\n onClick={() => onUpgradeRequest(reason)}\n >\n {t('chrome.systemScreens.planLocked.action')}\n </button>\n }\n />\n );\n}\n","'use client';\n\n// `shared/01-system-screens.md §SessionExpiredScreen`.\n//\n// Renders exactly when `useSession().status === 'expired'` — the open\n// credential's silent refresh has exhausted itself. Re-authentication is\n// routed through the HOST's own login; Kaafil has no login page to redirect\n// to, which is why `onReauthenticate` is required and has no default.\n//\n// DOES NOT retry any token call itself. A broken credential means the\n// host's own user is not authenticated and must re-authenticate through the\n// host's own flow — obtaining fresh tokens is `SessionShell`'s concern on\n// the NEXT successful mount, not this screen's on the current broken one.\n//\n// DOES NOT inspect which credential kind expired beyond the resolved\n// persona (`00-decisions.md` U-003 forecloses reading a JWT at any layer\n// above `SessionShell`).\n\nimport type { ReactNode } from 'react';\nimport { useState } from 'react';\nimport { useKaafilChrome } from '../../core/shells/I18nShell.js';\nimport type { KaafilTokenOverrides } from '../../primitives/internal/tokenStyle.js';\nimport { SystemScreenFrame } from './internal/SystemScreenFrame';\n\nexport interface SessionExpiredScreenProps {\n /** The host's own re-auth entry point. Required: there is no default\n * action, because Kaafil has no login page of its own. */\n readonly onReauthenticate: (returnTo?: string) => void | Promise<void>;\n /** Threaded back to `onReauthenticate` UNEXAMINED. This screen never\n * interprets it, builds a URL from it, or hands it to a router — it\n * exists purely so the host's own flow can restore the right destination\n * afterwards. */\n readonly returnTo?: string;\n readonly components?: { readonly SessionExpiredScreenUI?: () => ReactNode };\n readonly style?: KaafilTokenOverrides;\n}\n\nexport function SessionExpiredScreen({\n onReauthenticate,\n returnTo,\n components,\n style,\n}: SessionExpiredScreenProps): ReactNode {\n const { t } = useKaafilChrome();\n const [pending, setPending] = useState(false);\n // The host's own auth failure reason is not something Kaafil can\n // classify, so the rejection's own message is shown verbatim.\n const [failure, setFailure] = useState<string | undefined>(undefined);\n\n const Override = components?.SessionExpiredScreenUI;\n if (Override !== undefined) return <Override />;\n\n async function reauthenticate(): Promise<void> {\n setPending(true);\n setFailure(undefined);\n try {\n await onReauthenticate(returnTo);\n } catch (error) {\n setFailure(error instanceof Error ? error.message : String(error));\n } finally {\n setPending(false);\n }\n }\n\n return (\n <SystemScreenFrame\n block=\"kf-session-expired-screen\"\n title={t('chrome.systemScreens.sessionExpired.title')}\n body={<p>{t('chrome.systemScreens.sessionExpired.body')}</p>}\n {...(failure !== undefined ? { banner: <span data-tone=\"warn\">{failure}</span> } : {})}\n {...(style !== undefined ? { style } : {})}\n actions={\n <button\n type=\"button\"\n className=\"kf-system-screen__primary\"\n disabled={pending}\n onClick={() => void reauthenticate()}\n >\n {t(\n pending\n ? 'chrome.systemScreens.sessionExpired.busy'\n : 'chrome.systemScreens.sessionExpired.action',\n )}\n </button>\n }\n />\n );\n}\n"]}
@@ -1,16 +1,16 @@
1
1
  'use strict';
2
2
 
3
- var chunkUALMCHNW_cjs = require('./chunk-UALMCHNW.cjs');
3
+ var chunk3N2EQ2KC_cjs = require('./chunk-3N2EQ2KC.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(chunk3N2EQ2KC_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
+ chunk3N2EQ2KC_cjs.useOfflineEvent("sync.error", () => forceRefresh((current) => current + 1));
13
+ chunk3N2EQ2KC_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 = chunk3N2EQ2KC_cjs.useKaafilClient();
51
+ const engine = react.useContext(chunk3N2EQ2KC_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 = chunk3N2EQ2KC_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-KETEED2T.cjs.map
127
+ //# sourceMappingURL=chunk-KETEED2T.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-KETEED2T.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,4 +1,4 @@
1
- import { OfflineEngineContext, useOfflineEvent, useKaafilClient } from './chunk-A4Z2YBEF.js';
1
+ import { OfflineEngineContext, useOfflineEvent, useKaafilClient } from './chunk-7G6IENKN.js';
2
2
  import { useContext, useState, useMemo, useEffect } from 'react';
3
3
 
4
4
  function isVersionConflict(op) {
@@ -119,5 +119,5 @@ function useNotifications() {
119
119
  }
120
120
 
121
121
  export { useConflicts, useManagerMe, useNotifications };
122
- //# sourceMappingURL=chunk-RWLO6IFX.js.map
123
- //# sourceMappingURL=chunk-RWLO6IFX.js.map
122
+ //# sourceMappingURL=chunk-KTEXDGAA.js.map
123
+ //# sourceMappingURL=chunk-KTEXDGAA.js.map