import { derived, writable } from 'svelte/store'; import { getRole } from './auth'; export type Role = 'guest' | 'host' | 'admin'; /** * The viewer's LIVE role. * * The JWT is never reissued — the backend slides the session row forward instead and * deliberately ignores the token's own role claim (`auth/middleware.rs`: "the live user row * is authoritative"). So `getRole()`, which decodes the claim, is frozen for the lifetime of * the token: up to 30 days for a guest. A guest promoted to host saw no Host-Dashboard until * they signed out and back in, even though `/me/context` had already told the client their * real role on the very next page load — it was fetched and the `role` field dropped on the * floor in 4 of its 6 call sites. * * This store is seeded from the claim (so there is no flash of the wrong nav on boot) and * corrected by every `/me/context` response via `setRole`. Read this instead of calling * `getRole()` ad hoc. */ export const role = writable(getRole()); /** True for host and admin — the "can moderate" predicate used across the UI. */ export const isStaff = derived(role, ($role) => $role === 'host' || $role === 'admin'); /** Apply the authoritative role from a `/me/context` response. */ export function setRole(next: Role | null): void { role.set(next); } /** Re-seed from the token, e.g. straight after a login/join that minted a new one. */ export function syncRoleFromToken(): void { role.set(getRole()); }