/**
* Rate Limiter for API Requests
* Limits concurrent requests to prevent API overload
*/
export class RateLimiter {
private queue: Array<() => void> = [];
private activeRequests: number = 0;
private maxConcurrent: number;
private requestsPerSecond: number;
private lastRequestTime: number = 0;
constructor(maxConcurrent: number = 5, requestsPerSecond: number = 10) {
this.maxConcurrent = maxConcurrent;
this.requestsPerSecond = requestsPerSecond;
}
/**
* Execute a function with rate limiting
*/
async execute<T>(fn: () => Promise<T>): Promise<T> {
// Wait for a slot to become available
await this.waitForSlot();
this.activeRequests++;
try {
// Enforce requests per second limit
await this.enforceRateLimit();
const result = await fn();
return result;
} finally {
this.activeRequests--;
this.processQueue();
}
}
/**
* Wait until there's an available slot
*/
private waitForSlot(): Promise<void> {
if (this.activeRequests < this.maxConcurrent) {
return Promise.resolve();
}
return new Promise((resolve) => {
this.queue.push(resolve);
});
}
/**
* Enforce requests per second limit
*/
private async enforceRateLimit(): Promise<void> {
const now = Date.now();
const minInterval = 1000 / this.requestsPerSecond;
const timeSinceLastRequest = now - this.lastRequestTime;
if (timeSinceLastRequest < minInterval) {
const waitTime = minInterval - timeSinceLastRequest;
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.lastRequestTime = Date.now();
}
/**
* Process next item in queue
*/
private processQueue(): void {
if (this.queue.length > 0 && this.activeRequests < this.maxConcurrent) {
const resolve = this.queue.shift();
if (resolve) {
resolve();
}
}
}
/**
* Get current rate limiter statistics
*/
getStats(): { activeRequests: number; queueLength: number; maxConcurrent: number } {
return {
activeRequests: this.activeRequests,
queueLength: this.queue.length,
maxConcurrent: this.maxConcurrent
};
}
/**
* Clear the queue (useful for cleanup)
*/
clear(): void {
this.queue = [];
}
}