/** * Retry wrapper around a `fetch`-shaped function, specialized for the * Salesforce REST and Tooling APIs. * * Retries on: * - transport errors (fetch throws — DNS, ECONNRESET, etc.) * - HTTP 429 / 500 / 502 / 503 / 504 * - Response bodies matching `REQUEST_LIMIT_EXCEEDED`, * `UNABLE_TO_LOCK_ROW`, `SERVER_UNAVAILABLE`, or * `REQUEST_RUNNING_TOO_LONG` (Salesforce occasionally surfaces these * with 400 or 403, so HTTP status alone isn't enough). * * Backoff: 500ms, 1s, 2s, 4s, 8s with ±25% jitter. Capped at 5 attempts. * Configurable per-call for tests or recovery paths that want different * behavior. * * What we deliberately DO NOT retry: * - 4xx bodies without a known throttling error code (the request is * semantically wrong; a retry won't help). * - 401 (auth is stale — surface fast so the caller can re-auth). * - 2xx responses (even if the body shape is odd, caller handles it). * * The wrapper returns the final `Response` unchanged — callers keep * their existing `res.ok` / `res.json()` handling. For bodies we peek * at for retry purposes, the response is `clone()`d first so the * caller can still read the original. */ import type { OrgAuth } from "./auth/sf-auth.ts"; export type RetryLogger = (msg: string) => void | Promise; export type SalesforceFetchOptions = { /** Max total attempts including the first. Default 5. */ maxAttempts?: number; /** Base delay in ms for the first retry. Default 500. */ baseDelayMs?: number; /** Cap on any single delay in ms. Default 8000. */ maxDelayMs?: number; /** Optional sink for retry events. Receives one line per retry attempt. */ log?: RetryLogger; /** * Injected for tests — lets us short-circuit sleeps. Matches `setTimeout` * without the extra args. Defaults to `setTimeout`. */ sleepFn?: (ms: number) => Promise; }; /** * Execute `fetchFn(url, init)` with retries on transient failures. * * Caller supplies the same `fetchFn` they'd use directly (i.e. the * injected test double or `fetch`). The retry layer is transparent — * a single successful response or a non-retryable failure returns * the underlying `Response` unchanged. */ export declare function salesforceFetch(fetchFn: typeof fetch, url: string, init?: RequestInit, opts?: SalesforceFetchOptions): Promise; /** * Convenience: `salesforceFetch` with an `Authorization: Bearer` header * merged into `init.headers`. Most callers want this shape. */ export declare function salesforceFetchAuthed(fetchFn: typeof fetch, auth: OrgAuth, url: string, init?: RequestInit, opts?: SalesforceFetchOptions): Promise;