Files
shockbot/utils/retry.ts
T
Colin McDonnell 8a7db7bba2 Maybe fix gemini
2025-12-27 16:28:41 -08:00

49 lines
1.3 KiB
TypeScript

import { log } from "./cli.ts";
export type RetryOptions = {
maxAttempts?: number;
delayMs?: number;
shouldRetry?: (error: unknown) => boolean;
label?: string;
};
const defaultShouldRetry = (error: unknown): boolean => {
if (!(error instanceof Error)) return false;
// retry on transient network errors
return (
error.name === "AbortError" ||
error.message.includes("fetch failed") ||
error.message.includes("ECONNRESET") ||
error.message.includes("ETIMEDOUT")
);
};
export async function retry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {
const maxAttempts = options.maxAttempts ?? 3;
const delayMs = options.delayMs ?? 1000;
const shouldRetry = options.shouldRetry ?? defaultShouldRetry;
const label = options.label ?? "operation";
let lastError: unknown;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt === maxAttempts || !shouldRetry(error)) {
throw error;
}
const delay = delayMs * attempt;
log.warning(
`» ${label} failed (attempt ${attempt}/${maxAttempts}), retrying in ${delay}ms...`
);
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw lastError;
}