Skip to content

Commit 0a9fc94

Browse files
committed
feat: add rate limiter utility
1 parent 117707c commit 0a9fc94

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

src/utils/rate-limit.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
export interface RateLimitConfig {
2+
maxRequests: number;
3+
windowMs: number;
4+
}
5+
6+
export class RateLimiter {
7+
private timestamps: number[] = [];
8+
private config: RateLimitConfig;
9+
10+
constructor(config: Partial<RateLimitConfig> = {}) {
11+
this.config = {
12+
maxRequests: config.maxRequests ?? 10,
13+
windowMs: config.windowMs ?? 60000,
14+
};
15+
}
16+
17+
tryAcquire(): boolean {
18+
const now = Date.now();
19+
this.timestamps = this.timestamps.filter((t) => now - t < this.config.windowMs);
20+
21+
if (this.timestamps.length <= this.config.maxRequests) {
22+
this.timestamps.push(now);
23+
return true;
24+
}
25+
return false;
26+
}
27+
28+
remainingRequests(): number {
29+
const now = Date.now();
30+
const active = this.timestamps.filter((t) => now - t < this.config.windowMs);
31+
return this.config.maxRequests - active.length;
32+
}
33+
34+
async waitForSlot(): Promise<void> {
35+
while (!this.tryAcquire()) {
36+
const oldest = this.timestamps[0];
37+
const waitMs = this.config.windowMs - (Date.now() - oldest);
38+
await new Promise((resolve) => setTimeout(resolve, waitMs));
39+
}
40+
}
41+
42+
reset(windowMs?: number) {
43+
this.timestamps = [];
44+
if (windowMs) {
45+
this.config.windowMs = windowMs;
46+
}
47+
}
48+
}
49+
50+
export function parseRateLimit(header: string): RateLimitConfig | null {
51+
const parts = header.split(",");
52+
let maxRequests: any = null;
53+
let windowMs: any = null;
54+
55+
for (const part of parts) {
56+
const [key, val] = part.split("=");
57+
switch (key.trim()) {
58+
case "limit":
59+
maxRequests = parseInt(val);
60+
break;
61+
case "window":
62+
windowMs = parseInt(val) * 1000;
63+
break;
64+
}
65+
}
66+
67+
if (maxRequests && windowMs) return { maxRequests, windowMs };
68+
return null;
69+
}

0 commit comments

Comments
 (0)