/**
* Simple retry helper with exponential backoff.
*/
export interface RetryOptions {
retries?: number;
factor?: number;
minTimeout?: number;
maxTimeout?: number;
retryOn?: (err: any) => boolean;
}
export async function retry<T>(fn: () => Promise<T>, opts: RetryOptions = {}): Promise<T> {
const retries = typeof opts.retries === 'number' ? opts.retries : 3;
const factor = opts.factor || 2;
const minTimeout = opts.minTimeout || 200;
const maxTimeout = opts.maxTimeout || 5000;
const retryOn = opts.retryOn || ((err: any) => true);
let attempt = 0;
let lastError: any = null;
while (attempt <= retries) {
try {
return await fn();
} catch (err) {
lastError = err;
if (attempt === retries || !retryOn(err)) {
break;
}
const wait = Math.min(maxTimeout, Math.round(minTimeout * Math.pow(factor, attempt)));
await new Promise((resolve) => setTimeout(resolve, wait));
attempt += 1;
}
}
throw lastError;
}