@shipfox/client-shell 26.0.0 → 27.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/auth.tsx"],"sourcesContent":["import {ApiError, configureApiClient} from '@shipfox/client-api';\nimport {type QueryClient, useQueryClient} from '@tanstack/react-query';\nimport {atom, useAtomValue, useSetAtom, useStore} from 'jotai';\nimport type {PropsWithChildren} from 'react';\nimport {useCallback, useEffect, useMemo} from 'react';\nimport type {AuthenticatedSession, UserIdentity, WorkspaceSummary} from '#core/session.js';\nimport {\n authRefreshQueryKey,\n authRefreshQueryOptions,\n userWorkspacesQueryOptions,\n} from '#hooks/api/session-auth.js';\nimport {lastWorkspaceIdAtom} from './last-workspace.js';\n\nconst REFRESH_EARLY_MS = 5 * 60 * 1000;\nconst REFRESH_RETRY_DELAY_MS = 60_000;\nconst BASE64_URL_REPLACEMENTS = {dash: /-/g, underscore: /_/g} as const;\nconst refreshPromises = new WeakMap<QueryClient, Promise<AuthenticatedSession>>();\n\nfunction invalidateRefresh(queryClient: QueryClient): void {\n refreshPromises.delete(queryClient);\n}\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'guest';\n\nexport type Workspace = WorkspaceSummary;\n\nexport interface AuthState {\n status: AuthStatus;\n token?: string;\n user?: UserIdentity;\n workspaces?: Workspace[];\n}\n\nexport interface AuthStateValue extends AuthState {\n isLoading: boolean;\n isAuthenticated: boolean;\n workspaces: Workspace[];\n hasWorkspace: boolean;\n}\n\nexport const initialAuthState: AuthState = {status: 'loading'};\nexport const authStateAtom = atom<AuthState>(initialAuthState);\nconst authTransitionEpochAtom = atom(0);\n\nexport function toAuthenticatedState(\n session: AuthenticatedSession,\n workspaces: WorkspaceSummary[] = [],\n): AuthState {\n return {\n status: 'authenticated',\n token: session.accessToken,\n user: session.user,\n workspaces,\n };\n}\n\nexport function useAuthState(): AuthStateValue {\n const state = useAtomValue(authStateAtom);\n return useMemo(\n () => ({\n ...state,\n workspaces: state.workspaces ?? [],\n isLoading: state.status === 'loading',\n isAuthenticated: state.status === 'authenticated',\n hasWorkspace: (state.workspaces ?? []).length > 0,\n }),\n [state],\n );\n}\n\nexport {\n authRefreshQueryKey,\n authRefreshQueryOptions,\n listUserWorkspaces,\n userWorkspacesQueryKey,\n userWorkspacesQueryOptions,\n} from '#hooks/api/session-auth.js';\n\nfunction decodeBase64Url(value: string): string {\n const base64 = value\n .replace(BASE64_URL_REPLACEMENTS.dash, '+')\n .replace(BASE64_URL_REPLACEMENTS.underscore, '/');\n return atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '='));\n}\n\nfunction readJwtExp(token: string): number | undefined {\n const [, payload] = token.split('.');\n if (!payload) return undefined;\n try {\n const parsed = JSON.parse(decodeBase64Url(payload)) as {exp?: unknown};\n return typeof parsed.exp === 'number' && Number.isFinite(parsed.exp) ? parsed.exp : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function getAuthRefreshDelayMs(token: string, nowMs = Date.now()): number | undefined {\n const exp = readJwtExp(token);\n return exp === undefined ? undefined : exp * 1000 - nowMs - REFRESH_EARLY_MS;\n}\n\nexport function useAuthTransition() {\n const queryClient = useQueryClient();\n const store = useStore();\n const setState = useSetAtom(authStateAtom);\n const setLastWorkspaceId = useSetAtom(lastWorkspaceIdAtom);\n\n const beginAuthTransition = useCallback(() => {\n const transitionEpoch = store.get(authTransitionEpochAtom) + 1;\n store.set(authTransitionEpochAtom, transitionEpoch);\n return transitionEpoch;\n }, [store]);\n\n const clearPrivateState = useCallback(async () => {\n await queryClient.cancelQueries();\n queryClient.clear();\n }, [queryClient]);\n\n const enterGuest = useCallback(\n async (transitionEpoch?: number) => {\n const isExternalTransition = transitionEpoch === undefined;\n const epoch = transitionEpoch ?? beginAuthTransition();\n if (isExternalTransition) invalidateRefresh(queryClient);\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n\n await clearPrivateState();\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n setLastWorkspaceId(undefined);\n setState({status: 'guest'});\n return true;\n },\n [beginAuthTransition, clearPrivateState, queryClient, setLastWorkspaceId, setState, store],\n );\n\n const enterAuthenticated = useCallback(\n async (session: AuthenticatedSession, transitionEpoch?: number) => {\n const isExternalTransition = transitionEpoch === undefined;\n const epoch = transitionEpoch ?? beginAuthTransition();\n if (isExternalTransition) invalidateRefresh(queryClient);\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n\n const previousState = store.get(authStateAtom);\n const principalChanged =\n previousState.status !== 'authenticated' || previousState.user?.id !== session.user.id;\n\n if (principalChanged) await clearPrivateState();\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n if (principalChanged) setLastWorkspaceId(undefined);\n\n queryClient.setQueryData(authRefreshQueryKey, session);\n let workspaces: WorkspaceSummary[] = [];\n try {\n const hydratedWorkspaces = await queryClient.fetchQuery(\n userWorkspacesQueryOptions(session.accessToken),\n );\n workspaces = hydratedWorkspaces.memberships;\n } catch {\n // The authenticated session remains usable while workspace hydration retries on the next route load.\n }\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n\n setState(toAuthenticatedState(session, workspaces));\n return true;\n },\n [beginAuthTransition, clearPrivateState, queryClient, setLastWorkspaceId, setState, store],\n );\n\n return {beginAuthTransition, enterAuthenticated, enterGuest};\n}\n\nexport function useRefreshAuth() {\n const queryClient = useQueryClient();\n const {beginAuthTransition, enterAuthenticated, enterGuest} = useAuthTransition();\n\n return useCallback(() => {\n const existingRefresh = refreshPromises.get(queryClient);\n if (existingRefresh) return existingRefresh;\n\n const transitionEpoch = beginAuthTransition();\n const refresh = (async () => {\n try {\n const result = await queryClient.fetchQuery(authRefreshQueryOptions());\n const accepted = await enterAuthenticated(result, transitionEpoch);\n if (!accepted) {\n throw new ApiError({\n message: 'Authentication refresh was superseded.',\n code: 'unauthorized',\n status: 401,\n });\n }\n return result;\n } catch (error) {\n if (error instanceof ApiError && error.status === 401) {\n await enterGuest(transitionEpoch);\n }\n throw error;\n }\n })();\n refreshPromises.set(queryClient, refresh);\n void refresh.then(\n () => {\n if (refreshPromises.get(queryClient) === refresh) refreshPromises.delete(queryClient);\n },\n () => {\n if (refreshPromises.get(queryClient) === refresh) refreshPromises.delete(queryClient);\n },\n );\n return refresh;\n }, [beginAuthTransition, enterAuthenticated, enterGuest, queryClient]);\n}\n\nexport interface AuthRuntimeProps extends PropsWithChildren {\n effects?: boolean;\n}\n\nexport function AuthRuntime({children, effects = true}: AuthRuntimeProps) {\n const store = useStore();\n const authState = useAtomValue(authStateAtom);\n const refreshAuth = useRefreshAuth();\n\n useEffect(() => {\n if (!effects) return;\n configureApiClient({\n getAccessToken: () => store.get(authStateAtom).token,\n refreshAccessToken: async () => (await refreshAuth()).accessToken,\n });\n }, [effects, refreshAuth, store]);\n\n useEffect(() => {\n if (!effects) return;\n refreshAuth().catch(() => undefined);\n }, [effects, refreshAuth]);\n\n useEffect(() => {\n if (!effects || authState.status !== 'authenticated' || !authState.token) return;\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let disposed = false;\n let refreshing = false;\n const clearRefreshTimer = () => {\n if (timeout !== undefined) clearTimeout(timeout);\n timeout = undefined;\n };\n const scheduleRefresh = (delayMs: number) => {\n clearRefreshTimer();\n timeout = setTimeout(runRefresh, Math.max(0, delayMs));\n };\n const retryIfStillDue = () => {\n const current = store.get(authStateAtom);\n if (current.status !== 'authenticated' || !current.token) return;\n const delay = getAuthRefreshDelayMs(current.token);\n if (delay !== undefined && delay <= 0) scheduleRefresh(REFRESH_RETRY_DELAY_MS);\n };\n function runRefresh() {\n if (disposed || refreshing) return;\n refreshing = true;\n clearRefreshTimer();\n refreshAuth()\n .catch(() => undefined)\n .finally(() => {\n refreshing = false;\n if (!disposed) retryIfStillDue();\n });\n }\n const refreshIfDue = () => {\n const current = store.get(authStateAtom);\n if (current.status !== 'authenticated' || !current.token) return;\n const delay = getAuthRefreshDelayMs(current.token);\n if (delay !== undefined && delay <= 0) runRefresh();\n };\n const refreshIfVisible = () => {\n if (document.visibilityState === 'visible') refreshIfDue();\n };\n const delay = getAuthRefreshDelayMs(authState.token);\n if (delay !== undefined) scheduleRefresh(delay);\n window.addEventListener('focus', refreshIfDue);\n window.addEventListener('online', refreshIfDue);\n document.addEventListener('visibilitychange', refreshIfVisible);\n return () => {\n disposed = true;\n clearRefreshTimer();\n window.removeEventListener('focus', refreshIfDue);\n window.removeEventListener('online', refreshIfDue);\n document.removeEventListener('visibilitychange', refreshIfVisible);\n };\n }, [authState.status, authState.token, effects, refreshAuth, store]);\n\n return children;\n}\n"],"names":["ApiError","configureApiClient","useQueryClient","atom","useAtomValue","useSetAtom","useStore","useCallback","useEffect","useMemo","authRefreshQueryKey","authRefreshQueryOptions","userWorkspacesQueryOptions","lastWorkspaceIdAtom","REFRESH_EARLY_MS","REFRESH_RETRY_DELAY_MS","BASE64_URL_REPLACEMENTS","dash","underscore","refreshPromises","WeakMap","invalidateRefresh","queryClient","delete","initialAuthState","status","authStateAtom","authTransitionEpochAtom","toAuthenticatedState","session","workspaces","token","accessToken","user","useAuthState","state","isLoading","isAuthenticated","hasWorkspace","length","listUserWorkspaces","userWorkspacesQueryKey","decodeBase64Url","value","base64","replace","atob","padEnd","Math","ceil","readJwtExp","payload","split","undefined","parsed","JSON","parse","exp","Number","isFinite","getAuthRefreshDelayMs","nowMs","Date","now","useAuthTransition","store","setState","setLastWorkspaceId","beginAuthTransition","transitionEpoch","get","set","clearPrivateState","cancelQueries","clear","enterGuest","isExternalTransition","epoch","enterAuthenticated","previousState","principalChanged","id","setQueryData","hydratedWorkspaces","fetchQuery","memberships","useRefreshAuth","existingRefresh","refresh","result","accepted","message","code","error","then","AuthRuntime","children","effects","authState","refreshAuth","getAccessToken","refreshAccessToken","catch","timeout","disposed","refreshing","clearRefreshTimer","clearTimeout","scheduleRefresh","delayMs","setTimeout","runRefresh","max","retryIfStillDue","current","delay","finally","refreshIfDue","refreshIfVisible","document","visibilityState","window","addEventListener","removeEventListener"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/runtime/auth.tsx"],"sourcesContent":["import {ApiError, configureApiClient} from '@shipfox/client-api';\nimport {type QueryClient, useQueryClient} from '@tanstack/react-query';\nimport {atom, useAtomValue, useSetAtom, useStore} from 'jotai';\nimport type {PropsWithChildren} from 'react';\nimport {useCallback, useEffect, useMemo} from 'react';\nimport type {AuthenticatedSession, UserIdentity, WorkspaceSummary} from '#core/session.js';\nimport {\n authRefreshQueryKey,\n authRefreshQueryOptions,\n userWorkspacesQueryOptions,\n} from '#hooks/api/session-auth.js';\nimport {lastWorkspaceIdAtom} from './last-workspace.js';\n\nconst REFRESH_EARLY_MS = 5 * 60 * 1000;\nconst REFRESH_RETRY_DELAY_MS = 60_000;\n// Consecutive renewal responses that do not advance the adopted window (a\n// malformed response, or an expiry no later than the one already held) end\n// the adoption instead of driving an unbounded zero-delay renew loop. A\n// valid response that merely lost a race to a longer expiry is not one.\nconst ADOPTED_RENEWAL_STALL_LIMIT = 3;\nconst BASE64_URL_REPLACEMENTS = {dash: /-/g, underscore: /_/g} as const;\nconst refreshPromises = new WeakMap<QueryClient, Promise<AuthenticatedSession>>();\n\n/**\n * A renewal response for an adopted session. `expiresAt` and `serverTime` are\n * both issuer timestamps; their difference is the server-side remaining\n * lifetime, which is the renewal scheduling anchor.\n */\nexport interface AdoptedSessionRenewal {\n session: AuthenticatedSession;\n expiresAt: string;\n serverTime: string;\n}\n\nexport type AdoptedSessionRenewalSupplier = () => Promise<AdoptedSessionRenewal | null>;\n\n/**\n * Options for the adopted-session renewal. A `null` renewal result ends the\n * adoption and falls back to the ordinary cookie refresh.\n */\nexport interface AdoptSessionOptions {\n expiresAt: string;\n serverTime: string;\n renew: AdoptedSessionRenewalSupplier;\n}\n\n/** The adopted session as exposed to composing consumers. */\nexport interface AdoptedSessionState {\n session: AuthenticatedSession;\n expiresAt: string;\n serverTime: string;\n}\n\ninterface AdoptedSessionRuntimeState extends AdoptedSessionState {\n generation: number;\n receivedAtMs: number;\n renew: AdoptedSessionRenewalSupplier;\n /**\n * Consecutive malformed or non-advancing renewal responses (responses that\n * merely lost a race to a longer expiry excluded). Capped at\n * {@link ADOPTED_RENEWAL_STALL_LIMIT} before the adoption falls back to the\n * ordinary cookie refresh.\n */\n stalledRenewals: number;\n}\n\nconst adoptedSessionAtom = atom<AdoptedSessionRuntimeState | null>(null);\nconst adoptionGenerationAtom = atom(0);\n/**\n * Expiry reserved by a renewal whose adoption transition is still running.\n * Overlapping renewals compare against it so a shorter response cannot slip\n * past the later-`expires_at` check while the longer one is entering.\n *\n * It lives in its own atom instead of inside {@link adoptedSessionAtom} so a\n * reservation write does not re-run `AuthRuntime`'s renewal effect: an effect\n * re-run while the first transition is still pending would reset the\n * in-flight guard and schedule another renewal from the old expiry, letting\n * an already-due adoption invoke the supplier a second time.\n */\nconst adoptedRenewalReservationAtom = atom(0);\n\nfunction invalidateRefresh(queryClient: QueryClient): void {\n refreshPromises.delete(queryClient);\n}\n\nexport type AuthStatus = 'loading' | 'authenticated' | 'guest';\n\nexport type Workspace = WorkspaceSummary;\n\nexport interface AuthState {\n status: AuthStatus;\n token?: string;\n user?: UserIdentity;\n workspaces?: Workspace[];\n}\n\nexport interface AuthStateValue extends AuthState {\n isLoading: boolean;\n isAuthenticated: boolean;\n workspaces: Workspace[];\n hasWorkspace: boolean;\n}\n\nexport const initialAuthState: AuthState = {status: 'loading'};\nexport const authStateAtom = atom<AuthState>(initialAuthState);\nconst authTransitionEpochAtom = atom(0);\n\nexport function toAuthenticatedState(\n session: AuthenticatedSession,\n workspaces: WorkspaceSummary[] = [],\n): AuthState {\n return {\n status: 'authenticated',\n token: session.accessToken,\n user: session.user,\n workspaces,\n };\n}\n\nexport function useAuthState(): AuthStateValue {\n const state = useAtomValue(authStateAtom);\n return useMemo(\n () => ({\n ...state,\n workspaces: state.workspaces ?? [],\n isLoading: state.status === 'loading',\n isAuthenticated: state.status === 'authenticated',\n hasWorkspace: (state.workspaces ?? []).length > 0,\n }),\n [state],\n );\n}\n\nexport {\n authRefreshQueryKey,\n authRefreshQueryOptions,\n listUserWorkspaces,\n userWorkspacesQueryKey,\n userWorkspacesQueryOptions,\n} from '#hooks/api/session-auth.js';\n\nfunction decodeBase64Url(value: string): string {\n const base64 = value\n .replace(BASE64_URL_REPLACEMENTS.dash, '+')\n .replace(BASE64_URL_REPLACEMENTS.underscore, '/');\n return atob(base64.padEnd(Math.ceil(base64.length / 4) * 4, '='));\n}\n\nfunction readJwtExp(token: string): number | undefined {\n const [, payload] = token.split('.');\n if (!payload) return undefined;\n try {\n const parsed = JSON.parse(decodeBase64Url(payload)) as {exp?: unknown};\n return typeof parsed.exp === 'number' && Number.isFinite(parsed.exp) ? parsed.exp : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function getAuthRefreshDelayMs(token: string, nowMs = Date.now()): number | undefined {\n const exp = readJwtExp(token);\n return exp === undefined ? undefined : exp * 1000 - nowMs - REFRESH_EARLY_MS;\n}\n\n/**\n * Delay until the adopted session's renewal point, derived from the issuer\n * timestamps only. Both values come from the server, so a skewed browser\n * clock cannot shorten or lengthen the window. Negative when the renewal\n * point already passed; callers clamp.\n */\nexport function getAdoptedSessionRenewDelayMs(expiresAt: string, serverTime: string): number {\n return Date.parse(expiresAt) - Date.parse(serverTime) - REFRESH_EARLY_MS;\n}\n\nexport function useAuthTransition() {\n const queryClient = useQueryClient();\n const store = useStore();\n const setState = useSetAtom(authStateAtom);\n const setLastWorkspaceId = useSetAtom(lastWorkspaceIdAtom);\n\n const beginAuthTransition = useCallback(() => {\n const transitionEpoch = store.get(authTransitionEpochAtom) + 1;\n store.set(authTransitionEpochAtom, transitionEpoch);\n return transitionEpoch;\n }, [store]);\n\n const clearPrivateState = useCallback(\n async (epoch: number): Promise<boolean> => {\n await queryClient.cancelQueries();\n // A superseded transition must not clear the cache: the clear would\n // destroy an in-flight query owned by the transition that superseded it\n // (for example the cookie fallback started by a release during a mint).\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n queryClient.clear();\n return true;\n },\n [queryClient, store],\n );\n\n const enterGuest = useCallback(\n async (transitionEpoch?: number) => {\n const isExternalTransition = transitionEpoch === undefined;\n const epoch = transitionEpoch ?? beginAuthTransition();\n if (isExternalTransition) invalidateRefresh(queryClient);\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n\n if (!(await clearPrivateState(epoch))) return false;\n setLastWorkspaceId(undefined);\n setState({status: 'guest'});\n return true;\n },\n [beginAuthTransition, clearPrivateState, queryClient, setLastWorkspaceId, setState, store],\n );\n\n const enterAuthenticated = useCallback(\n async (session: AuthenticatedSession, transitionEpoch?: number) => {\n const isExternalTransition = transitionEpoch === undefined;\n const epoch = transitionEpoch ?? beginAuthTransition();\n if (isExternalTransition) invalidateRefresh(queryClient);\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n\n const previousState = store.get(authStateAtom);\n const principalChanged =\n previousState.status !== 'authenticated' || previousState.user?.id !== session.user.id;\n\n if (principalChanged) {\n if (!(await clearPrivateState(epoch))) return false;\n setLastWorkspaceId(undefined);\n }\n\n queryClient.setQueryData(authRefreshQueryKey, session);\n let workspaces: WorkspaceSummary[] = [];\n try {\n const hydratedWorkspaces = await queryClient.fetchQuery(\n userWorkspacesQueryOptions(session.accessToken),\n );\n workspaces = hydratedWorkspaces.memberships;\n } catch {\n // The authenticated session remains usable while workspace hydration retries on the next route load.\n }\n if (store.get(authTransitionEpochAtom) !== epoch) return false;\n\n setState(toAuthenticatedState(session, workspaces));\n return true;\n },\n [beginAuthTransition, clearPrivateState, queryClient, setLastWorkspaceId, setState, store],\n );\n\n return {beginAuthTransition, enterAuthenticated, enterGuest};\n}\n\nexport function useRefreshAuth() {\n const queryClient = useQueryClient();\n const store = useStore();\n const {beginAuthTransition, enterAuthenticated, enterGuest} = useAuthTransition();\n\n return useCallback(() => {\n const existingRefresh = refreshPromises.get(queryClient);\n if (existingRefresh) return existingRefresh;\n\n if (store.get(adoptedSessionAtom) !== null) {\n // The ordinary refresh restores the cookie principal; running it while\n // an adoption is live would desync the adopted token from its metadata\n // and make product requests carry the administrator credential. ADR\n // 0014: the adopted bearer token is the only request credential.\n return Promise.reject(\n new ApiError({\n message: 'The adopted session must end before the ordinary cookie refresh.',\n code: 'unauthorized',\n status: 401,\n }),\n );\n }\n\n const transitionEpoch = beginAuthTransition();\n const refresh = (async () => {\n try {\n const result = await queryClient.fetchQuery(authRefreshQueryOptions());\n const accepted = await enterAuthenticated(result, transitionEpoch);\n if (!accepted) {\n throw new ApiError({\n message: 'Authentication refresh was superseded.',\n code: 'unauthorized',\n status: 401,\n });\n }\n return result;\n } catch (error) {\n if (error instanceof ApiError && error.status === 401) {\n await enterGuest(transitionEpoch);\n }\n throw error;\n }\n })();\n refreshPromises.set(queryClient, refresh);\n void refresh.then(\n () => {\n if (refreshPromises.get(queryClient) === refresh) refreshPromises.delete(queryClient);\n },\n () => {\n if (refreshPromises.get(queryClient) === refresh) refreshPromises.delete(queryClient);\n },\n );\n return refresh;\n }, [beginAuthTransition, enterAuthenticated, enterGuest, queryClient, store]);\n}\n\n/**\n * The adopted-session runtime seam. An adopted session is an externally minted\n * access-token-only session entered through the ordinary authenticated path;\n * it suspends the cookie-based proactive refresh and runs until its issuer\n * expiry, then asks the renewal supplier or falls back to the cookie.\n *\n * Release is terminal for the tab: it increments an adoption generation before\n * ending the adoption, and a mint or renewal response is adopted only when its\n * generation still matches the current one. Tabs share no adopted token and no\n * release state.\n *\n * The renew timer asks the supplier near the issuer expiry without operator\n * action. ADR 0014 renewal is a deliberate administrator action, so callers\n * gate the supplier on the operator's Extend signal (returning `null`\n * otherwise) to keep every extension deliberate and audited.\n */\nexport function useAdoptedSession() {\n const store = useStore();\n const queryClient = useQueryClient();\n const {beginAuthTransition, enterAuthenticated, enterGuest} = useAuthTransition();\n const refreshAuth = useRefreshAuth();\n const adoptedSession = useAtomValue(adoptedSessionAtom);\n\n const endAdoption = useCallback(async () => {\n store.set(adoptionGenerationAtom, store.get(adoptionGenerationAtom) + 1);\n store.set(adoptedRenewalReservationAtom, 0);\n store.set(adoptedSessionAtom, null);\n try {\n await refreshAuth();\n } catch (error) {\n // A superseded restore is not a failure: a newer transition (for\n // example a fresh adoption) already owns the session, and forcing\n // guest would log out that newer session.\n if (error instanceof ApiError && error.message === 'Authentication refresh was superseded.') {\n return;\n }\n // The cookie restore failed (transient network error, or a cookie that\n // died while the adoption suspended the refresh). The adopted token must\n // never remain the ambient request credential after Stop, so fall back\n // to guest instead of leaving the tab under the adopted principal.\n if (store.get(authStateAtom).status !== 'guest') await enterGuest();\n }\n }, [enterGuest, refreshAuth, store]);\n\n const adoptSession = useCallback(\n async (session: AuthenticatedSession, options: AdoptSessionOptions): Promise<boolean> => {\n // Each adoption starts a new generation: a renewal still in flight from\n // a previous adoption, or a release racing the mint, must not land over\n // the new adoption.\n const generation = store.get(adoptionGenerationAtom) + 1;\n store.set(adoptionGenerationAtom, generation);\n // A fresh adoption must not inherit an expiry reserved by a renewal of\n // the previous adoption whose transition is still in flight.\n store.set(adoptedRenewalReservationAtom, 0);\n const transitionEpoch = beginAuthTransition();\n // The transition supersedes any cookie refresh still in flight; leaving\n // it in the map would make the release fallback reuse a promise that\n // rejects as superseded instead of restoring the cookie session.\n invalidateRefresh(queryClient);\n // The token's server-side lifetime started when it was received; a slow\n // workspace hydration must not push the renewal point past expiresAt.\n // The monotonic clock keeps the elapsed measurement immune to wall-clock\n // steps (NTP correction, VM suspend/resume).\n const receivedAtMs = performance.now();\n const accepted = await enterAuthenticated(session, transitionEpoch);\n if (!accepted || store.get(adoptionGenerationAtom) !== generation) return false;\n store.set(adoptedSessionAtom, {\n generation,\n receivedAtMs,\n session,\n expiresAt: options.expiresAt,\n serverTime: options.serverTime,\n renew: options.renew,\n stalledRenewals: 0,\n });\n return true;\n },\n [beginAuthTransition, enterAuthenticated, queryClient, store],\n );\n\n const renewAdoptedSession = useCallback(async (): Promise<AdoptedSessionRenewal | null> => {\n const adopted = store.get(adoptedSessionAtom);\n if (adopted === null) return null;\n const generation = store.get(adoptionGenerationAtom);\n\n let result: AdoptedSessionRenewal | null;\n try {\n result = await adopted.renew();\n } catch {\n // A failed renewal degrades like a refused one: back to the cookie.\n result = null;\n }\n\n // The generation is captured when the request started; a response that\n // resolves after a release is discarded.\n if (store.get(adoptionGenerationAtom) !== generation) return null;\n if (result === null) {\n await endAdoption();\n return null;\n }\n\n const candidateExpiryMs = Date.parse(result.expiresAt);\n const candidateServerTimeMs = Date.parse(result.serverTime);\n const candidateValid =\n Number.isFinite(candidateExpiryMs) &&\n Number.isFinite(candidateServerTimeMs) &&\n candidateExpiryMs > candidateServerTimeMs;\n const current = store.get(adoptedSessionAtom);\n if (current === null) return null;\n const currentExpiryMs = Date.parse(current.expiresAt);\n const bestReservedExpiryMs = Math.max(\n currentExpiryMs,\n store.get(adoptedRenewalReservationAtom),\n );\n // A candidate that is malformed, stuck on the expiry the adoption already\n // holds, or shorter than the best expiry adopted or reserved by another\n // renewal did not advance the window and is never adopted.\n const windowDidNotAdvance = !candidateValid || candidateExpiryMs <= bestReservedExpiryMs;\n if (windowDidNotAdvance) {\n // Count a stall only when the window really did not move: a malformed\n // response, or one stuck on an expiry the adoption already holds. A\n // valid response that merely lost a race to a longer expiry adopted or\n // reserved while this renewal was in flight is a healthy concurrent\n // renewal; counting it would burn the stall budget and fire extra\n // supplier requests. The cap ends the adoption instead of driving an\n // unbounded zero-delay renew loop and falls back to the cookie refresh.\n const lostRace =\n candidateValid &&\n (candidateExpiryMs > currentExpiryMs || currentExpiryMs > Date.parse(adopted.expiresAt));\n if (!candidateValid || !lostRace) {\n const stalled = {...current, stalledRenewals: current.stalledRenewals + 1};\n store.set(adoptedSessionAtom, stalled);\n if (stalled.stalledRenewals >= ADOPTED_RENEWAL_STALL_LIMIT) {\n await endAdoption();\n }\n }\n return null;\n }\n if (candidateExpiryMs > store.get(adoptedRenewalReservationAtom)) {\n // Reserve the candidate expiry before the asynchronous transition so an\n // overlapping shorter response cannot overwrite the longer token. The\n // reservation lives outside adoptedSessionAtom so this write does not\n // re-run the renew effect while the transition is still pending.\n store.set(adoptedRenewalReservationAtom, candidateExpiryMs);\n }\n\n const receivedAtMs = performance.now();\n const transitionEpoch = beginAuthTransition();\n // The renewal transition supersedes any cookie refresh still in flight;\n // release must not reuse a promise that will reject as superseded.\n invalidateRefresh(queryClient);\n const accepted = await enterAuthenticated(result.session, transitionEpoch);\n if (!accepted || store.get(adoptionGenerationAtom) !== generation) {\n // Drop our reservation only while it is still the current one; another\n // renewal may have reserved a later expiry meanwhile.\n if (store.get(adoptedRenewalReservationAtom) === candidateExpiryMs) {\n store.set(adoptedRenewalReservationAtom, 0);\n }\n return null;\n }\n store.set(adoptedRenewalReservationAtom, 0);\n store.set(adoptedSessionAtom, {\n generation,\n receivedAtMs,\n session: result.session,\n expiresAt: result.expiresAt,\n serverTime: result.serverTime,\n renew: adopted.renew,\n stalledRenewals: 0,\n });\n return result;\n }, [beginAuthTransition, endAdoption, enterAuthenticated, queryClient, store]);\n\n const releaseAdoptedSession = useCallback(async () => {\n // Always advance the generation and fall back to the cookie: a release\n // while a mint is still entering must still invalidate that mint, or the\n // mint would re-enter the session after Stop.\n await endAdoption();\n }, [endAdoption]);\n\n return {adoptSession, renewAdoptedSession, releaseAdoptedSession, adoptedSession};\n}\n\nexport interface AuthRuntimeProps extends PropsWithChildren {\n effects?: boolean;\n}\n\nexport function AuthRuntime({children, effects = true}: AuthRuntimeProps) {\n const store = useStore();\n const authState = useAtomValue(authStateAtom);\n const refreshAuth = useRefreshAuth();\n const {adoptedSession, renewAdoptedSession, releaseAdoptedSession} = useAdoptedSession();\n\n useEffect(() => {\n if (!effects) return;\n configureApiClient({\n getAccessToken: () => store.get(authStateAtom).token,\n refreshAccessToken: async () => {\n if (store.get(adoptedSessionAtom) !== null) {\n // A 401 under an adopted token ends the adoption: the cookie\n // refresh restores the cookie's principal, and the renew timer must\n // not resurrect the adopted session afterwards. The falsy result\n // stops the failed request from being re-sent under the restored\n // administrator's principal (ADR 0014: the adopted bearer token is\n // the only request credential).\n await releaseAdoptedSession();\n return undefined;\n }\n return (await refreshAuth()).accessToken;\n },\n });\n }, [effects, refreshAuth, releaseAdoptedSession, store]);\n\n useEffect(() => {\n if (!effects) return;\n refreshAuth().catch(() => undefined);\n }, [effects, refreshAuth]);\n\n useEffect(() => {\n if (\n !effects ||\n authState.status !== 'authenticated' ||\n !authState.token ||\n adoptedSession !== null\n ) {\n return;\n }\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let disposed = false;\n let refreshing = false;\n const clearRefreshTimer = () => {\n if (timeout !== undefined) clearTimeout(timeout);\n timeout = undefined;\n };\n const scheduleRefresh = (delayMs: number) => {\n clearRefreshTimer();\n timeout = setTimeout(runRefresh, Math.max(0, delayMs));\n };\n const retryIfStillDue = () => {\n const current = store.get(authStateAtom);\n if (current.status !== 'authenticated' || !current.token) return;\n if (store.get(adoptedSessionAtom) !== null) return;\n const delay = getAuthRefreshDelayMs(current.token);\n if (delay !== undefined && delay <= 0) scheduleRefresh(REFRESH_RETRY_DELAY_MS);\n };\n function runRefresh() {\n if (disposed || refreshing || store.get(adoptedSessionAtom) !== null) return;\n refreshing = true;\n clearRefreshTimer();\n refreshAuth()\n .catch(() => undefined)\n .finally(() => {\n refreshing = false;\n if (!disposed) retryIfStillDue();\n });\n }\n const refreshIfDue = () => {\n const current = store.get(authStateAtom);\n if (current.status !== 'authenticated' || !current.token) return;\n if (store.get(adoptedSessionAtom) !== null) return;\n const delay = getAuthRefreshDelayMs(current.token);\n if (delay !== undefined && delay <= 0) runRefresh();\n };\n const refreshIfVisible = () => {\n if (document.visibilityState === 'visible') refreshIfDue();\n };\n const delay = getAuthRefreshDelayMs(authState.token);\n if (delay !== undefined) scheduleRefresh(delay);\n window.addEventListener('focus', refreshIfDue);\n window.addEventListener('online', refreshIfDue);\n document.addEventListener('visibilitychange', refreshIfVisible);\n return () => {\n disposed = true;\n clearRefreshTimer();\n window.removeEventListener('focus', refreshIfDue);\n window.removeEventListener('online', refreshIfDue);\n document.removeEventListener('visibilitychange', refreshIfVisible);\n };\n }, [adoptedSession, authState.status, authState.token, effects, refreshAuth, store]);\n\n useEffect(() => {\n if (!effects || adoptedSession === null) return;\n\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let disposed = false;\n let renewing = false;\n let nextFireAtMs = 0;\n const clearRenewTimer = () => {\n if (timeout !== undefined) clearTimeout(timeout);\n timeout = undefined;\n };\n const scheduleRenew = () => {\n clearRenewTimer();\n const current = store.get(adoptedSessionAtom);\n if (current === null) return;\n // The fire point comes from the issuer timestamps; elapsed time is\n // measured on the monotonic clock so a wall-clock step (NTP correction,\n // VM suspend/resume) cannot shift the renewal point.\n const earlyFireAtMs =\n current.receivedAtMs + getAdoptedSessionRenewDelayMs(current.expiresAt, current.serverTime);\n // A missed early point (tab suspended, timer throttled) must renew now\n // instead of waiting for the hard expiry.\n nextFireAtMs = Math.max(earlyFireAtMs, performance.now());\n timeout = setTimeout(runRenew, Math.max(0, nextFireAtMs - performance.now()));\n };\n const runRenew = () => {\n if (disposed || renewing) return;\n renewing = true;\n clearRenewTimer();\n renewAdoptedSession()\n .catch(() => undefined)\n .finally(() => {\n renewing = false;\n // Re-arm when the adoption survived (for example a racing response\n // with an earlier expiry was discarded); the effect re-runs and\n // re-arms when a renewal is adopted instead.\n if (!disposed) scheduleRenew();\n });\n };\n const renewIfDue = () => {\n if (performance.now() >= nextFireAtMs) runRenew();\n };\n const renewIfVisible = () => {\n if (document.visibilityState === 'visible') renewIfDue();\n };\n scheduleRenew();\n window.addEventListener('focus', renewIfDue);\n window.addEventListener('online', renewIfDue);\n document.addEventListener('visibilitychange', renewIfVisible);\n return () => {\n disposed = true;\n clearRenewTimer();\n window.removeEventListener('focus', renewIfDue);\n window.removeEventListener('online', renewIfDue);\n document.removeEventListener('visibilitychange', renewIfVisible);\n };\n }, [adoptedSession, effects, renewAdoptedSession, store]);\n\n return children;\n}\n"],"names":["ApiError","configureApiClient","useQueryClient","atom","useAtomValue","useSetAtom","useStore","useCallback","useEffect","useMemo","authRefreshQueryKey","authRefreshQueryOptions","userWorkspacesQueryOptions","lastWorkspaceIdAtom","REFRESH_EARLY_MS","REFRESH_RETRY_DELAY_MS","ADOPTED_RENEWAL_STALL_LIMIT","BASE64_URL_REPLACEMENTS","dash","underscore","refreshPromises","WeakMap","adoptedSessionAtom","adoptionGenerationAtom","adoptedRenewalReservationAtom","invalidateRefresh","queryClient","delete","initialAuthState","status","authStateAtom","authTransitionEpochAtom","toAuthenticatedState","session","workspaces","token","accessToken","user","useAuthState","state","isLoading","isAuthenticated","hasWorkspace","length","listUserWorkspaces","userWorkspacesQueryKey","decodeBase64Url","value","base64","replace","atob","padEnd","Math","ceil","readJwtExp","payload","split","undefined","parsed","JSON","parse","exp","Number","isFinite","getAuthRefreshDelayMs","nowMs","Date","now","getAdoptedSessionRenewDelayMs","expiresAt","serverTime","useAuthTransition","store","setState","setLastWorkspaceId","beginAuthTransition","transitionEpoch","get","set","clearPrivateState","epoch","cancelQueries","clear","enterGuest","isExternalTransition","enterAuthenticated","previousState","principalChanged","id","setQueryData","hydratedWorkspaces","fetchQuery","memberships","useRefreshAuth","existingRefresh","Promise","reject","message","code","refresh","result","accepted","error","then","useAdoptedSession","refreshAuth","adoptedSession","endAdoption","adoptSession","options","generation","receivedAtMs","performance","renew","stalledRenewals","renewAdoptedSession","adopted","candidateExpiryMs","candidateServerTimeMs","candidateValid","current","currentExpiryMs","bestReservedExpiryMs","max","windowDidNotAdvance","lostRace","stalled","releaseAdoptedSession","AuthRuntime","children","effects","authState","getAccessToken","refreshAccessToken","catch","timeout","disposed","refreshing","clearRefreshTimer","clearTimeout","scheduleRefresh","delayMs","setTimeout","runRefresh","retryIfStillDue","delay","finally","refreshIfDue","refreshIfVisible","document","visibilityState","window","addEventListener","removeEventListener","renewing","nextFireAtMs","clearRenewTimer","scheduleRenew","earlyFireAtMs","runRenew","renewIfDue","renewIfVisible"],"mappings":"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"}
@@ -3,7 +3,7 @@ export { FOCUSED_FRAME_CONTENT_CLASS_NAME, FocusedFrame, } from '#components/foc
3
3
  export { WorkspaceCrumb, type WorkspaceCrumbProps } from '#components/workspace-crumb.js';
4
4
  export { WorkspaceSwitcher } from '#components/workspace-switcher.js';
5
5
  export type { AuthenticatedSession, UserIdentity, WorkspaceMembership, WorkspaceSummary, } from '#core/session.js';
6
- export { toAuthenticatedSession, toUserIdentity } from '#hooks/api/session-mapper.js';
6
+ export { type SessionResponseDto, toAuthenticatedSession, toUserIdentity, } from '#hooks/api/session-mapper.js';
7
7
  export * from '../compose/compose-client-features.js';
8
8
  export * from '../compose/compose-routes.js';
9
9
  export * from '../compose/errors.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AACtF,OAAO,EACL,gCAAgC,EAChC,YAAY,GACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAC,cAAc,EAAE,KAAK,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AACxF,OAAO,EAAC,iBAAiB,EAAC,MAAM,mCAAmC,CAAC;AACpE,YAAY,EACV,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAC,sBAAsB,EAAE,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACpF,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,SAAS,EAAE,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AACtF,OAAO,EACL,gCAAgC,EAChC,YAAY,GACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAC,cAAc,EAAE,KAAK,mBAAmB,EAAC,MAAM,gCAAgC,CAAC;AACxF,OAAO,EAAC,iBAAiB,EAAC,MAAM,mCAAmC,CAAC;AACpE,YAAY,EACV,oBAAoB,EACpB,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,kBAAkB,EACvB,sBAAsB,EACtB,cAAc,GACf,MAAM,8BAA8B,CAAC;AACtC,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,gCAAgC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/runtime/index.ts"],"sourcesContent":["export {AuthActions, AuthShell, type AuthShellProps} from '#components/auth-shell.js';\nexport {\n FOCUSED_FRAME_CONTENT_CLASS_NAME,\n FocusedFrame,\n} from '#components/focused-frame.js';\nexport {WorkspaceCrumb, type WorkspaceCrumbProps} from '#components/workspace-crumb.js';\nexport {WorkspaceSwitcher} from '#components/workspace-switcher.js';\nexport type {\n AuthenticatedSession,\n UserIdentity,\n WorkspaceMembership,\n WorkspaceSummary,\n} from '#core/session.js';\nexport {toAuthenticatedSession, toUserIdentity} from '#hooks/api/session-mapper.js';\nexport * from '../compose/compose-client-features.js';\nexport * from '../compose/compose-routes.js';\nexport * from '../compose/errors.js';\nexport * from '../compose/merge-config.js';\nexport * from '../compose/normalize-route-path.js';\nexport * from '../compose/validate-providers.js';\nexport * from '../compose/validate-registries.js';\nexport * from './active-workspace.js';\nexport * from './anchor-paths.js';\nexport * from './anchors.js';\nexport * from './auth.js';\nexport * from './chrome-context.js';\nexport * from './client-analytics.js';\nexport * from './compose-client-app.js';\nexport * from './define-route.js';\nexport * from './last-workspace.js';\nexport * from './layout-navigation.js';\nexport * from './nav-order.js';\nexport * from './route-frame.js';\nexport * from './route-inputs.js';\nexport * from './router-context.js';\nexport * from './search-serialization.js';\nexport * from './workspace-setup.js';\nexport * from './workspace-setup-dismissal.js';\n"],"names":["AuthActions","AuthShell","FOCUSED_FRAME_CONTENT_CLASS_NAME","FocusedFrame","WorkspaceCrumb","WorkspaceSwitcher","toAuthenticatedSession","toUserIdentity"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,SAAS,QAA4B,4BAA4B;AACtF,SACEC,gCAAgC,EAChCC,YAAY,QACP,+BAA+B;AACtC,SAAQC,cAAc,QAAiC,iCAAiC;AACxF,SAAQC,iBAAiB,QAAO,oCAAoC;AAOpE,SAAQC,sBAAsB,EAAEC,cAAc,QAAO,+BAA+B;AACpF,cAAc,wCAAwC;AACtD,cAAc,+BAA+B;AAC7C,cAAc,uBAAuB;AACrC,cAAc,6BAA6B;AAC3C,cAAc,qCAAqC;AACnD,cAAc,mCAAmC;AACjD,cAAc,oCAAoC;AAClD,cAAc,wBAAwB;AACtC,cAAc,oBAAoB;AAClC,cAAc,eAAe;AAC7B,cAAc,YAAY;AAC1B,cAAc,sBAAsB;AACpC,cAAc,wBAAwB;AACtC,cAAc,0BAA0B;AACxC,cAAc,oBAAoB;AAClC,cAAc,sBAAsB;AACpC,cAAc,yBAAyB;AACvC,cAAc,iBAAiB;AAC/B,cAAc,mBAAmB;AACjC,cAAc,oBAAoB;AAClC,cAAc,sBAAsB;AACpC,cAAc,4BAA4B;AAC1C,cAAc,uBAAuB;AACrC,cAAc,iCAAiC"}
1
+ {"version":3,"sources":["../../src/runtime/index.ts"],"sourcesContent":["export {AuthActions, AuthShell, type AuthShellProps} from '#components/auth-shell.js';\nexport {\n FOCUSED_FRAME_CONTENT_CLASS_NAME,\n FocusedFrame,\n} from '#components/focused-frame.js';\nexport {WorkspaceCrumb, type WorkspaceCrumbProps} from '#components/workspace-crumb.js';\nexport {WorkspaceSwitcher} from '#components/workspace-switcher.js';\nexport type {\n AuthenticatedSession,\n UserIdentity,\n WorkspaceMembership,\n WorkspaceSummary,\n} from '#core/session.js';\nexport {\n type SessionResponseDto,\n toAuthenticatedSession,\n toUserIdentity,\n} from '#hooks/api/session-mapper.js';\nexport * from '../compose/compose-client-features.js';\nexport * from '../compose/compose-routes.js';\nexport * from '../compose/errors.js';\nexport * from '../compose/merge-config.js';\nexport * from '../compose/normalize-route-path.js';\nexport * from '../compose/validate-providers.js';\nexport * from '../compose/validate-registries.js';\nexport * from './active-workspace.js';\nexport * from './anchor-paths.js';\nexport * from './anchors.js';\nexport * from './auth.js';\nexport * from './chrome-context.js';\nexport * from './client-analytics.js';\nexport * from './compose-client-app.js';\nexport * from './define-route.js';\nexport * from './last-workspace.js';\nexport * from './layout-navigation.js';\nexport * from './nav-order.js';\nexport * from './route-frame.js';\nexport * from './route-inputs.js';\nexport * from './router-context.js';\nexport * from './search-serialization.js';\nexport * from './workspace-setup.js';\nexport * from './workspace-setup-dismissal.js';\n"],"names":["AuthActions","AuthShell","FOCUSED_FRAME_CONTENT_CLASS_NAME","FocusedFrame","WorkspaceCrumb","WorkspaceSwitcher","toAuthenticatedSession","toUserIdentity"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,SAAS,QAA4B,4BAA4B;AACtF,SACEC,gCAAgC,EAChCC,YAAY,QACP,+BAA+B;AACtC,SAAQC,cAAc,QAAiC,iCAAiC;AACxF,SAAQC,iBAAiB,QAAO,oCAAoC;AAOpE,SAEEC,sBAAsB,EACtBC,cAAc,QACT,+BAA+B;AACtC,cAAc,wCAAwC;AACtD,cAAc,+BAA+B;AAC7C,cAAc,uBAAuB;AACrC,cAAc,6BAA6B;AAC3C,cAAc,qCAAqC;AACnD,cAAc,mCAAmC;AACjD,cAAc,oCAAoC;AAClD,cAAc,wBAAwB;AACtC,cAAc,oBAAoB;AAClC,cAAc,eAAe;AAC7B,cAAc,YAAY;AAC1B,cAAc,sBAAsB;AACpC,cAAc,wBAAwB;AACtC,cAAc,0BAA0B;AACxC,cAAc,oBAAoB;AAClC,cAAc,sBAAsB;AACpC,cAAc,yBAAyB;AACvC,cAAc,iBAAiB;AAC/B,cAAc,mBAAmB;AACjC,cAAc,oBAAoB;AAClC,cAAc,sBAAsB;AACpC,cAAc,4BAA4B;AAC1C,cAAc,uBAAuB;AACrC,cAAc,iCAAiC"}