@zayne-labs/callapi 1.15.0 → 1.16.1

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.
@@ -1,12 +1,22 @@
1
1
  //#region src/constants/common.d.ts
2
2
  declare const fetchSpecificKeys: readonly (keyof RequestInit | "duplex" | "extraFetchOptions")[];
3
3
  //#endregion
4
+ //#region src/types/default-types.d.ts
5
+ type DefaultDataType = unknown;
6
+ type DefaultPluginArray = CallApiPlugin[];
7
+ type DefaultThrowOnError = boolean;
8
+ type DefaultMetaObject = Record<string, unknown>;
9
+ type DefaultInferredExtraOptions = unknown;
10
+ type DefaultCallApiContext = Omit<CallApiContext, "Meta">;
11
+ //#endregion
4
12
  //#region src/types/type-helpers.d.ts
5
- type AnyString = string & NonNullable<unknown>;
6
- type AnyNumber = number & NonNullable<unknown>;
13
+ type NonNullableUnknown = NonNullable<unknown>;
14
+ type AnyString = string & NonNullableUnknown;
15
+ type AnyNumber = number & NonNullableUnknown;
7
16
  type AnyFunction<TResult = unknown> = (...args: any[]) => TResult;
8
17
  type Prettify<TObject> = NonNullable<unknown> & { [Key in keyof TObject]: TObject[Key]; };
9
18
  type WriteableLevel = "deep" | "shallow";
19
+ type IsEmptyObject<TObject> = keyof TObject extends never ? true : false;
10
20
  /**
11
21
  * Makes all properties in an object type writeable (removes readonly modifiers).
12
22
  * Supports both shallow and deep modes, and handles special cases like arrays, tuples, and unions.
@@ -37,208 +47,343 @@ type CommonRequestHeaders = "Access-Control-Allow-Credentials" | "Access-Control
37
47
  type CommonAuthorizationHeaders = `${"Basic" | "Bearer" | "Token"} ${string}`;
38
48
  type CommonContentTypes = "application/epub+zip" | "application/gzip" | "application/json" | "application/ld+json" | "application/octet-stream" | "application/ogg" | "application/pdf" | "application/rtf" | "application/vnd.ms-fontobject" | "application/wasm" | "application/xhtml+xml" | "application/xml" | "application/zip" | "audio/aac" | "audio/mpeg" | "audio/ogg" | "audio/opus" | "audio/webm" | "audio/x-midi" | "font/otf" | "font/ttf" | "font/woff" | "font/woff2" | "image/avif" | "image/bmp" | "image/gif" | "image/jpeg" | "image/png" | "image/svg+xml" | "image/tiff" | "image/webp" | "image/x-icon" | "model/gltf-binary" | "model/gltf+json" | "text/calendar" | "text/css" | "text/csv" | "text/html" | "text/javascript" | "text/plain" | "video/3gpp" | "video/3gpp2" | "video/av1" | "video/mp2t" | "video/mp4" | "video/mpeg" | "video/ogg" | "video/webm" | "video/x-msvideo" | AnyString;
39
49
  //#endregion
40
- //#region src/types/standard-schema.d.ts
41
- /** The Standard Typed interface. This is a base type extended by other specs. */
42
- interface StandardTypedV1<Input = unknown, Output = Input> {
43
- /** The Standard properties. */
44
- readonly "~standard": StandardTypedV1.Props<Input, Output>;
45
- }
46
- declare namespace StandardTypedV1 {
47
- /** The Standard Typed properties interface. */
48
- interface Props<Input = unknown, Output = Input> {
49
- /** Inferred types associated with the schema. */
50
- readonly types?: Types<Input, Output> | undefined;
51
- /** The vendor name of the schema library. */
52
- readonly vendor: string;
53
- /** The version number of the standard. */
54
- readonly version: 1;
55
- }
56
- /** The Standard Typed types interface. */
57
- interface Types<Input = unknown, Output = Input> {
58
- /** The input type of the schema. */
59
- readonly input: Input;
60
- /** The output type of the schema. */
61
- readonly output: Output;
62
- }
63
- /** Infers the input type of a Standard Typed. */
64
- type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
65
- /** Infers the output type of a Standard Typed. */
66
- type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
67
- }
68
- /** The Standard Schema interface. */
69
- interface StandardSchemaV1<Input = unknown, Output = Input> {
70
- /** The Standard Schema properties. */
71
- readonly "~standard": StandardSchemaV1.Props<Input, Output>;
72
- }
73
- declare namespace StandardSchemaV1 {
74
- /** The Standard Schema properties interface. */
75
- interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
76
- /** Validates unknown input values. */
77
- readonly validate: (value: unknown, options?: StandardSchemaV1.Options) => Promise<Result<Output>> | Result<Output>;
78
- }
79
- /** The result interface of the validate function. */
80
- type Result<Output> = FailureResult | SuccessResult<Output>;
81
- /** The result interface if validation succeeds. */
82
- interface SuccessResult<Output> {
83
- /** A falsy value for `issues` indicates success. */
84
- readonly issues?: undefined;
85
- /** The typed output value. */
86
- readonly value: Output;
87
- }
88
- interface Options {
89
- /** Explicit support for additional vendor-specific parameters, if needed. */
90
- readonly libraryOptions?: Record<string, unknown> | undefined;
91
- }
92
- /** The result interface if validation fails. */
93
- interface FailureResult {
94
- /** The issues of failed validation. */
95
- readonly issues: readonly Issue[];
96
- }
97
- /** The issue interface of the failure output. */
98
- interface Issue {
99
- /** The error message of the issue. */
100
- readonly message: string;
101
- /** The path of the issue, if any. */
102
- readonly path?: ReadonlyArray<PathSegment | PropertyKey> | undefined;
103
- }
104
- /** The path segment interface of the issue. */
105
- interface PathSegment {
106
- /** The key representing a path segment. */
107
- readonly key: PropertyKey;
108
- }
109
- /** The Standard types interface. */
110
- interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
111
- /** Infers the input type of a Standard. */
112
- type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
113
- /** Infers the output type of a Standard. */
114
- type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
115
- }
116
- //#endregion
117
- //#region src/constants/validation.d.ts
118
- declare const fallBackRouteSchemaKey = "@default";
119
- type FallBackRouteSchemaKey = typeof fallBackRouteSchemaKey;
120
- //#endregion
121
- //#region src/url.d.ts
122
- declare const atSymbol = "@";
123
- type AtSymbol = typeof atSymbol;
124
- type AllowedQueryParamValues = UnmaskType<boolean | number | string>;
125
- type RecordStyleParams = UnmaskType<Record<string, AllowedQueryParamValues>>;
126
- type TupleStyleParams = UnmaskType<AllowedQueryParamValues[]>;
127
- type Params = UnmaskType<RecordStyleParams | TupleStyleParams>;
128
- type StructuredQueryValues = Record<string, unknown> | unknown[] | null | undefined;
129
- type Query = UnmaskType<Record<string, AllowedQueryParamValues | StructuredQueryValues> | URLSearchParams>;
130
- type InitURLOrURLObject = AnyString | RouteKeyMethodsURLUnion | URL;
131
- interface URLOptions {
50
+ //#region src/dedupe.d.ts
51
+ type DedupeStrategyUnion = UnmaskType<"cancel" | "defer" | "none">;
52
+ type DedupeOptions = {
132
53
  /**
133
- * Base URL for all API requests. Will only be prepended to relative URLs.
54
+ * Controls the scope of request deduplication caching.
134
55
  *
135
- * Absolute URLs (starting with http/https) will not be prepended by the baseURL.
56
+ * - `"global"`: Shares deduplication cache across all `createFetchClient` instances with the same `dedupeCacheScopeKey`.
57
+ * Useful for applications with multiple API clients that should share deduplication state.
58
+ * - `"local"`: Limits deduplication to requests within the same `createFetchClient` instance.
59
+ * Provides better isolation and is recommended for most use cases.
60
+ *
61
+ *
62
+ * **Real-world Scenarios:**
63
+ * - Use `"global"` when you have multiple API clients (user service, auth service, etc.) that might make overlapping requests
64
+ * - Use `"local"` (default) for single-purpose clients or when you want strict isolation between different parts of your app
136
65
  *
137
66
  * @example
138
67
  * ```ts
139
- * // Set base URL for all requests
140
- * baseURL: "https://api.example.com/v1"
141
- *
142
- * // Then use relative URLs in requests
143
- * callApi("/users") // → https://api.example.com/v1/users
144
- * callApi("/posts/123") // → https://api.example.com/v1/posts/123
68
+ * // Local scope - each client has its own deduplication cache
69
+ * const userClient = createFetchClient({ baseURL: "/api/users" });
70
+ * const postClient = createFetchClient({ baseURL: "/api/posts" });
71
+ * // These clients won't share deduplication state
145
72
  *
146
- * // Environment-specific base URLs
147
- * baseURL: process.env.NODE_ENV === "production"
148
- * ? "https://api.example.com"
149
- * : "http://localhost:3000/api"
73
+ * // Global scope - share cache across related clients
74
+ * const userClient = createFetchClient({
75
+ * baseURL: "/api/users",
76
+ * dedupeCacheScope: "global",
77
+ * });
78
+ * const postClient = createFetchClient({
79
+ * baseURL: "/api/posts",
80
+ * dedupeCacheScope: "global",
81
+ * });
82
+ * // These clients will share deduplication state
150
83
  * ```
151
- */
152
- baseURL?: string;
153
- /**
154
- * Resolved request URL after processing baseURL, parameters, and query strings (readonly)
155
- *
156
- * This is the final URL that will be used for the HTTP request, computed from
157
- * baseURL, initURL, params, and query parameters.
158
- *
159
- */
160
- readonly fullURL?: string;
161
- /**
162
- * The original URL string passed to the callApi instance (readonly)
163
- *
164
- * This preserves the original URL as provided, including any method modifiers like "@get/" or "@post/".
165
84
  *
85
+ * @default "local"
166
86
  */
167
- readonly initURL?: string;
87
+ dedupeCacheScope?: "global" | "local";
168
88
  /**
169
- * The URL string after normalization, with method modifiers removed(readonly)
89
+ * Unique namespace for the global deduplication cache when using `dedupeCacheScope: "global"`.
170
90
  *
171
- * Method modifiers like "@get/", "@post/" are stripped to create a clean URL
172
- * for parameter substitution and final URL construction.
91
+ * This creates logical groupings of deduplication caches. All instances with the same key
92
+ * will share the same cache namespace, allowing fine-grained control over which clients
93
+ * share deduplication state.
173
94
  *
174
- */
175
- readonly initURLNormalized?: string;
176
- /**
177
- * Parameters to be substituted into URL path segments.
95
+ * **Best Practices:**
96
+ * - Use descriptive names that reflect the logical grouping (e.g., "user-service", "analytics-api")
97
+ * - Keep scope keys consistent across related API clients
98
+ * - Consider using different scope keys for different environments (dev, staging, prod)
99
+ * - Avoid overly broad scope keys that might cause unintended cache sharing
178
100
  *
179
- * Supports both object-style (named parameters) and array-style (positional parameters)
180
- * for flexible URL parameter substitution.
101
+ * **Cache Management:**
102
+ * - Each scope key maintains its own independent cache
103
+ * - Caches are automatically cleaned up when no references remain
104
+ * - Consider the memory implications of multiple global scopes
181
105
  *
182
106
  * @example
183
- * ```typescript
184
- * // Object-style parameters (recommended)
185
- * const namedParams: URLOptions = {
186
- * initURL: "/users/:userId/posts/:postId",
187
- * params: { userId: "123", postId: "456" }
188
- * };
189
- * // Results in: /users/123/posts/456
107
+ * ```ts
108
+ * // Group related API clients together
109
+ * const userClient = createFetchClient({
110
+ * baseURL: "/api/users",
111
+ * dedupeCacheScope: "global",
112
+ * dedupeCacheScopeKey: "user-service"
113
+ * });
114
+ * const profileClient = createFetchClient({
115
+ * baseURL: "/api/profiles",
116
+ * dedupeCacheScope: "global",
117
+ * dedupeCacheScopeKey: "user-service" // Same scope - will share cache
118
+ * });
190
119
  *
191
- * // Array-style parameters (positional)
192
- * const positionalParams: URLOptions = {
193
- * initURL: "/users/:userId/posts/:postId",
194
- * params: ["123", "456"] // Maps in order: userId=123, postId=456
195
- * };
196
- * // Results in: /users/123/posts/456
120
+ * // Separate analytics client with its own cache
121
+ * const analyticsClient = createFetchClient({
122
+ * baseURL: "/api/analytics",
123
+ * dedupeCacheScope: "global",
124
+ * dedupeCacheScopeKey: "analytics-service" // Different scope
125
+ * });
197
126
  *
198
- * // Single parameter
199
- * const singleParam: URLOptions = {
200
- * initURL: "/users/:id",
201
- * params: { id: "user-123" }
202
- * };
203
- * // Results in: /users/user-123
127
+ * // Environment-specific scoping
128
+ * const apiClient = createFetchClient({
129
+ * dedupeCacheScope: "global",
130
+ * dedupeCacheScopeKey: `api-${process.env.NODE_ENV}` // "api-development", "api-production", etc.
131
+ * });
204
132
  * ```
133
+ *
134
+ * @default "default"
205
135
  */
206
- params?: Params;
136
+ dedupeCacheScopeKey?: "default" | AnyString | ((context: RequestContext) => string | undefined);
207
137
  /**
208
- * Query parameters to append to the URL as search parameters.
138
+ * Custom key generator for request deduplication.
209
139
  *
210
- * These will be serialized into the URL query string using standard
211
- * URL encoding practices.
140
+ * Override the default key generation strategy to control exactly which requests
141
+ * are considered duplicates. The default key combines URL, method, body, and
142
+ * relevant headers (excluding volatile ones like 'Date', 'Authorization', etc.).
212
143
  *
213
- * @example
214
- * ```typescript
215
- * // Basic query parameters
216
- * const queryOptions: URLOptions = {
217
- * initURL: "/users",
218
- * query: {
219
- * page: 1,
220
- * limit: 10,
221
- * search: "john doe",
222
- * active: true
223
- * }
224
- * };
225
- * // Results in: /users?page=1&limit=10&search=john%20doe&active=true
144
+ * **Default Key Generation:**
145
+ * The auto-generated key includes:
146
+ * - Full request URL (including query parameters)
147
+ * - HTTP method (GET, POST, etc.)
148
+ * - Request body (for POST/PUT/PATCH requests)
149
+ * - Stable headers (excludes Date, Authorization, User-Agent, etc.)
226
150
  *
227
- * // Filtering and sorting
228
- * const filterOptions: URLOptions = {
229
- * initURL: "/products",
230
- * query: {
231
- * category: "electronics",
232
- * minPrice: 100,
233
- * maxPrice: 500,
234
- * sortBy: "price",
235
- * order: "asc"
151
+ * **Custom Key Best Practices:**
152
+ * - Include only the parts of the request that should affect deduplication
153
+ * - Avoid including volatile data (timestamps, random IDs, etc.)
154
+ * - Consider performance - simpler keys are faster to compute and compare
155
+ * - Ensure keys are deterministic for the same logical request
156
+ * - Use consistent key formats across your application
157
+ *
158
+ * **Performance Considerations:**
159
+ * - Function-based keys are computed on every request - keep them lightweight
160
+ * - String keys are fastest but least flexible
161
+ * - Consider caching expensive key computations if needed
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { callApi } from "@zayne-labs/callapi";
166
+ *
167
+ * // Simple static key - useful for singleton requests
168
+ * const config = callApi("/api/config", {
169
+ * dedupeKey: "app-config",
170
+ * dedupeStrategy: "defer" // Share the same config across all requests
171
+ * });
172
+ *
173
+ * // URL and method only - ignore headers and body
174
+ * const userData = callApi("/api/user/123", {
175
+ * dedupeKey: (context) => `${context.options.method}:${context.options.fullURL}`
176
+ * });
177
+ *
178
+ * // Include specific headers in deduplication
179
+ * const apiCall = callApi("/api/data", {
180
+ * dedupeKey: (context) => {
181
+ * const authHeader = context.request.headers.get("Authorization");
182
+ * return `${context.options.fullURL}-${authHeader}`;
183
+ * }
184
+ * });
185
+ *
186
+ * // User-specific deduplication
187
+ * const userSpecificCall = callApi("/api/dashboard", {
188
+ * dedupeKey: (context) => {
189
+ * const userId = context.options.fullURL.match(/user\/(\d+)/)?.[1];
190
+ * return `dashboard-${userId}`;
191
+ * }
192
+ * });
193
+ *
194
+ * // Ignore certain query parameters
195
+ * const searchCall = callApi("/api/search?q=test&timestamp=123456", {
196
+ * dedupeKey: (context) => {
197
+ * const url = new URL(context.options.fullURL);
198
+ * url.searchParams.delete("timestamp"); // Remove volatile param
199
+ * return `search:${url.toString()}`;
200
+ * }
201
+ * });
202
+ * ```
203
+ *
204
+ * @default Auto-generated from request details
205
+ */
206
+ dedupeKey?: string | ((context: RequestContext) => string | undefined);
207
+ /**
208
+ * Strategy for handling duplicate requests. Can be a static string or callback function.
209
+ *
210
+ * **Available Strategies:**
211
+ * - `"cancel"`: Cancel previous request when new one starts (good for search)
212
+ * - `"defer"`: Share response between duplicate requests (good for config loading)
213
+ * - `"none"`: No deduplication, all requests execute independently
214
+ *
215
+ * @example
216
+ * ```ts
217
+ * // Static strategies
218
+ * const searchClient = createFetchClient({
219
+ * dedupeStrategy: "cancel" // Cancel previous searches
220
+ * });
221
+ *
222
+ * const configClient = createFetchClient({
223
+ * dedupeStrategy: "defer" // Share config across components
224
+ * });
225
+ *
226
+ * // Dynamic strategy based on request
227
+ * const smartClient = createFetchClient({
228
+ * dedupeStrategy: (context) => {
229
+ * return context.options.method === "GET" ? "defer" : "cancel";
230
+ * }
231
+ * });
232
+ *
233
+ * // Search-as-you-type with cancel strategy
234
+ * const handleSearch = async (query: string) => {
235
+ * try {
236
+ * const { data } = await callApi("/api/search", {
237
+ * method: "POST",
238
+ * body: { query },
239
+ * dedupeStrategy: "cancel",
240
+ * dedupeKey: "search" // Cancel previous searches, only latest one goes through
241
+ * });
242
+ *
243
+ * updateSearchResults(data);
244
+ * } catch (error) {
245
+ * if (error.name === "AbortError") {
246
+ * // Previous search cancelled - (expected behavior)
247
+ * return;
248
+ * }
249
+ * console.error("Search failed:", error);
236
250
  * }
237
251
  * };
238
- * // Results in: /products?category=electronics&minPrice=100&maxPrice=500&sortBy=price&order=asc
252
+ *
239
253
  * ```
254
+ *
255
+ * @default "cancel"
240
256
  */
241
- query?: Query;
257
+ dedupeStrategy?: DedupeStrategyUnion | ((context: RequestContext) => DedupeStrategyUnion);
258
+ };
259
+ //#endregion
260
+ //#region src/middlewares.d.ts
261
+ type FetchImpl = UnmaskType<(input: string | Request | URL, init?: RequestInit) => Awaitable<Response>>;
262
+ type FetchMiddlewareContext<TCallApiContext extends CallApiContext> = RequestContext<TCallApiContext> & {
263
+ fetchImpl: FetchImpl;
264
+ };
265
+ interface Middlewares<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> {
266
+ /**
267
+ * Wraps the fetch implementation to intercept requests at the network layer.
268
+ *
269
+ * Takes a context object containing the current fetch function and returns a new fetch function.
270
+ * Use it to cache responses, add logging, handle offline mode, or short-circuit requests etc.
271
+ * Multiple middleware compose in order: plugins → base config → per-request.
272
+ *
273
+ * Unlike `customFetchImpl`, middleware can call through to the original fetch.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * // Cache responses
278
+ * const cache = new Map();
279
+ *
280
+ * fetchMiddleware: (ctx) => async (input, init) => {
281
+ * const key = input.toString();
282
+ *
283
+ * const cachedResponse = cache.get(key);
284
+ *
285
+ * if (cachedResponse) {
286
+ * return cachedResponse.clone();
287
+ * }
288
+ *
289
+ * const response = await ctx.fetchImpl(input, init);
290
+ * cache.set(key, response.clone());
291
+ *
292
+ * return response;
293
+ * }
294
+ *
295
+ * // Handle offline
296
+ * fetchMiddleware: (ctx) => async (...parameters) => {
297
+ * if (!navigator.onLine) {
298
+ * return new Response('{"error": "offline"}', { status: 503 });
299
+ * }
300
+ *
301
+ * return ctx.fetchImpl(...parameters);
302
+ * }
303
+ * ```
304
+ */
305
+ fetchMiddleware?: (context: FetchMiddlewareContext<TCallApiContext>) => FetchImpl;
306
+ }
307
+ //#endregion
308
+ //#region src/constants/validation.d.ts
309
+ declare const fallBackRouteSchemaKey = "@default";
310
+ type FallBackRouteSchemaKey = typeof fallBackRouteSchemaKey;
311
+ //#endregion
312
+ //#region src/types/standard-schema.d.ts
313
+ /** The Standard Typed interface. This is a base type extended by other specs. */
314
+ interface StandardTypedV1<Input = unknown, Output = Input> {
315
+ /** The Standard properties. */
316
+ readonly "~standard": StandardTypedV1.Props<Input, Output>;
317
+ }
318
+ declare namespace StandardTypedV1 {
319
+ /** The Standard Typed properties interface. */
320
+ interface Props<Input = unknown, Output = Input> {
321
+ /** Inferred types associated with the schema. */
322
+ readonly types?: Types<Input, Output> | undefined;
323
+ /** The vendor name of the schema library. */
324
+ readonly vendor: string;
325
+ /** The version number of the standard. */
326
+ readonly version: 1;
327
+ }
328
+ /** The Standard Typed types interface. */
329
+ interface Types<Input = unknown, Output = Input> {
330
+ /** The input type of the schema. */
331
+ readonly input: Input;
332
+ /** The output type of the schema. */
333
+ readonly output: Output;
334
+ }
335
+ /** Infers the input type of a Standard Typed. */
336
+ type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
337
+ /** Infers the output type of a Standard Typed. */
338
+ type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
339
+ }
340
+ /** The Standard Schema interface. */
341
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
342
+ /** The Standard Schema properties. */
343
+ readonly "~standard": StandardSchemaV1.Props<Input, Output>;
344
+ }
345
+ declare namespace StandardSchemaV1 {
346
+ /** The Standard Schema properties interface. */
347
+ interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
348
+ /** Validates unknown input values. */
349
+ readonly validate: (value: unknown, options?: StandardSchemaV1.Options) => Promise<Result<Output>> | Result<Output>;
350
+ }
351
+ /** The result interface of the validate function. */
352
+ type Result<Output> = FailureResult | SuccessResult<Output>;
353
+ /** The result interface if validation succeeds. */
354
+ interface SuccessResult<Output> {
355
+ /** A falsy value for `issues` indicates success. */
356
+ readonly issues?: undefined;
357
+ /** The typed output value. */
358
+ readonly value: Output;
359
+ }
360
+ interface Options {
361
+ /** Explicit support for additional vendor-specific parameters, if needed. */
362
+ readonly libraryOptions?: Record<string, unknown> | undefined;
363
+ }
364
+ /** The result interface if validation fails. */
365
+ interface FailureResult {
366
+ /** The issues of failed validation. */
367
+ readonly issues: readonly Issue[];
368
+ }
369
+ /** The issue interface of the failure output. */
370
+ interface Issue {
371
+ /** The error message of the issue. */
372
+ readonly message: string;
373
+ /** The path of the issue, if any. */
374
+ readonly path?: ReadonlyArray<PathSegment | PropertyKey> | undefined;
375
+ }
376
+ /** The path segment interface of the issue. */
377
+ interface PathSegment {
378
+ /** The key representing a path segment. */
379
+ readonly key: PropertyKey;
380
+ }
381
+ /** The Standard types interface. */
382
+ interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
383
+ /** Infers the input type of a Standard. */
384
+ type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
385
+ /** Infers the output type of a Standard. */
386
+ type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
242
387
  }
