/** Attempts including the first. */ export declare const DEFAULT_RETRIES = 3; /** Upper bound on an honored `Retry-After`, so a large value can't stall. */ export declare const MAX_RETRY_AFTER_MS = 30000; export interface RetryPolicy { /** Max attempts including the first. Default {@link DEFAULT_RETRIES}. `1` disables retrying. */ retries?: number; /** Base backoff in ms (exponential, jittered). Default {@link DEFAULT_BASE_DELAY_MS}. Set 0 in tests. */ baseDelayMs?: number; /** Caller cancellation. An abort is never retried and never re-branded. */ signal?: AbortSignal; } /** * Jittered exponential backoff before the retry that follows `attempt` * (1-based). The jitter (±25%) is the only thing keeping a design with fifty * images on one downed host from retrying them in lockstep. */ export declare function retryDelayMs(attempt: number, baseDelayMs?: number): number; /** `Retry-After` as ms — seconds or an HTTP date — capped, never negative. */ export declare function parseRetryAfterMs(header: string | null | undefined): number | undefined; /** 5xx, plus the two throttling statuses. A non-429 4xx is deterministic. */ export declare function isRetryableStatus(status: number): boolean; /** * A transport failure worth repeating. Browser "Failed to fetch" and undici * "fetch failed" both arrive as a bare `TypeError` with no cause worth reading, * which is why the message and `code` sniffing is here too. */ export declare function isRetryableNetworkError(err: unknown): boolean; /** * A URL whose load cannot fail transiently — the bytes are already local, so a * second attempt is pure cost. Everything else (including a relative path) can. */ export declare function isDeterministicUrl(url: string): boolean; interface WithRetryOptions extends RetryPolicy { /** * Called for every failure that is not the last attempt. A number means * "retry, and wait at least this long" — a server-stated `Retry-After`; the * backoff is used instead when it is longer. */ shouldRetry: (err: unknown, attempt: number) => boolean | number; } /** * Run `attempt` until it resolves, `shouldRetry` says stop, or the attempts run * out. The last failure is rethrown untouched — shaping a terminal error is the * caller's job, because only the caller knows which code it belongs under. */ export declare function withRetry(attempt: (attemptNo: number) => Promise, { shouldRetry, retries, baseDelayMs, signal, }: WithRetryOptions): Promise; interface FetchWithRetryOptions extends RetryPolicy { /** Per-attempt timeout in ms. Default {@link DEFAULT_FETCH_TIMEOUT_MS}. */ timeout?: number; /** * SSRF check, run before the request and before following every redirect. * Throws to reject. Injected by the Node platforms (the browser passes * nothing), so this module stays free of Node-only APIs. When present, * redirects are followed by hand so each hop is validated — otherwise a * public URL could 302 to an internal address past a first-hop-only check. */ validateUrl?: (url: string) => Promise; /** Called once per outgoing HTTP request, for metrics. */ onRequest?: () => void; } interface FetchBytesWithRetryOptions extends FetchWithRetryOptions { /** * Reject fetched bytes that fail this check and retry, treating them as a * throttle/error page served as HTTP 200. `looksLikeFont` * (`@polotno/core/fonts/fetch`) is the canonical one. */ validate?: (bytes: Uint8Array) => boolean; } /** * `fetch` with a per-attempt timeout and the shared retry policy. Resolves with * an ok `Response` whose body the caller streams; a terminal failure throws * `FETCH_FAILED` carrying `{ url, status?, attempts, reason }`. * * The timeout covers the HEADERS only — the caller owns the body. Reach for * {@link fetchBytesWithRetry} when you want the whole download bounded. */ export declare function fetchWithRetry(url: string, opts?: FetchWithRetryOptions): Promise; /** * The whole download — headers and body — under one per-attempt timeout and the * shared retry policy. A 200 whose bytes fail `validate` is treated as a * throttle page and retried. */ export declare function fetchBytesWithRetry(url: string, opts?: FetchBytesWithRetryOptions): Promise; export {};