AutoLoop / lib /retry.ts
shubhjn's picture
Deploy Clean V1
8dd52b2
// Retry Logic for API Calls
// Exponential backoff with jitter
interface HttpError extends Error {
response?: {
status: number;
statusText: string;
};
}
export interface RetryOptions {
maxRetries?: number;
initialDelay?: number;
maxDelay?: number;
backoffMultiplier?: number;
shouldRetry?: (error: Error | HttpError) => boolean;
}
const defaultOptions: Required<RetryOptions> = {
maxRetries: 3,
initialDelay: 1000, // 1 second
maxDelay: 30000, // 30 seconds
backoffMultiplier: 2,
shouldRetry: (error) => {
// Retry on network errors and 5xx server errors
const httpError = error as HttpError;
if (!httpError.response) return true; // Network error
const status = httpError.response.status;
return status >= 500 && status < 600;
},
};
function calculateDelay(attempt: number, options: Required<RetryOptions>): number {
const delay = options.initialDelay * Math.pow(options.backoffMultiplier, attempt);
const jitter = Math.random() * 0.3 * delay; // Add 0-30% jitter
return Math.min(delay + jitter, options.maxDelay);
}
export async function withRetry<T>(
fn: () => Promise<T>,
options: RetryOptions = {}
): Promise<T> {
const opts = { ...defaultOptions, ...options };
let lastError: Error | HttpError | unknown;
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
// Don't retry if it's the last attempt or if we shouldn't retry this error
if (attempt === opts.maxRetries || !opts.shouldRetry(error as Error)) {
throw error;
}
// Wait before retrying
const delay = calculateDelay(attempt, opts);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Wrapper for fetch with automatic retries
export async function fetchWithRetry(
url: string,
options?: RequestInit,
retryOptions?: RetryOptions
): Promise<Response> {
return withRetry(async () => {
const response = await fetch(url, options);
if (!response.ok) {
const error = new Error(`HTTP ${response.status}: ${response.statusText}`) as HttpError;
error.response = { status: response.status, statusText: response.statusText };
throw error;
}
return response;
}, retryOptions);
}