243
388
  //#endregion
244
389
  //#region src/validation.d.ts
@@ -342,27 +487,150 @@ declare const getCurrentRouteSchemaKeyAndMainInitURL: (context: Pick<GetResolved
342
487
  mainInitURL: string;
343
488
  };
344
489
  //#endregion
345
- //#region src/utils/external/error.d.ts
346
- type HTTPErrorDetails<TErrorData> = Pick<CallApiExtraOptions, "defaultHTTPErrorMessage"> & {
347
- errorData: TErrorData;
348
- response: Response;
349
- };
350
- declare class HTTPError<TErrorData = Record<string, unknown>> extends Error {
351
- errorData: HTTPErrorDetails<TErrorData>["errorData"];
352
- readonly httpErrorSymbol: symbol;
353
- name: "HTTPError";
354
- response: HTTPErrorDetails<TErrorData>["response"];
355
- constructor(errorDetails: HTTPErrorDetails<TErrorData>, errorOptions?: ErrorOptions);
356
- /**
357
- * @description Checks if the given error is an instance of HTTPError
358
- * @param error - The error to check
359
- * @returns true if the error is an instance of HTTPError, false otherwise
360
- */
361
- static isError<TErrorData>(error: unknown): error is HTTPError<TErrorData>;
362
- }
363
- type SafeExtract<TUnion, TKey extends TUnion> = Extract<TUnion, TKey>;
364
- type ValidationErrorDetails = {
365
- /**
490
+ //#region src/url.d.ts
491
+ declare const atSymbol = "@";
492
+ type AtSymbol = typeof atSymbol;
493
+ type AllowedQueryParamValues = UnmaskType<boolean | number | string>;
494
+ type RecordStyleParams = UnmaskType<Record<string, AllowedQueryParamValues>>;
495
+ type TupleStyleParams = UnmaskType<AllowedQueryParamValues[]>;
496
+ type Params = UnmaskType<RecordStyleParams | TupleStyleParams>;
497
+ type StructuredQueryValues = Record<string, unknown> | unknown[] | null | undefined;
498
+ type Query = UnmaskType<Record<string, AllowedQueryParamValues | StructuredQueryValues> | URLSearchParams>;
499
+ type InitURLOrURLObject = AnyString | RouteKeyMethodsURLUnion | URL;
500
+ interface URLOptions {
501
+ /**
502
+ * Base URL for all API requests. Will only be prepended to relative URLs.
503
+ *
504
+ * Absolute URLs (starting with http/https) will not be prepended by the baseURL.
505
+ *
506
+ * @example
507
+ * ```ts
508
+ * // Set base URL for all requests
509
+ * baseURL: "https://api.example.com/v1"
510
+ *
511
+ * // Then use relative URLs in requests
512
+ * callApi("/users") // → https://api.example.com/v1/users
513
+ * callApi("/posts/123") // → https://api.example.com/v1/posts/123
514
+ *
515
+ * // Environment-specific base URLs
516
+ * baseURL: process.env.NODE_ENV === "production"
517
+ * ? "https://api.example.com"
518
+ * : "http://localhost:3000/api"
519
+ * ```
520
+ */
521
+ baseURL?: string;
522
+ /**
523
+ * Resolved request URL after processing baseURL, parameters, and query strings (readonly)
524
+ *
525
+ * This is the final URL that will be used for the HTTP request, computed from
526
+ * baseURL, initURL, params, and query parameters.
527
+ *
528
+ */
529
+ readonly fullURL?: string;
530
+ /**
531
+ * The original URL string passed to the callApi instance (readonly)
532
+ *
533
+ * This preserves the original URL as provided, including any method modifiers like "@get/" or "@post/".
534
+ *
535
+ */
536
+ readonly initURL?: string;
537
+ /**
538
+ * The URL string after normalization, with method modifiers removed(readonly)
539
+ *
540
+ * Method modifiers like "@get/", "@post/" are stripped to create a clean URL
541
+ * for parameter substitution and final URL construction.
542
+ *
543
+ */
544
+ readonly initURLNormalized?: string;
545
+ /**
546
+ * Parameters to be substituted into URL path segments.
547
+ *
548
+ * Supports both object-style (named parameters) and array-style (positional parameters)
549
+ * for flexible URL parameter substitution.
550
+ *
551
+ * @example
552
+ * ```typescript
553
+ * // Object-style parameters (recommended)
554
+ * const namedParams: URLOptions = {
555
+ * initURL: "/users/:userId/posts/:postId",
556
+ * params: { userId: "123", postId: "456" }
557
+ * };
558
+ * // Results in: /users/123/posts/456
559
+ *
560
+ * // Array-style parameters (positional)
561
+ * const positionalParams: URLOptions = {
562
+ * initURL: "/users/:userId/posts/:postId",
563
+ * params: ["123", "456"] // Maps in order: userId=123, postId=456
564
+ * };
565
+ * // Results in: /users/123/posts/456
566
+ *
567
+ * // Single parameter
568
+ * const singleParam: URLOptions = {
569
+ * initURL: "/users/:id",
570
+ * params: { id: "user-123" }
571
+ * };
572
+ * // Results in: /users/user-123
573
+ * ```
574
+ */
575
+ params?: Params;
576
+ /**
577
+ * Query parameters to append to the URL as search parameters.
578
+ *
579
+ * These will be serialized into the URL query string using standard
580
+ * URL encoding practices.
581
+ *
582
+ * @example
583
+ * ```typescript
584
+ * // Basic query parameters
585
+ * const queryOptions: URLOptions = {
586
+ * initURL: "/users",
587
+ * query: {
588
+ * page: 1,
589
+ * limit: 10,
590
+ * search: "john doe",
591
+ * active: true
592
+ * }
593
+ * };
594
+ * // Results in: /users?page=1&limit=10&search=john%20doe&active=true
595
+ *
596
+ * // Filtering and sorting
597
+ * const filterOptions: URLOptions = {
598
+ * initURL: "/products",
599
+ * query: {
600
+ * category: "electronics",
601
+ * minPrice: 100,
602
+ * maxPrice: 500,
603
+ * sortBy: "price",
604
+ * order: "asc"
605
+ * }
606
+ * };
607
+ * // Results in: /products?category=electronics&minPrice=100&maxPrice=500&sortBy=price&order=asc
608
+ * ```
609
+ */
610
+ query?: Query;
611
+ }
612
+ //#endregion
613
+ //#region src/utils/external/error.d.ts
614
+ type HTTPErrorDetails<TErrorData> = Pick<CallApiExtraOptions, "defaultHTTPErrorMessage"> & {
615
+ errorData: TErrorData;
616
+ response: Response;
617
+ };
618
+ declare class HTTPError<TErrorData = Record<string, unknown>> extends Error {
619
+ errorData: HTTPErrorDetails<TErrorData>["errorData"];
620
+ readonly httpErrorSymbol: symbol;
621
+ name: "HTTPError";
622
+ response: HTTPErrorDetails<TErrorData>["response"];
623
+ constructor(errorDetails: HTTPErrorDetails<TErrorData>, errorOptions?: ErrorOptions);
624
+ /**
625
+ * @description Checks if the given error is an instance of HTTPError
626
+ * @param error - The error to check
627
+ * @returns true if the error is an instance of HTTPError, false otherwise
628
+ */
629
+ static isError<TErrorData>(error: unknown): error is HTTPError<TErrorData>;
630
+ }
631
+ type SafeExtract<TUnion, TKey extends TUnion> = Extract<TUnion, TKey>;
632
+ type ValidationErrorDetails = {
633
+ /**
366
634
  * The cause of the validation error.
367
635
  *
368
636
  * It's either the name the schema for which validation failed, or the name of the schema config option that led to the validation error.
@@ -392,1533 +660,1261 @@ declare class ValidationError extends Error {
392
660
  static isError(error: unknown): error is ValidationError;
393
661
  }
394
662
  //#endregion
395
- //#region src/result.d.ts
396
- type ResponseParser<TData> = (text: string) => Awaitable<TData>;
397
- declare const getResponseType: <TData>(response: Response, responseParser: ResponseParser<TData>) => {
398
- arrayBuffer: () => Promise<ArrayBuffer>;
399
- blob: () => Promise<Blob>;
400
- formData: () => Promise<FormData>;
401
- json: () => Promise<TData>;
402
- stream: () => ReadableStream<Uint8Array<ArrayBuffer>> | null;
403
- text: () => Promise<string>;
404
- };
405
- type InitResponseTypeMap<TData = unknown> = ReturnType<typeof getResponseType<TData>>;
406
- type ResponseTypeUnion = keyof InitResponseTypeMap;
407
- type ResponseTypePlaceholder = null;
408
- type ResponseTypeType = ResponseTypePlaceholder | ResponseTypeUnion;
409
- type ResponseTypeMap<TData> = { [Key in keyof InitResponseTypeMap<TData>]: Awaited<ReturnType<InitResponseTypeMap<TData>[Key]>>; };
410
- type GetResponseType<TData, TResponseType extends ResponseTypeType, TComputedResponseTypeMap extends ResponseTypeMap<TData> = ResponseTypeMap<TData>> = null extends TResponseType ? TComputedResponseTypeMap["json"] : TResponseType extends NonNullable<ResponseTypeType> ? TComputedResponseTypeMap[TResponseType] : never;
411
- type CallApiResultSuccessVariant<TData> = {
412
- data: NoInferUnMasked<TData>;
413
- error: null;
414
- response: Response;
415
- };
416
- type PossibleJavaScriptError = UnmaskType<{
417
- errorData: false;
418
- message: string;
419
- name: "AbortError" | "Error" | "SyntaxError" | "TimeoutError" | "TypeError" | AnyString;
420
- originalError: DOMException | Error | SyntaxError | TypeError;
421
- }>;
422
- type PossibleHTTPError<TErrorData> = UnmaskType<{
423
- errorData: NoInferUnMasked<TErrorData>;
424
- message: string;
425
- name: "HTTPError";
426
- originalError: HTTPError;
427
- }>;
428
- type PossibleValidationError = UnmaskType<{
429
- errorData: ValidationError["errorData"];
430
- issueCause: ValidationError["issueCause"];
431
- message: string;
432
- name: "ValidationError";
433
- originalError: ValidationError;
434
- }>;
435
- type CallApiResultErrorVariant<TErrorData> = {
436
- data: null;
437
- error: PossibleHTTPError<TErrorData>;
438
- response: Response;
439
- } | {
440
- data: null;
441
- error: PossibleJavaScriptError;
442
- response: Response | null;
443
- } | {
444
- data: null;
445
- error: PossibleValidationError;
446
- response: Response | null;
447
- };
448
- type CallApiResultSuccessOrErrorVariant<TData, TError> = CallApiResultErrorVariant<TError> | CallApiResultSuccessVariant<TData>;
449
- type GetCallApiResult<TThrowOnError extends ThrowOnErrorBoolean, TResultWithException extends CallApiResultSuccessVariant<unknown>, TResultWithoutException extends CallApiResultSuccessOrErrorVariant<unknown, unknown>> = TThrowOnError extends true ? TResultWithException : TResultWithoutException;
450
- type ResultModeMap<TData = DefaultDataType, TErrorData = DefaultDataType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TComputedResult extends GetCallApiResult<TThrowOnError, CallApiResultSuccessVariant<TData>, CallApiResultSuccessOrErrorVariant<TData, TErrorData>> = GetCallApiResult<TThrowOnError, CallApiResultSuccessVariant<TData>, CallApiResultSuccessOrErrorVariant<TData, TErrorData>>> = UnmaskType<{
451
- all: TComputedResult;
452
- fetchApi: TComputedResult["response"];
453
- onlyData: TComputedResult["data"];
454
- onlyResponse: TComputedResult["response"];
455
- withoutResponse: Prettify<DistributiveOmit<TComputedResult, "response">>;
456
- }>;
457
- type ResultModePlaceholder = null;
458
- type ResultModeUnion = keyof ResultModeMap;
459
- type ResultModeType = ResultModePlaceholder | ResultModeUnion;
460
- type InferCallApiResult<TData, TErrorData, TResultMode extends ResultModeType, TThrowOnError extends ThrowOnErrorBoolean, TComputedResultModeMapWithException extends ResultModeMap<TData, TErrorData, true> = ResultModeMap<TData, TErrorData, true>, TComputedResultModeMapWithoutException extends ResultModeMap<TData, TErrorData, TThrowOnError> = ResultModeMap<TData, TErrorData, TThrowOnError>> = TErrorData extends false ? TComputedResultModeMapWithException["onlyData"] : TErrorData extends false | undefined ? TComputedResultModeMapWithException["onlyData"] : ResultModePlaceholder extends TResultMode ? TComputedResultModeMapWithoutException["all"] : TResultMode extends ResultModeUnion ? TComputedResultModeMapWithoutException[TResultMode] : never;
461
- type ErrorInfoOptions = Pick<CallApiExtraOptions, "cloneResponse" | "resultMode"> & {
462
- message?: string;
463
- };
464
- //#endregion
465
- //#region src/middlewares.d.ts
466
- type FetchImpl = UnmaskType<(input: string | Request | URL, init?: RequestInit) => Awaitable<Response>>;
467
- type FetchMiddlewareContext<TCallApiContext extends CallApiContext> = RequestContext<TCallApiContext> & {
468
- fetchImpl: FetchImpl;
663
+ //#region src/types/options-types.d.ts
664
+ interface Register {}
665
+ type GlobalMeta = Register extends {
666
+ meta?: infer TMeta extends DefaultMetaObject;
667
+ } ? TMeta : DefaultMetaObject;
668
+ type FetchSpecificKeysUnion = Exclude<(typeof fetchSpecificKeys)[number], "body" | "headers" | "method">;
669
+ type ModifiedRequestInit = RequestInit & {
670
+ duplex?: "half";
671
+ /**
672
+ * Custom fetch options that are merged into the final request configuration.
673
+ *
674
+ * This property is intended for environment-specific extensions not included in the standard web `RequestInit` type, such as `dispatcher` for Undici/Node.js or the `next` object for Next.js extended fetch.
675
+ */
676
+ extraFetchOptions?: RequestInit;
469
677
  };
470
- interface Middlewares<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> {
678
+ type CallApiRequestOptions<TBody = Body> = {
471
679
  /**
472
- * Wraps the fetch implementation to intercept requests at the network layer.
680
+ * Body of the request, can be a object or any other supported body type.
681
+ */
682
+ body?: TBody;
683
+ /**
684
+ * Headers to be used in the request.
685
+ */
686
+ headers?: HeadersOption;
687
+ /**
688
+ * HTTP method for the request.
689
+ * @default "GET"
690
+ */
691
+ method?: MethodUnion;
692
+ } & Pick<ModifiedRequestInit, FetchSpecificKeysUnion>;
693
+ type SharedExtraOptions<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBody = Body> = DedupeOptions & HookConfigOptions & HooksOrHooksArray<NoInferUnMasked<TCallApiContext>> & Middlewares<NoInferUnMasked<TCallApiContext>> & RefetchOptions & ResultModeOption<TErrorData, TResultMode> & RetryOptions<TErrorData> & Partial<TCallApiContext["InferredExtraOptions"]> & ThrowOnErrorOption<TErrorData, TThrowOnError> & URLOptions & {
694
+ /**
695
+ * Automatically add an Authorization header value.
473
696
  *
474
- * Takes a context object containing the current fetch function and returns a new fetch function.
475
- * Use it to cache responses, add logging, handle offline mode, or short-circuit requests etc.
476
- * Multiple middleware compose in order: plugins → base config → per-request.
697
+ * Supports multiple authentication patterns:
698
+ * - String: Direct authorization header value
699
+ * - Auth object: Structured authentication configuration
477
700
  *
478
- * Unlike `customFetchImpl`, middleware can call through to the original fetch.
701
+ * ```
702
+ */
703
+ auth?: AuthOption;
704
+ /**
705
+ * Custom function to serialize request body objects into strings.
706
+ *
707
+ * Useful for custom string serialization formats or when the default JSON
708
+ * serialization doesn't meet your needs.
479
709
  *
480
710
  * @example
481
711
  * ```ts
482
- * // Cache responses
483
- * const cache = new Map();
484
- *
485
- * fetchMiddleware: (ctx) => async (input, init) => {
486
- * const key = input.toString();
712
+ * // XML serialization
713
+ * bodySerializer: (body) => {
714
+ * return `<request>${Object.entries(body)
715
+ * .map(([key, value]) => `<${key}>${value}</${key}>`)
716
+ * .join('')}</request>`;
717
+ * }
487
718
  *
488
- * const cachedResponse = cache.get(key);
719
+ * // Custom JSON with specific formatting
720
+ * bodySerializer: (body) => JSON.stringify(body, null, 2)
721
+ * ```
722
+ */
723
+ bodySerializer?: (body: TBody extends SerializableObject ? TBody : SerializableObject) => string;
724
+ /**
725
+ * Custom function to transform the request body before it is passed to fetch.
489
726
  *
490
- * if (cachedResponse) {
491
- * return cachedResponse.clone();
492
- * }
727
+ * Useful for converting plain objects into formats like `FormData`,
728
+ * `URLSearchParams`, `Blob`, or other Fetch-compatible body values.
493
729
  *
494
- * const response = await ctx.fetchImpl(input, init);
495
- * cache.set(key, response.clone());
730
+ * Takes precedence over `bodySerializer`.
496
731
  *
497
- * return response;
498
- * }
732
+ * @example
733
+ * ```ts
734
+ * bodyTransformer: ({ body }) => {
735
+ * const formData = new FormData();
499
736
  *
500
- * // Handle offline
501
- * fetchMiddleware: (ctx) => async (...parameters) => {
502
- * if (!navigator.onLine) {
503
- * return new Response('{"error": "offline"}', { status: 503 });
504
- * }
737
+ * Object.entries(body).forEach(([key, value]) => {
738
+ * formData.append(key, String(value));
739
+ * });
505
740
  *
506
- * return ctx.fetchImpl(...parameters);
741
+ * return formData;
507
742
  * }
508
743
  * ```
509
744
  */
510
- fetchMiddleware?: (context: FetchMiddlewareContext<TCallApiContext>) => FetchImpl;
511
- }
512
- //#endregion
513
- //#region src/plugins.d.ts
514
- type PluginSetupContext<TCallApiContext extends CallApiContext = DefaultCallApiContext> = RequestContext<TCallApiContext> & ReturnType<typeof getCurrentRouteSchemaKeyAndMainInitURL>;
515
- type PluginInitResult<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Partial<Omit<PluginSetupContext<TCallApiContext>, "initURL" | "request"> & {
516
- initURL: InitURLOrURLObject;
517
- request: CallApiRequestOptions;
518
- }>;
519
- type GetDefaultDataTypeForPlugins<TData> = DefaultDataType extends TData ? never : TData;
520
- type PluginHooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> = HooksOrHooksArray<OverrideCallApiContext<TCallApiContext, {
521
- Data: GetDefaultDataTypeForPlugins<TCallApiContext["Data"]>;
522
- ErrorData: GetDefaultDataTypeForPlugins<TCallApiContext["ErrorData"]>;
523
- }>>;
524
- type PluginMiddlewares<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Middlewares<OverrideCallApiContext<TCallApiContext, {
525
- Data: GetDefaultDataTypeForPlugins<TCallApiContext["Data"]>;
526
- ErrorData: GetDefaultDataTypeForPlugins<TCallApiContext["ErrorData"]>;
527
- }>>;
528
- interface CallApiPlugin<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
745
+ bodyTransformer?: (context: {
746
+ body: TBody;
747
+ headers: Headers;
748
+ }) => Body;
529
749
  /**
530
- * Defines additional options that can be passed to callApi
750
+ * Whether to clone the response so it can be read multiple times.
751
+ *
752
+ * By default, response streams can only be consumed once. Enable this when you need
753
+ * to read the response in multiple places (e.g., in hooks and main code).
754
+ *
755
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/clone
756
+ * @default false
531
757
  */
532
- defineExtraOptions?: () => TCallApiContext["InferredExtraOptions"];
758
+ cloneResponse?: boolean;
533
759
  /**
534
- * A description for the plugin
760
+ * Custom fetch implementation to replace the default fetch function.
761
+ *
762
+ * Useful for testing, adding custom behavior, or using alternative HTTP clients
763
+ * that implement the fetch API interface.
764
+ *
765
+ * @example
766
+ * ```ts
767
+ * // Use node-fetch in Node.js environments
768
+ * import fetch from 'node-fetch';
769
+ *
770
+ * // Mock fetch for testing
771
+ * customFetchImpl: async (url, init) => {
772
+ * return new Response(JSON.stringify({ mocked: true }), {
773
+ * status: 200,
774
+ * headers: { 'Content-Type': 'application/json' }
775
+ * });
776
+ * }
777
+ *
778
+ * // Add custom logging to all requests
779
+ * customFetchImpl: async (url, init) => {
780
+ * console.log(`Fetching: ${url}`);
781
+ * const response = await fetch(url, init);
782
+ * console.log(`Response: ${response.status}`);
783
+ * return response;
784
+ * }
785
+ *
786
+ * // Use with custom HTTP client
787
+ * customFetchImpl: async (url, init) => {
788
+ * // Convert to your preferred HTTP client format
789
+ * return await customHttpClient.request({
790
+ * url: url.toString(),
791
+ * method: init?.method || 'GET',
792
+ * headers: init?.headers,
793
+ * body: init?.body
794
+ * });
795
+ * }
796
+ * ```
535
797
  */
536
- description?: string;
798
+ customFetchImpl?: FetchImpl;
537
799
  /**
538
- * Hooks for the plugin
800
+ * Enable debug mode for the request.
801
+ *
802
+ * @default true
539
803
  */
540
- hooks?: PluginHooks<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginHooks<TCallApiContext>> | Awaitable<void>);
804
+ debugMode?: boolean;
541
805
  /**
542
- * A unique id for the plugin
806
+ * Default HTTP error message when server doesn't provide one.
807
+ *
808
+ * Can be a static string or a function that receives error context
809
+ * to generate dynamic error messages based on the response.
810
+ *
811
+ * @default "Failed to fetch data from server!"
812
+ *
813
+ * @example
814
+ * ```ts
815
+ * // Static error message
816
+ * defaultHTTPErrorMessage: "API request failed. Please try again."
817
+ *
818
+ * // Dynamic error message based on status code
819
+ * defaultHTTPErrorMessage: ({ response }) => {
820
+ * switch (response.status) {
821
+ * case 401: return "Authentication required. Please log in.";
822
+ * case 403: return "Access denied. Insufficient permissions.";
823
+ * case 404: return "Resource not found.";
824
+ * case 429: return "Too many requests. Please wait and try again.";
825
+ * case 500: return "Server error. Please contact support.";
826
+ * default: return `Request failed with status ${response.status}`;
827
+ * }
828
+ * }
829
+ *
830
+ * // Include error data in message
831
+ * defaultHTTPErrorMessage: ({ errorData, response }) => {
832
+ * const userMessage = errorData?.message || "Unknown error occurred";
833
+ * return `${userMessage} (Status: ${response.status})`;
834
+ * }
835
+ * ```
543
836
  */
