/** * Internal — shared HTTP transport used by every service. Not part of the * public API; do not import from outside `src/`. * * Uses the global `fetch` (Node 18+ / browsers / Deno / Bun all ship it). * Errors from the gateway (status >= 400) are parsed and surfaced as * `APIError`. Network errors propagate as the underlying fetch reject. */ export interface TransportOptions { baseURL: string; /** Static bearer token. Required unless `apiKeyResolver` is supplied. */ apiKey?: string; /** Dynamic resolver — called immediately before each request. Use this * when the token is short-lived and refreshed externally (e.g. running * inside a runjobs resource bundle that exposes * `window.runjobs.getToken()`). The result is awaited per request, so * the caller can return a Promise that hits a token endpoint. Wins * over `apiKey` when both are supplied. */ apiKeyResolver?: () => string | Promise; /** * Hook invoked on 401 responses, *before* the request is automatically * retried once. Use this to invalidate any cached token so the next * `apiKeyResolver` call fetches a fresh one. Throwing here cancels * the retry — the original 401 propagates. * * Typical wiring (browser-auth): * onUnauthorized: () => auth.invalidate() * which clears the in-memory + persisted token without flipping the * sticky "signed out" flag. The retry's resolveToken() then triggers * the standard signIn() redirect-grant flow. */ onUnauthorized?: () => void | Promise; /** Optional fetch override (e.g. node-fetch with custom agent). */ fetchImpl?: typeof fetch; } export declare class Transport { readonly baseURL: string; private readonly apiKey; private readonly apiKeyResolver; private readonly onUnauthorized; private readonly fetchImpl; constructor(opts: TransportOptions); /** Resolve the bearer token for the next request. */ private resolveToken; /** * Wrap a fetch call with single-attempt 401 retry. `build` produces a * fresh RequestInit each call (so headers can re-resolve a fresh token, * and bodies can be re-streamed for retry). On 401, invokes * `onUnauthorized()` (giving the caller a chance to invalidate cached * auth state) then retries once with a fresh init. No retry if * `onUnauthorized` is absent or already retried. */ private fetchWithAuthRetry; /** * Every request goes out uncacheable. * * Our identity lives in the Authorization header, never in the URL — so * two different apps calling e.g. `/v1/files/projects/index.json` hit the * *same* URL with different tokens. The browser's HTTP cache doesn't key * on request headers, and it is partitioned by the top-level registrable * domain, which every `*.runjobs.dev` bundle shares. One cacheable * response was therefore enough for app A to read app B's file and then * save it back as its own. The server no longer marks these cacheable; * this is the client-side half of that fix, and it also protects bundles * pinned to an older backend. */ private buildInit; /** POST JSON body; parse JSON response. */ postJSON(path: string, body: unknown, init?: { signal?: AbortSignal; }): Promise; /** GET path; parse JSON response. */ getJSON(path: string, init?: { signal?: AbortSignal; }): Promise; /** GET path; return raw bytes + content-type (for video/audio downloads). */ getRaw(path: string, init?: { signal?: AbortSignal; }): Promise<{ data: Uint8Array; contentType: string; }>; /** POST a multipart/form-data body. Used by audio.transcribe and image.edit. */ postMultipart(path: string, form: FormData, init?: { signal?: AbortSignal; }): Promise; /** * POST JSON body; return the raw streaming Response. Used by chat * streaming. Caller is responsible for consuming the SSE body. */ postJSONStream(path: string, body: unknown, init?: { signal?: AbortSignal; }): Promise; /** PUT raw bytes with caller-supplied content type and headers. */ putBytes(path: string, body: BodyInit, opts?: { contentType?: string; headers?: Record; signal?: AbortSignal; parse?: "json" | "none"; }): Promise; /** DELETE path; parse JSON response (or no body). */ deletePath(path: string, init?: { signal?: AbortSignal; parse?: "json" | "none"; }): Promise; /** HEAD path; surface status + selected response headers. Used by * exists / stat where the body would just be an opaque blob. */ head(path: string, init?: { signal?: AbortSignal; }): Promise<{ status: number; headers: Headers; }>; private jsonHeaders; private authHeaders; private parseError; } //# sourceMappingURL=transport.d.ts.map