| 122 | const rateBuckets = new Map<string, RateBucket>(); |
| 123 | |
| 124 | function checkRateLimit(clientId: string, limit: number): { allowed: boolean; retryAfterMs?: number } { |
| 125 | if (limit <= 0) return { allowed: true }; |
| 126 | |
| 127 | const now = Date.now(); |
| 128 | const bucket = rateBuckets.get(clientId); |
| 129 | |
| 130 | if (!bucket || now - bucket.windowStart >= 1000) { |
| 131 | rateBuckets.set(clientId, { count: 1, windowStart: now }); |
| 132 | return { allowed: true }; |
| 133 | } |
| 134 | |
| 135 | if (bucket.count >= limit) { |
| 136 | const retryAfterMs = 1000 - (now - bucket.windowStart); |
| 137 | return { allowed: false, retryAfterMs: Math.max(retryAfterMs, 100) }; |
| 138 | } |
| 139 | |
| 140 | bucket.count++; |
| 141 | return { allowed: true }; |
| 142 | } |
| 143 | |
| 144 | // ─── Token Registry ───────────────────────────────────────────── |
| 145 | |