544
- id: string;
837
+ defaultHTTPErrorMessage?: string | ((context: Pick<HTTPError<TErrorData>, "errorData" | "response">) => string);
545
838
  /**
546
- * Middlewares that for the plugin
839
+ * Custom function to parse response strings into actual value instead of the default response.json().
840
+ *
841
+ * Useful when you need custom parsing logic for specific response formats.
842
+ *
843
+ * @example
844
+ * ```ts
845
+ * responseParser: (text) => {
846
+ * return JSON.parse(text);
847
+ * }
848
+ *
849
+ * // Parse XML responses
850
+ * responseParser: (text) => {
851
+ * const parser = new DOMParser();
852
+ * const doc = parser.parseFromString(text, "text/xml");
853
+ * return xmlToObject(doc);
854
+ * }
855
+ *
856
+ * // Parse CSV responses
857
+ * responseParser: (text) => {
858
+ * const lines = text.split('\n');
859
+ * const headers = lines[0].split(',');
860
+ * const data = lines.slice(1).map(line => {
861
+ * const values = line.split(',');
862
+ * return headers.reduce((obj, header, index) => {
863
+ * obj[header] = values[index];
864
+ * return obj;
865
+ * }, {});
866
+ * });
867
+ * return data;
868
+ * }
869
+ *
870
+ * ```
547
871
  */
548
- middlewares?: PluginMiddlewares<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginMiddlewares<TCallApiContext>> | Awaitable<void>);
872
+ responseParser?: ResponseParser<TData>;
549
873
  /**
550
- * A name for the plugin
874
+ * Expected response type, determines how the response body is parsed.
875
+ *
876
+ * Different response types trigger different parsing methods:
877
+ * - **"json"**: Parses as JSON using response.json()
878
+ * - **"text"**: Returns as plain text using response.text()
879
+ * - **"blob"**: Returns as Blob using response.blob()
880
+ * - **"arrayBuffer"**: Returns as ArrayBuffer using response.arrayBuffer()
881
+ * - **"stream"**: Returns the response body stream directly
882
+ *
883
+ * @default "json"
884
+ *
885
+ * @example
886
+ * ```ts
887
+ * // JSON API responses (default)
888
+ * responseType: "json"
889
+ *
890
+ * // Plain text responses
891
+ * responseType: "text"
892
+ * // Usage: const csvData = await callApi("/export.csv", { responseType: "text" });
893
+ *
894
+ * // File downloads
895
+ * responseType: "blob"
896
+ * // Usage: const file = await callApi("/download/file.pdf", { responseType: "blob" });
897
+ *
898
+ * // Binary data
899
+ * responseType: "arrayBuffer"
900
+ * // Usage: const buffer = await callApi("/binary-data", { responseType: "arrayBuffer" });
901
+ *
902
+ * // Streaming responses
903
+ * responseType: "stream"
904
+ * // Usage: const stream = await callApi("/large-dataset", { responseType: "stream" });
905
+ * ```
551
906
  */
552
- name: string;
907
+ responseType?: TResponseType;
553
908
  /**
554
- * Base schema for the client.
555
- */
556
- schema?: BaseCallApiSchemaAndConfig;
909
+ * Dictates how CallApi processes and returns the final result
910
+ *
911
+ - **"all"** (default): Returns `{ data, error, response }`. Standard lifecycle.
912
+ - **"onlyData"**: Returns only the data from the response.
913
+ - **"onlyResponse"**: Returns only the `Response` object.
914
+ - **"fetchApi"**: Also returns only the `Response` object, but also skips parsing of the response body internally and data/errorData schema validation.
915
+ - **"withoutResponse"**: Returns `{ data, error }`. Standard lifecycle, but omits the `response` property.
916
+ *
917
+ *
918
+ * **Note:**
919
+ * By default, simplified modes (`"onlyData"`, `"onlyResponse"`, `"fetchApi"`) do not throw errors.
920
+ * Success/failure should be handled via hooks or by checking the return value (e.g., `if (data)` or `if (response?.ok)`).
921
+ * To force an exception instead, set `throwOnError: true`.
922
+ *
923
+ *
924
+ * @default "all"
925
+ *
926
+ */
927
+ resultMode?: TResultMode;
557
928
  /**
558
- * A function that will be called when the plugin is initialized. This will be called before the any of the other internal functions.
559
- */
560
- setup?: (context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginInitResult<TCallApiContext>> | Awaitable<void>;
561
- /**
562
- * A version for the plugin
563
- */
564
- version?: string;
565
- }
566
- type InferPluginExtraOptions<TPluginArray extends CallApiPlugin[]> = UnionToIntersection<TPluginArray extends Array<infer TPlugin> ? TPlugin extends CallApiPlugin ? TPlugin["defineExtraOptions"] extends AnyFunction<infer TResult> ? InferSchemaOutput<TResult, TResult> : never : never : never>;
567
- //#endregion
568
- //#region src/types/default-types.d.ts
569
- type DefaultDataType = unknown;
570
- type DefaultPluginArray = CallApiPlugin[];
571
- type DefaultThrowOnError = boolean;
572
- type DefaultMetaObject = Record<string, unknown>;
573
- type DefaultCallApiContext = Prettify<OverrideCallApiContext<Required<CallApiContext>, {
574
- Meta: GlobalMeta;
575
- }>>;
576
- //#endregion
577
- //#region src/utils/external/body.d.ts
578
- type BodyType = NonNullable<CallApiRequestOptions["body"]>;
579
- declare const toSearchParams: <TSchema extends CallApiSchemaType<BodyType>>(data: InferSchemaOutput<TSchema>, schema?: TSchema) => URLSearchParams;
580
- declare const toQueryString: <TSchema extends CallApiSchemaType<BodyType>>(...parameters: Parameters<typeof toSearchParams<TSchema>>) => string;
581
- /**
582
- * @description Converts a plain object to FormData.
583
- *
584
- * Handles various data types:
585
- * - **Primitives** (string, number, boolean): Converted to strings
586
- * - **Blobs/Files**: Added directly to FormData
587
- * - **Arrays**: Each item is appended (allows multiple values for same key)
588
- * - **Objects**: JSON stringified before adding to FormData
589
- *
590
- * @example
591
- * ```ts
592
- * // Basic usage
593
- * const formData = toFormData({
594
- * name: "John",
595
- * age: 30,
596
- * active: true
597
- * });
598
- *
599
- * // With arrays
600
- * const formData = toFormData({
601
- * tags: ["javascript", "typescript"],
602
- * name: "John"
603
- * });
604
- *
605
- * // With files
606
- * const formData = toFormData({
607
- * avatar: fileBlob,
608
- * name: "John"
609
- * });
610
- *
611
- * // With nested objects (one level only)
612
- * const formData = toFormData({
613
- * user: { name: "John", age: 30 },
614
- * settings: { theme: "dark" }
615
- * });
616
- */
617
- declare const toFormData: <TSchema extends CallApiSchemaType<BodyType>>(data: InferSchemaOutput<TSchema>, schema?: TSchema) => FormData;
618
- //#endregion
619
- //#region src/utils/external/define.d.ts
620
- declare const defineSchema: <const TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, const TSchemaConfig extends CallApiSchemaConfig>(routes: TBaseSchemaRoutes, config?: Satisfies<TSchemaConfig, CallApiSchemaConfig>) => {
621
- routes: Writeable<TBaseSchemaRoutes, "deep">;
622
- config: Writeable<Satisfies<TSchemaConfig, CallApiSchemaConfig>, "deep">;
623
- };
624
- declare const defineSchemaRoutes: <const TSchemaRoutes extends BaseCallApiSchemaRoutes>(routes: TSchemaRoutes) => Writeable<typeof routes, "deep">;
625
- declare const defineMainSchema: <const TSchema extends CallApiSchema>(mainSchema: Satisfies<TSchema, CallApiSchema>) => Writeable<typeof mainSchema, "deep">;
626
- declare const defineSchemaConfig: <const TSchemaConfig extends CallApiSchemaConfig>(config: Satisfies<TSchemaConfig, CallApiSchemaConfig>) => Writeable<typeof config, "deep">;
627
- declare const definePlugin: <const TPlugin extends CallApiPlugin>(plugin: TPlugin) => Writeable<typeof plugin, "deep">;
628
- type BaseConfigObject = Exclude<BaseCallApiConfig, AnyFunction>;
629
- type BaseConfigFn = Extract<BaseCallApiConfig, AnyFunction>;
630
- type DefineBaseConfig = {
631
- <const TBaseConfig extends BaseConfigObject>(baseConfig: Satisfies<TBaseConfig, BaseConfigObject>): Writeable<typeof baseConfig, "deep">;
632
- <const TBaseConfig extends BaseConfigObject>(baseConfig: (...parameters: Parameters<BaseConfigFn>) => Writeable<TBaseConfig, "deep">): typeof baseConfig;
633
- };
634
- declare const defineBaseConfig: DefineBaseConfig;
635
- declare const defineInstanceConfig: <const TInstanceConfig extends CallApiConfig>(config: TInstanceConfig) => Writeable<typeof config, "deep">;
636
- declare const defineFallbackRouteSchema: <const TSchema extends CallApiSchema>(schema: TSchema) => {
637
- "@default": NonNullable<TSchema> extends Record<string | number | symbol, unknown> | unknown[] | readonly unknown[] ? Writeable<TSchema, "deep"> : TSchema;
638
- };
639
- //#endregion
640
- //#region src/utils/external/guards.d.ts
641
- declare const isHTTPError: <TErrorData>(error: CallApiResultErrorVariant<TErrorData>["error"] | null) => error is PossibleHTTPError<TErrorData>;
642
- declare const isHTTPErrorInstance: <TErrorData>(error: unknown) => error is HTTPError<TErrorData>;
643
- declare const isValidationError: (error: CallApiResultErrorVariant<unknown>["error"] | null) => error is PossibleValidationError;
644
- declare const isValidationErrorInstance: (error: unknown) => error is ValidationError;
645
- declare const isJavascriptError: (error: CallApiResultErrorVariant<unknown>["error"] | null) => error is PossibleJavaScriptError;
646
- //#endregion
647
- //#region src/utils/external/headers.d.ts
648
- declare const objectifyHeaders: (headers: CallApiRequestOptions["headers"]) => Record<string, string>;
649
- //#endregion
650
- //#region src/retry.d.ts
651
- declare const defaultRetryStatusCodesLookup: () => Readonly<{
652
- 408: "Request Timeout";
653
- 409: "Conflict";
654
- 425: "Too Early";
655
- 429: "Too Many Requests";
656
- 500: "Internal Server Error";
657
- 502: "Bad Gateway";
658
- 503: "Service Unavailable";
659
- 504: "Gateway Timeout";
660
- }>;
661
- type RetryStatusCodes = UnmaskType<AnyNumber | keyof ReturnType<typeof defaultRetryStatusCodesLookup>>;
662
- type RetryCondition<TErrorData> = (context: ErrorContext<{
663
- ErrorData: TErrorData;
664
- }>) => Awaitable<boolean>;
665
- type CallApiLooseImpl = (initURL: InitURLOrURLObject, init?: CallApiConfig) => Promise<CallApiResultLoose<unknown, unknown>>;
666
- interface RetryOptions<TErrorData> {
667
- /**
668
- * Tracks the number of times the request has already been retried internally
669
- * @internal
670
- * @deprecated **WARNING**: This property is used internally to track retries. Please abstain from reading or modifying it.
671
- */
672
- readonly ["~retryAttemptCount"]?: number;
673
- /**
674
- * Use a valid `Retry-After` response header instead of the configured retry delay
929
+ * Controls whether errors are thrown as exceptions or returned in the result.
930
+ *
931
+ * Can be a boolean or a function that receives the error and decides whether to throw.
932
+ * When true, errors are thrown as exceptions instead of being returned in the result object.
933
+ *
675
934
  * @default false
676
- */
677
- respectRetryAfter?: boolean;
678
- /**
679
- * Number of allowed retry attempts on HTTP errors
680
- * @default 0
681
- */
682
- retryAttempts?: number;
683
- /**
684
- * Callback whose return value determines if a request should be retried or not
685
- */
686
- retryCondition?: RetryCondition<TErrorData>;
687
- /**
688
- * Delay between retries in milliseconds
689
- * @default 1000
690
- */
691
- retryDelay?: number | ((currentAttemptCount: number) => number);
692
- /**
693
- * Maximum delay in milliseconds. Only applies to exponential strategy
694
- * @default 10000
695
- */
696
- retryMaxDelay?: number;
697
- /**
698
- * HTTP methods that are allowed to retry
699
- * @default ["GET", "POST"]
700
- */
701
- retryMethods?: MethodUnion[];
702
- /**
703
- * HTTP status codes that trigger a retry
704
- */
705
- retryStatusCodes?: RetryStatusCodes[];
706
- /**
707
- * Strategy to use when retrying
708
- * @default "linear"
709
- */
710
- retryStrategy?: "exponential" | "linear";
711
- }
712
- type RetryManagerContext = {
713
- callApi: CallApiLooseImpl;
714
- callApiArgs: {
715
- config: CallApiConfig;
716
- initURL: InitURLOrURLObject;
717
- };
718
- error: unknown;
719
- errorContext: ErrorContext;
720
- hookInfo: ExecuteHookInfo;
721
- removeDedupeCacheEntry: () => void;
722
- };
723
- //#endregion
724
- //#region src/refetch.d.ts
725
- declare const shouldAttemptRefetchSymbol: unique symbol;
726
- interface RefetchOptions {
727
- /**
728
- * Tracks if the refetching of the request should be attempted
729
- * @internal
730
- * @deprecated **WARNING**: This property is used internally to track retries. Please abstain from reading or modifying it.
731
- */
732
- [shouldAttemptRefetchSymbol]?: boolean;
733
- }
734
- type RefetchFn = () => void;
735
- type RefetchManagerResult = {
736
- handleRefetch: () => Promise<CallApiResultLoose<unknown, unknown>> | null;
737
- refetch: RefetchFn;
738
- };
739
- declare const createRefetchManager: (ctx: Pick<RetryManagerContext, "callApi" | "callApiArgs" | "removeDedupeCacheEntry"> & {
740
- options: CallApiExtraOptions;
741
- }) => RefetchManagerResult;
742
- type RefetchFnOption = Pick<ReturnType<typeof createRefetchManager>, "refetch">;
743
- //#endregion
744
- //#region src/stream.d.ts
745
- type StreamProgressEvent = {
746
- /**
747
- * Current chunk of data being streamed.
748
935
  *
749
- * Will be `null` on the final completion tick (when progress reaches 100%).
750
- */
751
- chunk: Uint8Array | null;
752
- /**
753
- * Progress in percentage
754
- */
755
- progress: number;
756
- /**
757
- * Total size of data in bytes
758
- */
759
- totalBytes: number;
760
- /**
761
- * Amount of data transferred so far
762
- */
763
- transferredBytes: number;
764
- };
765
- //#endregion
766
- //#region src/hooks.d.ts
767
- type CallApiRequestOptionsForHooks = Omit<CallApiRequestOptions, "headers"> & {
768
- headers: Partial<Record<"Authorization" | "Content-Type" | CommonRequestHeaders, string>>;
769
- };
770
- type CallApiExtraOptionsForHooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Hooks & Omit<CallApiExtraOptions<TCallApiContext>, keyof Hooks> & Pick<RefetchFnOption, "refetch">;
771
- interface Hooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
772
- /**
773
- * Hook called when any error occurs within the request/response lifecycle.
936
+ * @example
937
+ * ```ts
938
+ * // Always throw errors
939
+ * throwOnError: true
940
+ * try {
941
+ * const data = await callApi("/users");
942
+ * console.log("Users:", data);
943
+ * } catch (error) {
944
+ * console.error("Request failed:", error);
945
+ * }
774
946
  *
775
- * This is a unified error handler that catches both request errors (network failures,
776
- * timeouts, etc.) and response errors (HTTP error status codes). It's essentially
777
- * a combination of `onRequestError` and `onResponseError` hooks.
947
+ * // Never throw errors (default)
948
+ * throwOnError: false
949
+ * const { data, error } = await callApi("/users");
950
+ * if (error) {
951
+ * console.error("Request failed:", error);
952
+ * }
778
953
  *
779
- * @param context - Error context containing error details, request info, and response (if available)
780
- * @returns Promise or void - Hook can be async or sync
954
+ * // Conditionally throw based on error type
955
+ * throwOnError: (error) => {
956
+ * // Throw on client errors (4xx) but not server errors (5xx)
957
+ * return error.response?.status >= 400 && error.response?.status < 500;
958
+ * }
959
+ *
960
+ * // Throw only on specific status codes
961
+ * throwOnError: (error) => {
962
+ * const criticalErrors = [401, 403, 404];
963
+ * return criticalErrors.includes(error.response?.status);
964
+ * }
965
+ *
966
+ * // Throw on validation errors but not network errors
967
+ * throwOnError: (error) => {
968
+ * return error.type === "validation";
969
+ * }
970
+ * ```
781
971
  */
782
- onError?: (context: ErrorContext<TCallApiContext>) => Awaitable<unknown>;
972
+ throwOnError?: ThrowOnErrorType<TErrorData, TThrowOnError>;
783
973
  /**
784
- * Hook called before the HTTP request is sent and before any internal processing of the request object begins.
974
+ * Request timeout in milliseconds. Request will be aborted if it takes longer.
785
975
  *
786
- * This is the ideal place to modify request headers, add authentication,
787
- * implement request logging, or perform any setup before the network call.
976
+ * Useful for preventing requests from hanging indefinitely and providing
977
+ * better user experience with predictable response times.
788
978
  *
789
- * @param context - Request context with mutable request object and configuration
790
- * @returns Promise or void - Hook can be async or sync
979
+ * @example
980
+ * ```ts
981
+ * // 5 second timeout
982
+ * timeout: 5000
791
983
  *
792
- */
793
- onRequest?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
794
- /**
795
- * Hook called when an error occurs during the fetch request itself.
984
+ * // Different timeouts for different endpoints
985
+ * const quickApi = createFetchClient({ timeout: 3000 }); // 3s for fast endpoints
986
+ * const slowApi = createFetchClient({ timeout: 30000 }); // 30s for slow operations
796
987
  *
797
- * This handles network-level errors like connection failures, timeouts,
798
- * DNS resolution errors, or other issues that prevent getting an HTTP response.
799
- * Note that HTTP error status codes (4xx, 5xx) are handled by `onResponseError`.
988
+ * // Per-request timeout override
989
+ * await callApi("/quick-data", { timeout: 1000 });
990
+ * await callApi("/slow-report", { timeout: 60000 });
800
991
  *
801
- * @param context - Request error context with error details and null response
802
- * @returns Promise or void - Hook can be async or sync
992
+ * // No timeout (use with caution)
993
+ * timeout: 0
994
+ * ```
803
995
  */
