import { Role } from '@shared/domain/role'; /** * Dev-only role stand-in (the reading MECHANISM; the `Role` type is domain). This * POC has one faked self-service user and no real identities, so the two-person * letter workflow (drafter vs approver) plus admin is driven by a `?role=` query * param. The backend receives it as an `X-Role` header (see role.interceptor), * resolves it into a `Principal` server-side, and is the sole authority on what that * principal may do (PRD-0002 phase P1, `Authz.Can`) — the FE only renders the * resulting decision flags, it no longer derives permission from this value itself. * * **Sticky within the tab (sessionStorage):** the interceptor reads this per request, * but navigation drops the query param (login redirects to /dashboard, RouterLinks * don't carry it), which would silently revert an admin to drafter mid-session and * 403 the admin endpoints. So a `?role=` seen in the URL is remembered for the tab; * later requests use the remembered value. Set `?role=drafter` (or a fresh tab) to * reset. Dev-only — the interceptor itself is only wired under `isDevMode()`. */ const STORAGE_KEY = 'dev-role'; export const ROLES: readonly Role[] = ['drafter', 'approver', 'admin']; const isRole = (v: string | null): v is Role => !!v && ROLES.includes(v as Role); export function currentRole(): Role { const fromUrl = new URLSearchParams(window.location.search).get('role'); if (isRole(fromUrl)) { sessionStorage.setItem(STORAGE_KEY, fromUrl); return fromUrl; } const stored = sessionStorage.getItem(STORAGE_KEY); return isRole(stored) ? stored : 'drafter'; } /** Dev switcher entry point: persist the chosen role for the tab (WP-33). */ export function setRole(r: Role): void { sessionStorage.setItem(STORAGE_KEY, r); }