@securitydept/client 0.2.0-beta.4 → 0.2.0-beta.5

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.
@@ -1,4 +1,4 @@
1
- import { S as ClientRuntime, d as Subscription, n as FetchTransportRedirectKind, r as createFetchTransport, t as FetchTransportOptions, z as CancellationTokenTrait } from "../fetch-transport-CMG4tfDu.mjs";
1
+ import { I as CancellationTokenTrait, d as Subscription, n as FetchTransportRedirectKind, r as createFetchTransport, t as FetchTransportOptions, y as ClientRuntime } from "../fetch-transport-Bd5RyWj2.mjs";
2
2
 
3
3
  //#region src/web/cancellation.d.ts
4
4
  interface AbortSignalBridge {
@@ -114,6 +114,43 @@ declare function fromAbortSignal(options: FromAbortSignalOptions): Subscription;
114
114
  */
115
115
  declare function fromStorageEvent(options: FromStorageEventOptions): Subscription;
116
116
  //#endregion
117
+ //#region src/web/page-resume-reconciler.d.ts
118
+ declare const PageResumeTriggerKind: {
119
+ readonly Visibility: "visibility";
120
+ readonly PageShow: "pageshow";
121
+ readonly Focus: "focus";
122
+ readonly Online: "online";
123
+ };
124
+ type PageResumeTriggerKind = (typeof PageResumeTriggerKind)[keyof typeof PageResumeTriggerKind];
125
+ interface PageResumeEvent {
126
+ trigger: PageResumeTriggerKind;
127
+ persisted?: boolean;
128
+ }
129
+ type PageResumeCallback = (event: PageResumeEvent) => void | Promise<void>;
130
+ interface PageResumeDocumentTarget {
131
+ addEventListener(type: "visibilitychange", handler: EventListener): void;
132
+ removeEventListener(type: "visibilitychange", handler: EventListener): void;
133
+ visibilityState: DocumentVisibilityState;
134
+ }
135
+ interface PageResumeWindowTarget {
136
+ addEventListener(type: "pageshow" | "focus" | "online", handler: EventListener): void;
137
+ removeEventListener(type: "pageshow" | "focus" | "online", handler: EventListener): void;
138
+ }
139
+ interface CreatePageResumeReconcilerOptions {
140
+ onReconcile: PageResumeCallback;
141
+ /** Defaults to 5000ms. Use 0 in deterministic tests. */
142
+ throttleMs?: number;
143
+ document?: PageResumeDocumentTarget | null;
144
+ window?: PageResumeWindowTarget | null;
145
+ now?: () => number;
146
+ onError?: (error: unknown, event: PageResumeEvent) => void;
147
+ }
148
+ interface PageResumeReconciler {
149
+ dispose(): void;
150
+ readonly reconcileCount: number;
151
+ }
152
+ declare function createPageResumeReconciler(options: CreatePageResumeReconcilerOptions): PageResumeReconciler;
153
+ //#endregion
117
154
  //#region src/web/popup.d.ts
118
155
  /** Stable error codes for popup-related failures. */
119
156
  declare const PopupErrorCode: {
@@ -325,5 +362,5 @@ interface VisibilityReconciler {
325
362
  */
326
363
  declare function createVisibilityReconciler(options: CreateVisibilityReconcilerOptions): VisibilityReconciler;
327
364
  //#endregion
328
- export { type AbortSignalBridge, type CreateCrossTabSyncOptions, type CreateVisibilityReconcilerOptions, type CreateWebRuntimeOptions, type CrossTabSync, type CrossTabSyncCallback, type FetchTransportOptions, FetchTransportRedirectKind, type FromAbortSignalOptions, type FromStorageEventOptions, type FromVisibilityChangeOptions, PopupErrorCode, type PopupFeaturesOptions, type PopupRelayMessage, type PopupWindowHandle, type ReconcileCallback, type RelayPopupCallbackOptions, type VisibilityReconciler, VisibilityState, type WaitForPopupRelayOptions, computePopupFeatures, createAbortSignalBridge, createCancellationTokenFromAbortSignal, createCrossTabSync, createFetchTransport, createVisibilityReconciler, createWebRuntime, fromAbortSignal, fromStorageEvent, fromVisibilityChange, normalizeAbortError, openPopupWindow, relayPopupCallback, waitForPopupRelay };
365
+ export { type AbortSignalBridge, type CreateCrossTabSyncOptions, type CreatePageResumeReconcilerOptions, type CreateVisibilityReconcilerOptions, type CreateWebRuntimeOptions, type CrossTabSync, type CrossTabSyncCallback, type FetchTransportOptions, FetchTransportRedirectKind, type FromAbortSignalOptions, type FromStorageEventOptions, type FromVisibilityChangeOptions, type PageResumeCallback, type PageResumeDocumentTarget, type PageResumeEvent, type PageResumeReconciler, PageResumeTriggerKind, type PageResumeWindowTarget, PopupErrorCode, type PopupFeaturesOptions, type PopupRelayMessage, type PopupWindowHandle, type ReconcileCallback, type RelayPopupCallbackOptions, type VisibilityReconciler, VisibilityState, type WaitForPopupRelayOptions, computePopupFeatures, createAbortSignalBridge, createCancellationTokenFromAbortSignal, createCrossTabSync, createFetchTransport, createPageResumeReconciler, createVisibilityReconciler, createWebRuntime, fromAbortSignal, fromStorageEvent, fromVisibilityChange, normalizeAbortError, openPopupWindow, relayPopupCallback, waitForPopupRelay };
329
366
  //# sourceMappingURL=index.d.mts.map
@@ -48,6 +48,94 @@ function createCrossTabSync(options) {
48
48
  };
49
49
  }
50
50
  //#endregion
51
+ //#region src/web/page-resume-reconciler.ts
52
+ const PageResumeTriggerKind = {
53
+ Visibility: "visibility",
54
+ PageShow: "pageshow",
55
+ Focus: "focus",
56
+ Online: "online"
57
+ };
58
+ function createPageResumeReconciler(options) {
59
+ const throttleMs = options.throttleMs ?? 5e3;
60
+ const getNow = options.now ?? (() => Date.now());
61
+ const doc = options.document ?? maybeDocument();
62
+ const win = options.window ?? maybeWindow();
63
+ let previousVisibilityState = doc?.visibilityState ?? "visible";
64
+ let lastReconcileAt = 0;
65
+ let reconcileCount = 0;
66
+ let disposed = false;
67
+ const cleanups = [];
68
+ const reconcile = (event) => {
69
+ if (disposed) return;
70
+ const now = getNow();
71
+ if (now - lastReconcileAt < throttleMs) return;
72
+ lastReconcileAt = now;
73
+ reconcileCount += 1;
74
+ try {
75
+ const result = options.onReconcile(event);
76
+ if (isPromiseLike(result)) result.catch((error) => options.onError?.(error, event));
77
+ } catch (error) {
78
+ options.onError?.(error, event);
79
+ }
80
+ };
81
+ if (doc) {
82
+ const handler = () => {
83
+ const nextVisibilityState = doc.visibilityState;
84
+ if (nextVisibilityState === "visible" && previousVisibilityState !== "visible") reconcile({ trigger: PageResumeTriggerKind.Visibility });
85
+ previousVisibilityState = nextVisibilityState;
86
+ };
87
+ doc.addEventListener("visibilitychange", handler);
88
+ cleanups.push(() => doc.removeEventListener("visibilitychange", handler));
89
+ }
90
+ if (win) {
91
+ const pageshowHandler = (event) => {
92
+ reconcile({
93
+ trigger: PageResumeTriggerKind.PageShow,
94
+ persisted: Boolean(event.persisted)
95
+ });
96
+ };
97
+ const focusHandler = () => {
98
+ reconcile({ trigger: PageResumeTriggerKind.Focus });
99
+ };
100
+ const onlineHandler = () => {
101
+ reconcile({ trigger: PageResumeTriggerKind.Online });
102
+ };
103
+ win.addEventListener("pageshow", pageshowHandler);
104
+ win.addEventListener("focus", focusHandler);
105
+ win.addEventListener("online", onlineHandler);
106
+ cleanups.push(() => win.removeEventListener("pageshow", pageshowHandler));
107
+ cleanups.push(() => win.removeEventListener("focus", focusHandler));
108
+ cleanups.push(() => win.removeEventListener("online", onlineHandler));
109
+ }
110
+ return {
111
+ dispose() {
112
+ if (disposed) return;
113
+ disposed = true;
114
+ for (const cleanup of cleanups.splice(0)) cleanup();
115
+ },
116
+ get reconcileCount() {
117
+ return reconcileCount;
118
+ }
119
+ };
120
+ }
121
+ function maybeDocument() {
122
+ const documentTarget = globalThis.document;
123
+ return isPageResumeDocumentTarget(documentTarget) ? documentTarget : null;
124
+ }
125
+ function maybeWindow() {
126
+ const windowTarget = globalThis.window;
127
+ return isPageResumeWindowTarget(windowTarget) ? windowTarget : null;
128
+ }
129
+ function isPageResumeDocumentTarget(value) {
130
+ return typeof value === "object" && value !== null && "visibilityState" in value && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function";
131
+ }
132
+ function isPageResumeWindowTarget(value) {
133
+ return typeof value === "object" && value !== null && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function";
134
+ }
135
+ function isPromiseLike(value) {
136
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
137
+ }
138
+ //#endregion
51
139
  //#region src/web/popup.ts
52
140
  /** Stable error codes for popup-related failures. */
53
141
  const PopupErrorCode = {
@@ -181,7 +269,9 @@ function relayPopupCallback(options = {}) {
181
269
  error: options.error
182
270
  };
183
271
  opener.postMessage(message, targetOrigin);
184
- if (options.closeAfterRelay !== false) window.close();
272
+ if (options.closeAfterRelay !== false) setTimeout(() => {
273
+ window.close();
274
+ }, 0);
185
275
  }
186
276
  //#endregion
187
277
  //#region src/web/runtime.ts
@@ -287,6 +377,6 @@ function createVisibilityReconciler(options) {
287
377
  };
288
378
  }
289
379
  //#endregion
290
- export { FetchTransportRedirectKind, PopupErrorCode, VisibilityState, computePopupFeatures, createAbortSignalBridge, createCancellationTokenFromAbortSignal, createCrossTabSync, createFetchTransport, createVisibilityReconciler, createWebRuntime, fromAbortSignal, fromStorageEvent, fromVisibilityChange, normalizeAbortError, openPopupWindow, relayPopupCallback, waitForPopupRelay };
380
+ export { FetchTransportRedirectKind, PageResumeTriggerKind, PopupErrorCode, VisibilityState, computePopupFeatures, createAbortSignalBridge, createCancellationTokenFromAbortSignal, createCrossTabSync, createFetchTransport, createPageResumeReconciler, createVisibilityReconciler, createWebRuntime, fromAbortSignal, fromStorageEvent, fromVisibilityChange, normalizeAbortError, openPopupWindow, relayPopupCallback, waitForPopupRelay };
291
381
 
292
382
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/web/cross-tab-sync.ts","../../src/web/popup.ts","../../src/web/runtime.ts","../../src/web/visibility.ts","../../src/web/visibility-reconciler.ts"],"sourcesContent":["import { fromStorageEvent } from \"./input-sources\";\n\n// Cross-tab auth state sync — minimal baseline using storage events\n//\n// Provides a minimal, composable cross-tab sync mechanism for auth state.\n// When another tab writes to the auth persistence key (via localStorage),\n// this listener detects the change and notifies the current tab so it can\n// reconcile its in-memory state.\n//\n// This is intentionally thin — it uses the browser `storage` event which\n// fires on OTHER tabs when localStorage is modified. The reconciliation\n// callback is the adopter's responsibility (e.g. re-read from persistence,\n// compare with in-memory state, and update).\n//\n// Architecture boundary:\n// - This module does NOT own the persistence store.\n// - It only listens for cross-tab mutations and notifies via callback.\n// - The adopter wires this to their AuthMaterialController.\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Callback invoked when a cross-tab storage change is detected for the watched key. */\nexport type CrossTabSyncCallback = (event: {\n\t/** The new value written by the other tab, or null if the key was removed. */\n\tnewValue: string | null;\n\t/** The old value before the other tab's write. */\n\toldValue: string | null;\n}) => void;\n\n/** Options for {@link createCrossTabSync}. */\nexport interface CreateCrossTabSyncOptions {\n\t/** The localStorage key to watch for cross-tab changes. */\n\tkey: string;\n\n\t/** Called when another tab modifies the watched key. */\n\tonSync: CrossTabSyncCallback;\n\n\t/**\n\t * Override the window target (primarily for testing).\n\t * Defaults to `globalThis`.\n\t */\n\ttarget?: {\n\t\taddEventListener(type: string, listener: EventListener): void;\n\t\tremoveEventListener(type: string, listener: EventListener): void;\n\t};\n}\n\n/** Handle returned by {@link createCrossTabSync}. */\nexport interface CrossTabSync {\n\t/** Stop listening for cross-tab storage events. */\n\tdispose(): void;\n\t/** Number of times a sync event has been received. */\n\treadonly syncCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a cross-tab sync listener for a specific localStorage key.\n *\n * The browser `storage` event fires on ALL tabs EXCEPT the one that made the\n * change. This makes it a natural primitive for cross-tab state notification.\n *\n * @example\n * ```ts\n * const sync = createCrossTabSync({\n * key: \"auth:v1\",\n * onSync: ({ newValue }) => {\n * if (newValue) {\n * // Another tab updated auth state — reconcile\n * controller.restoreFromPersistence();\n * } else {\n * // Another tab cleared auth state — log out\n * controller.clearState({ clearPersisted: false });\n * }\n * },\n * });\n * // Later:\n * sync.dispose();\n * ```\n */\nexport function createCrossTabSync(\n\toptions: CreateCrossTabSyncOptions,\n): CrossTabSync {\n\tconst target = options.target ?? globalThis;\n\tlet syncCount = 0;\n\tconst subscription = fromStorageEvent({\n\t\ttarget,\n\t\tcallback: (storageEvent) => {\n\t\t\t// Only react to changes on our watched key.\n\t\t\tif (storageEvent.key !== options.key) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsyncCount++;\n\t\t\toptions.onSync({\n\t\t\t\tnewValue: storageEvent.newValue,\n\t\t\t\toldValue: storageEvent.oldValue,\n\t\t\t});\n\t\t},\n\t});\n\n\treturn {\n\t\tdispose() {\n\t\t\tsubscription.unsubscribe();\n\t\t},\n\t\tget syncCount() {\n\t\t\treturn syncCount;\n\t\t},\n\t};\n}\n","// Popup shared infrastructure — browser-only helpers\n//\n// Provides cross-mode popup window management for login flows.\n// Owned by @securitydept/client/web, consumed by backend-oidc-mode\n// and frontend-oidc-mode popup login entries.\n\nimport { ClientError, ClientErrorKind, UserRecovery } from \"../errors/index\";\n\n// ---------------------------------------------------------------------------\n// Error codes (stable contract)\n// ---------------------------------------------------------------------------\n\n/** Stable error codes for popup-related failures. */\nexport const PopupErrorCode = {\n\t/** The browser blocked the popup window from opening. */\n\tBlocked: \"popup.blocked\",\n\t/** The popup was closed by the user before completing the flow. */\n\tClosed: \"popup.closed_by_user\",\n\t/** The relay message was not received within the timeout. */\n\tTimeout: \"popup.relay_timeout\",\n\t/** The relay message contained an error from the callback page. */\n\tRelayError: \"popup.relay_error\",\n\t/** An unexpected error occurred during popup lifecycle. */\n\tInternal: \"popup.internal\",\n} as const;\n\nexport type PopupErrorCode =\n\t(typeof PopupErrorCode)[keyof typeof PopupErrorCode];\n\n// ---------------------------------------------------------------------------\n// Popup features\n// ---------------------------------------------------------------------------\n\n/**\n * Options for computing the popup window features string.\n */\nexport interface PopupFeaturesOptions {\n\t/** Width in pixels (default: 500). */\n\twidth?: number;\n\t/** Height in pixels (default: 600). */\n\theight?: number;\n}\n\n/**\n * Compute the `features` string for `window.open()`, centered on screen.\n */\nexport function computePopupFeatures(\n\toptions: PopupFeaturesOptions = {},\n): string {\n\tconst width = options.width ?? 500;\n\tconst height = options.height ?? 600;\n\tconst left = Math.max(\n\t\t0,\n\t\tMath.round(window.screenX + (window.innerWidth - width) / 2),\n\t);\n\tconst top = Math.max(\n\t\t0,\n\t\tMath.round(window.screenY + (window.innerHeight - height) / 2),\n\t);\n\n\treturn `width=${width},height=${height},left=${left},top=${top},popup=yes,toolbar=no,menubar=no`;\n}\n\n// ---------------------------------------------------------------------------\n// Popup open + blocked detection\n// ---------------------------------------------------------------------------\n\n/**\n * Result of opening a popup window.\n */\nexport interface PopupWindowHandle {\n\t/** The opened window reference. */\n\twindow: Window;\n}\n\n/**\n * Open a popup window to the given URL.\n *\n * Throws a `ClientError` with code `popup.blocked` if the browser\n * blocks the popup.\n */\nexport function openPopupWindow(\n\turl: string,\n\toptions?: PopupFeaturesOptions & { target?: string },\n): PopupWindowHandle {\n\tconst features = computePopupFeatures(options);\n\tconst target = options?.target ?? \"_blank\";\n\n\tconst win = window.open(url, target, features);\n\n\tif (!win || win.closed) {\n\t\tthrow new ClientError({\n\t\t\tkind: ClientErrorKind.Authorization,\n\t\t\tcode: PopupErrorCode.Blocked,\n\t\t\tmessage:\n\t\t\t\t\"Popup window was blocked by the browser. Please allow popups for this site.\",\n\t\t\trecovery: UserRecovery.Retry,\n\t\t\tsource: \"popup\",\n\t\t});\n\t}\n\n\treturn { window: win };\n}\n\n// ---------------------------------------------------------------------------\n// PostMessage relay\n// ---------------------------------------------------------------------------\n\n/**\n * Expected shape of a relay message from the popup callback page.\n */\nexport interface PopupRelayMessage {\n\t/** Discriminator for the message type. */\n\ttype: \"securitydept:popup_callback\";\n\t/** The callback URL or fragment received by the popup. */\n\tpayload: string;\n\t/** Optional error from the callback page. */\n\terror?: string;\n}\n\n/**\n * Options for waiting for a popup relay message.\n */\nexport interface WaitForPopupRelayOptions {\n\t/** The popup window handle to monitor. */\n\tpopup: PopupWindowHandle;\n\t/** Maximum time to wait in milliseconds (default: 120000 = 2 minutes). */\n\ttimeoutMs?: number;\n\t/** Expected origin for the relay message (default: current origin). */\n\texpectedOrigin?: string;\n\t/** Poll interval for checking if the popup is closed (default: 500ms). */\n\tpollIntervalMs?: number;\n}\n\n/**\n * Wait for a relay message from a popup callback page.\n *\n * The popup callback page should call `relayPopupCallback()` to post\n * the callback URL back to the opener.\n *\n * Resolves with the callback payload (URL or fragment string).\n * Rejects with a `ClientError` if:\n * - the popup is closed without relaying (popup.closed_by_user)\n * - the timeout expires (popup.relay_timeout)\n * - the relay message contains an error (popup.relay_error)\n */\nexport function waitForPopupRelay(\n\toptions: WaitForPopupRelayOptions,\n): Promise<string> {\n\tconst timeoutMs = options.timeoutMs ?? 120_000;\n\tconst expectedOrigin = options.expectedOrigin ?? window.location.origin;\n\tconst pollIntervalMs = options.pollIntervalMs ?? 500;\n\n\treturn new Promise<string>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timeoutId: ReturnType<typeof setTimeout>;\n\t\tlet pollId: ReturnType<typeof setInterval>;\n\n\t\tfunction cleanup() {\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeoutId);\n\t\t\tclearInterval(pollId);\n\t\t\twindow.removeEventListener(\"message\", onMessage);\n\t\t}\n\n\t\tfunction onMessage(event: MessageEvent) {\n\t\t\tif (settled) return;\n\t\t\tif (event.origin !== expectedOrigin) return;\n\n\t\t\tconst data = event.data as PopupRelayMessage | undefined;\n\t\t\tif (!data || data.type !== \"securitydept:popup_callback\") return;\n\n\t\t\tcleanup();\n\n\t\t\tif (data.error) {\n\t\t\t\treject(\n\t\t\t\t\tnew ClientError({\n\t\t\t\t\t\tkind: ClientErrorKind.Protocol,\n\t\t\t\t\t\tcode: PopupErrorCode.RelayError,\n\t\t\t\t\t\tmessage: `Popup callback relay error: ${data.error}`,\n\t\t\t\t\t\tsource: \"popup\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tresolve(data.payload);\n\t\t}\n\n\t\t// Listen for relay messages.\n\t\twindow.addEventListener(\"message\", onMessage);\n\n\t\t// Poll for popup closure.\n\t\tpollId = setInterval(() => {\n\t\t\tif (settled) return;\n\t\t\tif (options.popup.window.closed) {\n\t\t\t\tcleanup();\n\t\t\t\treject(\n\t\t\t\t\tnew ClientError({\n\t\t\t\t\t\tkind: ClientErrorKind.Authorization,\n\t\t\t\t\t\tcode: PopupErrorCode.Closed,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Popup window was closed before completing the login flow.\",\n\t\t\t\t\t\trecovery: UserRecovery.RestartFlow,\n\t\t\t\t\t\tsource: \"popup\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}, pollIntervalMs);\n\n\t\t// Timeout.\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tcleanup();\n\t\t\ttry {\n\t\t\t\toptions.popup.window.close();\n\t\t\t} catch {\n\t\t\t\t// Best-effort close.\n\t\t\t}\n\t\t\treject(\n\t\t\t\tnew ClientError({\n\t\t\t\t\tkind: ClientErrorKind.Timeout,\n\t\t\t\t\tcode: PopupErrorCode.Timeout,\n\t\t\t\t\tmessage: `Popup login timed out after ${timeoutMs}ms.`,\n\t\t\t\t\trecovery: UserRecovery.Retry,\n\t\t\t\t\tsource: \"popup\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}, timeoutMs);\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Popup callback relay (for use in popup callback pages)\n// ---------------------------------------------------------------------------\n\n/**\n * Options for relaying the callback from the popup window back to the opener.\n */\nexport interface RelayPopupCallbackOptions {\n\t/** The callback payload (URL or fragment) to relay. */\n\tpayload?: string;\n\t/** An error message to relay instead of a successful payload. */\n\terror?: string;\n\t/** Whether to close the popup after relaying (default: true). */\n\tcloseAfterRelay?: boolean;\n\t/** Target origin for the postMessage (default: current origin). */\n\ttargetOrigin?: string;\n}\n\n/**\n * Relay the popup callback result back to the opener window.\n *\n * Call this function from the popup callback page to send the callback\n * URL/fragment back to the parent window that initiated the popup login.\n *\n * @example\n * ```html\n * <script type=\"module\">\n * import { relayPopupCallback } from \"@securitydept/client/web\";\n * relayPopupCallback({ payload: window.location.href });\n * </script>\n * ```\n */\nexport function relayPopupCallback(\n\toptions: RelayPopupCallbackOptions = {},\n): void {\n\tconst opener = window.opener;\n\tif (!opener) {\n\t\treturn;\n\t}\n\n\tconst targetOrigin = options.targetOrigin ?? window.location.origin;\n\tconst message: PopupRelayMessage = {\n\t\ttype: \"securitydept:popup_callback\",\n\t\tpayload: options.payload ?? window.location.href,\n\t\terror: options.error,\n\t};\n\n\topener.postMessage(message, targetOrigin);\n\n\tif (options.closeAfterRelay !== false) {\n\t\twindow.close();\n\t}\n}\n","import { createRuntime } from \"../runtime/create-runtime\";\nimport type { ClientRuntime } from \"../runtime/types\";\nimport {\n\tcreateFetchTransport,\n\ttype FetchTransportOptions,\n} from \"../transport/fetch-transport\";\n\nexport interface CreateWebRuntimeOptions\n\textends Partial<Omit<ClientRuntime, \"transport\">> {\n\ttransport?: ClientRuntime[\"transport\"];\n\tfetchTransport?: FetchTransportOptions;\n}\n\n/**\n * Create a `ClientRuntime` for browser/Web-capable environments.\n */\nexport function createWebRuntime(\n\toverrides: CreateWebRuntimeOptions = {},\n): ClientRuntime {\n\tconst { fetchTransport, transport, ...runtime } = overrides;\n\treturn createRuntime({\n\t\t...runtime,\n\t\ttransport: transport ?? createFetchTransport(fetchTransport),\n\t});\n}\n","// Browser visibility change adapter — built on fromEventPattern.\n//\n// Adapts the browser `visibilitychange` event into the SDK's Subscription\n// model, built on the foundation fromEventPattern helper.\n\nimport { fromEventPattern, type Subscription } from \"../scheduling/helpers\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Visibility state snapshot passed to the callback. */\nexport const VisibilityState = {\n\tVisible: \"visible\",\n\tHidden: \"hidden\",\n} as const;\n\nexport type VisibilityState =\n\t(typeof VisibilityState)[keyof typeof VisibilityState];\n\n/** Options for {@link fromVisibilityChange}. */\nexport interface FromVisibilityChangeOptions {\n\t/**\n\t * Callback invoked when the document visibility state changes.\n\t *\n\t * The callback receives the new visibility state (\"visible\" or \"hidden\").\n\t */\n\tcallback: (state: VisibilityState) => void;\n\t/**\n\t * Override the document target (primarily for testing).\n\t * Defaults to `globalThis.document`.\n\t */\n\tdocument?: Pick<\n\t\tDocument,\n\t\t\"addEventListener\" | \"removeEventListener\" | \"visibilityState\"\n\t>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Subscribe to browser document visibility changes.\n *\n * Built on {@link fromEventPattern} so it follows the same Subscription\n * contract. Returns a {@link Subscription} handle for cleanup.\n *\n * @example\n * ```ts\n * const sub = fromVisibilityChange({\n * callback: (state) => {\n * if (state === VisibilityState.Visible) {\n * scheduleRefresh();\n * }\n * },\n * });\n * // Cleanup:\n * sub.unsubscribe();\n * ```\n */\nexport function fromVisibilityChange(\n\toptions: FromVisibilityChangeOptions,\n): Subscription {\n\tconst doc = options.document ?? globalThis.document;\n\n\treturn fromEventPattern<Event>({\n\t\taddHandler: (handler) => {\n\t\t\tdoc.addEventListener(\"visibilitychange\", handler);\n\t\t},\n\t\tremoveHandler: (handler) => {\n\t\t\tdoc.removeEventListener(\"visibilitychange\", handler);\n\t\t},\n\t\tcallback: () => {\n\t\t\tconst state: VisibilityState =\n\t\t\t\tdoc.visibilityState === \"visible\"\n\t\t\t\t\t? VisibilityState.Visible\n\t\t\t\t\t: VisibilityState.Hidden;\n\t\t\toptions.callback(state);\n\t\t},\n\t});\n}\n","// Visibility reconciler — cross-tab / visibility lifecycle hardening baseline\n//\n// Provides a minimal, composable integration between the browser visibility\n// change adapter and the auth material lifecycle. When a tab transitions from\n// hidden → visible, the reconciler triggers a user-supplied reconciliation\n// callback (e.g. check token freshness, re-validate with server, or sync\n// cross-tab state).\n//\n// This is the SDK-side baseline that real projects (e.g. outposts) can wire\n// directly to their mode client's refresh or state-check logic.\n\nimport type { Subscription } from \"../scheduling/helpers\";\nimport type { FromVisibilityChangeOptions } from \"./visibility\";\nimport { fromVisibilityChange, VisibilityState } from \"./visibility\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Callback invoked when the page becomes visible and reconciliation is needed. */\nexport type ReconcileCallback = () => void | Promise<void>;\n\n/** Options for {@link createVisibilityReconciler}. */\nexport interface CreateVisibilityReconcilerOptions {\n\t/**\n\t * Called when the document transitions from hidden → visible.\n\t * The reconciler only fires on the hidden→visible edge; repeated\n\t * visible→visible events are suppressed.\n\t */\n\tonReconcile: ReconcileCallback;\n\n\t/**\n\t * Minimum interval (ms) between reconciliation triggers.\n\t * Prevents rapid-fire reconciliation when the user toggles tabs quickly.\n\t * Defaults to 5000ms (5 seconds).\n\t */\n\tthrottleMs?: number;\n\n\t/**\n\t * Override the document target (primarily for testing).\n\t * Defaults to `globalThis.document`.\n\t */\n\tdocument?: FromVisibilityChangeOptions[\"document\"];\n\n\t/**\n\t * Clock override for testing.\n\t */\n\tnow?: () => number;\n}\n\n/** Handle returned by {@link createVisibilityReconciler}. */\nexport interface VisibilityReconciler {\n\t/** Stop listening for visibility changes. */\n\tdispose(): void;\n\t/** Number of times reconciliation has been triggered. */\n\treadonly reconcileCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a visibility reconciler that triggers a callback when the page\n * transitions from hidden → visible.\n *\n * @example\n * ```ts\n * const reconciler = createVisibilityReconciler({\n * onReconcile: () => {\n * // Re-check token freshness or sync cross-tab state\n * if (controller.snapshot?.tokens.accessTokenExpiresAt) {\n * scheduleRefreshIfExpired();\n * }\n * },\n * });\n * // Later:\n * reconciler.dispose();\n * ```\n */\nexport function createVisibilityReconciler(\n\toptions: CreateVisibilityReconcilerOptions,\n): VisibilityReconciler {\n\tconst throttleMs = options.throttleMs ?? 5000;\n\tconst getNow = options.now ?? (() => Date.now());\n\n\tlet lastReconcileAt = 0;\n\tlet reconcileCount = 0;\n\t// Initialize from the real document state so that tabs opened in background\n\t// (initial hidden) will correctly trigger on their first visible transition.\n\tconst doc = options.document ?? globalThis.document;\n\tlet previousState: VisibilityState =\n\t\tdoc.visibilityState === \"visible\"\n\t\t\t? VisibilityState.Visible\n\t\t\t: VisibilityState.Hidden;\n\n\tconst subscription: Subscription = fromVisibilityChange({\n\t\tcallback: (state) => {\n\t\t\t// Only fire on the hidden → visible edge.\n\t\t\tif (\n\t\t\t\tstate === VisibilityState.Visible &&\n\t\t\t\tpreviousState === VisibilityState.Hidden\n\t\t\t) {\n\t\t\t\tconst now = getNow();\n\t\t\t\tif (now - lastReconcileAt >= throttleMs) {\n\t\t\t\t\tlastReconcileAt = now;\n\t\t\t\t\treconcileCount++;\n\t\t\t\t\toptions.onReconcile();\n\t\t\t\t}\n\t\t\t}\n\t\t\tpreviousState = state;\n\t\t},\n\t\tdocument: doc,\n\t});\n\n\treturn {\n\t\tdispose() {\n\t\t\tsubscription.unsubscribe();\n\t\t},\n\t\tget reconcileCount() {\n\t\t\treturn reconcileCount;\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/web/cross-tab-sync.ts","../../src/web/page-resume-reconciler.ts","../../src/web/popup.ts","../../src/web/runtime.ts","../../src/web/visibility.ts","../../src/web/visibility-reconciler.ts"],"sourcesContent":["import { fromStorageEvent } from \"./input-sources\";\n\n// Cross-tab auth state sync — minimal baseline using storage events\n//\n// Provides a minimal, composable cross-tab sync mechanism for auth state.\n// When another tab writes to the auth persistence key (via localStorage),\n// this listener detects the change and notifies the current tab so it can\n// reconcile its in-memory state.\n//\n// This is intentionally thin — it uses the browser `storage` event which\n// fires on OTHER tabs when localStorage is modified. The reconciliation\n// callback is the adopter's responsibility (e.g. re-read from persistence,\n// compare with in-memory state, and update).\n//\n// Architecture boundary:\n// - This module does NOT own the persistence store.\n// - It only listens for cross-tab mutations and notifies via callback.\n// - The adopter wires this to their AuthMaterialController.\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Callback invoked when a cross-tab storage change is detected for the watched key. */\nexport type CrossTabSyncCallback = (event: {\n\t/** The new value written by the other tab, or null if the key was removed. */\n\tnewValue: string | null;\n\t/** The old value before the other tab's write. */\n\toldValue: string | null;\n}) => void;\n\n/** Options for {@link createCrossTabSync}. */\nexport interface CreateCrossTabSyncOptions {\n\t/** The localStorage key to watch for cross-tab changes. */\n\tkey: string;\n\n\t/** Called when another tab modifies the watched key. */\n\tonSync: CrossTabSyncCallback;\n\n\t/**\n\t * Override the window target (primarily for testing).\n\t * Defaults to `globalThis`.\n\t */\n\ttarget?: {\n\t\taddEventListener(type: string, listener: EventListener): void;\n\t\tremoveEventListener(type: string, listener: EventListener): void;\n\t};\n}\n\n/** Handle returned by {@link createCrossTabSync}. */\nexport interface CrossTabSync {\n\t/** Stop listening for cross-tab storage events. */\n\tdispose(): void;\n\t/** Number of times a sync event has been received. */\n\treadonly syncCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a cross-tab sync listener for a specific localStorage key.\n *\n * The browser `storage` event fires on ALL tabs EXCEPT the one that made the\n * change. This makes it a natural primitive for cross-tab state notification.\n *\n * @example\n * ```ts\n * const sync = createCrossTabSync({\n * key: \"auth:v1\",\n * onSync: ({ newValue }) => {\n * if (newValue) {\n * // Another tab updated auth state — reconcile\n * controller.restoreFromPersistence();\n * } else {\n * // Another tab cleared auth state — log out\n * controller.clearState({ clearPersisted: false });\n * }\n * },\n * });\n * // Later:\n * sync.dispose();\n * ```\n */\nexport function createCrossTabSync(\n\toptions: CreateCrossTabSyncOptions,\n): CrossTabSync {\n\tconst target = options.target ?? globalThis;\n\tlet syncCount = 0;\n\tconst subscription = fromStorageEvent({\n\t\ttarget,\n\t\tcallback: (storageEvent) => {\n\t\t\t// Only react to changes on our watched key.\n\t\t\tif (storageEvent.key !== options.key) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsyncCount++;\n\t\t\toptions.onSync({\n\t\t\t\tnewValue: storageEvent.newValue,\n\t\t\t\toldValue: storageEvent.oldValue,\n\t\t\t});\n\t\t},\n\t});\n\n\treturn {\n\t\tdispose() {\n\t\t\tsubscription.unsubscribe();\n\t\t},\n\t\tget syncCount() {\n\t\t\treturn syncCount;\n\t\t},\n\t};\n}\n","// Page resume reconciler — browser lifecycle hardening primitive.\n\nexport const PageResumeTriggerKind = {\n\tVisibility: \"visibility\",\n\tPageShow: \"pageshow\",\n\tFocus: \"focus\",\n\tOnline: \"online\",\n} as const;\n\nexport type PageResumeTriggerKind =\n\t(typeof PageResumeTriggerKind)[keyof typeof PageResumeTriggerKind];\n\nexport interface PageResumeEvent {\n\ttrigger: PageResumeTriggerKind;\n\tpersisted?: boolean;\n}\n\nexport type PageResumeCallback = (\n\tevent: PageResumeEvent,\n) => void | Promise<void>;\n\nexport interface PageResumeDocumentTarget {\n\taddEventListener(type: \"visibilitychange\", handler: EventListener): void;\n\tremoveEventListener(type: \"visibilitychange\", handler: EventListener): void;\n\tvisibilityState: DocumentVisibilityState;\n}\n\nexport interface PageResumeWindowTarget {\n\taddEventListener(\n\t\ttype: \"pageshow\" | \"focus\" | \"online\",\n\t\thandler: EventListener,\n\t): void;\n\tremoveEventListener(\n\t\ttype: \"pageshow\" | \"focus\" | \"online\",\n\t\thandler: EventListener,\n\t): void;\n}\n\nexport interface CreatePageResumeReconcilerOptions {\n\tonReconcile: PageResumeCallback;\n\t/** Defaults to 5000ms. Use 0 in deterministic tests. */\n\tthrottleMs?: number;\n\tdocument?: PageResumeDocumentTarget | null;\n\twindow?: PageResumeWindowTarget | null;\n\tnow?: () => number;\n\tonError?: (error: unknown, event: PageResumeEvent) => void;\n}\n\nexport interface PageResumeReconciler {\n\tdispose(): void;\n\treadonly reconcileCount: number;\n}\n\ntype Cleanup = () => void;\n\nexport function createPageResumeReconciler(\n\toptions: CreatePageResumeReconcilerOptions,\n): PageResumeReconciler {\n\tconst throttleMs = options.throttleMs ?? 5000;\n\tconst getNow = options.now ?? (() => Date.now());\n\tconst doc = options.document ?? maybeDocument();\n\tconst win = options.window ?? maybeWindow();\n\n\tlet previousVisibilityState: DocumentVisibilityState =\n\t\tdoc?.visibilityState ?? \"visible\";\n\tlet lastReconcileAt = 0;\n\tlet reconcileCount = 0;\n\tlet disposed = false;\n\tconst cleanups: Cleanup[] = [];\n\n\tconst reconcile = (event: PageResumeEvent): void => {\n\t\tif (disposed) return;\n\t\tconst now = getNow();\n\t\tif (now - lastReconcileAt < throttleMs) return;\n\n\t\tlastReconcileAt = now;\n\t\treconcileCount += 1;\n\t\ttry {\n\t\t\tconst result = options.onReconcile(event);\n\t\t\tif (isPromiseLike(result)) {\n\t\t\t\tresult.catch((error) => options.onError?.(error, event));\n\t\t\t}\n\t\t} catch (error) {\n\t\t\toptions.onError?.(error, event);\n\t\t}\n\t};\n\n\tif (doc) {\n\t\tconst handler: EventListener = () => {\n\t\t\tconst nextVisibilityState = doc.visibilityState;\n\t\t\tif (\n\t\t\t\tnextVisibilityState === \"visible\" &&\n\t\t\t\tpreviousVisibilityState !== \"visible\"\n\t\t\t) {\n\t\t\t\treconcile({ trigger: PageResumeTriggerKind.Visibility });\n\t\t\t}\n\t\t\tpreviousVisibilityState = nextVisibilityState;\n\t\t};\n\t\tdoc.addEventListener(\"visibilitychange\", handler);\n\t\tcleanups.push(() => doc.removeEventListener(\"visibilitychange\", handler));\n\t}\n\n\tif (win) {\n\t\tconst pageshowHandler: EventListener = (event) => {\n\t\t\treconcile({\n\t\t\t\ttrigger: PageResumeTriggerKind.PageShow,\n\t\t\t\tpersisted: Boolean((event as PageTransitionEvent).persisted),\n\t\t\t});\n\t\t};\n\t\tconst focusHandler: EventListener = () => {\n\t\t\treconcile({ trigger: PageResumeTriggerKind.Focus });\n\t\t};\n\t\tconst onlineHandler: EventListener = () => {\n\t\t\treconcile({ trigger: PageResumeTriggerKind.Online });\n\t\t};\n\n\t\twin.addEventListener(\"pageshow\", pageshowHandler);\n\t\twin.addEventListener(\"focus\", focusHandler);\n\t\twin.addEventListener(\"online\", onlineHandler);\n\t\tcleanups.push(() => win.removeEventListener(\"pageshow\", pageshowHandler));\n\t\tcleanups.push(() => win.removeEventListener(\"focus\", focusHandler));\n\t\tcleanups.push(() => win.removeEventListener(\"online\", onlineHandler));\n\t}\n\n\treturn {\n\t\tdispose() {\n\t\t\tif (disposed) return;\n\t\t\tdisposed = true;\n\t\t\tfor (const cleanup of cleanups.splice(0)) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t},\n\t\tget reconcileCount() {\n\t\t\treturn reconcileCount;\n\t\t},\n\t};\n}\n\nfunction maybeDocument(): PageResumeDocumentTarget | null {\n\tconst documentTarget = globalThis.document;\n\treturn isPageResumeDocumentTarget(documentTarget) ? documentTarget : null;\n}\n\nfunction maybeWindow(): PageResumeWindowTarget | null {\n\tconst windowTarget = globalThis.window;\n\treturn isPageResumeWindowTarget(windowTarget) ? windowTarget : null;\n}\n\nfunction isPageResumeDocumentTarget(\n\tvalue: unknown,\n): value is PageResumeDocumentTarget {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"visibilityState\" in value &&\n\t\ttypeof (value as { addEventListener?: unknown }).addEventListener ===\n\t\t\t\"function\" &&\n\t\ttypeof (value as { removeEventListener?: unknown }).removeEventListener ===\n\t\t\t\"function\"\n\t);\n}\n\nfunction isPageResumeWindowTarget(\n\tvalue: unknown,\n): value is PageResumeWindowTarget {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\ttypeof (value as { addEventListener?: unknown }).addEventListener ===\n\t\t\t\"function\" &&\n\t\ttypeof (value as { removeEventListener?: unknown }).removeEventListener ===\n\t\t\t\"function\"\n\t);\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<void> {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"then\" in value &&\n\t\ttypeof (value as { then?: unknown }).then === \"function\"\n\t);\n}\n","// Popup shared infrastructure — browser-only helpers\n//\n// Provides cross-mode popup window management for login flows.\n// Owned by @securitydept/client/web, consumed by backend-oidc-mode\n// and frontend-oidc-mode popup login entries.\n\nimport { ClientError, ClientErrorKind, UserRecovery } from \"../errors/index\";\n\n// ---------------------------------------------------------------------------\n// Error codes (stable contract)\n// ---------------------------------------------------------------------------\n\n/** Stable error codes for popup-related failures. */\nexport const PopupErrorCode = {\n\t/** The browser blocked the popup window from opening. */\n\tBlocked: \"popup.blocked\",\n\t/** The popup was closed by the user before completing the flow. */\n\tClosed: \"popup.closed_by_user\",\n\t/** The relay message was not received within the timeout. */\n\tTimeout: \"popup.relay_timeout\",\n\t/** The relay message contained an error from the callback page. */\n\tRelayError: \"popup.relay_error\",\n\t/** An unexpected error occurred during popup lifecycle. */\n\tInternal: \"popup.internal\",\n} as const;\n\nexport type PopupErrorCode =\n\t(typeof PopupErrorCode)[keyof typeof PopupErrorCode];\n\n// ---------------------------------------------------------------------------\n// Popup features\n// ---------------------------------------------------------------------------\n\n/**\n * Options for computing the popup window features string.\n */\nexport interface PopupFeaturesOptions {\n\t/** Width in pixels (default: 500). */\n\twidth?: number;\n\t/** Height in pixels (default: 600). */\n\theight?: number;\n}\n\n/**\n * Compute the `features` string for `window.open()`, centered on screen.\n */\nexport function computePopupFeatures(\n\toptions: PopupFeaturesOptions = {},\n): string {\n\tconst width = options.width ?? 500;\n\tconst height = options.height ?? 600;\n\tconst left = Math.max(\n\t\t0,\n\t\tMath.round(window.screenX + (window.innerWidth - width) / 2),\n\t);\n\tconst top = Math.max(\n\t\t0,\n\t\tMath.round(window.screenY + (window.innerHeight - height) / 2),\n\t);\n\n\treturn `width=${width},height=${height},left=${left},top=${top},popup=yes,toolbar=no,menubar=no`;\n}\n\n// ---------------------------------------------------------------------------\n// Popup open + blocked detection\n// ---------------------------------------------------------------------------\n\n/**\n * Result of opening a popup window.\n */\nexport interface PopupWindowHandle {\n\t/** The opened window reference. */\n\twindow: Window;\n}\n\n/**\n * Open a popup window to the given URL.\n *\n * Throws a `ClientError` with code `popup.blocked` if the browser\n * blocks the popup.\n */\nexport function openPopupWindow(\n\turl: string,\n\toptions?: PopupFeaturesOptions & { target?: string },\n): PopupWindowHandle {\n\tconst features = computePopupFeatures(options);\n\tconst target = options?.target ?? \"_blank\";\n\n\tconst win = window.open(url, target, features);\n\n\tif (!win || win.closed) {\n\t\tthrow new ClientError({\n\t\t\tkind: ClientErrorKind.Authorization,\n\t\t\tcode: PopupErrorCode.Blocked,\n\t\t\tmessage:\n\t\t\t\t\"Popup window was blocked by the browser. Please allow popups for this site.\",\n\t\t\trecovery: UserRecovery.Retry,\n\t\t\tsource: \"popup\",\n\t\t});\n\t}\n\n\treturn { window: win };\n}\n\n// ---------------------------------------------------------------------------\n// PostMessage relay\n// ---------------------------------------------------------------------------\n\n/**\n * Expected shape of a relay message from the popup callback page.\n */\nexport interface PopupRelayMessage {\n\t/** Discriminator for the message type. */\n\ttype: \"securitydept:popup_callback\";\n\t/** The callback URL or fragment received by the popup. */\n\tpayload: string;\n\t/** Optional error from the callback page. */\n\terror?: string;\n}\n\n/**\n * Options for waiting for a popup relay message.\n */\nexport interface WaitForPopupRelayOptions {\n\t/** The popup window handle to monitor. */\n\tpopup: PopupWindowHandle;\n\t/** Maximum time to wait in milliseconds (default: 120000 = 2 minutes). */\n\ttimeoutMs?: number;\n\t/** Expected origin for the relay message (default: current origin). */\n\texpectedOrigin?: string;\n\t/** Poll interval for checking if the popup is closed (default: 500ms). */\n\tpollIntervalMs?: number;\n}\n\n/**\n * Wait for a relay message from a popup callback page.\n *\n * The popup callback page should call `relayPopupCallback()` to post\n * the callback URL back to the opener.\n *\n * Resolves with the callback payload (URL or fragment string).\n * Rejects with a `ClientError` if:\n * - the popup is closed without relaying (popup.closed_by_user)\n * - the timeout expires (popup.relay_timeout)\n * - the relay message contains an error (popup.relay_error)\n */\nexport function waitForPopupRelay(\n\toptions: WaitForPopupRelayOptions,\n): Promise<string> {\n\tconst timeoutMs = options.timeoutMs ?? 120_000;\n\tconst expectedOrigin = options.expectedOrigin ?? window.location.origin;\n\tconst pollIntervalMs = options.pollIntervalMs ?? 500;\n\n\treturn new Promise<string>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timeoutId: ReturnType<typeof setTimeout>;\n\t\tlet pollId: ReturnType<typeof setInterval>;\n\n\t\tfunction cleanup() {\n\t\t\tsettled = true;\n\t\t\tclearTimeout(timeoutId);\n\t\t\tclearInterval(pollId);\n\t\t\twindow.removeEventListener(\"message\", onMessage);\n\t\t}\n\n\t\tfunction onMessage(event: MessageEvent) {\n\t\t\tif (settled) return;\n\t\t\tif (event.origin !== expectedOrigin) return;\n\n\t\t\tconst data = event.data as PopupRelayMessage | undefined;\n\t\t\tif (!data || data.type !== \"securitydept:popup_callback\") return;\n\n\t\t\tcleanup();\n\n\t\t\tif (data.error) {\n\t\t\t\treject(\n\t\t\t\t\tnew ClientError({\n\t\t\t\t\t\tkind: ClientErrorKind.Protocol,\n\t\t\t\t\t\tcode: PopupErrorCode.RelayError,\n\t\t\t\t\t\tmessage: `Popup callback relay error: ${data.error}`,\n\t\t\t\t\t\tsource: \"popup\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tresolve(data.payload);\n\t\t}\n\n\t\t// Listen for relay messages.\n\t\twindow.addEventListener(\"message\", onMessage);\n\n\t\t// Poll for popup closure.\n\t\tpollId = setInterval(() => {\n\t\t\tif (settled) return;\n\t\t\tif (options.popup.window.closed) {\n\t\t\t\tcleanup();\n\t\t\t\treject(\n\t\t\t\t\tnew ClientError({\n\t\t\t\t\t\tkind: ClientErrorKind.Authorization,\n\t\t\t\t\t\tcode: PopupErrorCode.Closed,\n\t\t\t\t\t\tmessage:\n\t\t\t\t\t\t\t\"Popup window was closed before completing the login flow.\",\n\t\t\t\t\t\trecovery: UserRecovery.RestartFlow,\n\t\t\t\t\t\tsource: \"popup\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}, pollIntervalMs);\n\n\t\t// Timeout.\n\t\ttimeoutId = setTimeout(() => {\n\t\t\tif (settled) return;\n\t\t\tcleanup();\n\t\t\ttry {\n\t\t\t\toptions.popup.window.close();\n\t\t\t} catch {\n\t\t\t\t// Best-effort close.\n\t\t\t}\n\t\t\treject(\n\t\t\t\tnew ClientError({\n\t\t\t\t\tkind: ClientErrorKind.Timeout,\n\t\t\t\t\tcode: PopupErrorCode.Timeout,\n\t\t\t\t\tmessage: `Popup login timed out after ${timeoutMs}ms.`,\n\t\t\t\t\trecovery: UserRecovery.Retry,\n\t\t\t\t\tsource: \"popup\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}, timeoutMs);\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Popup callback relay (for use in popup callback pages)\n// ---------------------------------------------------------------------------\n\n/**\n * Options for relaying the callback from the popup window back to the opener.\n */\nexport interface RelayPopupCallbackOptions {\n\t/** The callback payload (URL or fragment) to relay. */\n\tpayload?: string;\n\t/** An error message to relay instead of a successful payload. */\n\terror?: string;\n\t/** Whether to close the popup after relaying (default: true). */\n\tcloseAfterRelay?: boolean;\n\t/** Target origin for the postMessage (default: current origin). */\n\ttargetOrigin?: string;\n}\n\n/**\n * Relay the popup callback result back to the opener window.\n *\n * Call this function from the popup callback page to send the callback\n * URL/fragment back to the parent window that initiated the popup login.\n *\n * @example\n * ```html\n * <script type=\"module\">\n * import { relayPopupCallback } from \"@securitydept/client/web\";\n * relayPopupCallback({ payload: window.location.href });\n * </script>\n * ```\n */\nexport function relayPopupCallback(\n\toptions: RelayPopupCallbackOptions = {},\n): void {\n\tconst opener = window.opener;\n\tif (!opener) {\n\t\treturn;\n\t}\n\n\tconst targetOrigin = options.targetOrigin ?? window.location.origin;\n\tconst message: PopupRelayMessage = {\n\t\ttype: \"securitydept:popup_callback\",\n\t\tpayload: options.payload ?? window.location.href,\n\t\terror: options.error,\n\t};\n\n\topener.postMessage(message, targetOrigin);\n\n\tif (options.closeAfterRelay !== false) {\n\t\tsetTimeout(() => {\n\t\t\twindow.close();\n\t\t}, 0);\n\t}\n}\n","import { createRuntime } from \"../runtime/create-runtime\";\nimport type { ClientRuntime } from \"../runtime/types\";\nimport {\n\tcreateFetchTransport,\n\ttype FetchTransportOptions,\n} from \"../transport/fetch-transport\";\n\nexport interface CreateWebRuntimeOptions\n\textends Partial<Omit<ClientRuntime, \"transport\">> {\n\ttransport?: ClientRuntime[\"transport\"];\n\tfetchTransport?: FetchTransportOptions;\n}\n\n/**\n * Create a `ClientRuntime` for browser/Web-capable environments.\n */\nexport function createWebRuntime(\n\toverrides: CreateWebRuntimeOptions = {},\n): ClientRuntime {\n\tconst { fetchTransport, transport, ...runtime } = overrides;\n\treturn createRuntime({\n\t\t...runtime,\n\t\ttransport: transport ?? createFetchTransport(fetchTransport),\n\t});\n}\n","// Browser visibility change adapter — built on fromEventPattern.\n//\n// Adapts the browser `visibilitychange` event into the SDK's Subscription\n// model, built on the foundation fromEventPattern helper.\n\nimport { fromEventPattern, type Subscription } from \"../scheduling/helpers\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Visibility state snapshot passed to the callback. */\nexport const VisibilityState = {\n\tVisible: \"visible\",\n\tHidden: \"hidden\",\n} as const;\n\nexport type VisibilityState =\n\t(typeof VisibilityState)[keyof typeof VisibilityState];\n\n/** Options for {@link fromVisibilityChange}. */\nexport interface FromVisibilityChangeOptions {\n\t/**\n\t * Callback invoked when the document visibility state changes.\n\t *\n\t * The callback receives the new visibility state (\"visible\" or \"hidden\").\n\t */\n\tcallback: (state: VisibilityState) => void;\n\t/**\n\t * Override the document target (primarily for testing).\n\t * Defaults to `globalThis.document`.\n\t */\n\tdocument?: Pick<\n\t\tDocument,\n\t\t\"addEventListener\" | \"removeEventListener\" | \"visibilityState\"\n\t>;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Subscribe to browser document visibility changes.\n *\n * Built on {@link fromEventPattern} so it follows the same Subscription\n * contract. Returns a {@link Subscription} handle for cleanup.\n *\n * @example\n * ```ts\n * const sub = fromVisibilityChange({\n * callback: (state) => {\n * if (state === VisibilityState.Visible) {\n * scheduleRefresh();\n * }\n * },\n * });\n * // Cleanup:\n * sub.unsubscribe();\n * ```\n */\nexport function fromVisibilityChange(\n\toptions: FromVisibilityChangeOptions,\n): Subscription {\n\tconst doc = options.document ?? globalThis.document;\n\n\treturn fromEventPattern<Event>({\n\t\taddHandler: (handler) => {\n\t\t\tdoc.addEventListener(\"visibilitychange\", handler);\n\t\t},\n\t\tremoveHandler: (handler) => {\n\t\t\tdoc.removeEventListener(\"visibilitychange\", handler);\n\t\t},\n\t\tcallback: () => {\n\t\t\tconst state: VisibilityState =\n\t\t\t\tdoc.visibilityState === \"visible\"\n\t\t\t\t\t? VisibilityState.Visible\n\t\t\t\t\t: VisibilityState.Hidden;\n\t\t\toptions.callback(state);\n\t\t},\n\t});\n}\n","// Visibility reconciler — cross-tab / visibility lifecycle hardening baseline\n//\n// Provides a minimal, composable integration between the browser visibility\n// change adapter and the auth material lifecycle. When a tab transitions from\n// hidden → visible, the reconciler triggers a user-supplied reconciliation\n// callback (e.g. check token freshness, re-validate with server, or sync\n// cross-tab state).\n//\n// This is the SDK-side baseline that real projects (e.g. outposts) can wire\n// directly to their mode client's refresh or state-check logic.\n\nimport type { Subscription } from \"../scheduling/helpers\";\nimport type { FromVisibilityChangeOptions } from \"./visibility\";\nimport { fromVisibilityChange, VisibilityState } from \"./visibility\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Callback invoked when the page becomes visible and reconciliation is needed. */\nexport type ReconcileCallback = () => void | Promise<void>;\n\n/** Options for {@link createVisibilityReconciler}. */\nexport interface CreateVisibilityReconcilerOptions {\n\t/**\n\t * Called when the document transitions from hidden → visible.\n\t * The reconciler only fires on the hidden→visible edge; repeated\n\t * visible→visible events are suppressed.\n\t */\n\tonReconcile: ReconcileCallback;\n\n\t/**\n\t * Minimum interval (ms) between reconciliation triggers.\n\t * Prevents rapid-fire reconciliation when the user toggles tabs quickly.\n\t * Defaults to 5000ms (5 seconds).\n\t */\n\tthrottleMs?: number;\n\n\t/**\n\t * Override the document target (primarily for testing).\n\t * Defaults to `globalThis.document`.\n\t */\n\tdocument?: FromVisibilityChangeOptions[\"document\"];\n\n\t/**\n\t * Clock override for testing.\n\t */\n\tnow?: () => number;\n}\n\n/** Handle returned by {@link createVisibilityReconciler}. */\nexport interface VisibilityReconciler {\n\t/** Stop listening for visibility changes. */\n\tdispose(): void;\n\t/** Number of times reconciliation has been triggered. */\n\treadonly reconcileCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Create a visibility reconciler that triggers a callback when the page\n * transitions from hidden → visible.\n *\n * @example\n * ```ts\n * const reconciler = createVisibilityReconciler({\n * onReconcile: () => {\n * // Re-check token freshness or sync cross-tab state\n * if (controller.snapshot?.tokens.accessTokenExpiresAt) {\n * scheduleRefreshIfExpired();\n * }\n * },\n * });\n * // Later:\n * reconciler.dispose();\n * ```\n */\nexport function createVisibilityReconciler(\n\toptions: CreateVisibilityReconcilerOptions,\n): VisibilityReconciler {\n\tconst throttleMs = options.throttleMs ?? 5000;\n\tconst getNow = options.now ?? (() => Date.now());\n\n\tlet lastReconcileAt = 0;\n\tlet reconcileCount = 0;\n\t// Initialize from the real document state so that tabs opened in background\n\t// (initial hidden) will correctly trigger on their first visible transition.\n\tconst doc = options.document ?? globalThis.document;\n\tlet previousState: VisibilityState =\n\t\tdoc.visibilityState === \"visible\"\n\t\t\t? VisibilityState.Visible\n\t\t\t: VisibilityState.Hidden;\n\n\tconst subscription: Subscription = fromVisibilityChange({\n\t\tcallback: (state) => {\n\t\t\t// Only fire on the hidden → visible edge.\n\t\t\tif (\n\t\t\t\tstate === VisibilityState.Visible &&\n\t\t\t\tpreviousState === VisibilityState.Hidden\n\t\t\t) {\n\t\t\t\tconst now = getNow();\n\t\t\t\tif (now - lastReconcileAt >= throttleMs) {\n\t\t\t\t\tlastReconcileAt = now;\n\t\t\t\t\treconcileCount++;\n\t\t\t\t\toptions.onReconcile();\n\t\t\t\t}\n\t\t\t}\n\t\t\tpreviousState = state;\n\t\t},\n\t\tdocument: doc,\n\t});\n\n\treturn {\n\t\tdispose() {\n\t\t\tsubscription.unsubscribe();\n\t\t},\n\t\tget reconcileCount() {\n\t\t\treturn reconcileCount;\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAqFA,SAAgB,mBACf,SACe;CACf,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI,YAAY;CAChB,MAAM,eAAe,iBAAiB;EACrC;EACA,WAAW,iBAAiB;AAE3B,OAAI,aAAa,QAAQ,QAAQ,IAChC;AAED;AACA,WAAQ,OAAO;IACd,UAAU,aAAa;IACvB,UAAU,aAAa;IACvB,CAAC;;EAEH,CAAC;AAEF,QAAO;EACN,UAAU;AACT,gBAAa,aAAa;;EAE3B,IAAI,YAAY;AACf,UAAO;;EAER;;;;AC9GF,MAAa,wBAAwB;CACpC,YAAY;CACZ,UAAU;CACV,OAAO;CACP,QAAQ;CACR;AAgDD,SAAgB,2BACf,SACuB;CACvB,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,SAAS,QAAQ,cAAc,KAAK,KAAK;CAC/C,MAAM,MAAM,QAAQ,YAAY,eAAe;CAC/C,MAAM,MAAM,QAAQ,UAAU,aAAa;CAE3C,IAAI,0BACH,KAAK,mBAAmB;CACzB,IAAI,kBAAkB;CACtB,IAAI,iBAAiB;CACrB,IAAI,WAAW;CACf,MAAM,WAAsB,EAAE;CAE9B,MAAM,aAAa,UAAiC;AACnD,MAAI,SAAU;EACd,MAAM,MAAM,QAAQ;AACpB,MAAI,MAAM,kBAAkB,WAAY;AAExC,oBAAkB;AAClB,oBAAkB;AAClB,MAAI;GACH,MAAM,SAAS,QAAQ,YAAY,MAAM;AACzC,OAAI,cAAc,OAAO,CACxB,QAAO,OAAO,UAAU,QAAQ,UAAU,OAAO,MAAM,CAAC;WAEjD,OAAO;AACf,WAAQ,UAAU,OAAO,MAAM;;;AAIjC,KAAI,KAAK;EACR,MAAM,gBAA+B;GACpC,MAAM,sBAAsB,IAAI;AAChC,OACC,wBAAwB,aACxB,4BAA4B,UAE5B,WAAU,EAAE,SAAS,sBAAsB,YAAY,CAAC;AAEzD,6BAA0B;;AAE3B,MAAI,iBAAiB,oBAAoB,QAAQ;AACjD,WAAS,WAAW,IAAI,oBAAoB,oBAAoB,QAAQ,CAAC;;AAG1E,KAAI,KAAK;EACR,MAAM,mBAAkC,UAAU;AACjD,aAAU;IACT,SAAS,sBAAsB;IAC/B,WAAW,QAAS,MAA8B,UAAU;IAC5D,CAAC;;EAEH,MAAM,qBAAoC;AACzC,aAAU,EAAE,SAAS,sBAAsB,OAAO,CAAC;;EAEpD,MAAM,sBAAqC;AAC1C,aAAU,EAAE,SAAS,sBAAsB,QAAQ,CAAC;;AAGrD,MAAI,iBAAiB,YAAY,gBAAgB;AACjD,MAAI,iBAAiB,SAAS,aAAa;AAC3C,MAAI,iBAAiB,UAAU,cAAc;AAC7C,WAAS,WAAW,IAAI,oBAAoB,YAAY,gBAAgB,CAAC;AACzE,WAAS,WAAW,IAAI,oBAAoB,SAAS,aAAa,CAAC;AACnE,WAAS,WAAW,IAAI,oBAAoB,UAAU,cAAc,CAAC;;AAGtE,QAAO;EACN,UAAU;AACT,OAAI,SAAU;AACd,cAAW;AACX,QAAK,MAAM,WAAW,SAAS,OAAO,EAAE,CACvC,UAAS;;EAGX,IAAI,iBAAiB;AACpB,UAAO;;EAER;;AAGF,SAAS,gBAAiD;CACzD,MAAM,iBAAiB,WAAW;AAClC,QAAO,2BAA2B,eAAe,GAAG,iBAAiB;;AAGtE,SAAS,cAA6C;CACrD,MAAM,eAAe,WAAW;AAChC,QAAO,yBAAyB,aAAa,GAAG,eAAe;;AAGhE,SAAS,2BACR,OACoC;AACpC,QACC,OAAO,UAAU,YACjB,UAAU,QACV,qBAAqB,SACrB,OAAQ,MAAyC,qBAChD,cACD,OAAQ,MAA4C,wBACnD;;AAIH,SAAS,yBACR,OACkC;AAClC,QACC,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAyC,qBAChD,cACD,OAAQ,MAA4C,wBACnD;;AAIH,SAAS,cAAc,OAA4C;AAClE,QACC,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAQ,MAA6B,SAAS;;;;;ACvKhD,MAAa,iBAAiB;CAE7B,SAAS;CAET,QAAQ;CAER,SAAS;CAET,YAAY;CAEZ,UAAU;CACV;;;;AAsBD,SAAgB,qBACf,UAAgC,EAAE,EACzB;CACT,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,SAAS,QAAQ,UAAU;AAUjC,QAAO,SAAS,MAAM,UAAU,OAAO,QAT1B,KAAK,IACjB,GACA,KAAK,MAAM,OAAO,WAAW,OAAO,aAAa,SAAS,EAAE,CAC5D,CAMmD,OALxC,KAAK,IAChB,GACA,KAAK,MAAM,OAAO,WAAW,OAAO,cAAc,UAAU,EAAE,CAC9D,CAE8D;;;;;;;;AAqBhE,SAAgB,gBACf,KACA,SACoB;CACpB,MAAM,WAAW,qBAAqB,QAAQ;CAC9C,MAAM,SAAS,SAAS,UAAU;CAElC,MAAM,MAAM,OAAO,KAAK,KAAK,QAAQ,SAAS;AAE9C,KAAI,CAAC,OAAO,IAAI,OACf,OAAM,IAAI,YAAY;EACrB,MAAM,gBAAgB;EACtB,MAAM,eAAe;EACrB,SACC;EACD,UAAU,aAAa;EACvB,QAAQ;EACR,CAAC;AAGH,QAAO,EAAE,QAAQ,KAAK;;;;;;;;;;;;;;AA6CvB,SAAgB,kBACf,SACkB;CAClB,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,iBAAiB,QAAQ,kBAAkB,OAAO,SAAS;CACjE,MAAM,iBAAiB,QAAQ,kBAAkB;AAEjD,QAAO,IAAI,SAAiB,SAAS,WAAW;EAC/C,IAAI,UAAU;EACd,IAAI;EACJ,IAAI;EAEJ,SAAS,UAAU;AAClB,aAAU;AACV,gBAAa,UAAU;AACvB,iBAAc,OAAO;AACrB,UAAO,oBAAoB,WAAW,UAAU;;EAGjD,SAAS,UAAU,OAAqB;AACvC,OAAI,QAAS;AACb,OAAI,MAAM,WAAW,eAAgB;GAErC,MAAM,OAAO,MAAM;AACnB,OAAI,CAAC,QAAQ,KAAK,SAAS,8BAA+B;AAE1D,YAAS;AAET,OAAI,KAAK,OAAO;AACf,WACC,IAAI,YAAY;KACf,MAAM,gBAAgB;KACtB,MAAM,eAAe;KACrB,SAAS,+BAA+B,KAAK;KAC7C,QAAQ;KACR,CAAC,CACF;AACD;;AAGD,WAAQ,KAAK,QAAQ;;AAItB,SAAO,iBAAiB,WAAW,UAAU;AAG7C,WAAS,kBAAkB;AAC1B,OAAI,QAAS;AACb,OAAI,QAAQ,MAAM,OAAO,QAAQ;AAChC,aAAS;AACT,WACC,IAAI,YAAY;KACf,MAAM,gBAAgB;KACtB,MAAM,eAAe;KACrB,SACC;KACD,UAAU,aAAa;KACvB,QAAQ;KACR,CAAC,CACF;;KAEA,eAAe;AAGlB,cAAY,iBAAiB;AAC5B,OAAI,QAAS;AACb,YAAS;AACT,OAAI;AACH,YAAQ,MAAM,OAAO,OAAO;WACrB;AAGR,UACC,IAAI,YAAY;IACf,MAAM,gBAAgB;IACtB,MAAM,eAAe;IACrB,SAAS,+BAA+B,UAAU;IAClD,UAAU,aAAa;IACvB,QAAQ;IACR,CAAC,CACF;KACC,UAAU;GACZ;;;;;;;;;;;;;;;;AAmCH,SAAgB,mBACf,UAAqC,EAAE,EAChC;CACP,MAAM,SAAS,OAAO;AACtB,KAAI,CAAC,OACJ;CAGD,MAAM,eAAe,QAAQ,gBAAgB,OAAO,SAAS;CAC7D,MAAM,UAA6B;EAClC,MAAM;EACN,SAAS,QAAQ,WAAW,OAAO,SAAS;EAC5C,OAAO,QAAQ;EACf;AAED,QAAO,YAAY,SAAS,aAAa;AAEzC,KAAI,QAAQ,oBAAoB,MAC/B,kBAAiB;AAChB,SAAO,OAAO;IACZ,EAAE;;;;;;;AC5QP,SAAgB,iBACf,YAAqC,EAAE,EACvB;CAChB,MAAM,EAAE,gBAAgB,WAAW,GAAG,YAAY;AAClD,QAAO,cAAc;EACpB,GAAG;EACH,WAAW,aAAa,qBAAqB,eAAe;EAC5D,CAAC;;;;;ACXH,MAAa,kBAAkB;CAC9B,SAAS;CACT,QAAQ;CACR;;;;;;;;;;;;;;;;;;;;AA8CD,SAAgB,qBACf,SACe;CACf,MAAM,MAAM,QAAQ,YAAY,WAAW;AAE3C,QAAO,iBAAwB;EAC9B,aAAa,YAAY;AACxB,OAAI,iBAAiB,oBAAoB,QAAQ;;EAElD,gBAAgB,YAAY;AAC3B,OAAI,oBAAoB,oBAAoB,QAAQ;;EAErD,gBAAgB;GACf,MAAM,QACL,IAAI,oBAAoB,YACrB,gBAAgB,UAChB,gBAAgB;AACpB,WAAQ,SAAS,MAAM;;EAExB,CAAC;;;;;;;;;;;;;;;;;;;;;;ACAH,SAAgB,2BACf,SACuB;CACvB,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,SAAS,QAAQ,cAAc,KAAK,KAAK;CAE/C,IAAI,kBAAkB;CACtB,IAAI,iBAAiB;CAGrB,MAAM,MAAM,QAAQ,YAAY,WAAW;CAC3C,IAAI,gBACH,IAAI,oBAAoB,YACrB,gBAAgB,UAChB,gBAAgB;CAEpB,MAAM,eAA6B,qBAAqB;EACvD,WAAW,UAAU;AAEpB,OACC,UAAU,gBAAgB,WAC1B,kBAAkB,gBAAgB,QACjC;IACD,MAAM,MAAM,QAAQ;AACpB,QAAI,MAAM,mBAAmB,YAAY;AACxC,uBAAkB;AAClB;AACA,aAAQ,aAAa;;;AAGvB,mBAAgB;;EAEjB,UAAU;EACV,CAAC;AAEF,QAAO;EACN,UAAU;AACT,gBAAa,aAAa;;EAE3B,IAAI,iBAAiB;AACpB,UAAO;;EAER"}
@@ -1,4 +1,4 @@
1
- import { i as PlannerHost, s as RequirementsClientSetComposition, t as AuthGuardClientOption } from "../planner-host-DUZCkLj3.mjs";
1
+ import { i as PlannerHost, s as RequirementsClientSetComposition, t as AuthGuardClientOption } from "../planner-host-CPyGNyQV.mjs";
2
2
 