804
- onRequestError?: (context: RequestErrorContext<TCallApiContext>) => Awaitable<unknown>;
996
+ timeout?: number;
997
+ };
998
+ type BaseCallApiExtraOptions<TBaseCallApiContext extends CallApiContext = DefaultCallApiContext, TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeType = ResultModeType, TBaseThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeType = ResponseTypeType, TBaseMeta extends DefaultMetaObject = DefaultMetaObject, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig = BaseCallApiSchemaAndConfig> = SharedExtraOptions<TBaseCallApiContext, TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType> & {
999
+ meta?: TBaseMeta;
805
1000
  /**
806
- * Hook called just before the HTTP request is sent and after the request has been processed.
1001
+ * Array of base CallApi plugins to extend library functionality.
807
1002
  *
808
- * @param context - Request context with mutable request object and configuration
809
- */
810
- onRequestReady?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
811
- /**
812
- * Hook called during upload stream progress tracking.
1003
+ * Base plugins are applied to all instances created from this base configuration
1004
+ * and provide foundational functionality like authentication, logging, or caching.
813
1005
  *
814
- * This hook is triggered when uploading data (like file uploads) and provides
815
- * progress information about the upload. Useful for implementing progress bars
816
- * or upload status indicators.
1006
+ * @example
1007
+ * ```ts
1008
+ * // Add logging plugin
817
1009
  *
818
- * @param context - Request stream context with progress event and request instance
819
- * @returns Promise or void - Hook can be async or sync
1010
+ * // Create base client with common plugins
1011
+ * const callApi = createFetchClient({
1012
+ * baseURL: "https://api.example.com",
1013
+ * plugins: [loggerPlugin({ enabled: true })]
1014
+ * });
1015
+ *
1016
+ * // All requests inherit base plugins
1017
+ * await callApi("/users");
1018
+ * await callApi("/posts");
820
1019
  *
1020
+ * ```
821
1021
  */
822
- onRequestStream?: (context: RequestStreamContext<TCallApiContext>) => Awaitable<unknown>;
1022
+ plugins?: TBasePluginArray;
823
1023
  /**
824
- * Hook called when any HTTP response is received from the API.
825
- *
826
- * This hook is triggered for both successful (2xx) and error (4xx, 5xx) responses.
827
- * It's useful for response logging, metrics collection, or any processing that
828
- * should happen regardless of response status.
829
- *
830
- * @param context - Response context with either success data or error information
831
- * @returns Promise or void - Hook can be async or sync
1024
+ * Base validation schemas for the client configuration.
832
1025
  *
1026
+ * Defines validation rules for requests and responses that apply to all
1027
+ * instances created from this base configuration. Provides type safety
1028
+ * and runtime validation for API interactions.
833
1029
  */
834
- onResponse?: (context: ResponseContext<TCallApiContext>) => Awaitable<unknown>;
1030
+ schema?: TBaseSchemaAndConfig;
835
1031
  /**
836
- * Hook called when an HTTP error response (4xx, 5xx) is received from the API.
1032
+ * Controls which configuration parts skip automatic merging between base and instance configs.
837
1033
  *
838
- * This handles server-side errors where an HTTP response was successfully received
839
- * but indicates an error condition. Different from `onRequestError` which handles
840
- * network-level failures.
1034
+ * By default, CallApi automatically merges base configuration with instance configuration.
1035
+ * This option allows you to disable automatic merging for specific parts when you need
1036
+ * manual control over how configurations are combined.
841
1037
  *
842
- * @param context - Response error context with HTTP error details and response
843
- * @returns Promise or void - Hook can be async or sync
844
- */
845
- onResponseError?: (context: ResponseErrorContext<TCallApiContext>) => Awaitable<unknown>;
846
- /**
847
- * Hook called during download stream progress tracking.
1038
+ * @enum
1039
+ * - **"all"**: Disables automatic merging for both request options and extra options
1040
+ * - **"options"**: Disables automatic merging of extra options only (hooks, plugins, etc.)
1041
+ * - **"request"**: Disables automatic merging of request options only (headers, body, etc.)
848
1042
  *
849
- * This hook is triggered when downloading data (like file downloads) and provides
850
- * progress information about the download. Useful for implementing progress bars
851
- * or download status indicators.
1043
+ * @example
1044
+ * ```ts
1045
+ * // Skip all automatic merging - full manual control
1046
+ * const client = callApi.create((ctx) => ({
1047
+ * skipAutoMergeFor: "all",
852
1048
  *
853
- * @param context - Response stream context with progress event and response
854
- * @returns Promise or void - Hook can be async or sync
1049
+ * // Manually decide what to merge
1050
+ * baseURL: ctx.options.baseURL, // Keep base URL
1051
+ * timeout: 5000, // Override timeout
1052
+ * headers: {
1053
+ * ...ctx.request.headers, // Merge headers manually
1054
+ * "X-Custom": "value" // Add custom header
1055
+ * }
1056
+ * }));
855
1057
  *
856
- */
857
- onResponseStream?: (context: ResponseStreamContext<TCallApiContext>) => Awaitable<unknown>;
858
- /**
859
- * Hook called when a request is being retried.
1058
+ * // Skip options merging - manual plugin/hook control
1059
+ * const client = callApi.create((ctx) => ({
1060
+ * skipAutoMergeFor: "options",
860
1061
  *
861
- * This hook is triggered before each retry attempt, providing information about
862
- * the previous failure and the current retry attempt number. Useful for implementing
863
- * custom retry logic, exponential backoff, or retry logging.
1062
+ * // Manually control which plugins to use
1063
+ * plugins: [
1064
+ * ...ctx.options.plugins?.filter(p => p.name !== "unwanted") || [],
1065
+ * customPlugin
1066
+ * ],
864
1067
  *
865
- * @param context - Retry context with error details and retry attempt count
866
- * @returns Promise or void - Hook can be async or sync
1068
+ * // Request options still auto-merge
1069
+ * method: "POST"
1070
+ * }));
867
1071
  *
868
- */
869
- onRetry?: (context: RetryContext<TCallApiContext>) => Awaitable<unknown>;
870
- /**
871
- * Hook called when a successful response (2xx status) is received from the API.
1072
+ * // Skip request merging - manual request control
1073
+ * const client = callApi.create((ctx) => ({
1074
+ * skipAutoMergeFor: "request",
872
1075
  *
873
- * This hook is triggered only for successful responses and provides access to
874
- * the parsed response data. Ideal for success logging, caching, or post-processing
875
- * of successful API responses.
1076
+ * // Extra options still auto-merge (plugins, hooks, etc.)
876
1077
  *
877
- * @param context - Success context with parsed response data and response object
878
- * @returns Promise or void - Hook can be async or sync
1078
+ * // Manually control request options
1079
+ * headers: {
1080
+ * "Content-Type": "application/json",
1081
+ * // Don't merge base headers
1082
+ * },
1083
+ * method: ctx.request.method || "GET"
1084
+ * }));
879
1085
  *
1086
+ * // Use case: Conditional merging based on request
1087
+ * const client = createFetchClient((ctx) => ({
1088
+ * skipAutoMergeFor: "options",
1089
+ *
1090
+ * // Only use auth plugin for protected routes
1091
+ * plugins: ctx.initURL.includes("/protected/")
1092
+ * ? [...(ctx.options.plugins || []), authPlugin]
1093
+ * : ctx.options.plugins?.filter(p => p.name !== "auth") || []
1094
+ * }));
1095
+ * ```
880
1096
  */
881
- onSuccess?: (context: SuccessContext<TCallApiContext>) => Awaitable<unknown>;
1097
+ skipAutoMergeFor?: "all" | "options" | "request";
1098
+ };
1099
+ type GetBaseSchemaRoutes<TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig> = Writeable<TBaseSchemaAndConfig["routes"], "deep">;
1100
+ type GetBaseSchemaConfig<TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig> = Writeable<NonNullable<TBaseSchemaAndConfig["config"]>, "deep">;
1101
+ type InferExtendSchemaContext<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string> = {
1102
+ baseSchemaRoutes: TBaseSchemaRoutes;
1103
+ currentRouteSchema: GetCurrentRouteSchema<TBaseSchemaRoutes, TCurrentRouteSchemaKey>;
1104
+ currentRouteSchemaKey: TCurrentRouteSchemaKey;
1105
+ };
1106
+ type GetExtendSchemaConfigContext<TBaseSchemaConfig extends CallApiSchemaConfig> = {
1107
+ baseSchemaConfig: TBaseSchemaConfig;
1108
+ };
1109
+ type InferExtendPluginContext<TBasePluginArray extends CallApiPlugin[]> = {
1110
+ basePlugins: TBasePluginArray;
1111
+ };
1112
+ type CallApiExtraOptions<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes = BaseCallApiSchemaRoutes, TSchema extends CallApiSchema = CallApiSchema, TBaseSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TCurrentRouteSchemaKey extends string = string, TBody extends InferSchemaOutput<TSchema["body"], Body> = InferSchemaOutput<TSchema["body"], Body>> = InferRequiredExtraOptions<TSchema, TBaseSchemaRoutes, TCurrentRouteSchemaKey, TCallApiContext> & Omit<SharedExtraOptions<TCallApiContext, TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TBody>, keyof InferRequiredExtraOptions<CallApiSchema, BaseCallApiSchemaRoutes, string, CallApiContext>> & {
882
1113
  /**
883
- * Hook called when a validation error occurs.
884
- *
885
- * This hook is triggered when request or response data fails validation against
886
- * a defined schema. It provides access to the validation error details and can
887
- * be used for custom error handling, logging, or fallback behavior.
1114
+ * Array of instance-specific CallApi plugins or a function to configure plugins.
888
1115
  *
889
- * @param context - Validation error context with error details and response (if available)
890
- * @returns Promise or void - Hook can be async or sync
1116
+ * Instance plugins are added to the base plugins and provide functionality
1117
+ * specific to this particular API instance. Can be a static array or a function
1118
+ * that receives base plugins and returns the instance plugins.
891
1119
  *
892
1120
  */
893
- onValidationError?: (context: ValidationErrorContext<TCallApiContext>) => Awaitable<unknown>;
894
- }
895
- type HooksOrHooksArray<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> = { [Key in keyof Hooks<TCallApiContext>]: Hooks<TCallApiContext>[Key] | Array<Hooks<TCallApiContext>[Key]>; };
896
- interface HookConfigOptions {
1121
+ plugins?: TPluginArray | ((context: InferExtendPluginContext<TBasePluginArray>) => TPluginArray);
897
1122
  /**
898
- * Controls the execution mode of all composed hooks (main + plugin hooks).
899
- *
900
- * - **"parallel"**: All hooks execute simultaneously via Promise.all() for better performance
901
- * - **"sequential"**: All hooks execute one by one in registration order via await in a loop
902
- *
903
- * This affects how ALL hooks execute together, regardless of their source (main or plugin).
1123
+ * For instance-specific validation schemas
904
1124
  *
905
- * @default "parallel"
906
- */
907
- hooksExecutionMode?: "parallel" | "sequential";
908
- }
909
- type RequestContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = {
910
- /**
911
- * Base configuration object passed to createFetchClient.
1125
+ * Defines validation rules specific to this API instance, extending or overriding the base schema.
912
1126
  *
913
- * Contains the foundational configuration that applies to all requests
914
- * made by this client instance, such as baseURL, default headers, and
915
- * global options.
916
- */
917
- baseConfig: Exclude<BaseCallApiConfig, AnyFunction>;
918
- /**
919
- * Instance-specific configuration object passed to the callApi instance.
1127
+ * Can be a static schema object or a function that receives base schema context and returns instance schemas.
920
1128
  *
921
- * Contains configuration specific to this particular API call, which
922
- * can override or extend the base configuration.
923
1129
  */
924
- config: CallApiConfig;
1130
+ schema?: TSchema | ((context: InferExtendSchemaContext<TBaseSchemaRoutes, TCurrentRouteSchemaKey>) => TSchema);
925
1131
  /**
926
- * Merged options combining base config, instance config, and default options.
1132
+ * Instance-specific schema configuration or a function to configure schema behavior.
927
1133
  *
928
- * This is the final resolved configuration that will be used for the request,
929
- * with proper precedence applied (instance > base > defaults).
930
- */
931
- options: CallApiExtraOptionsForHooks<TCallApiContext>;
932
- /**
933
- * Merged request object ready to be sent.
1134
+ * Controls how validation schemas are applied and behave for this specific API instance.
1135
+ * Can override base schema configuration or extend it with instance-specific validation rules.
934
1136
  *
935
- * Contains the final request configuration including URL, method, headers,
936
- * body, and other fetch options. This object can be modified in onRequest
937
- * hooks to customize the outgoing request.
938
1137
  */
939
- request: CallApiRequestOptionsForHooks;
1138
+ schemaConfig?: TSchemaConfig | ((context: GetExtendSchemaConfigContext<TBaseSchemaConfig>) => TSchemaConfig);
940
1139
  };
941
- type SuccessContext<TCallApiContext extends Pick<CallApiContext, "Data" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = DistributiveOmit<CallApiResultSuccessVariant<TCallApiContext["Data"]>, "error"> & RequestContext<TCallApiContext>;
942
- type ResponseContext<TCallApiContext extends Pick<CallApiContext, "Data" | "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & (Prettify<CallApiResultSuccessVariant<TCallApiContext["Data"]>> | Prettify<Extract<CallApiResultErrorVariant<TCallApiContext["ErrorData"]>, {
943
- error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
944
- }>>);
945
- type RequestStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
946
- event: StreamProgressEvent;
947
- requestInstance: Request;
1140
+ type InstanceContext = {
1141
+ initURL: string;
1142
+ options: CallApiExtraOptions;
1143
+ request: CallApiRequestOptions;
948
1144
  };
949
- type ResponseStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
950
- event: StreamProgressEvent;
951
- response: Response;
1145
+ type BaseCallApiConfig<TBaseCallApiContext extends CallApiContext = DefaultCallApiContext, TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeType = ResultModeType, TBaseThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeType = ResponseTypeType, TBaseMeta extends DefaultMetaObject = DefaultMetaObject, TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig = BaseCallApiSchemaAndConfig, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedBaseExtraOptions = BaseCallApiExtraOptions<TBaseCallApiContext, TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBaseMeta, TBasePluginArray, TBaseSchemaAndConfig>> = (CallApiRequestOptions & TComputedBaseExtraOptions) | ((context: InstanceContext) => CallApiRequestOptions & TComputedBaseExtraOptions);
1146
+ type CallApiConfig<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes = BaseCallApiSchemaRoutes, TSchema extends CallApiSchema = CallApiSchema, TBaseSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TInitURL extends InitURLOrURLObject = InitURLOrURLObject, TCurrentRouteSchemaKey extends string = string, TBody extends InferSchemaOutput<TSchema["body"], Body> = InferSchemaOutput<TSchema["body"], Body>, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TPluginArray extends CallApiPlugin[] = DefaultPluginArray> = CallApiExtraOptions<TCallApiContext, TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TBasePluginArray, TPluginArray, TBaseSchemaRoutes, TSchema, TBaseSchemaConfig, TSchemaConfig, TCurrentRouteSchemaKey, TBody> & InferRequestOptions<TSchema, TInitURL, TBody> & Omit<CallApiRequestOptions<TBody>, keyof InferRequestOptions<CallApiSchema, string>>;
1147
+ type CallApiParameters<TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TCallApiContext extends CallApiContext = DefaultCallApiContext, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes = BaseCallApiSchemaRoutes, TSchema extends CallApiSchema = CallApiSchema, TBaseSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TInitURL extends InitURLOrURLObject = InitURLOrURLObject, TCurrentRouteSchemaKey extends string = string, TBody extends InferSchemaOutput<TSchema["body"], Body> = InferSchemaOutput<TSchema["body"], Body>, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedRequiredOptions = InferRequestOptions<TSchema, TInitURL, TBody> & InferRequiredExtraOptions<TSchema, TBaseSchemaRoutes, TCurrentRouteSchemaKey, TCallApiContext>, TComputedConfig = CallApiConfig<TCallApiContext, TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TBaseSchemaRoutes, TSchema, TBaseSchemaConfig, TSchemaConfig, TInitURL, TCurrentRouteSchemaKey, TBody, TBasePluginArray, TPluginArray>> = NonNullableUnknown extends TComputedRequiredOptions ? [initURL: TInitURL, config?: TComputedConfig] : [initURL: TInitURL, config: TComputedConfig];
1148
+ type CallApiResult<TData, TErrorData, TResultMode extends ResultModeType, TThrowOnError extends ThrowOnErrorBoolean> = InferCallApiResult<TData, TErrorData, TResultMode, TThrowOnError>;
1149
+ type CallApiResultLoose<TData, TErrorData, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = ThrowOnErrorBoolean> = InferCallApiResult<TData, TErrorData, TResultMode, TThrowOnError>;
1150
+ //#endregion
1151
+ //#region src/auth.d.ts
1152
+ type PossibleAuthValue = Awaitable<string | null | undefined>;
1153
+ type PossibleAuthValueOrGetter = PossibleAuthValue | (() => PossibleAuthValue);
1154
+ type BearerAuth = {
1155
+ type: "Bearer";
1156
+ value: PossibleAuthValueOrGetter;
952
1157
  };
