Skip to content
Clone
main
83 lines 2.5 KB
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() {},
}
}