import type { NativeCacheConfig } from '../types' export interface LocalStorageAdapterOptions { /** * Key prefix applied to every store key to avoid collisions with other * data in the same storage. Defaults to 'zse:'. * Example: prefix 'app:' + key 'user' → stored as 'app:user'. */ prefix?: string /** * Storage backend. Defaults to window.localStorage. * Pass window.sessionStorage for session-scoped persistence, * or a custom object for testing. */ storage?: Pick } export interface LocalStorageAdapter { nativeCache: NativeCacheConfig /** Remove a single entry from storage. */ remove: (key: string) => void /** Remove all entries whose storage key starts with the configured prefix. */ clear: () => void } /** * Creates a NativeCacheConfig backed by localStorage (or any Storage-compatible object). * * Usage: * const browserCache = createLocalStorageAdapter({ prefix: 'myapp:' }) * const engine = new WorkflowEngine({ queryClient, nativeCache: browserCache.nativeCache }) * * Session-scoped (cleared on tab close): * createLocalStorageAdapter({ storage: window.sessionStorage }) */ export function createLocalStorageAdapter( options: LocalStorageAdapterOptions = {}, ): LocalStorageAdapter { const prefix = options.prefix ?? 'zse:' const getStorage = (): Pick => options.storage ?? globalThis.localStorage const storageKey = (key: string) => `${prefix}${key}` const retrieve = async (key: string): Promise => { try { const raw = getStorage().getItem(storageKey(key)) if (raw == null) return null return JSON.parse(raw) } catch { return null } } const upsert = async (key: string, data: unknown): Promise => { try { getStorage().setItem(storageKey(key), JSON.stringify(data)) } catch { // Swallow QuotaExceededError — cache miss on next load is acceptable } } const remove = (key: string): void => { try { getStorage().removeItem(storageKey(key)) } catch { // ignore } } const clear = (): void => { try { const storage = getStorage() as Storage const keysToRemove: string[] = [] for (let i = 0; i < storage.length; i++) { const k = storage.key(i) if (k?.startsWith(prefix)) keysToRemove.push(k) } for (const k of keysToRemove) storage.removeItem(k) } catch { // ignore } } return { nativeCache: { retrieve, upsert }, remove, clear } }