953
- type ErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = DistributiveOmit<CallApiResultErrorVariant<TCallApiContext["ErrorData"]>, "data"> & RequestContext<TCallApiContext>;
954
- type ValidationErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
955
- error: PossibleValidationError;
956
- }> & RequestContext<TCallApiContext>;
957
- type RequestErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
958
- error: PossibleJavaScriptError;
959
- }> & RequestContext<TCallApiContext>;
960
- type ResponseErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
961
- error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
962
- }> & RequestContext<TCallApiContext>;
963
- type RetryContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = ErrorContext<TCallApiContext> & {
964
- retryAttemptCount: number;
1158
+ type TokenAuth = {
1159
+ type: "Token";
1160
+ value: PossibleAuthValueOrGetter;
965
1161
  };
966
- type ExecuteHookInfo = {
967
- errorInfoOptions: ErrorInfoOptions;
968
- shouldThrowOnError: boolean | undefined;
1162
+ type BasicAuth = {
1163
+ type: "Basic";
1164
+ username: PossibleAuthValueOrGetter;
1165
+ password: PossibleAuthValueOrGetter;
1166
+ };
1167
+ /**
1168
+ * Custom auth
1169
+ *
1170
+ * @param prefix - prefix of the header
1171
+ * @param authValue - value of the header
1172
+ *
1173
+ * @example
1174
+ * ```ts
1175
+ * {
1176
+ * type: "Custom",
1177
+ * prefix: "Token",
1178
+ * authValue: "token"
1179
+ * }
1180
+ * ```
1181
+ */
1182
+ type CustomAuth = {
1183
+ type: "Custom";
1184
+ prefix: PossibleAuthValueOrGetter;
1185
+ value: PossibleAuthValueOrGetter;
969
1186
  };
1187
+ type AuthOption = PossibleAuthValueOrGetter | BearerAuth | TokenAuth | BasicAuth | CustomAuth;
970
1188
  //#endregion
971
- //#region src/dedupe.d.ts
972
- type DedupeStrategyUnion = UnmaskType<"cancel" | "defer" | "none">;
973
- type DedupeOptions = {
1189
+ //#region src/types/conditional-types.d.ts
1190
+ /**
1191
+ * @description Makes a type partial if the output type of TSchema is not provided or has undefined in the union, otherwise makes it required
1192
+ */
1193
+ type MakeSchemaOptionRequiredIfDefined<TSchemaOption extends CallApiSchema[keyof CallApiSchema], TObject> = undefined extends InferSchemaOutput<TSchemaOption, undefined> ? TObject : Required<TObject>;
1194
+ type MergeBaseWithRouteKey<TBaseURLOrPrefix extends string | undefined, TRouteKey extends string> = TBaseURLOrPrefix extends string ? TRouteKey extends `${AtSymbol}${infer TMethod extends RouteKeyMethods}/${infer TRestOfRoutKey}` ? `${AtSymbol}${TMethod}/${RemoveLeadingSlash<RemoveTrailingSlash<TBaseURLOrPrefix>>}/${RemoveLeadingSlash<TRestOfRoutKey>}` : `${TBaseURLOrPrefix}${TRouteKey}` : TRouteKey;
1195
+ type ApplyURLBasedConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["prefix"] extends string ? MergeBaseWithRouteKey<TSchemaConfig["prefix"], TSchemaRouteKeys> : TSchemaConfig["baseURL"] extends string ? MergeBaseWithRouteKey<TSchemaConfig["baseURL"], TSchemaRouteKeys> : TSchemaRouteKeys;
1196
+ type ApplyStrictConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["strict"] extends true ? TSchemaRouteKeys // eslint-disable-next-line perfectionist/sort-union-types -- Don't sort union types
1197
+ : TSchemaRouteKeys | Exclude<InitURLOrURLObject, RouteKeyMethodsURLUnion>;
1198
+ type ApplySchemaConfiguration<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, TSchemaRouteKeys>>;
1199
+ type InferAllMainRoutes<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes> = Omit<TBaseSchemaRoutes, FallBackRouteSchemaKey>;
1200
+ type InferAllMainRouteKeys<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = ApplySchemaConfiguration<TSchemaConfig, Extract<keyof InferAllMainRoutes<TBaseSchemaRoutes>, string>>;
1201
+ type InferInitURL<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = keyof TBaseSchemaRoutes extends never ? InitURLOrURLObject : InferAllMainRouteKeys<TBaseSchemaRoutes, TSchemaConfig>;
1202
+ type GetCurrentRouteSchemaKey<TSchemaConfig extends CallApiSchemaConfig, TPath> = TPath extends URL ? string : TSchemaConfig["prefix"] extends string ? TPath extends (`${AtSymbol}${infer TMethod extends RouteKeyMethods}/${RemoveLeadingSlash<TSchemaConfig["prefix"]>}${infer TCurrentRoute}`) ? `${AtSymbol}${TMethod}/${RemoveLeadingSlash<TCurrentRoute>}` : TPath extends `${TSchemaConfig["prefix"]}${infer TCurrentRoute}` ? TCurrentRoute : string : TSchemaConfig["baseURL"] extends string ? TPath extends (`${AtSymbol}${infer TMethod extends RouteKeyMethods}/${TSchemaConfig["baseURL"]}${infer TCurrentRoute}`) ? `${AtSymbol}${TMethod}/${RemoveLeadingSlash<TCurrentRoute>}` : TPath extends `${TSchemaConfig["baseURL"]}${infer TCurrentRoute}` ? TCurrentRoute : string : TPath;
1203
+ type GetCurrentRouteSchema<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string, TComputedFallBackRouteSchema = TBaseSchemaRoutes[FallBackRouteSchemaKey], TComputedCurrentRouteSchema = TBaseSchemaRoutes[TCurrentRouteSchemaKey], TComputedRouteSchema extends CallApiSchema = NonNullable<Omit<TComputedFallBackRouteSchema, keyof TComputedCurrentRouteSchema> & TComputedCurrentRouteSchema>> = TComputedRouteSchema extends CallApiSchema ? Writeable<TComputedRouteSchema, "deep"> : CallApiSchema;
1204
+ type JsonPrimitive = boolean | number | string | null | undefined;
1205
+ type SerializableObject = Record<PropertyKey, unknown>;
1206
+ type SerializableArray = Array<JsonPrimitive | SerializableObject> | ReadonlyArray<JsonPrimitive | SerializableObject>;
1207
+ type Body = UnmaskType<Exclude<RequestInit["body"], undefined> | SerializableArray | SerializableObject>;
1208
+ type InferBodyOption<TSchema extends CallApiSchema, TBody = InferSchemaOutput<TSchema["body"], Body>> = MakeSchemaOptionRequiredIfDefined<TSchema["body"], {
974
1209
  /**
975
- * Controls the scope of request deduplication caching.
976
- *
977
- * - `"global"`: Shares deduplication cache across all `createFetchClient` instances with the same `dedupeCacheScopeKey`.
978
- * Useful for applications with multiple API clients that should share deduplication state.
979
- * - `"local"`: Limits deduplication to requests within the same `createFetchClient` instance.
980
- * Provides better isolation and is recommended for most use cases.
981
- *
982
- *
983
- * **Real-world Scenarios:**
984
- * - Use `"global"` when you have multiple API clients (user service, auth service, etc.) that might make overlapping requests
985
- * - Use `"local"` (default) for single-purpose clients or when you want strict isolation between different parts of your app
986
- *
987
- * @example
988
- * ```ts
989
- * // Local scope - each client has its own deduplication cache
990
- * const userClient = createFetchClient({ baseURL: "/api/users" });
991
- * const postClient = createFetchClient({ baseURL: "/api/posts" });
992
- * // These clients won't share deduplication state
993
- *
994
- * // Global scope - share cache across related clients
995
- * const userClient = createFetchClient({
996
- * baseURL: "/api/users",
997
- * dedupeCacheScope: "global",
998
- * });
999
- * const postClient = createFetchClient({
1000
- * baseURL: "/api/posts",
1001
- * dedupeCacheScope: "global",
1002
- * });
1003
- * // These clients will share deduplication state
1004
- * ```
1005
- *
1006
- * @default "local"
1210
+ * Body of the request, can be a object or any other supported body type.
1007
1211
  */
1008
- dedupeCacheScope?: "global" | "local";
1212
+ body?: TBody;
1213
+ }>;
1214
+ type MethodUnion = UnmaskType<"CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" | AnyString>;
1215
+ type ExtractMethodFromURL<TInitURL> = string extends TInitURL ? MethodUnion : TInitURL extends `${AtSymbol}${infer TMethod extends RouteKeyMethods}/${string}` ? Uppercase<TMethod> : MethodUnion;
1216
+ type InferMethodOption<TSchema extends CallApiSchema, TInitURL extends InitURLOrURLObject> = MakeSchemaOptionRequiredIfDefined<TSchema["method"], {
1009
1217
  /**
1010
- * Unique namespace for the global deduplication cache when using `dedupeCacheScope: "global"`.
1011
- *
1012
- * This creates logical groupings of deduplication caches. All instances with the same key
1013
- * will share the same cache namespace, allowing fine-grained control over which clients
1014
- * share deduplication state.
1015
- *
1016
- * **Best Practices:**
1017
- * - Use descriptive names that reflect the logical grouping (e.g., "user-service", "analytics-api")
1018
- * - Keep scope keys consistent across related API clients
1019
- * - Consider using different scope keys for different environments (dev, staging, prod)
1020
- * - Avoid overly broad scope keys that might cause unintended cache sharing
1021
- *
1022
- * **Cache Management:**
1023
- * - Each scope key maintains its own independent cache
1024
- * - Caches are automatically cleaned up when no references remain
1025
- * - Consider the memory implications of multiple global scopes
1026
- *
1027
- * @example
1028
- * ```ts
1029
- * // Group related API clients together
1030
- * const userClient = createFetchClient({
1031
- * baseURL: "/api/users",
1032
- * dedupeCacheScope: "global",
1033
- * dedupeCacheScopeKey: "user-service"
1034
- * });
1035
- * const profileClient = createFetchClient({
1036
- * baseURL: "/api/profiles",
1037
- * dedupeCacheScope: "global",
1038
- * dedupeCacheScopeKey: "user-service" // Same scope - will share cache
1039
- * });
1040
- *
1041
- * // Separate analytics client with its own cache
1042
- * const analyticsClient = createFetchClient({
1043
- * baseURL: "/api/analytics",
1044
- * dedupeCacheScope: "global",
1045
- * dedupeCacheScopeKey: "analytics-service" // Different scope
1046
- * });
1047
- *
1048
- * // Environment-specific scoping
1049
- * const apiClient = createFetchClient({
1050
- * dedupeCacheScope: "global",
1051
- * dedupeCacheScopeKey: `api-${process.env.NODE_ENV}` // "api-development", "api-production", etc.
1052
- * });
1053
- * ```
1054
- *
1055
- * @default "default"
1218
+ * HTTP method for the request.
1219
+ * @default "GET"
1056
1220
  */
1057
- dedupeCacheScopeKey?: "default" | AnyString | ((context: RequestContext) => string | undefined);
1058
- /**
1059
- * Custom key generator for request deduplication.
1060
- *
1061
- * Override the default key generation strategy to control exactly which requests
1062
- * are considered duplicates. The default key combines URL, method, body, and
1063
- * relevant headers (excluding volatile ones like 'Date', 'Authorization', etc.).
1064
- *
1065
- * **Default Key Generation:**
1066
- * The auto-generated key includes:
1067
- * - Full request URL (including query parameters)
1068
- * - HTTP method (GET, POST, etc.)
1069
- * - Request body (for POST/PUT/PATCH requests)
1070
- * - Stable headers (excludes Date, Authorization, User-Agent, etc.)
1071
- *
1072
- * **Custom Key Best Practices:**
1073
- * - Include only the parts of the request that should affect deduplication
1074
- * - Avoid including volatile data (timestamps, random IDs, etc.)
1075
- * - Consider performance - simpler keys are faster to compute and compare
1076
- * - Ensure keys are deterministic for the same logical request
1077
- * - Use consistent key formats across your application
1221
+ method?: InferSchemaOutput<TSchema["method"], ExtractMethodFromURL<TInitURL>>;
1222
+ }>;
1223
+ type HeadersOption = UnmaskType<Headers | Record<"Authorization", CommonAuthorizationHeaders | undefined> | Record<"Content-Type", CommonContentTypes | undefined> | Record<CommonRequestHeaders, string | undefined> | Record<string, string | undefined> | Array<[string, string]>>;
1224
+ type InferHeadersOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["headers"], {
1225
+ /**
1226
+ * Headers to be used in the request.
1227
+ */
1228
+ headers?: InferSchemaOutput<TSchema["headers"], HeadersOption> | ((context: {
1229
+ baseHeaders: Extract<HeadersOption, Record<string, unknown>>;
1230
+ }) => InferSchemaOutput<TSchema["headers"], HeadersOption>);
1231
+ }>;
1232
+ type InferRequestOptions<TSchema extends CallApiSchema, TInitURL extends InferInitURL<BaseCallApiSchemaRoutes, CallApiSchemaConfig>, TBody = InferSchemaOutput<TSchema["body"], Body>> = InferBodyOption<TSchema, TBody> & InferHeadersOption<TSchema> & InferMethodOption<TSchema, TInitURL>;
1233
+ type InferMetaOption<TSchema extends CallApiSchema, TCallApiContext extends CallApiContext> = MakeSchemaOptionRequiredIfDefined<TSchema["meta"], {
1234
+ /**
1235
+ * Optional metadata field for associating additional information with requests.
1078
1236
  *
1079
- * **Performance Considerations:**
1080
- * - Function-based keys are computed on every request - keep them lightweight
1081
- * - String keys are fastest but least flexible
1082
- * - Consider caching expensive key computations if needed
1237
+ * Useful for logging, tracing, or handling specific cases in shared interceptors.
1238
+ * The meta object is passed through to all hooks and can be accessed in error handlers.
1083
1239
  *
1084
1240
  * @example
1085
1241
  * ```ts
1086
- * import { callApi } from "@zayne-labs/callapi";
1087
- *
1088
- * // Simple static key - useful for singleton requests
1089
- * const config = callApi("/api/config", {
1090
- * dedupeKey: "app-config",
1091
- * dedupeStrategy: "defer" // Share the same config across all requests
1092
- * });
1093
- *
1094
- * // URL and method only - ignore headers and body
1095
- * const userData = callApi("/api/user/123", {
1096
- * dedupeKey: (context) => `${context.options.method}:${context.options.fullURL}`
1242
+ * const callMainApi = callApi.create({
1243
+ * baseURL: "https://main-api.com",
1244
+ * onResponseError: ({ response, options }) => {
1245
+ * if (options.meta?.userId) {
1246
+ * console.error(`User ${options.meta.userId} made an error`);
1247
+ * }
1248
+ * },
1097
1249
  * });
1098
1250
  *
1099
- * // Include specific headers in deduplication
1100
- * const apiCall = callApi("/api/data", {
1101
- * dedupeKey: (context) => {
1102
- * const authHeader = context.request.headers.get("Authorization");
1103
- * return `${context.options.fullURL}-${authHeader}`;
1104
- * }
1251
+ * const response = await callMainApi({
1252
+ * url: "https://example.com/api/data",
1253
+ * meta: { userId: "123" },
1105
1254
  * });
1106
1255
  *
1107
- * // User-specific deduplication
1108
- * const userSpecificCall = callApi("/api/dashboard", {
1109
- * dedupeKey: (context) => {
1110
- * const userId = context.options.fullURL.match(/user\/(\d+)/)?.[1];
1111
- * return `dashboard-${userId}`;
1256
+ * // Use case: Request tracking
1257
+ * const result = await callMainApi({
1258
+ * url: "https://example.com/api/data",
1259
+ * meta: {
1260
+ * requestId: generateId(),
1261
+ * source: "user-dashboard",
1262
+ * priority: "high"
1112
1263
  * }
1113
1264
  * });
1114
1265
  *
1115
- * // Ignore certain query parameters
1116
- * const searchCall = callApi("/api/search?q=test&timestamp=123456", {
1117
- * dedupeKey: (context) => {
1118
- * const url = new URL(context.options.fullURL);
1119
- * url.searchParams.delete("timestamp"); // Remove volatile param
1120
- * return `search:${url.toString()}`;
1266
+ * // Use case: Feature flags
1267
+ * const client = callApi.create({
1268
+ * baseURL: "https://api.example.com",
1269
+ * meta: {
1270
+ * features: ["newUI", "betaFeature"],
1271
+ * experiment: "variantA"
1121
1272
  * }
1122
1273
  * });
1123
1274
  * ```
1124
- *
1125
- * @default Auto-generated from request details
1126
1275
  */
1127
- dedupeKey?: string | ((context: RequestContext) => string | undefined);
1276
+ meta?: InferSchemaOutput<TSchema["meta"], TCallApiContext["Meta"] extends DefaultMetaObject ? TCallApiContext["Meta"] : GlobalMeta>;
1277
+ }>;
1278
+ type InferAuthOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["auth"], {
1128
1279
  /**
1129
- * Strategy for handling duplicate requests. Can be a static string or callback function.
1130
- *
1131
- * **Available Strategies:**
1132
- * - `"cancel"`: Cancel previous request when new one starts (good for search)
1133
- * - `"defer"`: Share response between duplicate requests (good for config loading)
1134
- * - `"none"`: No deduplication, all requests execute independently
1135
- *
1136
- * @example
1137
- * ```ts
1138
- * // Static strategies
1139
- * const searchClient = createFetchClient({
1140
- * dedupeStrategy: "cancel" // Cancel previous searches
1141
- * });
1142
- *
1143
- * const configClient = createFetchClient({
1144
- * dedupeStrategy: "defer" // Share config across components
1145
- * });
1146
- *
1147
- * // Dynamic strategy based on request
1148
- * const smartClient = createFetchClient({
1149
- * dedupeStrategy: (context) => {
1150
- * return context.options.method === "GET" ? "defer" : "cancel";
1151
- * }
1152
- * });
1153
- *
1154
- * // Search-as-you-type with cancel strategy
1155
- * const handleSearch = async (query: string) => {
1156
- * try {
1157
- * const { data } = await callApi("/api/search", {
1158
- * method: "POST",
1159
- * body: { query },
1160
- * dedupeStrategy: "cancel",
1161
- * dedupeKey: "search" // Cancel previous searches, only latest one goes through
1162
- * });
1163
- *
1164
- * updateSearchResults(data);
1165
- * } catch (error) {
1166
- * if (error.name === "AbortError") {
1167
- * // Previous search cancelled - (expected behavior)
1168
- * return;
1169
- * }
1170
- * console.error("Search failed:", error);
1171
- * }
1172
- * };
1173
- *
1174
- * ```
1175
- *
1176
- * @default "cancel"
1280
+ * Automatically add an Authorization header value.
1281
+ *
1282
+ * Supports multiple authentication patterns:
1283
+ * - String: Direct authorization header value
1284
+ * - Auth object: Structured authentication configuration
1285
+ *
1286
+ * @example
1287
+ * ```ts
1288
+ * // Bearer auth
1289
+ * const response = await callMainApi({
1290
+ * url: "https://example.com/api/data",
1291
+ * auth: "123456",
1292
+ * });
1293
+ *
1294
+ * // Bearer auth
1295
+ * const response = await callMainApi({
1296
+ * url: "https://example.com/api/data",
1297
+ * auth: {
1298
+ * type: "Bearer",
1299
+ * value: "123456",
1300
+ * },
1301
+ })
1302
+ *
1303
+ * // Token auth
1304
+ * const response = await callMainApi({
1305
+ * url: "https://example.com/api/data",
1306
+ * auth: {
1307
+ * type: "Token",
1308
+ * value: "123456",
1309
+ * },
1310
+ * });
1311
+ *
1312
+ * // Basic auth
1313
+ * const response = await callMainApi({
1314
+ * url: "https://example.com/api/data",
1315
+ * auth: {
1316
+ * type: "Basic",
1317
+ * username: "username",
1318
+ * password: "password",
1319
+ * },
1320
+ * });
1321
+ *
1322
+ * ```
1323
+ */
1324
+ auth?: InferSchemaOutput<TSchema["auth"], AuthOption>;
1325
+ }>;
1326
+ type InferQueryOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["query"], {
1327
+ /**
1328
+ * Parameters to be appended to the URL (i.e: /:id)
1177
1329
  */
1178
- dedupeStrategy?: DedupeStrategyUnion | ((context: RequestContext) => DedupeStrategyUnion);
1330
+ query?: InferSchemaOutput<TSchema["query"], Query>;
1331
+ }>;
1332
+ type EmptyString = "";
1333
+ type EmptyTuple = readonly [];
1334
+ type StringTuple = readonly string[];
1335
+ type PossibleParamNamePatterns = `${string}:${string}` | `${string}{${string}}${"" | AnyString}`;
1336
+ type ExtractRouteParamNames<TCurrentRoute, TParamNamesAccumulator extends StringTuple = EmptyTuple> = TCurrentRoute extends PossibleParamNamePatterns ? TCurrentRoute extends `${infer TRoutePrefix}:${infer TParamAndRemainingRoute}` ? TParamAndRemainingRoute extends `${infer TCurrentParam}/${infer TRemainingRoute}` ? TCurrentParam extends EmptyString ? ExtractRouteParamNames<`${TRoutePrefix}/${TRemainingRoute}`, TParamNamesAccumulator> : ExtractRouteParamNames<`${TRoutePrefix}/${TRemainingRoute}`, [...TParamNamesAccumulator, TCurrentParam]> : TParamAndRemainingRoute extends `${infer TCurrentParam}` ? TCurrentParam extends EmptyString ? ExtractRouteParamNames<TRoutePrefix, TParamNamesAccumulator> : ExtractRouteParamNames<TRoutePrefix, [...TParamNamesAccumulator, TCurrentParam]> : ExtractRouteParamNames<TRoutePrefix, TParamNamesAccumulator> : TCurrentRoute extends `${infer TRoutePrefix}{${infer TCurrentParam}}${infer TRemainingRoute}` ? TCurrentParam extends EmptyString ? ExtractRouteParamNames<`${TRoutePrefix}${TRemainingRoute}`, TParamNamesAccumulator> : ExtractRouteParamNames<`${TRoutePrefix}${TRemainingRoute}`, [...TParamNamesAccumulator, TCurrentParam]> : TParamNamesAccumulator : TParamNamesAccumulator;
1337
+ type ConvertParamNamesToRecord<TParamNames extends StringTuple> = Prettify<TParamNames extends (readonly [infer TFirstParamName extends string, ...infer TRemainingParamNames extends StringTuple]) ? Record<TFirstParamName, AllowedQueryParamValues> & ConvertParamNamesToRecord<TRemainingParamNames> : NonNullableUnknown>;
1338
+ type ConvertParamNamesToTuple<TParamNames extends StringTuple> = TParamNames extends readonly [string, ...infer TRemainingParamNames extends StringTuple] ? [AllowedQueryParamValues, ...ConvertParamNamesToTuple<TRemainingParamNames>] : [];
1339
+ type InferParamsFromRoute<TCurrentRoute> = ExtractRouteParamNames<TCurrentRoute> extends StringTuple ? ExtractRouteParamNames<TCurrentRoute> extends EmptyTuple ? Params : ConvertParamNamesToRecord<ExtractRouteParamNames<TCurrentRoute>> | ConvertParamNamesToTuple<ExtractRouteParamNames<TCurrentRoute>> : Params;
1340
+ type MakeParamsOptionRequired<TParamsSchemaOption extends CallApiSchema["params"], TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string, TObject> = MakeSchemaOptionRequiredIfDefined<TParamsSchemaOption, Params extends InferParamsFromRoute<TCurrentRouteSchemaKey> ? TObject : TCurrentRouteSchemaKey extends Extract<keyof TBaseSchemaRoutes, TCurrentRouteSchemaKey> ? undefined extends InferSchemaOutput<TParamsSchemaOption, null> ? TObject : Required<TObject> : TObject>;
1341
+ type InferParamsOption<TSchema extends CallApiSchema, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string> = MakeParamsOptionRequired<TSchema["params"], TBaseSchemaRoutes, TCurrentRouteSchemaKey, {
1342
+ /**
1343
+ * Parameters to be appended to the URL (i.e: /:id)
1344
+ */
1345
+ params?: InferSchemaOutput<TSchema["params"], InferParamsFromRoute<TCurrentRouteSchemaKey>>;
1346
+ }>;
1347
+ type InferRequiredExtraOptions<TSchema extends CallApiSchema, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string, TCallApiContext extends CallApiContext> = InferAuthOption<TSchema> & InferMetaOption<TSchema, TCallApiContext> & InferParamsOption<TSchema, TBaseSchemaRoutes, TCurrentRouteSchemaKey> & InferQueryOption<TSchema>;
1348
+ type ResultModeOption<TErrorData, TResultMode extends ResultModeType> = TErrorData extends false ? {
1349
+ resultMode: "onlyData";
1350
+ } : TErrorData extends false | undefined ? {
1351
+ resultMode?: "onlyData";
1352
+ } : {
1353
+ resultMode?: TResultMode;
1354
+ };
1355
+ type ThrowOnErrorBoolean = boolean;
1356
+ type ThrowOnErrorType<TErrorData, TThrowOnError extends ThrowOnErrorBoolean> = TThrowOnError | ((context: ErrorContext<{
1357
+ ErrorData: TErrorData;
1358
+ }>) => TThrowOnError);
1359
+ type ThrowOnErrorOption<TErrorData, TThrowOnError extends ThrowOnErrorBoolean> = TErrorData extends false ? {
1360
+ throwOnError: true;
1361
+ } : TErrorData extends false | undefined ? {
1362
+ throwOnError?: true;
1363
+ } : {
1364
+ throwOnError?: ThrowOnErrorType<TErrorData, TThrowOnError>;
1365
+ };
1366
+ //#endregion
1367
+ //#region src/result.d.ts
1368
+ type ResponseParser<TData> = (text: string) => Awaitable<TData>;
1369
+ declare const getResponseType: <TData>(response: Response, responseParser: ResponseParser<TData>) => {
1370
+ arrayBuffer: () => Promise<ArrayBuffer>;
1371
+ blob: () => Promise<Blob>;
1372
+ formData: () => Promise<FormData>;
1373
+ json: () => Promise<TData>;
1374
+ stream: () => ReadableStream<Uint8Array<ArrayBuffer>> | null;
1375
+ text: () => Promise<string>;
1376
+ };
1377
+ type InitResponseTypeMap<TData = unknown> = ReturnType<typeof getResponseType<TData>>;
1378
+ type ResponseTypeUnion = keyof InitResponseTypeMap;
1379
+ type ResponseTypePlaceholder = null;
1380
+ type ResponseTypeType = ResponseTypePlaceholder | ResponseTypeUnion;
1381
+ type ResponseTypeMap<TData> = { [Key in keyof InitResponseTypeMap<TData>]: Awaited<ReturnType<InitResponseTypeMap<TData>[Key]>>; };
1382
+ type GetResponseType<TData, TResponseType extends ResponseTypeType, TComputedResponseTypeMap extends ResponseTypeMap<TData> = ResponseTypeMap<TData>> = null extends TResponseType ? TComputedResponseTypeMap["json"] : TResponseType extends NonNullable<ResponseTypeType> ? TComputedResponseTypeMap[TResponseType] : never;
1383
+ type CallApiResultSuccessVariant<TData> = {
1384
+ data: NoInferUnMasked<TData>;
1385
+ error: null;
1386
+ response: Response;
1387
+ };
1388
+ type PossibleJavaScriptError = UnmaskType<{
1389
+ errorData: false;
1390
+ message: string;
1391
+ name: "AbortError" | "Error" | "SyntaxError" | "TimeoutError" | "TypeError" | AnyString;
1392
+ originalError: DOMException | Error | SyntaxError | TypeError;
1393
+ }>;
1394
+ type PossibleHTTPError<TErrorData> = UnmaskType<{
1395
+ errorData: NoInferUnMasked<TErrorData>;
1396
+ message: string;
1397
+ name: "HTTPError";
1398
+ originalError: HTTPError;
1399
+ }>;
1400
+ type PossibleValidationError = UnmaskType<{
1401
+ errorData: ValidationError["errorData"];
1402
+ issueCause: ValidationError["issueCause"];
1403
+ message: string;
1404
+ name: "ValidationError";
1405
+ originalError: ValidationError;
1406
+ }>;
1407
+ type CallApiResultErrorVariant<TErrorData> = {
1408
+ data: null;
1409
+ error: PossibleHTTPError<TErrorData>;
1410
+ response: Response;
1411
+ } | {
1412
+ data: null;
1413
+ error: PossibleJavaScriptError;
1414
+ response: Response | null;
1415
+ } | {
1416
+ data: null;
1417
+ error: PossibleValidationError;
1418
+ response: Response | null;
1419
+ };
1420
+ type CallApiResultSuccessOrErrorVariant<TData, TError> = CallApiResultErrorVariant<TError> | CallApiResultSuccessVariant<TData>;
1421
+ type GetCallApiResult<TThrowOnError extends ThrowOnErrorBoolean, TResultWithException extends CallApiResultSuccessVariant<unknown>, TResultWithoutException extends CallApiResultSuccessOrErrorVariant<unknown, unknown>> = TThrowOnError extends true ? TResultWithException : TResultWithoutException;
1422
+ type ResultModeMap<TData = DefaultDataType, TErrorData = DefaultDataType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TComputedResult extends GetCallApiResult<TThrowOnError, CallApiResultSuccessVariant<TData>, CallApiResultSuccessOrErrorVariant<TData, TErrorData>> = GetCallApiResult<TThrowOnError, CallApiResultSuccessVariant<TData>, CallApiResultSuccessOrErrorVariant<TData, TErrorData>>> = UnmaskType<{
1423
+ all: TComputedResult;
1424
+ fetchApi: TComputedResult["response"];
1425
+ onlyData: TComputedResult["data"];
1426
+ onlyResponse: TComputedResult["response"];
1427
+ withoutResponse: Prettify<DistributiveOmit<TComputedResult, "response">>;
1428
+ }>;
1429
+ type ResultModePlaceholder = null;
1430
+ type ResultModeUnion = keyof ResultModeMap;
1431
+ type ResultModeType = ResultModePlaceholder | ResultModeUnion;
1432
+ type InferCallApiResult<TData, TErrorData, TResultMode extends ResultModeType, TThrowOnError extends ThrowOnErrorBoolean, TComputedResultModeMapWithException extends ResultModeMap<TData, TErrorData, true> = ResultModeMap<TData, TErrorData, true>, TComputedResultModeMapWithoutException extends ResultModeMap<TData, TErrorData, TThrowOnError> = ResultModeMap<TData, TErrorData, TThrowOnError>> = TErrorData extends false ? TComputedResultModeMapWithException["onlyData"] : TErrorData extends false | undefined ? TComputedResultModeMapWithException["onlyData"] : ResultModePlaceholder extends TResultMode ? TComputedResultModeMapWithoutException["all"] : TResultMode extends ResultModeUnion ? TComputedResultModeMapWithoutException[TResultMode] : never;
1433
+ type ErrorInfoOptions = Pick<CallApiExtraOptions, "cloneResponse" | "resultMode"> & {
1434
+ message?: string;
1179
1435
  };
1180
1436
  //#endregion
1181
- //#region src/types/options-types.d.ts
1182
- interface Register {}
1183
- type GlobalMeta = Register extends {
1184
- meta?: infer TMeta extends DefaultMetaObject;
1185
- } ? TMeta : DefaultMetaObject;
1186
- interface CallApiContext {
1187
- Data?: DefaultDataType;
1188
- ErrorData?: DefaultDataType;
1189
- InferredExtraOptions?: unknown;
1190
- Meta?: DefaultMetaObject;
1191
- ResultMode?: ResultModeType;
1192
- }
1193
- type GetCallApiContext<TCallApiContext extends CallApiContext> = TCallApiContext;
1194
- type GetCallApiContextRequired<TCallApiContext extends Required<CallApiContext>> = TCallApiContext;
1195
- type OverrideCallApiContext<TFullCallApiContext extends CallApiContext, TOverrideCallApiContext extends CallApiContext> = Omit<TFullCallApiContext, keyof TOverrideCallApiContext> & TOverrideCallApiContext;
1196
- type FetchSpecificKeysUnion = Exclude<(typeof fetchSpecificKeys)[number], "body" | "headers" | "method">;
1197
- type ModifiedRequestInit = RequestInit & {
1198
- duplex?: "half";
1199
- /**
1200
- * Custom fetch options that are merged into the final request configuration.
1201
- *
1202
- * This property is intended for environment-specific extensions not included in the standard web `RequestInit` type, such as `dispatcher` for Undici/Node.js or the `next` object for Next.js extended fetch.
1203
- */
1204
- extraFetchOptions?: RequestInit;
1437
+ //#region src/utils/external/body.d.ts
1438
+ type BodyType = NonNullable<CallApiRequestOptions["body"]>;
1439
+ declare const toSearchParams: <TSchema extends CallApiSchemaType<BodyType>>(data: InferSchemaOutput<TSchema>, schema?: TSchema) => URLSearchParams;
1440
+ declare const toQueryString: <TSchema extends CallApiSchemaType<BodyType>>(...parameters: Parameters<typeof toSearchParams<TSchema>>) => string;
1441
+ /**
1442
+ * @description Converts a plain object to FormData.
1443
+ *
1444
+ * Handles various data types:
1445
+ * - **Primitives** (string, number, boolean): Converted to strings
1446
+ * - **Blobs/Files**: Added directly to FormData
1447
+ * - **Arrays**: Each item is appended (allows multiple values for same key)
1448
+ * - **Objects**: JSON stringified before adding to FormData
1449
+ *
1450
+ * @example
1451
+ * ```ts
1452
+ * // Basic usage
1453
+ * const formData = toFormData({
1454
+ * name: "John",
1455
+ * age: 30,
1456
+ * active: true
1457
+ * });
1458
+ *
1459
+ * // With arrays
1460
+ * const formData = toFormData({
1461
+ * tags: ["javascript", "typescript"],
1462
+ * name: "John"
1463
+ * });
1464
+ *
1465
+ * // With files
1466
+ * const formData = toFormData({
1467
+ * avatar: fileBlob,
1468
+ * name: "John"
1469
+ * });
1470
+ *
1471
+ * // With nested objects (one level only)
1472
+ * const formData = toFormData({
1473
+ * user: { name: "John", age: 30 },
1474
+ * settings: { theme: "dark" }
1475
+ * });
1476
+ */
1477
+ declare const toFormData: <TSchema extends CallApiSchemaType<BodyType>>(data: InferSchemaOutput<TSchema>, schema?: TSchema) => FormData;
1478
+ //#endregion
1479
+ //#region src/utils/external/define.d.ts
1480
+ declare const defineSchema: <const TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, const TSchemaConfig extends CallApiSchemaConfig>(routes: TBaseSchemaRoutes, config?: Satisfies<TSchemaConfig, CallApiSchemaConfig>) => {
1481
+ routes: Writeable<TBaseSchemaRoutes, "deep">;
1482
+ config: Writeable<Satisfies<TSchemaConfig, CallApiSchemaConfig>, "deep">;
1205
1483
  };
1206
- type CallApiRequestOptions<TBody = Body> = {
1207
- /**
1208
- * Body of the request, can be a object or any other supported body type.
1209
- */
1210
- body?: TBody;
1211
- /**
1212
- * Headers to be used in the request.
1213
- */
1214
- headers?: HeadersOption;
1484
+ declare const defineSchemaRoutes: <const TSchemaRoutes extends BaseCallApiSchemaRoutes>(routes: TSchemaRoutes) => Writeable<typeof routes, "deep">;
1485
+ declare const defineMainSchema: <const TSchema extends CallApiSchema>(mainSchema: Satisfies<TSchema, CallApiSchema>) => Writeable<typeof mainSchema, "deep">;
1486
+ declare const defineSchemaConfig: <const TSchemaConfig extends CallApiSchemaConfig>(config: Satisfies<TSchemaConfig, CallApiSchemaConfig>) => Writeable<typeof config, "deep">;
1487
+ declare const definePluginWithContext: <const TCallApiContext extends CallApiContext = NonNullableUnknown>() => <const TPlugin extends CallApiPlugin<TCallApiContext>>(plugin: TPlugin) => NonNullableUnknown extends TCallApiContext ? Writeable<TPlugin, "deep"> : ContextTag<Writeable<TPlugin, "deep">, TCallApiContext>;
1488
+ declare const definePlugin: <const TPlugin extends CallApiPlugin<{}>>(plugin: TPlugin) => Writeable<TPlugin, "deep">;
1489
+ type BaseConfigObject = Exclude<BaseCallApiConfig, AnyFunction>;
1490
+ type BaseConfigFn = Extract<BaseCallApiConfig, AnyFunction>;
1491
+ type DefineBaseConfig = {
1492
+ <const TBaseConfig extends BaseConfigObject>(baseConfig: Satisfies<TBaseConfig, BaseConfigObject>): Writeable<typeof baseConfig, "deep">;
1493
+ <const TBaseConfig extends BaseConfigObject>(baseConfig: (...parameters: Parameters<BaseConfigFn>) => Writeable<TBaseConfig, "deep">): typeof baseConfig;
1494
+ };
1495
+ declare const defineBaseConfig: DefineBaseConfig;
1496
+ declare const defineInstanceConfig: <const TInstanceConfig extends CallApiConfig>(config: TInstanceConfig) => Writeable<typeof config, "deep">;
1497
+ declare const defineFallbackRouteSchema: <const TSchema extends CallApiSchema>(schema: TSchema) => {
1498
+ "@default": NonNullable<TSchema> extends Record<string | number | symbol, unknown> | unknown[] | readonly unknown[] ? Writeable<TSchema, "deep"> : TSchema;
1499
+ };
1500
+ //#endregion
1501
+ //#region src/utils/external/guards.d.ts
1502
+ declare const isHTTPError: <TErrorData>(error: CallApiResultErrorVariant<TErrorData>["error"] | null) => error is PossibleHTTPError<TErrorData>;
1503
+ declare const isHTTPErrorInstance: <TErrorData>(error: unknown) => error is HTTPError<TErrorData>;
1504
+ declare const isValidationError: (error: CallApiResultErrorVariant<unknown>["error"] | null) => error is PossibleValidationError;
1505
+ declare const isValidationErrorInstance: (error: unknown) => error is ValidationError;
1506
+ declare const isJavascriptError: (error: CallApiResultErrorVariant<unknown>["error"] | null) => error is PossibleJavaScriptError;
1507
+ //#endregion
1508
+ //#region src/utils/external/headers.d.ts
1509
+ declare const objectifyHeaders: (headers: CallApiRequestOptions["headers"]) => Record<string, string>;
1510
+ //#endregion
1511
+ //#region src/utils/external/helpers.d.ts
1512
+ declare const extraOptionsHelper: <TExtraOptions>() => ExtraOptionsWithContextTag<TExtraOptions>;
1513
+ declare const metaHelper: <TMeta extends DefaultMetaObject>() => MetaWithContextTag<TMeta>;
1514
+ //#endregion
1515
+ //#region src/retry.d.ts
1516
+ declare const defaultRetryStatusCodesLookup: () => Readonly<{
1517
+ 408: "Request Timeout";
1518
+ 409: "Conflict";
1519
+ 425: "Too Early";
1520
+ 429: "Too Many Requests";
1521
+ 500: "Internal Server Error";
1522
+ 502: "Bad Gateway";
1523
+ 503: "Service Unavailable";
1524
+ 504: "Gateway Timeout";
1525
+ }>;
1526
+ type RetryStatusCodes = UnmaskType<AnyNumber | keyof ReturnType<typeof defaultRetryStatusCodesLookup>>;
1527
+ type RetryCondition<TErrorData> = (context: ErrorContext<{
1528
+ ErrorData: TErrorData;
1529
+ }>) => Awaitable<boolean>;
1530
+ type CallApiLooseImpl = (initURL: InitURLOrURLObject, init?: CallApiConfig) => Promise<CallApiResultLoose<unknown, unknown>>;
1531
+ interface RetryOptions<TErrorData> {
1215
1532
  /**
1216
- * HTTP method for the request.
1217
- * @default "GET"
1533
+ * Tracks the number of times the request has already been retried internally
1534
+ * @internal
1535
+ * @deprecated **WARNING**: This property is used internally to track retries. Please abstain from reading or modifying it.
1218
1536
  */
1219
- method?: MethodUnion;
1220
- } & Pick<ModifiedRequestInit, FetchSpecificKeysUnion>;
1221
- type SharedExtraOptions<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TBody = Body, TComputedMergedPluginExtraOptions = Partial<InferPluginExtraOptions<TPluginArray> & InferSchemaOutput<TCallApiContext["InferredExtraOptions"], TCallApiContext["InferredExtraOptions"]>>, TComputedCallApiContext extends CallApiContext = OverrideCallApiContext<TCallApiContext, {
1222
- Data: TData;
1223
- ErrorData: TErrorData;
1224
- InferredExtraOptions: TComputedMergedPluginExtraOptions;
1225
- ResultMode: TResultMode;
1226
- }>> = DedupeOptions & HookConfigOptions & HooksOrHooksArray<NoInferUnMasked<TComputedCallApiContext>> & Middlewares<NoInferUnMasked<TComputedCallApiContext>> & RefetchOptions & ResultModeOption<TErrorData, TResultMode> & RetryOptions<TErrorData> & TComputedMergedPluginExtraOptions & ThrowOnErrorOption<TErrorData, TThrowOnError> & URLOptions & {
1537
+ readonly ["~retryAttemptCount"]?: number;
1227
1538
  /**
1228
- * Automatically add an Authorization header value.
1229
- *
1230
- * Supports multiple authentication patterns:
1231
- * - String: Direct authorization header value
1232
- * - Auth object: Structured authentication configuration
1233
- *
1234
- * ```
1539
+ * Use a valid `Retry-After` response header instead of the configured retry delay
1540
+ * @default false
1235
1541
  */
1236
- auth?: AuthOption;
1542
+ respectRetryAfter?: boolean;
1237
1543
  /**
1238
- * Custom function to serialize request body objects into strings.
1239
- *
1240
- * Useful for custom string serialization formats or when the default JSON
1241
- * serialization doesn't meet your needs.
1242
- *
1243
- * @example
1244
- * ```ts
1245
- * // XML serialization
1246
- * bodySerializer: (body) => {
1247
- * return `<request>${Object.entries(body)
1248
- * .map(([key, value]) => `<${key}>${value}</${key}>`)
1249
- * .join('')}</request>`;
1250
- * }
1251
- *
1252
- * // Custom JSON with specific formatting
1253
- * bodySerializer: (body) => JSON.stringify(body, null, 2)
1254
- * ```
1544
+ * Number of allowed retry attempts on HTTP errors
1545
+ * @default 0
1255
1546
  */
1256
- bodySerializer?: (body: TBody extends SerializableObject ? TBody : SerializableObject) => string;
1547
+ retryAttempts?: number;
1257
1548
  /**
1258
- * Custom function to transform the request body before it is passed to fetch.
1259
- *
1260
- * Useful for converting plain objects into formats like `FormData`,
1261
- * `URLSearchParams`, `Blob`, or other Fetch-compatible body values.
1262
- *
1263
- * Takes precedence over `bodySerializer`.
1264
- *
1265
- * @example
1266
- * ```ts
1267
- * bodyTransformer: ({ body }) => {
1268
- * const formData = new FormData();
1269
- *
1270
- * Object.entries(body).forEach(([key, value]) => {
1271
- * formData.append(key, String(value));
1272
- * });
1273
- *
1274
- * return formData;
1275
- * }
1276
- * ```
1549
+ * Callback whose return value determines if a request should be retried or not
1277
1550
  */
1278
- bodyTransformer?: (context: {
1279
- body: TBody;
1280
- headers: Headers;
1281
- }) => Body;
1551
+ retryCondition?: RetryCondition<TErrorData>;
1282
1552
  /**
1283
- * Whether to clone the response so it can be read multiple times.
1284
- *
1285
- * By default, response streams can only be consumed once. Enable this when you need
1286
- * to read the response in multiple places (e.g., in hooks and main code).
1287
- *
1288
- * @see https://developer.mozilla.org/en-US/docs/Web/API/Response/clone
1289
- * @default false
1553
+ * Delay between retries in milliseconds
1554
+ * @default 1000
1290
1555
  */
1291
- cloneResponse?: boolean;
1556
+ retryDelay?: number | ((currentAttemptCount: number) => number);
1292
1557
  /**
1293
- * Custom fetch implementation to replace the default fetch function.
1294
- *
1295
- * Useful for testing, adding custom behavior, or using alternative HTTP clients
1296
- * that implement the fetch API interface.
1297
- *
1298
- * @example
1299
- * ```ts
1300
- * // Use node-fetch in Node.js environments
1301
- * import fetch from 'node-fetch';
1302
- *
1303
- * // Mock fetch for testing
1304
- * customFetchImpl: async (url, init) => {
1305
- * return new Response(JSON.stringify({ mocked: true }), {
1306
- * status: 200,
1307
- * headers: { 'Content-Type': 'application/json' }
1308
- * });
1309
- * }
1310
- *
1311
- * // Add custom logging to all requests
1312
- * customFetchImpl: async (url, init) => {
1313
- * console.log(`Fetching: ${url}`);
1314
- * const response = await fetch(url, init);
1315
- * console.log(`Response: ${response.status}`);
1316
- * return response;
1317
- * }
1318
- *
1319
- * // Use with custom HTTP client
1320
- * customFetchImpl: async (url, init) => {
1321
- * // Convert to your preferred HTTP client format
1322
- * return await customHttpClient.request({
1323
- * url: url.toString(),
1324
- * method: init?.method || 'GET',
1325
- * headers: init?.headers,
1326
- * body: init?.body
1327
- * });
1328
- * }
1329
- * ```
1558
+ * Maximum delay in milliseconds. Only applies to exponential strategy
1559
+ * @default 10000
1330
1560
  */
1331
- customFetchImpl?: FetchImpl;
1561
+ retryMaxDelay?: number;
1332
1562
  /**
1333
- * Enable debug mode for the request.
1334
- *
1335
- * @default true
1563
+ * HTTP methods that are allowed to retry
1564
+ * @default ["GET", "POST"]
1336
1565
  */
1337
- debugMode?: boolean;
1566
+ retryMethods?: MethodUnion[];
1338
1567
  /**
1339
- * Default HTTP error message when server doesn't provide one.
1340
- *
1341
- * Can be a static string or a function that receives error context
1342
- * to generate dynamic error messages based on the response.
1343
- *
1344
- * @default "Failed to fetch data from server!"
1345
- *
1346
- * @example
1347
- * ```ts
1348
- * // Static error message
1349
- * defaultHTTPErrorMessage: "API request failed. Please try again."
1350
- *
1351
- * // Dynamic error message based on status code
1352
- * defaultHTTPErrorMessage: ({ response }) => {
1353
- * switch (response.status) {
1354
- * case 401: return "Authentication required. Please log in.";
1355
- * case 403: return "Access denied. Insufficient permissions.";
1356
- * case 404: return "Resource not found.";
1357
- * case 429: return "Too many requests. Please wait and try again.";
1358
- * case 500: return "Server error. Please contact support.";
1359
- * default: return `Request failed with status ${response.status}`;
1360
- * }
1361
- * }
1362
- *
1363
- * // Include error data in message
1364
- * defaultHTTPErrorMessage: ({ errorData, response }) => {
1365
- * const userMessage = errorData?.message || "Unknown error occurred";
1366
- * return `${userMessage} (Status: ${response.status})`;
1367
- * }
1368
- * ```
1568
+ * HTTP status codes that trigger a retry
1369
1569
  */
1370
- defaultHTTPErrorMessage?: string | ((context: Pick<HTTPError<TErrorData>, "errorData" | "response">) => string);
1570
+ retryStatusCodes?: RetryStatusCodes[];
1371
1571
  /**
1372
- * Optional metadata field for associating additional information with requests.
1373
- *
1374
- * Useful for logging, tracing, or handling specific cases in shared interceptors.
1375
- * The meta object is passed through to all hooks and can be accessed in error handlers.
1376
- *
1377
- * @example
1378
- * ```ts
1379
- * const callMainApi = callApi.create({
1380
- * baseURL: "https://main-api.com",
1381
- * onResponseError: ({ response, options }) => {
1382
- * if (options.meta?.userId) {
1383
- * console.error(`User ${options.meta.userId} made an error`);
1384
- * }
1385
- * },
1386
- * });
1387
- *
1388
- * const response = await callMainApi({
1389
- * url: "https://example.com/api/data",
1390
- * meta: { userId: "123" },
1391
- * });
1392
- *
1393
- * // Use case: Request tracking
1394
- * const result = await callMainApi({
1395
- * url: "https://example.com/api/data",
1396
- * meta: {
1397
- * requestId: generateId(),
1398
- * source: "user-dashboard",
1399
- * priority: "high"
1400
- * }
1401
- * });
1402
- *
1403
- * // Use case: Feature flags
1404
- * const client = callApi.create({
1405
- * baseURL: "https://api.example.com",
1406
- * meta: {
1407
- * features: ["newUI", "betaFeature"],
1408
- * experiment: "variantA"
1409
- * }
1410
- * });
1411
- * ```
1572
+ * Strategy to use when retrying
1573
+ * @default "linear"
1412
1574
  */
1413
- meta?: TCallApiContext["Meta"] extends DefaultMetaObject ? TCallApiContext["Meta"] : DefaultCallApiContext["Meta"];
1575
+ retryStrategy?: "exponential" | "linear";
1576
+ }
1577
+ type RetryManagerContext = {
1578
+ callApi: CallApiLooseImpl;
1579
+ callApiArgs: {
1580
+ config: CallApiConfig;
1581
+ initURL: InitURLOrURLObject;
1582
+ };
1583
+ error: unknown;
1584
+ errorContext: ErrorContext;
1585
+ hookInfo: ExecuteHookInfo;
1586
+ removeDedupeCacheEntry: () => void;
1587
+ };
1588
+ //#endregion
1589
+ //#region src/refetch.d.ts
1590
+ declare const refetchAttemptTrackerSymbol: unique symbol;
1591
+ interface RefetchOptions {
1414
1592
  /**
1415
- * Custom function to parse response strings into actual value instead of the default response.json().
1416
- *
1417
- * Useful when you need custom parsing logic for specific response formats.
1418
- *
1419
- * @example
1420
- * ```ts
1421
- * responseParser: (text) => {
1422
- * return JSON.parse(text);
1423
- * }
1424
- *
1425
- * // Parse XML responses
1426
- * responseParser: (text) => {
1427
- * const parser = new DOMParser();
1428
- * const doc = parser.parseFromString(text, "text/xml");
1429
- * return xmlToObject(doc);
1430
- * }
1431
- *
1432
- * // Parse CSV responses
1433
- * responseParser: (text) => {
1434
- * const lines = text.split('\n');
1435
- * const headers = lines[0].split(',');
1436
- * const data = lines.slice(1).map(line => {
1437
- * const values = line.split(',');
1438
- * return headers.reduce((obj, header, index) => {
1439
- * obj[header] = values[index];
1440
- * return obj;
1441
- * }, {});
1442
- * });
1443
- * return data;
1444
- * }
1445
- *
1446
- * ```
1593
+ * Tracks if the refetching of the request should be attempted
1594
+ * @internal
1595
+ * @deprecated **WARNING**: This property is used internally to track retries. Please abstain from reading or modifying it.
1447
1596
  */
1448
- responseParser?: ResponseParser<TData>;
1597
+ [refetchAttemptTrackerSymbol]?: boolean;
1598
+ }
1599
+ type RefetchFn = () => void;
1600
+ type RefetchManagerResult = {
1601
+ handleRefetch: () => Promise<CallApiResultLoose<unknown, unknown>> | null;
1602
+ refetch: RefetchFn;
1603
+ };
1604
+ declare const createRefetchManager: (ctx: Pick<RetryManagerContext, "callApi" | "callApiArgs" | "removeDedupeCacheEntry"> & {
1605
+ options: CallApiExtraOptions;
1606
+ }) => RefetchManagerResult;
1607
+ type RefetchFnOption = Pick<ReturnType<typeof createRefetchManager>, "refetch">;
1608
+ //#endregion
1609
+ //#region src/stream.d.ts
1610
+ type StreamProgressEvent = {
1449
1611
  /**
1450
- * Expected response type, determines how the response body is parsed.
1451
- *
1452
- * Different response types trigger different parsing methods:
1453
- * - **"json"**: Parses as JSON using response.json()
1454
- * - **"text"**: Returns as plain text using response.text()
1455
- * - **"blob"**: Returns as Blob using response.blob()
1456
- * - **"arrayBuffer"**: Returns as ArrayBuffer using response.arrayBuffer()
1457
- * - **"stream"**: Returns the response body stream directly
1458
- *
1459
- * @default "json"
1460
- *
1461
- * @example
1462
- * ```ts
1463
- * // JSON API responses (default)
1464
- * responseType: "json"
1465
- *
1466
- * // Plain text responses
1467
- * responseType: "text"
1468
- * // Usage: const csvData = await callApi("/export.csv", { responseType: "text" });
1469
- *
1470
- * // File downloads
1471
- * responseType: "blob"
1472
- * // Usage: const file = await callApi("/download/file.pdf", { responseType: "blob" });
1473
- *
1474
- * // Binary data
1475
- * responseType: "arrayBuffer"
1476
- * // Usage: const buffer = await callApi("/binary-data", { responseType: "arrayBuffer" });
1612
+ * Current chunk of data being streamed.
1477
1613
  *
1478
- * // Streaming responses
1479
- * responseType: "stream"
1480
- * // Usage: const stream = await callApi("/large-dataset", { responseType: "stream" });
1481
- * ```
1614
+ * Will be `null` on the final completion tick (when progress reaches 100%).
1482
1615
  */
1483
- responseType?: TResponseType;
1616
+ chunk: Uint8Array | null;
1484
1617
  /**
1485
- * Dictates how CallApi processes and returns the final result
1486
- *
1487
- - **"all"** (default): Returns `{ data, error, response }`. Standard lifecycle.
1488
- - **"onlyData"**: Returns only the data from the response.
1489
- - **"onlyResponse"**: Returns only the `Response` object.
1490
- - **"fetchApi"**: Also returns only the `Response` object, but also skips parsing of the response body internally and data/errorData schema validation.
1491
- - **"withoutResponse"**: Returns `{ data, error }`. Standard lifecycle, but omits the `response` property.
1492
- *
1493
- *
1494
- * **Note:**
1495
- * By default, simplified modes (`"onlyData"`, `"onlyResponse"`, `"fetchApi"`) do not throw errors.
1496
- * Success/failure should be handled via hooks or by checking the return value (e.g., `if (data)` or `if (response?.ok)`).
1497
- * To force an exception instead, set `throwOnError: true`.
1498
- *
1499
- *
1500
- * @default "all"
1501
- *
1502
- */
1503
- resultMode?: TResultMode;
1618
+ * Progress in percentage
1619
+ */
1620
+ progress: number;
1504
1621
  /**
1505
- * Controls whether errors are thrown as exceptions or returned in the result.
1622
+ * Total size of data in bytes
1623
+ */
1624
+ totalBytes: number;
1625
+ /**
1626
+ * Amount of data transferred so far
1627
+ */
1628
+ transferredBytes: number;
1629
+ };
1630
+ //#endregion
1631
+ //#region src/hooks.d.ts
1632
+ type CallApiRequestOptionsForHooks = Omit<CallApiRequestOptions, "headers"> & {
1633
+ headers: Partial<Record<"Authorization" | "Content-Type" | CommonRequestHeaders, string>>;
1634
+ };
1635
+ type CallApiExtraOptionsForHooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Hooks & Omit<CallApiExtraOptions<TCallApiContext>, keyof Hooks> & Pick<RefetchFnOption, "refetch">;
1636
+ interface Hooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
1637
+ /**
1638
+ * Hook called when any error occurs within the request/response lifecycle.
1506
1639
  *
1507
- * Can be a boolean or a function that receives the error and decides whether to throw.
1508
- * When true, errors are thrown as exceptions instead of being returned in the result object.
1640
+ * This is a unified error handler that catches both request errors (network failures,
1641
+ * timeouts, etc.) and response errors (HTTP error status codes). It's essentially
1642
+ * a combination of `onRequestError` and `onResponseError` hooks.
1509
1643
  *
1510
- * @default false
1644
+ * @param context - Error context containing error details, request info, and response (if available)
1645
+ * @returns Promise or void - Hook can be async or sync
1646
+ */
1647
+ onError?: (context: ErrorContext<TCallApiContext>) => Awaitable<unknown>;
1648
+ /**
1649
+ * Hook called before the HTTP request is sent and before any internal processing of the request object begins.
1511
1650
  *
1512
- * @example
1513
- * ```ts
1514
- * // Always throw errors
1515
- * throwOnError: true
1516
- * try {
1517
- * const data = await callApi("/users");
1518
- * console.log("Users:", data);
1519
- * } catch (error) {
1520
- * console.error("Request failed:", error);
1521
- * }
1651
+ * This is the ideal place to modify request headers, add authentication,
1652
+ * implement request logging, or perform any setup before the network call.
1522
1653
  *
1523
- * // Never throw errors (default)
1524
- * throwOnError: false
1525
- * const { data, error } = await callApi("/users");
1526
- * if (error) {
1527
- * console.error("Request failed:", error);
1528
- * }
1654
+ * @param context - Request context with mutable request object and configuration
1655
+ * @returns Promise or void - Hook can be async or sync
1529
1656
  *
1530
- * // Conditionally throw based on error type
1531
- * throwOnError: (error) => {
1532
- * // Throw on client errors (4xx) but not server errors (5xx)
1533
- * return error.response?.status >= 400 && error.response?.status < 500;
1534
- * }
1657
+ */
1658
+ onRequest?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
1659
+ /**
1660
+ * Hook called when an error occurs during the fetch request itself.
1535
1661
  *
1536
- * // Throw only on specific status codes
1537
- * throwOnError: (error) => {
1538
- * const criticalErrors = [401, 403, 404];
1539
- * return criticalErrors.includes(error.response?.status);
1540
- * }
1662
+ * This handles network-level errors like connection failures, timeouts,
1663
+ * DNS resolution errors, or other issues that prevent getting an HTTP response.
1664
+ * Note that HTTP error status codes (4xx, 5xx) are handled by `onResponseError`.
1541
1665
  *
1542
- * // Throw on validation errors but not network errors
1543
- * throwOnError: (error) => {
1544
- * return error.type === "validation";
1545
- * }
1546
- * ```
1666
+ * @param context - Request error context with error details and null response
1667
+ * @returns Promise or void - Hook can be async or sync
1547
1668
  */
1548
- throwOnError?: ThrowOnErrorType<TErrorData, TThrowOnError>;
1669
+ onRequestError?: (context: RequestErrorContext<TCallApiContext>) => Awaitable<unknown>;
1549
1670
  /**
1550
- * Request timeout in milliseconds. Request will be aborted if it takes longer.
1671
+ * Hook called just before the HTTP request is sent and after the request has been processed.
1551
1672
  *
1552
- * Useful for preventing requests from hanging indefinitely and providing
1553
- * better user experience with predictable response times.
1673
+ * @param context - Request context with mutable request object and configuration
1674
+ */
1675
+ onRequestReady?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
1676
+ /**
1677
+ * Hook called during upload stream progress tracking.
1554
1678
  *
1555
- * @example
1556
- * ```ts
1557
- * // 5 second timeout
1558
- * timeout: 5000
1679
+ * This hook is triggered when uploading data (like file uploads) and provides
1680
+ * progress information about the upload. Useful for implementing progress bars
1681
+ * or upload status indicators.
1559
1682
  *
1560
- * // Different timeouts for different endpoints
1561
- * const quickApi = createFetchClient({ timeout: 3000 }); // 3s for fast endpoints
1562
- * const slowApi = createFetchClient({ timeout: 30000 }); // 30s for slow operations
1683
+ * @param context - Request stream context with progress event and request instance
1684
+ * @returns Promise or void - Hook can be async or sync
1563
1685
  *
1564
- * // Per-request timeout override
1565
- * await callApi("/quick-data", { timeout: 1000 });
1566
- * await callApi("/slow-report", { timeout: 60000 });
1686
+ */
1687
+ onRequestStream?: (context: RequestStreamContext<TCallApiContext>) => Awaitable<unknown>;
1688
+ /**
1689
+ * Hook called when any HTTP response is received from the API.
1690
+ *
1691
+ * This hook is triggered for both successful (2xx) and error (4xx, 5xx) responses.
1692
+ * It's useful for response logging, metrics collection, or any processing that
1693
+ * should happen regardless of response status.
1694
+ *
1695
+ * @param context - Response context with either success data or error information
1696
+ * @returns Promise or void - Hook can be async or sync
1567
1697
  *
1568
- * // No timeout (use with caution)
1569
- * timeout: 0
1570
- * ```
1571
1698
  */
1572
- timeout?: number;
1573
- };
1574
- type BaseCallApiExtraOptions<TBaseCallApiContext extends CallApiContext = DefaultCallApiContext, TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeType = ResultModeType, TBaseThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeType = ResponseTypeType, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig = BaseCallApiSchemaAndConfig> = SharedExtraOptions<TBaseCallApiContext, TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBasePluginArray> & {
1699
+ onResponse?: (context: ResponseContext<TCallApiContext>) => Awaitable<unknown>;
1575
1700
  /**
1576
- * Array of base CallApi plugins to extend library functionality.
1701
+ * Hook called when an HTTP error response (4xx, 5xx) is received from the API.
1577
1702
  *
1578
- * Base plugins are applied to all instances created from this base configuration
1579
- * and provide foundational functionality like authentication, logging, or caching.
1703
+ * This handles server-side errors where an HTTP response was successfully received
1704
+ * but indicates an error condition. Different from `onRequestError` which handles
1705
+ * network-level failures.
1580
1706
  *
1581
- * @example
1582
- * ```ts
1583
- * // Add logging plugin
1707
+ * @param context - Response error context with HTTP error details and response
1708
+ * @returns Promise or void - Hook can be async or sync
1709
+ */
1710
+ onResponseError?: (context: ResponseErrorContext<TCallApiContext>) => Awaitable<unknown>;
1711
+ /**
1712
+ * Hook called during download stream progress tracking.
1584
1713
  *
1585
- * // Create base client with common plugins
1586
- * const callApi = createFetchClient({
1587
- * baseURL: "https://api.example.com",
1588
- * plugins: [loggerPlugin({ enabled: true })]
1589
- * });
1714
+ * This hook is triggered when downloading data (like file downloads) and provides
1715
+ * progress information about the download. Useful for implementing progress bars
1716
+ * or download status indicators.
1590
1717
  *
1591
- * // All requests inherit base plugins
1592
- * await callApi("/users");
1593
- * await callApi("/posts");
1718
+ * @param context - Response stream context with progress event and response
1719
+ * @returns Promise or void - Hook can be async or sync
1594
1720
  *
1595
- * ```
1596
1721
  */
1597
- plugins?: TBasePluginArray;
1722
+ onResponseStream?: (context: ResponseStreamContext<TCallApiContext>) => Awaitable<unknown>;
1598
1723
  /**
1599
- * Base validation schemas for the client configuration.
1724
+ * Hook called when a request is being retried.
1725
+ *
1726
+ * This hook is triggered before each retry attempt, providing information about
1727
+ * the previous failure and the current retry attempt number. Useful for implementing
1728
+ * custom retry logic, exponential backoff, or retry logging.
1729
+ *
1730
+ * @param context - Retry context with error details and retry attempt count
1731
+ * @returns Promise or void - Hook can be async or sync
1600
1732
  *
1601
- * Defines validation rules for requests and responses that apply to all
1602
- * instances created from this base configuration. Provides type safety
1603
- * and runtime validation for API interactions.
1604
1733
  */
1605
- schema?: TBaseSchemaAndConfig;
1734
+ onRetry?: (context: RetryContext<TCallApiContext>) => Awaitable<unknown>;
1606
1735
  /**
1607
- * Controls which configuration parts skip automatic merging between base and instance configs.
1736
+ * Hook called when a successful response (2xx status) is received from the API.
1608
1737
  *
1609
- * By default, CallApi automatically merges base configuration with instance configuration.
1610
- * This option allows you to disable automatic merging for specific parts when you need
1611
- * manual control over how configurations are combined.
1738
+ * This hook is triggered only for successful responses and provides access to
1739
+ * the parsed response data. Ideal for success logging, caching, or post-processing
1740
+ * of successful API responses.
1612
1741
  *
1613
- * @enum
1614
- * - **"all"**: Disables automatic merging for both request options and extra options
1615
- * - **"options"**: Disables automatic merging of extra options only (hooks, plugins, etc.)
1616
- * - **"request"**: Disables automatic merging of request options only (headers, body, etc.)
1742
+ * @param context - Success context with parsed response data and response object
1743
+ * @returns Promise or void - Hook can be async or sync
1617
1744
  *
1618
- * @example
1619
- * ```ts
1620
- * // Skip all automatic merging - full manual control
1621
- * const client = callApi.create((ctx) => ({
1622
- * skipAutoMergeFor: "all",
1745
+ */
1746
+ onSuccess?: (context: SuccessContext<TCallApiContext>) => Awaitable<unknown>;
1747
+ /**
1748
+ * Hook called when a validation error occurs.
1623
1749
  *
1624
- * // Manually decide what to merge
1625
- * baseURL: ctx.options.baseURL, // Keep base URL
1626
- * timeout: 5000, // Override timeout
1627
- * headers: {
1628
- * ...ctx.request.headers, // Merge headers manually
1629
- * "X-Custom": "value" // Add custom header
1630
- * }
1631
- * }));
1750
+ * This hook is triggered when request or response data fails validation against
1751
+ * a defined schema. It provides access to the validation error details and can
1752
+ * be used for custom error handling, logging, or fallback behavior.
1632
1753
  *
1633
- * // Skip options merging - manual plugin/hook control
1634
- * const client = callApi.create((ctx) => ({
1635
- * skipAutoMergeFor: "options",
1754
+ * @param context - Validation error context with error details and response (if available)
1755
+ * @returns Promise or void - Hook can be async or sync
1636
1756
  *
1637
- * // Manually control which plugins to use
1638
- * plugins: [
1639
- * ...ctx.options.plugins?.filter(p => p.name !== "unwanted") || [],
1640
- * customPlugin
1641
- * ],
1757
+ */
1758
+ onValidationError?: (context: ValidationErrorContext<TCallApiContext>) => Awaitable<unknown>;
1759
+ }
1760
+ type HooksOrHooksArray<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> = { [Key in keyof Hooks<TCallApiContext>]: Hooks<TCallApiContext>[Key] | Array<Hooks<TCallApiContext>[Key]>; };
1761
+ interface HookConfigOptions {
1762
+ /**
1763
+ * Controls the execution mode of all composed hooks (main + plugin hooks).
1642
1764
  *
1643
- * // Request options still auto-merge
1644
- * method: "POST"
1645
- * }));
1765
+ * - **"parallel"**: All hooks execute simultaneously via Promise.all() for better performance
1766
+ * - **"sequential"**: All hooks execute one by one in registration order via await in a loop
1646
1767
  *
1647
- * // Skip request merging - manual request control
1648
- * const client = callApi.create((ctx) => ({
1649
- * skipAutoMergeFor: "request",
1768
+ * This affects how ALL hooks execute together, regardless of their source (main or plugin).
1650
1769
  *
1651
- * // Extra options still auto-merge (plugins, hooks, etc.)
1770
+ * @default "parallel"
1771
+ */
1772
+ hooksExecutionMode?: "parallel" | "sequential";
1773
+ }
1774
+ type RequestContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = {
1775
+ /**
1776
+ * Base configuration object passed to createFetchClient.
1652
1777
  *
1653
- * // Manually control request options
1654
- * headers: {
1655
- * "Content-Type": "application/json",
1656
- * // Don't merge base headers
1657
- * },
1658
- * method: ctx.request.method || "GET"
1659
- * }));
1778
+ * Contains the foundational configuration that applies to all requests
1779
+ * made by this client instance, such as baseURL, default headers, and
1780
+ * global options.
1781
+ */
1782
+ baseConfig: Exclude<BaseCallApiConfig, AnyFunction>;
1783
+ /**
1784
+ * Instance-specific configuration object passed to the callApi instance.
1660
1785
  *
1661
- * // Use case: Conditional merging based on request
1662
- * const client = createFetchClient((ctx) => ({
1663
- * skipAutoMergeFor: "options",
1786
+ * Contains configuration specific to this particular API call, which
1787
+ * can override or extend the base configuration.
1788
+ */
1789
+ config: CallApiConfig;
1790
+ /**
1791
+ * Merged options combining base config, instance config, and default options.
1664
1792
  *
1665
- * // Only use auth plugin for protected routes
1666
- * plugins: ctx.initURL.includes("/protected/")
1667
- * ? [...(ctx.options.plugins || []), authPlugin]
1668
- * : ctx.options.plugins?.filter(p => p.name !== "auth") || []
1669
- * }));
1670
- * ```
1793
+ * This is the final resolved configuration that will be used for the request,
1794
+ * with proper precedence applied (instance > base > defaults).
1671
1795
  */
1672
- skipAutoMergeFor?: "all" | "options" | "request";
1796
+ options: CallApiExtraOptionsForHooks<TCallApiContext>;
1797
+ /**
1798
+ * Merged request object ready to be sent.
1799
+ *
1800
+ * Contains the final request configuration including URL, method, headers,
1801
+ * body, and other fetch options. This object can be modified in onRequest
1802
+ * hooks to customize the outgoing request.
1803
+ */
1804
+ request: CallApiRequestOptionsForHooks;
1673
1805
  };
1674
- type GetBaseSchemaRoutes<TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig> = Writeable<TBaseSchemaAndConfig["routes"], "deep">;
1675
- type GetBaseSchemaConfig<TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig> = Writeable<NonNullable<TBaseSchemaAndConfig["config"]>, "deep">;
1676
- type InferExtendSchemaContext<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string> = {
1677
- baseSchemaRoutes: TBaseSchemaRoutes;
1678
- currentRouteSchema: GetCurrentRouteSchema<TBaseSchemaRoutes, TCurrentRouteSchemaKey>;
1679
- currentRouteSchemaKey: TCurrentRouteSchemaKey;
1806
+ type SuccessContext<TCallApiContext extends Pick<CallApiContext, "Data" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = DistributiveOmit<CallApiResultSuccessVariant<TCallApiContext["Data"]>, "error"> & RequestContext<TCallApiContext>;
1807
+ type ResponseContext<TCallApiContext extends Pick<CallApiContext, "Data" | "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & (Prettify<CallApiResultSuccessVariant<TCallApiContext["Data"]>> | Prettify<Extract<CallApiResultErrorVariant<TCallApiContext["ErrorData"]>, {
1808
+ error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
1809
+ }>>);
1810
+ type RequestStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
1811
+ event: StreamProgressEvent;
1812
+ requestInstance: Request;
1680
1813
  };
1681
- type GetExtendSchemaConfigContext<TBaseSchemaConfig extends CallApiSchemaConfig> = {
1682
- baseSchemaConfig: TBaseSchemaConfig;
1814
+ type ResponseStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
1815
+ event: StreamProgressEvent;
1816
+ response: Response;
1683
1817
  };
1684
- type InferExtendPluginContext<TBasePluginArray extends CallApiPlugin[]> = {
1685
- basePlugins: TBasePluginArray;
1818
+ type ErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = DistributiveOmit<CallApiResultErrorVariant<TCallApiContext["ErrorData"]>, "data"> & RequestContext<TCallApiContext>;
1819
+ type ValidationErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
1820
+ error: PossibleValidationError;
1821
+ }> & RequestContext<TCallApiContext>;
1822
+ type RequestErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
1823
+ error: PossibleJavaScriptError;
1824
+ }> & RequestContext<TCallApiContext>;
1825
+ type ResponseErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
1826
+ error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
1827
+ }> & RequestContext<TCallApiContext>;
1828
+ type RetryContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = ErrorContext<TCallApiContext> & {
1829
+ retryAttemptCount: number;
1686
1830
  };
