const MINUTE = 1000 * 60; const DEFAULT_TTL = 5 * MINUTE; export interface TtlCacheOptions { ttlMs?: number; } export class TtlCache { private readonly data: Map; private readonly timers: Map>; private readonly ttl: number; constructor({ ttlMs = DEFAULT_TTL }: TtlCacheOptions = {}) { if (ttlMs < 0) { throw new Error('default TTL must be zero or greater'); } this.data = new Map(); this.timers = new Map(); this.ttl = ttlMs; } set(key: string, value: T, ttl?: number) { this.setTimeout(key, ttl ?? this.ttl); this.data.set(key, value); } get(key: string) { return this.data.get(key); } has(key: string) { return this.data.has(key); } delete(key: string) { this.cancelTimeout(key); this.data.delete(key); } clear() { this.data.clear(); this.timers.forEach(value => clearTimeout(value)); this.timers.clear(); } private cancelTimeout(key: string) { const id = this.timers.get(key); if (id) { clearTimeout(id); this.timers.delete(key); } } private setTimeout(key: string, ttl: number) { this.cancelTimeout(key); if (ttl < 0) { return; } const id = setTimeout(() => this.delete(key), ttl); this.timers.set(key, id); } }