@securitydept/client 0.3.0-beta.1 → 0.3.0-beta.3

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 (33) hide show
  1. package/dist/auth-coordination/index.d.mts +1 -1
  2. package/dist/auth-coordination/index.mjs +1 -1
  3. package/dist/auth-coordination/index.mjs.map +1 -1
  4. package/dist/{fetch-transport-DtnSRWAH.mjs → client-environment-CaumNmir.mjs} +187 -6
  5. package/dist/client-environment-CaumNmir.mjs.map +1 -0
  6. package/dist/{fetch-transport-Bd5RyWj2.d.mts → client-environment-CdHtVxKW.d.mts} +70 -8
  7. package/dist/events/index.d.mts +1 -1
  8. package/dist/events-BUlfsRfl.mjs.map +1 -1
  9. package/dist/{index-C_wgl6dW.d.mts → index-C4xlKhG7.d.mts} +2 -2
  10. package/dist/{index-DbYSFHDe.d.mts → index-COUt5x9Z.d.mts} +2 -2
  11. package/dist/index.d.mts +10 -10
  12. package/dist/index.mjs +2 -2
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/persistence/index.d.mts +2 -2
  15. package/dist/persistence/web/index.d.mts +1 -1
  16. package/dist/persistence/web/index.mjs +1 -50
  17. package/dist/persistence-CSB8770k.mjs.map +1 -1
  18. package/dist/{planner-host-CV38bMSn.mjs → planner-host-BTWie0lo.mjs} +3 -1
  19. package/dist/{planner-host-CV38bMSn.mjs.map → planner-host-BTWie0lo.mjs.map} +1 -1
  20. package/dist/{planner-host-CPyGNyQV.d.mts → planner-host-DN9dRsQx.d.mts} +1 -1
  21. package/dist/{types-DQc088ZP.d.mts → types-BJqUdH2e.d.mts} +1 -1
  22. package/dist/{types-C59fcM5W.d.mts → types-BwcnUGNl.d.mts} +1 -1
  23. package/dist/web/index.d.mts +40 -12
  24. package/dist/web/index.mjs +104 -14
  25. package/dist/web/index.mjs.map +1 -1
  26. package/dist/web-DQmMqZnR.mjs +51 -0
  27. package/dist/web-DQmMqZnR.mjs.map +1 -0
  28. package/dist/web-router/index.d.mts +1 -1
  29. package/dist/web-router/index.mjs +1 -1
  30. package/dist/web-router/index.mjs.map +1 -1
  31. package/package.json +2 -2
  32. package/dist/fetch-transport-DtnSRWAH.mjs.map +0 -1
  33. package/dist/persistence/web/index.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/web-router/navigation-adapter.ts","../../src/web-router/web-router.ts"],"sourcesContent":["// Navigation adapter — abstraction over Web Navigation API / History API\n//\n// Canonical subpath: @securitydept/client/web-router\n//\n// The Web Navigation API (window.navigation) is the modern, cleaner\n// primitive for client-side routing (pre-commit intercept, typed intents,\n// rollback support). Where available (Chromium ≥ 102), we prefer it.\n// Otherwise we fall back to the classic History API + popstate + link\n// click capture, which every browser still supports.\n//\n// Both adapters implement the same `NavigationAdapter` contract so the\n// higher-level `createWebRouter` factory does not need to branch on which\n// backend is active — tests can also target either path deterministically.\n//\n// Stability: provisional (new in iteration 110)\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\nexport const NavigationAdapterKind = {\n\tNavigationApi: \"navigation-api\",\n\tHistory: \"history\",\n} as const;\nexport type NavigationAdapterKind =\n\t(typeof NavigationAdapterKind)[keyof typeof NavigationAdapterKind];\n\nexport const NavigationCause = {\n\tInitial: \"initial\",\n\tPush: \"push\",\n\tReplace: \"replace\",\n\tTraverse: \"traverse\",\n} as const;\nexport type NavigationCause =\n\t(typeof NavigationCause)[keyof typeof NavigationCause];\n\nexport interface NavigationIntent {\n\treadonly url: URL;\n\treadonly cause: NavigationCause;\n\treadonly replace: boolean;\n\treadonly state: unknown;\n\t/** Abort this navigation before it commits. */\n\tpreventDefault(): void;\n\t/** Cancel the current intent and navigate to the given URL instead. */\n\tredirect(url: string | URL, options?: { replace?: boolean }): void;\n}\n\nexport interface NavigationCommit {\n\treadonly url: URL;\n}\n\nexport interface NavigateOptions {\n\t/** When true, replace the current history entry instead of pushing. */\n\treplace?: boolean;\n\t/** Serializable state to attach to the history entry. */\n\tstate?: unknown;\n\t/**\n\t * Arbitrary per-navigation metadata. Passed through to `NavigateEvent.info`\n\t * when the Navigation API adapter is active; ignored by the History\n\t * adapter (no wire support).\n\t */\n\tinfo?: unknown;\n}\n\nexport interface NavigationAdapter {\n\treadonly kind: NavigationAdapterKind;\n\tcurrentUrl(): URL;\n\tnavigate(url: string | URL, options?: NavigateOptions): Promise<void>;\n\tback(): void;\n\tforward(): void;\n\t/**\n\t * Called *before* a navigation commits, so listeners can cancel or\n\t * redirect. Both adapters forward synchronous and asynchronous handlers\n\t * (adapters await the intent being fully processed before committing).\n\t */\n\tonBeforeNavigate(\n\t\tlistener: (intent: NavigationIntent) => void | Promise<void>,\n\t): () => void;\n\t/** Called *after* a navigation commits. */\n\tonNavigate(listener: (commit: NavigationCommit) => void): () => void;\n\tdestroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Navigation API typings (reduced; TS DOM libs cover this but the types are\n// still experimental in some TypeScript versions, so we mirror the minimal\n// surface we consume here).\n// ---------------------------------------------------------------------------\n\ninterface NavigationApiNavigation {\n\treadonly currentEntry: { getState(): unknown } | null;\n\tnavigate(\n\t\turl: string,\n\t\toptions?: { history?: \"push\" | \"replace\"; state?: unknown; info?: unknown },\n\t): { committed: Promise<unknown>; finished: Promise<unknown> };\n\tback(): { committed: Promise<unknown> };\n\tforward(): { committed: Promise<unknown> };\n\taddEventListener(\n\t\ttype: \"navigate\",\n\t\tlistener: (event: NavigationApiNavigateEvent) => void,\n\t): void;\n\taddEventListener(\n\t\ttype: \"navigatesuccess\",\n\t\tlistener: (event: Event) => void,\n\t): void;\n\tremoveEventListener(\n\t\ttype: \"navigate\",\n\t\tlistener: (event: NavigationApiNavigateEvent) => void,\n\t): void;\n\tremoveEventListener(\n\t\ttype: \"navigatesuccess\",\n\t\tlistener: (event: Event) => void,\n\t): void;\n}\n\ninterface NavigationApiNavigateEvent extends Event {\n\treadonly destination: { url: string; getState(): unknown };\n\treadonly navigationType: \"push\" | \"replace\" | \"reload\" | \"traverse\";\n\treadonly canIntercept: boolean;\n\treadonly userInitiated: boolean;\n\tintercept(options?: { handler?: () => Promise<void> | void }): void;\n\tredirect?(url: string, options?: { history?: \"push\" | \"replace\" }): void;\n}\n\nfunction navigationApi(): NavigationApiNavigation | undefined {\n\treturn (globalThis as { navigation?: NavigationApiNavigation }).navigation;\n}\n\n// ---------------------------------------------------------------------------\n// Navigation API adapter\n// ---------------------------------------------------------------------------\n\nexport function createNavigationApiAdapter(): NavigationAdapter {\n\tconst nav = navigationApi();\n\tif (!nav) {\n\t\tthrow new Error(\n\t\t\t\"[createNavigationApiAdapter] window.navigation is not available in this environment.\",\n\t\t);\n\t}\n\n\tconst beforeListeners = new Set<\n\t\t(intent: NavigationIntent) => void | Promise<void>\n\t>();\n\tconst commitListeners = new Set<(commit: NavigationCommit) => void>();\n\n\tconst handleNavigate = (event: NavigationApiNavigateEvent) => {\n\t\tif (beforeListeners.size === 0 && commitListeners.size === 0) return;\n\t\tif (!event.canIntercept) return;\n\n\t\tconst url = new URL(event.destination.url, location.href);\n\t\tconst cause: NavigationCause =\n\t\t\tevent.navigationType === \"push\"\n\t\t\t\t? NavigationCause.Push\n\t\t\t\t: event.navigationType === \"replace\"\n\t\t\t\t\t? NavigationCause.Replace\n\t\t\t\t\t: NavigationCause.Traverse;\n\n\t\tlet prevented = false;\n\t\tlet redirectTarget: { url: URL; replace: boolean } | null = null;\n\n\t\tconst intent: NavigationIntent = {\n\t\t\turl,\n\t\t\tcause,\n\t\t\treplace: event.navigationType === \"replace\",\n\t\t\tstate: event.destination.getState(),\n\t\t\tpreventDefault: () => {\n\t\t\t\tprevented = true;\n\t\t\t\t// There is no native preventDefault on NavigateEvent for\n\t\t\t\t// cancellation outside of `intercept({ handler })`, so we\n\t\t\t\t// throw inside the handler to abort the committed promise.\n\t\t\t},\n\t\t\tredirect: (target, options) => {\n\t\t\t\tredirectTarget = {\n\t\t\t\t\turl: new URL(target.toString(), url),\n\t\t\t\t\treplace: options?.replace ?? false,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\n\t\tevent.intercept({\n\t\t\thandler: async () => {\n\t\t\t\tfor (const listener of beforeListeners) {\n\t\t\t\t\tawait listener(intent);\n\t\t\t\t\tif (prevented || redirectTarget) break;\n\t\t\t\t}\n\t\t\t\tif (prevented && !redirectTarget) {\n\t\t\t\t\tthrow new DOMException(\n\t\t\t\t\t\t\"Navigation prevented by interceptor\",\n\t\t\t\t\t\t\"AbortError\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (redirectTarget) {\n\t\t\t\t\tconst target = redirectTarget as { url: URL; replace: boolean };\n\t\t\t\t\tnav.navigate(target.url.toString(), {\n\t\t\t\t\t\thistory: target.replace ? \"replace\" : \"push\",\n\t\t\t\t\t});\n\t\t\t\t\tthrow new DOMException(\n\t\t\t\t\t\t\"Navigation redirected by interceptor\",\n\t\t\t\t\t\t\"AbortError\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfor (const listener of commitListeners) listener({ url });\n\t\t\t},\n\t\t});\n\t};\n\n\tconst handleSuccess = (_event: Event) => {\n\t\t// Navigation API fires `navigatesuccess` even when no handler\n\t\t// intercepted. We still want to notify commit listeners for\n\t\t// such cases (typed external navigations).\n\t\tconst url = new URL(location.href);\n\t\tfor (const listener of commitListeners) listener({ url });\n\t};\n\n\tnav.addEventListener(\"navigate\", handleNavigate);\n\tnav.addEventListener(\"navigatesuccess\", handleSuccess);\n\n\treturn {\n\t\tkind: NavigationAdapterKind.NavigationApi,\n\t\tcurrentUrl: () => new URL(location.href),\n\t\tnavigate: async (url, options) => {\n\t\t\tconst target = new URL(url.toString(), location.href);\n\t\t\tconst result = nav.navigate(target.toString(), {\n\t\t\t\thistory: options?.replace ? \"replace\" : \"push\",\n\t\t\t\tstate: options?.state,\n\t\t\t\tinfo: options?.info,\n\t\t\t});\n\t\t\tawait result.committed.catch(() => undefined);\n\t\t},\n\t\tback: () => {\n\t\t\tnav.back();\n\t\t},\n\t\tforward: () => {\n\t\t\tnav.forward();\n\t\t},\n\t\tonBeforeNavigate: (listener) => {\n\t\t\tbeforeListeners.add(listener);\n\t\t\treturn () => beforeListeners.delete(listener);\n\t\t},\n\t\tonNavigate: (listener) => {\n\t\t\tcommitListeners.add(listener);\n\t\t\treturn () => commitListeners.delete(listener);\n\t\t},\n\t\tdestroy: () => {\n\t\t\tnav.removeEventListener(\"navigate\", handleNavigate);\n\t\t\tnav.removeEventListener(\"navigatesuccess\", handleSuccess);\n\t\t\tbeforeListeners.clear();\n\t\t\tcommitListeners.clear();\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// History API fallback adapter\n// ---------------------------------------------------------------------------\n\nexport interface CreateHistoryAdapterOptions {\n\t/**\n\t * When true (default), the adapter captures clicks on in-document\n\t * `<a href>` elements and routes them through `navigate()` so guards\n\t * can intercept. Disable to handle anchor navigation manually.\n\t */\n\tcaptureAnchorClicks?: boolean;\n}\n\nexport function createHistoryAdapter(\n\toptions: CreateHistoryAdapterOptions = {},\n): NavigationAdapter {\n\tconst captureAnchorClicks = options.captureAnchorClicks ?? true;\n\n\tconst beforeListeners = new Set<\n\t\t(intent: NavigationIntent) => void | Promise<void>\n\t>();\n\tconst commitListeners = new Set<(commit: NavigationCommit) => void>();\n\n\tconst dispatchBefore = async (intent: NavigationIntent): Promise<boolean> => {\n\t\tfor (const listener of beforeListeners) {\n\t\t\tawait listener(intent);\n\t\t}\n\t\treturn true;\n\t};\n\n\tconst commit = (url: URL) => {\n\t\tfor (const listener of commitListeners) listener({ url });\n\t};\n\n\tconst handlePopstate = async (event: PopStateEvent) => {\n\t\tconst url = new URL(location.href);\n\t\tlet prevented = false;\n\t\tlet redirectTarget: { url: URL; replace: boolean } | null = null;\n\t\tconst intent: NavigationIntent = {\n\t\t\turl,\n\t\t\tcause: NavigationCause.Traverse,\n\t\t\treplace: false,\n\t\t\tstate: event.state,\n\t\t\tpreventDefault: () => {\n\t\t\t\tprevented = true;\n\t\t\t},\n\t\t\tredirect: (target, opts) => {\n\t\t\t\tredirectTarget = {\n\t\t\t\t\turl: new URL(target.toString(), url),\n\t\t\t\t\treplace: opts?.replace ?? false,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t\tawait dispatchBefore(intent);\n\t\tif (prevented && !redirectTarget) {\n\t\t\t// History API has no native way to cancel a popstate; the\n\t\t\t// best we can do is navigate forward to the previous URL.\n\t\t\t// Adopters that need true cancellation should prefer the\n\t\t\t// Navigation API path or use `intercept` for *pre*-action\n\t\t\t// decisions (e.g. chooser dialogs) rather than cancellation.\n\t\t\treturn;\n\t\t}\n\t\tif (redirectTarget) {\n\t\t\tconst target = redirectTarget as { url: URL; replace: boolean };\n\t\t\tif (target.replace) {\n\t\t\t\thistory.replaceState(null, \"\", target.url.toString());\n\t\t\t} else {\n\t\t\t\thistory.pushState(null, \"\", target.url.toString());\n\t\t\t}\n\t\t\tcommit(target.url);\n\t\t\treturn;\n\t\t}\n\t\tcommit(url);\n\t};\n\n\tconst handleAnchorClick = async (event: MouseEvent) => {\n\t\tif (\n\t\t\tevent.defaultPrevented ||\n\t\t\tevent.button !== 0 ||\n\t\t\tevent.metaKey ||\n\t\t\tevent.ctrlKey ||\n\t\t\tevent.shiftKey ||\n\t\t\tevent.altKey\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst path = event.composedPath();\n\t\tconst anchor = path.find(\n\t\t\t(node): node is HTMLAnchorElement => node instanceof HTMLAnchorElement,\n\t\t);\n\t\tif (!anchor?.href || anchor.target === \"_blank\") return;\n\t\tconst targetUrl = new URL(anchor.href);\n\t\tif (targetUrl.origin !== location.origin) return;\n\t\tevent.preventDefault();\n\t\tawait navigateInternal(targetUrl, { replace: false });\n\t};\n\n\tconst navigateInternal = async (\n\t\turl: URL,\n\t\toptions: NavigateOptions,\n\t): Promise<void> => {\n\t\tlet prevented = false;\n\t\tlet redirectTarget: { url: URL; replace: boolean } | null = null;\n\t\tconst intent: NavigationIntent = {\n\t\t\turl,\n\t\t\tcause: options.replace ? NavigationCause.Replace : NavigationCause.Push,\n\t\t\treplace: options.replace ?? false,\n\t\t\tstate: options.state,\n\t\t\tpreventDefault: () => {\n\t\t\t\tprevented = true;\n\t\t\t},\n\t\t\tredirect: (target, opts) => {\n\t\t\t\tredirectTarget = {\n\t\t\t\t\turl: new URL(target.toString(), url),\n\t\t\t\t\treplace: opts?.replace ?? false,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t\tawait dispatchBefore(intent);\n\t\tif (prevented && !redirectTarget) return;\n\t\tconst target = redirectTarget\n\t\t\t? (redirectTarget as { url: URL; replace: boolean })\n\t\t\t: { url, replace: options.replace ?? false };\n\t\tif (target.replace) {\n\t\t\thistory.replaceState(options.state ?? null, \"\", target.url.toString());\n\t\t} else {\n\t\t\thistory.pushState(options.state ?? null, \"\", target.url.toString());\n\t\t}\n\t\tcommit(target.url);\n\t};\n\n\tif (typeof window !== \"undefined\") {\n\t\twindow.addEventListener(\"popstate\", handlePopstate);\n\t\tif (captureAnchorClicks) {\n\t\t\tdocument.addEventListener(\"click\", handleAnchorClick);\n\t\t}\n\t}\n\n\treturn {\n\t\tkind: NavigationAdapterKind.History,\n\t\tcurrentUrl: () => new URL(location.href),\n\t\tnavigate: (url, navOptions) =>\n\t\t\tnavigateInternal(\n\t\t\t\tnew URL(url.toString(), location.href),\n\t\t\t\tnavOptions ?? {},\n\t\t\t),\n\t\tback: () => {\n\t\t\thistory.back();\n\t\t},\n\t\tforward: () => {\n\t\t\thistory.forward();\n\t\t},\n\t\tonBeforeNavigate: (listener) => {\n\t\t\tbeforeListeners.add(listener);\n\t\t\treturn () => beforeListeners.delete(listener);\n\t\t},\n\t\tonNavigate: (listener) => {\n\t\t\tcommitListeners.add(listener);\n\t\t\treturn () => commitListeners.delete(listener);\n\t\t},\n\t\tdestroy: () => {\n\t\t\tif (typeof window !== \"undefined\") {\n\t\t\t\twindow.removeEventListener(\"popstate\", handlePopstate);\n\t\t\t\tif (captureAnchorClicks) {\n\t\t\t\t\tdocument.removeEventListener(\"click\", handleAnchorClick);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeforeListeners.clear();\n\t\t\tcommitListeners.clear();\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Auto-select adapter\n// ---------------------------------------------------------------------------\n\nexport interface CreateNavigationAdapterOptions {\n\t/**\n\t * Force a specific adapter kind. Useful for tests that need to exercise\n\t * the fallback path in a Chromium environment that ships the Navigation\n\t * API.\n\t * @default \"auto\"\n\t */\n\tprefer?: \"auto\" | NavigationAdapterKind;\n\t/**\n\t * Options forwarded to {@link createHistoryAdapter} when the history\n\t * adapter is selected.\n\t */\n\thistory?: CreateHistoryAdapterOptions;\n}\n\n/**\n * Pick the best available navigation backend. Prefers the Web Navigation\n * API when `window.navigation` is defined; falls back to the History API\n * otherwise.\n */\nexport function createNavigationAdapter(\n\toptions: CreateNavigationAdapterOptions = {},\n): NavigationAdapter {\n\tconst prefer = options.prefer ?? \"auto\";\n\tif (prefer === NavigationAdapterKind.History) {\n\t\treturn createHistoryAdapter(options.history);\n\t}\n\tif (prefer === NavigationAdapterKind.NavigationApi) {\n\t\treturn createNavigationApiAdapter();\n\t}\n\tif (navigationApi()) {\n\t\treturn createNavigationApiAdapter();\n\t}\n\treturn createHistoryAdapter(options.history);\n}\n\n/**\n * Quick capability probe — true when the Web Navigation API is present.\n */\nexport function isNavigationApiAvailable(): boolean {\n\treturn navigationApi() !== undefined;\n}\n","// @securitydept/client/web-router — framework-free web router baseline\n//\n// Provides a thin router layer built on `NavigationAdapter` (Navigation API\n// or History API fallback) with planner-host integration for declarative,\n// auth-aware route guards.\n//\n// Iteration 110 review-1 fix: the router now supports a nested route tree\n// with full-route requirement aggregation (inherit / merge / replace\n// composition) so that parent + child + sibling security declarations are\n// combined into a single candidate set before the planner-host runs — the\n// same contract depth offered by the Angular adapter and the TanStack\n// Router adapter. Flat declarations remain supported: a route without\n// `children` behaves exactly as before.\n//\n// Stability: provisional (new in iteration 110)\n\nimport type {\n\tAuthGuardClientOption,\n\tPlannerHost,\n\tRequirementsClientSetComposition as RequirementsClientSetCompositionType,\n} from \"../auth-coordination/planner-host\";\nimport {\n\tRequirementsClientSetComposition,\n\tresolveEffectiveClientSet,\n} from \"../auth-coordination/planner-host\";\nimport {\n\ttype CreateNavigationAdapterOptions,\n\tcreateNavigationAdapter,\n\ttype NavigationAdapter,\n\tNavigationAdapterKind,\n\ttype NavigationCommit,\n\ttype NavigationIntent,\n} from \"./navigation-adapter\";\n\n// ---------------------------------------------------------------------------\n// Route definition\n// ---------------------------------------------------------------------------\n\nexport interface WebRouteContext {\n\t/** The URL being navigated to. */\n\turl: URL;\n\t/** Intent metadata. Null for programmatic `activate()` calls. */\n\tintent: NavigationIntent | null;\n}\n\nexport type WebRouteMatcher =\n\t| string\n\t| RegExp\n\t| ((url: URL) => Record<string, string> | null | undefined);\n\nexport interface WebRouteDefinition<TData = unknown> {\n\t/** Opaque identifier. */\n\tid: string;\n\t/**\n\t * Matcher for this route segment. Required on leaves — parents that\n\t * only carry metadata for children may omit `match`.\n\t *\n\t * A string is treated as a path-with-params pattern\n\t * (`\"/users/:id/profile\"` → `{ id: \"123\" }` for `/users/123/profile`).\n\t * A RegExp uses `exec(pathname)` and passes named/indexed groups\n\t * through as params. A function returns the parsed params or null.\n\t */\n\tmatch?: WebRouteMatcher;\n\t/**\n\t * Auth requirements declared at this route segment. The router walks\n\t * the matched route chain from root to leaf and aggregates every\n\t * segment's requirements via {@link extractFullRouteRequirements}\n\t * before asking the planner-host to evaluate.\n\t */\n\trequirements?: readonly AuthGuardClientOption[];\n\t/**\n\t * Composition strategy with the parent segment in the matched chain.\n\t *\n\t * - `merge` (default): parent requirements are kept, child requirements\n\t * append/override by `requirementId`.\n\t * - `inherit`: child declares no additional requirements; parent set is\n\t * used as-is.\n\t * - `replace`: parent requirements are discarded entirely; this child\n\t * contributes the full effective set (use for \"public\" subtrees of an\n\t * otherwise protected parent).\n\t */\n\tcomposition?: RequirementsClientSetCompositionType;\n\t/**\n\t * Nested child routes. When present, `match` on the parent is optional\n\t * and is not consulted for matching leaves — only the leaves' matchers\n\t * participate in URL resolution. The chain from root to leaf is what\n\t * drives requirement aggregation.\n\t */\n\tchildren?: readonly WebRouteDefinition<TData>[];\n\t/** Arbitrary data attached to matched routes. */\n\tdata?: TData;\n}\n\nexport interface WebRouteMatch<TData = unknown> {\n\t/** The leaf route that matched. */\n\treadonly route: WebRouteDefinition<TData>;\n\treadonly url: URL;\n\treadonly params: Record<string, string>;\n\t/**\n\t * The complete chain from root ancestor → leaf. A flat route without\n\t * children produces a single-element chain.\n\t */\n\treadonly chain: readonly WebRouteDefinition<TData>[];\n}\n\n// ---------------------------------------------------------------------------\n// Declaration helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Sugar for declaring a route segment. Enforces a consistent shape so\n * adopter code reads like the Angular / TanStack helpers without forcing\n * any particular DI or decorator style.\n */\nexport function defineWebRoute<TData = unknown>(\n\tdefinition: WebRouteDefinition<TData>,\n): WebRouteDefinition<TData> {\n\treturn definition;\n}\n\nexport interface FullRouteRequirementsOptions {\n\t/**\n\t * Default composition for segments that do not set their own\n\t * `composition` field. Defaults to `merge` to match the Angular helper\n\t * and the TanStack adapter.\n\t */\n\tdefaultComposition?: RequirementsClientSetCompositionType;\n}\n\n/**\n * Aggregate every segment's `requirements` across the matched chain into\n * a single {@link AuthGuardClientOption} list, honouring each segment's\n * composition strategy (`inherit` / `merge` / `replace`).\n *\n * This is the extraction counter-part to the nested `WebRouteDefinition`\n * tree — the router calls it once per matched navigation and forwards the\n * resulting flat list to the planner-host.\n */\nexport function extractFullRouteRequirements<TData = unknown>(\n\tchain: readonly WebRouteDefinition<TData>[],\n\toptions: FullRouteRequirementsOptions = {},\n): AuthGuardClientOption[] {\n\tconst defaultComposition =\n\t\toptions.defaultComposition ?? RequirementsClientSetComposition.Merge;\n\tlet effective: AuthGuardClientOption[] = [];\n\tfor (const segment of chain) {\n\t\tconst composition = segment.composition ?? defaultComposition;\n\t\tconst options = segment.requirements ?? [];\n\t\teffective = resolveEffectiveClientSet(effective, {\n\t\t\tcomposition,\n\t\t\toptions,\n\t\t});\n\t}\n\treturn effective;\n}\n\n// ---------------------------------------------------------------------------\n// Matcher compilation\n// ---------------------------------------------------------------------------\n\ninterface CompiledMatcher {\n\tmatch(url: URL): Record<string, string> | null;\n}\n\nfunction compileMatcher(matcher: WebRouteMatcher): CompiledMatcher {\n\tif (typeof matcher === \"function\") {\n\t\treturn {\n\t\t\tmatch: (url) => matcher(url) ?? null,\n\t\t};\n\t}\n\tif (matcher instanceof RegExp) {\n\t\treturn {\n\t\t\tmatch: (url) => {\n\t\t\t\tconst result = matcher.exec(url.pathname);\n\t\t\t\tif (!result) return null;\n\t\t\t\treturn { ...(result.groups ?? {}) };\n\t\t\t},\n\t\t};\n\t}\n\t// String path with \":param\" placeholders, case-insensitive, supports\n\t// a trailing \"*\" wildcard segment.\n\tconst paramNames: string[] = [];\n\tlet wildcardName: string | null = null;\n\tconst segments = matcher.split(\"/\");\n\tconst parts = segments.map((segment) => {\n\t\tif (segment.startsWith(\":\")) {\n\t\t\tparamNames.push(segment.slice(1));\n\t\t\treturn \"([^/]+)\";\n\t\t}\n\t\tif (segment === \"*\") {\n\t\t\twildcardName = \"wildcard\";\n\t\t\treturn \"(.*)\";\n\t\t}\n\t\tif (segment.startsWith(\"*\")) {\n\t\t\twildcardName = segment.slice(1);\n\t\t\treturn \"(.*)\";\n\t\t}\n\t\treturn segment.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\t});\n\tconst pattern = new RegExp(`^${parts.join(\"/\")}$`);\n\treturn {\n\t\tmatch: (url) => {\n\t\t\tconst result = pattern.exec(url.pathname);\n\t\t\tif (!result) return null;\n\t\t\tconst params: Record<string, string> = {};\n\t\t\tfor (let i = 0; i < paramNames.length; i++) {\n\t\t\t\tparams[paramNames[i]] = decodeURIComponent(result[i + 1] ?? \"\");\n\t\t\t}\n\t\t\tif (wildcardName) {\n\t\t\t\tparams[wildcardName] = result[result.length - 1] ?? \"\";\n\t\t\t}\n\t\t\treturn params;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Route tree flattening\n// ---------------------------------------------------------------------------\n\ninterface FlatRoute<TData> {\n\tleaf: WebRouteDefinition<TData>;\n\tchain: readonly WebRouteDefinition<TData>[];\n\tcompiled: CompiledMatcher | null;\n}\n\nfunction flattenRoutes<TData>(\n\troutes: readonly WebRouteDefinition<TData>[],\n\tparentChain: readonly WebRouteDefinition<TData>[] = [],\n): FlatRoute<TData>[] {\n\tconst result: FlatRoute<TData>[] = [];\n\tfor (const route of routes) {\n\t\tconst chain = [...parentChain, route];\n\t\tconst hasChildren = !!route.children && route.children.length > 0;\n\t\tif (hasChildren) {\n\t\t\tresult.push(...flattenRoutes(route.children ?? [], chain));\n\t\t\t// A route with children is treated as a non-matchable container\n\t\t\t// by default — the child routes own URL matching. Adopters who\n\t\t\t// want a parent to also participate in matching should declare\n\t\t\t// it via `children`-less sibling route with an explicit\n\t\t\t// `composition: \"inherit\"` if requirement inheritance is needed.\n\t\t\tcontinue;\n\t\t}\n\t\tconst compiled = route.match ? compileMatcher(route.match) : null;\n\t\tresult.push({ leaf: route, chain, compiled });\n\t}\n\treturn result;\n}\n\n// ---------------------------------------------------------------------------\n// Router\n// ---------------------------------------------------------------------------\n\nexport interface CreateWebRouterOptions<TData = unknown> {\n\t/**\n\t * Initial routes. Nested `children` are supported: the router\n\t * flattens the declared tree into matchable leaves and reconstructs\n\t * the root→leaf chain on every match for full-route aggregation.\n\t *\n\t * More routes can be added via `router.addRoute(...)` at runtime.\n\t */\n\troutes?: readonly WebRouteDefinition<TData>[];\n\t/** Planner-host for evaluating aggregated route requirements. */\n\tplannerHost?: PlannerHost;\n\t/**\n\t * Options for the underlying navigation adapter. When omitted the\n\t * router auto-selects the Navigation API if available.\n\t */\n\tnavigationAdapter?: CreateNavigationAdapterOptions | NavigationAdapter;\n\t/**\n\t * Called after every committed navigation. Prefer `onNavigate` for\n\t * reactivity; this option exists for convenience.\n\t */\n\tonNavigate?: (commit: NavigationCommit) => void;\n\t/**\n\t * Default requirement composition strategy applied when a route\n\t * segment does not declare its own `composition`. Defaults to `merge`\n\t * (the Angular / TanStack default).\n\t */\n\tdefaultComposition?: RequirementsClientSetCompositionType;\n}\n\nexport interface WebRouter<TData = unknown> {\n\treadonly adapter: NavigationAdapter;\n\t/** Current URL (from the underlying adapter). */\n\tcurrentUrl(): URL;\n\t/** Current matched route (or null if no route matches). */\n\tcurrentMatch(): WebRouteMatch<TData> | null;\n\t/** Register an additional root-level route at runtime. */\n\taddRoute(route: WebRouteDefinition<TData>): () => void;\n\t/** All currently registered root-level routes. */\n\troutes(): readonly WebRouteDefinition<TData>[];\n\t/**\n\t * Match a URL against the registered routes. Returns the first match in\n\t * registration order, or `null` if none match. When a match is found,\n\t * {@link WebRouteMatch.chain} contains the root→leaf route chain so\n\t * adopters can feed it to {@link extractFullRouteRequirements}.\n\t */\n\tmatch(url: URL): WebRouteMatch<TData> | null;\n\t/**\n\t * Compute the effective aggregated requirements for a given match,\n\t * honouring every segment's composition strategy.\n\t */\n\textractRequirements(match: WebRouteMatch<TData>): AuthGuardClientOption[];\n\t/** Programmatically navigate. */\n\tnavigate(\n\t\turl: string | URL,\n\t\toptions?: { replace?: boolean; state?: unknown },\n\t): Promise<void>;\n\tback(): void;\n\tforward(): void;\n\t/** Subscribe to committed navigations. */\n\tonNavigate(listener: (commit: NavigationCommit) => void): () => void;\n\t/** Tear down event listeners on the underlying adapter. */\n\tdestroy(): void;\n}\n\n/**\n * Create a {@link WebRouter} wired to the selected navigation backend and\n * optionally to a planner-host for aggregated route requirements evaluation.\n *\n * The router listens for pre-commit navigation intents, matches the\n * destination URL against the registered route tree, aggregates the full\n * root→leaf chain's requirements (respecting each segment's composition\n * strategy), and asks `plannerHost.evaluate()` to rule on the complete\n * candidate set. Depending on the host's decision it either:\n *\n * - lets the navigation commit (all requirements authenticated);\n * - redirects to the URL returned by `onUnauthenticated` (when a candidate\n * picks a redirect); or\n * - cancels the navigation (when `onUnauthenticated` returns `false`).\n */\nexport function createWebRouter<TData = unknown>(\n\toptions: CreateWebRouterOptions<TData> = {},\n): WebRouter<TData> {\n\tconst adapter: NavigationAdapter =\n\t\toptions.navigationAdapter && \"kind\" in options.navigationAdapter\n\t\t\t? (options.navigationAdapter as NavigationAdapter)\n\t\t\t: createNavigationAdapter(\n\t\t\t\t\t(options.navigationAdapter as CreateNavigationAdapterOptions) ?? {},\n\t\t\t\t);\n\n\tconst roots: WebRouteDefinition<TData>[] = [...(options.routes ?? [])];\n\tlet flat: FlatRoute<TData>[] = flattenRoutes(roots);\n\n\tconst rebuild = () => {\n\t\tflat = flattenRoutes(roots);\n\t};\n\n\tconst matchUrl = (url: URL): WebRouteMatch<TData> | null => {\n\t\tfor (const entry of flat) {\n\t\t\tif (!entry.compiled) continue;\n\t\t\tconst params = entry.compiled.match(url);\n\t\t\tif (params) {\n\t\t\t\treturn {\n\t\t\t\t\troute: entry.leaf,\n\t\t\t\t\turl,\n\t\t\t\t\tparams,\n\t\t\t\t\tchain: entry.chain,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst extractRequirements = (\n\t\tmatch: WebRouteMatch<TData>,\n\t): AuthGuardClientOption[] =>\n\t\textractFullRouteRequirements(match.chain, {\n\t\t\tdefaultComposition: options.defaultComposition,\n\t\t});\n\n\tlet lastMatch: WebRouteMatch<TData> | null = matchUrl(adapter.currentUrl());\n\n\tconst beforeUnsub = adapter.onBeforeNavigate(async (intent) => {\n\t\tconst match = matchUrl(intent.url);\n\t\tif (!match || !options.plannerHost) return;\n\t\tconst aggregated = extractRequirements(match);\n\t\tif (aggregated.length === 0) return;\n\t\tconst result = await options.plannerHost.evaluate(aggregated);\n\t\tif (result.allAuthenticated) return;\n\t\tconst pending = result.pendingCandidate;\n\t\tif (!pending) return;\n\t\tconst outcome = await pending.onUnauthenticated();\n\t\tif (outcome === true) return;\n\t\tif (outcome === false) {\n\t\t\tintent.preventDefault();\n\t\t\treturn;\n\t\t}\n\t\tif (typeof outcome === \"string\") {\n\t\t\tintent.redirect(outcome);\n\t\t}\n\t});\n\n\tconst navUnsub = adapter.onNavigate((commit) => {\n\t\tlastMatch = matchUrl(commit.url);\n\t\toptions.onNavigate?.(commit);\n\t});\n\n\treturn {\n\t\tadapter,\n\t\tcurrentUrl: () => adapter.currentUrl(),\n\t\tcurrentMatch: () => lastMatch,\n\t\troutes: () => roots.slice(),\n\t\taddRoute: (route) => {\n\t\t\troots.push(route);\n\t\t\trebuild();\n\t\t\treturn () => {\n\t\t\t\tconst idx = roots.indexOf(route);\n\t\t\t\tif (idx >= 0) {\n\t\t\t\t\troots.splice(idx, 1);\n\t\t\t\t\trebuild();\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tmatch: matchUrl,\n\t\textractRequirements,\n\t\tnavigate: (url, navOptions) => adapter.navigate(url, navOptions),\n\t\tback: () => adapter.back(),\n\t\tforward: () => adapter.forward(),\n\t\tonNavigate: (listener) => adapter.onNavigate(listener),\n\t\tdestroy: () => {\n\t\t\tbeforeUnsub();\n\t\t\tnavUnsub();\n\t\t\tadapter.destroy();\n\t\t},\n\t};\n}\n\n// Re-export navigation adapter surface for convenience.\nexport { NavigationAdapterKind, RequirementsClientSetComposition };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/web-router/navigation-adapter.ts","../../src/web-router/web-router.ts"],"sourcesContent":["// Navigation adapter — abstraction over Web Navigation API / History API\n//\n// Canonical subpath: @securitydept/client/web-router\n//\n// The Web Navigation API (window.navigation) is the modern, cleaner\n// primitive for client-side routing (pre-commit intercept, typed intents,\n// rollback support). Where available (Chromium ≥ 102), we prefer it.\n// Otherwise we fall back to the classic History API + popstate + link\n// click capture, which every browser still supports.\n//\n// Both adapters implement the same `NavigationAdapter` contract so the\n// higher-level `createWebRouter` factory does not need to branch on which\n// backend is active — tests can also target either path deterministically.\n//\n// Stability: provisional\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\nexport const NavigationAdapterKind = {\n\tNavigationApi: \"navigation-api\",\n\tHistory: \"history\",\n} as const;\nexport type NavigationAdapterKind =\n\t(typeof NavigationAdapterKind)[keyof typeof NavigationAdapterKind];\n\nexport const NavigationCause = {\n\tInitial: \"initial\",\n\tPush: \"push\",\n\tReplace: \"replace\",\n\tTraverse: \"traverse\",\n} as const;\nexport type NavigationCause =\n\t(typeof NavigationCause)[keyof typeof NavigationCause];\n\nexport interface NavigationIntent {\n\treadonly url: URL;\n\treadonly cause: NavigationCause;\n\treadonly replace: boolean;\n\treadonly state: unknown;\n\t/** Abort this navigation before it commits. */\n\tpreventDefault(): void;\n\t/** Cancel the current intent and navigate to the given URL instead. */\n\tredirect(url: string | URL, options?: { replace?: boolean }): void;\n}\n\nexport interface NavigationCommit {\n\treadonly url: URL;\n}\n\nexport interface NavigateOptions {\n\t/** When true, replace the current history entry instead of pushing. */\n\treplace?: boolean;\n\t/** Serializable state to attach to the history entry. */\n\tstate?: unknown;\n\t/**\n\t * Arbitrary per-navigation metadata. Passed through to `NavigateEvent.info`\n\t * when the Navigation API adapter is active; ignored by the History\n\t * adapter (no wire support).\n\t */\n\tinfo?: unknown;\n}\n\nexport interface NavigationAdapter {\n\treadonly kind: NavigationAdapterKind;\n\tcurrentUrl(): URL;\n\tnavigate(url: string | URL, options?: NavigateOptions): Promise<void>;\n\tback(): void;\n\tforward(): void;\n\t/**\n\t * Called *before* a navigation commits, so listeners can cancel or\n\t * redirect. Both adapters forward synchronous and asynchronous handlers\n\t * (adapters await the intent being fully processed before committing).\n\t */\n\tonBeforeNavigate(\n\t\tlistener: (intent: NavigationIntent) => void | Promise<void>,\n\t): () => void;\n\t/** Called *after* a navigation commits. */\n\tonNavigate(listener: (commit: NavigationCommit) => void): () => void;\n\tdestroy(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Navigation API typings (reduced; TS DOM libs cover this but the types are\n// still experimental in some TypeScript versions, so we mirror the minimal\n// surface we consume here).\n// ---------------------------------------------------------------------------\n\ninterface NavigationApiNavigation {\n\treadonly currentEntry: { getState(): unknown } | null;\n\tnavigate(\n\t\turl: string,\n\t\toptions?: { history?: \"push\" | \"replace\"; state?: unknown; info?: unknown },\n\t): { committed: Promise<unknown>; finished: Promise<unknown> };\n\tback(): { committed: Promise<unknown> };\n\tforward(): { committed: Promise<unknown> };\n\taddEventListener(\n\t\ttype: \"navigate\",\n\t\tlistener: (event: NavigationApiNavigateEvent) => void,\n\t): void;\n\taddEventListener(\n\t\ttype: \"navigatesuccess\",\n\t\tlistener: (event: Event) => void,\n\t): void;\n\tremoveEventListener(\n\t\ttype: \"navigate\",\n\t\tlistener: (event: NavigationApiNavigateEvent) => void,\n\t): void;\n\tremoveEventListener(\n\t\ttype: \"navigatesuccess\",\n\t\tlistener: (event: Event) => void,\n\t): void;\n}\n\ninterface NavigationApiNavigateEvent extends Event {\n\treadonly destination: { url: string; getState(): unknown };\n\treadonly navigationType: \"push\" | \"replace\" | \"reload\" | \"traverse\";\n\treadonly canIntercept: boolean;\n\treadonly userInitiated: boolean;\n\tintercept(options?: { handler?: () => Promise<void> | void }): void;\n\tredirect?(url: string, options?: { history?: \"push\" | \"replace\" }): void;\n}\n\nfunction navigationApi(): NavigationApiNavigation | undefined {\n\treturn (globalThis as { navigation?: NavigationApiNavigation }).navigation;\n}\n\n// ---------------------------------------------------------------------------\n// Navigation API adapter\n// ---------------------------------------------------------------------------\n\nexport function createNavigationApiAdapter(): NavigationAdapter {\n\tconst nav = navigationApi();\n\tif (!nav) {\n\t\tthrow new Error(\n\t\t\t\"[createNavigationApiAdapter] window.navigation is not available in this environment.\",\n\t\t);\n\t}\n\n\tconst beforeListeners = new Set<\n\t\t(intent: NavigationIntent) => void | Promise<void>\n\t>();\n\tconst commitListeners = new Set<(commit: NavigationCommit) => void>();\n\n\tconst handleNavigate = (event: NavigationApiNavigateEvent) => {\n\t\tif (beforeListeners.size === 0 && commitListeners.size === 0) return;\n\t\tif (!event.canIntercept) return;\n\n\t\tconst url = new URL(event.destination.url, location.href);\n\t\tconst cause: NavigationCause =\n\t\t\tevent.navigationType === \"push\"\n\t\t\t\t? NavigationCause.Push\n\t\t\t\t: event.navigationType === \"replace\"\n\t\t\t\t\t? NavigationCause.Replace\n\t\t\t\t\t: NavigationCause.Traverse;\n\n\t\tlet prevented = false;\n\t\tlet redirectTarget: { url: URL; replace: boolean } | null = null;\n\n\t\tconst intent: NavigationIntent = {\n\t\t\turl,\n\t\t\tcause,\n\t\t\treplace: event.navigationType === \"replace\",\n\t\t\tstate: event.destination.getState(),\n\t\t\tpreventDefault: () => {\n\t\t\t\tprevented = true;\n\t\t\t\t// There is no native preventDefault on NavigateEvent for\n\t\t\t\t// cancellation outside of `intercept({ handler })`, so we\n\t\t\t\t// throw inside the handler to abort the committed promise.\n\t\t\t},\n\t\t\tredirect: (target, options) => {\n\t\t\t\tredirectTarget = {\n\t\t\t\t\turl: new URL(target.toString(), url),\n\t\t\t\t\treplace: options?.replace ?? false,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\n\t\tevent.intercept({\n\t\t\thandler: async () => {\n\t\t\t\tfor (const listener of beforeListeners) {\n\t\t\t\t\tawait listener(intent);\n\t\t\t\t\tif (prevented || redirectTarget) break;\n\t\t\t\t}\n\t\t\t\tif (prevented && !redirectTarget) {\n\t\t\t\t\tthrow new DOMException(\n\t\t\t\t\t\t\"Navigation prevented by interceptor\",\n\t\t\t\t\t\t\"AbortError\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (redirectTarget) {\n\t\t\t\t\tconst target = redirectTarget as { url: URL; replace: boolean };\n\t\t\t\t\tnav.navigate(target.url.toString(), {\n\t\t\t\t\t\thistory: target.replace ? \"replace\" : \"push\",\n\t\t\t\t\t});\n\t\t\t\t\tthrow new DOMException(\n\t\t\t\t\t\t\"Navigation redirected by interceptor\",\n\t\t\t\t\t\t\"AbortError\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tfor (const listener of commitListeners) listener({ url });\n\t\t\t},\n\t\t});\n\t};\n\n\tconst handleSuccess = (_event: Event) => {\n\t\t// Navigation API fires `navigatesuccess` even when no handler\n\t\t// intercepted. We still want to notify commit listeners for\n\t\t// such cases (typed external navigations).\n\t\tconst url = new URL(location.href);\n\t\tfor (const listener of commitListeners) listener({ url });\n\t};\n\n\tnav.addEventListener(\"navigate\", handleNavigate);\n\tnav.addEventListener(\"navigatesuccess\", handleSuccess);\n\n\treturn {\n\t\tkind: NavigationAdapterKind.NavigationApi,\n\t\tcurrentUrl: () => new URL(location.href),\n\t\tnavigate: async (url, options) => {\n\t\t\tconst target = new URL(url.toString(), location.href);\n\t\t\tconst result = nav.navigate(target.toString(), {\n\t\t\t\thistory: options?.replace ? \"replace\" : \"push\",\n\t\t\t\tstate: options?.state,\n\t\t\t\tinfo: options?.info,\n\t\t\t});\n\t\t\tawait result.committed.catch(() => undefined);\n\t\t},\n\t\tback: () => {\n\t\t\tnav.back();\n\t\t},\n\t\tforward: () => {\n\t\t\tnav.forward();\n\t\t},\n\t\tonBeforeNavigate: (listener) => {\n\t\t\tbeforeListeners.add(listener);\n\t\t\treturn () => beforeListeners.delete(listener);\n\t\t},\n\t\tonNavigate: (listener) => {\n\t\t\tcommitListeners.add(listener);\n\t\t\treturn () => commitListeners.delete(listener);\n\t\t},\n\t\tdestroy: () => {\n\t\t\tnav.removeEventListener(\"navigate\", handleNavigate);\n\t\t\tnav.removeEventListener(\"navigatesuccess\", handleSuccess);\n\t\t\tbeforeListeners.clear();\n\t\t\tcommitListeners.clear();\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// History API fallback adapter\n// ---------------------------------------------------------------------------\n\nexport interface CreateHistoryAdapterOptions {\n\t/**\n\t * When true (default), the adapter captures clicks on in-document\n\t * `<a href>` elements and routes them through `navigate()` so guards\n\t * can intercept. Disable to handle anchor navigation manually.\n\t */\n\tcaptureAnchorClicks?: boolean;\n}\n\nexport function createHistoryAdapter(\n\toptions: CreateHistoryAdapterOptions = {},\n): NavigationAdapter {\n\tconst captureAnchorClicks = options.captureAnchorClicks ?? true;\n\n\tconst beforeListeners = new Set<\n\t\t(intent: NavigationIntent) => void | Promise<void>\n\t>();\n\tconst commitListeners = new Set<(commit: NavigationCommit) => void>();\n\n\tconst dispatchBefore = async (intent: NavigationIntent): Promise<boolean> => {\n\t\tfor (const listener of beforeListeners) {\n\t\t\tawait listener(intent);\n\t\t}\n\t\treturn true;\n\t};\n\n\tconst commit = (url: URL) => {\n\t\tfor (const listener of commitListeners) listener({ url });\n\t};\n\n\tconst handlePopstate = async (event: PopStateEvent) => {\n\t\tconst url = new URL(location.href);\n\t\tlet prevented = false;\n\t\tlet redirectTarget: { url: URL; replace: boolean } | null = null;\n\t\tconst intent: NavigationIntent = {\n\t\t\turl,\n\t\t\tcause: NavigationCause.Traverse,\n\t\t\treplace: false,\n\t\t\tstate: event.state,\n\t\t\tpreventDefault: () => {\n\t\t\t\tprevented = true;\n\t\t\t},\n\t\t\tredirect: (target, opts) => {\n\t\t\t\tredirectTarget = {\n\t\t\t\t\turl: new URL(target.toString(), url),\n\t\t\t\t\treplace: opts?.replace ?? false,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t\tawait dispatchBefore(intent);\n\t\tif (prevented && !redirectTarget) {\n\t\t\t// History API has no native way to cancel a popstate; the\n\t\t\t// best we can do is navigate forward to the previous URL.\n\t\t\t// Adopters that need true cancellation should prefer the\n\t\t\t// Navigation API path or use `intercept` for *pre*-action\n\t\t\t// decisions (e.g. chooser dialogs) rather than cancellation.\n\t\t\treturn;\n\t\t}\n\t\tif (redirectTarget) {\n\t\t\tconst target = redirectTarget as { url: URL; replace: boolean };\n\t\t\tif (target.replace) {\n\t\t\t\thistory.replaceState(null, \"\", target.url.toString());\n\t\t\t} else {\n\t\t\t\thistory.pushState(null, \"\", target.url.toString());\n\t\t\t}\n\t\t\tcommit(target.url);\n\t\t\treturn;\n\t\t}\n\t\tcommit(url);\n\t};\n\n\tconst handleAnchorClick = async (event: MouseEvent) => {\n\t\tif (\n\t\t\tevent.defaultPrevented ||\n\t\t\tevent.button !== 0 ||\n\t\t\tevent.metaKey ||\n\t\t\tevent.ctrlKey ||\n\t\t\tevent.shiftKey ||\n\t\t\tevent.altKey\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst path = event.composedPath();\n\t\tconst anchor = path.find(\n\t\t\t(node): node is HTMLAnchorElement => node instanceof HTMLAnchorElement,\n\t\t);\n\t\tif (!anchor?.href || anchor.target === \"_blank\") return;\n\t\tconst targetUrl = new URL(anchor.href);\n\t\tif (targetUrl.origin !== location.origin) return;\n\t\tevent.preventDefault();\n\t\tawait navigateInternal(targetUrl, { replace: false });\n\t};\n\n\tconst navigateInternal = async (\n\t\turl: URL,\n\t\toptions: NavigateOptions,\n\t): Promise<void> => {\n\t\tlet prevented = false;\n\t\tlet redirectTarget: { url: URL; replace: boolean } | null = null;\n\t\tconst intent: NavigationIntent = {\n\t\t\turl,\n\t\t\tcause: options.replace ? NavigationCause.Replace : NavigationCause.Push,\n\t\t\treplace: options.replace ?? false,\n\t\t\tstate: options.state,\n\t\t\tpreventDefault: () => {\n\t\t\t\tprevented = true;\n\t\t\t},\n\t\t\tredirect: (target, opts) => {\n\t\t\t\tredirectTarget = {\n\t\t\t\t\turl: new URL(target.toString(), url),\n\t\t\t\t\treplace: opts?.replace ?? false,\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t\tawait dispatchBefore(intent);\n\t\tif (prevented && !redirectTarget) return;\n\t\tconst target = redirectTarget\n\t\t\t? (redirectTarget as { url: URL; replace: boolean })\n\t\t\t: { url, replace: options.replace ?? false };\n\t\tif (target.replace) {\n\t\t\thistory.replaceState(options.state ?? null, \"\", target.url.toString());\n\t\t} else {\n\t\t\thistory.pushState(options.state ?? null, \"\", target.url.toString());\n\t\t}\n\t\tcommit(target.url);\n\t};\n\n\tif (typeof window !== \"undefined\") {\n\t\twindow.addEventListener(\"popstate\", handlePopstate);\n\t\tif (captureAnchorClicks) {\n\t\t\tdocument.addEventListener(\"click\", handleAnchorClick);\n\t\t}\n\t}\n\n\treturn {\n\t\tkind: NavigationAdapterKind.History,\n\t\tcurrentUrl: () => new URL(location.href),\n\t\tnavigate: (url, navOptions) =>\n\t\t\tnavigateInternal(\n\t\t\t\tnew URL(url.toString(), location.href),\n\t\t\t\tnavOptions ?? {},\n\t\t\t),\n\t\tback: () => {\n\t\t\thistory.back();\n\t\t},\n\t\tforward: () => {\n\t\t\thistory.forward();\n\t\t},\n\t\tonBeforeNavigate: (listener) => {\n\t\t\tbeforeListeners.add(listener);\n\t\t\treturn () => beforeListeners.delete(listener);\n\t\t},\n\t\tonNavigate: (listener) => {\n\t\t\tcommitListeners.add(listener);\n\t\t\treturn () => commitListeners.delete(listener);\n\t\t},\n\t\tdestroy: () => {\n\t\t\tif (typeof window !== \"undefined\") {\n\t\t\t\twindow.removeEventListener(\"popstate\", handlePopstate);\n\t\t\t\tif (captureAnchorClicks) {\n\t\t\t\t\tdocument.removeEventListener(\"click\", handleAnchorClick);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeforeListeners.clear();\n\t\t\tcommitListeners.clear();\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Auto-select adapter\n// ---------------------------------------------------------------------------\n\nexport interface CreateNavigationAdapterOptions {\n\t/**\n\t * Force a specific adapter kind. Useful for tests that need to exercise\n\t * the fallback path in a Chromium environment that ships the Navigation\n\t * API.\n\t * @default \"auto\"\n\t */\n\tprefer?: \"auto\" | NavigationAdapterKind;\n\t/**\n\t * Options forwarded to {@link createHistoryAdapter} when the history\n\t * adapter is selected.\n\t */\n\thistory?: CreateHistoryAdapterOptions;\n}\n\n/**\n * Pick the best available navigation backend. Prefers the Web Navigation\n * API when `window.navigation` is defined; falls back to the History API\n * otherwise.\n */\nexport function createNavigationAdapter(\n\toptions: CreateNavigationAdapterOptions = {},\n): NavigationAdapter {\n\tconst prefer = options.prefer ?? \"auto\";\n\tif (prefer === NavigationAdapterKind.History) {\n\t\treturn createHistoryAdapter(options.history);\n\t}\n\tif (prefer === NavigationAdapterKind.NavigationApi) {\n\t\treturn createNavigationApiAdapter();\n\t}\n\tif (navigationApi()) {\n\t\treturn createNavigationApiAdapter();\n\t}\n\treturn createHistoryAdapter(options.history);\n}\n\n/**\n * Quick capability probe — true when the Web Navigation API is present.\n */\nexport function isNavigationApiAvailable(): boolean {\n\treturn navigationApi() !== undefined;\n}\n","// @securitydept/client/web-router — framework-free web router baseline\n//\n// Provides a thin router layer built on `NavigationAdapter` (Navigation API\n// or History API fallback) with planner-host integration for declarative,\n// auth-aware route guards.\n//\n// The router supports a nested route tree\n// with full-route requirement aggregation (inherit / merge / replace\n// composition) so that parent + child + sibling security declarations are\n// combined into a single candidate set before the planner-host runs — the\n// same contract depth offered by the Angular adapter and the TanStack\n// Router adapter. Flat declarations remain supported: a route without\n// `children` behaves exactly as before.\n//\n// Stability: provisional\n\nimport type {\n\tAuthGuardClientOption,\n\tPlannerHost,\n\tRequirementsClientSetComposition as RequirementsClientSetCompositionType,\n} from \"../auth-coordination/planner-host\";\nimport {\n\tRequirementsClientSetComposition,\n\tresolveEffectiveClientSet,\n} from \"../auth-coordination/planner-host\";\nimport {\n\ttype CreateNavigationAdapterOptions,\n\tcreateNavigationAdapter,\n\ttype NavigationAdapter,\n\tNavigationAdapterKind,\n\ttype NavigationCommit,\n\ttype NavigationIntent,\n} from \"./navigation-adapter\";\n\n// ---------------------------------------------------------------------------\n// Route definition\n// ---------------------------------------------------------------------------\n\nexport interface WebRouteContext {\n\t/** The URL being navigated to. */\n\turl: URL;\n\t/** Intent metadata. Null for programmatic `activate()` calls. */\n\tintent: NavigationIntent | null;\n}\n\nexport type WebRouteMatcher =\n\t| string\n\t| RegExp\n\t| ((url: URL) => Record<string, string> | null | undefined);\n\nexport interface WebRouteDefinition<TData = unknown> {\n\t/** Opaque identifier. */\n\tid: string;\n\t/**\n\t * Matcher for this route segment. Required on leaves — parents that\n\t * only carry metadata for children may omit `match`.\n\t *\n\t * A string is treated as a path-with-params pattern\n\t * (`\"/users/:id/profile\"` → `{ id: \"123\" }` for `/users/123/profile`).\n\t * A RegExp uses `exec(pathname)` and passes named/indexed groups\n\t * through as params. A function returns the parsed params or null.\n\t */\n\tmatch?: WebRouteMatcher;\n\t/**\n\t * Auth requirements declared at this route segment. The router walks\n\t * the matched route chain from root to leaf and aggregates every\n\t * segment's requirements via {@link extractFullRouteRequirements}\n\t * before asking the planner-host to evaluate.\n\t */\n\trequirements?: readonly AuthGuardClientOption[];\n\t/**\n\t * Composition strategy with the parent segment in the matched chain.\n\t *\n\t * - `merge` (default): parent requirements are kept, child requirements\n\t * append/override by `requirementId`.\n\t * - `inherit`: child declares no additional requirements; parent set is\n\t * used as-is.\n\t * - `replace`: parent requirements are discarded entirely; this child\n\t * contributes the full effective set (use for \"public\" subtrees of an\n\t * otherwise protected parent).\n\t */\n\tcomposition?: RequirementsClientSetCompositionType;\n\t/**\n\t * Nested child routes. When present, `match` on the parent is optional\n\t * and is not consulted for matching leaves — only the leaves' matchers\n\t * participate in URL resolution. The chain from root to leaf is what\n\t * drives requirement aggregation.\n\t */\n\tchildren?: readonly WebRouteDefinition<TData>[];\n\t/** Arbitrary data attached to matched routes. */\n\tdata?: TData;\n}\n\nexport interface WebRouteMatch<TData = unknown> {\n\t/** The leaf route that matched. */\n\treadonly route: WebRouteDefinition<TData>;\n\treadonly url: URL;\n\treadonly params: Record<string, string>;\n\t/**\n\t * The complete chain from root ancestor → leaf. A flat route without\n\t * children produces a single-element chain.\n\t */\n\treadonly chain: readonly WebRouteDefinition<TData>[];\n}\n\n// ---------------------------------------------------------------------------\n// Declaration helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Sugar for declaring a route segment. Enforces a consistent shape so\n * adopter code reads like the Angular / TanStack helpers without forcing\n * any particular DI or decorator style.\n */\nexport function defineWebRoute<TData = unknown>(\n\tdefinition: WebRouteDefinition<TData>,\n): WebRouteDefinition<TData> {\n\treturn definition;\n}\n\nexport interface FullRouteRequirementsOptions {\n\t/**\n\t * Default composition for segments that do not set their own\n\t * `composition` field. Defaults to `merge` to match the Angular helper\n\t * and the TanStack adapter.\n\t */\n\tdefaultComposition?: RequirementsClientSetCompositionType;\n}\n\n/**\n * Aggregate every segment's `requirements` across the matched chain into\n * a single {@link AuthGuardClientOption} list, honouring each segment's\n * composition strategy (`inherit` / `merge` / `replace`).\n *\n * This is the extraction counter-part to the nested `WebRouteDefinition`\n * tree — the router calls it once per matched navigation and forwards the\n * resulting flat list to the planner-host.\n */\nexport function extractFullRouteRequirements<TData = unknown>(\n\tchain: readonly WebRouteDefinition<TData>[],\n\toptions: FullRouteRequirementsOptions = {},\n): AuthGuardClientOption[] {\n\tconst defaultComposition =\n\t\toptions.defaultComposition ?? RequirementsClientSetComposition.Merge;\n\tlet effective: AuthGuardClientOption[] = [];\n\tfor (const segment of chain) {\n\t\tconst composition = segment.composition ?? defaultComposition;\n\t\tconst options = segment.requirements ?? [];\n\t\teffective = resolveEffectiveClientSet(effective, {\n\t\t\tcomposition,\n\t\t\toptions,\n\t\t});\n\t}\n\treturn effective;\n}\n\n// ---------------------------------------------------------------------------\n// Matcher compilation\n// ---------------------------------------------------------------------------\n\ninterface CompiledMatcher {\n\tmatch(url: URL): Record<string, string> | null;\n}\n\nfunction compileMatcher(matcher: WebRouteMatcher): CompiledMatcher {\n\tif (typeof matcher === \"function\") {\n\t\treturn {\n\t\t\tmatch: (url) => matcher(url) ?? null,\n\t\t};\n\t}\n\tif (matcher instanceof RegExp) {\n\t\treturn {\n\t\t\tmatch: (url) => {\n\t\t\t\tconst result = matcher.exec(url.pathname);\n\t\t\t\tif (!result) return null;\n\t\t\t\treturn { ...(result.groups ?? {}) };\n\t\t\t},\n\t\t};\n\t}\n\t// String path with \":param\" placeholders, case-insensitive, supports\n\t// a trailing \"*\" wildcard segment.\n\tconst paramNames: string[] = [];\n\tlet wildcardName: string | null = null;\n\tconst segments = matcher.split(\"/\");\n\tconst parts = segments.map((segment) => {\n\t\tif (segment.startsWith(\":\")) {\n\t\t\tparamNames.push(segment.slice(1));\n\t\t\treturn \"([^/]+)\";\n\t\t}\n\t\tif (segment === \"*\") {\n\t\t\twildcardName = \"wildcard\";\n\t\t\treturn \"(.*)\";\n\t\t}\n\t\tif (segment.startsWith(\"*\")) {\n\t\t\twildcardName = segment.slice(1);\n\t\t\treturn \"(.*)\";\n\t\t}\n\t\treturn segment.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\t});\n\tconst pattern = new RegExp(`^${parts.join(\"/\")}$`);\n\treturn {\n\t\tmatch: (url) => {\n\t\t\tconst result = pattern.exec(url.pathname);\n\t\t\tif (!result) return null;\n\t\t\tconst params: Record<string, string> = {};\n\t\t\tfor (let i = 0; i < paramNames.length; i++) {\n\t\t\t\tparams[paramNames[i]] = decodeURIComponent(result[i + 1] ?? \"\");\n\t\t\t}\n\t\t\tif (wildcardName) {\n\t\t\t\tparams[wildcardName] = result[result.length - 1] ?? \"\";\n\t\t\t}\n\t\t\treturn params;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Route tree flattening\n// ---------------------------------------------------------------------------\n\ninterface FlatRoute<TData> {\n\tleaf: WebRouteDefinition<TData>;\n\tchain: readonly WebRouteDefinition<TData>[];\n\tcompiled: CompiledMatcher | null;\n}\n\nfunction flattenRoutes<TData>(\n\troutes: readonly WebRouteDefinition<TData>[],\n\tparentChain: readonly WebRouteDefinition<TData>[] = [],\n): FlatRoute<TData>[] {\n\tconst result: FlatRoute<TData>[] = [];\n\tfor (const route of routes) {\n\t\tconst chain = [...parentChain, route];\n\t\tconst hasChildren = !!route.children && route.children.length > 0;\n\t\tif (hasChildren) {\n\t\t\tresult.push(...flattenRoutes(route.children ?? [], chain));\n\t\t\t// A route with children is treated as a non-matchable container\n\t\t\t// by default — the child routes own URL matching. Adopters who\n\t\t\t// want a parent to also participate in matching should declare\n\t\t\t// it via `children`-less sibling route with an explicit\n\t\t\t// `composition: \"inherit\"` if requirement inheritance is needed.\n\t\t\tcontinue;\n\t\t}\n\t\tconst compiled = route.match ? compileMatcher(route.match) : null;\n\t\tresult.push({ leaf: route, chain, compiled });\n\t}\n\treturn result;\n}\n\n// ---------------------------------------------------------------------------\n// Router\n// ---------------------------------------------------------------------------\n\nexport interface CreateWebRouterOptions<TData = unknown> {\n\t/**\n\t * Initial routes. Nested `children` are supported: the router\n\t * flattens the declared tree into matchable leaves and reconstructs\n\t * the root→leaf chain on every match for full-route aggregation.\n\t *\n\t * More routes can be added via `router.addRoute(...)` at runtime.\n\t */\n\troutes?: readonly WebRouteDefinition<TData>[];\n\t/** Planner-host for evaluating aggregated route requirements. */\n\tplannerHost?: PlannerHost;\n\t/**\n\t * Options for the underlying navigation adapter. When omitted the\n\t * router auto-selects the Navigation API if available.\n\t */\n\tnavigationAdapter?: CreateNavigationAdapterOptions | NavigationAdapter;\n\t/**\n\t * Called after every committed navigation. Prefer `onNavigate` for\n\t * reactivity; this option exists for convenience.\n\t */\n\tonNavigate?: (commit: NavigationCommit) => void;\n\t/**\n\t * Default requirement composition strategy applied when a route\n\t * segment does not declare its own `composition`. Defaults to `merge`\n\t * (the Angular / TanStack default).\n\t */\n\tdefaultComposition?: RequirementsClientSetCompositionType;\n}\n\nexport interface WebRouter<TData = unknown> {\n\treadonly adapter: NavigationAdapter;\n\t/** Current URL (from the underlying adapter). */\n\tcurrentUrl(): URL;\n\t/** Current matched route (or null if no route matches). */\n\tcurrentMatch(): WebRouteMatch<TData> | null;\n\t/** Register an additional root-level route at runtime. */\n\taddRoute(route: WebRouteDefinition<TData>): () => void;\n\t/** All currently registered root-level routes. */\n\troutes(): readonly WebRouteDefinition<TData>[];\n\t/**\n\t * Match a URL against the registered routes. Returns the first match in\n\t * registration order, or `null` if none match. When a match is found,\n\t * {@link WebRouteMatch.chain} contains the root→leaf route chain so\n\t * adopters can feed it to {@link extractFullRouteRequirements}.\n\t */\n\tmatch(url: URL): WebRouteMatch<TData> | null;\n\t/**\n\t * Compute the effective aggregated requirements for a given match,\n\t * honouring every segment's composition strategy.\n\t */\n\textractRequirements(match: WebRouteMatch<TData>): AuthGuardClientOption[];\n\t/** Programmatically navigate. */\n\tnavigate(\n\t\turl: string | URL,\n\t\toptions?: { replace?: boolean; state?: unknown },\n\t): Promise<void>;\n\tback(): void;\n\tforward(): void;\n\t/** Subscribe to committed navigations. */\n\tonNavigate(listener: (commit: NavigationCommit) => void): () => void;\n\t/** Tear down event listeners on the underlying adapter. */\n\tdestroy(): void;\n}\n\n/**\n * Create a {@link WebRouter} wired to the selected navigation backend and\n * optionally to a planner-host for aggregated route requirements evaluation.\n *\n * The router listens for pre-commit navigation intents, matches the\n * destination URL against the registered route tree, aggregates the full\n * root→leaf chain's requirements (respecting each segment's composition\n * strategy), and asks `plannerHost.evaluate()` to rule on the complete\n * candidate set. Depending on the host's decision it either:\n *\n * - lets the navigation commit (all requirements authenticated);\n * - redirects to the URL returned by `onUnauthenticated` (when a candidate\n * picks a redirect); or\n * - cancels the navigation (when `onUnauthenticated` returns `false`).\n */\nexport function createWebRouter<TData = unknown>(\n\toptions: CreateWebRouterOptions<TData> = {},\n): WebRouter<TData> {\n\tconst adapter: NavigationAdapter =\n\t\toptions.navigationAdapter && \"kind\" in options.navigationAdapter\n\t\t\t? (options.navigationAdapter as NavigationAdapter)\n\t\t\t: createNavigationAdapter(\n\t\t\t\t\t(options.navigationAdapter as CreateNavigationAdapterOptions) ?? {},\n\t\t\t\t);\n\n\tconst roots: WebRouteDefinition<TData>[] = [...(options.routes ?? [])];\n\tlet flat: FlatRoute<TData>[] = flattenRoutes(roots);\n\n\tconst rebuild = () => {\n\t\tflat = flattenRoutes(roots);\n\t};\n\n\tconst matchUrl = (url: URL): WebRouteMatch<TData> | null => {\n\t\tfor (const entry of flat) {\n\t\t\tif (!entry.compiled) continue;\n\t\t\tconst params = entry.compiled.match(url);\n\t\t\tif (params) {\n\t\t\t\treturn {\n\t\t\t\t\troute: entry.leaf,\n\t\t\t\t\turl,\n\t\t\t\t\tparams,\n\t\t\t\t\tchain: entry.chain,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t};\n\n\tconst extractRequirements = (\n\t\tmatch: WebRouteMatch<TData>,\n\t): AuthGuardClientOption[] =>\n\t\textractFullRouteRequirements(match.chain, {\n\t\t\tdefaultComposition: options.defaultComposition,\n\t\t});\n\n\tlet lastMatch: WebRouteMatch<TData> | null = matchUrl(adapter.currentUrl());\n\n\tconst beforeUnsub = adapter.onBeforeNavigate(async (intent) => {\n\t\tconst match = matchUrl(intent.url);\n\t\tif (!match || !options.plannerHost) return;\n\t\tconst aggregated = extractRequirements(match);\n\t\tif (aggregated.length === 0) return;\n\t\tconst result = await options.plannerHost.evaluate(aggregated);\n\t\tif (result.allAuthenticated) return;\n\t\tconst pending = result.pendingCandidate;\n\t\tif (!pending) return;\n\t\tconst outcome = await pending.onUnauthenticated();\n\t\tif (outcome === true) return;\n\t\tif (outcome === false) {\n\t\t\tintent.preventDefault();\n\t\t\treturn;\n\t\t}\n\t\tif (typeof outcome === \"string\") {\n\t\t\tintent.redirect(outcome);\n\t\t}\n\t});\n\n\tconst navUnsub = adapter.onNavigate((commit) => {\n\t\tlastMatch = matchUrl(commit.url);\n\t\toptions.onNavigate?.(commit);\n\t});\n\n\treturn {\n\t\tadapter,\n\t\tcurrentUrl: () => adapter.currentUrl(),\n\t\tcurrentMatch: () => lastMatch,\n\t\troutes: () => roots.slice(),\n\t\taddRoute: (route) => {\n\t\t\troots.push(route);\n\t\t\trebuild();\n\t\t\treturn () => {\n\t\t\t\tconst idx = roots.indexOf(route);\n\t\t\t\tif (idx >= 0) {\n\t\t\t\t\troots.splice(idx, 1);\n\t\t\t\t\trebuild();\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tmatch: matchUrl,\n\t\textractRequirements,\n\t\tnavigate: (url, navOptions) => adapter.navigate(url, navOptions),\n\t\tback: () => adapter.back(),\n\t\tforward: () => adapter.forward(),\n\t\tonNavigate: (listener) => adapter.onNavigate(listener),\n\t\tdestroy: () => {\n\t\t\tbeforeUnsub();\n\t\t\tnavUnsub();\n\t\t\tadapter.destroy();\n\t\t},\n\t};\n}\n\n// Re-export navigation adapter surface for convenience.\nexport { NavigationAdapterKind, RequirementsClientSetComposition };\n"],"mappings":";;AAoBA,MAAa,wBAAwB;CACpC,eAAe;CACf,SAAS;CACT;AAID,MAAa,kBAAkB;CAC9B,SAAS;CACT,MAAM;CACN,SAAS;CACT,UAAU;CACV;AA4FD,SAAS,gBAAqD;CAC7D,OAAQ,WAAwD;;AAOjE,SAAgB,6BAAgD;CAC/D,MAAM,MAAM,eAAe;CAC3B,IAAI,CAAC,KACJ,MAAM,IAAI,MACT,uFACA;CAGF,MAAM,kCAAkB,IAAI,KAEzB;CACH,MAAM,kCAAkB,IAAI,KAAyC;CAErE,MAAM,kBAAkB,UAAsC;EAC7D,IAAI,gBAAgB,SAAS,KAAK,gBAAgB,SAAS,GAAG;EAC9D,IAAI,CAAC,MAAM,cAAc;EAEzB,MAAM,MAAM,IAAI,IAAI,MAAM,YAAY,KAAK,SAAS,KAAK;EACzD,MAAM,QACL,MAAM,mBAAmB,SACtB,gBAAgB,OAChB,MAAM,mBAAmB,YACxB,gBAAgB,UAChB,gBAAgB;EAErB,IAAI,YAAY;EAChB,IAAI,iBAAwD;EAE5D,MAAM,SAA2B;GAChC;GACA;GACA,SAAS,MAAM,mBAAmB;GAClC,OAAO,MAAM,YAAY,UAAU;GACnC,sBAAsB;IACrB,YAAY;;GAKb,WAAW,QAAQ,YAAY;IAC9B,iBAAiB;KAChB,KAAK,IAAI,IAAI,OAAO,UAAU,EAAE,IAAI;KACpC,SAAS,SAAS,WAAW;KAC7B;;GAEF;EAED,MAAM,UAAU,EACf,SAAS,YAAY;GACpB,KAAK,MAAM,YAAY,iBAAiB;IACvC,MAAM,SAAS,OAAO;IACtB,IAAI,aAAa,gBAAgB;;GAElC,IAAI,aAAa,CAAC,gBACjB,MAAM,IAAI,aACT,uCACA,aACA;GAEF,IAAI,gBAAgB;IACnB,MAAM,SAAS;IACf,IAAI,SAAS,OAAO,IAAI,UAAU,EAAE,EACnC,SAAS,OAAO,UAAU,YAAY,QACtC,CAAC;IACF,MAAM,IAAI,aACT,wCACA,aACA;;GAEF,KAAK,MAAM,YAAY,iBAAiB,SAAS,EAAE,KAAK,CAAC;KAE1D,CAAC;;CAGH,MAAM,iBAAiB,WAAkB;EAIxC,MAAM,MAAM,IAAI,IAAI,SAAS,KAAK;EAClC,KAAK,MAAM,YAAY,iBAAiB,SAAS,EAAE,KAAK,CAAC;;CAG1D,IAAI,iBAAiB,YAAY,eAAe;CAChD,IAAI,iBAAiB,mBAAmB,cAAc;CAEtD,OAAO;EACN,MAAM,sBAAsB;EAC5B,kBAAkB,IAAI,IAAI,SAAS,KAAK;EACxC,UAAU,OAAO,KAAK,YAAY;GACjC,MAAM,SAAS,IAAI,IAAI,IAAI,UAAU,EAAE,SAAS,KAAK;GAMrD,MALe,IAAI,SAAS,OAAO,UAAU,EAAE;IAC9C,SAAS,SAAS,UAAU,YAAY;IACxC,OAAO,SAAS;IAChB,MAAM,SAAS;IACf,CACW,CAAC,UAAU,YAAY,KAAA,EAAU;;EAE9C,YAAY;GACX,IAAI,MAAM;;EAEX,eAAe;GACd,IAAI,SAAS;;EAEd,mBAAmB,aAAa;GAC/B,gBAAgB,IAAI,SAAS;GAC7B,aAAa,gBAAgB,OAAO,SAAS;;EAE9C,aAAa,aAAa;GACzB,gBAAgB,IAAI,SAAS;GAC7B,aAAa,gBAAgB,OAAO,SAAS;;EAE9C,eAAe;GACd,IAAI,oBAAoB,YAAY,eAAe;GACnD,IAAI,oBAAoB,mBAAmB,cAAc;GACzD,gBAAgB,OAAO;GACvB,gBAAgB,OAAO;;EAExB;;AAgBF,SAAgB,qBACf,UAAuC,EAAE,EACrB;CACpB,MAAM,sBAAsB,QAAQ,uBAAuB;CAE3D,MAAM,kCAAkB,IAAI,KAEzB;CACH,MAAM,kCAAkB,IAAI,KAAyC;CAErE,MAAM,iBAAiB,OAAO,WAA+C;EAC5E,KAAK,MAAM,YAAY,iBACtB,MAAM,SAAS,OAAO;EAEvB,OAAO;;CAGR,MAAM,UAAU,QAAa;EAC5B,KAAK,MAAM,YAAY,iBAAiB,SAAS,EAAE,KAAK,CAAC;;CAG1D,MAAM,iBAAiB,OAAO,UAAyB;EACtD,MAAM,MAAM,IAAI,IAAI,SAAS,KAAK;EAClC,IAAI,YAAY;EAChB,IAAI,iBAAwD;EAgB5D,MAAM,eAAe;GAdpB;GACA,OAAO,gBAAgB;GACvB,SAAS;GACT,OAAO,MAAM;GACb,sBAAsB;IACrB,YAAY;;GAEb,WAAW,QAAQ,SAAS;IAC3B,iBAAiB;KAChB,KAAK,IAAI,IAAI,OAAO,UAAU,EAAE,IAAI;KACpC,SAAS,MAAM,WAAW;KAC1B;;GAGwB,CAAC;EAC5B,IAAI,aAAa,CAAC,gBAMjB;EAED,IAAI,gBAAgB;GACnB,MAAM,SAAS;GACf,IAAI,OAAO,SACV,QAAQ,aAAa,MAAM,IAAI,OAAO,IAAI,UAAU,CAAC;QAErD,QAAQ,UAAU,MAAM,IAAI,OAAO,IAAI,UAAU,CAAC;GAEnD,OAAO,OAAO,IAAI;GAClB;;EAED,OAAO,IAAI;;CAGZ,MAAM,oBAAoB,OAAO,UAAsB;EACtD,IACC,MAAM,oBACN,MAAM,WAAW,KACjB,MAAM,WACN,MAAM,WACN,MAAM,YACN,MAAM,QAEN;EAGD,MAAM,SADO,MAAM,cACA,CAAC,MAClB,SAAoC,gBAAgB,kBACrD;EACD,IAAI,CAAC,QAAQ,QAAQ,OAAO,WAAW,UAAU;EACjD,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK;EACtC,IAAI,UAAU,WAAW,SAAS,QAAQ;EAC1C,MAAM,gBAAgB;EACtB,MAAM,iBAAiB,WAAW,EAAE,SAAS,OAAO,CAAC;;CAGtD,MAAM,mBAAmB,OACxB,KACA,YACmB;EACnB,IAAI,YAAY;EAChB,IAAI,iBAAwD;EAgB5D,MAAM,eAAe;GAdpB;GACA,OAAO,QAAQ,UAAU,gBAAgB,UAAU,gBAAgB;GACnE,SAAS,QAAQ,WAAW;GAC5B,OAAO,QAAQ;GACf,sBAAsB;IACrB,YAAY;;GAEb,WAAW,QAAQ,SAAS;IAC3B,iBAAiB;KAChB,KAAK,IAAI,IAAI,OAAO,UAAU,EAAE,IAAI;KACpC,SAAS,MAAM,WAAW;KAC1B;;GAGwB,CAAC;EAC5B,IAAI,aAAa,CAAC,gBAAgB;EAClC,MAAM,SAAS,iBACX,iBACD;GAAE;GAAK,SAAS,QAAQ,WAAW;GAAO;EAC7C,IAAI,OAAO,SACV,QAAQ,aAAa,QAAQ,SAAS,MAAM,IAAI,OAAO,IAAI,UAAU,CAAC;OAEtE,QAAQ,UAAU,QAAQ,SAAS,MAAM,IAAI,OAAO,IAAI,UAAU,CAAC;EAEpE,OAAO,OAAO,IAAI;;CAGnB,IAAI,OAAO,WAAW,aAAa;EAClC,OAAO,iBAAiB,YAAY,eAAe;EACnD,IAAI,qBACH,SAAS,iBAAiB,SAAS,kBAAkB;;CAIvD,OAAO;EACN,MAAM,sBAAsB;EAC5B,kBAAkB,IAAI,IAAI,SAAS,KAAK;EACxC,WAAW,KAAK,eACf,iBACC,IAAI,IAAI,IAAI,UAAU,EAAE,SAAS,KAAK,EACtC,cAAc,EAAE,CAChB;EACF,YAAY;GACX,QAAQ,MAAM;;EAEf,eAAe;GACd,QAAQ,SAAS;;EAElB,mBAAmB,aAAa;GAC/B,gBAAgB,IAAI,SAAS;GAC7B,aAAa,gBAAgB,OAAO,SAAS;;EAE9C,aAAa,aAAa;GACzB,gBAAgB,IAAI,SAAS;GAC7B,aAAa,gBAAgB,OAAO,SAAS;;EAE9C,eAAe;GACd,IAAI,OAAO,WAAW,aAAa;IAClC,OAAO,oBAAoB,YAAY,eAAe;IACtD,IAAI,qBACH,SAAS,oBAAoB,SAAS,kBAAkB;;GAG1D,gBAAgB,OAAO;GACvB,gBAAgB,OAAO;;EAExB;;;;;;;AA2BF,SAAgB,wBACf,UAA0C,EAAE,EACxB;CACpB,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI,WAAW,sBAAsB,SACpC,OAAO,qBAAqB,QAAQ,QAAQ;CAE7C,IAAI,WAAW,sBAAsB,eACpC,OAAO,4BAA4B;CAEpC,IAAI,eAAe,EAClB,OAAO,4BAA4B;CAEpC,OAAO,qBAAqB,QAAQ,QAAQ;;;;;AAM7C,SAAgB,2BAAoC;CACnD,OAAO,eAAe,KAAK,KAAA;;;;;;;;;ACnW5B,SAAgB,eACf,YAC4B;CAC5B,OAAO;;;;;;;;;;;AAqBR,SAAgB,6BACf,OACA,UAAwC,EAAE,EAChB;CAC1B,MAAM,qBACL,QAAQ,sBAAsB,iCAAiC;CAChE,IAAI,YAAqC,EAAE;CAC3C,KAAK,MAAM,WAAW,OAAO;EAC5B,MAAM,cAAc,QAAQ,eAAe;EAC3C,MAAM,UAAU,QAAQ,gBAAgB,EAAE;EAC1C,YAAY,0BAA0B,WAAW;GAChD;GACA;GACA,CAAC;;CAEH,OAAO;;AAWR,SAAS,eAAe,SAA2C;CAClE,IAAI,OAAO,YAAY,YACtB,OAAO,EACN,QAAQ,QAAQ,QAAQ,IAAI,IAAI,MAChC;CAEF,IAAI,mBAAmB,QACtB,OAAO,EACN,QAAQ,QAAQ;EACf,MAAM,SAAS,QAAQ,KAAK,IAAI,SAAS;EACzC,IAAI,CAAC,QAAQ,OAAO;EACpB,OAAO,EAAE,GAAI,OAAO,UAAU,EAAE,EAAG;IAEpC;CAIF,MAAM,aAAuB,EAAE;CAC/B,IAAI,eAA8B;CAElC,MAAM,QADW,QAAQ,MAAM,IACT,CAAC,KAAK,YAAY;EACvC,IAAI,QAAQ,WAAW,IAAI,EAAE;GAC5B,WAAW,KAAK,QAAQ,MAAM,EAAE,CAAC;GACjC,OAAO;;EAER,IAAI,YAAY,KAAK;GACpB,eAAe;GACf,OAAO;;EAER,IAAI,QAAQ,WAAW,IAAI,EAAE;GAC5B,eAAe,QAAQ,MAAM,EAAE;GAC/B,OAAO;;EAER,OAAO,QAAQ,QAAQ,uBAAuB,OAAO;GACpD;CACF,MAAM,UAAU,IAAI,OAAO,IAAI,MAAM,KAAK,IAAI,CAAC,GAAG;CAClD,OAAO,EACN,QAAQ,QAAQ;EACf,MAAM,SAAS,QAAQ,KAAK,IAAI,SAAS;EACzC,IAAI,CAAC,QAAQ,OAAO;EACpB,MAAM,SAAiC,EAAE;EACzC,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KACtC,OAAO,WAAW,MAAM,mBAAmB,OAAO,IAAI,MAAM,GAAG;EAEhE,IAAI,cACH,OAAO,gBAAgB,OAAO,OAAO,SAAS,MAAM;EAErD,OAAO;IAER;;AAaF,SAAS,cACR,QACA,cAAoD,EAAE,EACjC;CACrB,MAAM,SAA6B,EAAE;CACrC,KAAK,MAAM,SAAS,QAAQ;EAC3B,MAAM,QAAQ,CAAC,GAAG,aAAa,MAAM;EAErC,IADoB,CAAC,CAAC,MAAM,YAAY,MAAM,SAAS,SAAS,GAC/C;GAChB,OAAO,KAAK,GAAG,cAAc,MAAM,YAAY,EAAE,EAAE,MAAM,CAAC;GAM1D;;EAED,MAAM,WAAW,MAAM,QAAQ,eAAe,MAAM,MAAM,GAAG;EAC7D,OAAO,KAAK;GAAE,MAAM;GAAO;GAAO;GAAU,CAAC;;CAE9C,OAAO;;;;;;;;;;;;;;;;;AAsFR,SAAgB,gBACf,UAAyC,EAAE,EACxB;CACnB,MAAM,UACL,QAAQ,qBAAqB,UAAU,QAAQ,oBAC3C,QAAQ,oBACT,wBACC,QAAQ,qBAAwD,EAAE,CACnE;CAEJ,MAAM,QAAqC,CAAC,GAAI,QAAQ,UAAU,EAAE,CAAE;CACtE,IAAI,OAA2B,cAAc,MAAM;CAEnD,MAAM,gBAAgB;EACrB,OAAO,cAAc,MAAM;;CAG5B,MAAM,YAAY,QAA0C;EAC3D,KAAK,MAAM,SAAS,MAAM;GACzB,IAAI,CAAC,MAAM,UAAU;GACrB,MAAM,SAAS,MAAM,SAAS,MAAM,IAAI;GACxC,IAAI,QACH,OAAO;IACN,OAAO,MAAM;IACb;IACA;IACA,OAAO,MAAM;IACb;;EAGH,OAAO;;CAGR,MAAM,uBACL,UAEA,6BAA6B,MAAM,OAAO,EACzC,oBAAoB,QAAQ,oBAC5B,CAAC;CAEH,IAAI,YAAyC,SAAS,QAAQ,YAAY,CAAC;CAE3E,MAAM,cAAc,QAAQ,iBAAiB,OAAO,WAAW;EAC9D,MAAM,QAAQ,SAAS,OAAO,IAAI;EAClC,IAAI,CAAC,SAAS,CAAC,QAAQ,aAAa;EACpC,MAAM,aAAa,oBAAoB,MAAM;EAC7C,IAAI,WAAW,WAAW,GAAG;EAC7B,MAAM,SAAS,MAAM,QAAQ,YAAY,SAAS,WAAW;EAC7D,IAAI,OAAO,kBAAkB;EAC7B,MAAM,UAAU,OAAO;EACvB,IAAI,CAAC,SAAS;EACd,MAAM,UAAU,MAAM,QAAQ,mBAAmB;EACjD,IAAI,YAAY,MAAM;EACtB,IAAI,YAAY,OAAO;GACtB,OAAO,gBAAgB;GACvB;;EAED,IAAI,OAAO,YAAY,UACtB,OAAO,SAAS,QAAQ;GAExB;CAEF,MAAM,WAAW,QAAQ,YAAY,WAAW;EAC/C,YAAY,SAAS,OAAO,IAAI;EAChC,QAAQ,aAAa,OAAO;GAC3B;CAEF,OAAO;EACN;EACA,kBAAkB,QAAQ,YAAY;EACtC,oBAAoB;EACpB,cAAc,MAAM,OAAO;EAC3B,WAAW,UAAU;GACpB,MAAM,KAAK,MAAM;GACjB,SAAS;GACT,aAAa;IACZ,MAAM,MAAM,MAAM,QAAQ,MAAM;IAChC,IAAI,OAAO,GAAG;KACb,MAAM,OAAO,KAAK,EAAE;KACpB,SAAS;;;;EAIZ,OAAO;EACP;EACA,WAAW,KAAK,eAAe,QAAQ,SAAS,KAAK,WAAW;EAChE,YAAY,QAAQ,MAAM;EAC1B,eAAe,QAAQ,SAAS;EAChC,aAAa,aAAa,QAAQ,WAAW,SAAS;EACtD,eAAe;GACd,aAAa;GACb,UAAU;GACV,QAAQ,SAAS;;EAElB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securitydept/client",
