tsonik
Version:
A TypeScript client library for the Iconik API based on Swagger documentation
189 lines • 6.7 kB
JavaScript
;
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