Refactor runtime bootstrap and ship control flows

This commit is contained in:
2026-04-03 01:12:26 -04:00
parent 0bb72bee35
commit 706e1cda8f
129 changed files with 9588 additions and 3548 deletions

View File

@@ -0,0 +1,76 @@
import type { AuthSessionResponse } from "./contractsAuth";
const STORAGE_KEY = "space-game.auth.session";
export interface AuthSession {
userId: string;
email: string;
roles: string[];
accessToken: string;
accessTokenExpiresAtUtc: string;
refreshToken: string;
refreshTokenExpiresAtUtc: string;
}
let currentSession: AuthSession | null = loadStoredSession();
const listeners = new Set<(session: AuthSession | null) => void>();
export function getAuthSession(): AuthSession | null {
return currentSession;
}
export function setAuthSession(session: AuthSessionResponse | null) {
currentSession = session ? { ...session } : null;
persistSession(currentSession);
notifyListeners();
}
export function clearAuthSession() {
currentSession = null;
persistSession(null);
notifyListeners();
}
export function subscribeToAuthSession(listener: (session: AuthSession | null) => void) {
listeners.add(listener);
return () => listeners.delete(listener);
}
function loadStoredSession(): AuthSession | null {
if (typeof window === "undefined") {
return null;
}
const raw = window.localStorage.getItem(STORAGE_KEY);
if (!raw) {
return null;
}
try {
const parsed = JSON.parse(raw) as AuthSession;
return parsed?.accessToken && parsed?.refreshToken
? { ...parsed, roles: Array.isArray(parsed.roles) ? parsed.roles : [] }
: null;
} catch {
return null;
}
}
function persistSession(session: AuthSession | null) {
if (typeof window === "undefined") {
return;
}
if (!session) {
window.localStorage.removeItem(STORAGE_KEY);
return;
}
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(session));
}
function notifyListeners() {
for (const listener of listeners) {
listener(currentSession);
}
}