website-to-markdown-mcp
Version:
Advanced MCP server for fetching websites and converting to markdown with AI-powered features and stealth capabilities
101 lines • 3.66 kB
JavaScript
export class RetryError extends Error {
attempts;
lastError;
constructor(message, attempts, lastError) {
super(message);
this.name = 'RetryError';
this.attempts = attempts;
this.lastError = lastError;
}
}
export async function retry(fn, options) {
const { retries, factor, minTimeout, maxTimeout, randomize = false, onRetry } = options;
let lastError;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await fn();
}
catch (error) {
lastError = error;
// 如果是最後一次嘗試,直接拋出錯誤
if (attempt === retries) {
throw new RetryError(`操作失敗,已重試 ${retries} 次: ${lastError.message}`, attempt + 1, lastError);
}
// 調用重試回調
if (onRetry) {
onRetry(lastError, attempt + 1);
}
// 計算延遲時間
let delay = Math.min(minTimeout * Math.pow(factor, attempt), maxTimeout);
if (randomize) {
delay = delay * (Math.random() + 0.5); // 0.5x 到 1.5x 的隨機化
}
// 等待延遲
await new Promise(resolve => setTimeout(resolve, delay));
}
}
// 這行不應該被執行到,但為了 TypeScript 的類型檢查
throw lastError;
}
// 裝飾器版本的重試功能
export function retryMethod(options) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args) {
return retry(() => originalMethod.apply(this, args), options);
};
return descriptor;
};
}
// 特定錯誤類型的重試
export async function retryOnError(fn, options, retryableErrors) {
const isRetryableError = (error) => {
return retryableErrors.some(pattern => {
if (typeof pattern === 'string') {
return error.message.includes(pattern) || error.name === pattern;
}
else {
return pattern.test(error.message) || pattern.test(error.name);
}
});
};
let lastError;
for (let attempt = 0; attempt <= options.retries; attempt++) {
try {
return await fn();
}
catch (error) {
lastError = error;
// 如果不是可重試的錯誤,直接拋出
if (!isRetryableError(lastError)) {
throw lastError;
}
// 如果是最後一次嘗試,拋出重試錯誤
if (attempt === options.retries) {
throw new RetryError(`操作失敗,已重試 ${options.retries} 次: ${lastError.message}`, attempt + 1, lastError);
}
// 調用重試回調
if (options.onRetry) {
options.onRetry(lastError, attempt + 1);
}
// 計算延遲時間
let delay = Math.min(options.minTimeout * Math.pow(options.factor, attempt), options.maxTimeout);
if (options.randomize) {
delay = delay * (Math.random() + 0.5);
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// 指數退避重試(常用於網路請求)
export async function exponentialBackoff(fn, maxRetries = 3, baseDelay = 1000) {
return retry(fn, {
retries: maxRetries,
factor: 2,
minTimeout: baseDelay,
maxTimeout: 30000,
randomize: true
});
}
//# sourceMappingURL=retry.js.map