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(fn: () => Promise, options: RetryOptions = {}): Promise { 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; }