1687
- type CallApiExtraOptions<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes = BaseCallApiSchemaRoutes, TSchema extends CallApiSchema = CallApiSchema, TBaseSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TCurrentRouteSchemaKey extends string = string, TBody extends InferSchemaOutput<TSchema["body"], Body> = InferSchemaOutput<TSchema["body"], Body>> = SharedExtraOptions<TCallApiContext, TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TPluginArray, TBody> & {
1831
+ type ExecuteHookInfo = {
1832
+ errorInfoOptions: ErrorInfoOptions;
1833
+ shouldThrowOnError: boolean | undefined;
1834
+ };
1835
+ //#endregion
1836
+ //#region src/plugins.d.ts
1837
+ type PluginSetupContext<TCallApiContext extends CallApiContext = DefaultCallApiContext> = RequestContext<TCallApiContext> & ReturnType<typeof getCurrentRouteSchemaKeyAndMainInitURL>;
1838
+ type PluginInitResult<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Partial<Omit<PluginSetupContext<TCallApiContext>, "initURL" | "request"> & {
1839
+ initURL: InitURLOrURLObject;
1840
+ request: CallApiRequestOptions;
1841
+ }>;
1842
+ type GetDefaultDataTypeForPlugins<TData> = DefaultDataType extends TData ? never : TData;
1843
+ type PluginHooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> = HooksOrHooksArray<OverrideCallApiContext<TCallApiContext, {
1844
+ Data: GetDefaultDataTypeForPlugins<TCallApiContext["Data"]>;
1845
+ ErrorData: GetDefaultDataTypeForPlugins<TCallApiContext["ErrorData"]>;
1846
+ }>>;
1847
+ type PluginMiddlewares<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Middlewares<OverrideCallApiContext<TCallApiContext, {
1848
+ Data: GetDefaultDataTypeForPlugins<TCallApiContext["Data"]>;
1849
+ ErrorData: GetDefaultDataTypeForPlugins<TCallApiContext["ErrorData"]>;
1850
+ }>>;
1851
+ interface CallApiPlugin<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
1688
1852
  /**
1689
- * Array of instance-specific CallApi plugins or a function to configure plugins.
1690
- *
1691
- * Instance plugins are added to the base plugins and provide functionality
1692
- * specific to this particular API instance. Can be a static array or a function
1693
- * that receives base plugins and returns the instance plugins.
1694
- *
1853
+ * A description for the plugin
1695
1854
  */
1696
- plugins?: TPluginArray | ((context: InferExtendPluginContext<TBasePluginArray>) => TPluginArray);
1855
+ description?: string;
1697
1856
  /**
1698
- * For instance-specific validation schemas
1699
- *
1700
- * Defines validation rules specific to this API instance, extending or overriding the base schema.
1701
- *
1702
- * Can be a static schema object or a function that receives base schema context and returns instance schemas.
1703
- *
1857
+ * Defines additional options that can be passed to callApi
1704
1858
  */
1705
- schema?: TSchema | ((context: InferExtendSchemaContext<TBaseSchemaRoutes, TCurrentRouteSchemaKey>) => TSchema);
1859
+ extraOptionsDef?: ExtraOptionsWithContextTag<DefaultInferredExtraOptions>;
1706
1860
  /**
1707
- * Instance-specific schema configuration or a function to configure schema behavior.
1708
- *
1709
- * Controls how validation schemas are applied and behave for this specific API instance.
1710
- * Can override base schema configuration or extend it with instance-specific validation rules.
1711
- *
1861
+ * Hooks for the plugin
1712
1862
  */
1713
- schemaConfig?: TSchemaConfig | ((context: GetExtendSchemaConfigContext<TBaseSchemaConfig>) => TSchemaConfig);
1714
- };
1715
- type InstanceContext = {
1716
- initURL: string;
1717
- options: CallApiExtraOptions;
1718
- request: CallApiRequestOptions;
1719
- };
1720
- type BaseCallApiConfig<TBaseCallApiContext extends CallApiContext = DefaultCallApiContext, TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeType = ResultModeType, TBaseThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeType = ResponseTypeType, TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig = BaseCallApiSchemaAndConfig, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedBaseConfig = BaseCallApiExtraOptions<TBaseCallApiContext, TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBasePluginArray, TBaseSchemaAndConfig>> = (CallApiRequestOptions & TComputedBaseConfig) | ((context: InstanceContext) => CallApiRequestOptions & TComputedBaseConfig);
1721
- type CallApiConfig<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes = BaseCallApiSchemaRoutes, TSchema extends CallApiSchema = CallApiSchema, TBaseSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TInitURL extends InitURLOrURLObject = InitURLOrURLObject, TCurrentRouteSchemaKey extends string = string, TBody extends InferSchemaOutput<TSchema["body"], Body> = InferSchemaOutput<TSchema["body"], Body>, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TPluginArray extends CallApiPlugin[] = DefaultPluginArray> = InferExtraOptions<TSchema, TBaseSchemaRoutes, TCurrentRouteSchemaKey, TCallApiContext> & InferRequestOptions<TSchema, TInitURL, TBody> & Omit<CallApiExtraOptions<TCallApiContext, TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TBasePluginArray, TPluginArray, TBaseSchemaRoutes, TSchema, TBaseSchemaConfig, TSchemaConfig, TCurrentRouteSchemaKey, TBody>, keyof InferExtraOptions<CallApiSchema, BaseCallApiSchemaRoutes, string, CallApiContext>> & Omit<CallApiRequestOptions<TBody>, keyof InferRequestOptions<CallApiSchema, string, TBody>>;
1722
- type CallApiParameters<TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TCallApiContext extends CallApiContext = DefaultCallApiContext, TThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes = BaseCallApiSchemaRoutes, TSchema extends CallApiSchema = CallApiSchema, TBaseSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TInitURL extends InitURLOrURLObject = InitURLOrURLObject, TCurrentRouteSchemaKey extends string = string, TBody extends InferSchemaOutput<TSchema["body"], Body> = InferSchemaOutput<TSchema["body"], Body>, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedConfig = CallApiConfig<TCallApiContext, TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TBaseSchemaRoutes, TSchema, TBaseSchemaConfig, TSchemaConfig, TInitURL, TCurrentRouteSchemaKey, TBody, TBasePluginArray, TPluginArray>, TComputedRequiredOptions = InferExtraOptions<TSchema, TBaseSchemaRoutes, TCurrentRouteSchemaKey, TCallApiContext> & InferRequestOptions<TSchema, TInitURL, TBody>> = NonNullable<unknown> extends TComputedRequiredOptions ? [initURL: TInitURL, config?: TComputedConfig] : [initURL: TInitURL, config: TComputedConfig];
1723
- type CallApiResult<TData, TErrorData, TResultMode extends ResultModeType, TThrowOnError extends ThrowOnErrorBoolean> = InferCallApiResult<TData, TErrorData, TResultMode, TThrowOnError>;
1724
- type CallApiResultLoose<TData, TErrorData, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorBoolean = ThrowOnErrorBoolean> = InferCallApiResult<TData, TErrorData, TResultMode, TThrowOnError>;
1725
- //#endregion
1726
- //#region src/auth.d.ts
1727
- type PossibleAuthValue = Awaitable<string | null | undefined>;
1728
- type PossibleAuthValueOrGetter = PossibleAuthValue | (() => PossibleAuthValue);
1729
- type BearerAuth = {
1730
- type: "Bearer";
1731
- value: PossibleAuthValueOrGetter;
1732
- };
1733
- type TokenAuth = {
1734
- type: "Token";
1735
- value: PossibleAuthValueOrGetter;
1736
- };
1737
- type BasicAuth = {
1738
- type: "Basic";
1739
- username: PossibleAuthValueOrGetter;
1740
- password: PossibleAuthValueOrGetter;
1741
- };
1742
- /**
1743
- * Custom auth
1744
- *
1745
- * @param prefix - prefix of the header
1746
- * @param authValue - value of the header
1747
- *
1748
- * @example
1749
- * ```ts
1750
- * {
1751
- * type: "Custom",
1752
- * prefix: "Token",
1753
- * authValue: "token"
1754
- * }
1755
- * ```
1756
- */
1757
- type CustomAuth = {
1758
- type: "Custom";
1759
- prefix: PossibleAuthValueOrGetter;
1760
- value: PossibleAuthValueOrGetter;
1761
- };
1762
- type AuthOption = PossibleAuthValueOrGetter | BearerAuth | TokenAuth | BasicAuth | CustomAuth;
1763
- //#endregion
1764
- //#region src/types/conditional-types.d.ts
1765
- /**
1766
- * @description Makes a type partial if the output type of TSchema is not provided or has undefined in the union, otherwise makes it required
1767
- */
1768
- type MakeSchemaOptionRequiredIfDefined<TSchemaOption extends CallApiSchema[keyof CallApiSchema], TObject> = undefined extends InferSchemaOutput<TSchemaOption, undefined> ? TObject : Required<TObject>;
1769
- type MergeBaseWithRouteKey<TBaseURLOrPrefix extends string | undefined, TRouteKey extends string> = TBaseURLOrPrefix extends string ? TRouteKey extends `${AtSymbol}${infer TMethod extends RouteKeyMethods}/${infer TRestOfRoutKey}` ? `${AtSymbol}${TMethod}/${RemoveLeadingSlash<RemoveTrailingSlash<TBaseURLOrPrefix>>}/${RemoveLeadingSlash<TRestOfRoutKey>}` : `${TBaseURLOrPrefix}${TRouteKey}` : TRouteKey;
1770
- type ApplyURLBasedConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["prefix"] extends string ? MergeBaseWithRouteKey<TSchemaConfig["prefix"], TSchemaRouteKeys> : TSchemaConfig["baseURL"] extends string ? MergeBaseWithRouteKey<TSchemaConfig["baseURL"], TSchemaRouteKeys> : TSchemaRouteKeys;
1771
- type ApplyStrictConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["strict"] extends true ? TSchemaRouteKeys // eslint-disable-next-line perfectionist/sort-union-types -- Don't sort union types
1772
- : TSchemaRouteKeys | Exclude<InitURLOrURLObject, RouteKeyMethodsURLUnion>;
1773
- type ApplySchemaConfiguration<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, TSchemaRouteKeys>>;
1774
- type InferAllMainRoutes<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes> = Omit<TBaseSchemaRoutes, FallBackRouteSchemaKey>;
1775
- type InferAllMainRouteKeys<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = ApplySchemaConfiguration<TSchemaConfig, Extract<keyof InferAllMainRoutes<TBaseSchemaRoutes>, string>>;
1776
- type InferInitURL<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = keyof TBaseSchemaRoutes extends never ? InitURLOrURLObject : InferAllMainRouteKeys<TBaseSchemaRoutes, TSchemaConfig>;
1777
- type GetCurrentRouteSchemaKey<TSchemaConfig extends CallApiSchemaConfig, TPath> = TPath extends URL ? string : TSchemaConfig["prefix"] extends string ? TPath extends (`${AtSymbol}${infer TMethod extends RouteKeyMethods}/${RemoveLeadingSlash<TSchemaConfig["prefix"]>}${infer TCurrentRoute}`) ? `${AtSymbol}${TMethod}/${RemoveLeadingSlash<TCurrentRoute>}` : TPath extends `${TSchemaConfig["prefix"]}${infer TCurrentRoute}` ? TCurrentRoute : string : TSchemaConfig["baseURL"] extends string ? TPath extends (`${AtSymbol}${infer TMethod extends RouteKeyMethods}/${TSchemaConfig["baseURL"]}${infer TCurrentRoute}`) ? `${AtSymbol}${TMethod}/${RemoveLeadingSlash<TCurrentRoute>}` : TPath extends `${TSchemaConfig["baseURL"]}${infer TCurrentRoute}` ? TCurrentRoute : string : TPath;
1778
- type GetCurrentRouteSchema<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string, TComputedFallBackRouteSchema = TBaseSchemaRoutes[FallBackRouteSchemaKey], TComputedCurrentRouteSchema = TBaseSchemaRoutes[TCurrentRouteSchemaKey], TComputedRouteSchema extends CallApiSchema = NonNullable<Omit<TComputedFallBackRouteSchema, keyof TComputedCurrentRouteSchema> & TComputedCurrentRouteSchema>> = TComputedRouteSchema extends CallApiSchema ? Writeable<TComputedRouteSchema, "deep"> : CallApiSchema;
1779
- type JsonPrimitive = boolean | number | string | null | undefined;
1780
- type SerializableObject = Record<PropertyKey, unknown>;
1781
- type SerializableArray = Array<JsonPrimitive | SerializableObject> | ReadonlyArray<JsonPrimitive | SerializableObject>;
1782
- type Body = UnmaskType<Exclude<RequestInit["body"], undefined> | SerializableArray | SerializableObject>;
1783
- type InferBodyOption<TSchema extends CallApiSchema, TBody = InferSchemaOutput<TSchema["body"], Body>> = MakeSchemaOptionRequiredIfDefined<TSchema["body"], {
1863
+ hooks?: PluginHooks<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginHooks<TCallApiContext>> | Awaitable<void>);
1784
1864
  /**
1785
- * Body of the request, can be a object or any other supported body type.
1865
+ * A unique id for the plugin
1786
1866
  */
