UNPKG

@dima_aryze/reforge

Version:

TypeScript/JavaScript SDK for Reforge - Cross-chain token operations

154 lines 5.33 kB
"use strict"; /** * Retry handler for HTTP requests with exponential backoff */ Object.defineProperty(exports, "__esModule", { value: true }); exports.RetryHandler = void 0; const errors_1 = require("../errors"); const utils_1 = require("../utils"); /** * Handles retry logic for HTTP requests with intelligent backoff strategies */ class RetryHandler { constructor(config, // eslint-disable-next-line no-unused-vars logger) { Object.defineProperty(this, "logger", { enumerable: true, configurable: true, writable: true, value: logger }); Object.defineProperty(this, "config", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.config = { attempts: config.attempts ?? 3, delay: config.delay ?? 1000, maxDelay: config.maxDelay ?? 30000, backoffMultiplier: config.backoffMultiplier ?? 2, }; } /** * Execute a function with retry logic */ async execute(fn, customConfig) { const retryConfig = customConfig ? { ...this.config, ...customConfig } : this.config; let lastError; const maxAttempts = retryConfig.attempts; for (let attempt = 0; attempt < maxAttempts; attempt++) { try { const result = await fn(attempt); if (attempt > 0) { this.logger?.info(`Request succeeded after ${attempt + 1} attempts`); } return result; } catch (error) { lastError = error instanceof errors_1.ReforgeError ? error : new errors_1.ReforgeError(String(error)); // Don't retry on the last attempt if (attempt === maxAttempts - 1) { this.logger?.error(`Request failed after ${maxAttempts} attempts`, { error: lastError.message, finalAttempt: true, }); break; } // Check if error is retryable if (!this.shouldRetry(lastError, attempt)) { this.logger?.debug('Error is not retryable, stopping attempts', { error: lastError.message, attempt: attempt + 1, }); break; } // Calculate delay and wait const delay = (0, utils_1.exponentialBackoff)(attempt, retryConfig.delay, retryConfig.maxDelay, retryConfig.backoffMultiplier); this.logger?.debug(`Retrying request after ${delay}ms`, { attempt: attempt + 1, maxAttempts, error: lastError.message, delay, }); await (0, utils_1.sleep)(delay); } } throw lastError; } /** * Determine if an error should trigger a retry */ shouldRetry(error, attempt) { // Don't retry if we've exceeded max attempts if (attempt >= this.config.attempts - 1) { return false; } // Always retry network errors if (error.name === 'ReforgeNetworkError') { return true; } // Handle API errors if (error instanceof errors_1.ReforgeApiError) { return this.shouldRetryApiError(error); } // Retry generic errors by default (unless they're validation errors) return error.name !== 'ReforgeValidationError'; } /** * Determine if an API error should be retried */ shouldRetryApiError(error) { // Always retry server errors (5xx) if (error.isServerError()) { return true; } // Retry specific client errors if (error.isClientError()) { // Retry rate limit errors if (error.hasCode('RATE_LIMIT') || error.status === 429) { return true; } // Retry request timeout if (error.status === 408) { return true; } // Don't retry other client errors (authentication, validation, etc.) return false; } // Don't retry other status codes return false; } /** * Get current retry configuration */ getConfig() { return { ...this.config }; } /** * Update retry configuration */ updateConfig(newConfig) { Object.assign(this.config, newConfig); } /** * Calculate the total maximum time that could be spent retrying */ calculateMaxRetryTime() { let totalTime = 0; for (let attempt = 0; attempt < this.config.attempts - 1; attempt++) { totalTime += (0, utils_1.exponentialBackoff)(attempt, this.config.delay, this.config.maxDelay, this.config.backoffMultiplier); } return totalTime; } /** * Create a child retry handler with modified configuration */ child(config) { return new RetryHandler({ ...this.config, ...config }, this.logger); } } exports.RetryHandler = RetryHandler; //# sourceMappingURL=retry.js.map