11import { TTLCache } from './cache' ;
22
3- // In-memory rate limiter to prevent basic DoS/spam (Denial of Wallet).
4- // Note: In a serverless environment, this resets per cold-start/instance,
5- // but it is highly effective at stopping aggressive single-instance spikes.
6- // For multi-instance strict syncing, a Redis store (Vercel KV/Upstash) should be used.
3+ /**
4+ * In-memory rate limiter to prevent basic DoS/spam (Denial of Wallet).
5+ *
6+ * Note: In a serverless environment, this resets per cold-start/instance,
7+ * but it is highly effective at stopping aggressive single-instance spikes.
8+ * For multi-instance strict syncing, a Redis store (Vercel KV/Upstash) should be used.
9+ */
710export class RateLimiter {
811 private cache : TTLCache < number > ;
912 private limit : number ;
1013 private windowMs : number ;
1114
15+ /**
16+ * Creates a new RateLimiter instance.
17+ *
18+ * @param limit - Maximum number of requests allowed per window. Defaults to 5.
19+ * @param windowMs - Time window in milliseconds. Defaults to 60000 (1 minute).
20+ */
1221 constructor ( limit = 5 , windowMs = 60000 ) {
1322 this . limit = limit ;
1423 this . windowMs = windowMs ;
15- // Set max capacity to 10000 IPs to prevent memory leaks from the rate limiter itself
1624 this . cache = new TTLCache < number > ( 10000 , windowMs ) ;
1725 }
1826
19- // Returns true if allowed, false if rate limited
27+ /**
28+ * Checks whether a request from the given IP is allowed.
29+ *
30+ * Increments the request count for the IP and resets the TTL on each call,
31+ * behaving similarly to a sliding window timeout.
32+ *
33+ * @param ip - The IP address to check.
34+ * @returns `true` if the request is allowed, `false` if rate limited.
35+ *
36+ * @example
37+ * if (!rateLimiter.check(ip)) {
38+ * return new Response("Too Many Requests", { status: 429 });
39+ * }
40+ */
2041 check ( ip : string ) : boolean {
2142 const current = this . cache . get ( ip ) || 0 ;
2243 if ( current >= this . limit ) {
2344 return false ;
2445 }
25- // We increment the count and reset the TTL, behaving similarly to a sliding window timeout.
2646 this . cache . set ( ip , current + 1 , this . windowMs ) ;
2747 return true ;
2848 }
@@ -47,6 +67,20 @@ interface RateLimitResult {
4767
4868const trackers = new TTLCache < { count : number } > ( 2000 , 60000 ) ;
4969
70+ /**
71+ * Checks if a request from a given IP should be rate limited.
72+ *
73+ * @param ip - The IP address to track.
74+ * @param limit - Maximum number of requests allowed in the window. Defaults to 60.
75+ * @param windowMs - Time window in milliseconds. Defaults to 60000 (1 minute).
76+ * @returns A {@link RateLimitResult} containing success status, limit, remaining count, and reset time.
77+ *
78+ * @example
79+ * const result = rateLimit(ip);
80+ * if (!result.success) {
81+ * return new Response("Too Many Requests", { status: 429 });
82+ * }
83+ */
5084export function rateLimit (
5185 ip : string ,
5286 limit : number = 60 ,
0 commit comments