@athenaintel/react 0.26.6 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/diagnostics/errors.cjs +11 -2
- package/dist/diagnostics/errors.cjs.map +1 -1
- package/dist/diagnostics/errors.d.ts +11 -2
- package/dist/diagnostics/errors.js +11 -2
- package/dist/diagnostics/errors.js.map +1 -1
- package/dist/packages/react/package.json.cjs +1 -1
- package/dist/packages/react/package.json.js +1 -1
- package/dist/provider/AthenaProvider.cjs +3 -0
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +3 -0
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/runtime/auth.cjs +219 -104
- package/dist/runtime/auth.cjs.map +1 -1
- package/dist/runtime/auth.d.ts +79 -5
- package/dist/runtime/auth.js +219 -104
- package/dist/runtime/auth.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sources":["../../src/runtime/auth.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { AthenaTokenRenewer } from '../auth/credential-source';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\n\n/**\n * Check whether a PostMessage origin is trusted.\n * Accepts:\n * - explicit trusted origins passed by the host app\n * - the current iframe origin (for same-origin wrappers like Marathon)\n * - any *.athenaintel.com subdomain and localhost (legacy/default behavior)\n */\nfunction isTrustedOrigin({\n origin,\n trustedOrigins,\n}: {\n origin: string;\n trustedOrigins: string[];\n}): boolean {\n try {\n const normalizedOrigin = new URL(origin).origin;\n if (trustedOrigins.includes(normalizedOrigin)) {\n return true;\n }\n\n const { hostname } = new URL(normalizedOrigin);\n return (\n hostname === 'athenaintel.com' ||\n hostname.endsWith('.athenaintel.com') ||\n hostname === 'localhost'\n );\n } catch {\n return false;\n }\n}\n\nexport interface ParentBridgeState {\n /** PropelAuth access token from the parent window. */\n token: string | null;\n /**\n * Statewire sync mount (`…/v2/threads`) minted by the host. `null` when the\n * host minted none; the provider then derives it from the configured\n * `apiUrl`'s origin (or the environment preset).\n */\n statewireSyncUrl: string | null;\n /**\n * Bare Agora API base (no path) minted by the host. `null` when the host\n * minted none; the provider then uses the configured `backendUrl` (or the\n * environment preset).\n */\n backendBaseUrl: string | null;\n /** Athena frontend origin provided by the parent wrapper. */\n appUrl: string | null;\n /**\n * True once a host answered the bridge — a `/_athena/auth` mint or an\n * `athena-config` message — whether or not it minted the statewire fields.\n * Lets the provider tell an outdated host (answered, no mount) from no host\n * at all (the environment preset applies).\n */\n hostAnswered: boolean;\n /** True once config has been received from the parent (or timeout fired). */\n ready: boolean;\n}\n\n/**\n * {@link ParentBridgeState} plus the bridge's token renewal: `force` (the\n * token was refused) asks the host for a new one — a fresh `/_athena/auth`\n * mint on a Marathon origin, or an `athena-auth-ready` request answered by the\n * parent window's next `athena-auth` — and resolves the held token otherwise.\n */\nexport type ParentBridge = ParentBridgeState & { renewToken: AthenaTokenRenewer };\n\nconst BRIDGE_URL_FIELDS = ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'] as const;\n\ntype BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];\n\n/** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */\nexport type ParentBridgeUrls = Pick<ParentBridgeState, BridgeUrlField>;\n\nconst EMPTY_BRIDGE_URLS: ParentBridgeUrls = {\n statewireSyncUrl: null,\n backendBaseUrl: null,\n appUrl: null,\n};\n\n/**\n * Fold an `athena-config` postMessage payload over the current URL set. A\n * field is taken only when it is a string; anything else keeps the previous\n * value. Keys outside {@link BridgeUrlField} (a legacy parent's `apiUrl` /\n * `backendUrl` among them) are dropped.\n */\nexport function mergeBridgeConfigMessage(\n previous: ParentBridgeUrls,\n payload: Partial<Record<BridgeUrlField, unknown>>,\n): ParentBridgeUrls {\n const next: ParentBridgeUrls = {\n statewireSyncUrl: previous.statewireSyncUrl,\n backendBaseUrl: previous.backendBaseUrl,\n appUrl: previous.appUrl,\n };\n for (const field of BRIDGE_URL_FIELDS) {\n const value = payload[field];\n if (typeof value === 'string') {\n next[field] = value;\n }\n }\n return next;\n}\n\n/**\n * Read the URL set out of a `/_athena/auth` mint body. Non-string fields are\n * `null`.\n */\nexport function readAuthMintUrls(data: Partial<Record<BridgeUrlField, unknown>>): ParentBridgeUrls {\n return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);\n}\n\nexport interface ParentBridgeOptions {\n /**\n * Exact parent origins that are allowed to send auth/config via postMessage.\n * Useful for private-cloud or custom-domain deployments.\n */\n trustedOrigins?: string[];\n}\n\n/** How long to wait for the parent bridge before falling back to defaults. */\nconst BRIDGE_TIMEOUT_MS = 2000;\n/** How long a renewal waits for the parent window to answer `athena-auth-ready`. */\nconst PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5000;\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\nconst TOKEN_REFRESH_RETRY_BASE_MS = 60 * 1000;\nconst TOKEN_REFRESH_RETRY_MAX_MS = 5 * 60 * 1000;\nconst DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1000;\nconst EMPTY_TRUSTED_ORIGINS: string[] = [];\n\n/** Untrusted shape of the `/_athena/auth` mint body; every field is type-guarded on read. */\ninterface AthenaAuthResponse {\n token?: unknown;\n statewireSyncUrl?: unknown;\n backendBaseUrl?: unknown;\n appUrl?: unknown;\n expires_at?: unknown;\n}\n\nconst normalizeOrigin = (value: string): string | null => {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nexport const getTokenRefreshDelay = (expiresAt: unknown, nowMs: number): number => {\n if (typeof expiresAt !== 'string') {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n const expiresAtMs = Date.parse(expiresAt);\n if (!Number.isFinite(expiresAtMs)) {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n return Math.max(TOKEN_REFRESH_RETRY_BASE_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);\n};\n\nexport const getAuthRetryDelay = (retryAttempt: number): number => {\n const safeAttempt = Number.isFinite(retryAttempt) ? Math.max(0, Math.floor(retryAttempt)) : 0;\n return Math.min(TOKEN_REFRESH_RETRY_MAX_MS, TOKEN_REFRESH_RETRY_BASE_MS * 2 ** safeAttempt);\n};\n\n/**\n * Listens for auth tokens AND environment config from the host environment.\n *\n * Two transport modes, picked automatically:\n *\n * 1. **Iframe (legacy / Olympus inline preview)**: a parent window posts\n * `athena-config` and `athena-auth` via PostMessage. The hook also posts\n * `athena-auth-ready` so the parent knows when to (re-)send.\n *\n * 2. **Standalone (Marathon-served deploy URL)**: top-level navigation, no\n * parent. The hook fetches `/_athena/auth` on its own origin. Marathon\n * validates its session cookie (sent by the browser automatically) and\n * returns `{ token, statewireSyncUrl, backendBaseUrl, appUrl }` — the\n * statewire mount, the bare Agora base and the app origin. A 404 means\n * we're not on a Marathon origin — the hook falls through with empty\n * state.\n *\n * In both modes a timeout safety net flips `ready: true` so the app never\n * blocks indefinitely.\n */\nexport function useParentBridge({\n trustedOrigins = EMPTY_TRUSTED_ORIGINS,\n}: ParentBridgeOptions = {}): ParentBridge {\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const runtimeTrustedOrigins = useMemo(() => {\n const origins = new Set<string>();\n\n if (typeof window !== 'undefined') {\n origins.add(window.location.origin);\n }\n\n for (const trustedOrigin of trustedOrigins) {\n const normalizedOrigin = normalizeOrigin(trustedOrigin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n }\n }\n\n return [...origins];\n }, [trustedOrigins]);\n\n const [state, setState] = useState<ParentBridgeState>({\n token: null,\n ...EMPTY_BRIDGE_URLS,\n hostAnswered: false,\n ready: false,\n });\n const readySignalSent = useRef(false);\n const configReceived = useRef(false);\n const tokenRef = useRef<string | null>(state.token);\n tokenRef.current = state.token;\n // Installed by whichever transport mode is active (iframe or Marathon).\n const renewerRef = useRef<AthenaTokenRenewer | null>(null);\n\n useEffect(() => {\n if (!isInIframe) return;\n\n const endBridgeSpan = athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', {\n mode: 'iframe',\n });\n let spanEnded = false;\n const endOnce: typeof endBridgeSpan = (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return endBridgeSpan(name, options);\n };\n\n // Renewals waiting for the parent's next `athena-auth`.\n const tokenWaiters = new Set<(token: string | null) => void>();\n\n const handler = (event: MessageEvent) => {\n if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {\n return;\n }\n if (!event.data || typeof event.data !== 'object') return;\n\n if (event.data.type === 'athena-config') {\n configReceived.current = true;\n setState((prev) => ({\n ...prev,\n ...mergeBridgeConfigMessage(prev, event.data),\n hostAnswered: true,\n // Don't set ready here — wait for athena-auth (token) or the timeout.\n // Setting ready on config alone causes a race: the thread list fires\n // before the token arrives, falling back to X-API-KEY.\n }));\n }\n\n if (event.data.type === 'athena-auth' && typeof event.data.token === 'string') {\n endOnce('auth.bridge.ready', { data: { mode: 'iframe' } });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'iframe-postmessage' },\n });\n setState((prev) => ({\n ...prev,\n token: event.data.token,\n ready: true,\n }));\n tokenRef.current = event.data.token;\n for (const resolve of [...tokenWaiters]) resolve(event.data.token);\n }\n };\n window.addEventListener('message', handler);\n\n // A renewal asks the parent to send its current token again — the same\n // `athena-auth-ready` handshake a parent already answers — and settles on\n // the next `athena-auth`, or on the held token when none arrives in time.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return new Promise<string | null>((resolve) => {\n const settle = (token: string | null) => {\n clearTimeout(timer);\n tokenWaiters.delete(settle);\n resolve(token ?? tokenRef.current);\n };\n const timer = setTimeout(() => settle(null), PARENT_TOKEN_RENEWAL_TIMEOUT_MS);\n tokenWaiters.add(settle);\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n });\n };\n\n if (!readySignalSent.current) {\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n readySignalSent.current = true;\n }\n\n const timer = setTimeout(() => {\n endOnce('auth.bridge.timeout', {\n data: {\n mode: 'iframe',\n timeout_ms: BRIDGE_TIMEOUT_MS,\n config_received: configReceived.current,\n },\n });\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n }, BRIDGE_TIMEOUT_MS);\n\n return () => {\n window.removeEventListener('message', handler);\n clearTimeout(timer);\n renewerRef.current = null;\n for (const resolve of [...tokenWaiters]) resolve(null);\n };\n }, [isInIframe, runtimeTrustedOrigins]);\n\n useEffect(() => {\n if (isInIframe) return;\n if (typeof window === 'undefined') return;\n\n let cancelled = false;\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n let controller: AbortController | null = null;\n let inFlight: Promise<string | null> | null = null;\n let retryAttempt = 0;\n\n const clearRefreshTimer = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n const markReady = () => {\n if (cancelled) return;\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n };\n\n const scheduleRefresh = (expiresAt: unknown) => {\n clearRefreshTimer();\n retryAttempt = 0;\n const delay = getTokenRefreshDelay(expiresAt, Date.now());\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const scheduleRetry = () => {\n clearRefreshTimer();\n const delay = getAuthRetryDelay(retryAttempt);\n retryAttempt += 1;\n refreshTimer = setTimeout(() => {\n void fetchAuth({ markReadyOnFailure: false });\n }, delay);\n };\n\n const mintAuth = async ({\n markReadyOnFailure,\n }: {\n markReadyOnFailure: boolean;\n }): Promise<string | null> => {\n const requestController = new AbortController();\n controller = requestController;\n\n // `markReadyOnFailure` distinguishes the initial acquisition (a span the\n // provider blocks on) from a background refresh. Idempotent: the abort\n // timer and the catch path can both reach an end call.\n const isInitial = markReadyOnFailure;\n const rawEndSpan = isInitial\n ? athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', { mode: 'marathon' })\n : null;\n let spanEnded = false;\n const endBridgeSpan: typeof rawEndSpan = rawEndSpan\n ? (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return rawEndSpan(name, options);\n }\n : null;\n\n const requestTimer = setTimeout(() => {\n requestController.abort();\n endBridgeSpan?.('auth.bridge.timeout', {\n data: { mode: 'marathon', timeout_ms: BRIDGE_TIMEOUT_MS },\n });\n if (markReadyOnFailure) {\n markReady();\n }\n }, BRIDGE_TIMEOUT_MS);\n\n try {\n const resp = await fetch('/_athena/auth', {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: requestController.signal,\n });\n if (cancelled) return null;\n\n if (!resp.ok) {\n if (resp.status === 404) {\n // Not a Marathon origin — expected for standalone/local dev.\n endBridgeSpan?.('auth.bridge.absent', {\n data: { mode: 'marathon', status: resp.status },\n });\n markReady();\n } else {\n const failure = {\n level: 'warn' as const,\n data: { mode: 'marathon', status: resp.status },\n };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n if (markReadyOnFailure) {\n markReady();\n }\n scheduleRetry();\n }\n return null;\n }\n\n const data = (await resp.json()) as AthenaAuthResponse;\n if (cancelled) return null;\n\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.ready', {\n data: { mode: 'marathon', has_token: typeof data.token === 'string' },\n });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n } else {\n athenaDiagnostics.emit('auth.token.refreshed', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n }\n\n const token = typeof data.token === 'string' ? data.token : null;\n setState({\n token,\n ...readAuthMintUrls(data),\n hostAnswered: true,\n ready: true,\n });\n tokenRef.current = token;\n scheduleRefresh(data.expires_at);\n return token;\n } catch (error) {\n if (!cancelled && !(error instanceof DOMException && error.name === 'AbortError')) {\n const failure = { level: 'warn' as const, data: { mode: 'marathon' }, error };\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.error', failure);\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', failure);\n }\n }\n if (markReadyOnFailure) {\n markReady();\n }\n if (!cancelled) {\n scheduleRetry();\n }\n return null;\n } finally {\n clearTimeout(requestTimer);\n if (controller === requestController) controller = null;\n }\n };\n\n // One mint at a time. The initial acquisition, a scheduled refresh, a\n // retry and a forced renewal that overlap share the request in flight\n // instead of aborting it, so each request keeps its own timeout and the\n // schedule never cancels a renewal a connection is waiting on.\n const fetchAuth = (options: { markReadyOnFailure: boolean }): Promise<string | null> => {\n inFlight ??= mintAuth(options).finally(() => {\n inFlight = null;\n });\n return inFlight;\n };\n\n // A forced renewal mints now instead of waiting for the scheduled refresh\n // (joining a mint already in flight); the schedule restarts from the new\n // token's expiry.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return fetchAuth({ markReadyOnFailure: false }).then((token) => token ?? tokenRef.current);\n };\n\n void fetchAuth({ markReadyOnFailure: true });\n\n return () => {\n cancelled = true;\n controller?.abort();\n clearRefreshTimer();\n renewerRef.current = null;\n };\n }, [isInIframe]);\n\n const renewToken = useCallback<AthenaTokenRenewer>(\n (options) => renewerRef.current?.(options) ?? Promise.resolve(tokenRef.current),\n [],\n );\n\n return useMemo(() => ({ ...state, renewToken }), [state, renewToken]);\n}\n\n/**\n * Legacy hook — returns just the auth token from the parent bridge.\n * Prefer useParentBridge() for new code.\n */\nexport function useParentAuth(): string | null {\n return useParentBridge().token;\n}\n"],"names":["timer"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"auth.js","sources":["../../src/runtime/auth.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { AthenaTokenRenewer } from '../auth/credential-source';\nimport { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\nimport { AthenaSdkError } from '../diagnostics/errors';\n\n/**\n * Check whether a PostMessage origin is trusted.\n * Accepts:\n * - explicit trusted origins passed by the host app\n * - the current iframe origin (for same-origin wrappers like Marathon)\n * - any *.athenaintel.com subdomain and localhost (legacy/default behavior)\n */\nfunction isTrustedOrigin({\n origin,\n trustedOrigins,\n}: {\n origin: string;\n trustedOrigins: string[];\n}): boolean {\n try {\n const normalizedOrigin = new URL(origin).origin;\n if (trustedOrigins.includes(normalizedOrigin)) {\n return true;\n }\n\n const { hostname } = new URL(normalizedOrigin);\n return (\n hostname === 'athenaintel.com' ||\n hostname.endsWith('.athenaintel.com') ||\n hostname === 'localhost'\n );\n } catch {\n return false;\n }\n}\n\nexport interface ParentBridgeState {\n /** PropelAuth access token from the parent window. */\n token: string | null;\n /**\n * Statewire sync mount (`…/v2/threads`) minted by the host. `null` when the\n * host minted none; the provider then derives it from the configured\n * `apiUrl`'s origin (or the environment preset).\n */\n statewireSyncUrl: string | null;\n /**\n * Bare Agora API base (no path) minted by the host. `null` when the host\n * minted none; the provider then uses the configured `backendUrl` (or the\n * environment preset).\n */\n backendBaseUrl: string | null;\n /** Athena frontend origin provided by the parent wrapper. */\n appUrl: string | null;\n /**\n * True once a host answered the bridge — a `/_athena/auth` mint or an\n * `athena-config` message — whether or not it minted the statewire fields.\n * Lets the provider tell an outdated host (answered, no mount) from no host\n * at all (the environment preset applies).\n */\n hostAnswered: boolean;\n /** True once config has been received from the parent (or timeout fired). */\n ready: boolean;\n /**\n * Set when the Marathon mint (`/_athena/auth`) gave up. It gives up when it\n * gets a refusal (401, 403, or any 4xx other than 404), a body it cannot\n * read, or a transient failure (5xx, 408, 429, a network error, a timeout)\n * on every attempt of one bounded retry cycle. `code` is\n * `auth_bridge_failed`, or `auth_bridge_timeout` when the last attempt\n * timed out. After that the bridge does not mint again in the background. A\n * forced renewal (`renewToken({ force: true })`) starts one new bounded\n * cycle. A token that an earlier mint handed out is kept (the server still\n * validates it), and the bridge never puts an empty token in its place. The\n * next successful mint clears it. Absent or `null` otherwise.\n */\n error?: AthenaSdkError | null;\n}\n\n/**\n * {@link ParentBridgeState} plus the bridge's token renewal: `force` (the\n * token was refused) asks the host for a new one — a fresh `/_athena/auth`\n * mint on a Marathon origin, or an `athena-auth-ready` request answered by the\n * parent window's next `athena-auth` — and resolves the held token otherwise.\n */\nexport type ParentBridge = ParentBridgeState & { renewToken: AthenaTokenRenewer };\n\nconst BRIDGE_URL_FIELDS = ['statewireSyncUrl', 'backendBaseUrl', 'appUrl'] as const;\n\ntype BridgeUrlField = (typeof BRIDGE_URL_FIELDS)[number];\n\n/** The URL half of {@link ParentBridgeState} — what a host can hand the SDK. */\nexport type ParentBridgeUrls = Pick<ParentBridgeState, BridgeUrlField>;\n\nconst EMPTY_BRIDGE_URLS: ParentBridgeUrls = {\n statewireSyncUrl: null,\n backendBaseUrl: null,\n appUrl: null,\n};\n\n/**\n * Fold an `athena-config` postMessage payload over the current URL set. A\n * field is taken only when it is a string; anything else keeps the previous\n * value. Keys outside {@link BridgeUrlField} (a legacy parent's `apiUrl` /\n * `backendUrl` among them) are dropped.\n */\nexport function mergeBridgeConfigMessage(\n previous: ParentBridgeUrls,\n payload: Partial<Record<BridgeUrlField, unknown>>,\n): ParentBridgeUrls {\n const next: ParentBridgeUrls = {\n statewireSyncUrl: previous.statewireSyncUrl,\n backendBaseUrl: previous.backendBaseUrl,\n appUrl: previous.appUrl,\n };\n for (const field of BRIDGE_URL_FIELDS) {\n const value = payload[field];\n if (typeof value === 'string') {\n next[field] = value;\n }\n }\n return next;\n}\n\n/**\n * Read the URL set out of a `/_athena/auth` mint body. Non-string fields are\n * `null`.\n */\nexport function readAuthMintUrls(data: Partial<Record<BridgeUrlField, unknown>>): ParentBridgeUrls {\n return mergeBridgeConfigMessage(EMPTY_BRIDGE_URLS, data);\n}\n\nexport interface ParentBridgeOptions {\n /**\n * Exact parent origins that are allowed to send auth/config via postMessage.\n * Useful for private-cloud or custom-domain deployments.\n */\n trustedOrigins?: string[];\n /** @internal Overrides {@link DEFAULT_MINT_RETRY_POLICY}; used by tests. */\n mintRetryPolicy?: MintRetryPolicy;\n}\n\n/** How long to wait for the parent bridge before falling back to defaults. */\nconst BRIDGE_TIMEOUT_MS = 2000;\n/** How long a renewal waits for the parent window to answer `athena-auth-ready`. */\nconst PARENT_TOKEN_RENEWAL_TIMEOUT_MS = 5000;\nconst TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;\nconst TOKEN_REFRESH_MIN_DELAY_MS = 60 * 1000;\nconst DEFAULT_TOKEN_REFRESH_MS = 55 * 60 * 1000;\nconst EMPTY_TRUSTED_ORIGINS: string[] = [];\n\n/**\n * How one Marathon mint cycle retries. A cycle is the initial acquisition, a\n * scheduled refresh, or a forced renewal. It sends at most `maxAttempts`\n * requests. It retries only a transient failure (5xx, 408, 429, a network\n * error, a timeout), after a capped exponential backoff with jitter, and then\n * it gives up. Nothing retries in the background after a cycle gave up.\n */\nexport interface MintRetryPolicy {\n /** Requests per cycle, the first one included. */\n maxAttempts: number;\n /** Ceiling of the delay before the first retry; it doubles for each retry. */\n baseDelayMs: number;\n /** Largest delay before any retry. */\n maxDelayMs: number;\n /** How long one request can take before it is aborted and counted as a timeout. */\n timeoutMs: number;\n /** Source of jitter in `[0, 1)`. */\n random: () => number;\n}\n\nexport const DEFAULT_MINT_RETRY_POLICY: MintRetryPolicy = {\n maxAttempts: 4,\n baseDelayMs: 500,\n maxDelayMs: 4000,\n timeoutMs: BRIDGE_TIMEOUT_MS,\n random: Math.random,\n};\n\n/** Untrusted shape of the `/_athena/auth` mint body; every field is type-guarded on read. */\ninterface AthenaAuthResponse {\n token?: unknown;\n statewireSyncUrl?: unknown;\n backendBaseUrl?: unknown;\n appUrl?: unknown;\n expires_at?: unknown;\n}\n\nconst normalizeOrigin = (value: string): string | null => {\n try {\n return new URL(value).origin;\n } catch {\n return null;\n }\n};\n\nexport const getTokenRefreshDelay = (expiresAt: unknown, nowMs: number): number => {\n if (typeof expiresAt !== 'string') {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n const expiresAtMs = Date.parse(expiresAt);\n if (!Number.isFinite(expiresAtMs)) {\n return DEFAULT_TOKEN_REFRESH_MS;\n }\n\n return Math.max(TOKEN_REFRESH_MIN_DELAY_MS, expiresAtMs - nowMs - TOKEN_REFRESH_BUFFER_MS);\n};\n\n/**\n * The delay before retry number `retry` of a mint cycle (1 is the first\n * retry). The ceiling is `min(maxDelayMs, baseDelayMs * 2^(retry - 1))`, and\n * equal jitter puts the wait between half of that ceiling and all of it, so\n * viewers whose mint failed at the same time do not retry at the same time.\n */\nexport function getMintRetryDelay({\n retry,\n policy = DEFAULT_MINT_RETRY_POLICY,\n}: {\n retry: number;\n policy?: Pick<MintRetryPolicy, 'baseDelayMs' | 'maxDelayMs' | 'random'>;\n}): number {\n const safeRetry = Number.isFinite(retry) ? Math.max(1, Math.floor(retry)) : 1;\n const ceiling = Math.min(policy.maxDelayMs, policy.baseDelayMs * 2 ** (safeRetry - 1));\n const jitter = Math.min(1, Math.max(0, policy.random()));\n return Math.round(ceiling / 2 + (ceiling / 2) * jitter);\n}\n\n/** A mint status worth another attempt: the host, or something in front of it, is briefly unwell. */\nexport function isRetryableMintStatus(status: number): boolean {\n return status >= 500 || status === 408 || status === 429;\n}\n\n/** A failed `/_athena/auth` request that is worth another attempt. */\ninterface TransientMintFailure {\n kind: 'transient';\n status: number | null;\n reason: 'status' | 'network' | 'timeout';\n hostError: string | null;\n cause?: unknown;\n}\n\n/** A failed `/_athena/auth` request that ends its cycle at once. */\ninterface RefusedMintFailure {\n kind: 'refused';\n status: number;\n /** `status`: a 4xx other than 404, 408 and 429. `invalid_body`: a 2xx whose body is not a JSON object. */\n reason: 'status' | 'invalid_body';\n hostError: string | null;\n}\n\nexport type MintFailure = TransientMintFailure | RefusedMintFailure;\n\n/** The outcome of one `/_athena/auth` request. */\ntype MintAttempt =\n | { kind: 'minted'; data: AthenaAuthResponse }\n /** 404: this is not a Marathon origin. */\n | { kind: 'absent'; status: number }\n | MintFailure\n | { kind: 'cancelled' };\n\n/** The `error` string of a Marathon failure body (`{\"error\":\"mint_failed\"}`), if any. */\nasync function readHostError(resp: Response): Promise<string | null> {\n try {\n const body: unknown = await resp.json();\n if (body && typeof body === 'object' && 'error' in body) {\n const value = (body as { error?: unknown }).error;\n return typeof value === 'string' ? value.slice(0, 100) : null;\n }\n } catch {\n // An empty or non-JSON error body tells nothing more than the status.\n }\n return null;\n}\n\nconst MINT_RETRY_NOTE =\n 'The SDK stopped retrying and does not connect without a credential. Remount the provider, or call renewToken({ force: true }), to try again.';\n\nfunction mintFailureHint(failure: MintFailure): string {\n if (failure.kind === 'transient') {\n return `The host could not mint a viewer token. Check the Marathon and Agora logs. ${MINT_RETRY_NOTE}`;\n }\n if (failure.reason === 'invalid_body') {\n return `The host answered /_athena/auth without a JSON mint body, so this origin is not a Marathon bridge. Pass token, getToken or apiKey. ${MINT_RETRY_NOTE}`;\n }\n if (failure.status === 401) {\n return `The host has no session for this viewer. Sign in again through the app URL. ${MINT_RETRY_NOTE}`;\n }\n if (failure.status === 403) {\n return `The host does not give this viewer access to the app. ${MINT_RETRY_NOTE}`;\n }\n return `The host refused the mint request. ${MINT_RETRY_NOTE}`;\n}\n\n/** The typed error that a mint cycle reports when it gives up. */\nexport function mintFailureError({\n failure,\n attempts,\n phase,\n}: {\n failure: MintFailure;\n attempts: number;\n phase: 'initial' | 'renewal';\n}): AthenaSdkError {\n const timedOut = failure.kind === 'transient' && failure.reason === 'timeout';\n const answer =\n failure.status !== null ? `HTTP ${failure.status}` : timedOut ? 'a timeout' : 'a network error';\n const message =\n failure.kind === 'transient'\n ? `The host auth bridge (/_athena/auth) failed ${attempts} time${attempts === 1 ? '' : 's'} (last: ${answer}).`\n : failure.reason === 'invalid_body'\n ? `The host auth bridge (/_athena/auth) answered ${answer} without a JSON mint body.`\n : `The host auth bridge (/_athena/auth) refused the mint (${answer}).`;\n return new AthenaSdkError({\n code: timedOut ? 'auth_bridge_timeout' : 'auth_bridge_failed',\n message,\n hint: mintFailureHint(failure),\n ...(failure.status !== null && { status: failure.status }),\n ...(failure.hostError !== null && { detail: { code: failure.hostError } }),\n ...(failure.kind === 'transient' && failure.cause !== undefined && { cause: failure.cause }),\n context: {\n mode: 'marathon',\n phase,\n attempts,\n reason: failure.reason,\n host_error: failure.hostError,\n },\n });\n}\n\n/**\n * Listens for auth tokens AND environment config from the host environment.\n *\n * Two transport modes, picked automatically:\n *\n * 1. **Iframe (legacy / Olympus inline preview)**: a parent window posts\n * `athena-config` and `athena-auth` via PostMessage. The hook also posts\n * `athena-auth-ready` so the parent knows when to (re-)send.\n *\n * 2. **Standalone (Marathon-served deploy URL)**: top-level navigation, no\n * parent. The hook fetches `/_athena/auth` on its own origin. Marathon\n * validates its session cookie (sent by the browser automatically) and\n * returns `{ token, statewireSyncUrl, backendBaseUrl, appUrl }` — the\n * statewire mount, the bare Agora base and the app origin. A 404 means\n * we're not on a Marathon origin — the hook falls through with empty\n * state. Any other failure fails closed: one bounded retry cycle\n * ({@link MintRetryPolicy}) for a transient failure, none for a refusal,\n * and then `error` is set and `ready` flips with no token — never an\n * empty or anonymous one.\n *\n * In both modes the app never blocks indefinitely: the iframe bridge flips\n * `ready: true` after a timeout, and a Marathon mint cycle ends after at most\n * `maxAttempts` requests.\n */\nexport function useParentBridge({\n trustedOrigins = EMPTY_TRUSTED_ORIGINS,\n mintRetryPolicy = DEFAULT_MINT_RETRY_POLICY,\n}: ParentBridgeOptions = {}): ParentBridge {\n const isInIframe = typeof window !== 'undefined' && window.parent !== window;\n const runtimeTrustedOrigins = useMemo(() => {\n const origins = new Set<string>();\n\n if (typeof window !== 'undefined') {\n origins.add(window.location.origin);\n }\n\n for (const trustedOrigin of trustedOrigins) {\n const normalizedOrigin = normalizeOrigin(trustedOrigin);\n if (normalizedOrigin) {\n origins.add(normalizedOrigin);\n }\n }\n\n return [...origins];\n }, [trustedOrigins]);\n\n const [state, setState] = useState<ParentBridgeState>({\n token: null,\n ...EMPTY_BRIDGE_URLS,\n hostAnswered: false,\n ready: false,\n error: null,\n });\n // Read when a mint cycle starts, so a caller's inline policy object does not\n // restart the bridge on every render.\n const mintRetryPolicyRef = useRef(mintRetryPolicy);\n mintRetryPolicyRef.current = mintRetryPolicy;\n const readySignalSent = useRef(false);\n const configReceived = useRef(false);\n const tokenRef = useRef<string | null>(state.token);\n tokenRef.current = state.token;\n // Installed by whichever transport mode is active (iframe or Marathon).\n const renewerRef = useRef<AthenaTokenRenewer | null>(null);\n\n useEffect(() => {\n if (!isInIframe) return;\n\n const endBridgeSpan = athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', {\n mode: 'iframe',\n });\n let spanEnded = false;\n const endOnce: typeof endBridgeSpan = (name, options) => {\n if (spanEnded) return 0;\n spanEnded = true;\n return endBridgeSpan(name, options);\n };\n\n // Renewals waiting for the parent's next `athena-auth`.\n const tokenWaiters = new Set<(token: string | null) => void>();\n\n const handler = (event: MessageEvent) => {\n if (!isTrustedOrigin({ origin: event.origin, trustedOrigins: runtimeTrustedOrigins })) {\n return;\n }\n if (!event.data || typeof event.data !== 'object') return;\n\n if (event.data.type === 'athena-config') {\n configReceived.current = true;\n setState((prev) => ({\n ...prev,\n ...mergeBridgeConfigMessage(prev, event.data),\n hostAnswered: true,\n // Don't set ready here — wait for athena-auth (token) or the timeout.\n // Setting ready on config alone causes a race: the thread list fires\n // before the token arrives, falling back to X-API-KEY.\n }));\n }\n\n if (event.data.type === 'athena-auth' && typeof event.data.token === 'string') {\n endOnce('auth.bridge.ready', { data: { mode: 'iframe' } });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'iframe-postmessage' },\n });\n setState((prev) => ({\n ...prev,\n token: event.data.token,\n ready: true,\n }));\n tokenRef.current = event.data.token;\n for (const resolve of [...tokenWaiters]) resolve(event.data.token);\n }\n };\n window.addEventListener('message', handler);\n\n // A renewal asks the parent to send its current token again — the same\n // `athena-auth-ready` handshake a parent already answers — and settles on\n // the next `athena-auth`, or on the held token when none arrives in time.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return new Promise<string | null>((resolve) => {\n const settle = (token: string | null) => {\n clearTimeout(timer);\n tokenWaiters.delete(settle);\n resolve(token ?? tokenRef.current);\n };\n const timer = setTimeout(() => settle(null), PARENT_TOKEN_RENEWAL_TIMEOUT_MS);\n tokenWaiters.add(settle);\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n });\n };\n\n if (!readySignalSent.current) {\n window.parent.postMessage({ type: 'athena-auth-ready' }, '*');\n readySignalSent.current = true;\n }\n\n const timer = setTimeout(() => {\n endOnce('auth.bridge.timeout', {\n data: {\n mode: 'iframe',\n timeout_ms: BRIDGE_TIMEOUT_MS,\n config_received: configReceived.current,\n },\n });\n setState((prev) => (prev.ready ? prev : { ...prev, ready: true }));\n }, BRIDGE_TIMEOUT_MS);\n\n return () => {\n window.removeEventListener('message', handler);\n clearTimeout(timer);\n renewerRef.current = null;\n for (const resolve of [...tokenWaiters]) resolve(null);\n };\n }, [isInIframe, runtimeTrustedOrigins]);\n\n useEffect(() => {\n if (isInIframe) return;\n if (typeof window === 'undefined') return;\n\n let cancelled = false;\n let refreshTimer: ReturnType<typeof setTimeout> | null = null;\n let controller: AbortController | null = null;\n let inFlight: Promise<string | null> | null = null;\n let retryWait: { timer: ReturnType<typeof setTimeout>; resolve: () => void } | null = null;\n\n const clearRefreshTimer = () => {\n if (refreshTimer) {\n clearTimeout(refreshTimer);\n refreshTimer = null;\n }\n };\n\n const endRetryWait = () => {\n if (!retryWait) return;\n clearTimeout(retryWait.timer);\n const { resolve } = retryWait;\n retryWait = null;\n resolve();\n };\n\n /** The backoff between two attempts of one cycle; unmounting ends it at once. */\n const waitBeforeRetry = (delayMs: number) =>\n new Promise<void>((resolve) => {\n retryWait = { timer: setTimeout(endRetryWait, delayMs), resolve };\n });\n\n // The only background mint: once per token, shortly before it expires. It\n // is not rescheduled when its cycle gives up. A forced renewal that gives\n // up leaves it in place on purpose: the held token is still live, and\n // this one bounded cycle keeps a viewer connected past a brief outage.\n const scheduleRefresh = (expiresAt: unknown) => {\n clearRefreshTimer();\n const delay = getTokenRefreshDelay(expiresAt, Date.now());\n refreshTimer = setTimeout(() => {\n refreshTimer = null;\n void fetchAuth({ initial: false });\n }, delay);\n };\n\n /** One `/_athena/auth` request, classified. */\n const mintOnce = async (timeoutMs: number): Promise<MintAttempt> => {\n const requestController = new AbortController();\n controller = requestController;\n let timedOut = false;\n const requestTimer = setTimeout(() => {\n timedOut = true;\n requestController.abort();\n }, timeoutMs);\n\n try {\n const resp = await fetch('/_athena/auth', {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: requestController.signal,\n });\n if (cancelled) return { kind: 'cancelled' };\n // Not a Marathon origin: expected for standalone apps and local dev.\n if (resp.status === 404) return { kind: 'absent', status: resp.status };\n if (!resp.ok) {\n const hostError = await readHostError(resp);\n if (cancelled) return { kind: 'cancelled' };\n // `readHostError` swallows the abort of a stalled error body. Only a\n // retryable status becomes a timeout: a refusal (401, 403, other\n // 4xx) is known from its status and still fails after one request.\n if (timedOut && isRetryableMintStatus(resp.status)) {\n return { kind: 'transient', status: null, reason: 'timeout', hostError: null };\n }\n return isRetryableMintStatus(resp.status)\n ? { kind: 'transient', status: resp.status, reason: 'status', hostError }\n : { kind: 'refused', status: resp.status, reason: 'status', hostError };\n }\n\n let data: unknown;\n try {\n data = await resp.json();\n } catch (error) {\n if (cancelled) return { kind: 'cancelled' };\n if (timedOut) {\n return { kind: 'transient', status: null, reason: 'timeout', hostError: null, cause: error };\n }\n return { kind: 'refused', status: resp.status, reason: 'invalid_body', hostError: null };\n }\n if (cancelled) return { kind: 'cancelled' };\n // Marathon's 200 always carries a token. A 2xx without one is a\n // broken mint: it must not replace a held token or count as success.\n const token = data && typeof data === 'object' ? (data as { token?: unknown }).token : null;\n if (typeof token !== 'string' || token === '') {\n return { kind: 'refused', status: resp.status, reason: 'invalid_body', hostError: null };\n }\n return { kind: 'minted', data: data as AthenaAuthResponse };\n } catch (error) {\n if (cancelled) return { kind: 'cancelled' };\n if (timedOut) {\n return { kind: 'transient', status: null, reason: 'timeout', hostError: null, cause: error };\n }\n return { kind: 'transient', status: null, reason: 'network', hostError: null, cause: error };\n } finally {\n clearTimeout(requestTimer);\n if (controller === requestController) controller = null;\n }\n };\n\n /**\n * One bounded mint cycle: the initial acquisition (a span the provider\n * waits on) or a renewal (the scheduled refresh, a forced renewal). It\n * retries a transient failure up to `maxAttempts` requests in all, with\n * a capped, jittered backoff. On a refusal, or after the last attempt, it\n * gives up: it reports one typed error, sets `error`, keeps any token an\n * earlier mint handed out, and schedules nothing.\n */\n const runMintCycle = async ({ initial }: { initial: boolean }): Promise<string | null> => {\n const policy = mintRetryPolicyRef.current;\n const maxAttempts = Math.max(1, Math.floor(policy.maxAttempts));\n const phase = initial ? 'initial' : 'renewal';\n const endBridgeSpan = initial\n ? athenaDiagnostics.span('auth.bridge', 'auth.bridge.start', { mode: 'marathon' })\n : null;\n\n let attempts = 0;\n let lastFailure: MintFailure | null = null;\n while (attempts < maxAttempts) {\n attempts += 1;\n const outcome = await mintOnce(policy.timeoutMs);\n if (cancelled || outcome.kind === 'cancelled') return null;\n\n if (outcome.kind === 'absent') {\n endBridgeSpan?.('auth.bridge.absent', {\n data: { mode: 'marathon', status: outcome.status },\n });\n // 404 means no bridge here, so an earlier mint failure no longer\n // applies; the provider falls back to its other credentials.\n setState((prev) =>\n prev.ready && prev.error == null ? prev : { ...prev, ready: true, error: null },\n );\n return null;\n }\n\n if (outcome.kind === 'minted') {\n const { data } = outcome;\n if (endBridgeSpan) {\n endBridgeSpan('auth.bridge.ready', {\n data: { mode: 'marathon', has_token: typeof data.token === 'string', attempts },\n });\n athenaDiagnostics.emit('auth.token.acquired', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at },\n });\n } else {\n athenaDiagnostics.emit('auth.token.refreshed', {\n level: 'debug',\n data: { source: 'marathon', expires_at: data.expires_at, attempts },\n });\n }\n const token = typeof data.token === 'string' ? data.token : null;\n setState({\n token,\n ...readAuthMintUrls(data),\n hostAnswered: true,\n ready: true,\n error: null,\n });\n tokenRef.current = token;\n scheduleRefresh(data.expires_at);\n return token;\n }\n\n lastFailure = outcome;\n if (outcome.kind === 'refused' || attempts >= maxAttempts) break;\n\n const delayMs = getMintRetryDelay({ retry: attempts, policy });\n athenaDiagnostics.emit('auth.bridge.retry', {\n level: 'warn',\n data: {\n mode: 'marathon',\n phase,\n status: outcome.status,\n reason: outcome.reason,\n host_error: outcome.hostError,\n attempt: attempts,\n max_attempts: maxAttempts,\n delay_ms: delayMs,\n },\n });\n await waitBeforeRetry(delayMs);\n if (cancelled) return null;\n }\n\n if (!lastFailure) return null;\n const error = mintFailureError({ failure: lastFailure, attempts, phase });\n const failureData = {\n mode: 'marathon',\n phase,\n status: lastFailure.status,\n reason: lastFailure.reason,\n attempts,\n };\n if (endBridgeSpan) {\n endBridgeSpan(\n error.code === 'auth_bridge_timeout' ? 'auth.bridge.timeout' : 'auth.bridge.error',\n { data: failureData, error },\n );\n } else {\n athenaDiagnostics.emit('auth.token.refresh_failed', {\n level: 'error',\n data: failureData,\n error,\n });\n }\n setState((prev) => ({ ...prev, error, ready: true }));\n return null;\n };\n\n // One mint cycle at a time. The initial acquisition, a scheduled refresh\n // and forced renewals that overlap share the cycle in flight (its\n // requests, their timeouts and its backoff) instead of starting their own,\n // so concurrent callers never multiply the requests.\n const fetchAuth = (options: { initial: boolean }): Promise<string | null> => {\n inFlight ??= runMintCycle(options).finally(() => {\n inFlight = null;\n });\n return inFlight;\n };\n\n // A forced renewal mints now instead of waiting for the scheduled refresh\n // (joining a cycle already in flight), and may start a new bounded cycle\n // after one gave up. The schedule restarts from the new token's expiry.\n renewerRef.current = ({ force }) => {\n if (!force) return Promise.resolve(tokenRef.current);\n return fetchAuth({ initial: false }).then((token) => token ?? tokenRef.current);\n };\n\n void fetchAuth({ initial: true });\n\n return () => {\n cancelled = true;\n controller?.abort();\n clearRefreshTimer();\n endRetryWait();\n renewerRef.current = null;\n };\n }, [isInIframe]);\n\n const renewToken = useCallback<AthenaTokenRenewer>(\n (options) => renewerRef.current?.(options) ?? Promise.resolve(tokenRef.current),\n [],\n );\n\n return useMemo(() => ({ ...state, renewToken }), [state, renewToken]);\n}\n\n/**\n * Legacy hook — returns just the auth token from the parent bridge.\n * Prefer useParentBridge() for new code.\n */\nexport function useParentAuth(): string | null {\n return useParentBridge().token;\n}\n"],"names":["timer"],"mappings":"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"}
|