@remcostoeten/fync
Version:
A unified TypeScript library for easy access to popular APIs (GitHub, Spotify, GitLab, etc.)
119 lines (118 loc) • 3.66 kB
JavaScript
;
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;