UNPKG

@remcostoeten/fync

Version:

A unified TypeScript library for easy access to popular APIs (GitHub, Spotify, GitLab, etc.)

119 lines (118 loc) 3.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpErrorHandler = void 0; var _index = require("./index"); var _transformers = require("./transformers"); /** * Default retry configuration */ const DEFAULT_RETRY_CONFIG = { maxAttempts: 3, baseDelay: 1000, maxDelay: 10000, backoffMultiplier: 2, retryableStatuses: [408, 429, 500, 502, 503, 504] }; /** * Centralized HTTP error handler with automatic retry logic */ class HttpErrorHandler { constructor(retryConfig = {}) { this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...retryConfig }; // Register our transformers _index.ErrorUtil.registerTransformer(_transformers.httpErrorTransformer); _index.ErrorUtil.registerTransformer(_transformers.httpStatusTransformer); } /** * Execute HTTP request with error handling and retry logic */ async execute(requestFn, context) { let lastError = null; for (let attempt = 1; attempt <= this.retryConfig.maxAttempts; attempt++) { try { return await requestFn(); } catch (error) { const errorContext = { ...context, retryCount: attempt - 1, retryConfig: this.retryConfig }; const structuredError = _index.ErrorUtil.createError(error, errorContext); lastError = structuredError; // Handle the error (logging, etc.) await _index.ErrorUtil.handle(structuredError); // Don't retry if it's the last attempt or error is not retryable if (attempt === this.retryConfig.maxAttempts || !structuredError.info.isRetryable) { break; } // Don't retry if status is not in retryable list const statusCode = structuredError.info.context.metadata?.statusCode; if (statusCode && !this.retryConfig.retryableStatuses.includes(statusCode)) { break; } // Calculate delay with exponential backoff const delay = Math.min(this.retryConfig.baseDelay * Math.pow(this.retryConfig.backoffMultiplier, attempt - 1), this.retryConfig.maxDelay); // Add jitter to prevent thundering herd const jitteredDelay = delay + Math.random() * 1000; await this.sleep(jitteredDelay); } } // If we get here, all retries failed if (lastError) { throw lastError; } throw new _index.BaseError({ code: "UNKNOWN_ERROR", category: "unknown", severity: "critical", message: "Unknown error occurred during HTTP request", service: context.service || "core", context: { ...context, timestamp: new Date(), service: context.service || "core" }, isRetryable: false }); } /** * Handle a single HTTP error without retry logic */ async handleError(error, context) { const structuredError = _index.ErrorUtil.createError(error, context); await _index.ErrorUtil.handle(structuredError); return structuredError; } /** * Sleep for specified milliseconds */ sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Check if an error is retryable based on current configuration */ isRetryable(error) { if (!error.info.isRetryable) return false; const statusCode = error.info.context.metadata?.statusCode; if (statusCode && !this.retryConfig.retryableStatuses.includes(statusCode)) { return false; } return true; } /** * Update retry configuration */ updateRetryConfig(config) { this.retryConfig = { ...this.retryConfig, ...config }; } } exports.HttpErrorHandler = HttpErrorHandler;