UNPKG

tsonik

Version:

A TypeScript client library for the Iconik API based on Swagger documentation

189 lines 6.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.RetryPresets = exports.extractRequestInfo = exports.createRetryWrapper = exports.calculateDelay = exports.shouldRetryError = void 0; const p_retry_1 = __importStar(require("p-retry")); const config_1 = require("./config"); /** * Determine if an error should be retried based on configuration */ function shouldRetryError(error, config) { // If retry is disabled, never retry if (!config.enabled) { return false; } // Check if it's an Axios error if (error && typeof error === 'object' && 'isAxiosError' in error && error.isAxiosError) { const axiosError = error; // Check HTTP status code if (axiosError.response?.status) { const status = axiosError.response.status; // Don't retry client errors (4xx) except for specific cases if (status >= 400 && status < 500) { return config.retryOnStatus.includes(status); } // Retry server errors (5xx) if configured if (status >= 500) { return config.retryOnStatus.includes(status); } } // Check network errors if (axiosError.code && config.retryOnNetworkErrors.includes(axiosError.code)) { return true; } // Check HTTP method const method = axiosError.config?.method?.toUpperCase(); if (method && !config.retryOnMethods.includes(method)) { return false; } } // Check for common network error patterns if (error && typeof error === 'object' && 'code' in error && typeof error.code === 'string' && config.retryOnNetworkErrors.includes(error.code)) { return true; } // Use custom retry condition if provided return config.shouldRetry(error, 0); } exports.shouldRetryError = shouldRetryError; /** * Calculate delay for exponential backoff with jitter */ function calculateDelay(attemptNumber, config) { const exponentialDelay = config.minDelay * Math.pow(config.factor, attemptNumber - 1); const clampedDelay = Math.min(exponentialDelay, config.maxDelay); // Add jitter to prevent thundering herd const jitter = clampedDelay * config.randomize * Math.random(); return Math.round(clampedDelay + jitter); } exports.calculateDelay = calculateDelay; /** * Create a retry wrapper function for HTTP requests */ function createRetryWrapper(requestFn, retryConfig = {}) { const config = (0, config_1.mergeRetryConfig)(retryConfig); if (!config.enabled) { return requestFn(); } return (0, p_retry_1.default)(async (_attemptNumber) => { try { return await requestFn(); } catch (error) { // Check if we should retry this error if (!shouldRetryError(error, config)) { // If we shouldn't retry, throw AbortError to stop retrying throw new p_retry_1.AbortError(error); } // Let p-retry handle the retry logic throw error; } }, { retries: config.attempts, minTimeout: config.minDelay, maxTimeout: config.maxDelay, factor: config.factor, randomize: config.randomize > 0, onFailedAttempt: (error) => { // Optional: Add logging for failed attempts if (process.env.NODE_ENV !== 'production') { const requestInfo = extractRequestInfo(error); // eslint-disable-next-line no-console console.warn(`Retry attempt ${error.attemptNumber} failed. ${error.retriesLeft} retries left.`, { error: error.message, status: requestInfo.status, method: requestInfo.method, url: requestInfo.url, }); } }, }); } exports.createRetryWrapper = createRetryWrapper; /** * Extract request information for logging */ function extractRequestInfo(error) { if (error && typeof error === 'object' && 'isAxiosError' in error && error.isAxiosError) { const axiosError = error; return { method: axiosError.config?.method?.toUpperCase(), url: axiosError.config?.url, status: axiosError.response?.status, code: axiosError.code, }; } return { code: error && typeof error === 'object' && 'code' in error ? String(error.code) : undefined, }; } exports.extractRequestInfo = extractRequestInfo; /** * Create a retry configuration for specific use cases */ exports.RetryPresets = { /** * Conservative retry for read operations */ conservative: () => ({ attempts: 2, minDelay: 200, maxDelay: 10000, factor: 2, retryOnMethods: ['GET', 'HEAD', 'OPTIONS'], }), /** * Aggressive retry for critical operations */ aggressive: () => ({ attempts: 5, minDelay: 100, maxDelay: 60000, factor: 2.5, retryOnMethods: ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'], }), /** * Rate limit specific retry */ rateLimit: () => ({ attempts: 3, minDelay: 1000, maxDelay: 30000, factor: 3, retryOnStatus: [429], // Only retry rate limits }), /** * Network error specific retry */ networkOnly: () => ({ attempts: 3, minDelay: 500, maxDelay: 15000, factor: 2, retryOnStatus: [], // Don't retry HTTP errors }), }; //# sourceMappingURL=utils.js.map