import { type CacheEnvelope, ENVELOPE_VERSION, type KVDriver } from './driver' /** * localStorage driver — the automatic fallback when IndexedDB is unavailable * (privacy modes, blocked storage, corrupt profiles). * * Two constraints shape it: * - the ~5MB quota is shared with the whole origin, so a single oversized * value (a big repo tree, a long transcript seed) is skipped instead of * evicting everything else; * - every failure is contained here and surfaces as a rejected promise, which * the cache facade treats as "this write did not happen" — the app must run * identically with a full, empty or broken storage backend. */ const LS_PREFIX = 'kernel-cache:' const PROBE_KEY = '__probe__' /** Above this serialized size a value is not worth risking the quota. */ const MAX_VALUE_BYTES = 1_500_000 export function createLocalStorageDriver(): KVDriver { const storageKey = (key: string) => LS_PREFIX + key const parse = (raw: string | null): CacheEnvelope | undefined => { if (raw === null) return undefined try { const value = JSON.parse(raw) as CacheEnvelope return value && value.v === ENVELOPE_VERSION ? value : undefined } catch { return undefined } } return { name: 'localstorage', async get(key) { return parse(localStorage.getItem(storageKey(key))) }, async set(key, value) { const raw = JSON.stringify(value) if (raw.length > MAX_VALUE_BYTES) return localStorage.setItem(storageKey(key), raw) }, async del(key) { localStorage.removeItem(storageKey(key)) }, async clearPrefix(prefix) { const full = storageKey(prefix) const doomed: string[] = [] for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i) if (k !== null && k.startsWith(full)) doomed.push(k) } for (const k of doomed) localStorage.removeItem(k) }, async probe() { const probeStorage = storageKey(PROBE_KEY) localStorage.setItem(probeStorage, '1') const ok = localStorage.getItem(probeStorage) === '1' localStorage.removeItem(probeStorage) if (!ok) throw new Error('localStorage probe roundtrip failed') }, } } /** Last resort when every persistent backend is blocked: L1 memory only. */ export function createNoopDriver(): KVDriver { return { name: 'memory', async get() { return undefined }, async set() {}, async del() {}, async clearPrefix() {}, async probe() {}, } }