UNPKG

website-to-markdown-mcp

Version:

Advanced MCP server for fetching websites and converting to markdown with AI-powered features and stealth capabilities

172 lines 5.79 kB
// 簡單的速率限制器實作,不依賴外部包 export class RateLimiter { tokens; lastRefill; maxTokens; refillRate; // tokens per second constructor(maxTokens, refillRate) { this.maxTokens = maxTokens; this.refillRate = refillRate; this.tokens = maxTokens; this.lastRefill = Date.now(); } refill() { const now = Date.now(); const timePassed = (now - this.lastRefill) / 1000; const tokensToAdd = Math.floor(timePassed * this.refillRate); if (tokensToAdd > 0) { this.tokens = Math.min(this.maxTokens, this.tokens + tokensToAdd); this.lastRefill = now; } } async consume(tokens = 1) { this.refill(); if (this.tokens >= tokens) { this.tokens -= tokens; return; } // 計算需要等待的時間 const tokensNeeded = tokens - this.tokens; const waitTime = (tokensNeeded / this.refillRate) * 1000; await new Promise(resolve => setTimeout(resolve, waitTime)); await this.consume(tokens); } getAvailableTokens() { this.refill(); return this.tokens; } } // 全域速率限制器實例 const globalRateLimiter = new RateLimiter(10, 1); // 每秒最多10個請求 // 簡單的並發限制器 export class ConcurrencyLimiter { maxConcurrency; activeCount = 0; pendingQueue = []; constructor(maxConcurrency) { this.maxConcurrency = maxConcurrency; } async run(fn) { return new Promise((resolve, reject) => { const execute = async () => { this.activeCount++; try { const result = await fn(); resolve(result); } catch (error) { reject(error); } finally { this.activeCount--; this.processQueue(); } }; if (this.activeCount < this.maxConcurrency) { execute(); } else { this.pendingQueue.push(execute); } }); } processQueue() { if (this.pendingQueue.length > 0 && this.activeCount < this.maxConcurrency) { const next = this.pendingQueue.shift(); if (next) { next(); } } } get active() { return this.activeCount; } get pending() { return this.pendingQueue.length; } clearQueue() { this.pendingQueue = []; } } // 全域並發限制器 const globalConcurrencyLimiter = new ConcurrencyLimiter(5); // 最多5個並發請求 // 組合速率限制和並發控制的函數 export async function throttledRequest(fn, options = {}) { const { rateLimiter = globalRateLimiter, concurrencyLimiter = globalConcurrencyLimiter } = options; // 先消耗速率限制令牌 await rateLimiter.consume(1); // 然後在並發限制下執行 return concurrencyLimiter.run(fn); } // 智能速率限制器 - 根據錯誤狀態自動調整 export class AdaptiveRateLimiter { baseRate; maxTokens; errorThreshold; baseLimiter; errorCount = 0; successCount = 0; lastErrorTime = 0; adaptationWindow = 60000; // 1分鐘 constructor(baseRate, maxTokens = baseRate * 2, errorThreshold = 3) { this.baseRate = baseRate; this.maxTokens = maxTokens; this.errorThreshold = errorThreshold; this.baseLimiter = new RateLimiter(maxTokens, baseRate); } async consume() { // 檢查是否需要調整速率 this.adjustRate(); await this.baseLimiter.consume(1); } onSuccess() { this.successCount++; this.errorCount = Math.max(0, this.errorCount - 0.1); // 緩慢恢復 } onError() { this.errorCount++; this.lastErrorTime = Date.now(); this.successCount = Math.max(0, this.successCount - 1); } adjustRate() { const now = Date.now(); const timeSinceLastError = now - this.lastErrorTime; if (this.errorCount >= this.errorThreshold) { // 如果錯誤過多,降低速率 const slowdownFactor = Math.min(0.1, 1 / (this.errorCount - this.errorThreshold + 1)); const newRate = this.baseRate * slowdownFactor; this.baseLimiter = new RateLimiter(this.maxTokens, newRate); } else if (timeSinceLastError > this.adaptationWindow && this.successCount > 10) { // 如果長時間沒有錯誤且成功率高,逐漸恢復到基準速率 const recoveryFactor = Math.min(1, this.successCount / 20); const newRate = this.baseRate * recoveryFactor; this.baseLimiter = new RateLimiter(this.maxTokens, newRate); } } getStats() { return { errorCount: this.errorCount, successCount: this.successCount, currentRate: this.baseLimiter.refillRate, availableTokens: this.baseLimiter.getAvailableTokens() }; } } // 全域的調用時間記錄 const _lastCalls = new Map(); // 簡單的速率限制函數 export async function rateLimit(intervalMs = 1000) { const key = 'global'; const now = Date.now(); const lastCall = _lastCalls.get(key) || 0; const timeSinceLastCall = now - lastCall; if (timeSinceLastCall < intervalMs) { const waitTime = intervalMs - timeSinceLastCall; await new Promise(resolve => setTimeout(resolve, waitTime)); } _lastCalls.set(key, Date.now()); } // 導出便利函數 export { globalRateLimiter, globalConcurrencyLimiter }; //# sourceMappingURL=rate-limiter.js.map