UNPKG

@casoon/auditmysite

Version:

Professional website analysis suite with robust accessibility testing, Core Web Vitals performance monitoring, SEO analysis, and content optimization insights. Features isolated browser contexts, retry mechanisms, and comprehensive API endpoints for profe

268 lines 9.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NetworkThrottler = void 0; const events_1 = require("events"); class NetworkThrottler extends events_1.EventEmitter { constructor(config = {}) { super(); this.requestQueue = []; this.activeRequests = new Map(); this.requestHistory = []; this.isThrottling = false; this.lastRequestTime = 0; this.burstCount = 0; this.burstStartTime = Date.now(); this.backoffDelay = 0; this.config = { requestsPerSecond: 10, requestsPerMinute: 600, concurrentRequests: 5, delayBetweenRequests: 100, burstLimit: 20, burstWindow: 1000, enableBackoff: true, backoffMultiplier: 2, maxBackoffDelay: 5000, ...config }; this.stats = { totalRequests: 0, successfulRequests: 0, throttledRequests: 0, failedRequests: 0, averageResponseTime: 0, currentConcurrentRequests: 0, requestsInLastSecond: 0, requestsInLastMinute: 0, isThrottling: false }; // Cleanup alte Request-Historie setInterval(() => this.cleanupHistory(), 60000); // Jede Minute } async throttleRequest(requestFn, url, method = 'GET', priority = 5, maxRetries = 3) { const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const request = { id: requestId, url, method, timestamp: new Date(), priority, retryCount: 0, maxRetries }; // Request zur Queue hinzufügen this.addToQueue(request); // Warten bis Request ausgeführt werden kann await this.waitForSlot(request); try { // Request ausführen const startTime = Date.now(); const result = await this.executeRequest(request, requestFn); const responseTime = Date.now() - startTime; // Statistiken aktualisieren this.updateStats(true, responseTime); this.recordRequestSuccess(responseTime); this.emit('request:success', { request, responseTime, timestamp: new Date() }); return result; } catch (error) { // Retry-Logic if (request.retryCount < request.maxRetries) { request.retryCount++; console.warn(`🔄 Retrying request ${request.id} (attempt ${request.retryCount}/${request.maxRetries})`); // Backoff-Delay if (this.config.enableBackoff) { this.backoffDelay = Math.min(this.backoffDelay * this.config.backoffMultiplier, this.config.maxBackoffDelay); await this.delay(this.backoffDelay); } // Request erneut zur Queue hinzufügen this.addToQueue(request); return this.throttleRequest(requestFn, url, method, priority, maxRetries); } // Max Retries erreicht this.updateStats(false, 0); this.recordRequestFailure(); this.emit('request:failed', { request, error: String(error), timestamp: new Date() }); throw error; } finally { // Request aus aktiven Requests entfernen this.activeRequests.delete(requestId); this.stats.currentConcurrentRequests = this.activeRequests.size; } } addToQueue(request) { this.requestQueue.push(request); // Nach Priorität sortieren (höchste zuerst) this.requestQueue.sort((a, b) => b.priority - a.priority); this.emit('request:queued', { request, timestamp: new Date() }); } async waitForSlot(request) { return new Promise((resolve) => { const checkSlot = () => { if (this.canExecuteRequest()) { resolve(); } else { // Kurz warten und erneut prüfen setTimeout(checkSlot, 50); } }; checkSlot(); }); } canExecuteRequest() { // Concurrent Requests Limit if (this.activeRequests.size >= this.config.concurrentRequests) { return false; } // Rate Limiting const now = Date.now(); // Requests pro Sekunde const requestsInLastSecond = this.requestHistory.filter(req => now - req.timestamp.getTime() < 1000).length; if (requestsInLastSecond >= this.config.requestsPerSecond) { return false; } // Requests pro Minute const requestsInLastMinute = this.requestHistory.filter(req => now - req.timestamp.getTime() < 60000).length; if (requestsInLastMinute >= this.config.requestsPerMinute) { return false; } // Burst Limiting if (this.burstCount >= this.config.burstLimit) { const burstAge = now - this.burstStartTime; if (burstAge < this.config.burstWindow) { return false; } else { // Burst Window reset this.burstCount = 0; this.burstStartTime = now; } } // Delay zwischen Requests if (now - this.lastRequestTime < this.config.delayBetweenRequests) { return false; } return true; } async executeRequest(request, requestFn) { // Request als aktiv markieren this.activeRequests.set(request.id, request); this.stats.currentConcurrentRequests = this.activeRequests.size; // Burst Counter erhöhen this.burstCount++; this.lastRequestTime = Date.now(); // Request ausführen const result = await requestFn(); // Backoff zurücksetzen bei Erfolg if (this.config.enableBackoff) { this.backoffDelay = 0; } return result; } updateStats(success, responseTime) { this.stats.totalRequests++; if (success) { this.stats.successfulRequests++; } else { this.stats.failedRequests++; } // Durchschnittliche Response-Zeit aktualisieren if (responseTime > 0) { const totalResponseTime = this.stats.averageResponseTime * (this.stats.successfulRequests - 1) + responseTime; this.stats.averageResponseTime = totalResponseTime / this.stats.successfulRequests; } // Aktuelle Request-Raten aktualisieren this.updateCurrentRates(); } updateCurrentRates() { const now = Date.now(); this.stats.requestsInLastSecond = this.requestHistory.filter(req => now - req.timestamp.getTime() < 1000).length; this.stats.requestsInLastMinute = this.requestHistory.filter(req => now - req.timestamp.getTime() < 60000).length; } recordRequestSuccess(responseTime) { this.requestHistory.push({ timestamp: new Date(), success: true, responseTime }); } recordRequestFailure() { this.requestHistory.push({ timestamp: new Date(), success: false, responseTime: 0 }); } cleanupHistory() { const cutoffTime = Date.now() - 60000; // Nur letzte Minute behalten this.requestHistory = this.requestHistory.filter(req => req.timestamp.getTime() > cutoffTime); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Public Methods getStats() { return { ...this.stats }; } getQueueSize() { return this.requestQueue.length; } getActiveRequests() { return this.activeRequests.size; } getThrottlingStatus() { return this.isThrottling; } // Konfiguration ändern updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; this.emit('config:updated', { config: this.config, timestamp: new Date() }); } // Queue Management clearQueue() { this.requestQueue = []; this.emit('queue:cleared', { timestamp: new Date() }); } pauseThrottling() { this.isThrottling = true; this.stats.isThrottling = true; this.emit('throttling:paused', { timestamp: new Date() }); } resumeThrottling() { this.isThrottling = false; this.stats.isThrottling = false; this.emit('throttling:resumed', { timestamp: new Date() }); } // Utility Methods getConfig() { return { ...this.config }; } resetStats() { this.stats = { totalRequests: 0, successfulRequests: 0, throttledRequests: 0, failedRequests: 0, averageResponseTime: 0, currentConcurrentRequests: 0, requestsInLastSecond: 0, requestsInLastMinute: 0, isThrottling: false }; this.requestHistory = []; this.emit('stats:reset', { timestamp: new Date() }); } // Cleanup async cleanup() { this.clearQueue(); this.activeRequests.clear(); this.requestHistory = []; this.resetStats(); this.emit('throttler:cleanup', { timestamp: new Date() }); } } exports.NetworkThrottler = NetworkThrottler; //# sourceMappingURL=network-throttler.js.map