File size: 2,920 Bytes
2fe29a7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 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; } };
}
|