{"version":3,"file":"retry.cjs","names":[],"sources":["../../src/http/retry.ts"],"sourcesContent":["import { isApiError, isRetriableStatus } from \"./errors\";\n\nexport interface RetryOptions {\n    /** Max attempts (including the first). Default: 3. */\n    retries?: number;\n    /** Initial backoff in ms. Doubles each attempt, capped at `maxDelay`. Default: 300. */\n    initialDelay?: number;\n    /** Maximum delay between attempts. Default: 10_000. */\n    maxDelay?: number;\n    /**\n     * Honor a `Retry-After` hint on the thrown error (`error.retryAfter`, in\n     * seconds — populated by {@link createApiClient} on `429`/`503`). When\n     * present it overrides the exponential backoff for that attempt. The value\n     * is capped at `maxDelay`. Default: true.\n     */\n    respectRetryAfter?: boolean;\n    /**\n     * Return false to stop retrying for a specific error.\n     *\n     * Default: replay anything that is not a recognisable API error — a\n     * transport failure has no status to judge — and, for one that is, only the\n     * statuses {@link isRetriableStatus} accepts. A `403` on an admin-only\n     * endpoint and a `404` for a deleted record are the server's final answer;\n     * repeating them spends the caller's time to show the same error twice.\n     */\n    shouldRetry?: (error: unknown, attempt: number) => boolean;\n    /** Called before each retry with the upcoming delay. */\n    onRetry?: (info: { attempt: number; delay: number; error: unknown }) => void;\n    /** Cancel pending retries. */\n    signal?: AbortSignal;\n}\n\n/**\n * The default {@link RetryOptions.shouldRetry}.\n *\n * Permissive about what it cannot classify and strict about what it can: an\n * error with no API shape may well be a transport failure, while an API error\n * carrying a deliberate refusal will answer the same on every attempt.\n *\n * @param error - Whatever the attempt threw.\n * @returns Whether the helper should try again.\n */\nfunction defaultShouldRetry(error: unknown): boolean {\n    return !isApiError(error) || isRetriableStatus(error.status);\n}\n\nfunction wait(ms: number, signal?: AbortSignal): Promise<void> {\n    return new Promise<void>((resolve, reject) => {\n        if (signal?.aborted) {\n            reject(new DOMException(\"Aborted\", \"AbortError\"));\n            return;\n        }\n        const timer = setTimeout(resolve, ms);\n        signal?.addEventListener(\n            \"abort\",\n            () => {\n                clearTimeout(timer);\n                reject(new DOMException(\"Aborted\", \"AbortError\"));\n            },\n            { once: true },\n        );\n    });\n}\n\n/**\n * Run `factory()` with exponential backoff. Each attempt awaits an\n * increasing delay capped at `maxDelay`. Throws the last error if every\n * attempt fails.\n *\n * @example\n * const data = await retry(() => api.get(\"/flaky\"), { retries: 5 });\n */\nexport async function retry<T>(factory: () => Promise<T>, options: RetryOptions = {}): Promise<T> {\n    const {\n        retries = 3,\n        initialDelay = 300,\n        maxDelay = 10_000,\n        respectRetryAfter = true,\n        shouldRetry = defaultShouldRetry,\n        onRetry,\n        signal,\n    } = options;\n\n    let attempt = 0;\n    let lastError: unknown;\n\n    while (attempt < retries) {\n        if (signal?.aborted) throw new DOMException(\"Aborted\", \"AbortError\");\n        try {\n            return await factory();\n        } catch (error) {\n            lastError = error;\n            attempt += 1;\n            if (attempt >= retries || !shouldRetry(error, attempt)) {\n                throw error;\n            }\n            const retryAfter =\n                respectRetryAfter &&\n                typeof (error as { retryAfter?: unknown })?.retryAfter === \"number\"\n                    ? (error as { retryAfter: number }).retryAfter * 1000\n                    : null;\n            const delay =\n                retryAfter !== null\n                    ? Math.min(retryAfter, maxDelay)\n                    : Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);\n            onRetry?.({ attempt, delay, error });\n            await wait(delay, signal);\n        }\n    }\n\n    throw lastError;\n}\n"],"mappings":"gCA0CA,SAAS,EAAmB,EAAyB,CACjD,MAAO,CAAC,EAAA,WAAW,CAAK,GAAK,EAAA,kBAAkB,EAAM,MAAM,CAC/D,CAEA,SAAS,EAAK,EAAY,EAAqC,CAC3D,OAAO,IAAI,SAAe,EAAS,IAAW,CAC1C,GAAI,GAAQ,QAAS,CACjB,EAAO,IAAI,aAAa,UAAW,YAAY,CAAC,EAChD,MACJ,CACA,IAAM,EAAQ,WAAW,EAAS,CAAE,EACpC,GAAQ,iBACJ,YACM,CACF,aAAa,CAAK,EAClB,EAAO,IAAI,aAAa,UAAW,YAAY,CAAC,CACpD,EACA,CAAE,KAAM,EAAK,CACjB,CACJ,CAAC,CACL,CAUA,eAAsB,EAAS,EAA2B,EAAwB,CAAC,EAAe,CAC9F,GAAM,CACF,UAAU,EACV,eAAe,IACf,WAAW,IACX,oBAAoB,GACpB,cAAc,EACd,UACA,UACA,EAEA,EAAU,EACV,EAEJ,KAAO,EAAU,GAAS,CACtB,GAAI,GAAQ,QAAS,MAAM,IAAI,aAAa,UAAW,YAAY,EACnE,GAAI,CACA,OAAO,MAAM,EAAQ,CACzB,OAAS,EAAO,CAGZ,GAFA,EAAY,EACZ,GAAW,EACP,GAAW,GAAW,CAAC,EAAY,EAAO,CAAO,EACjD,MAAM,EAEV,IAAM,EACF,GACA,OAAQ,GAAoC,YAAe,SACpD,EAAiC,WAAa,IAC/C,KACJ,EAGI,KAAK,IAFX,IAAe,KAEA,EAAe,IAAM,EAAU,GAD/B,EACmC,CAAQ,EAC9D,IAAU,CAAE,UAAS,QAAO,OAAM,CAAC,EACnC,MAAM,EAAK,EAAO,CAAM,CAC5B,CACJ,CAEA,MAAM,CACV"}