import { ProviderError } from './index'; /** * Outbound HTTP with a budget that is actually respected. * * Some providers rate-limit cost rather than request count. A call may consume * many units while a status probe consumes one, so a per-request limiter can be * wrong by an order of magnitude. This generic ledger accepts the provider's * reported cost without embedding a vendor-specific protocol in the package. * * ## The budget is per HOST, and shared * * Every caller behind one address spends from one pool. A retry loop written in * three places will spend it three times over and none of the three will know. * So the budget lives here, keyed by host, and every call passes through it. * * ## Reservations, not counters * * Weight is reserved BEFORE the request and reconciled after, because the true * cost arrives in a response header. Counting only on the way back means a burst * of concurrent calls all pass the check and collectively blow the budget — * which is precisely what happens when a strategy wakes up. */ export interface HostBudget { host: string; /** Provider-defined units per window, or requests for a simpler API. */ limit: number; windowMs: number; /** Assumed cost before the real one is known. */ defaultCost: number; /** * Stop at a fraction of the limit rather than at it. * * The venue's clock and ours are not the same clock, and its window boundary * is not ours. Spending to exactly 100% means crossing it on the venue's * arithmetic while ours says there was room — and the penalty for crossing is * a ban, not a rejected request. */ headroom?: number; } interface Window { spent: number; resetAtMs: number; } export declare class BudgetExhausted extends ProviderError { readonly host: string; readonly retryAfterMs: number; constructor(host: string, retryAfterMs: number); } /** Tests and a credential rotation. Nothing else should need it. */ export declare function resetBudgets(): void; export declare function spend(budget: HostBudget, cost: number, nowMs: number): void; /** Reconcile a reservation against what the response says it actually cost. */ export declare function settle(host: string, reserved: number, actual: number): void; export declare const budgetState: (host: string) => Window | undefined; export interface HttpConfig { baseUrl: string; budget: HostBudget; /** * Reads the true cost out of a response. The provider adapter owns the * response-header contract rather than this generic client guessing. */ costOf?: (response: Response) => number | undefined; fetch?: typeof globalThis.fetch; timeoutMs?: number; } export interface HttpRequest { path: string; method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; query?: Record; body?: unknown; headers?: Record; /** What this call is expected to cost. Defaults to the budget's default. */ weight?: number; } export interface HttpResponse { status: number; body: T; /** What the call actually cost, when the venue reported it. */ cost?: number; } export declare function createHttpClient(config: HttpConfig): { call(request: HttpRequest): Promise>; budget: () => Window | undefined; }; export type HttpClient = ReturnType; /** * The registry face, so an outbound HTTP dependency gets the same credential * rotation and health tracking as a mail relay. * * `classify` is where the venue-specific knowledge lives, and it is the part * that decides whether a retry helps or makes things worse. */ export declare const http: import("./index").ProviderDefinition>>>; /** Build a provider-specific response-cost reader without coupling this module to a vendor. */ export declare const responseHeaderCost: (headerName: string) => (response: Response) => number | undefined; export declare const httpProviders: readonly [import("./index").ProviderDefinition>>>]; export {};