Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
export interface RetryOpts {
retries?: number;
baseMs?: number;
isRetryable?: (err: unknown) => boolean;
onRetry?: (attempt: number, err: unknown) => void;
sleep?: (ms: number) => Promise<void>;
}
export function isRateLimit(err: unknown): boolean {
const status = (err as { status?: number; statusCode?: number })?.status
?? (err as { statusCode?: number })?.statusCode;
return status === 429;
}
/** Exponential backoff ×N for rate limits (SPEC §4.5). */
export async function withRetry<T>(fn: () => Promise<T>, opts: RetryOpts = {}): Promise<T> {
const retries = opts.retries ?? 3;
const baseMs = opts.baseMs ?? 1000;
const isRetryable = opts.isRetryable ?? isRateLimit;
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
let lastErr: unknown;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
return await fn();
} catch (err) {
lastErr = err;
if (!isRetryable(err) || attempt === retries) throw err;
opts.onRetry?.(attempt + 1, err);
await sleep(baseMs * 2 ** attempt);
}
}
throw lastErr;
}
7f2688d19This file is pinned to the selected release. Download the original to open it in your design tool.