3
3
  //#region src/web-router/navigation-adapter.d.ts
4
4
  declare const NavigationAdapterKind: {
@@ -1,4 +1,4 @@
1
- import { r as resolveEffectiveClientSet, t as RequirementsClientSetComposition } from "../planner-host-CcEpcHws.mjs";
1
+ import { r as resolveEffectiveClientSet, t as RequirementsClientSetComposition } from "../planner-host-CV38bMSn.mjs";
2
2
  //#region src/web-router/navigation-adapter.ts
3
3
  const NavigationAdapterKind = {
4
4
  NavigationApi: "navigation-api",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securitydept/client",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.5",
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",
@@ -44,6 +44,10 @@
44
44
  "types": "./dist/web/index.d.mts",
45
45
  "import": "./dist/web/index.mjs"
46
46
  },
47
+ "./events": {
48
+ "types": "./dist/events/index.d.mts",
49
+ "import": "./dist/events/index.mjs"
50
+ },
47
51
  "./auth-coordination": {
48
52
  "types": "./dist/auth-coordination/index.d.mts",
49
53
  "import": "./dist/auth-coordination/index.mjs"
@@ -57,7 +61,8 @@
57
61
  "dist"
58
62
  ],
59
63
  "dependencies": {
60
- "@standard-schema/spec": "^1.0.0"
64
+ "@standard-schema/spec": "^1.0.0",
65
+ "rxjs": "^7.8.2"
61
66
  },
62
67
  "scripts": {
63
68
  "build": "tsdown",