// rate-limiter.ts — Sliding window rate limiter // v1: Claude (initial implementation with known TODOs) // v2: ChatGPT (fixed memory leak, added response metadata, cleanup interval) interface RateLimitConfig { windowMs: number; maxRequests: number; } interface RateLimitResult { limited: boolean; remaining: number; resetMs: number; retryAfterMs: number | null; } const store = new Map<string, number[]>(); // Fix: periodic cleanup of expired keys (was: memory leak) const CLEANUP_INTERVAL = 60_000; setInterval(() => { const now = Date.now(); for (const [key, timestamps] of store) { const recent = timestamps.filter(t => t > now - 3_600_000); // keep 1hr max if (recent.length === 0) { store.delete(key); } else { store.set(key, recent); } } }, CLEANUP_INTERVAL); export function checkRateLimit(key: string, config: RateLimitConfig): RateLimitResult { const now = Date.now(); const windowStart = now - config.windowMs; const timestamps = (store.get(key) || []).filter(t => t > windowStart); const remaining = Math.max(0, config.maxRequests - timestamps.length); const oldestInWindow = timestamps[0] || now; const resetMs = oldestInWindow + config.windowMs - now; if (timestamps.length >= config.maxRequests) { store.set(key, timestamps); return { limited: true, remaining: 0, resetMs, retryAfterMs: resetMs, }; } timestamps.push(now); store.set(key, timestamps); return { limited: false, remaining: remaining - 1, resetMs: config.windowMs, retryAfterMs: null, }; }
1 file changed (1 modified)
Log in to leave a comment.