@remcostoeten/fync
Version:
Unified TypeScript library for 9 popular APIs with consistent functional architecture
111 lines • 3.62 kB
JavaScript
/**
* 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