1787
- body?: TBody;
1788
- }>;
1789
- type MethodUnion = UnmaskType<"CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" | AnyString>;
1790
- type ExtractMethodFromURL<TInitURL> = string extends TInitURL ? MethodUnion : TInitURL extends `${AtSymbol}${infer TMethod extends RouteKeyMethods}/${string}` ? Uppercase<TMethod> : MethodUnion;
1791
- type InferMethodOption<TSchema extends CallApiSchema, TInitURL extends InitURLOrURLObject> = MakeSchemaOptionRequiredIfDefined<TSchema["method"], {
1867
+ id: string;
1792
1868
  /**
1793
- * HTTP method for the request.
1794
- * @default "GET"
1869
+ * Defines metadata that can be passed to callApi
1795
1870
  */
1796
- method?: InferSchemaOutput<TSchema["method"], ExtractMethodFromURL<TInitURL>>;
1797
- }>;
1798
- type HeadersOption = UnmaskType<Headers | Record<"Authorization", CommonAuthorizationHeaders | undefined> | Record<"Content-Type", CommonContentTypes | undefined> | Record<CommonRequestHeaders, string | undefined> | Record<string, string | undefined> | Array<[string, string]>>;
1799
- type InferHeadersOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["headers"], {
1871
+ metaDef?: MetaWithContextTag<DefaultMetaObject>;
1800
1872
  /**
1801
- * Headers to be used in the request.
1873
+ * Middlewares that for the plugin
1802
1874
  */
1803
- headers?: InferSchemaOutput<TSchema["headers"], HeadersOption> | ((context: {
1804
- baseHeaders: Extract<HeadersOption, Record<string, unknown>>;
1805
- }) => InferSchemaOutput<TSchema["headers"], HeadersOption>);
1806
- }>;
1807
- type InferRequestOptions<TSchema extends CallApiSchema, TInitURL extends InferInitURL<BaseCallApiSchemaRoutes, CallApiSchemaConfig>, TBody = InferSchemaOutput<TSchema["body"], Body>> = InferBodyOption<TSchema, TBody> & InferHeadersOption<TSchema> & InferMethodOption<TSchema, TInitURL>;
1808
- type InferMetaOption<TSchema extends CallApiSchema, TCallApiContext extends CallApiContext> = MakeSchemaOptionRequiredIfDefined<TSchema["meta"], {
1875
+ middlewares?: PluginMiddlewares<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginMiddlewares<TCallApiContext>> | Awaitable<void>);
1809
1876
  /**
1810
- * - An optional field you can fill with additional information,
1811
- * to associate with the request, typically used for logging or tracing.
1812
- *
1813
- * - A good use case for this, would be to use the info to handle specific cases in any of the shared interceptors.
1814
- *
1815
- * @example
1816
- * ```ts
1817
- * const callMainApi = callApi.create({
1818
- * baseURL: "https://main-api.com",
1819
- * onResponseError: ({ response, options }) => {
1820
- * if (options.meta?.userId) {
1821
- * console.error(`User ${options.meta.userId} made an error`);
1822
- * }
1823
- * },
1824
- * });
1825
- *
1826
- * const response = await callMainApi({
1827
- * url: "https://example.com/api/data",
1828
- * meta: { userId: "123" },
1829
- * });
1830
- * ```
1877
+ * A name for the plugin
1831
1878
  */
