import { LRUCache } from 'lru-cache'; import { CacheAdapter } from './types.js'; export class MemoryCache implements CacheAdapter { private cache: LRUCache; constructor() { const defaultTtl = process.env.CACHE_DEFAULT_TTL ? parseInt(process.env.CACHE_DEFAULT_TTL) : 1000 * 60 * 5; // 5 mins default this.cache = new LRUCache({ max: 1000, ttl: defaultTtl, updateAgeOnGet: false, }); } async get(key: string): Promise { const value = this.cache.get(key); return (value as T) || null; } async set(key: string, value: T, ttl?: number): Promise { this.cache.set(key, value, { ttl: ttl ? ttl * 1000 : undefined }); } async del(key: string): Promise { this.cache.delete(key); } async flush(pattern?: string): Promise { if (pattern) { // Support simple wildcard patterns (e.g. "home-feed*", "*-feed*") // Escape special regex chars except *, then replace * with .* const regexPattern = pattern .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape regex chars .replace(/\*/g, '.*'); // Convert * to .* const regex = new RegExp(`^${regexPattern}$`); for (const key of this.cache.keys()) { if (typeof key === 'string' && regex.test(key)) { this.cache.delete(key); } } } else { this.cache.clear(); } } keys(): string[] { return [...this.cache.keys()]; } info() { return { size: this.cache.size, max: this.cache.max, provider: 'memory-lru', }; } entries() { return [...this.cache.keys()].map(key => ({ key, remainingTTL: this.cache.getRemainingTTL(key), })); } }