UNPKG

@remcostoeten/fync

Version:

Unified TypeScript library for 9 popular APIs with consistent functional architecture

111 lines 3.62 kB
/** * Rate limiting implementation to prevent hitting API limits */ export class RateLimiter { constructor(defaultConfig) { this.limits = new Map(); this.state = new Map(); this.defaultConfig = { maxRequests: defaultConfig?.maxRequests || 100, windowMs: defaultConfig?.windowMs || 60 * 1000, // 1 minute default retryAfter: defaultConfig?.retryAfter || 1000, // 1 second default }; } setLimit(key, config) { this.limits.set(key, config); } getConfig(key) { return this.limits.get(key) || this.defaultConfig; } getState(key) { if (!this.state.has(key)) { this.state.set(key, { requests: [], blocked: false, }); } return this.state.get(key); } cleanOldRequests(state, windowMs) { const now = Date.now(); const cutoff = now - windowMs; state.requests = state.requests.filter(time => time > cutoff); } async checkLimit(key) { const config = this.getConfig(key); const state = this.getState(key); const now = Date.now(); // Check if currently blocked if (state.blocked && state.blockUntil) { if (now < state.blockUntil) { return false; } // Unblock if time has passed state.blocked = false; state.blockUntil = undefined; } // Clean old requests outside the window this.cleanOldRequests(state, config.windowMs); // Check if limit exceeded if (state.requests.length >= config.maxRequests) { state.blocked = true; state.blockUntil = now + (config.retryAfter || 1000); return false; } // Record the request state.requests.push(now); return true; } async waitForLimit(key) { const state = this.getState(key); if (state.blocked && state.blockUntil) { const waitTime = state.blockUntil - Date.now(); if (waitTime > 0) { await new Promise(resolve => setTimeout(resolve, waitTime)); } } } getRemainingRequests(key) { const config = this.getConfig(key); const state = this.getState(key); this.cleanOldRequests(state, config.windowMs); return Math.max(0, config.maxRequests - state.requests.length); } getResetTime(key) { const config = this.getConfig(key); const state = this.getState(key); if (state.requests.length === 0) { return null; } const oldestRequest = Math.min(...state.requests); return oldestRequest + config.windowMs; } reset(key) { if (key) { this.state.delete(key); } else { this.state.clear(); } } } export function createRateLimiter(defaultConfig) { return new RateLimiter(defaultConfig); } // Preset configurations for common APIs export const RATE_LIMIT_PRESETS = { github: { authenticated: { maxRequests: 5000, windowMs: 60 * 60 * 1000 }, // 5000/hour unauthenticated: { maxRequests: 60, windowMs: 60 * 60 * 1000 }, // 60/hour }, spotify: { default: { maxRequests: 180, windowMs: 60 * 1000 }, // ~180/minute }, vercel: { default: { maxRequests: 100, windowMs: 10 * 1000 }, // 100/10 seconds }, google: { default: { maxRequests: 1000, windowMs: 100 * 1000 }, // 1000/100 seconds }, }; //# sourceMappingURL=rate-limiter.js.map