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 }