type CacheEngine = 'session' | 'local'; interface CachedItem { value: T; ttl: number | null; time: number; } class Cache { private storage: Storage; private supportedEngines: CacheEngine[] = ['session', 'local']; constructor(engine: CacheEngine = 'session') { if (!this.supportedEngines.includes(engine)) { throw new Error(`Engine '${engine}' not supported.`); } this.storage = 'session' === engine ? window.sessionStorage : window.localStorage; } set(key: string, value: T, ttl: number | null = null): void { const item: CachedItem = { value, ttl, time: Date.now() }; this.storage.setItem(key, JSON.stringify(item)); } get(key: string): T | null { const item = this.storage.getItem(key); if (null === item) { return null; } try { const parsed: CachedItem = JSON.parse(item); if (null !== parsed.ttl && Date.now() - parsed.time > parsed.ttl) { this.remove(key); return null; } return parsed.value; } catch { return item as unknown as T; } } remove(key: string): void { this.storage.removeItem(key); } clear(filter: ((key: string) => boolean) | null = null): void { if (null !== filter) { Object.keys(this.storage) .filter(filter) .forEach((k) => this.storage.removeItem(k)); return; } this.storage.clear(); } has(key: string): boolean { return null !== this.get(key); } } const useLocalCache = (): Cache => new Cache('local'); const useSessionCache = (): Cache => new Cache('session'); export { useSessionCache, useLocalCache, Cache };