3
- "version": "0.3.0-beta.1",
3
+ "version": "0.3.0-beta.3",
4
4
  "description": "Client of the SecurityDept TypeScript SDK, a layered authentication and authorization toolkit built as reusable Rust crates, a TypeScript client SDK workspace, and reference server/web apps.",
5
5
  "author": "Zhou Yeheng <root@aitiotekt.com>",
6
6
  "license": "MIT",
@@ -66,6 +66,6 @@
66
66
  },
67
67
  "scripts": {
68
68
  "build": "tsdown",
69
- "typecheck": "tsc -b"
69
+ "typecheck": "tsc -b --force"
70
70
  }
71
71
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"fetch-transport-DtnSRWAH.mjs","names":[],"sources":["../src/errors/types.ts","../src/errors/client-error.ts","../src/logging/types.ts","../src/logging/operation-tracer.ts","../src/scheduling/default-clock.ts","../src/scheduling/default-scheduler.ts","../src/runtime/create-runtime.ts","../src/scheduling/helpers.ts","../src/web/input-sources.ts","../src/web/cancellation.ts","../src/transport/fetch-transport.ts"],"sourcesContent":["// --- Error model ---\n// Dual-layer: machine-facing runtime error + user-facing presentation / recovery hint.\n\n/** Recovery actions the user might take. Aligned with server-side `UserRecovery`. */\nexport const UserRecovery = {\n\tNone: \"none\",\n\tRetry: \"retry\",\n\tRestartFlow: \"restart_flow\",\n\tReauthenticate: \"reauthenticate\",\n\tContactSupport: \"contact_support\",\n} as const;\n\nexport type UserRecovery = (typeof UserRecovery)[keyof typeof UserRecovery];\n\n/** User-facing error presentation. `code` is the cross-platform stable contract. */\nexport interface ErrorPresentation {\n\tcode: string;\n\tmessage: string;\n\trecovery: UserRecovery;\n}\n\nexport const ErrorPresentationTone = {\n\tNeutral: \"neutral\",\n\tWarning: \"warning\",\n\tDanger: \"danger\",\n} as const;\n\nexport type ErrorPresentationTone =\n\t(typeof ErrorPresentationTone)[keyof typeof ErrorPresentationTone];\n\nexport interface ErrorPresentationActionDescriptor {\n\trecovery: UserRecovery;\n\tlabel: string;\n\thref: string | null;\n}\n\nexport interface ErrorPresentationDescriptor {\n\tcode: string | null;\n\tkind: ClientErrorKind | null;\n\tsource?: string;\n\ttitle: string;\n\tdescription: string;\n\trecovery: UserRecovery;\n\tretryable: boolean;\n\ttone: ErrorPresentationTone;\n\tprimaryAction: ErrorPresentationActionDescriptor | null;\n}\n\nexport interface ReadErrorPresentationDescriptorOptions {\n\tfallbackTitle?: string;\n\tfallbackDescription?: string;\n\trecoveryLinks?: Partial<Record<UserRecovery, string>>;\n\trecoveryLabels?: Partial<Record<UserRecovery, string>>;\n}\n\n/** Machine-facing error kind discriminator. */\nexport const ClientErrorKind = {\n\tAuthorization: \"authorization\",\n\tTransport: \"transport\",\n\tServer: \"server\",\n\tProtocol: \"protocol\",\n\tPresentation: \"presentation\",\n\tValidation: \"validation\",\n\tStorage: \"storage\",\n\tScheduler: \"scheduler\",\n\tConfiguration: \"configuration\",\n\tUnauthenticated: \"unauthenticated\",\n\tUnauthorized: \"unauthorized\",\n\tRedirectRequired: \"redirect_required\",\n\tCancelled: \"cancelled\",\n\tTimeout: \"timeout\",\n\tInternal: \"internal\",\n} as const;\n\nexport type ClientErrorKind =\n\t(typeof ClientErrorKind)[keyof typeof ClientErrorKind];\n\n/** Stable source vocabulary for common client error producers. */\nexport const ClientErrorSource = {\n\tTransport: \"transport\",\n\tServer: \"server\",\n\tClientRuntime: \"client_runtime\",\n} as const;\n\nexport type ClientErrorSource =\n\t(typeof ClientErrorSource)[keyof typeof ClientErrorSource];\n","import {\n\tClientErrorKind,\n\tClientErrorSource,\n\ttype ErrorPresentation,\n\tUserRecovery,\n\ttype UserRecovery as UserRecoveryType,\n} from \"./types\";\n\ntype ServerErrorBody = {\n\tkind?: string;\n\tcode?: string;\n\tmessage?: string;\n\trecovery?: string;\n\tpresentation?: {\n\t\tcode?: string;\n\t\tmessage?: string;\n\t\trecovery?: string;\n\t};\n};\n\nfunction mapServerErrorKind(\n\tkind: string | undefined,\n\tstatus: number,\n): ClientErrorKind {\n\tswitch (kind) {\n\t\tcase \"unauthenticated\":\n\t\t\treturn ClientErrorKind.Unauthenticated;\n\t\tcase \"unauthorized\":\n\t\t\treturn ClientErrorKind.Unauthorized;\n\t\tcase \"unavailable\":\n\t\tcase \"internal\":\n\t\t\treturn ClientErrorKind.Server;\n\t\tcase \"invalid_request\":\n\t\tcase \"conflict\":\n\t\t\treturn ClientErrorKind.Protocol;\n\t\tdefault:\n\t\t\treturn status === 401\n\t\t\t\t? ClientErrorKind.Unauthenticated\n\t\t\t\t: status === 403\n\t\t\t\t\t? ClientErrorKind.Unauthorized\n\t\t\t\t\t: status >= 500\n\t\t\t\t\t\t? ClientErrorKind.Server\n\t\t\t\t\t\t: ClientErrorKind.Protocol;\n\t}\n}\n\nfunction readServerErrorBody(body: unknown): ServerErrorBody | undefined {\n\tif (!body || typeof body !== \"object\") return undefined;\n\tconst record = body as Record<string, unknown>;\n\tconst error =\n\t\trecord.error && typeof record.error === \"object\"\n\t\t\t? (record.error as Record<string, unknown>)\n\t\t\t: record;\n\n\treturn {\n\t\tkind: typeof error.kind === \"string\" ? error.kind : undefined,\n\t\tcode: typeof error.code === \"string\" ? error.code : undefined,\n\t\tmessage: typeof error.message === \"string\" ? error.message : undefined,\n\t\trecovery: typeof error.recovery === \"string\" ? error.recovery : undefined,\n\t\tpresentation:\n\t\t\terror.presentation && typeof error.presentation === \"object\"\n\t\t\t\t? {\n\t\t\t\t\t\tcode:\n\t\t\t\t\t\t\ttypeof (error.presentation as Record<string, unknown>).code ===\n\t\t\t\t\t\t\t\"string\"\n\t\t\t\t\t\t\t\t? ((error.presentation as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t.code as string)\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\ttypeof (error.presentation as Record<string, unknown>).message ===\n\t\t\t\t\t\t\t\"string\"\n\t\t\t\t\t\t\t\t? ((error.presentation as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t.message as string)\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\trecovery:\n\t\t\t\t\t\t\ttypeof (error.presentation as Record<string, unknown>)\n\t\t\t\t\t\t\t\t.recovery === \"string\"\n\t\t\t\t\t\t\t\t? ((error.presentation as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t.recovery as string)\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t};\n}\n\n/**\n * Base client error carrying both machine-facing context and an optional\n * user-facing presentation layer.\n *\n * Top-level `code` and `recovery` provide stable machine contracts for\n * policy decisions without requiring callers to unwrap `presentation`.\n */\nexport class ClientError extends Error {\n\treadonly kind: ClientErrorKind;\n\n\t/**\n\t * Stable machine-readable error code.\n\t * Matches `presentation.code` when present, otherwise a default\n\t * derived from `kind`.\n\t */\n\treadonly code: string;\n\n\t/** Recovery hint at the machine level. */\n\treadonly recovery: UserRecoveryType;\n\n\t/** Whether this error is safe to retry. */\n\treadonly retryable: boolean;\n\n\t/** Structured user-facing error presentation from the server. */\n\treadonly presentation?: ErrorPresentation;\n\n\t/** Source subsystem or component that produced the error. */\n\treadonly source?: string;\n\n\tconstructor(options: {\n\t\tkind: ClientErrorKind;\n\t\tmessage: string;\n\t\tcode?: string;\n\t\trecovery?: UserRecoveryType;\n\t\tretryable?: boolean;\n\t\tpresentation?: ErrorPresentation;\n\t\tsource?: string;\n\t\tcause?: unknown;\n\t}) {\n\t\tsuper(options.message, { cause: options.cause });\n\t\tthis.name = \"ClientError\";\n\t\tthis.kind = options.kind;\n\t\tthis.code =\n\t\t\toptions.code ?? options.presentation?.code ?? `client.${options.kind}`;\n\t\tthis.recovery =\n\t\t\toptions.recovery ?? options.presentation?.recovery ?? UserRecovery.None;\n\t\tthis.retryable = options.retryable ?? this.recovery === UserRecovery.Retry;\n\t\tthis.presentation = options.presentation;\n\t\tthis.source = options.source;\n\t}\n\n\t/** Create a `ClientError` from a server error response body. */\n\tstatic fromServerError(\n\t\tbody: {\n\t\t\tkind?: string;\n\t\t\tcode?: string;\n\t\t\tmessage?: string;\n\t\t\trecovery?: string;\n\t\t\tpresentation?: {\n\t\t\t\tcode?: string;\n\t\t\t\tmessage?: string;\n\t\t\t\trecovery?: string;\n\t\t\t};\n\t\t},\n\t\toverrides?: { kind?: ClientErrorKind; source?: string },\n\t): ClientError {\n\t\tconst rawPresentation = body.presentation;\n\t\tconst presentation: ErrorPresentation | undefined =\n\t\t\t(rawPresentation?.code ?? body.code) &&\n\t\t\t(rawPresentation?.message ?? body.message)\n\t\t\t\t? {\n\t\t\t\t\t\tcode: rawPresentation?.code ?? body.code ?? \"unknown_server_error\",\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\trawPresentation?.message ??\n\t\t\t\t\t\t\tbody.message ??\n\t\t\t\t\t\t\t\"Unknown server error\",\n\t\t\t\t\t\trecovery:\n\t\t\t\t\t\t\t(rawPresentation?.recovery as ErrorPresentation[\"recovery\"]) ??\n\t\t\t\t\t\t\t(body.recovery as ErrorPresentation[\"recovery\"]) ??\n\t\t\t\t\t\t\tUserRecovery.None,\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\n\t\treturn new ClientError({\n\t\t\tkind: overrides?.kind ?? mapServerErrorKind(body.kind, 400),\n\t\t\tmessage: presentation?.message ?? body.message ?? \"Unknown server error\",\n\t\t\tpresentation,\n\t\t\tsource: overrides?.source,\n\t\t});\n\t}\n\n\t/**\n\t * Create a `ClientError` from an HTTP response status and optional body.\n\t * Used when a non-success status is unexpected.\n\t */\n\tstatic fromHttpResponse(status: number, body?: unknown): ClientError {\n\t\tconst serverBody = readServerErrorBody(body);\n\n\t\t// Derive kind and source from HTTP status first — these apply\n\t\t// whether or not the response carries a structured error body.\n\t\tconst kind = mapServerErrorKind(serverBody?.kind, status);\n\n\t\tconst source =\n\t\t\tstatus >= 500\n\t\t\t\t? ClientErrorSource.Server\n\t\t\t\t: ClientErrorSource.ClientRuntime;\n\n\t\t// If the server returned a structured error body, preserve it\n\t\t// but keep the status-derived kind/source.\n\t\tif (serverBody?.code && serverBody?.message) {\n\t\t\treturn ClientError.fromServerError(serverBody, { kind, source });\n\t\t}\n\n\t\tconst recovery: UserRecoveryType =\n\t\t\tkind === ClientErrorKind.Unauthenticated\n\t\t\t\t? UserRecovery.Reauthenticate\n\t\t\t\t: status >= 500\n\t\t\t\t\t? UserRecovery.Retry\n\t\t\t\t\t: UserRecovery.None;\n\n\t\treturn new ClientError({\n\t\t\tkind,\n\t\t\tmessage: serverBody?.message ?? `HTTP ${status}`,\n\t\t\tcode: serverBody?.code ?? `http.${status}`,\n\t\t\trecovery,\n\t\t\tretryable: status >= 500,\n\t\t\tsource,\n\t\t});\n\t}\n}\n","// --- Logging and observability ---\n\nexport const LogLevel = {\n\tDebug: \"debug\",\n\tInfo: \"info\",\n\tWarn: \"warn\",\n\tError: \"error\",\n} as const;\n\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\n\nexport interface LogEntry {\n\tlevel: LogLevel;\n\tmessage: string;\n\tscope?: string;\n\tcode?: string;\n\tattributes?: Record<string, unknown>;\n\terror?: unknown;\n\tat?: number;\n}\n\nexport interface LoggerTrait {\n\tlog(entry: LogEntry): void;\n}\n\nexport const OperationTraceEventType = {\n\tStarted: \"operation.started\",\n\tEvent: \"operation.event\",\n\tError: \"operation.error\",\n\tEnded: \"operation.ended\",\n} as const;\n\nexport type OperationTraceEventType =\n\t(typeof OperationTraceEventType)[keyof typeof OperationTraceEventType];\n\nexport interface TraceEvent {\n\ttype: string;\n\tat: number;\n\tscope?: string;\n\toperationId?: string;\n\tsource?: string;\n\tattributes?: Record<string, unknown>;\n}\n\nexport interface TraceEventSinkTrait {\n\trecord(event: TraceEvent): void;\n}\n\nexport interface OperationScope {\n\tid: string;\n\taddEvent(type: string, attributes?: Record<string, unknown>): void;\n\tsetAttribute(key: string, value: unknown): void;\n\trecordError(error: unknown, attributes?: Record<string, unknown>): void;\n\tend(attributes?: Record<string, unknown>): void;\n}\n\nexport interface OperationTracerTrait {\n\tstartOperation(\n\t\tname: string,\n\t\tattributes?: Record<string, unknown>,\n\t): OperationScope;\n}\n","import type { Clock } from \"../scheduling/types\";\nimport type {\n\tLoggerTrait,\n\tOperationScope,\n\tOperationTracerTrait,\n\tTraceEventSinkTrait,\n} from \"./types\";\nimport { LogLevel, OperationTraceEventType } from \"./types\";\n\nexport interface CreateOperationTracerOptions {\n\ttraceSink?: TraceEventSinkTrait;\n\tlogger?: LoggerTrait;\n\tclock?: Clock;\n\tscope?: string;\n\tsource?: string;\n\tidFactory?: () => string;\n}\n\nconst defaultClock: Clock = {\n\tnow: () => Date.now(),\n};\n\nfunction createDefaultOperationId(): string {\n\treturn `op_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nclass DefaultOperationScope implements OperationScope {\n\treadonly id: string;\n\n\tprivate readonly _attributes: Record<string, unknown>;\n\tprivate _ended = false;\n\n\tconstructor(\n\t\tprivate readonly _name: string,\n\t\tprivate readonly _clock: Clock,\n\t\tprivate readonly _traceSink: TraceEventSinkTrait | undefined,\n\t\tprivate readonly _logger: LoggerTrait | undefined,\n\t\tprivate readonly _scope: string | undefined,\n\t\tprivate readonly _source: string | undefined,\n\t\tidFactory: () => string,\n\t\tattributes?: Record<string, unknown>,\n\t) {\n\t\tthis.id = idFactory();\n\t\tthis._attributes = { ...(attributes ?? {}) };\n\t\tthis._recordTrace(OperationTraceEventType.Started, {\n\t\t\toperationName: this._name,\n\t\t\t...this._attributes,\n\t\t});\n\t}\n\n\taddEvent(type: string, attributes?: Record<string, unknown>): void {\n\t\tthis._recordTrace(OperationTraceEventType.Event, {\n\t\t\toperationName: this._name,\n\t\t\teventType: type,\n\t\t\t...this._attributes,\n\t\t\t...(attributes ?? {}),\n\t\t});\n\t}\n\n\tsetAttribute(key: string, value: unknown): void {\n\t\tthis._attributes[key] = value;\n\t}\n\n\trecordError(error: unknown, attributes?: Record<string, unknown>): void {\n\t\tconst errorAttributes = describeError(error);\n\t\tthis._recordTrace(OperationTraceEventType.Error, {\n\t\t\toperationName: this._name,\n\t\t\t...this._attributes,\n\t\t\t...errorAttributes,\n\t\t\t...(attributes ?? {}),\n\t\t});\n\t\tthis._logger?.log({\n\t\t\tlevel: LogLevel.Error,\n\t\t\tmessage: `Operation failed: ${this._name}`,\n\t\t\tscope: this._scope,\n\t\t\tattributes: {\n\t\t\t\toperationId: this.id,\n\t\t\t\toperationName: this._name,\n\t\t\t\t...this._attributes,\n\t\t\t\t...errorAttributes,\n\t\t\t\t...(attributes ?? {}),\n\t\t\t},\n\t\t\terror,\n\t\t\tat: this._clock.now(),\n\t\t});\n\t}\n\n\tend(attributes?: Record<string, unknown>): void {\n\t\tif (this._ended) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._ended = true;\n\t\tthis._recordTrace(OperationTraceEventType.Ended, {\n\t\t\toperationName: this._name,\n\t\t\t...this._attributes,\n\t\t\t...(attributes ?? {}),\n\t\t});\n\t}\n\n\tprivate _recordTrace(\n\t\ttype: string,\n\t\tattributes?: Record<string, unknown>,\n\t): void {\n\t\tthis._traceSink?.record({\n\t\t\ttype,\n\t\t\tat: this._clock.now(),\n\t\t\tscope: this._scope,\n\t\t\tsource: this._source,\n\t\t\toperationId: this.id,\n\t\t\tattributes,\n\t\t});\n\t}\n}\n\nexport function createOperationTracer(\n\toptions: CreateOperationTracerOptions = {},\n): OperationTracerTrait {\n\tconst clock = options.clock ?? defaultClock;\n\tconst idFactory = options.idFactory ?? createDefaultOperationId;\n\n\treturn {\n\t\tstartOperation(name, attributes) {\n\t\t\treturn new DefaultOperationScope(\n\t\t\t\tname,\n\t\t\t\tclock,\n\t\t\t\toptions.traceSink,\n\t\t\t\toptions.logger,\n\t\t\t\toptions.scope,\n\t\t\t\toptions.source,\n\t\t\t\tidFactory,\n\t\t\t\tattributes,\n\t\t\t);\n\t\t},\n\t};\n}\n\nfunction describeError(error: unknown): Record<string, unknown> {\n\tif (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"name\" in error &&\n\t\t\"message\" in error\n\t) {\n\t\tconst genericError = error as Pick<Error, \"name\" | \"message\">;\n\t\treturn {\n\t\t\terrorName: genericError.name,\n\t\t\terrorMessage: genericError.message,\n\t\t};\n\t}\n\n\treturn { errorValue: String(error) };\n}\n","import type { Clock } from \"./types\";\n\n/** Default clock backed by `Date.now()`. */\nexport function createDefaultClock(): Clock {\n\treturn {\n\t\tnow() {\n\t\t\treturn Date.now();\n\t\t},\n\t};\n}\n","import type { CancelableHandle, Scheduler } from \"./types\";\n\n/** Default scheduler backed by platform `setTimeout`. */\nexport function createDefaultScheduler(): Scheduler {\n\treturn {\n\t\tsetTimeout(delayMs: number, fn: () => void): CancelableHandle {\n\t\t\tconst id = globalThis.setTimeout(fn, delayMs);\n\t\t\treturn {\n\t\t\t\tcancel() {\n\t\t\t\t\tglobalThis.clearTimeout(id);\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n","import { createOperationTracer } from \"../logging/operation-tracer\";\nimport { createDefaultClock } from \"../scheduling/default-clock\";\nimport { createDefaultScheduler } from \"../scheduling/default-scheduler\";\nimport type { HttpTransport } from \"../transport/types\";\nimport type { ClientRuntime } from \"./types\";\n\nexport interface CreateRuntimeOptions\n\textends Partial<Omit<ClientRuntime, \"transport\">> {\n\ttransport: HttpTransport;\n}\n\n/**\n * Create a `ClientRuntime` with non-host-specific defaults filled in for\n * convenience.\n *\n * Host-specific capabilities such as browser `fetch` should be supplied\n * explicitly or composed via adapter-specific helpers such as\n * `@securitydept/client/web`.\n */\nexport function createRuntime(overrides: CreateRuntimeOptions): ClientRuntime {\n\tconst clock = overrides.clock ?? createDefaultClock();\n\tconst traceSink = overrides.traceSink;\n\tconst logger = overrides.logger;\n\n\treturn {\n\t\ttransport: overrides.transport,\n\t\tscheduler: overrides.scheduler ?? createDefaultScheduler(),\n\t\tclock,\n\t\tlogger,\n\t\ttraceSink,\n\t\toperationTracer:\n\t\t\toverrides.operationTracer ??\n\t\t\tcreateOperationTracer({\n\t\t\t\tclock,\n\t\t\t\tlogger,\n\t\t\t\ttraceSink,\n\t\t\t}),\n\t\tpersistentStore: overrides.persistentStore,\n\t\tsessionStore: overrides.sessionStore,\n\t};\n}\n","// Scheduling helpers — foundation-owned convenience over raw Scheduler.\n//\n// These helpers sit above the injectable Scheduler / Clock and provide\n// reusable, cancelable wrappers for common scheduling patterns.\n\nimport type { ReadableSignalTrait } from \"../signals/types\";\nimport type { CancelableHandle, Clock, Scheduler } from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Shared types\n// ---------------------------------------------------------------------------\n\n/** Subscription handle returned by input-source adapters. */\nexport interface Subscription {\n\t/** Unsubscribe and release resources. */\n\tunsubscribe(): void;\n}\n\nexport interface FromSignalOptions<T> {\n\t/** Signal to observe. */\n\tsignal: ReadableSignalTrait<T>;\n\t/** Callback invoked when the signal value changes. */\n\tcallback: (value: T) => void;\n\t/**\n\t * Emit the current snapshot immediately after subscription.\n\t * Defaults to `false`.\n\t */\n\temitInitialValue?: boolean;\n}\n\nexport const PromiseSettlementKind = {\n\tFulfilled: \"fulfilled\",\n\tRejected: \"rejected\",\n} as const;\n\nexport type PromiseSettlementKind =\n\t(typeof PromiseSettlementKind)[keyof typeof PromiseSettlementKind];\n\nexport type PromiseSettlement<T> =\n\t| {\n\t\t\tkind: typeof PromiseSettlementKind.Fulfilled;\n\t\t\tvalue: T;\n\t }\n\t| {\n\t\t\tkind: typeof PromiseSettlementKind.Rejected;\n\t\t\treason: unknown;\n\t };\n\nexport interface FromPromiseOptions<T> {\n\t/** Promise-like source to observe. */\n\tpromise: PromiseLike<T>;\n\t/** Callback invoked when the promise settles. */\n\tcallback: (settlement: PromiseSettlement<T>) => void;\n}\n\n// ---------------------------------------------------------------------------\n// timer\n// ---------------------------------------------------------------------------\n\n/** Options for {@link timer}. */\nexport interface TimerOptions {\n\t/** Scheduler to use (platform default if omitted). */\n\tscheduler: Scheduler;\n\t/** Delay in milliseconds. */\n\tdelayMs: number;\n\t/** Callback to invoke after the delay. */\n\tcallback: () => void;\n}\n\n/**\n * Schedule a one-shot callback after a delay.\n *\n * Returns a {@link CancelableHandle} that can cancel the pending timer.\n *\n * @example\n * ```ts\n * const handle = timer({\n * scheduler: runtime.scheduler,\n * delayMs: 5000,\n * callback: () => console.log(\"fired\"),\n * });\n * // Cancel before it fires:\n * handle.cancel();\n * ```\n */\nexport function timer(options: TimerOptions): CancelableHandle {\n\treturn options.scheduler.setTimeout(options.delayMs, options.callback);\n}\n\n// ---------------------------------------------------------------------------\n// interval\n// ---------------------------------------------------------------------------\n\n/** Options for {@link interval}. */\nexport interface IntervalOptions {\n\t/** Scheduler to use. */\n\tscheduler: Scheduler;\n\t/** Interval period in milliseconds. */\n\tperiodMs: number;\n\t/** Callback to invoke on each tick. */\n\tcallback: () => void;\n}\n\n/**\n * Schedule a repeating callback at a fixed interval.\n *\n * Built on top of `Scheduler.setTimeout` so it works with deterministic\n * test schedulers. Returns a {@link CancelableHandle} to stop the interval.\n *\n * @example\n * ```ts\n * const handle = interval({\n * scheduler: runtime.scheduler,\n * periodMs: 30_000,\n * callback: () => refreshTokens(),\n * });\n * // Stop:\n * handle.cancel();\n * ```\n */\nexport function interval(options: IntervalOptions): CancelableHandle {\n\tlet canceled = false;\n\tlet currentHandle: CancelableHandle | null = null;\n\n\tfunction tick(): void {\n\t\tif (canceled) return;\n\t\toptions.callback();\n\t\tscheduleNext();\n\t}\n\n\tfunction scheduleNext(): void {\n\t\tif (canceled) return;\n\t\tcurrentHandle = options.scheduler.setTimeout(options.periodMs, tick);\n\t}\n\n\t// Start the first tick.\n\tscheduleNext();\n\n\treturn {\n\t\tcancel() {\n\t\t\tcanceled = true;\n\t\t\tcurrentHandle?.cancel();\n\t\t\tcurrentHandle = null;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// scheduleAt\n// ---------------------------------------------------------------------------\n\n/** Options for {@link scheduleAt}. */\nexport interface ScheduleAtOptions {\n\t/** Scheduler to use. */\n\tscheduler: Scheduler;\n\t/** Clock to use for computing the delay. */\n\tclock: Clock;\n\t/** Target epoch milliseconds at which the callback should fire. */\n\tatMs: number;\n\t/** Callback to invoke at (or after) the target time. */\n\tcallback: () => void;\n}\n\n/**\n * Schedule a callback at a specific wall-clock time.\n *\n * Computes the delay from `clock.now()` to `atMs` and delegates to\n * `Scheduler.setTimeout`. If the target time is already in the past,\n * the callback fires on the next scheduler tick (delay 0).\n *\n * @example\n * ```ts\n * const handle = scheduleAt({\n * scheduler: runtime.scheduler,\n * clock: runtime.clock,\n * atMs: tokenExpiresAtMs - 30_000,\n * callback: () => refreshToken(),\n * });\n * ```\n */\nexport function scheduleAt(options: ScheduleAtOptions): CancelableHandle {\n\tconst delay = Math.max(0, options.atMs - options.clock.now());\n\treturn options.scheduler.setTimeout(delay, options.callback);\n}\n\n// ---------------------------------------------------------------------------\n// fromSignal\n// ---------------------------------------------------------------------------\n\n/**\n * Adapt an SDK signal into the shared {@link Subscription} callback model.\n *\n * @example\n * ```ts\n * const sub = fromSignal({\n * signal: authState,\n * callback: (snapshot) => console.log(snapshot),\n * });\n * sub.unsubscribe();\n * ```\n */\nexport function fromSignal<T>(options: FromSignalOptions<T>): Subscription {\n\tif (options.emitInitialValue) {\n\t\toptions.callback(options.signal.get());\n\t}\n\n\tconst unsubscribe = options.signal.subscribe(() => {\n\t\toptions.callback(options.signal.get());\n\t});\n\n\treturn {\n\t\tunsubscribe() {\n\t\t\tunsubscribe();\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// fromPromise\n// ---------------------------------------------------------------------------\n\n/**\n * Adapt a promise settlement into the shared {@link Subscription} callback model.\n *\n * This helper is intentionally thin: it reports one settlement and lets the\n * consumer decide how to model pending/fulfilled/error state.\n *\n * @example\n * ```ts\n * const sub = fromPromise({\n * promise: fetchConfig(),\n * callback: (settlement) => {\n * if (settlement.kind === PromiseSettlementKind.Fulfilled) {\n * console.log(settlement.value);\n * }\n * },\n * });\n * sub.unsubscribe();\n * ```\n */\nexport function fromPromise<T>(options: FromPromiseOptions<T>): Subscription {\n\tlet active = true;\n\n\tvoid Promise.resolve(options.promise).then(\n\t\t(value) => {\n\t\t\tif (!active) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toptions.callback({\n\t\t\t\tkind: PromiseSettlementKind.Fulfilled,\n\t\t\t\tvalue,\n\t\t\t});\n\t\t},\n\t\t(reason) => {\n\t\t\tif (!active) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toptions.callback({\n\t\t\t\tkind: PromiseSettlementKind.Rejected,\n\t\t\t\treason,\n\t\t\t});\n\t\t},\n\t);\n\n\treturn {\n\t\tunsubscribe() {\n\t\t\tactive = false;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// fromEventPattern\n// ---------------------------------------------------------------------------\n\n/** Options for {@link fromEventPattern}. */\nexport interface FromEventPatternOptions<T> {\n\t/**\n\t * Called to install the event listener. Should return whatever handle\n\t * is needed by `removeHandler` to unsubscribe.\n\t */\n\taddHandler: (handler: (value: T) => void) => void;\n\t/**\n\t * Called to uninstall the event listener.\n\t */\n\tremoveHandler: (handler: (value: T) => void) => void;\n\t/** Callback invoked each time the event fires. */\n\tcallback: (value: T) => void;\n}\n\n/**\n * Adapt an arbitrary add/remove event pattern into a {@link Subscription}.\n *\n * This is a thin bridge between imperative event APIs (e.g. `addEventListener`\n * / `removeEventListener`) and the SDK's subscription model.\n *\n * @example\n * ```ts\n * const sub = fromEventPattern<Event>({\n * addHandler: (h) => document.addEventListener(\"visibilitychange\", h),\n * removeHandler: (h) => document.removeEventListener(\"visibilitychange\", h),\n * callback: () => onVisibilityChange(),\n * });\n * // Cleanup:\n * sub.unsubscribe();\n * ```\n */\nexport function fromEventPattern<T>(\n\toptions: FromEventPatternOptions<T>,\n): Subscription {\n\tconst handler = (value: T): void => {\n\t\toptions.callback(value);\n\t};\n\toptions.addHandler(handler);\n\treturn {\n\t\tunsubscribe() {\n\t\t\toptions.removeHandler(handler);\n\t\t},\n\t};\n}\n","import { fromEventPattern, type Subscription } from \"../scheduling/helpers\";\n\nexport interface AbortSignalSource {\n\taborted: boolean;\n\treason: unknown;\n\taddEventListener(type: string, listener: EventListener): void;\n\tremoveEventListener(type: string, listener: EventListener): void;\n}\n\nexport interface FromAbortSignalOptions {\n\t/** AbortSignal to observe. */\n\tsignal?: AbortSignalSource;\n\t/** Callback invoked when the signal aborts. */\n\tcallback: (reason: unknown) => void;\n\t/**\n\t * Emit immediately when the signal is already aborted at subscribe time.\n\t * Defaults to `false`.\n\t */\n\temitIfAborted?: boolean;\n}\n\nexport interface StorageEventTarget {\n\taddEventListener(type: string, listener: EventListener): void;\n\tremoveEventListener(type: string, listener: EventListener): void;\n}\n\nexport interface FromStorageEventOptions {\n\t/**\n\t * Storage-event target. Defaults to `globalThis`, which is appropriate for\n\t * browser hosts and jsdom-based tests.\n\t */\n\ttarget?: StorageEventTarget;\n\t/** Callback invoked for each browser `storage` event. */\n\tcallback: (event: StorageEvent) => void;\n}\n\n/**\n * Adapt a browser AbortSignal into the shared Subscription model.\n */\nexport function fromAbortSignal(options: FromAbortSignalOptions): Subscription {\n\tconst signal = options.signal;\n\tif (!signal) {\n\t\treturn {\n\t\t\tunsubscribe() {},\n\t\t};\n\t}\n\n\tif (options.emitIfAborted && signal.aborted) {\n\t\toptions.callback(signal.reason);\n\t}\n\n\treturn fromEventPattern<Event>({\n\t\taddHandler: (handler) => {\n\t\t\tsignal.addEventListener(\"abort\", handler);\n\t\t},\n\t\tremoveHandler: (handler) => {\n\t\t\tsignal.removeEventListener(\"abort\", handler);\n\t\t},\n\t\tcallback: () => {\n\t\t\toptions.callback(signal.reason);\n\t\t},\n\t});\n}\n\n/**\n * Adapt browser `storage` events into the shared Subscription model.\n */\nexport function fromStorageEvent(\n\toptions: FromStorageEventOptions,\n): Subscription {\n\tconst target = options.target ?? (globalThis as StorageEventTarget);\n\n\treturn fromEventPattern<Event>({\n\t\taddHandler: (handler) => {\n\t\t\ttarget.addEventListener(\"storage\", handler);\n\t\t},\n\t\tremoveHandler: (handler) => {\n\t\t\ttarget.removeEventListener(\"storage\", handler);\n\t\t},\n\t\tcallback: (event) => {\n\t\t\toptions.callback(event as StorageEvent);\n\t\t},\n\t});\n}\n","import type {\n\tCancellationTokenTrait,\n\tDisposableTrait,\n} from \"../cancellation/types\";\nimport { ClientError } from \"../errors/client-error\";\nimport { ClientErrorKind, ClientErrorSource } from \"../errors/types\";\nimport { fromAbortSignal } from \"./input-sources\";\n\nexport interface AbortSignalBridge {\n\tsignal?: AbortSignal;\n\tdispose(): void;\n}\n\n/**\n * Bridge a web `AbortSignal` back into the foundation cancellation contract.\n *\n * This is the canonical consumer-side path for browser hosts that receive an\n * `AbortSignal` from framework/runtime APIs (for example React Query queryFns)\n * but need to call SDK surfaces that accept `CancellationTokenTrait`.\n */\nexport function createCancellationTokenFromAbortSignal(\n\tsignal?: AbortSignal,\n): CancellationTokenTrait | undefined {\n\tif (!signal) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tget isCancellationRequested() {\n\t\t\treturn signal.aborted;\n\t\t},\n\t\tget reason() {\n\t\t\treturn signal.reason;\n\t\t},\n\t\tonCancellationRequested(listener: (reason: unknown) => void) {\n\t\t\tconst subscription = fromAbortSignal({\n\t\t\t\tsignal,\n\t\t\t\tcallback: listener,\n\t\t\t\temitIfAborted: true,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tdispose() {\n\t\t\t\t\tsubscription.unsubscribe();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tthrowIfCancellationRequested() {\n\t\t\tif (signal.aborted) {\n\t\t\t\tthrow new ClientError({\n\t\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\t\tmessage: \"Request was cancelled via AbortSignal\",\n\t\t\t\t\tcode: \"client.cancelled\",\n\t\t\t\t\tsource: ClientErrorSource.Transport,\n\t\t\t\t\tcause: signal.reason,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t};\n}\n\n/**\n * Bridge foundation cancellation to web `AbortSignal` without exposing\n * `AbortSignal` in core contracts.\n */\nexport function createAbortSignalBridge(\n\ttoken?: CancellationTokenTrait,\n): AbortSignalBridge {\n\tif (!token) {\n\t\treturn {\n\t\t\tsignal: undefined,\n\t\t\tdispose() {},\n\t\t};\n\t}\n\n\tconst controller = new AbortController();\n\tlet subscription: DisposableTrait | null = token.onCancellationRequested(\n\t\t(reason) => {\n\t\t\tcontroller.abort(reason);\n\t\t},\n\t);\n\n\tif (token.isCancellationRequested) {\n\t\tcontroller.abort(token.reason);\n\t}\n\n\treturn {\n\t\tsignal: controller.signal,\n\t\tdispose() {\n\t\t\tsubscription?.dispose();\n\t\t\tsubscription = null;\n\t\t},\n\t};\n}\n\nexport function normalizeAbortError(\n\ttoken: CancellationTokenTrait | undefined,\n\terror: unknown,\n): unknown {\n\tif (!isAbortError(error) && !token?.isCancellationRequested) {\n\t\treturn error;\n\t}\n\n\tconst reason = token?.reason;\n\tif (reason instanceof ClientError) {\n\t\treturn reason;\n\t}\n\n\tif (reason instanceof Error) {\n\t\treturn new ClientError({\n\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\tmessage: reason.message,\n\t\t\tcode: \"client.cancelled\",\n\t\t\tsource: ClientErrorSource.Transport,\n\t\t\tcause: reason,\n\t\t});\n\t}\n\n\treturn new ClientError({\n\t\tkind: ClientErrorKind.Cancelled,\n\t\tmessage: \"HTTP request was cancelled\",\n\t\tcode: \"client.cancelled\",\n\t\tsource: ClientErrorSource.Transport,\n\t\tcause: reason ?? error,\n\t});\n}\n\nfunction isAbortError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"name\" in error &&\n\t\terror.name === \"AbortError\"\n\t);\n}\n","import {\n\tcreateAbortSignalBridge,\n\tnormalizeAbortError,\n} from \"../web/cancellation\";\nimport type { HttpRequest, HttpResponse, HttpTransport } from \"./types\";\n\nexport const FetchTransportRedirectKind = {\n\tFollow: \"follow\",\n\tManual: \"manual\",\n} as const;\n\nexport type FetchTransportRedirectKind =\n\t(typeof FetchTransportRedirectKind)[keyof typeof FetchTransportRedirectKind];\n\nexport interface FetchTransportOptions {\n\t/**\n\t * How to handle HTTP redirects.\n\t * - `\"follow\"`: automatically follow redirects (default fetch behavior)\n\t * - `\"manual\"`: capture redirect responses as-is (needed for fragment-based protocols)\n\t */\n\tredirect?: FetchTransportRedirectKind;\n}\n\n/**\n * Default `HttpTransport` backed by the global `fetch` API.\n *\n * The caller is responsible for ensuring `fetch` is available in the runtime.\n * No polyfill is injected — see the polyfill strategy in the design guide.\n */\nexport function createFetchTransport(\n\toptions: FetchTransportOptions = {},\n): HttpTransport {\n\tconst redirect = options.redirect ?? FetchTransportRedirectKind.Manual;\n\n\treturn {\n\t\tasync execute(request: HttpRequest): Promise<HttpResponse> {\n\t\t\tconst abortBridge = createAbortSignalBridge(request.cancellationToken);\n\t\t\tconst init: RequestInit = {\n\t\t\t\tmethod: request.method,\n\t\t\t\theaders: request.headers,\n\t\t\t\tredirect,\n\t\t\t\tsignal: abortBridge.signal,\n\t\t\t};\n\n\t\t\tif (\n\t\t\t\trequest.body !== undefined &&\n\t\t\t\trequest.method !== \"GET\" &&\n\t\t\t\trequest.method !== \"HEAD\"\n\t\t\t) {\n\t\t\t\tinit.body =\n\t\t\t\t\ttypeof request.body === \"string\"\n\t\t\t\t\t\t? request.body\n\t\t\t\t\t\t: JSON.stringify(request.body);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(request.url, init);\n\n\t\t\t\tconst responseHeaders: Record<string, string> = {};\n\t\t\t\tres.headers.forEach((value, key) => {\n\t\t\t\t\tresponseHeaders[key] = value;\n\t\t\t\t});\n\n\t\t\t\tlet body: unknown;\n\t\t\t\tconst contentType = res.headers.get(\"content-type\");\n\t\t\t\tif (contentType?.includes(\"application/json\")) {\n\t\t\t\t\tbody = await res.json();\n\t\t\t\t} else if (\n\t\t\t\t\tres.status !== 302 &&\n\t\t\t\t\tres.status !== 303 &&\n\t\t\t\t\tres.status !== 301\n\t\t\t\t) {\n\t\t\t\t\tbody = await res.text();\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tstatus: res.status,\n\t\t\t\t\theaders: responseHeaders,\n\t\t\t\t\tbody,\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tthrow normalizeAbortError(request.cancellationToken, error);\n\t\t\t} finally {\n\t\t\t\tabortBridge.dispose();\n\t\t\t}\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/persistence/web/stores.ts"],"sourcesContent":["import type { RecordStore } from \"../types\";\n\n/**\n * `RecordStore` backed by `localStorage`.\n * @param prefix Optional key prefix to isolate the keyspace.\n */\nexport function createLocalStorageStore(prefix = \"\"): RecordStore {\n\treturn {\n\t\tasync get(key: string): Promise<string | null> {\n\t\t\treturn globalThis.localStorage.getItem(prefix + key);\n\t\t},\n\t\tasync set(key: string, value: string): Promise<void> {\n\t\t\tglobalThis.localStorage.setItem(prefix + key, value);\n\t\t},\n\t\tasync take(key: string): Promise<string | null> {\n\t\t\tconst storageKey = prefix + key;\n\t\t\tconst value = globalThis.localStorage.getItem(storageKey);\n\t\t\tif (value !== null) {\n\t\t\t\tglobalThis.localStorage.removeItem(storageKey);\n\t\t\t}\n\t\t\treturn value;\n\t\t},\n\t\tasync remove(key: string): Promise<void> {\n\t\t\tglobalThis.localStorage.removeItem(prefix + key);\n\t\t},\n\t};\n}\n\n/**\n * `RecordStore` backed by `sessionStorage`.\n * @param prefix Optional key prefix to isolate the keyspace.\n */\nexport function createSessionStorageStore(prefix = \"\"): RecordStore {\n\treturn {\n\t\tasync get(key: string): Promise<string | null> {\n\t\t\treturn globalThis.sessionStorage.getItem(prefix + key);\n\t\t},\n\t\tasync set(key: string, value: string): Promise<void> {\n\t\t\tglobalThis.sessionStorage.setItem(prefix + key, value);\n\t\t},\n\t\tasync take(key: string): Promise<string | null> {\n\t\t\tconst storageKey = prefix + key;\n\t\t\tconst value = globalThis.sessionStorage.getItem(storageKey);\n\t\t\tif (value !== null) {\n\t\t\t\tglobalThis.sessionStorage.removeItem(storageKey);\n\t\t\t}\n\t\t\treturn value;\n\t\t},\n\t\tasync remove(key: string): Promise<void> {\n\t\t\tglobalThis.sessionStorage.removeItem(prefix + key);\n\t\t},\n\t};\n}\n"],"mappings":";;;;;AAMA,SAAgB,wBAAwB,SAAS,IAAiB;AACjE,QAAO;EACN,MAAM,IAAI,KAAqC;AAC9C,UAAO,WAAW,aAAa,QAAQ,SAAS,IAAI;;EAErD,MAAM,IAAI,KAAa,OAA8B;AACpD,cAAW,aAAa,QAAQ,SAAS,KAAK,MAAM;;EAErD,MAAM,KAAK,KAAqC;GAC/C,MAAM,aAAa,SAAS;GAC5B,MAAM,QAAQ,WAAW,aAAa,QAAQ,WAAW;AACzD,OAAI,UAAU,KACb,YAAW,aAAa,WAAW,WAAW;AAE/C,UAAO;;EAER,MAAM,OAAO,KAA4B;AACxC,cAAW,aAAa,WAAW,SAAS,IAAI;;EAEjD;;;;;;AAOF,SAAgB,0BAA0B,SAAS,IAAiB;AACnE,QAAO;EACN,MAAM,IAAI,KAAqC;AAC9C,UAAO,WAAW,eAAe,QAAQ,SAAS,IAAI;;EAEvD,MAAM,IAAI,KAAa,OAA8B;AACpD,cAAW,eAAe,QAAQ,SAAS,KAAK,MAAM;;EAEvD,MAAM,KAAK,KAAqC;GAC/C,MAAM,aAAa,SAAS;GAC5B,MAAM,QAAQ,WAAW,eAAe,QAAQ,WAAW;AAC3D,OAAI,UAAU,KACb,YAAW,eAAe,WAAW,WAAW;AAEjD,UAAO;;EAER,MAAM,OAAO,KAA4B;AACxC,cAAW,eAAe,WAAW,SAAS,IAAI;;EAEnD"}