UNPKG

japanpost-api

Version:
144 lines 5.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CircuitBreaker = exports.CircuitBreakerState = void 0; exports.withRetry = withRetry; const JapanPostAPIError_1 = require("./JapanPostAPIError"); const Logger_1 = require("./Logger"); /** * Default retry configuration */ const DEFAULT_RETRY_OPTIONS = { maxRetries: 3, baseDelay: 1000, backoffMultiplier: 2, maxDelay: 30000, jitter: 1000, retryableErrors: [JapanPostAPIError_1.RateLimitError, JapanPostAPIError_1.ServerError, JapanPostAPIError_1.NetworkError], }; /** * Function that executes retry with exponential backoff */ async function withRetry(operation, options = {}) { const config = { ...DEFAULT_RETRY_OPTIONS, ...options }; let lastError = null; for (let attempt = 0; attempt <= config.maxRetries; attempt++) { try { return await operation(); } catch (error) { const errorInstance = error instanceof Error ? error : new Error(String(error)); lastError = errorInstance; // If this is the last attempt, re-throw the error if (attempt === config.maxRetries) { throw errorInstance; } // Check if error is retryable const isRetryable = config.retryableErrors.some((ErrorType) => errorInstance instanceof ErrorType); if (!isRetryable) { throw errorInstance; } // For rate limit errors, consider Retry-After header let delay = calculateDelay(attempt, config); if (errorInstance instanceof JapanPostAPIError_1.RateLimitError && errorInstance.retryAfter) { delay = Math.max(delay, errorInstance.retryAfter * 1000); } (0, Logger_1.debugLog)(() => `Executing retry: ${attempt + 1}/${config.maxRetries + 1}, ` + `Error: ${errorInstance.constructor.name}, ` + `Wait time: ${delay}ms`); await sleep(delay); } } // Should not reach here, but for type safety throw lastError || new Error("Unknown error during retry"); } /** * Calculate delay time with exponential backoff */ function calculateDelay(attempt, config) { const exponentialDelay = config.baseDelay * Math.pow(config.backoffMultiplier, attempt); const jitter = Math.random() * config.jitter; const totalDelay = exponentialDelay + jitter; return Math.min(totalDelay, config.maxDelay); } /** * Wait for specified milliseconds */ function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Circuit breaker state */ var CircuitBreakerState; (function (CircuitBreakerState) { CircuitBreakerState["CLOSED"] = "CLOSED"; CircuitBreakerState["OPEN"] = "OPEN"; CircuitBreakerState["HALF_OPEN"] = "HALF_OPEN"; })(CircuitBreakerState || (exports.CircuitBreakerState = CircuitBreakerState = {})); /** * Basic circuit breaker implementation * Protects the system from cascading failures */ class CircuitBreaker { constructor(options = {}) { this.state = CircuitBreakerState.CLOSED; this.failureCount = 0; this.lastFailureTime = 0; this.options = { failureThreshold: 5, resetTimeout: 60000, // 1 minute monitoringPeriod: 60000, // 1 minute ...options, }; } async execute(operation) { if (this.state === CircuitBreakerState.OPEN) { if (this.shouldAttemptReset()) { this.state = CircuitBreakerState.HALF_OPEN; (0, Logger_1.debugLog)(() => "Circuit breaker: Transitioning to HALF_OPEN state"); } else { throw new Error("Circuit breaker is in OPEN state. Please wait and try again later."); } } try { const result = await operation(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } shouldAttemptReset() { return Date.now() - this.lastFailureTime >= this.options.resetTimeout; } onSuccess() { this.failureCount = 0; this.state = CircuitBreakerState.CLOSED; (0, Logger_1.debugLog)(() => "Circuit breaker: Transitioning to CLOSED state"); } onFailure() { this.failureCount++; this.lastFailureTime = Date.now(); if (this.failureCount >= this.options.failureThreshold) { this.state = CircuitBreakerState.OPEN; (0, Logger_1.debugLog)(() => `Circuit breaker: Transitioning to OPEN state (failure count: ${this.failureCount})`); } } getState() { return this.state; } getFailureCount() { return this.failureCount; } reset() { this.state = CircuitBreakerState.CLOSED; this.failureCount = 0; this.lastFailureTime = 0; (0, Logger_1.debugLog)(() => "Circuit breaker: Manual reset"); } } exports.CircuitBreaker = CircuitBreaker; //# sourceMappingURL=retry.js.map