/** * Persistence driver contract for the local-first cache. * * One envelope shape travels through every layer (memory, IndexedDB, * localStorage), so a value written by one driver stays readable after the * page degrades to another: `v` guards against envelope changes between * builds, `at` powers the prefetch throttle window. */ export const ENVELOPE_VERSION = 1 export interface CacheEnvelope { /** Envelope schema version — entries from other versions are discarded. */ v: number /** Write timestamp in ms, used for prefetch freshness checks. */ at: number data: T } export interface KVDriver { readonly name: 'indexeddb' | 'localstorage' | 'memory' get(key: string): Promise set(key: string, value: CacheEnvelope): Promise del(key: string): Promise /** Remove every key starting with `prefix`; `''` wipes the whole store. */ clearPrefix(prefix: string): Promise /** Real write-read-delete roundtrip; rejects when the backend is unusable. */ probe(): Promise } export function withTimeout(promise: Promise, ms: number, reason: string): Promise { return new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error(reason)), ms) promise.then( (value) => { clearTimeout(timer) resolve(value) }, (err) => { clearTimeout(timer) reject(err) }, ) }) }