refactor(confidence-engine): add investigation storage provider

This commit is contained in:
2026-08-27 15:24:38 +01:00
parent 22e1d7484b
commit ba956eeb3a
3 changed files with 344 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
// investigation-storage — generic persistence boundary
// Exposes loadInvestigation / saveInvestigation / clearInvestigation
// and delegates internally to the concrete localStorage provider.
export { loadInvestigation, saveInvestigation, clearInvestigation } from "./providers/local-storage.js";
+111
View File
@@ -0,0 +1,111 @@
// ── canonical identifiers ────────────────────────────────────────────
const CANONICAL_KEY = "confidence-engine-investigation";
const LEGACY_KEY = "confidence-engine-session";
const SCHEMA_VERSION = 1;
// ── helpers ──────────────────────────────────────────────────────────
function safeGet(storage, key) {
if (!storage || typeof storage.getItem !== "function") return null;
try { return storage.getItem(key); } catch (_) { return null; }
}
function isPlainObject(value) {
return (
value !== null &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.prototype.toString.call(value) === "[object Object]"
);
}
// ── loadInvestigation ────────────────────────────────────────────────
/**
* Returns the persisted investigation snapshot (normalised to current schema)
* or null when no usable state exists.
*/
export function loadInvestigation() {
const storage = _getTargetStorage();
if (!storage) return null;
// 1 canonical key first
let raw = safeGet(storage, CANONICAL_KEY);
if (raw !== null) {
try {
const parsed = JSON.parse(raw);
if (isPlainObject(parsed)) {
if (!("schemaVersion" in parsed)) parsed.schemaVersion = SCHEMA_VERSION;
return parsed;
}
} catch (_) { /* malformed — fall through to legacy */ }
}
// 2 legacy fallback
const legacyStorage = _getLegacyStorage();
if (!legacyStorage) return null;
raw = safeGet(legacyStorage, LEGACY_KEY);
if (raw === null) return null;
let parsed;
try {
parsed = JSON.parse(raw);
} catch (_) { return null; }
if (!isPlainObject(parsed)) return null;
// Normalise schemaVersion for legacy data
if (!("schemaVersion" in parsed)) parsed.schemaVersion = SCHEMA_VERSION;
// Migrate into canonical key
_persist(storage, CANONICAL_KEY, JSON.stringify(parsed));
return parsed;
}
// ── saveInvestigation ────────────────────────────────────────────────
/**
* Persists the supplied snapshot to the canonical localStorage key.
* The caller's object is never mutated.
*/
export function saveInvestigation(snapshot) {
const storage = _getTargetStorage();
if (!storage) return; // silently no-op in non-browser
try {
const record = JSON.parse(JSON.stringify(snapshot));
record.schemaVersion = SCHEMA_VERSION;
_persist(storage, CANONICAL_KEY, JSON.stringify(record));
} catch (_) { /* storage errors must not crash caller */ }
}
// ── clearInvestigation ───────────────────────────────────────────────
/** Removes only the canonical investigation key. */
export function clearInvestigation() {
const storage = _getTargetStorage();
if (!storage) return;
try { storage.removeItem(CANONICAL_KEY); } catch (_) {}
}
// ── internals ────────────────────────────────────────────────────────
function _persist(storage, key, value) {
try { storage.setItem(key, value); } catch (_) {}
}
function _getTargetStorage() {
if (typeof globalThis.window === "undefined") return null;
const s = globalThis.window.localStorage;
if (!s || typeof s.getItem !== "function") return null;
return s;
}
function _getLegacyStorage() {
if (typeof globalThis.window === "undefined") return null;
const s = globalThis.window.sessionStorage;
if (!s || typeof s.getItem !== "function") return null;
return s;
}