react-native-fetch-client 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1744 @@
1
+ /**
2
+ * Error hierarchy for react-native-fetch-client.
3
+ *
4
+
5
+ * Key design decision: HttpError is NOT a subclass of NetworkError.
6
+ * A 404 or 500 is a received HTTP response — not a failure to connect.
7
+ * Use the `.code` property or `instanceof` to distinguish error types.
8
+ */
9
+
10
+ type BaseOptions = {
11
+ config?: ResolvedRequestConfig;
12
+ cause?: unknown;
13
+ };
14
+ /**
15
+ * Base class for all errors thrown by react-native-fetch-client.
16
+ * Extends the native Error class and adds a discriminant `code` field.
17
+ */
18
+ declare class FetchNativeError extends Error {
19
+ /** Discriminant code identifying the specific error type. */
20
+ readonly code: ErrorCode;
21
+ /**
22
+ * The resolved request configuration at the time of the error.
23
+ * Authorization header values are redacted to '[REDACTED]'.
24
+ */
25
+ readonly config?: ResolvedRequestConfig;
26
+ constructor(message: string, code: ErrorCode, options?: BaseOptions);
27
+ }
28
+ /**
29
+ * Thrown when the server returns a non-2xx HTTP response.
30
+ *
31
+ * This is NOT a network error. The request was successfully sent and a
32
+ * response was received — it just carried an error status code.
33
+ */
34
+ declare class HttpError extends FetchNativeError {
35
+ /** HTTP status code (e.g. 404, 500). */
36
+ readonly status: number;
37
+ /** HTTP status text (e.g. 'Not Found', 'Internal Server Error'). */
38
+ readonly statusText: string;
39
+ /**
40
+ * Parsed response body, if available and parseable.
41
+ * May be a string, object, or null depending on the response content.
42
+ */
43
+ readonly data: unknown;
44
+ /** Response headers from the server. Keys are normalized to lowercase. */
45
+ readonly headers: Record<string, string>;
46
+ /**
47
+ * The original Fetch Response object.
48
+ * Do not log this directly — it may contain sensitive header values.
49
+ */
50
+ readonly response: Response;
51
+ constructor(message: string, options: {
52
+ status: number;
53
+ statusText: string;
54
+ data: unknown;
55
+ headers: Record<string, string>;
56
+ response: Response;
57
+ config?: ResolvedRequestConfig;
58
+ });
59
+ }
60
+ /**
61
+ * Thrown when fetch() rejects before any response is received.
62
+ * This represents a true network failure: DNS failure, connection refused,
63
+ * offline, CORS preflight failure, etc.
64
+ *
65
+ * Distinct from HttpError: no HTTP response was received.
66
+ */
67
+ declare class NetworkError extends FetchNativeError {
68
+ constructor(message: string, options?: {
69
+ config?: ResolvedRequestConfig;
70
+ cause?: unknown;
71
+ });
72
+ }
73
+ /**
74
+ * Thrown when a request exceeds the configured timeout.
75
+ * The request was aborted — no response was received.
76
+ *
77
+ * Note: This is a distinct class from NetworkError, even though both represent
78
+ * cases where no HTTP response was received. Callers can distinguish them with
79
+ * `instanceof` or the `.code` field ('TIMEOUT' vs 'NETWORK_ERROR').
80
+ */
81
+ declare class TimeoutError extends FetchNativeError {
82
+ /** The timeout that was exceeded, in milliseconds. */
83
+ readonly timeoutMs: number;
84
+ constructor(timeoutMs: number, options?: {
85
+ config?: ResolvedRequestConfig;
86
+ cause?: unknown;
87
+ });
88
+ }
89
+ /**
90
+ * Thrown when a request is cancelled via an AbortSignal provided by the caller.
91
+ *
92
+ * Note: Timeout cancellations throw TimeoutError, not CancelError.
93
+ * CancelError is only for caller-initiated cancellation.
94
+ */
95
+ declare class CancelError extends FetchNativeError {
96
+ constructor(options?: {
97
+ config?: ResolvedRequestConfig;
98
+ cause?: unknown;
99
+ });
100
+ }
101
+ /**
102
+ * Thrown when a response body cannot be parsed as the expected type.
103
+ * The HTTP request succeeded (2xx), but the body was malformed.
104
+ */
105
+ declare class ParseError extends FetchNativeError {
106
+ /** HTTP status code of the response whose body could not be parsed. */
107
+ readonly status: number;
108
+ /** The raw response body text that failed to parse, if retrievable. */
109
+ readonly rawBody: string | null;
110
+ constructor(message: string, options: {
111
+ status: number;
112
+ rawBody: string | null;
113
+ config?: ResolvedRequestConfig;
114
+ cause?: unknown;
115
+ });
116
+ }
117
+ /**
118
+ * Thrown when a request cannot be dispatched due to invalid configuration.
119
+ * Examples: missing URL, invalid base URL, conflicting options.
120
+ */
121
+ declare class ConfigError extends FetchNativeError {
122
+ constructor(message: string, options?: {
123
+ cause?: unknown;
124
+ });
125
+ }
126
+ /**
127
+ * Thrown when all retry attempts have been consumed and the last attempt failed.
128
+ * Wraps the last error as `cause` for full diagnostics.
129
+ */
130
+ declare class RetryExhaustedError extends FetchNativeError {
131
+ /** The total number of attempts made (initial + retries). */
132
+ readonly attempts: number;
133
+ /** The error from the last failed attempt. */
134
+ readonly lastError: unknown;
135
+ constructor(attempts: number, lastError: unknown, options?: {
136
+ config?: ResolvedRequestConfig;
137
+ });
138
+ }
139
+ /**
140
+ * Thrown when response data fails schema validation (e.g. via Zod, Standard Schema,
141
+ * or a custom functional validator).
142
+ */
143
+ declare class ValidationError extends FetchNativeError {
144
+ /** The raw unvalidated data returned by the server. */
145
+ readonly data: unknown;
146
+ /** Detailed validation issue objects or messages if provided by the validator. */
147
+ readonly issues?: unknown;
148
+ constructor(message: string, options?: {
149
+ data?: unknown;
150
+ issues?: unknown;
151
+ config?: ResolvedRequestConfig;
152
+ cause?: unknown;
153
+ });
154
+ }
155
+ /**
156
+ * Thrown when a request is short-circuited because the circuit breaker is currently OPEN
157
+ * due to consecutive upstream failures.
158
+ */
159
+ declare class CircuitBreakerError extends FetchNativeError {
160
+ /** Current state of the circuit breaker at the moment of the rejection ('OPEN' | 'HALF_OPEN'). */
161
+ readonly state: 'OPEN' | 'HALF_OPEN';
162
+ /** Configured cooldown period before canary trial in milliseconds. */
163
+ readonly resetTimeoutMs: number;
164
+ constructor(message: string, options: {
165
+ state: 'OPEN' | 'HALF_OPEN';
166
+ resetTimeoutMs: number;
167
+ config?: ResolvedRequestConfig;
168
+ cause?: unknown;
169
+ });
170
+ }
171
+ /** Returns true if `err` is any react-native-fetch-client error. */
172
+ declare function isFetchNativeError(err: unknown): err is FetchNativeError;
173
+ /** Returns true if `err` is an HttpError (non-2xx response received). */
174
+ declare function isHttpError(err: unknown): err is HttpError;
175
+ /** Returns true if `err` is a NetworkError (no response received). */
176
+ declare function isNetworkError(err: unknown): err is NetworkError;
177
+ /** Returns true if `err` is a TimeoutError. */
178
+ declare function isTimeoutError(err: unknown): err is TimeoutError;
179
+ /** Returns true if `err` is a CancelError (caller-initiated cancellation). */
180
+ declare function isCancelError(err: unknown): err is CancelError;
181
+ /** Returns true if `err` is a RetryExhaustedError. */
182
+ declare function isRetryExhaustedError(err: unknown): err is RetryExhaustedError;
183
+ /** Returns true if `err` is a ValidationError. */
184
+ declare function isValidationError(err: unknown): err is ValidationError;
185
+ /** Returns true if `err` is a CircuitBreakerError. */
186
+ declare function isCircuitBreakerError(err: unknown): err is CircuitBreakerError;
187
+
188
+ /**
189
+ * Public types for react-native-fetch-client.
190
+ *
191
+
192
+ * All types in this file form the stable public API surface.
193
+ * Implementation details are kept in their respective modules.
194
+ *
195
+ * Design principles:
196
+ * - Zero production runtime dependencies (`dependencies: {}`)
197
+ * - Fetch-native primitives (Request, Response, Headers, ReadableStream)
198
+ * - Strict type inference with TypeScript 5+
199
+ * - Comprehensive JSDoc definitions for enhanced developer experience
200
+ */
201
+
202
+ /** Supported HTTP methods. */
203
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
204
+ /** The expected response body format. */
205
+ type ResponseType = 'json' | 'text' | 'blob' | 'arrayBuffer' | 'none' | 'stream';
206
+ /** A fetch implementation compatible with the global `fetch` signature. */
207
+ type FetchImpl = typeof globalThis.fetch;
208
+ /**
209
+ * Progress event payload emitted by download and upload progress callbacks.
210
+ */
211
+ interface ProgressEvent {
212
+ /** Number of bytes transferred so far. */
213
+ loaded: number;
214
+ /** Total number of bytes if known (from Content-Length header or body size). */
215
+ total?: number;
216
+ /** Fraction of transfer completed (0.0 to 1.0) if total is known. */
217
+ progress?: number;
218
+ /** Number of bytes transferred in the most recent chunk. */
219
+ bytes: number;
220
+ }
221
+ /**
222
+ * Path parameters for URL template interpolation.
223
+ * Replaces `:paramName` or `{paramName}` placeholders in the URL with URI-encoded values.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * client.get('/users/:id/orders/{orderId}', {
228
+ * pathParams: { id: 123, orderId: 'ord-456' }
229
+ * });
230
+ * // Dispatches GET to: /users/123/orders/ord-456
231
+ * ```
232
+ */
233
+ type PathParams = Record<string, string | number | boolean>;
234
+ /**
235
+ * Query parameters accepted on a request.
236
+ * Values of `null` or `undefined` are omitted from the serialized query string.
237
+ */
238
+ type QueryParams = Record<string, string | number | boolean | null | undefined>;
239
+ /**
240
+ * Standard Schema V1 interface compliant with the cross-library specification.
241
+ * Supported by modern versions of Zod (3.24+), Valibot, ArkType, etc.
242
+ */
243
+ interface StandardSchemaV1<T = unknown> {
244
+ readonly '~standard': {
245
+ readonly version: 1;
246
+ readonly vendor: string;
247
+ readonly validate: (value: unknown) => {
248
+ readonly value: T;
249
+ readonly issues?: never;
250
+ } | {
251
+ readonly issues: ReadonlyArray<{
252
+ readonly message: string;
253
+ readonly path?: ReadonlyArray<unknown>;
254
+ }>;
255
+ readonly value?: never;
256
+ } | Promise<{
257
+ readonly value: T;
258
+ readonly issues?: never;
259
+ } | {
260
+ readonly issues: ReadonlyArray<{
261
+ readonly message: string;
262
+ readonly path?: ReadonlyArray<unknown>;
263
+ }>;
264
+ readonly value?: never;
265
+ }>;
266
+ };
267
+ }
268
+ /**
269
+ * Zod-like validator interface exposing `.parse()` or `.safeParse()`.
270
+ */
271
+ interface ZodLikeSchema<T = unknown> {
272
+ parse?(data: unknown): T;
273
+ safeParse?(data: unknown): {
274
+ success: true;
275
+ data: T;
276
+ } | {
277
+ success: false;
278
+ error: {
279
+ message: string;
280
+ issues?: unknown;
281
+ };
282
+ };
283
+ }
284
+ /**
285
+ * Plain synchronous or asynchronous function validator.
286
+ */
287
+ type FunctionalValidator<T = unknown> = (data: unknown) => T | Promise<T>;
288
+ /**
289
+ * Duck-typed schema validator accepted by react-native-fetch-client.
290
+ * Compatible with:
291
+
292
+ * 1. Standard Schema v1 (`~standard`)
293
+ * 2. Zod (`z.object({...})`)
294
+ * 3. Valibot / ArkType / Yup / Superstruct
295
+ * 4. Plain validation functions `(data: unknown) => T`
296
+ */
297
+ type SchemaValidator<T = unknown> = StandardSchemaV1<T> | ZodLikeSchema<T> | FunctionalValidator<T>;
298
+ /**
299
+ * An event emitted by a Server-Sent Events (SSE) or chunk stream.
300
+ * Parsed according to the WHATWG EventSource specification.
301
+ */
302
+ interface StreamEvent {
303
+ /** The event type specified in `event: <name>`. Defaults to undefined (standard message). */
304
+ event?: string;
305
+ /** The text payload accumulated from `data: <content>` lines. */
306
+ data: string;
307
+ /** The event ID specified in `id: <id>`. */
308
+ id?: string;
309
+ /** The reconnection time in milliseconds specified in `retry: <ms>`. */
310
+ retry?: number;
311
+ }
312
+ /**
313
+ * Options for streaming requests via `api.stream()`.
314
+ */
315
+ interface StreamOptions extends Omit<RequestConfig, 'responseType'> {
316
+ /**
317
+ * If true, yields raw non-empty text lines rather than parsing SSE event envelopes.
318
+ * Default: false (parses SSE lines into StreamEvent objects).
319
+ */
320
+ rawLines?: boolean;
321
+ }
322
+ /**
323
+ * Concurrency configuration for capping simultaneous HTTP requests.
324
+ */
325
+ interface ConcurrencyConfig {
326
+ /**
327
+ * Maximum number of concurrent in-flight requests permitted simultaneously.
328
+ * Requests exceeding this limit wait in an in-memory FIFO queue.
329
+ * 0 or undefined means unlimited. Default: undefined.
330
+ */
331
+ maxConcurrentRequests?: number;
332
+ }
333
+ /** State of the circuit breaker state machine. */
334
+ type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
335
+ /**
336
+ * Configuration for the request circuit breaker.
337
+ * Prevents cascading outages by short-circuiting calls to repeatedly failing endpoints.
338
+ */
339
+ interface CircuitBreakerConfig {
340
+ /** Must be set to true to enable the circuit breaker. Default: false. */
341
+ enabled: boolean;
342
+ /** Number of consecutive failures before tripping from CLOSED to OPEN. Default: 5. */
343
+ failureThreshold?: number;
344
+ /** Time in milliseconds to remain OPEN before testing in HALF_OPEN state. Default: 30000 (30s). */
345
+ resetTimeoutMs?: number;
346
+ /** Custom predicate to determine whether an error counts towards failure threshold. */
347
+ isFailure?: (error: unknown) => boolean;
348
+ }
349
+ /**
350
+ * Result tuple returned by `api.safe.*` methods.
351
+ * Implements Go / Rust result semantics: `[data, null]` on success, `[null, error]` on failure.
352
+ * Eliminates try/catch boilerplate in application code.
353
+ */
354
+ type SafeResult<T> = [data: T, error: null] | [data: null, error: FetchNativeError];
355
+ /**
356
+ * Diagnostic log event generated when `debug: true` or a custom logger is enabled.
357
+ */
358
+ interface DebugLogEntry {
359
+ /** HTTP method of the request. */
360
+ method: HttpMethod;
361
+ /** Target request URL. */
362
+ url: string;
363
+ /** HTTP status code (if a response was received). */
364
+ status?: number;
365
+ /** Total elapsed round-trip duration in milliseconds. */
366
+ durationMs: number;
367
+ /** Normalized request headers with authorization tokens redacted. */
368
+ headers?: Record<string, string>;
369
+ /** Whether the response was served from the in-memory cache. */
370
+ cached?: boolean;
371
+ /** Whether the response was returned from an in-flight duplicate deduplication. */
372
+ fromDedup?: boolean;
373
+ /** Error object if the request failed. */
374
+ error?: unknown;
375
+ }
376
+ /**
377
+ * Custom logger function accepted by the `debug` option.
378
+ */
379
+ type DebugLogger = (entry: DebugLogEntry) => void;
380
+ /**
381
+ * Supported file input types for `api.upload()`.
382
+ */
383
+ type UploadFileSource = Blob | File | FormData | ArrayBuffer | Uint8Array | Record<string, unknown>;
384
+ /**
385
+ * Additional options for `api.upload()`.
386
+ */
387
+ interface UploadOptions extends Omit<RequestConfig, 'body'> {
388
+ /**
389
+ * The form field name used when wrapping a raw Blob / File into FormData.
390
+ * Default: `'file'`.
391
+ */
392
+ fieldName?: string;
393
+ /**
394
+ * Optional file name passed to `FormData.append(fieldName, blob, fileName)`.
395
+ */
396
+ fileName?: string;
397
+ /**
398
+ * Additional text or metadata fields appended to the FormData.
399
+ */
400
+ extraFields?: Record<string, string | number | boolean>;
401
+ }
402
+ /**
403
+ * Per-request configuration. All fields are optional.
404
+ * Merged on top of ClientConfig defaults; per-request values take precedence.
405
+ */
406
+ interface RequestConfig {
407
+ /** Absolute URL, or path appended to `baseURL`. */
408
+ url?: string;
409
+ /** HTTP method (default: 'GET'). */
410
+ method?: HttpMethod;
411
+ /**
412
+ * Base URL prepended to `url` when `url` is a relative path.
413
+ * Overrides the client-level baseURL for this request only.
414
+ */
415
+ baseURL?: string;
416
+ /**
417
+ * HTTP headers. Merged with client-level defaults; per-request values win.
418
+ * Header names are case-insensitive.
419
+ */
420
+ headers?: HeadersInit;
421
+ /**
422
+ * Query parameters appended to the URL.
423
+ * Merged with any params already present in `url`.
424
+ */
425
+ params?: QueryParams;
426
+ /**
427
+ * Path parameters interpolated into `:paramName` or `{paramName}` placeholders in the URL.
428
+ */
429
+ pathParams?: PathParams;
430
+ /**
431
+ * Request body.
432
+ * - Objects and arrays are JSON-serialized; Content-Type is set to application/json.
433
+ * - `FormData` is passed through; Content-Type is intentionally NOT set (Fetch sets it with boundary).
434
+ * - `string`, `Blob`, `ArrayBuffer`, `URLSearchParams` are passed through as-is.
435
+ * - `null` / `undefined` result in no body.
436
+ */
437
+ body?: BodyInit | Record<string, unknown> | unknown[] | null;
438
+ /**
439
+ * Request timeout in milliseconds.
440
+ * 0 means no timeout.
441
+ * Overrides the client-level timeout for this request.
442
+ */
443
+ timeout?: number;
444
+ /**
445
+ * An AbortSignal for caller-controlled cancellation.
446
+ * Composed with the internal timeout signal using AbortSignal.any() or a polyfill.
447
+ */
448
+ signal?: AbortSignal;
449
+ /**
450
+ * Expected response body type.
451
+ * Defaults to 'json'. Use 'none' for responses with no body (e.g. 204 No Content).
452
+ * 'json' falls back to 'text' if the body is empty to avoid parse errors.
453
+ */
454
+ responseType?: ResponseType;
455
+ /** Controls credentials (cookies) behavior. Passed directly to fetch(). */
456
+ credentials?: RequestCredentials;
457
+ /**
458
+ * Fetch redirect mode ('follow' | 'manual' | 'error').
459
+ * Passed directly to fetch(). Default: 'follow'.
460
+ */
461
+ redirect?: RequestRedirect;
462
+ /**
463
+ * Node.js Undici Dispatcher (ProxyAgent, custom Agent, mTLS).
464
+ * Passed through to fetch() in Node.js 18+ environments.
465
+ */
466
+ dispatcher?: unknown;
467
+ /**
468
+ * If true, serializes a plain object body to application/x-www-form-urlencoded
469
+ * using URLSearchParams.
470
+ */
471
+ formUrlEncoded?: boolean;
472
+ /**
473
+ * Callback invoked as response bytes are received.
474
+ * Uses WHATWG ReadableStream under the hood.
475
+ */
476
+ onDownloadProgress?: (event: ProgressEvent) => void;
477
+ /**
478
+ * Callback invoked as request body bytes are uploaded.
479
+ */
480
+ onUploadProgress?: (event: ProgressEvent) => void;
481
+ /**
482
+ * Per-request retry policy override.
483
+ * Set to `false` to disable retry for this request even if the client has a default policy.
484
+ */
485
+ retry?: Partial<RetryPolicy> | false;
486
+ /**
487
+ * Override the Fetch implementation for this specific request.
488
+ * Useful for testing or platform-specific transports.
489
+ */
490
+ fetchImpl?: FetchImpl;
491
+ /**
492
+ * Request-level authentication override.
493
+ * Set to `false` or `{ skip: true }` to bypass token attachment and refresh for this request.
494
+ */
495
+ auth?: boolean | {
496
+ skip?: boolean;
497
+ };
498
+ /**
499
+ * Static or dynamic Bearer token string or async getter function.
500
+ * Automatically attaches `Authorization: Bearer <token>` if no Authorization header exists.
501
+ */
502
+ bearerToken?: string | (() => string | null | undefined | Promise<string | null | undefined>);
503
+ /**
504
+ * Request-level deduplication override.
505
+ * Set to `true` or `{ enabled: true }` to enable in-flight deduplication for this request.
506
+ * Set to `false` or `{ enabled: false }` to disable.
507
+ */
508
+ dedup?: DedupConfig | boolean;
509
+ /**
510
+ * Request-level cache override.
511
+ * Set to `true` to enable caching for this request (uses default TTL).
512
+ * Set to `false` or `{ skip: true }` to bypass caching.
513
+ * Pass `{ ttl: number }` to specify a custom TTL in milliseconds.
514
+ */
515
+ cache?: RequestCacheConfig | boolean;
516
+ /**
517
+ * Optional schema validator (Zod, Standard Schema, Valibot, or function) to parse and validate
518
+ * the response data before returning.
519
+ */
520
+ schema?: SchemaValidator<unknown>;
521
+ /**
522
+ * If true, unwraps the response and returns `response.data` directly instead of the full envelope.
523
+ */
524
+ unwrap?: boolean;
525
+ /**
526
+ * Enables diagnostic logging to the console, or invokes a custom logger function.
527
+ */
528
+ debug?: boolean | DebugLogger;
529
+ /**
530
+ * Per-request circuit breaker override or configuration.
531
+ */
532
+ circuitBreaker?: CircuitBreakerConfig | boolean;
533
+ /**
534
+ * Internal tracking flag indicating whether this request is a post-refresh retry.
535
+ */
536
+ _isRefreshed?: boolean;
537
+ /** Internal tracking flag indicating whether this request is being executed inside dedup wrapper. */
538
+ _isFromDedup?: boolean;
539
+ /** Internal tracking flag indicating whether this request has already acquired a concurrency slot. */
540
+ _isFromConcurrency?: boolean;
541
+ /**
542
+ * Callback fired when background revalidation finishes in 'stale-while-revalidate' mode.
543
+ */
544
+ onRevalidated?: (response: HttpResponse<unknown>) => void;
545
+ /** Internal tracking flag indicating whether this request is a background SWR revalidation. */
546
+ _isRevalidating?: boolean;
547
+ }
548
+ /**
549
+ * Client-level defaults. All values apply to every request unless overridden.
550
+ * Extends RequestConfig so any RequestConfig field can be set as a client default.
551
+ */
552
+ interface ClientConfig extends Omit<RequestConfig, 'url' | 'method' | 'signal' | 'auth' | 'dedup' | 'cache' | 'circuitBreaker'> {
553
+ /**
554
+ * Base URL prepended to all relative request URLs.
555
+ * Must not include a trailing slash (enforced at runtime with a warning).
556
+ */
557
+ baseURL?: string;
558
+ /**
559
+ * Default timeout for all requests in milliseconds. Default: 0 (no timeout).
560
+ */
561
+ timeout?: number;
562
+ /**
563
+ * Default retry policy applied to all requests.
564
+ * Individual requests can override or disable via RequestConfig.retry.
565
+ */
566
+ retry?: Partial<RetryPolicy>;
567
+ /**
568
+ * Request deduplication configuration.
569
+ * Disabled by default — must be explicitly enabled.
570
+ */
571
+ dedup?: DedupConfig;
572
+ /**
573
+ * In-memory cache configuration.
574
+ * Disabled by default — must be explicitly enabled.
575
+ */
576
+ cache?: CacheConfig;
577
+ /**
578
+ * Authentication configuration.
579
+ * Provides callbacks for attaching tokens and refreshing them on 401.
580
+ */
581
+ auth?: AuthConfig;
582
+ /**
583
+ * Static or dynamic Bearer token string or async getter function.
584
+ * Automatically attaches `Authorization: Bearer <token>` if no Authorization header exists.
585
+ */
586
+ bearerToken?: string | (() => string | null | undefined | Promise<string | null | undefined>);
587
+ /**
588
+ * Maximum number of concurrent HTTP requests permitted simultaneously.
589
+ * Excess requests wait in a FIFO queue. Default: undefined (unlimited).
590
+ */
591
+ maxConcurrentRequests?: number;
592
+ /**
593
+ * Circuit breaker configuration to prevent cascading failures.
594
+ */
595
+ circuitBreaker?: CircuitBreakerConfig;
596
+ /**
597
+ * Custom Fetch implementation used for all requests from this client.
598
+ * Defaults to `globalThis.fetch`.
599
+ */
600
+ fetchImpl?: FetchImpl;
601
+ /**
602
+ * Enables diagnostic logging for all requests dispatched by this client.
603
+ */
604
+ debug?: boolean | DebugLogger;
605
+ /**
606
+ * Default unwrapping behavior. When true, returns `response.data` directly.
607
+ */
608
+ unwrap?: boolean;
609
+ }
610
+ /**
611
+ * The fully resolved configuration after merging client defaults and per-request overrides.
612
+ * Exposed on HttpResponse and error objects for debugging.
613
+ * Authorization headers are redacted before being stored here.
614
+ */
615
+ interface ResolvedRequestConfig {
616
+ url: string;
617
+ method: HttpMethod;
618
+ headers: Record<string, string>;
619
+ params?: QueryParams;
620
+ pathParams?: PathParams;
621
+ timeout: number;
622
+ responseType: ResponseType;
623
+ credentials?: RequestCredentials;
624
+ retry: ResolvedRetryPolicy;
625
+ }
626
+ /**
627
+ * The resolved response envelope returned from client methods.
628
+ * @template T The type of the parsed response body.
629
+ */
630
+ interface HttpResponse<T = unknown> {
631
+ /** Parsed response data. Type depends on `responseType` and `schema`. */
632
+ data: T;
633
+ /** HTTP status code (e.g. 200, 404). */
634
+ status: number;
635
+ /** HTTP status text (e.g. 'OK', 'Not Found'). */
636
+ statusText: string;
637
+ /** Response headers. Keys are normalized to lowercase. */
638
+ headers: Record<string, string>;
639
+ /** The resolved configuration used for this request. Authorization is redacted. */
640
+ config: ResolvedRequestConfig;
641
+ /** True when data was served from the in-memory cache or 304 Not Modified. */
642
+ cached: boolean;
643
+ /** True when data was served from stale cache in stale-while-revalidate mode. */
644
+ stale?: boolean;
645
+ /** True when this response was shared with another in-flight duplicate request. */
646
+ fromDedup: boolean;
647
+ /** The original Fetch Request object. */
648
+ request: Request;
649
+ }
650
+ /**
651
+ * Retry policy for a request.
652
+ * Only idempotent methods are retried by default.
653
+ */
654
+ interface RetryPolicy {
655
+ /**
656
+ * Maximum number of retry attempts after the initial request.
657
+ * Default: 0 (no retries).
658
+ */
659
+ attempts: number;
660
+ /**
661
+ * Backoff strategy between retries.
662
+ * - 'exponential': delay doubles each attempt (e.g. 1s, 2s, 4s).
663
+ * - 'linear': delay grows linearly (delay * attempt).
664
+ * Default: 'exponential'.
665
+ */
666
+ backoff: 'exponential' | 'linear';
667
+ /**
668
+ * Base delay in milliseconds between retries.
669
+ * Default: 1000.
670
+ */
671
+ delay: number;
672
+ /**
673
+ * Maximum delay cap in milliseconds (for exponential backoff).
674
+ * Default: 30000.
675
+ */
676
+ maxDelay: number;
677
+ /**
678
+ * Add random jitter to the retry delay to avoid thundering herd.
679
+ * Default: true.
680
+ */
681
+ jitter: boolean;
682
+ /**
683
+ * HTTP status codes or predicate function that triggers a retry.
684
+ * Default: [429, 500, 502, 503, 504].
685
+ */
686
+ retryOn: number[] | ((error: unknown, attempt: number) => boolean);
687
+ /**
688
+ * Methods that are eligible for retry.
689
+ * Default: ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE'].
690
+ * POST and PATCH are excluded to prevent duplicate mutations.
691
+ */
692
+ safeMethods: HttpMethod[];
693
+ /**
694
+ * Whether to respect the Retry-After response header.
695
+ * When true, uses the header value as the retry delay (up to maxDelay).
696
+ * Default: true.
697
+ */
698
+ respectRetryAfter: boolean;
699
+ /**
700
+ * Optional custom retry predicate. When provided, evaluated to decide if retry occurs.
701
+ */
702
+ retryCondition?: (error: unknown, attempt: number) => boolean;
703
+ }
704
+ /** Internal resolved retry policy with all required fields guaranteed present. */
705
+ interface ResolvedRetryPolicy {
706
+ attempts: number;
707
+ backoff: 'exponential' | 'linear';
708
+ delay: number;
709
+ maxDelay: number;
710
+ jitter: boolean;
711
+ retryOn: number[] | ((error: unknown, attempt: number) => boolean);
712
+ safeMethods: HttpMethod[];
713
+ respectRetryAfter: boolean;
714
+ retryCondition?: (error: unknown, attempt: number) => boolean;
715
+ }
716
+ /**
717
+ * Authentication configuration provided at client creation.
718
+ * The library does not manage token storage — that is the caller's responsibility.
719
+ */
720
+ interface AuthConfig {
721
+ /**
722
+ * Called before each request. Must return the current access token string,
723
+ * or `null`/`undefined` to skip adding an Authorization header.
724
+ */
725
+ getToken: () => string | null | undefined | Promise<string | null | undefined>;
726
+ /**
727
+ * Called when a 401 response is received.
728
+ * Must refresh the access token and return the new token string.
729
+ * Throw from this function to signal that refresh failed.
730
+ * Multiple concurrent 401s share a single refresh call (single-flight).
731
+ */
732
+ refreshToken?: () => string | Promise<string>;
733
+ /**
734
+ * HTTP status codes that trigger a token refresh attempt.
735
+ * Default: [401].
736
+ */
737
+ refreshOn?: number[];
738
+ /**
739
+ * Header name to attach the token to.
740
+ * Default: 'Authorization'.
741
+ */
742
+ headerName?: string;
743
+ /**
744
+ * Token prefix placed before the token value.
745
+ * Default: 'Bearer '.
746
+ */
747
+ tokenPrefix?: string;
748
+ /**
749
+ * URLs that should NOT trigger a token refresh when they return 401.
750
+ * Typically includes the refresh endpoint itself to prevent recursive refresh.
751
+ * Accepts exact strings or RegExp patterns.
752
+ */
753
+ refreshExcludeUrls?: Array<string | RegExp>;
754
+ }
755
+ /**
756
+ * In-flight request deduplication configuration.
757
+ * When enabled, concurrent identical GET requests share one network call.
758
+ */
759
+ interface DedupConfig {
760
+ /** Must be explicitly set to true to enable deduplication. Default: false. */
761
+ enabled: boolean;
762
+ /**
763
+ * Methods eligible for deduplication.
764
+ * Default: ['GET', 'HEAD'].
765
+ * POST, PUT, PATCH, DELETE are excluded to prevent deduplicating mutations.
766
+ */
767
+ methods?: HttpMethod[];
768
+ }
769
+ /**
770
+ * Pluggable persistent storage adapter for response caching (MMKV, AsyncStorage, localStorage, etc.).
771
+ */
772
+ interface CacheStorageAdapter {
773
+ getItem(key: string): Promise<string | null> | string | null;
774
+ setItem(key: string, value: string): Promise<void> | void;
775
+ removeItem(key: string): Promise<void> | void;
776
+ clear?(): Promise<void> | void;
777
+ }
778
+ /**
779
+ * Request-level cache configuration.
780
+ */
781
+ interface RequestCacheConfig {
782
+ /** Time-to-live for this specific request in milliseconds. */
783
+ ttl?: number;
784
+ /** Explicitly bypass the cache for this request. Default: false. */
785
+ skip?: boolean;
786
+ /**
787
+ * Enable RFC 7234 conditional validation (ETag / If-None-Match, Last-Modified / If-Modified-Since).
788
+ * When true, sends validation headers and reuses cached body on 304 Not Modified.
789
+ */
790
+ etag?: boolean;
791
+ /**
792
+ * Caching strategy for this request:
793
+ * - 'cache-first': Return cached data if fresh; otherwise hit network. (Default)
794
+ * - 'stale-while-revalidate': Return stale cached data immediately (0ms), then revalidate in background.
795
+ */
796
+ strategy?: 'cache-first' | 'stale-while-revalidate';
797
+ /**
798
+ * Callback fired when background revalidation finishes in 'stale-while-revalidate' mode.
799
+ */
800
+ onRevalidated?: (response: HttpResponse<unknown>) => void;
801
+ }
802
+ /** In-memory and persistent cache configuration for client-level setup. */
803
+ interface CacheConfig {
804
+ /** Must be explicitly set to true to enable caching. Default: false. */
805
+ enabled: boolean;
806
+ /**
807
+ * Default time-to-live for cached entries in milliseconds.
808
+ * Default: 60000 (1 minute).
809
+ */
810
+ defaultTtl?: number;
811
+ /** Legacy alias or shorthand for defaultTtl. */
812
+ ttl?: number;
813
+ /**
814
+ * Maximum number of cached responses in memory before LRU eviction.
815
+ * Default: 100 entries.
816
+ */
817
+ maxSize?: number;
818
+ /**
819
+ * Methods whose responses are eligible for caching.
820
+ * Default: ['GET', 'HEAD'].
821
+ * POST, PUT, PATCH, and DELETE are strictly disallowed.
822
+ */
823
+ methods?: HttpMethod[];
824
+ /**
825
+ * Whether to allow caching authenticated requests (requests with an Authorization header).
826
+ * When true, cache keys partition responses per unique authorization header.
827
+ * When false (default), requests carrying Authorization are never cached.
828
+ * Default: false.
829
+ */
830
+ allowAuthenticated?: boolean;
831
+ /**
832
+ * Enable RFC 7234 conditional validation (ETag / If-None-Match) globally.
833
+ * Default: true when cache is enabled.
834
+ */
835
+ etag?: boolean;
836
+ /**
837
+ * Optional persistent storage adapter (e.g. MMKV, AsyncStorage, localStorage, MemoryStorageAdapter).
838
+ * When configured, cached responses survive application reboots and are hydrated on initialization.
839
+ */
840
+ storage?: CacheStorageAdapter;
841
+ /**
842
+ * Storage key prefix or key name used when persisting cache entries to `storage`.
843
+ * Default: 'rnfc_http_cache'.
844
+ */
845
+ storageKey?: string;
846
+ /**
847
+ * Default caching strategy across all cacheable requests:
848
+ * - 'cache-first' (default)
849
+ * - 'stale-while-revalidate'
850
+ */
851
+ strategy?: 'cache-first' | 'stale-while-revalidate';
852
+ /**
853
+ * Alias for `strategy`.
854
+ */
855
+ defaultStrategy?: 'cache-first' | 'stale-while-revalidate';
856
+ }
857
+ /**
858
+ * Public interface of the response cache manager.
859
+ */
860
+ interface ResponseCache {
861
+ /** Get a cached response by key if present and not expired. */
862
+ get<T = unknown>(key: string): HttpResponse<T> | undefined;
863
+ /** Store a response in cache with optional TTL. */
864
+ set<T = unknown>(key: string, response: HttpResponse<T>, ttlMs?: number): void;
865
+ /** Check if a key exists in cache and is not expired. */
866
+ has(key: string): boolean;
867
+ /** Delete a specific cache entry. */
868
+ delete(key: string): boolean;
869
+ /** Delete all cache entries matching a string prefix or RegExp pattern. */
870
+ deletePattern(pattern: string | RegExp): number;
871
+ /** Clear all cache entries. */
872
+ clear(): void;
873
+ /** Number of currently cached entries in memory. */
874
+ readonly size: number;
875
+ /** Hydrate cache store from persistent storage adapter if configured. */
876
+ hydrate(): Promise<void>;
877
+ /** Persist current in-memory cache to storage adapter if configured. */
878
+ persist(): Promise<void>;
879
+ }
880
+ /**
881
+ * A fulfilled interceptor handler.
882
+ * @template V The value type this handler receives and may return.
883
+ */
884
+ type InterceptorFulfilled<V> = (value: V) => V | Promise<V>;
885
+ /**
886
+ * A rejected interceptor handler.
887
+ * Receives the error. May rethrow, transform, or recover by returning a value.
888
+ */
889
+ type InterceptorRejected = (error: unknown) => unknown;
890
+ /** A registered interceptor pair. */
891
+ interface Interceptor<V> {
892
+ fulfilled: InterceptorFulfilled<V>;
893
+ rejected?: InterceptorRejected;
894
+ }
895
+ /**
896
+ * Manages a list of interceptors of a specific type.
897
+ * @template V The value type flowing through the interceptor chain.
898
+ */
899
+ interface InterceptorManager<V> {
900
+ /**
901
+ * Register a new interceptor.
902
+ * @returns A numeric ID that can be passed to `eject()` to remove the interceptor.
903
+ */
904
+ use(fulfilled: InterceptorFulfilled<V>, rejected?: InterceptorRejected): number;
905
+ /** Remove a previously registered interceptor by its ID. */
906
+ eject(id: number): void;
907
+ /** Remove all registered interceptors. */
908
+ clear(): void;
909
+ }
910
+ /** Discriminant code on all FetchNativeError instances. */
911
+ type ErrorCode = 'HTTP_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT' | 'CANCELLED' | 'PARSE_ERROR' | 'CONFIG_ERROR' | 'RETRY_EXHAUSTED' | 'VALIDATION_ERROR' | 'CIRCUIT_OPEN';
912
+ /**
913
+ * Safe client methods that return `[data, error]` tuples instead of throwing.
914
+ */
915
+ interface SafeHttpClient {
916
+ /** Send a safe GET request. */
917
+ get<T = unknown>(url: string, config?: RequestConfig): Promise<SafeResult<T>>;
918
+ /** Send a safe POST request. */
919
+ post<T = unknown>(url: string, body?: RequestConfig['body'], config?: RequestConfig): Promise<SafeResult<T>>;
920
+ /** Send a safe PUT request. */
921
+ put<T = unknown>(url: string, body?: RequestConfig['body'], config?: RequestConfig): Promise<SafeResult<T>>;
922
+ /** Send a safe PATCH request. */
923
+ patch<T = unknown>(url: string, body?: RequestConfig['body'], config?: RequestConfig): Promise<SafeResult<T>>;
924
+ /** Send a safe DELETE request. */
925
+ delete<T = unknown>(url: string, config?: RequestConfig): Promise<SafeResult<T>>;
926
+ /** Send a safe request with arbitrary method. */
927
+ request<T = unknown>(config: RequestConfig & {
928
+ url: string;
929
+ method: HttpMethod;
930
+ }): Promise<SafeResult<T>>;
931
+ }
932
+ /**
933
+ * Options for configuring auto-pagination traversal.
934
+ */
935
+ interface PaginationConfig<T = unknown> {
936
+ /**
937
+ * Strategy:
938
+ * - 'page': Increments page number query param. (Default)
939
+ * - 'offset': Increments offset query param by limit/pageSize.
940
+ * - 'cursor': Uses cursor token extracted from response.
941
+ * - 'linkHeader': Parses RFC 5988 / 8288 `Link: <url>; rel="next"` header.
942
+ */
943
+ type?: 'page' | 'offset' | 'cursor' | 'linkHeader';
944
+ /** Query parameter name for page number. Default: 'page'. */
945
+ pageParam?: string;
946
+ /** Alias for pageParam. */
947
+ param?: string;
948
+ /** Initial page index (usually 1 or 0). Default: 1. */
949
+ initialPage?: number;
950
+ /** Query parameter name for page size / limit. Default: 'limit'. */
951
+ pageSizeParam?: string;
952
+ /** Alias for pageSizeParam. */
953
+ limitParam?: string;
954
+ /** Page size limit value. */
955
+ pageSize?: number;
956
+ /** Alias for pageSize. */
957
+ limit?: number;
958
+ /** Query parameter name for offset. Default: 'offset'. */
959
+ offsetParam?: string;
960
+ /** Initial offset index. Default: 0. */
961
+ initialOffset?: number;
962
+ /** Query parameter name for cursor token. Default: 'cursor'. */
963
+ cursorParam?: string;
964
+ /** Initial cursor token value. */
965
+ initialCursor?: string | number;
966
+ /**
967
+ * Custom function to extract the next cursor from the response.
968
+ * Return null or undefined when no further pages exist.
969
+ */
970
+ getNextCursor?: (response: HttpResponse<T>) => string | number | null | undefined;
971
+ /**
972
+ * Custom function to extract the next URL directly from the response envelope.
973
+ * Return null or undefined when no further pages exist.
974
+ */
975
+ getNextUrl?: (response: HttpResponse<T>) => string | null | undefined;
976
+ /**
977
+ * Custom function to extract the array of items from response data.
978
+ * Default: returns `data` if it is an Array, or the first Array property found in `data`.
979
+ */
980
+ getItems?: (data: T) => unknown[];
981
+ /** Maximum number of pages to fetch to prevent infinite loops. Default: 50. */
982
+ maxPages?: number;
983
+ /** Alias for maxPages. */
984
+ totalPages?: number;
985
+ }
986
+ /**
987
+ * Request options extended with pagination configuration.
988
+ */
989
+ interface PaginateOptions<T = unknown> extends Omit<RequestConfig, 'params'> {
990
+ params?: QueryParams;
991
+ pagination?: PaginationConfig<T>;
992
+ }
993
+ /**
994
+ * Auto-pagination helper attached to the client instance (`api.paginate`).
995
+ */
996
+ interface PaginateHelper {
997
+ /**
998
+ * Yields items individually across paginated endpoints.
999
+ *
1000
+ * @example
1001
+ * ```ts
1002
+ * for await (const user of api.paginate<User>('/users')) {
1003
+ * console.log(user.name);
1004
+ * }
1005
+ * ```
1006
+ */
1007
+ <TItem = unknown, TResponse = unknown>(url: string, options?: PaginateOptions<TResponse>): AsyncGenerator<TItem, void, unknown>;
1008
+ /**
1009
+ * Yields full HttpResponse page objects across paginated endpoints.
1010
+ */
1011
+ pages<TResponse = unknown>(url: string, options?: PaginateOptions<TResponse>): AsyncGenerator<HttpResponse<TResponse>, void, unknown>;
1012
+ /**
1013
+ * Collects all paginated items into a single flattened array.
1014
+ */
1015
+ all<TItem = unknown, TResponse = unknown>(url: string, options?: PaginateOptions<TResponse>): Promise<TItem[]>;
1016
+ }
1017
+ /**
1018
+ * Dynamic callback generating a mock response based on the dispatched request config.
1019
+ */
1020
+ type MockReplyCallback<T = unknown> = (config: RequestConfig & {
1021
+ url: string;
1022
+ method: HttpMethod;
1023
+ }) => [number, T?, Record<string, string>?] | Promise<[number, T?, Record<string, string>?]>;
1024
+ /**
1025
+ * Route handler definition for the mock adapter.
1026
+ */
1027
+ interface MockHandler {
1028
+ readonly method: HttpMethod | 'ANY';
1029
+ readonly url: string | RegExp;
1030
+ /** Respond with a specific HTTP status code, data, and optional headers. */
1031
+ reply(status: number, data?: unknown, headers?: Record<string, string>): void;
1032
+ /** Respond with a dynamic callback returning [status, data, headers]. */
1033
+ reply(callback: MockReplyCallback): void;
1034
+ /** Simulate a network timeout error (`TimeoutError`). */
1035
+ replyWithTimeout(): void;
1036
+ /** Simulate a network connection failure (`NetworkError`). */
1037
+ replyWithNetworkError(message?: string): void;
1038
+ }
1039
+ /**
1040
+ * Captured history record of a request intercepted by the mock adapter.
1041
+ */
1042
+ interface MockHistoryItem {
1043
+ url: string;
1044
+ method: HttpMethod;
1045
+ headers?: Record<string, string>;
1046
+ body?: unknown;
1047
+ params?: QueryParams;
1048
+ pathParams?: PathParams;
1049
+ }
1050
+ /**
1051
+ * Mock adapter instance returned by `createMockAdapter(client)`.
1052
+ */
1053
+ interface MockAdapter {
1054
+ onGet(url?: string | RegExp): MockHandler;
1055
+ onPost(url?: string | RegExp): MockHandler;
1056
+ onPut(url?: string | RegExp): MockHandler;
1057
+ onPatch(url?: string | RegExp): MockHandler;
1058
+ onDelete(url?: string | RegExp): MockHandler;
1059
+ onAny(url?: string | RegExp): MockHandler;
1060
+ /** Clear all mock route handlers and captured history. */
1061
+ reset(): void;
1062
+ /** Clear all registered mock route handlers. */
1063
+ resetHandlers(): void;
1064
+ /** Clear captured request history. */
1065
+ resetHistory(): void;
1066
+ /** Restore the original fetch implementation on the client. */
1067
+ restore(): void;
1068
+ /** Captured request history grouped by method. */
1069
+ history: Record<Lowercase<HttpMethod> | 'all', MockHistoryItem[]>;
1070
+ }
1071
+ /**
1072
+ * An HTTP client instance returned by `createClient()`.
1073
+ */
1074
+ interface HttpClient {
1075
+ /**
1076
+ * Send a GET request.
1077
+ * @template T Expected type of the parsed response body.
1078
+ */
1079
+ get<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>;
1080
+ /**
1081
+ * Send a POST request.
1082
+ * @template T Expected type of the parsed response body.
1083
+ */
1084
+ post<T = unknown>(url: string, body?: RequestConfig['body'], config?: RequestConfig): Promise<HttpResponse<T>>;
1085
+ /**
1086
+ * Send a PUT request.
1087
+ * @template T Expected type of the parsed response body.
1088
+ */
1089
+ put<T = unknown>(url: string, body?: RequestConfig['body'], config?: RequestConfig): Promise<HttpResponse<T>>;
1090
+ /**
1091
+ * Send a PATCH request.
1092
+ * @template T Expected type of the parsed response body.
1093
+ */
1094
+ patch<T = unknown>(url: string, body?: RequestConfig['body'], config?: RequestConfig): Promise<HttpResponse<T>>;
1095
+ /**
1096
+ * Send a DELETE request.
1097
+ * @template T Expected type of the parsed response body.
1098
+ */
1099
+ delete<T = unknown>(url: string, config?: RequestConfig): Promise<HttpResponse<T>>;
1100
+ /**
1101
+ * Send a request with full configuration control.
1102
+ * All other methods delegate to this.
1103
+ */
1104
+ request<T = unknown>(config: RequestConfig & {
1105
+ url: string;
1106
+ method: HttpMethod;
1107
+ }): Promise<HttpResponse<T>>;
1108
+ /** Response cache manager. */
1109
+ cache: ResponseCache;
1110
+ /** Request and response interceptor managers. */
1111
+ interceptors: {
1112
+ request: InterceptorManager<RequestConfig>;
1113
+ response: InterceptorManager<HttpResponse<unknown>>;
1114
+ };
1115
+ /**
1116
+ * Safe request interface returning Go/Rust style `[data, error]` tuples without throwing.
1117
+ */
1118
+ safe: SafeHttpClient;
1119
+ /**
1120
+ * Stream Server-Sent Events (SSE) or chunked responses using an AsyncGenerator.
1121
+ */
1122
+ stream(url: string, options?: StreamOptions): AsyncGenerator<StreamEvent, void, unknown>;
1123
+ /**
1124
+ * Upload helper that simplifies file and FormData uploads with automatic boundary handling
1125
+ * and progress tracking.
1126
+ */
1127
+ upload<T = unknown>(url: string, file: UploadFileSource, options?: UploadOptions): Promise<HttpResponse<T>>;
1128
+ /**
1129
+ * Auto-pagination traversal helper.
1130
+ */
1131
+ paginate: PaginateHelper;
1132
+ }
1133
+
1134
+ /**
1135
+ * Main HTTP Client Factory for react-native-fetch-client.
1136
+ *
1137
+
1138
+ * Provides a modern, Fetch-native HTTP client instance with:
1139
+ * - Axios-like convenience methods (`.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.request()`)
1140
+ * - Safe error handling (`.safe.get()`, `.safe.post()`, etc.) returning `[data, error]` tuples
1141
+ * - Native AI / SSE streaming (`.stream()`) using WHATWG ReadableStream and AsyncGenerator
1142
+ * - File and FormData upload helper (`.upload()`) with progress tracking
1143
+ * - Interceptors for requests and responses
1144
+ * - Pluggable in-memory caching and deduplication
1145
+ * - Concurrency throttling and Circuit Breaker resiliency
1146
+ *
1147
+ * @module client
1148
+ */
1149
+
1150
+ /**
1151
+ * Creates and configures a new `HttpClient` instance.
1152
+ *
1153
+ * @param config Optional client-level configuration defaults.
1154
+ * @returns An initialized `HttpClient` instance.
1155
+ *
1156
+ * @example
1157
+ * ```ts
1158
+ * import { createClient } from 'react-native-fetch-client';
1159
+ *
1160
+
1161
+ * const api = createClient({
1162
+ * baseURL: 'https://api.example.com/v1',
1163
+ * timeout: 10000,
1164
+ * bearerToken: 'my-jwt-token',
1165
+ * retry: { attempts: 3 },
1166
+ * });
1167
+ *
1168
+ * // Standard request
1169
+ * const res = await api.get('/users/123');
1170
+ * console.log(res.data);
1171
+ *
1172
+ * // Safe request without try/catch
1173
+ * const [user, error] = await api.safe.get('/users/123');
1174
+ * if (error) {
1175
+ * console.error('Request failed:', error.message);
1176
+ * }
1177
+ * ```
1178
+ */
1179
+ declare function createClient(config?: ClientConfig): HttpClient;
1180
+
1181
+ /**
1182
+ * Manages authentication token attachment and single-flight token refresh.
1183
+ * Scoped to each client instance to ensure state isolation.
1184
+ */
1185
+ declare class AuthManager {
1186
+ private readonly config?;
1187
+ private activeRefreshPromise;
1188
+ constructor(config?: AuthConfig | undefined);
1189
+ /** Returns the configured header name in lowercase, defaulting to 'authorization'. */
1190
+ getHeaderName(): string;
1191
+ /**
1192
+ * Attaches the access token retrieved from `getToken()` to request headers.
1193
+ * Does not overwrite an existing explicit header of the same name.
1194
+ */
1195
+ attachToken(headers: Record<string, string>, _url: string, requestAuth?: boolean | {
1196
+ skip?: boolean;
1197
+ }): Promise<void>;
1198
+ /**
1199
+ * Checks whether an error from a request URL is eligible for a token refresh attempt.
1200
+ */
1201
+ isEligibleForRefresh(error: unknown, url: string, isRefreshed?: boolean, requestAuth?: boolean | {
1202
+ skip?: boolean;
1203
+ }): boolean;
1204
+ /**
1205
+ * Initiates or shares an in-flight single-flight token refresh operation.
1206
+ * Concurrent 401s await the same Promise. Cleared on completion or error.
1207
+ */
1208
+ refresh(): Promise<string>;
1209
+ /**
1210
+ * Waits for token refresh while respecting the caller's request cancellation signal.
1211
+ */
1212
+ waitForRefresh(signal?: AbortSignal): Promise<string>;
1213
+ private isExcludedUrl;
1214
+ }
1215
+
1216
+ /**
1217
+ * Generates a stable, deterministic key representing an in-flight request identity.
1218
+ * Accounts for method, normalized URL, sorted relevant headers, credentials, and body.
1219
+ */
1220
+ declare function generateDedupKey(method: string, url: string, headers?: Record<string, string>, credentials?: RequestCredentials, body?: unknown): string;
1221
+ /**
1222
+ * Coordinates in-flight request deduplication with fine-grained cancellation semantics.
1223
+ * Each client maintains its own DedupManager instance for complete isolation.
1224
+ */
1225
+ declare class DedupManager {
1226
+ private inFlight;
1227
+ /**
1228
+ * Executes or joins an in-flight request for the given deduplication key.
1229
+ * If a matching request is already in flight, the caller subscribes to it.
1230
+ * If an individual caller cancels their AbortSignal, that caller immediately rejects,
1231
+ * but the underlying request continues for other subscribers.
1232
+ * The underlying request is only aborted if all subscribers have cancelled.
1233
+ */
1234
+ execute<T>(key: string, factory: (signal: AbortSignal) => Promise<HttpResponse<T>>, callerSignal?: AbortSignal): Promise<HttpResponse<T>>;
1235
+ /** Returns the current number of active in-flight deduplicated requests. */
1236
+ getActiveCount(): number;
1237
+ /** Clears all in-flight entries. */
1238
+ clear(): void;
1239
+ }
1240
+
1241
+ /** Internal cache entry storing the cloned response and expiration timestamp. */
1242
+ interface CacheEntry<T = unknown> {
1243
+ response: HttpResponse<T>;
1244
+ expiresAt: number;
1245
+ etag?: string;
1246
+ lastModified?: string;
1247
+ }
1248
+ /**
1249
+ * Deep clones data to prevent consumer mutation of cached objects or
1250
+ * mutation of returned responses corrupting cache storage.
1251
+ */
1252
+ declare function cloneData<T>(data: T): T;
1253
+ /**
1254
+ * Generates a cache key for a request.
1255
+ * Normalizes URL and includes authorization header hash/slice if authenticated caching is allowed.
1256
+ */
1257
+ declare function generateCacheKey(method: string, url: string, headers?: Record<string, string>, allowAuthenticated?: boolean): string;
1258
+ /**
1259
+ * Checks if a request method and headers are eligible for caching.
1260
+ * Unsafe methods (POST, PUT, PATCH, DELETE) are never eligible.
1261
+ */
1262
+ declare function isCacheEligible(method: string, headers: Record<string, string> | undefined, requestCache: RequestCacheConfig | boolean | undefined, clientCache: CacheConfig | undefined): boolean;
1263
+ /**
1264
+ * LRU cache manager supporting in-memory and persistent storage adapters (MMKV / AsyncStorage)
1265
+ * with RFC 7234 conditional validation and Stale-While-Revalidate (SWR) support.
1266
+ */
1267
+ declare class CacheManager implements ResponseCache {
1268
+ private readonly store;
1269
+ private readonly defaultTtl;
1270
+ private readonly maxSize;
1271
+ readonly storage?: CacheStorageAdapter;
1272
+ readonly storageKey: string;
1273
+ readonly defaultStrategy: 'cache-first' | 'stale-while-revalidate';
1274
+ constructor(config?: CacheConfig);
1275
+ /**
1276
+ * Hydrates cached entries from persistent storage into memory.
1277
+ */
1278
+ hydrate(): Promise<void>;
1279
+ /**
1280
+ * Persists active (non-expired) cache entries to storage adapter.
1281
+ */
1282
+ persist(): Promise<void>;
1283
+ /**
1284
+ * Retrieves a cached response by key.
1285
+ * If expired and allowStale is false, evicts the entry and returns undefined.
1286
+ */
1287
+ get<T = unknown>(key: string, options?: {
1288
+ allowStale?: boolean;
1289
+ }): HttpResponse<T> | undefined;
1290
+ /**
1291
+ * Checks if an entry exists and is expired (stale).
1292
+ */
1293
+ isStale(key: string): boolean;
1294
+ /**
1295
+ * Retrieves the raw cache entry (including expired entries) for conditional revalidation.
1296
+ */
1297
+ getEntry<T = unknown>(key: string): CacheEntry<T> | undefined;
1298
+ /**
1299
+ * Revalidates an existing cache entry on receiving an HTTP 304 Not Modified response.
1300
+ * Updates headers, extends TTL, and returns the response with fresh cached data.
1301
+ */
1302
+ updateFrom304<T = unknown>(key: string, newHeaders: Record<string, string>, ttlMs?: number): HttpResponse<T> | undefined;
1303
+ set<T = unknown>(key: string, response: HttpResponse<T>, ttlMs?: number): void;
1304
+ has(key: string): boolean;
1305
+ delete(key: string): boolean;
1306
+ deletePattern(pattern: string | RegExp): number;
1307
+ clear(): void;
1308
+ get size(): number;
1309
+ }
1310
+
1311
+ /**
1312
+ * Manages an ordered chain of request or response interceptors.
1313
+ */
1314
+ declare class DefaultInterceptorManager<V> implements InterceptorManager<V> {
1315
+ private handlers;
1316
+ private nextId;
1317
+ /**
1318
+ * Register a new interceptor.
1319
+ * @returns Numeric registration ID used to eject.
1320
+ */
1321
+ use(fulfilled: InterceptorFulfilled<V>, rejected?: InterceptorRejected): number;
1322
+ /**
1323
+ * Remove a registered interceptor by ID.
1324
+ */
1325
+ eject(id: number): void;
1326
+ /**
1327
+ * Remove all registered interceptors.
1328
+ */
1329
+ clear(): void;
1330
+ /**
1331
+ * Returns active interceptors in registration order.
1332
+ */
1333
+ getInterceptors(): Array<Interceptor<V>>;
1334
+ }
1335
+
1336
+ /**
1337
+ * Circuit Breaker State Machine for Fetch-Native-Client.
1338
+ *
1339
+ * Implements the classic Release It! Circuit Breaker pattern with three states:
1340
+ *
1341
+ * [Consecutive failures >= threshold]
1342
+ * CLOSED -------------------------------------> OPEN
1343
+ * ^ |
1344
+ * | [Canary succeeded] | [resetTimeoutMs elapsed]
1345
+ * | v
1346
+ * +---------------------------------------- HALF_OPEN
1347
+ * [Canary failed: re-open]
1348
+ *
1349
+ * Purpose:
1350
+ * When an upstream microservice crashes or experiences an outage, attempting
1351
+ * continuous HTTP requests wastes sockets, drains mobile battery, and thrashes
1352
+ * downstream services. The circuit breaker fails fast locally with `CircuitBreakerError`,
1353
+ * protecting both client resources and the failing server.
1354
+ *
1355
+ * @module circuit-breaker
1356
+ */
1357
+
1358
+ /**
1359
+ * Manages the state machine for a circuit breaker instance.
1360
+ */
1361
+ declare class CircuitBreaker {
1362
+ readonly config: CircuitBreakerConfig;
1363
+ private state;
1364
+ private failureCount;
1365
+ private nextAttemptTime;
1366
+ private readonly failureThreshold;
1367
+ private readonly resetTimeoutMs;
1368
+ private readonly isFailureFn;
1369
+ constructor(config: CircuitBreakerConfig);
1370
+ /**
1371
+ * Current lifecycle state of the circuit breaker.
1372
+ */
1373
+ getState(): CircuitState;
1374
+ /**
1375
+ * Current consecutive failure count.
1376
+ */
1377
+ getFailures(): number;
1378
+ /**
1379
+ * Resets the circuit breaker state to CLOSED and clears failure counts.
1380
+ */
1381
+ reset(): void;
1382
+ /**
1383
+ * Checks if a request is allowed to execute.
1384
+ * Throws `CircuitBreakerError` if the circuit is currently OPEN.
1385
+ */
1386
+ beforeExecution(): void;
1387
+ /**
1388
+ * Records a successful execution. If the circuit was in HALF_OPEN state,
1389
+ * it transitions back to CLOSED.
1390
+ */
1391
+ recordSuccess(): void;
1392
+ /**
1393
+ * Records an execution error. Evaluates the failure predicate and
1394
+ * updates the circuit state accordingly.
1395
+ */
1396
+ recordFailure(error: unknown): void;
1397
+ /**
1398
+ * Executes a task wrapped in the circuit breaker lifecycle.
1399
+ *
1400
+ * @template T Return type of the task.
1401
+ * @param task Function returning a Promise to execute.
1402
+ * @returns Result of the task.
1403
+ * @throws {CircuitBreakerError} If the circuit is OPEN.
1404
+ */
1405
+ execute<T>(task: () => Promise<T>): Promise<T>;
1406
+ }
1407
+
1408
+ /**
1409
+ * Concurrency Limiter for Fetch-Native-Client.
1410
+ *
1411
+ * Implements an in-memory FIFO queue to cap the maximum number of simultaneous
1412
+ * in-flight HTTP requests dispatched by the client.
1413
+ *
1414
+ * Prevents socket exhaustion in Node.js, respects mobile browser connection limits
1415
+ * (typically 6 per domain on HTTP/1.1), and prevents overwhelming backend microservices.
1416
+ *
1417
+ * @module concurrency
1418
+ */
1419
+ /**
1420
+ * Manages request concurrency with strict FIFO ordering.
1421
+ */
1422
+ declare class ConcurrencyLimiter {
1423
+ readonly maxConcurrent: number;
1424
+ private activeCount;
1425
+ private readonly queue;
1426
+ /**
1427
+ * @param maxConcurrent Maximum number of concurrent tasks allowed to run simultaneously.
1428
+ * If less than or equal to 0, concurrency is unlimited.
1429
+ */
1430
+ constructor(maxConcurrent: number);
1431
+ /**
1432
+ * Acquires a concurrency slot. Resolves immediately if active count < limit,
1433
+ * otherwise blocks until a preceding task releases a slot.
1434
+ */
1435
+ private acquire;
1436
+ /**
1437
+ * Releases an active concurrency slot and unblocks the next queued task in FIFO order.
1438
+ */
1439
+ private release;
1440
+ /**
1441
+ * Schedules an asynchronous function through the concurrency limiter.
1442
+ *
1443
+ * @template T The return type of the function.
1444
+ * @param task Function returning a Promise to execute once a slot is available.
1445
+ * @param signal Optional AbortSignal to cancel waiting in the queue.
1446
+ * @returns The resolved value of the task.
1447
+ */
1448
+ run<T>(task: () => Promise<T>, signal?: AbortSignal): Promise<T>;
1449
+ /**
1450
+ * Current number of actively executing requests.
1451
+ */
1452
+ get active(): number;
1453
+ /**
1454
+ * Current number of requests queued waiting for an available slot.
1455
+ */
1456
+ get pending(): number;
1457
+ /**
1458
+ * Clears all queued pending requests and rejects them with the provided error.
1459
+ */
1460
+ clear(error?: Error): void;
1461
+ }
1462
+
1463
+ /**
1464
+ * Zero-dependency Duck-Typing Schema Validation.
1465
+ *
1466
+ * Supports:
1467
+ * 1. Standard Schema v1 (`~standard`) — supported by Zod 3.24+, Valibot, ArkType
1468
+ * 2. Zod-like schemas (`.parse()` or `.safeParse()`)
1469
+ * 3. Plain synchronous or asynchronous functions `(data: unknown) => T`
1470
+ *
1471
+ * Keeps `react-native-fetch-client` runtime dependencies strictly at zero (`dependencies: {}`)
1472
+ * while providing compile-time type inference and runtime validation.
1473
+ *
1474
+ * @module validator
1475
+ */
1476
+
1477
+ /**
1478
+ * Validates parsed HTTP response data against an arbitrary schema validator.
1479
+ *
1480
+ * @template T The validated data type.
1481
+ * @param schema The schema validator to execute (Standard Schema, Zod-like, or function).
1482
+ * @param data The raw parsed response data to validate.
1483
+ * @param config Optional resolved request configuration for error context.
1484
+ * @returns A promise resolving to the validated (and potentially transformed) data of type `T`.
1485
+ * @throws {ValidationError} When the data does not conform to the schema.
1486
+ *
1487
+ * @example
1488
+ * ```ts
1489
+ * import { z } from 'zod';
1490
+ * const UserSchema = z.object({ id: z.number(), name: z.string() });
1491
+ * const user = await validateData(UserSchema, { id: 1, name: 'Alice' });
1492
+ * ```
1493
+ */
1494
+ declare function validateData<T>(schema: SchemaValidator<T> | undefined, data: unknown, config?: ResolvedRequestConfig): Promise<T>;
1495
+
1496
+ /**
1497
+ * Native AI & Server-Sent Events (SSE) Streaming.
1498
+ *
1499
+ * Implements the WHATWG EventSource line-parsing specification using
1500
+ * standard `ReadableStream<Uint8Array>` and `TextDecoder`.
1501
+ *
1502
+ * Works in:
1503
+ * - Browsers
1504
+ * - Node.js 18+
1505
+ * - Cloudflare Workers
1506
+ * - Deno / Bun
1507
+ *
1508
+ * @module stream
1509
+ */
1510
+
1511
+ /**
1512
+ * Parses a WHATWG ReadableStream into an AsyncGenerator of StreamEvent objects.
1513
+ * Follows the W3C / WHATWG Server-Sent Events line format:
1514
+ * - `event: <name>`
1515
+ * - `data: <content>` (multiline supported via consecutive data lines)
1516
+ * - `id: <id>`
1517
+ * - `retry: <ms>`
1518
+ * - Blank line (`\n\n` or `\r\n\r\n`) dispatches the event.
1519
+ *
1520
+ * @param stream The response body ReadableStream to consume.
1521
+ * @param options Streaming configuration options (e.g. rawLines).
1522
+ * @returns An AsyncGenerator yielding parsed StreamEvent items.
1523
+ *
1524
+ * @example
1525
+ * ```ts
1526
+ * for await (const chunk of parseSseStream(response.body)) {
1527
+ * console.log('SSE Event:', chunk.event, chunk.data);
1528
+ * }
1529
+ * ```
1530
+ */
1531
+ declare function parseSseStream(stream: ReadableStream<Uint8Array>, options?: StreamOptions): AsyncGenerator<StreamEvent, void, unknown>;
1532
+
1533
+ /**
1534
+ * URL resolution and path parameter interpolation.
1535
+ *
1536
+ * Supports:
1537
+ * - RFC 6570 / Express style path parameters (`:id`, `{id}`)
1538
+ * - Relative vs absolute URL resolution against `baseURL`
1539
+ * - Query parameter merging with preservation of existing query strings
1540
+ *
1541
+ * @module url
1542
+ */
1543
+
1544
+ /**
1545
+ * Interpolates path parameters into a URL template string.
1546
+ * Supports both Express-style (`:param`) and OpenAPI-style (`{param}`) placeholders.
1547
+ * Parameter values are automatically URI-encoded.
1548
+ *
1549
+ * @param url The URL template string (e.g. `/users/:id/orders/{orderId}`).
1550
+ * @param pathParams Object containing the parameter key/value mappings.
1551
+ * @returns The interpolated URL string.
1552
+ *
1553
+ * @example
1554
+ * ```ts
1555
+ * interpolatePath('/users/:id/posts/{slug}', { id: 42, slug: 'my first post' });
1556
+ * // returns: '/users/42/posts/my%20first%20post'
1557
+ * ```
1558
+ */
1559
+ declare function interpolatePath(url: string, pathParams?: PathParams): string;
1560
+ /**
1561
+ * Builds a resolved URL from a base URL, a relative/absolute URL, query parameters,
1562
+ * and path parameters.
1563
+ *
1564
+ * @param url The relative or absolute target URL.
1565
+ * @param baseURL Optional base URL prepended if url is relative.
1566
+ * @param params Optional query parameters to serialize onto the query string.
1567
+ * @param pathParams Optional path parameters to interpolate into template placeholders.
1568
+ * @returns Fully qualified resolved URL string.
1569
+ */
1570
+ declare function buildUrl(url?: string, baseURL?: string, params?: QueryParams, pathParams?: PathParams): string;
1571
+
1572
+ /**
1573
+ * Auto-Pagination Traverser for react-native-fetch-client.
1574
+ *
1575
+ * Provides async generator iteration over paginated REST endpoints supporting:
1576
+ * - Page number pagination (`page=1, 2, 3...`)
1577
+ * - Offset / Limit pagination (`offset=0, 20, 40...`)
1578
+ * - Cursor-based pagination (`cursor=abc...`)
1579
+ * - RFC 5988 / RFC 8288 standard `Link` header pagination (`rel="next"`)
1580
+ *
1581
+ * @module paginate
1582
+ */
1583
+
1584
+ /**
1585
+ * Parses an RFC 5988 / RFC 8288 `Link` header and extracts the URL for `rel="next"`.
1586
+ * Example header: `<https://api.example.com/items?page=2>; rel="next", <...>; rel="last"`
1587
+ */
1588
+ declare function parseNextLinkHeader(header?: string): string | null;
1589
+ /**
1590
+ * Creates the PaginateHelper bound to an HttpClient instance.
1591
+ */
1592
+ declare function createPaginateHelper(client: HttpClient): PaginateHelper;
1593
+
1594
+ /**
1595
+ * Declarative Mock Adapter for react-native-fetch-client.
1596
+ *
1597
+ * Enables straightforward unit testing and route stubbing without external libraries
1598
+ * like MSW or nock. Intercepts outgoing requests, returns simulated responses or
1599
+ * simulated network errors, and records complete request history.
1600
+ *
1601
+ * @module mock
1602
+ */
1603
+
1604
+ /**
1605
+ * Creates an active mock adapter attached to an HttpClient instance.
1606
+ *
1607
+ * @param client The HttpClient instance to mock.
1608
+ * @param options Options including delay simulation or passthrough for unmatched routes.
1609
+ *
1610
+ * @example
1611
+ * ```ts
1612
+ * const mock = createMockAdapter(api);
1613
+ * mock.onGet('/users/1').reply(200, { id: 1, name: 'Alice' });
1614
+ * mock.onPost('/login').reply(401, { error: 'Invalid credentials' });
1615
+ *
1616
+ * const res = await api.get('/users/1');
1617
+ * expect(res.data.name).toBe('Alice');
1618
+ * expect(mock.history.get.length).toBe(1);
1619
+ * ```
1620
+ */
1621
+ declare function createMockAdapter(client: HttpClient, options?: {
1622
+ delayMs?: number;
1623
+ passthrough?: boolean;
1624
+ }): MockAdapter;
1625
+
1626
+ /**
1627
+ * Storage adapter interface for persistent offline queue.
1628
+ * Compatible with localStorage, IndexedDB, AsyncStorage, etc.
1629
+ */
1630
+ interface OfflineStorageAdapter {
1631
+ getItem(key: string): Promise<string | null> | string | null;
1632
+ setItem(key: string, value: string): Promise<void> | void;
1633
+ removeItem(key: string): Promise<void> | void;
1634
+ }
1635
+ /**
1636
+ * A serialized representation of a request queued for offline replay.
1637
+ */
1638
+ interface QueuedRequest {
1639
+ id: string;
1640
+ url: string;
1641
+ method: HttpMethod;
1642
+ headers?: Record<string, string>;
1643
+ params?: QueryParams;
1644
+ body?: string;
1645
+ enqueuedAt: number;
1646
+ attemptCount: number;
1647
+ maxAttempts: number;
1648
+ idempotencyKey: string;
1649
+ }
1650
+ /**
1651
+ * Summary of a queue process execution.
1652
+ */
1653
+ interface QueueProcessSummary {
1654
+ processed: number;
1655
+ succeeded: number;
1656
+ failedPermanent: number;
1657
+ failedTransient: number;
1658
+ remaining: number;
1659
+ }
1660
+ /**
1661
+ * Configuration for the offline request queue.
1662
+ */
1663
+ interface OfflineQueueConfig {
1664
+ /** The react-native-fetch-client instance used to execute replayed requests. */
1665
+ client: {
1666
+ request<T = unknown>(config: RequestConfig & {
1667
+ url: string;
1668
+ method: HttpMethod;
1669
+ }): Promise<HttpResponse<T>>;
1670
+ };
1671
+ /** Storage adapter for persisting the queue. Defaults to in-memory adapter. */
1672
+ storage?: OfflineStorageAdapter;
1673
+ /** Storage key used by the adapter. Default: 'fnc_offline_queue'. */
1674
+ storageKey?: string;
1675
+ /** Maximum number of requests stored in the queue. Default: 100. */
1676
+ maxQueueSize?: number;
1677
+ /** Maximum retry attempts for transient errors before eviction. Default: 5. */
1678
+ maxAttempts?: number;
1679
+ /** Maximum age of a queued request in milliseconds before eviction. Default: 86400000 (24 hours). */
1680
+ maxAgeMs?: number;
1681
+ /** Header name used for idempotency keys. Default: 'Idempotency-Key'. */
1682
+ idempotencyHeader?: string;
1683
+ /**
1684
+ * URLs that are strictly forbidden from offline queuing (e.g. payment/checkout).
1685
+ * Default includes common financial / mutation endpoints.
1686
+ */
1687
+ disallowedUrls?: Array<string | RegExp>;
1688
+ /** Custom predicate to evaluate whether a request is eligible to be enqueued. */
1689
+ shouldQueue?: (config: RequestConfig, error?: unknown) => boolean;
1690
+ /** Called when a queued request succeeds during replay. */
1691
+ onReplaySuccess?: (request: QueuedRequest, response: HttpResponse) => void;
1692
+ /** Called when a queued request fails permanently (e.g. 4xx) or exceeds maxAttempts. */
1693
+ onPermanentFailure?: (request: QueuedRequest, error: unknown) => void;
1694
+ /**
1695
+ * Automatically listen for browser `online` events and trigger `process()`
1696
+ * when network connectivity is restored.
1697
+ * Gracefully ignored in non-browser (Node.js/worker) environments.
1698
+ * Default: false.
1699
+ */
1700
+ autoSyncOnOnline?: boolean;
1701
+ }
1702
+ /**
1703
+ * Public interface of the offline request queue.
1704
+ */
1705
+ interface OfflineQueue {
1706
+ /** Enqueue a request for offline replay. */
1707
+ enqueue(config: RequestConfig): Promise<{
1708
+ enqueued: boolean;
1709
+ id: string;
1710
+ }>;
1711
+ /** Process the queue sequentially. Resolves with a summary of the run. */
1712
+ process(): Promise<QueueProcessSummary>;
1713
+ /** Check if the queue is currently processing. */
1714
+ isProcessing(): boolean;
1715
+ /** Get all currently queued requests. */
1716
+ getItems(): Promise<QueuedRequest[]>;
1717
+ /** Current count of queued requests. */
1718
+ size(): Promise<number>;
1719
+ /** Clear all queued requests from memory and persistent storage. */
1720
+ clear(): Promise<void>;
1721
+ /** Hydrate and clean stale entries from storage. Automatically called on first operation. */
1722
+ initialize(): Promise<void>;
1723
+ /** Unregisters any attached event listeners (e.g. `online` listener) and releases resources. */
1724
+ destroy(): void;
1725
+ }
1726
+
1727
+ /**
1728
+ * Universal in-memory storage adapter for environments without persistent storage
1729
+ * or for isolated unit testing.
1730
+ */
1731
+ declare class MemoryStorageAdapter implements OfflineStorageAdapter {
1732
+ private readonly store;
1733
+ getItem(key: string): string | null;
1734
+ setItem(key: string, value: string): void;
1735
+ removeItem(key: string): void;
1736
+ }
1737
+
1738
+ /**
1739
+ * Creates an offline request queue with persistent storage, sequential replay,
1740
+ * duplicate prevention via idempotency keys, and payment mutation guards.
1741
+ */
1742
+ declare function createOfflineQueue(config: OfflineQueueConfig): OfflineQueue;
1743
+
1744
+ export { type AuthConfig, AuthManager, type CacheConfig, CacheManager, type CacheStorageAdapter, CancelError, CircuitBreaker, type CircuitBreakerConfig, CircuitBreakerError, type CircuitState, type ClientConfig, type ConcurrencyConfig, ConcurrencyLimiter, ConfigError, type DebugLogEntry, type DebugLogger, type DedupConfig, DedupManager, DefaultInterceptorManager, type ErrorCode, type FetchImpl, FetchNativeError, type FunctionalValidator, type HttpClient, HttpError, type HttpMethod, type HttpResponse, type Interceptor, type InterceptorFulfilled, type InterceptorManager, type InterceptorRejected, MemoryStorageAdapter, type MockAdapter, type MockHandler, type MockHistoryItem, type MockReplyCallback, NetworkError, type OfflineQueue, type OfflineQueueConfig, type OfflineStorageAdapter, type PaginateHelper, type PaginateOptions, type PaginationConfig, ParseError, type PathParams, type ProgressEvent, type QueryParams, type QueueProcessSummary, type QueuedRequest, type RequestCacheConfig, type RequestConfig, type ResolvedRequestConfig, type ResolvedRetryPolicy, type ResponseCache, type ResponseType, RetryExhaustedError, type RetryPolicy, type SafeHttpClient, type SafeResult, type SchemaValidator, type StandardSchemaV1, type StreamEvent, type StreamOptions, TimeoutError, type UploadFileSource, type UploadOptions, ValidationError, type ZodLikeSchema, buildUrl, cloneData, createClient, createMockAdapter, createOfflineQueue, createPaginateHelper, generateCacheKey, generateDedupKey, interpolatePath, isCacheEligible, isCancelError, isCircuitBreakerError, isFetchNativeError, isHttpError, isNetworkError, isRetryExhaustedError, isTimeoutError, isValidationError, parseNextLinkHeader, parseSseStream, validateData };