1832
- meta?: InferSchemaOutput<TSchema["meta"], TCallApiContext["Meta"]>;
1833
- }>;
1834
- type InferAuthOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["auth"], {
1879
+ name: string;
1835
1880
  /**
1836
- * Automatically add an Authorization header value.
1837
- *
1838
- * Supports multiple authentication patterns:
1839
- * - String: Direct authorization header value
1840
- * - Auth object: Structured authentication configuration
1841
- *
1842
- * @example
1843
- * ```ts
1844
- * // Bearer auth
1845
- * const response = await callMainApi({
1846
- * url: "https://example.com/api/data",
1847
- * auth: "123456",
1848
- * });
1849
- *
1850
- * // Bearer auth
1851
- * const response = await callMainApi({
1852
- * url: "https://example.com/api/data",
1853
- * auth: {
1854
- * type: "Bearer",
1855
- * value: "123456",
1856
- * },
1857
- })
1858
- *
1859
- * // Token auth
1860
- * const response = await callMainApi({
1861
- * url: "https://example.com/api/data",
1862
- * auth: {
1863
- * type: "Token",
1864
- * value: "123456",
1865
- * },
1866
- * });
1867
- *
1868
- * // Basic auth
1869
- * const response = await callMainApi({
1870
- * url: "https://example.com/api/data",
1871
- * auth: {
1872
- * type: "Basic",
1873
- * username: "username",
1874
- * password: "password",
1875
- * },
1876
- * });
1877
- *
1878
- * ```
1879
- */
1880
- auth?: InferSchemaOutput<TSchema["auth"], AuthOption>;
1881
- }>;
1882
- type InferQueryOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["query"], {
1881
+ * Base schema for the client.
1882
+ */
1883
+ schema?: BaseCallApiSchemaAndConfig;
1883
1884
  /**
1884
- * Parameters to be appended to the URL (i.e: /:id)
1885
+ * A function that will be called when the plugin is initialized. This will be called before the any of the other internal functions.
1885
1886
  */
1886
- query?: InferSchemaOutput<TSchema["query"], Query>;
1887
- }>;
1888
- type EmptyString = "";
1889
- type EmptyTuple = readonly [];
1890
- type StringTuple = readonly string[];
1891
- type PossibleParamNamePatterns = `${string}:${string}` | `${string}{${string}}${"" | AnyString}`;
1892
- type ExtractRouteParamNames<TCurrentRoute, TParamNamesAccumulator extends StringTuple = EmptyTuple> = TCurrentRoute extends PossibleParamNamePatterns ? TCurrentRoute extends `${infer TRoutePrefix}:${infer TParamAndRemainingRoute}` ? TParamAndRemainingRoute extends `${infer TCurrentParam}/${infer TRemainingRoute}` ? TCurrentParam extends EmptyString ? ExtractRouteParamNames<`${TRoutePrefix}/${TRemainingRoute}`, TParamNamesAccumulator> : ExtractRouteParamNames<`${TRoutePrefix}/${TRemainingRoute}`, [...TParamNamesAccumulator, TCurrentParam]> : TParamAndRemainingRoute extends `${infer TCurrentParam}` ? TCurrentParam extends EmptyString ? ExtractRouteParamNames<TRoutePrefix, TParamNamesAccumulator> : ExtractRouteParamNames<TRoutePrefix, [...TParamNamesAccumulator, TCurrentParam]> : ExtractRouteParamNames<TRoutePrefix, TParamNamesAccumulator> : TCurrentRoute extends `${infer TRoutePrefix}{${infer TCurrentParam}}${infer TRemainingRoute}` ? TCurrentParam extends EmptyString ? ExtractRouteParamNames<`${TRoutePrefix}${TRemainingRoute}`, TParamNamesAccumulator> : ExtractRouteParamNames<`${TRoutePrefix}${TRemainingRoute}`, [...TParamNamesAccumulator, TCurrentParam]> : TParamNamesAccumulator : TParamNamesAccumulator;
1893
- type ConvertParamNamesToRecord<TParamNames extends StringTuple> = Prettify<TParamNames extends (readonly [infer TFirstParamName extends string, ...infer TRemainingParamNames extends StringTuple]) ? Record<TFirstParamName, AllowedQueryParamValues> & ConvertParamNamesToRecord<TRemainingParamNames> : NonNullable<unknown>>;
1894
- type ConvertParamNamesToTuple<TParamNames extends StringTuple> = TParamNames extends readonly [string, ...infer TRemainingParamNames extends StringTuple] ? [AllowedQueryParamValues, ...ConvertParamNamesToTuple<TRemainingParamNames>] : [];
1895
- type InferParamsFromRoute<TCurrentRoute> = ExtractRouteParamNames<TCurrentRoute> extends StringTuple ? ExtractRouteParamNames<TCurrentRoute> extends EmptyTuple ? Params : ConvertParamNamesToRecord<ExtractRouteParamNames<TCurrentRoute>> | ConvertParamNamesToTuple<ExtractRouteParamNames<TCurrentRoute>> : Params;
1896
- type MakeParamsOptionRequired<TParamsSchemaOption extends CallApiSchema["params"], TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string, TObject> = MakeSchemaOptionRequiredIfDefined<TParamsSchemaOption, Params extends InferParamsFromRoute<TCurrentRouteSchemaKey> ? TObject : TCurrentRouteSchemaKey extends Extract<keyof TBaseSchemaRoutes, TCurrentRouteSchemaKey> ? undefined extends InferSchemaOutput<TParamsSchemaOption, null> ? TObject : Required<TObject> : TObject>;
1897
- type InferParamsOption<TSchema extends CallApiSchema, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string> = MakeParamsOptionRequired<TSchema["params"], TBaseSchemaRoutes, TCurrentRouteSchemaKey, {
1887
+ setup?: (context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginInitResult<TCallApiContext>> | Awaitable<void>;
1898
1888
  /**
1899
- * Parameters to be appended to the URL (i.e: /:id)
1889
+ * A version for the plugin
1900
1890
  */
1901
- params?: InferSchemaOutput<TSchema["params"], InferParamsFromRoute<TCurrentRouteSchemaKey>>;
1902
- }>;
1903
- type InferExtraOptions<TSchema extends CallApiSchema, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string, TCallApiContext extends CallApiContext> = InferAuthOption<TSchema> & InferMetaOption<TSchema, TCallApiContext> & InferParamsOption<TSchema, TBaseSchemaRoutes, TCurrentRouteSchemaKey> & InferQueryOption<TSchema>;
1904
- type ResultModeOption<TErrorData, TResultMode extends ResultModeType> = TErrorData extends false ? {
1905
- resultMode: "onlyData";
1906
- } : TErrorData extends false | undefined ? {
1907
- resultMode?: "onlyData";
1908
- } : {
1909
- resultMode?: TResultMode;
1910
- };
1911
- type ThrowOnErrorBoolean = boolean;
1912
- type ThrowOnErrorType<TErrorData, TThrowOnError extends ThrowOnErrorBoolean> = TThrowOnError | ((context: ErrorContext<{
1913
- ErrorData: TErrorData;
1914
- }>) => TThrowOnError);
1915
- type ThrowOnErrorOption<TErrorData, TThrowOnError extends ThrowOnErrorBoolean> = TErrorData extends false ? {
1916
- throwOnError: true;
1917
- } : TErrorData extends false | undefined ? {
1918
- throwOnError?: true;
1919
- } : {
1920
- throwOnError?: ThrowOnErrorType<TErrorData, TThrowOnError>;
1891
+ version?: string;
1892
+ }
1893
+ //#endregion
1894
+ //#region src/types/callapi-context.d.ts
1895
+ interface CallApiContext {
1896
+ Data?: DefaultDataType;
1897
+ ErrorData?: DefaultDataType;
1898
+ InferredExtraOptions?: DefaultInferredExtraOptions;
1899
+ Meta?: DefaultMetaObject;
1900
+ ResultMode?: ResultModeType;
1901
+ }
1902
+ type GetCallApiContext<TCallApiContext extends CallApiContext> = TCallApiContext;
1903
+ type GetCallApiContextRequired<TCallApiContext extends Required<CallApiContext>> = TCallApiContext;
1904
+ type OverrideCallApiContext<TFullCallApiContext extends CallApiContext, TOverrideCallApiContext extends CallApiContext> = Prettify<Omit<TFullCallApiContext, keyof TOverrideCallApiContext> & TOverrideCallApiContext>;
1905
+ declare const callApiContextSymbol: unique symbol;
1906
+ type callApiContextSymbol = typeof callApiContextSymbol;
1907
+ type ContextTag<TType, TCallApiContext extends CallApiContext> = TType & {
1908
+ readonly [callApiContextSymbol]: TCallApiContext;
1921
1909
  };
1910
+ type MetaWithContextTag<TMeta extends DefaultMetaObject | undefined> = ContextTag<TMeta, {
1911
+ Meta: TMeta;
1912
+ }>;
1913
+ type ExtraOptionsWithContextTag<TExtraOptions extends DefaultInferredExtraOptions> = ContextTag<TExtraOptions, {
1914
+ InferredExtraOptions: TExtraOptions;
1915
+ }>;
1916
+ type InferMetaFromTag<TTaggedType, TFallback = never> = TTaggedType extends ContextTag<unknown, infer TCallApiContext extends CallApiContext> ? IsEmptyObject<TCallApiContext["Meta"]> extends true ? TFallback : TCallApiContext["Meta"] : TFallback;
1917
+ type InferExtraOptionsFromTag<TTaggedType> = TTaggedType extends ContextTag<unknown, infer TCallApiContext extends CallApiContext> ? unknown extends TCallApiContext["InferredExtraOptions"] ? never : TCallApiContext["InferredExtraOptions"] : never;
1922
1918
  //#endregion
1923
- export { defineInstanceConfig as $, Register as A, ValidationError as At, ResponseErrorContext as B, FallBackRouteSchemaKey as Bt, CallApiResultLoose as C, PossibleHTTPError as Ct, GetCallApiContextRequired as D, ResponseTypeType as Dt, GetCallApiContext as E, ResponseTypeMap as Et, Hooks as F, CallApiSchemaConfig as Ft, objectifyHeaders as G, DistributiveOmit as Gt, SuccessContext as H, AnyString as Ht, HooksOrHooksArray as I, InferSchemaInput as It, isJavascriptError as J, fetchSpecificKeys as Jt, isHTTPError as K, NoInferUnMasked as Kt, RequestContext as L, InferSchemaOutput as Lt, CallApiExtraOptionsForHooks as M, BaseCallApiSchemaRoutes as Mt, CallApiRequestOptionsForHooks as N, BaseSchemaRouteKeyPrefixes as Nt, GlobalMeta as O, ResultModeType as Ot, ErrorContext as P, CallApiSchema as Pt, defineFallbackRouteSchema as Q, RequestStreamContext as R, InferSchemaResult as Rt, CallApiResult as S, InferCallApiResult as St, GetBaseSchemaRoutes as T, PossibleValidationError as Tt, RefetchOptions as U, CommonContentTypes as Ut, ResponseStreamContext as V, fallBackRouteSchemaKey as Vt, RetryOptions as W, CommonRequestHeaders as Wt, isValidationErrorInstance as X, isValidationError as Y, defineBaseConfig as Z, CallApiConfig as _, Middlewares as _t, GetCurrentRouteSchemaKey as a, toFormData as at, CallApiParameters as b, CallApiResultSuccessVariant as bt, InferAllMainRoutes as c, DefaultCallApiContext as ct, SerializableArray as d, CallApiPlugin as dt, defineMainSchema as et, SerializableObject as f, PluginHooks as ft, BaseCallApiExtraOptions as g, FetchMiddlewareContext as gt, BaseCallApiConfig as h, FetchImpl as ht, GetCurrentRouteSchema as i, defineSchemaRoutes as it, DedupeOptions as j, BaseCallApiSchemaAndConfig as jt, InstanceContext as k, HTTPError as kt, InferInitURL as l, DefaultDataType as lt, AuthOption as m, PluginSetupContext as mt, ApplyURLBasedConfig as n, defineSchema as nt, HeadersOption as o, toQueryString as ot, ThrowOnErrorBoolean as p, PluginMiddlewares as pt, isHTTPErrorInstance as q, Writeable as qt, Body as r, defineSchemaConfig as rt, InferAllMainRouteKeys as s, toSearchParams as st, ApplyStrictConfig as t, definePlugin as tt, InferParamsFromRoute as u, DefaultPluginArray as ut, CallApiContext as v, CallApiResultErrorVariant as vt, GetBaseSchemaConfig as w, PossibleJavaScriptError as wt, CallApiRequestOptions as x, GetResponseType as xt, CallApiExtraOptions as y, CallApiResultSuccessOrErrorVariant as yt, ResponseContext as z, URLOptions as zt };
1924
- //# sourceMappingURL=conditional-types-DaKFU9_F.d.ts.map
1919
+ export { PossibleValidationError as $, DefaultCallApiContext as $t, isHTTPErrorInstance as A, ValidationError as At, defineSchema as B, FallBackRouteSchemaKey as Bt, SuccessContext as C, CallApiResult as Ct, metaHelper as D, InstanceContext as Dt, extraOptionsHelper as E, GetBaseSchemaRoutes as Et, defineFallbackRouteSchema as F, CallApiSchema as Ft, toSearchParams as G, DedupeOptions as Gt, defineSchemaRoutes as H, FetchImpl as Ht, defineInstanceConfig as I, CallApiSchemaConfig as It, CallApiResultSuccessVariant as J, CommonRequestHeaders as Jt, CallApiResultErrorVariant as K, AnyString as Kt, defineMainSchema as L, InferSchemaInput as Lt, isValidationError as M, BaseCallApiSchemaAndConfig as Mt, isValidationErrorInstance as N, BaseCallApiSchemaRoutes as Nt, objectifyHeaders as O, Register as Ot, defineBaseConfig as P, BaseSchemaRouteKeyPrefixes as Pt, PossibleJavaScriptError as Q, Writeable as Qt, definePlugin as R, InferSchemaOutput as Rt, ResponseStreamContext as S, CallApiRequestOptions as St, RetryOptions as T, GetBaseSchemaConfig as Tt, toFormData as U, FetchMiddlewareContext as Ut, defineSchemaConfig as V, fallBackRouteSchemaKey as Vt, toQueryString as W, Middlewares as Wt, InferCallApiResult as X, NoInferUnMasked as Xt, GetResponseType as Y, DistributiveOmit as Yt, PossibleHTTPError as Z, UnionToIntersection as Zt, HooksOrHooksArray as _, BaseCallApiConfig as _t, GetCallApiContextRequired as a, Body as at, ResponseContext as b, CallApiExtraOptions as bt, MetaWithContextTag as c, HeadersOption as ct, PluginMiddlewares as d, InferInitURL as dt, DefaultMetaObject as en, ResponseTypeMap as et, PluginSetupContext as f, InferParamsFromRoute as ft, Hooks as g, AuthOption as gt, ErrorContext as h, ThrowOnErrorBoolean as ht, GetCallApiContext as i, ApplyURLBasedConfig as it, isJavascriptError as j, URLOptions as jt, isHTTPError as k, HTTPError as kt, CallApiPlugin as l, InferAllMainRouteKeys as lt, CallApiRequestOptionsForHooks as m, SerializableObject as mt, ContextTag as n, fetchSpecificKeys as nn, ResultModeType as nt, InferExtraOptionsFromTag as o, GetCurrentRouteSchema as ot, CallApiExtraOptionsForHooks as p, SerializableArray as pt, CallApiResultSuccessOrErrorVariant as q, CommonContentTypes as qt, ExtraOptionsWithContextTag as r, ApplyStrictConfig as rt, InferMetaFromTag as s, GetCurrentRouteSchemaKey as st, CallApiContext as t, DefaultPluginArray as tn, ResponseTypeType as tt, PluginHooks as u, InferAllMainRoutes as ut, RequestContext as v, BaseCallApiExtraOptions as vt, RefetchOptions as w, CallApiResultLoose as wt, ResponseErrorContext as x, CallApiParameters as xt, RequestStreamContext as y, CallApiConfig as yt, definePluginWithContext as z, InferSchemaResult as zt };
1920
+ //# sourceMappingURL=callapi-context-NF0HXJwF.d.ts.map