// Session state is kept in memory only — NEVER localStorage/sessionStorage/URLs. // It holds (a) the bearer token and (b) the active demo user. Both are lost on a // full page refresh by design (in-memory only); this is acceptable for the trusted // demo environment and documented in the UI ("sign in again after refresh"). // If the backend later moves to HTTP-only cookies, `credentials: 'include'` on the // client already carries them and the token half of this module becomes a no-op. export interface ActiveUser { userId: string via: 'demo' | 'passkey' } let token: string | null = null let activeUser: ActiveUser | null = null const listeners = new Set<() => void>() function emit(): void { listeners.forEach((fn) => fn()) } export function setToken(t: string | null): void { token = t emit() } export function getToken(): string | null { return token } export function setActiveUser(user: ActiveUser | null): void { activeUser = user emit() } export function getActiveUser(): ActiveUser | null { return activeUser } export function isAuthenticated(): boolean { return token !== null } /** Clears both the token and the active user. */ export function logout(): void { token = null activeUser = null emit() } export function onSessionChange(fn: () => void): () => void { listeners.add(fn) return () => listeners.delete(fn) }