interface RateLimitEntry { count: number; resetAt: number; } const store = new Map(); const DEFAULT_WINDOW_MS = 60 * 60 * 1000; // 1 hour const DEFAULT_MAX_REQUESTS = 60; // Clean up expired entries periodically setInterval(() => { const now = Date.now(); for (const [key, entry] of store) { if (now > entry.resetAt) { store.delete(key); } } }, 60 * 1000); export function checkRateLimit( key: string, options?: { windowMs?: number; maxRequests?: number }, ): { allowed: boolean; remaining: number; retryAfterSeconds?: number } { const windowMs = options?.windowMs ?? DEFAULT_WINDOW_MS; const maxRequests = options?.maxRequests ?? DEFAULT_MAX_REQUESTS; const now = Date.now(); let entry = store.get(key); if (!entry || now > entry.resetAt) { entry = { count: 0, resetAt: now + windowMs }; store.set(key, entry); } entry.count++; if (entry.count > maxRequests) { const retryAfterSeconds = Math.ceil((entry.resetAt - now) / 1000); return { allowed: false, remaining: 0, retryAfterSeconds }; } return { allowed: true, remaining: maxRequests - entry.count }; }