UNPKG

mcp-quickbase

Version:

Work with Quickbase via Model Context Protocol

88 lines 3.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateBackoff = calculateBackoff; exports.withRetry = withRetry; const logger_1 = require("./logger"); const logger = (0, logger_1.createLogger)("RetryUtil"); /** * Default retry options */ const DEFAULT_RETRY_OPTIONS = { maxRetries: 3, baseDelay: 1000, maxDelay: 10000, backoffFactor: 2, isRetryable: (error) => { // Default logic for determining if an error is retryable if (!error) return false; // Handle fetch response errors if (typeof error === "object" && error !== null && "status" in error) { const httpError = error; // Retry 429 (Too Many Requests), 408 (Request Timeout), and 5xx errors return (httpError.status === 429 || httpError.status === 408 || (httpError.status >= 500 && httpError.status < 600)); } // Handle network errors if (error instanceof Error) { return (error.message.includes("network") || error.message.includes("timeout") || error.message.includes("connection")); } return false; }, }; /** * Calculates the delay time for a retry attempt with exponential backoff * @param attempt Retry attempt number (0-based) * @param options Retry options * @returns Delay time in milliseconds */ function calculateBackoff(attempt, options) { const { baseDelay, backoffFactor = 2, maxDelay = 10000 } = options; // Use exponential backoff with jitter const exponentialDelay = baseDelay * Math.pow(backoffFactor, attempt); const jitter = Math.random() * 0.2 * exponentialDelay; // 20% jitter const delay = exponentialDelay + jitter; // Ensure delay doesn't exceed maximum return Math.min(delay, maxDelay); } /** * Wrapper function that adds retry logic to any async function * @param fn Function to add retry logic to * @param options Retry options * @returns Function with retry logic */ function withRetry(fn, options = {}) { const fullOptions = { ...DEFAULT_RETRY_OPTIONS, ...options }; return async function retryWrapper(...args) { let lastError; for (let attempt = 0; attempt <= fullOptions.maxRetries; attempt++) { try { if (attempt > 0) { logger.info(`Retry attempt ${attempt} of ${fullOptions.maxRetries}`); } return await fn(...args); } catch (error) { lastError = error; const shouldRetry = attempt < fullOptions.maxRetries && fullOptions.isRetryable && fullOptions.isRetryable(error); if (!shouldRetry) { logger.debug("Error not retryable or max retries reached", { error }); throw error; } const delay = calculateBackoff(attempt, fullOptions); logger.debug(`Retrying after ${delay}ms due to error`, { error }); // Wait before retrying await new Promise((resolve) => setTimeout(resolve, delay)); } } // This should never be reached due to the loop structure, // but TypeScript requires it for type safety throw lastError; }; } //# sourceMappingURL=retry.js.map