@remcostoeten/fync
Version:
A unified TypeScript library for easy access to popular APIs (GitHub, Spotify, GitLab, etc.)
105 lines • 3.91 kB
JavaScript
import { BaseError, ErrorUtil } from "./index";
import { httpErrorTransformer, httpStatusTransformer } from "./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
*/
export class HttpErrorHandler {
constructor(retryConfig = {}) {
this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };
// Register our transformers
ErrorUtil.registerTransformer(httpErrorTransformer);
ErrorUtil.registerTransformer(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 = ErrorUtil.createError(error, errorContext);
lastError = structuredError;
// Handle the error (logging, etc.)
await 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 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 = ErrorUtil.createError(error, context);
await 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 };
}
}
//# sourceMappingURL=http-handler.js.map