/** * vapor-chamber — HTTP client * * Adapted and improved from useFetch (2026-02-05A). * TypeScript rewrite aligned with vapor-chamber conventions and CDCC thresholds. * * Improvements over the original: * - Full TypeScript types (no `any` casts) * - CDCC-compliant function sizes * - `AbortSignal.any` with manual fallback for older environments * - Jitter on exponential backoff (avoids thundering herd) * - `X-RateLimit-Reset` header as Retry-After fallback * - 419 CSRF refresh coalesces concurrent requests (no duplicate refreshes) * - `session-expired` CustomEvent + configurable callback * - `TimeoutError` distinct from `AbortError` (user abort vs timeout) */ export type HttpConfig = { /** Request timeout in ms. Default: 10_000 */ timeout?: number; /** Max retry attempts on 5xx/429/408. Default: 0 */ retry?: number; /** External abort signal (e.g. from component unmount) */ signal?: AbortSignal; /** Read CSRF token from DOM and attach as header. Default: false */ csrf?: boolean; /** * URL to fetch when a CSRF-expiry response (HTTP 419) occurs, to obtain a * fresh token. The default targets the Laravel Sanctum SPA convention * because it's the most common backend issuing 419 — override for other * frameworks, or set to '' to disable the auto-refresh entirely (the lib * will then only re-read the token from the DOM on retry). * Default: '/sanctum/csrf-cookie'. */ csrfCookieUrl?: string; /** Additional headers merged into every request */ headers?: Record; /** Called when a 401 session-expired response is received */ onSessionExpired?: (status: number) => void; /** * Stamps a thrown error's `.silent` so a caller-provided global error * handler can skip it — for fire-and-forget requests (best-effort * telemetry, background prefetch) that shouldn't surface UI noise. * Default: false. */ silent?: boolean; }; export type HttpResponse = { data: T; status: number; headers: Record; ok: boolean; /** True when this response was served from a stale (past-fresh) cache entry. */ stale?: boolean; /** Present on a stale hit: resolves with the fresh response once the background revalidation lands. */ revalidation?: Promise>; /** True when this is a retained cache entry served in place of a transient failure (`cache.serveStaleOnError`). */ servedOnError?: boolean; /** The transient error `servedOnError` masked — surfaced alongside the stale data, never silently dropped. */ error?: unknown; }; export type HttpError = Error & { name: 'HttpError' | 'TimeoutError' | 'AbortError'; response?: HttpResponse; status?: number; /** Machine-readable error code from response body (e.g. `'CART_ITEM_LIMIT_EXCEEDED'`). */ code?: string; /** Set when the request's `silent: true` config opts the caller out of a global error handler/toast. */ silent?: boolean; }; type CsrfResult = { token: string; headerName: string; }; /** Read CSRF token from DOM: meta tag → cookie → hidden input. TTL-cached for 5 min. */ export declare function readCsrfToken(): CsrfResult | null; /** Invalidate the CSRF token cache (e.g. after logout). */ export declare function invalidateCsrfCache(): void; export declare function postCommand(url: string, body: unknown, config?: HttpConfig): Promise>; export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; export type ResponseType = 'json' | 'blob' | 'text'; export type HttpRequestConfig = HttpConfig & { /** HTTP method. Default: 'GET' */ method?: HttpMethod; /** Request body (auto-serialized if object, passthrough for FormData) */ data?: unknown; /** Query parameters — supports arrays and nested objects */ params?: Record; /** Base URL prepended to relative paths */ baseURL?: string; /** Response parsing mode. Default: 'json' */ responseType?: ResponseType; /** * Enable LRU caching for GET. `true` = default TTL, no stale window. * `{ ttl }` sets the fresh-window duration. `{ staleTtl }` opts into * stale-while-revalidate: a hit within `ttl + staleTtl` past `ttl` is * served instantly with `{ stale: true, revalidation: Promise }` attached, * while a background fetch refreshes the entry. `serveStaleOnError` is a * separate opt-in: a transient failure (timeout/network/5xx) with ANY * retained entry (even past its stale window) resolves to * `{ stale: true, servedOnError: true, error }` instead of rejecting. */ cache?: boolean | { ttl?: number; staleTtl?: number; serveStaleOnError?: boolean; }; /** Enable request deduplication for GET. Default: true */ dedupe?: boolean; /** @internal marks a CSRF-retried request */ _csrfRetried?: boolean; }; export type SafeResult = { data: T | null; error: { message: string; code?: string; [key: string]: unknown; } | null; status: number; }; export type DownloadResult = { data: Blob; status: number; filename: string; }; export type Interceptor = { onFulfilled?: (value: T) => T | void; onRejected?: (error: unknown) => void; }; export type InterceptorManager = { use(onFulfilled?: (value: T) => T | void, onRejected?: (error: unknown) => void): number; eject(id: number): void; }; export type HttpClient = { get(url: string, config?: HttpRequestConfig): Promise>; post(url: string, data?: unknown, config?: HttpRequestConfig): Promise>; put(url: string, data?: unknown, config?: HttpRequestConfig): Promise>; patch(url: string, data?: unknown, config?: HttpRequestConfig): Promise>; delete(url: string, config?: HttpRequestConfig): Promise>; request(url: string, config?: HttpRequestConfig): Promise>; download(url: string, filename?: string, config?: HttpRequestConfig): Promise; safe: { get(url: string, config?: HttpRequestConfig): Promise>; post(url: string, data?: unknown, config?: HttpRequestConfig): Promise>; put(url: string, data?: unknown, config?: HttpRequestConfig): Promise>; patch(url: string, data?: unknown, config?: HttpRequestConfig): Promise>; delete(url: string, config?: HttpRequestConfig): Promise>; }; interceptors: { request: InterceptorManager; response: InterceptorManager; }; create(defaults?: Partial): HttpClient; clearCache(): void; invalidateCache(pattern: string | RegExp): void; }; /** * createHttpClient — multi-method HTTP client with interceptors, caching, * deduplication, safe mode, and file download. * * Aligned with useFetch (2026-02-05A) patterns. Framework-agnostic — no Vue imports. * * @example * const http = createHttpClient({ baseURL: '/api', csrf: true }); * * // All methods * const users = await http.get('/users', { params: { page: 1 } }); * await http.post('/cart', { itemId: 1, qty: 2 }); * await http.put('/cart/1', { qty: 5 }); * await http.delete('/cart/1'); * * // Safe mode — never throws * const result = await http.safe.post('/login', credentials); * if (result.error) console.log(result.error.message); * * // File download * await http.download('/export/csv', 'products.csv'); * * // Interceptors * http.interceptors.request.use((config) => { config.headers = { ...config.headers, 'X-Custom': '1' }; return config; }); * * // Create scoped instance * const adminHttp = http.create({ baseURL: '/admin/api', headers: { 'X-Admin': 'true' } }); */ export declare function createHttpClient(instanceDefaults?: Partial): HttpClient; export {}; //# sourceMappingURL=http.d.ts.map