"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withRetry = withRetry;
async function withRetry(fn, opts = {}) {
    const maxRetries = opts.maxRetries ?? 2;
    const backoffMs = opts.backoffMs ?? 1000;
    const timeout = opts.timeout;
    const externalSignal = opts.signal;
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        if (externalSignal?.aborted) {
            throw externalSignal.reason ?? new DOMException("Aborted", "AbortError");
        }
        let timer;
        const controller = timeout || externalSignal ? new AbortController() : undefined;
        if (controller) {
            if (timeout) {
                timer = setTimeout(() => controller.abort(new DOMException("Request timed out", "TimeoutError")), timeout);
            }
            if (externalSignal) {
                const onExternalAbort = () => controller.abort(externalSignal.reason ?? new DOMException("Aborted", "AbortError"));
                if (externalSignal.aborted) {
                    controller.abort(externalSignal.reason ?? new DOMException("Aborted", "AbortError"));
                }
                else {
                    externalSignal.addEventListener("abort", onExternalAbort, { once: true });
                    controller.signal.addEventListener("abort", () => externalSignal.removeEventListener("abort", onExternalAbort), { once: true });
                }
            }
        }
        try {
            const result = await fn(controller?.signal);
            return result;
        }
        catch (err) {
            clearTimeout(timer);
            if (externalSignal?.aborted) {
                throw err;
            }
            const errStr = String(err);
            const isRetriable = errStr.includes('429') ||
                errStr.includes('timeout') ||
                errStr.includes('TimeoutError') ||
                errStr.includes('ECONNRESET') ||
                errStr.includes('ETIMEDOUT') ||
                errStr.includes('AbortError') ||
                /5\d\d/.test(errStr);
            if (isRetriable && attempt < maxRetries) {
                const delay = backoffMs * Math.pow(2, attempt);
                await new Promise((resolve) => setTimeout(resolve, delay));
                continue;
            }
            throw err;
        }
        finally {
            clearTimeout(timer);
        }
    }
    throw new Error('withRetry: exhausted retries');
}
//# sourceMappingURL=withRetry.js.map