interface CreateCacheOptions { expires?: number; // Default expiration time for all cache entries } interface SetCacheOptions { expires?: number; // Override expiration for individual cache entries } export const caches = new Map< string, { info: () => { name: string; count: number; sizeInKB: number }; clear: () => void; } >(); export function createCache( cacheName: string, createOpts: CreateCacheOptions = {}, ) { const cache = new Map(); const api = { get(key: string): T | undefined { const entry = cache.get(key); if (!entry) return undefined; const now = Date.now(); if (entry.expiresAt && entry.expiresAt <= now) { cache.delete(key); // Remove expired entry return undefined; } return entry.value; // Return value if not expired }, clear() { cache.clear(); }, set(key: string, value: T | unknown, opts: SetCacheOptions = {}) { const now = Date.now(); const expiresIn = opts.expires ?? createOpts.expires; const expiresAt = expiresIn ? now + expiresIn : undefined; cache.set(key, { value: value as T, expiresAt }); }, cleanup() { const now = Date.now(); for (const [key, entry] of cache.entries()) { if (entry.expiresAt && entry.expiresAt <= now) { cache.delete(key); } } }, info() { // Cleanup expired entries before calculating info this.cleanup(); // Count the number of objects in the cache const count = cache.size; // Approximate the size in KB by serializing each key and value let totalBytes = 0; for (const [key, entry] of cache.entries()) { const keySize = new TextEncoder().encode(key).length; const valueSize = new TextEncoder().encode( JSON.stringify(entry.value), ).length; totalBytes += keySize + valueSize; } const sizeInKB = Math.floor(totalBytes / 1024); // Convert bytes to kilobytes return { name: cacheName, count, sizeInKB }; }, has(key: string): boolean { const entry = cache.get(key); if (!entry) return false; const now = Date.now(); if (entry.expiresAt && entry.expiresAt <= now) { cache.delete(key); // Remove expired entry return false; } return true; }, keys(): string[] { this.cleanup(); // Cleanup before returning keys return Array.from(cache.keys()); }, size(): number { this.cleanup(); // Cleanup before returning size return cache.size; }, }; caches.set(cacheName, { clear: api.clear.bind(api), info: api.info.bind(api), }); return api; } export function getCacheInfo() { return [...caches.values().map((c) => c.info())]; }