UNPKG

guardz-axios

Version:

Type-safe HTTP client built on top of Axios with runtime validation using guardz. Part of the guardz ecosystem for comprehensive TypeScript type safety.

229 lines 7.57 kB
"use strict"; /** * Retry Utilities - Pure functions for retry logic * Following Functional Programming (FP) principles */ Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateRetryDelay = calculateRetryDelay; exports.shouldRetry = shouldRetry; exports.isRetryableError = isRetryableError; exports.isRetryableValidationError = isRetryableValidationError; exports.createRetryConfig = createRetryConfig; exports.validateRetryConfig = validateRetryConfig; exports.mergeRetryConfigs = mergeRetryConfigs; exports.createRetryStrategy = createRetryStrategy; /** * Calculates delay for retry attempts with improved performance * Pure function - no side effects, deterministic output * * @param attempt - Current attempt number (1-based) * @param baseDelay - Base delay in milliseconds * @param backoff - Backoff strategy ('linear' or 'exponential') * @returns Calculated delay in milliseconds */ function calculateRetryDelay(attempt, baseDelay, backoff) { // Handle invalid attempt numbers if (attempt <= 0) { return 0; } // Handle invalid base delay if (baseDelay < 0) { return 0; } if (backoff === "exponential") { // Use bit shifting for better performance with small numbers const multiplier = attempt <= 31 ? 1 << (attempt - 1) : Math.pow(2, attempt - 1); return baseDelay * multiplier; } return baseDelay * attempt; } /** * Checks if retry should be attempted with improved logic * Pure function - no side effects, deterministic output * * @param attempt - Current attempt number * @param maxAttempts - Maximum number of attempts * @param error - Error that occurred * @param retryOn - Custom retry condition function * @returns Whether retry should be attempted */ function shouldRetry(attempt, maxAttempts, error, retryOn) { if (attempt >= maxAttempts) { return false; } if (retryOn) { return retryOn(error); } // Default retry logic - retry on any error unless it's a validation error return !isRetryableValidationError(error); } /** * Determines if an error is retryable with comprehensive error detection * Pure function - no side effects, deterministic output * * @param error - Error to check * @returns Whether the error is retryable */ function isRetryableError(error) { if (!error || typeof error !== "object") { return false; } const errorObj = error; // Network errors const networkErrorCodes = [ "ECONNABORTED", "ENOTFOUND", "ECONNREFUSED", "ETIMEDOUT", "ERR_NETWORK", ]; if (typeof errorObj.code === "string" && networkErrorCodes.includes(errorObj.code)) { return true; } // HTTP 5xx errors if (errorObj.response && typeof errorObj.response === "object") { const response = errorObj.response; if (typeof response.status === "number" && response.status >= 500 && response.status < 600) { return true; } } // Timeout errors if (typeof errorObj.message === "string" && errorObj.message.toLowerCase().includes("timeout")) { return true; } return false; } /** * Determines if an error is a validation error that should not be retried * Pure function - no side effects, deterministic output * * @param error - Error to check * @returns Whether the error is a validation error */ function isRetryableValidationError(error) { if (!error || typeof error !== "object") { return false; } const errorObj = error; // Check for validation error codes const validationErrorCodes = ["VALIDATION_ERROR", "INVALID_INPUT"]; if (typeof errorObj.code === "string" && validationErrorCodes.includes(errorObj.code)) { return true; } // Check for validation error type if (errorObj.type === "validation") { return true; } // Check for validation error messages if (typeof errorObj.message === "string") { const message = errorObj.message.toLowerCase(); const validationKeywords = [ "validation", "invalid", "required", "schema", "type", ]; if (validationKeywords.some((keyword) => message.includes(keyword))) { return true; } } // HTTP 4xx errors (except 429 - too many requests) if (errorObj.response && typeof errorObj.response === "object") { const response = errorObj.response; if (typeof response.status === "number" && response.status >= 400 && response.status < 500 && response.status !== 429) { return true; } } return false; } /** * Creates retry configuration with defaults and validation * Pure function - no side effects, deterministic output * * @param attempts - Number of retry attempts * @param delay - Base delay in milliseconds * @param backoff - Backoff strategy * @param retryOn - Custom retry condition function * @returns Retry configuration object */ function createRetryConfig(attempts = 3, delay = 1000, backoff = "exponential", retryOn) { return { attempts: Math.max(1, attempts), delay: Math.max(0, delay), backoff, retryOn, }; } /** * Validates retry configuration with comprehensive checks * Pure function - no side effects, deterministic output * * @param config - Retry configuration to validate * @returns Error message if invalid, null if valid */ function validateRetryConfig(config) { if (!config || typeof config !== "object") { return "Retry configuration must be an object"; } if (typeof config.attempts !== "number" || config.attempts < 1) { return "Attempts must be at least 1"; } if (typeof config.delay !== "number" || config.delay < 0) { return "Delay must be non-negative"; } if (!["linear", "exponential"].includes(config.backoff)) { return 'Backoff must be either "linear" or "exponential"'; } if (config.retryOn && typeof config.retryOn !== "function") { return "Retry condition must be a function"; } return null; } /** * Merges retry configurations with validation * Pure function - no side effects, deterministic output * * @param base - Base retry configuration * @param override - Override configuration * @returns Merged retry configuration */ function mergeRetryConfigs(base, override) { const merged = { ...base, ...override, }; // Validate the merged configuration const validationError = validateRetryConfig(merged); if (validationError) { throw new Error(`Invalid merged retry configuration: ${validationError}`); } return merged; } /** * Creates a retry strategy function with memoization for performance * Pure function - no side effects, deterministic output * * @param config - Retry configuration * @returns Retry strategy object with shouldRetry and getDelay methods */ function createRetryStrategy(config) { // Validate configuration const validationError = validateRetryConfig(config); if (validationError) { throw new Error(`Invalid retry configuration: ${validationError}`); } return { shouldRetry: (attempt, error) => shouldRetry(attempt, config.attempts, error, config.retryOn), getDelay: (attempt) => calculateRetryDelay(attempt, config.delay, config.backoff), }; } //# sourceMappingURL=retry-utils.js.map