import { createSession, validateSession } from "./domain.js"; export const STORAGE_KEY = "branchjam:session:v1"; const canonicalValue = (value) => { if (Array.isArray(value)) return value.map(canonicalValue); if (!value || typeof value !== "object") return value; return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalValue(value[key])])); }; export function serializeSession(state) { const ordered = { schemaVersion: state.schemaVersion, catalogVersion: state.catalogVersion, phase: state.phase, seedNodeId: state.seedNodeId, activeNodeId: state.activeNodeId, inspectedNodeId: state.inspectedNodeId, selectedConstraintId: state.selectedConstraintId, currentRoundId: state.currentRoundId, compareSelection: state.compareSelection, nodesById: canonicalValue(state.nodesById), roundsById: canonicalValue(state.roundsById) }; return JSON.stringify(ordered); } export function createPersistence(storage, source) { let memory = null; let available = true; const save = (state) => { const serialized = serializeSession(state); memory = serialized; try { storage.setItem(STORAGE_KEY, serialized); return { ok: true, available, notice: "Local session saved." }; } catch { available = false; return { ok: false, available, notice: "Storage could not save this session; play continues in memory only." }; } }; const load = () => { let raw = memory; try { raw = storage.getItem(STORAGE_KEY) ?? raw; } catch { available = false; } if (!raw) return { state: createSession(source), recovered: false, available, notice: available ? null : "Storage is unavailable; this session continues in memory only." }; try { const state = JSON.parse(raw); if (!validateSession(state, source)) throw new Error("invalid"); return { state, recovered: false, available, notice: available ? null : "Storage is unavailable; the in-memory session was restored for this page only." }; } catch { memory = null; try { storage.removeItem(STORAGE_KEY); } catch { available = false; } return { state: createSession(source), recovered: true, available, notice: available ? "Saved session was invalid or incompatible and was removed. A fresh local session is ready." : "Saved session was invalid or incompatible. A fresh in-memory session is ready; storage cleanup was unavailable." }; } }; const reset = () => { memory = null; try { storage.removeItem(STORAGE_KEY); } catch { available = false; } return { state: createSession(source), available, notice: available ? "Local session reset to the seed." : "Session reset to the seed in memory; storage cleanup was unavailable." }; }; return { save, load, reset, get available() { return available; } }; }