type NavigateFn = (to: string, opts?: { replace?: boolean }) => void; let navigateFn: NavigateFn | null = null; /** Register (or clear) the router's navigate fn. Called by NavigationBridge. */ export function setNavigate(fn: NavigateFn | null): void { navigateFn = fn; } /** Soft-redirect to login on a 401, preserving SPA state and the return path. * Falls back to a hard navigation when no router navigate is registered yet * (e.g. a 401 during the very first load). No-op when already on /login. */ export function redirectToLogin(): void { const { pathname, search } = window.location; if (pathname === "/login") return; const from = encodeURIComponent(pathname + search); const target = `/login?reason=expired&from=${from}`; if (navigateFn) { navigateFn(target, { replace: true }); } else { window.location.assign(target); } }