/** In-memory sliding-window limiter (single-process deployment). Keys are scoped by the caller. */ const buckets = new Map(); export function rateLimit(key: string, max: number, windowMs: number) { const now = Date.now(); const arr = (buckets.get(key) ?? []).filter((t) => now - t < windowMs); if (arr.length >= max) { buckets.set(key, arr); return false; } arr.push(now); buckets.set(key, arr); if (buckets.size > 10000) for (const [k, v] of buckets) if (!v.some((t) => now - t < windowMs)) buckets.delete(k); return true; }