import { combatantId, createEncounter, type Encounter, isDomainError, } from "@initiative/domain"; const STORAGE_KEY = "initiative:encounter"; export function saveEncounter(encounter: Encounter): void { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(encounter)); } catch { // Silently swallow errors (quota exceeded, storage unavailable) } } export function loadEncounter(): Encounter | null { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw === null) return null; const parsed: unknown = JSON.parse(raw); if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null; const obj = parsed as Record; if (!Array.isArray(obj.combatants)) return null; if (typeof obj.activeIndex !== "number") return null; if (typeof obj.roundNumber !== "number") return null; const combatants = obj.combatants as unknown[]; for (const c of combatants) { if (typeof c !== "object" || c === null || Array.isArray(c)) return null; const entry = c as Record; if (typeof entry.id !== "string") return null; if (typeof entry.name !== "string") return null; } const rehydrated = combatants.map((c) => { const entry = c as Record; const base = { id: combatantId(entry.id as string), name: entry.name as string, initiative: typeof entry.initiative === "number" ? entry.initiative : undefined, }; // Validate AC field const ac = entry.ac; const validAc = typeof ac === "number" && Number.isInteger(ac) && ac >= 0 ? ac : undefined; // Validate and attach HP fields if valid const maxHp = entry.maxHp; const currentHp = entry.currentHp; if (typeof maxHp === "number" && Number.isInteger(maxHp) && maxHp >= 1) { const validCurrentHp = typeof currentHp === "number" && Number.isInteger(currentHp) && currentHp >= 0 && currentHp <= maxHp; return { ...base, ac: validAc, maxHp, currentHp: validCurrentHp ? currentHp : maxHp, }; } return { ...base, ac: validAc }; }); const result = createEncounter( rehydrated, obj.activeIndex, obj.roundNumber, ); if (isDomainError(result)) return null; return result; } catch { return null; } }