export const CROSS_TAB_CHANNEL_NAME = 'hipctf.auth.v1'; export const CROSS_TAB_STORAGE_KEY = 'hipctf.auth.invalidate.v1'; export const CROSS_TAB_EVENT_TYPE = 'hipctf.session.invalidated'; export interface CrossTabInvalidationMessage { type: 'session-invalidated'; origin: string; ts: number; } export function encodeInvalidationMessage(origin: string): CrossTabInvalidationMessage { return { type: 'session-invalidated', origin, ts: Date.now(), }; } export function isCrossTabInvalidationMessage( value: unknown, ): value is CrossTabInvalidationMessage { if (!value || typeof value !== 'object') return false; const v = value as Record; return ( v['type'] === 'session-invalidated' && typeof v['origin'] === 'string' && typeof v['ts'] === 'number' ); } export function isStorageInvalidationEvent( event: { key?: string | null; newValue?: string | null } | null | undefined, ): boolean { if (!event) return false; if (event.key !== CROSS_TAB_STORAGE_KEY) return false; if (event.newValue == null) return false; try { const parsed = JSON.parse(event.newValue) as unknown; return isCrossTabInvalidationMessage(parsed); } catch { return false; } }