UNPKG

@dbs-portal/core-api

Version:

HTTP client and API utilities for DBS Portal

166 lines 4.56 kB
/** * Rate limiter for API requests */ /** * Token bucket rate limiter */ export class RateLimiter { tokens; lastRefill; config; constructor(config) { this.config = { maxRequests: config.maxRequests, perMilliseconds: config.perMilliseconds, maxRPS: config.maxRPS || Math.ceil(config.maxRequests / (config.perMilliseconds / 1000)), }; this.tokens = this.config.maxRequests; this.lastRefill = Date.now(); } /** * Acquires a token (waits if necessary) */ async acquire() { this.refillTokens(); if (this.tokens >= 1) { this.tokens--; return; } // Wait for next token const waitTime = this.calculateWaitTime(); if (waitTime > 0) { await this.delay(waitTime); return this.acquire(); } } /** * Tries to acquire a token without waiting */ tryAcquire() { this.refillTokens(); if (this.tokens >= 1) { this.tokens--; return true; } return false; } /** * Gets the current number of available tokens */ getAvailableTokens() { this.refillTokens(); return Math.floor(this.tokens); } /** * Gets the time until next token is available */ getTimeUntilNextToken() { this.refillTokens(); if (this.tokens >= 1) { return 0; } return this.calculateWaitTime(); } /** * Updates rate limit configuration */ updateConfig(config) { this.config = { ...this.config, ...config, maxRPS: config.maxRPS || Math.ceil((config.maxRequests || this.config.maxRequests) / ((config.perMilliseconds || this.config.perMilliseconds) / 1000)), }; // Reset tokens to new maximum this.tokens = Math.min(this.tokens, this.config.maxRequests); } /** * Gets current configuration */ getConfig() { return { ...this.config }; } /** * Resets the rate limiter */ reset() { this.tokens = this.config.maxRequests; this.lastRefill = Date.now(); } /** * Gets rate limiter statistics */ getStats() { return { availableTokens: this.getAvailableTokens(), maxTokens: this.config.maxRequests, refillRate: this.config.maxRequests / this.config.perMilliseconds, timeUntilNextToken: this.getTimeUntilNextToken(), }; } /** * Refills tokens based on elapsed time */ refillTokens() { const now = Date.now(); const elapsed = now - this.lastRefill; if (elapsed > 0) { const tokensToAdd = (elapsed / this.config.perMilliseconds) * this.config.maxRequests; this.tokens = Math.min(this.config.maxRequests, this.tokens + tokensToAdd); this.lastRefill = now; } } /** * Calculates wait time for next token */ calculateWaitTime() { const tokensNeeded = 1 - this.tokens; const timePerToken = this.config.perMilliseconds / this.config.maxRequests; return Math.ceil(tokensNeeded * timePerToken); } /** * Creates a delay promise */ delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Creates a rate limiter for requests per second */ static perSecond(requestsPerSecond) { return new RateLimiter({ maxRequests: requestsPerSecond, perMilliseconds: 1000, }); } /** * Creates a rate limiter for requests per minute */ static perMinute(requestsPerMinute) { return new RateLimiter({ maxRequests: requestsPerMinute, perMilliseconds: 60000, }); } /** * Creates a rate limiter for requests per hour */ static perHour(requestsPerHour) { return new RateLimiter({ maxRequests: requestsPerHour, perMilliseconds: 3600000, }); } /** * Creates a burst rate limiter (allows bursts up to maxBurst, then limits to sustainedRate) */ static burst(maxBurst, sustainedRate, perMilliseconds = 1000) { return new RateLimiter({ maxRequests: maxBurst, perMilliseconds, maxRPS: sustainedRate, }); } } //# sourceMappingURL=rate-limiter.js.map