@zayne-labs/callapi-plugins 4.0.34 → 4.0.36

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.
@@ -9,7 +9,7 @@ declare const fallBackRouteSchemaKey = "@default";
9
9
  type FallBackRouteSchemaKey = typeof fallBackRouteSchemaKey;
10
10
  //#endregion
11
11
  //#endregion
12
- //#region ../callapi/dist/index-YnlEWnbR.d.ts
12
+ //#region ../callapi/dist/index-CGn8h_Ai.d.ts
13
13
  //#region src/types/type-helpers.d.ts
14
14
  type AnyString = string & NonNullable<unknown>;
15
15
  type AnyNumber = number & NonNullable<unknown>;
@@ -101,432 +101,435 @@ type StreamProgressEvent = {
101
101
  transferredBytes: number;
102
102
  };
103
103
  //#endregion
104
- //#region src/types/standard-schema.d.ts
105
- /**
106
- * The Standard Schema interface.
107
- * @see https://github.com/standard-schema/standard-schema
108
- */
109
- /** The Standard Typed interface. This is a base type extended by other specs. */
110
- interface StandardTypedV1<Input = unknown, Output = Input> {
111
- /** The Standard properties. */
112
- readonly "~standard": StandardTypedV1.Props<Input, Output>;
113
- }
114
- declare namespace StandardTypedV1 {
115
- /** The Standard Typed properties interface. */
116
- interface Props<Input = unknown, Output = Input> {
117
- /** Inferred types associated with the schema. */
118
- readonly types?: Types<Input, Output> | undefined;
119
- /** The vendor name of the schema library. */
120
- readonly vendor: string;
121
- /** The version number of the standard. */
122
- readonly version: 1;
123
- }
124
- /** The Standard Typed types interface. */
125
- interface Types<Input = unknown, Output = Input> {
126
- /** The input type of the schema. */
127
- readonly input: Input;
128
- /** The output type of the schema. */
129
- readonly output: Output;
130
- }
131
- /** Infers the input type of a Standard Typed. */
132
- type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
133
- /** Infers the output type of a Standard Typed. */
134
- type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
135
- }
136
- /** The Standard Schema interface. */
137
- interface StandardSchemaV1<Input = unknown, Output = Input> {
138
- /** The Standard Schema properties. */
139
- readonly "~standard": StandardSchemaV1.Props<Input, Output>;
140
- }
141
- declare namespace StandardSchemaV1 {
142
- /** The Standard Schema properties interface. */
143
- interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
144
- /** Validates unknown input values. */
145
- readonly validate: (value: unknown, options?: StandardSchemaV1.Options) => Promise<Result<Output>> | Result<Output>;
146
- }
147
- /** The result interface of the validate function. */
148
- type Result<Output> = FailureResult | SuccessResult<Output>;
149
- /** The result interface if validation succeeds. */
150
- interface SuccessResult<Output> {
151
- /** A falsy value for `issues` indicates success. */
152
- readonly issues?: undefined;
153
- /** The typed output value. */
154
- readonly value: Output;
155
- }
156
- interface Options {
157
- /** Explicit support for additional vendor-specific parameters, if needed. */
158
- readonly libraryOptions?: Record<string, unknown> | undefined;
159
- }
160
- /** The result interface if validation fails. */
161
- interface FailureResult {
162
- /** The issues of failed validation. */
163
- readonly issues: readonly Issue[];
164
- }
165
- /** The issue interface of the failure output. */
166
- interface Issue {
167
- /** The error message of the issue. */
168
- readonly message: string;
169
- /** The path of the issue, if any. */
170
- readonly path?: ReadonlyArray<PathSegment | PropertyKey> | undefined;
171
- }
172
- /** The path segment interface of the issue. */
173
- interface PathSegment {
174
- /** The key representing a path segment. */
175
- readonly key: PropertyKey;
176
- }
177
- /** The Standard types interface. */
178
- type Types<Input = unknown, Output = Input> = StandardTypedV1.Types<Input, Output>;
179
- /** Infers the input type of a Standard. */
180
- type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
181
- /** Infers the output type of a Standard. */
182
- type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
183
- }
184
- //#endregion
185
- //#region src/validation.d.ts
186
- type ResultVariant = "infer-input" | "infer-output";
187
- type InferSchemaResult<TSchema, TFallbackResult, TResultVariant extends ResultVariant> = undefined extends TSchema ? TFallbackResult : TSchema extends StandardSchemaV1 ? TResultVariant extends "infer-input" ? StandardSchemaV1.InferInput<TSchema> : StandardSchemaV1.InferOutput<TSchema> : TSchema extends AnyFunction$1<infer TResult> ? Awaited<TResult> : TFallbackResult;
188
- type InferSchemaOutput<TSchema, TFallbackResult = unknown> = InferSchemaResult<TSchema, TFallbackResult, "infer-output">;
189
- type BooleanObject = { [Key in keyof CallApiSchema]: boolean };
190
- interface CallApiSchemaConfig {
191
- /**
192
- * The base url of the schema. By default it's the baseURL of the callApi instance.
193
- */
194
- baseURL?: string;
195
- /**
196
- * Disables runtime validation for the schema.
197
- */
198
- disableRuntimeValidation?: boolean | BooleanObject;
104
+ //#region src/hooks.d.ts
105
+ interface Hooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
199
106
  /**
200
- * If `true`, the original input value will be used instead of the transformed/validated output.
107
+ * Hook called when any error occurs within the request/response lifecycle.
201
108
  *
202
- * This is useful when you want to validate the input but don't want any transformations
203
- * applied by the validation schema (e.g., type coercion, default values, etc).
204
- */
205
- disableValidationOutputApplication?: boolean | BooleanObject;
206
- /**
207
- * Optional url prefix that will be substituted for the `baseURL` of the schemaConfig at runtime.
109
+ * This is a unified error handler that catches both request errors (network failures,
110
+ * timeouts, etc.) and response errors (HTTP error status codes). It's essentially
111
+ * a combination of `onRequestError` and `onResponseError` hooks.
208
112
  *
209
- * This allows you to reuse the same schema against different base URLs (for example,
210
- * swapping between `/api/v1` and `/api/v2`) without redefining the entire schema.
113
+ * @param context - Error context containing error details, request info, and response (if available)
114
+ * @returns Promise or void - Hook can be async or sync
211
115
  */
212
- prefix?: string;
116
+ onError?: (context: ErrorContext<TCallApiContext>) => Awaitable<unknown>;
213
117
  /**
214
- * Controls the strictness of API route validation.
118
+ * Hook called before the HTTP request is sent and before any internal processing of the request object begins.
215
119
  *
216
- * When true:
217
- * - Only routes explicitly defined in the schema will be considered valid to typescript and the runtime.
218
- * - Attempting to call routes not defined in the schema will result in both type errors and runtime validation errors.
219
- * - Useful for ensuring API calls conform exactly to your schema definition
120
+ * This is the ideal place to modify request headers, add authentication,
121
+ * implement request logging, or perform any setup before the network call.
122
+ *
123
+ * @param context - Request context with mutable request object and configuration
124
+ * @returns Promise or void - Hook can be async or sync
220
125
  *
221
- * When false or undefined (default):
222
- * - All routes will be allowed, whether they are defined in the schema or not
223
- */
224
- strict?: boolean;
225
- }
226
- interface CallApiSchema {
227
- auth?: StandardSchemaV1<AuthOption | undefined> | ((auth: AuthOption) => Awaitable<AuthOption | undefined>);
228
- /**
229
- * The schema to use for validating the request body.
230
- */
231
- body?: StandardSchemaV1<Body | undefined> | ((body: Body) => Awaitable<Body | undefined>);
232
- /**
233
- * The schema to use for validating the response data.
234
- */
235
- data?: StandardSchemaV1 | ((data: unknown) => unknown);
236
- /**
237
- * The schema to use for validating the response error data.
238
- */
239
- errorData?: StandardSchemaV1 | ((errorData: unknown) => unknown);
240
- /**
241
- * The schema to use for validating the request headers.
242
- */
243
- headers?: StandardSchemaV1<HeadersOption | undefined> | ((headers: HeadersOption) => Awaitable<HeadersOption | undefined>);
244
- /**
245
- * The schema to use for validating the meta option.
246
126
  */
247
- meta?: StandardSchemaV1<GlobalMeta | undefined> | ((meta: GlobalMeta) => Awaitable<GlobalMeta | undefined>);
127
+ onRequest?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
248
128
  /**
249
- * The schema to use for validating the request method.
129
+ * Hook called when an error occurs during the fetch request itself.
130
+ *
131
+ * This handles network-level errors like connection failures, timeouts,
132
+ * DNS resolution errors, or other issues that prevent getting an HTTP response.
133
+ * Note that HTTP error status codes (4xx, 5xx) are handled by `onResponseError`.
134
+ *
135
+ * @param context - Request error context with error details and null response
136
+ * @returns Promise or void - Hook can be async or sync
250
137
  */
251
- method?: StandardSchemaV1<MethodUnion | undefined> | ((method: MethodUnion) => Awaitable<MethodUnion | undefined>);
138
+ onRequestError?: (context: RequestErrorContext<TCallApiContext>) => Awaitable<unknown>;
252
139
  /**
253
- * The schema to use for validating the request url parameters.
140
+ * Hook called just before the HTTP request is sent and after the request has been processed.
141
+ *
142
+ * @param context - Request context with mutable request object and configuration
254
143
  */
255
- params?: StandardSchemaV1<Params | undefined> | ((params: Params) => Awaitable<Params | undefined>);
144
+ onRequestReady?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
256
145
  /**
257
- * The schema to use for validating the request url queries.
146
+ * Hook called during upload stream progress tracking.
147
+ *
148
+ * This hook is triggered when uploading data (like file uploads) and provides
149
+ * progress information about the upload. Useful for implementing progress bars
150
+ * or upload status indicators.
151
+ *
152
+ * @param context - Request stream context with progress event and request instance
153
+ * @returns Promise or void - Hook can be async or sync
154
+ *
258
155
  */
259
- query?: StandardSchemaV1<Query | undefined> | ((query: Query) => Awaitable<Query | undefined>);
260
- }
261
- declare const routeKeyMethods: readonly ["delete", "get", "patch", "post", "put"];
262
- type RouteKeyMethods = (typeof routeKeyMethods)[number];
263
- type RouteKeyMethodsURLUnion = `@${RouteKeyMethods}/`;
264
- type BaseSchemaRouteKeyPrefixes = FallBackRouteSchemaKey | RouteKeyMethodsURLUnion;
265
- type BaseCallApiSchemaRoutes = Partial<Record<AnyString | BaseSchemaRouteKeyPrefixes, CallApiSchema>>;
266
- type BaseCallApiSchemaAndConfig = {
267
- config?: CallApiSchemaConfig;
268
- routes: BaseCallApiSchemaRoutes;
269
- };
270
- //#endregion
271
- //#region src/url.d.ts
272
- type AllowedQueryParamValues = UnmaskType<boolean | number | string>;
273
- type RecordStyleParams = UnmaskType<Record<string, AllowedQueryParamValues>>;
274
- type TupleStyleParams = UnmaskType<AllowedQueryParamValues[]>;
275
- type Params = UnmaskType<RecordStyleParams | TupleStyleParams>;
276
- type Query = UnmaskType<Record<string, AllowedQueryParamValues>>;
277
- type InitURLOrURLObject = AnyString | RouteKeyMethodsURLUnion | URL;
278
- interface URLOptions {
156
+ onRequestStream?: (context: RequestStreamContext<TCallApiContext>) => Awaitable<unknown>;
279
157
  /**
280
- * Base URL for all API requests. Will only be prepended to relative URLs.
281
- *
282
- * Absolute URLs (starting with http/https) will not be prepended by the baseURL.
158
+ * Hook called when any HTTP response is received from the API.
283
159
  *
284
- * @example
285
- * ```ts
286
- * // Set base URL for all requests
287
- * baseURL: "https://api.example.com/v1"
160
+ * This hook is triggered for both successful (2xx) and error (4xx, 5xx) responses.
161
+ * It's useful for response logging, metrics collection, or any processing that
162
+ * should happen regardless of response status.
288
163
  *
289
- * // Then use relative URLs in requests
290
- * callApi("/users") // https://api.example.com/v1/users
291
- * callApi("/posts/123") // → https://api.example.com/v1/posts/123
164
+ * @param context - Response context with either success data or error information
165
+ * @returns Promise or void - Hook can be async or sync
292
166
  *
293
- * // Environment-specific base URLs
294
- * baseURL: process.env.NODE_ENV === "production"
295
- * ? "https://api.example.com"
296
- * : "http://localhost:3000/api"
297
- * ```
298
167
  */
299
- baseURL?: string;
168
+ onResponse?: (context: ResponseContext<TCallApiContext>) => Awaitable<unknown>;
300
169
  /**
301
- * Resolved request URL after processing baseURL, parameters, and query strings (readonly)
170
+ * Hook called when an HTTP error response (4xx, 5xx) is received from the API.
302
171
  *
303
- * This is the final URL that will be used for the HTTP request, computed from
304
- * baseURL, initURL, params, and query parameters.
172
+ * This handles server-side errors where an HTTP response was successfully received
173
+ * but indicates an error condition. Different from `onRequestError` which handles
174
+ * network-level failures.
305
175
  *
176
+ * @param context - Response error context with HTTP error details and response
177
+ * @returns Promise or void - Hook can be async or sync
306
178
  */
307
- readonly fullURL?: string;
179
+ onResponseError?: (context: ResponseErrorContext<TCallApiContext>) => Awaitable<unknown>;
308
180
  /**
309
- * The original URL string passed to the callApi instance (readonly)
181
+ * Hook called during download stream progress tracking.
310
182
  *
311
- * This preserves the original URL as provided, including any method modifiers like "@get/" or "@post/".
183
+ * This hook is triggered when downloading data (like file downloads) and provides
184
+ * progress information about the download. Useful for implementing progress bars
185
+ * or download status indicators.
186
+ *
187
+ * @param context - Response stream context with progress event and response
188
+ * @returns Promise or void - Hook can be async or sync
312
189
  *
313
190
  */
314
- readonly initURL?: string;
191
+ onResponseStream?: (context: ResponseStreamContext<TCallApiContext>) => Awaitable<unknown>;
315
192
  /**
316
- * The URL string after normalization, with method modifiers removed(readonly)
193
+ * Hook called when a request is being retried.
317
194
  *
318
- * Method modifiers like "@get/", "@post/" are stripped to create a clean URL
319
- * for parameter substitution and final URL construction.
195
+ * This hook is triggered before each retry attempt, providing information about
196
+ * the previous failure and the current retry attempt number. Useful for implementing
197
+ * custom retry logic, exponential backoff, or retry logging.
198
+ *
199
+ * @param context - Retry context with error details and retry attempt count
200
+ * @returns Promise or void - Hook can be async or sync
320
201
  *
321
202
  */
322
- readonly initURLNormalized?: string;
203
+ onRetry?: (response: RetryContext<TCallApiContext>) => Awaitable<unknown>;
323
204
  /**
324
- * Parameters to be substituted into URL path segments.
325
- *
326
- * Supports both object-style (named parameters) and array-style (positional parameters)
327
- * for flexible URL parameter substitution.
205
+ * Hook called when a successful response (2xx status) is received from the API.
328
206
  *
329
- * @example
330
- * ```typescript
331
- * // Object-style parameters (recommended)
332
- * const namedParams: URLOptions = {
333
- * initURL: "/users/:userId/posts/:postId",
334
- * params: { userId: "123", postId: "456" }
335
- * };
336
- * // Results in: /users/123/posts/456
207
+ * This hook is triggered only for successful responses and provides access to
208
+ * the parsed response data. Ideal for success logging, caching, or post-processing
209
+ * of successful API responses.
337
210
  *
338
- * // Array-style parameters (positional)
339
- * const positionalParams: URLOptions = {
340
- * initURL: "/users/:userId/posts/:postId",
341
- * params: ["123", "456"] // Maps in order: userId=123, postId=456
342
- * };
343
- * // Results in: /users/123/posts/456
211
+ * @param context - Success context with parsed response data and response object
212
+ * @returns Promise or void - Hook can be async or sync
344
213
  *
345
- * // Single parameter
346
- * const singleParam: URLOptions = {
347
- * initURL: "/users/:id",
348
- * params: { id: "user-123" }
349
- * };
350
- * // Results in: /users/user-123
351
- * ```
352
214
  */
353
- params?: Params;
215
+ onSuccess?: (context: SuccessContext<TCallApiContext>) => Awaitable<unknown>;
354
216
  /**
355
- * Query parameters to append to the URL as search parameters.
217
+ * Hook called when a validation error occurs.
356
218
  *
357
- * These will be serialized into the URL query string using standard
358
- * URL encoding practices.
219
+ * This hook is triggered when request or response data fails validation against
220
+ * a defined schema. It provides access to the validation error details and can
221
+ * be used for custom error handling, logging, or fallback behavior.
359
222
  *
360
- * @example
361
- * ```typescript
362
- * // Basic query parameters
363
- * const queryOptions: URLOptions = {
364
- * initURL: "/users",
365
- * query: {
366
- * page: 1,
367
- * limit: 10,
368
- * search: "john doe",
369
- * active: true
370
- * }
371
- * };
372
- * // Results in: /users?page=1&limit=10&search=john%20doe&active=true
223
+ * @param context - Validation error context with error details and response (if available)
224
+ * @returns Promise or void - Hook can be async or sync
373
225
  *
374
- * // Filtering and sorting
375
- * const filterOptions: URLOptions = {
376
- * initURL: "/products",
377
- * query: {
378
- * category: "electronics",
379
- * minPrice: 100,
380
- * maxPrice: 500,
381
- * sortBy: "price",
382
- * order: "asc"
383
- * }
384
- * };
385
- * // Results in: /products?category=electronics&minPrice=100&maxPrice=500&sortBy=price&order=asc
386
- * ```
387
226
  */
388
- query?: Query;
227
+ onValidationError?: (context: ValidationErrorContext<TCallApiContext>) => Awaitable<unknown>;
389
228
  }
390
- //#endregion
391
- //#region src/types/conditional-types.d.ts
392
- /**
393
- * @description Makes a type partial if the output type of TSchema is not provided or has undefined in the union, otherwise makes it required
394
- */
395
- type MakeSchemaOptionRequiredIfDefined<TSchemaOption extends CallApiSchema[keyof CallApiSchema], TObject> = undefined extends InferSchemaOutput<TSchemaOption, undefined> ? TObject : Required<TObject>;
396
- type ApplyURLBasedConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["prefix"] extends string ? `${TSchemaConfig["prefix"]}${TSchemaRouteKeys}` : TSchemaConfig["baseURL"] extends string ? `${TSchemaConfig["baseURL"]}${TSchemaRouteKeys}` : TSchemaRouteKeys;
397
- type ApplyStrictConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["strict"] extends true ? TSchemaRouteKeys :
398
- // eslint-disable-next-line perfectionist/sort-union-types -- Don't sort union types
399
- TSchemaRouteKeys | Exclude<InitURLOrURLObject, RouteKeyMethodsURLUnion>;
400
- type ApplySchemaConfiguration<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, TSchemaRouteKeys>>;
401
- type InferAllRouteKeys<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = ApplySchemaConfiguration<TSchemaConfig, Exclude<Extract<keyof TBaseSchemaRoutes, string>, FallBackRouteSchemaKey>>;
402
- type InferInitURL<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = keyof TBaseSchemaRoutes extends never ? InitURLOrURLObject : InferAllRouteKeys<TBaseSchemaRoutes, TSchemaConfig>;
403
- 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;
404
- type JsonPrimitive = boolean | number | string | null | undefined;
405
- type SerializableObject = Record<PropertyKey, unknown>;
406
- type SerializableArray = Array<JsonPrimitive | SerializableObject> | ReadonlyArray<JsonPrimitive | SerializableObject>;
407
- type Body = UnmaskType<Exclude<RequestInit["body"], undefined> | SerializableArray | SerializableObject>;
408
- type InferBodyOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["body"], {
229
+ type HooksOrHooksArray<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> = { [Key in keyof Hooks<TCallApiContext>]: Hooks<TCallApiContext>[Key] | Array<Hooks<TCallApiContext>[Key]> };
230
+ interface HookConfigOptions {
409
231
  /**
410
- * Body of the request, can be a object or any other supported body type.
232
+ * Controls the execution mode of all composed hooks (main + plugin hooks).
233
+ *
234
+ * - **"parallel"**: All hooks execute simultaneously via Promise.all() for better performance
235
+ * - **"sequential"**: All hooks execute one by one in registration order via await in a loop
236
+ *
237
+ * This affects how ALL hooks execute together, regardless of their source (main or plugin).
238
+ *
239
+ * @default "parallel"
411
240
  */
412
- body?: InferSchemaOutput<TSchema["body"], Body>;
413
- }>;
414
- type MethodUnion = UnmaskType<"CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" | AnyString>;
415
- type InferMethodFromURL<TInitURL> = string extends TInitURL ? MethodUnion : TInitURL extends `@${infer TMethod extends RouteKeyMethods}/${string}` ? Uppercase<TMethod> : MethodUnion;
416
- type InferMethodOption<TSchema extends CallApiSchema, TInitURL> = MakeSchemaOptionRequiredIfDefined<TSchema["method"], {
241
+ hooksExecutionMode?: "parallel" | "sequential";
242
+ }
243
+ type RequestContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = {
417
244
  /**
418
- * HTTP method for the request.
419
- * @default "GET"
245
+ * Base configuration object passed to createFetchClient.
246
+ *
247
+ * Contains the foundational configuration that applies to all requests
248
+ * made by this client instance, such as baseURL, default headers, and
249
+ * global options.
420
250
  */
421
- method?: InferSchemaOutput<TSchema["method"], InferMethodFromURL<TInitURL>>;
422
- }>;
423
- type HeadersOption = UnmaskType<Headers | Record<"Authorization", CommonAuthorizationHeaders | undefined> | Record<"Content-Type", CommonContentTypes | undefined> | Record<CommonRequestHeaders, string | undefined> | Record<string, string | undefined> | Array<[string, string]>>;
424
- type InferHeadersOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["headers"], {
251
+ baseConfig: Exclude<BaseCallApiConfig, AnyFunction$1>;
425
252
  /**
426
- * Headers to be used in the request.
253
+ * Instance-specific configuration object passed to the callApi instance.
254
+ *
255
+ * Contains configuration specific to this particular API call, which
256
+ * can override or extend the base configuration.
427
257
  */
428
- headers?: InferSchemaOutput<TSchema["headers"], HeadersOption> | ((context: {
429
- baseHeaders: Extract<HeadersOption, Record<string, unknown>>;
430
- }) => InferSchemaOutput<TSchema["headers"], HeadersOption>);
431
- }>;
432
- type InferRequestOptions<TSchema extends CallApiSchema, TInitURL extends InferInitURL<BaseCallApiSchemaRoutes, CallApiSchemaConfig>> = InferBodyOption<TSchema> & InferHeadersOption<TSchema> & InferMethodOption<TSchema, TInitURL>;
433
- type InferMetaOption<TSchema extends CallApiSchema, TCallApiContext extends CallApiContext> = MakeSchemaOptionRequiredIfDefined<TSchema["meta"], {
258
+ config: CallApiConfig;
434
259
  /**
435
- * - An optional field you can fill with additional information,
436
- * to associate with the request, typically used for logging or tracing.
260
+ * Merged options combining base config, instance config, and default options.
437
261
  *
438
- * - A good use case for this, would be to use the info to handle specific cases in any of the shared interceptors.
262
+ * This is the final resolved configuration that will be used for the request,
263
+ * with proper precedence applied (instance > base > defaults).
264
+ */
265
+ options: CallApiExtraOptionsForHooks<TCallApiContext>;
266
+ /**
267
+ * Merged request object ready to be sent.
268
+ *
269
+ * Contains the final request configuration including URL, method, headers,
270
+ * body, and other fetch options. This object can be modified in onRequest
271
+ * hooks to customize the outgoing request.
272
+ */
273
+ request: CallApiRequestOptionsForHooks;
274
+ };
275
+ type ValidationErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
276
+ error: PossibleValidationError;
277
+ response: Response | null;
278
+ };
279
+ type SuccessContext<TCallApiContext extends Pick<CallApiContext, "Data" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
280
+ data: TCallApiContext["Data"];
281
+ response: Response;
282
+ };
283
+ type ResponseContext<TCallApiContext extends Pick<CallApiContext, "Data" | "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & (Prettify<CallApiResultSuccessVariant<TCallApiContext["Data"]>> | Prettify<Extract<CallApiResultErrorVariant<TCallApiContext["ErrorData"]>, {
284
+ error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
285
+ }>>);
286
+ type RequestErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
287
+ error: PossibleJavaScriptError;
288
+ response: null;
289
+ };
290
+ type ErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & ({
291
+ error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
292
+ response: Response;
293
+ } | {
294
+ error: PossibleJavaScriptOrValidationError;
295
+ response: Response | null;
296
+ });
297
+ type ResponseErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
298
+ error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
299
+ }> & RequestContext<TCallApiContext>;
300
+ type RetryContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = ErrorContext<TCallApiContext> & {
301
+ retryAttemptCount: number;
302
+ };
303
+ type RequestStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
304
+ event: StreamProgressEvent;
305
+ requestInstance: Request;
306
+ };
307
+ type ResponseStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
308
+ event: StreamProgressEvent;
309
+ response: Response;
310
+ };
311
+ //#endregion
312
+ //#region src/dedupe.d.ts
313
+ type DedupeStrategyUnion = UnmaskType<"cancel" | "defer" | "none">;
314
+ type DedupeOptionKeys = Exclude<keyof DedupeOptions, "dedupe">;
315
+ type InnerDedupeOptions = { [Key in DedupeOptionKeys as RemovePrefix<"dedupe", Key>]?: DedupeOptions[Key] };
316
+ type DedupeOptions = {
317
+ /**
318
+ * All dedupe options in a single object instead of separate properties
319
+ */
320
+ dedupe?: InnerDedupeOptions;
321
+ /**
322
+ * Controls the scope of request deduplication caching.
323
+ *
324
+ * - `"global"`: Shares deduplication cache across all `createFetchClient` instances with the same `dedupeCacheScopeKey`.
325
+ * Useful for applications with multiple API clients that should share deduplication state.
326
+ * - `"local"`: Limits deduplication to requests within the same `createFetchClient` instance.
327
+ * Provides better isolation and is recommended for most use cases.
328
+ *
329
+ *
330
+ * **Real-world Scenarios:**
331
+ * - Use `"global"` when you have multiple API clients (user service, auth service, etc.) that might make overlapping requests
332
+ * - Use `"local"` (default) for single-purpose clients or when you want strict isolation between different parts of your app
439
333
  *
440
334
  * @example
441
335
  * ```ts
442
- * const callMainApi = callApi.create({
443
- * baseURL: "https://main-api.com",
444
- * onResponseError: ({ response, options }) => {
445
- * if (options.meta?.userId) {
446
- * console.error(`User ${options.meta.userId} made an error`);
447
- * }
448
- * },
449
- * });
336
+ * // Local scope - each client has its own deduplication cache
337
+ * const userClient = createFetchClient({ baseURL: "/api/users" });
338
+ * const postClient = createFetchClient({ baseURL: "/api/posts" });
339
+ * // These clients won't share deduplication state
450
340
  *
451
- * const response = await callMainApi({
452
- * url: "https://example.com/api/data",
453
- * meta: { userId: "123" },
341
+ * // Global scope - share cache across related clients
342
+ * const userClient = createFetchClient({
343
+ * baseURL: "/api/users",
344
+ * dedupeCacheScope: "global",
345
+ * });
346
+ * const postClient = createFetchClient({
347
+ * baseURL: "/api/posts",
348
+ * dedupeCacheScope: "global",
454
349
  * });
350
+ * // These clients will share deduplication state
455
351
  * ```
352
+ *
353
+ * @default "local"
456
354
  */
457
- meta?: InferSchemaOutput<TSchema["meta"], TCallApiContext["Meta"]>;
458
- }>;
459
- type InferAuthOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["auth"], {
355
+ dedupeCacheScope?: "global" | "local";
460
356
  /**
461
- * Automatically add an Authorization header value.
357
+ * Unique namespace for the global deduplication cache when using `dedupeCacheScope: "global"`.
462
358
  *
463
- * Supports multiple authentication patterns:
464
- * - String: Direct authorization header value
465
- * - Auth object: Structured authentication configuration
359
+ * This creates logical groupings of deduplication caches. All instances with the same key
360
+ * will share the same cache namespace, allowing fine-grained control over which clients
361
+ * share deduplication state.
362
+ *
363
+ * **Best Practices:**
364
+ * - Use descriptive names that reflect the logical grouping (e.g., "user-service", "analytics-api")
365
+ * - Keep scope keys consistent across related API clients
366
+ * - Consider using different scope keys for different environments (dev, staging, prod)
367
+ * - Avoid overly broad scope keys that might cause unintended cache sharing
368
+ *
369
+ * **Cache Management:**
370
+ * - Each scope key maintains its own independent cache
371
+ * - Caches are automatically cleaned up when no references remain
372
+ * - Consider the memory implications of multiple global scopes
466
373
  *
467
374
  * @example
468
375
  * ```ts
469
- * const callMainApi = callApi.create({
470
- * baseURL: "https://main-api.com",
471
- * onRequest: ({ options }) => {
472
- * if (options.auth) {
473
- * options.headers.Authorization = options.auth;
474
- * }
475
- * },
376
+ * // Group related API clients together
377
+ * const userClient = createFetchClient({
378
+ * baseURL: "/api/users",
379
+ * dedupeCacheScope: "global",
380
+ * dedupeCacheScopeKey: "user-service"
381
+ * });
382
+ * const profileClient = createFetchClient({
383
+ * baseURL: "/api/profiles",
384
+ * dedupeCacheScope: "global",
385
+ * dedupeCacheScopeKey: "user-service" // Same scope - will share cache
476
386
  * });
477
387
  *
478
- * const response = await callMainApi({
479
- * url: "https://example.com/api/data",
480
- * auth: "Bearer 123456",
388
+ * // Separate analytics client with its own cache
389
+ * const analyticsClient = createFetchClient({
390
+ * baseURL: "/api/analytics",
391
+ * dedupeCacheScope: "global",
392
+ * dedupeCacheScopeKey: "analytics-service" // Different scope
393
+ * });
394
+ *
395
+ * // Environment-specific scoping
396
+ * const apiClient = createFetchClient({
397
+ * dedupeCacheScope: "global",
398
+ * dedupeCacheScopeKey: `api-${process.env.NODE_ENV}` // "api-development", "api-production", etc.
481
399
  * });
482
400
  * ```
401
+ *
402
+ * @default "default"
483
403
  */
484
- auth?: InferSchemaOutput<TSchema["auth"], AuthOption>;
485
- }>;
486
- type InferQueryOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["query"], {
404
+ dedupeCacheScopeKey?: "default" | AnyString | ((context: RequestContext) => string | undefined);
487
405
  /**
488
- * Parameters to be appended to the URL (i.e: /:id)
406
+ * Custom key generator for request deduplication.
407
+ *
408
+ * Override the default key generation strategy to control exactly which requests
409
+ * are considered duplicates. The default key combines URL, method, body, and
410
+ * relevant headers (excluding volatile ones like 'Date', 'Authorization', etc.).
411
+ *
412
+ * **Default Key Generation:**
413
+ * The auto-generated key includes:
414
+ * - Full request URL (including query parameters)
415
+ * - HTTP method (GET, POST, etc.)
416
+ * - Request body (for POST/PUT/PATCH requests)
417
+ * - Stable headers (excludes Date, Authorization, User-Agent, etc.)
418
+ *
419
+ * **Custom Key Best Practices:**
420
+ * - Include only the parts of the request that should affect deduplication
421
+ * - Avoid including volatile data (timestamps, random IDs, etc.)
422
+ * - Consider performance - simpler keys are faster to compute and compare
423
+ * - Ensure keys are deterministic for the same logical request
424
+ * - Use consistent key formats across your application
425
+ *
426
+ * **Performance Considerations:**
427
+ * - Function-based keys are computed on every request - keep them lightweight
428
+ * - String keys are fastest but least flexible
429
+ * - Consider caching expensive key computations if needed
430
+ *
431
+ * @example
432
+ * ```ts
433
+ * import { callApi } from "@zayne-labs/callapi";
434
+ *
435
+ * // Simple static key - useful for singleton requests
436
+ * const config = callApi("/api/config", {
437
+ * dedupeKey: "app-config",
438
+ * dedupeStrategy: "defer" // Share the same config across all requests
439
+ * });
440
+ *
441
+ * // URL and method only - ignore headers and body
442
+ * const userData = callApi("/api/user/123", {
443
+ * dedupeKey: (context) => `${context.options.method}:${context.options.fullURL}`
444
+ * });
445
+ *
446
+ * // Include specific headers in deduplication
447
+ * const apiCall = callApi("/api/data", {
448
+ * dedupeKey: (context) => {
449
+ * const authHeader = context.request.headers.get("Authorization");
450
+ * return `${context.options.fullURL}-${authHeader}`;
451
+ * }
452
+ * });
453
+ *
454
+ * // User-specific deduplication
455
+ * const userSpecificCall = callApi("/api/dashboard", {
456
+ * dedupeKey: (context) => {
457
+ * const userId = context.options.fullURL.match(/user\/(\d+)/)?.[1];
458
+ * return `dashboard-${userId}`;
459
+ * }
460
+ * });
461
+ *
462
+ * // Ignore certain query parameters
463
+ * const searchCall = callApi("/api/search?q=test&timestamp=123456", {
464
+ * dedupeKey: (context) => {
465
+ * const url = new URL(context.options.fullURL);
466
+ * url.searchParams.delete("timestamp"); // Remove volatile param
467
+ * return `search:${url.toString()}`;
468
+ * }
469
+ * });
470
+ * ```
471
+ *
472
+ * @default Auto-generated from request details
489
473
  */
490
- query?: InferSchemaOutput<TSchema["query"], Query>;
491
- }>;
492
- type EmptyString = "";
493
- type EmptyTuple = readonly [];
494
- type StringTuple = readonly string[];
495
- type PossibleParamNamePatterns = `${string}:${string}` | `${string}{${string}}${"" | AnyString}`;
496
- 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;
497
- type ConvertParamNamesToRecord<TParamNames extends StringTuple> = Prettify<TParamNames extends (readonly [infer TFirstParamName extends string, ...infer TRemainingParamNames extends StringTuple]) ? Record<TFirstParamName, AllowedQueryParamValues> & ConvertParamNamesToRecord<TRemainingParamNames> : NonNullable<unknown>>;
498
- type ConvertParamNamesToTuple<TParamNames extends StringTuple> = TParamNames extends readonly [string, ...infer TRemainingParamNames extends StringTuple] ? [AllowedQueryParamValues, ...ConvertParamNamesToTuple<TRemainingParamNames>] : [];
499
- type InferParamsFromRoute<TCurrentRoute> = ExtractRouteParamNames<TCurrentRoute> extends StringTuple ? ExtractRouteParamNames<TCurrentRoute> extends EmptyTuple ? Params : ConvertParamNamesToRecord<ExtractRouteParamNames<TCurrentRoute>> | ConvertParamNamesToTuple<ExtractRouteParamNames<TCurrentRoute>> : Params;
500
- 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>;
501
- type InferParamsOption<TSchema extends CallApiSchema, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string> = MakeParamsOptionRequired<TSchema["params"], TBaseSchemaRoutes, TCurrentRouteSchemaKey, {
474
+ dedupeKey?: string | ((context: RequestContext) => string | undefined);
502
475
  /**
503
- * Parameters to be appended to the URL (i.e: /:id)
476
+ * Strategy for handling duplicate requests. Can be a static string or callback function.
477
+ *
478
+ * **Available Strategies:**
479
+ * - `"cancel"`: Cancel previous request when new one starts (good for search)
480
+ * - `"defer"`: Share response between duplicate requests (good for config loading)
481
+ * - `"none"`: No deduplication, all requests execute independently
482
+ *
483
+ * @example
484
+ * ```ts
485
+ * // Static strategies
486
+ * const searchClient = createFetchClient({
487
+ * dedupeStrategy: "cancel" // Cancel previous searches
488
+ * });
489
+ *
490
+ * const configClient = createFetchClient({
491
+ * dedupeStrategy: "defer" // Share config across components
492
+ * });
493
+ *
494
+ * // Dynamic strategy based on request
495
+ * const smartClient = createFetchClient({
496
+ * dedupeStrategy: (context) => {
497
+ * return context.options.method === "GET" ? "defer" : "cancel";
498
+ * }
499
+ * });
500
+ *
501
+ * // Search-as-you-type with cancel strategy
502
+ * const handleSearch = async (query: string) => {
503
+ * try {
504
+ * const { data } = await callApi("/api/search", {
505
+ * method: "POST",
506
+ * body: { query },
507
+ * dedupeStrategy: "cancel",
508
+ * dedupeKey: "search" // Cancel previous searches, only latest one goes through
509
+ * });
510
+ *
511
+ * updateSearchResults(data);
512
+ * } catch (error) {
513
+ * if (error.name === "AbortError") {
514
+ * // Previous search cancelled - (expected behavior)
515
+ * return;
516
+ * }
517
+ * console.error("Search failed:", error);
518
+ * }
519
+ * };
520
+ *
521
+ * ```
522
+ *
523
+ * @default "cancel"
504
524
  */
505
- params?: InferSchemaOutput<TSchema["params"], InferParamsFromRoute<TCurrentRouteSchemaKey>>;
506
- }>;
507
- 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>;
508
- type InferPluginExtraOptions<TPluginArray extends CallApiPlugin[]> = UnionToIntersection<TPluginArray extends Array<infer TPlugin> ? TPlugin extends CallApiPlugin ? TPlugin["defineExtraOptions"] extends AnyFunction$1<infer TReturnedSchema> ? InferSchemaOutput<TReturnedSchema> : never : never : never>;
509
- type ResultModeOption<TErrorData, TResultMode extends ResultModeType> = TErrorData extends false ? {
510
- resultMode: "onlyData";
511
- } : TErrorData extends false | undefined ? {
512
- resultMode?: "onlyData";
513
- } : {
514
- resultMode?: TResultMode;
515
- };
516
- type ThrowOnErrorUnion = boolean;
517
- type ThrowOnErrorType<TErrorData, TThrowOnError extends ThrowOnErrorUnion> = TThrowOnError | ((context: ErrorContext<{
518
- ErrorData: TErrorData;
519
- }>) => TThrowOnError);
520
- type ThrowOnErrorOption<TErrorData, TThrowOnError extends ThrowOnErrorUnion> = TErrorData extends false ? {
521
- throwOnError: true;
522
- } : TErrorData extends false | undefined ? {
523
- throwOnError?: true;
524
- } : {
525
- throwOnError?: ThrowOnErrorType<TErrorData, TThrowOnError>;
525
+ dedupeStrategy?: DedupeStrategyUnion | ((context: RequestContext) => DedupeStrategyUnion);
526
526
  };
527
527
  //#endregion
528
528
  //#region src/middlewares.d.ts
529
529
  type FetchImpl = UnmaskType<(input: string | Request | URL, init?: RequestInit) => Promise<Response>>;
530
+ type FetchMiddlewareContext<TCallApiContext extends CallApiContext> = RequestContext<TCallApiContext> & {
531
+ fetchImpl: FetchImpl;
532
+ };
530
533
  interface Middlewares<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> {
531
534
  /**
532
535
  * Wraps the fetch implementation to intercept requests at the network layer.
@@ -544,509 +547,476 @@ interface Middlewares<TCallApiContext extends NoInfer<CallApiContext> = DefaultC
544
547
  *
545
548
  * fetchMiddleware: (ctx) => async (input, init) => {
546
549
  * const key = input.toString();
547
- * if (cache.has(key)) return cache.get(key).clone();
550
+ *
551
+ * const cachedResponse = cache.get(key);
552
+ *
553
+ * if (cachedResponse) {
554
+ * return cachedResponse.clone();
555
+ * }
548
556
  *
549
557
  * const response = await ctx.fetchImpl(input, init);
550
558
  * cache.set(key, response.clone());
559
+ *
551
560
  * return response;
552
561
  * }
553
562
  *
554
563
  * // Handle offline
555
- * fetchMiddleware: (ctx) => async (input, init) => {
564
+ * fetchMiddleware: (ctx) => async (...parameters) => {
556
565
  * if (!navigator.onLine) {
557
566
  * return new Response('{"error": "offline"}', { status: 503 });
558
567
  * }
559
- * return ctx.fetchImpl(input, init);
568
+ *
569
+ * return ctx.fetchImpl(...parameters);
560
570
  * }
561
571
  * ```
562
572
  */
563
- fetchMiddleware?: (context: RequestContext<TCallApiContext> & {
564
- fetchImpl: FetchImpl;
565
- }) => FetchImpl;
573
+ fetchMiddleware?: (context: FetchMiddlewareContext<TCallApiContext>) => FetchImpl;
574
+ }
575
+ //#endregion
576
+ //#region src/types/standard-schema.d.ts
577
+ /**
578
+ * The Standard Schema interface.
579
+ * @see https://github.com/standard-schema/standard-schema
580
+ */
581
+ /** The Standard Typed interface. This is a base type extended by other specs. */
582
+ interface StandardTypedV1<Input = unknown, Output = Input> {
583
+ /** The Standard properties. */
584
+ readonly "~standard": StandardTypedV1.Props<Input, Output>;
585
+ }
586
+ declare namespace StandardTypedV1 {
587
+ /** The Standard Typed properties interface. */
588
+ interface Props<Input = unknown, Output = Input> {
589
+ /** Inferred types associated with the schema. */
590
+ readonly types?: Types<Input, Output> | undefined;
591
+ /** The vendor name of the schema library. */
592
+ readonly vendor: string;
593
+ /** The version number of the standard. */
594
+ readonly version: 1;
595
+ }
596
+ /** The Standard Typed types interface. */
597
+ interface Types<Input = unknown, Output = Input> {
598
+ /** The input type of the schema. */
599
+ readonly input: Input;
600
+ /** The output type of the schema. */
601
+ readonly output: Output;
602
+ }
603
+ /** Infers the input type of a Standard Typed. */
604
+ type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
605
+ /** Infers the output type of a Standard Typed. */
606
+ type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
607
+ }
608
+ /** The Standard Schema interface. */
609
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
610
+ /** The Standard Schema properties. */
611
+ readonly "~standard": StandardSchemaV1.Props<Input, Output>;
612
+ }
613
+ declare namespace StandardSchemaV1 {
614
+ /** The Standard Schema properties interface. */
615
+ interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
616
+ /** Validates unknown input values. */
617
+ readonly validate: (value: unknown, options?: StandardSchemaV1.Options) => Promise<Result<Output>> | Result<Output>;
618
+ }
619
+ /** The result interface of the validate function. */
620
+ type Result<Output> = FailureResult | SuccessResult<Output>;
621
+ /** The result interface if validation succeeds. */
622
+ interface SuccessResult<Output> {
623
+ /** A falsy value for `issues` indicates success. */
624
+ readonly issues?: undefined;
625
+ /** The typed output value. */
626
+ readonly value: Output;
627
+ }
628
+ interface Options {
629
+ /** Explicit support for additional vendor-specific parameters, if needed. */
630
+ readonly libraryOptions?: Record<string, unknown> | undefined;
631
+ }
632
+ /** The result interface if validation fails. */
633
+ interface FailureResult {
634
+ /** The issues of failed validation. */
635
+ readonly issues: readonly Issue[];
636
+ }
637
+ /** The issue interface of the failure output. */
638
+ interface Issue {
639
+ /** The error message of the issue. */
640
+ readonly message: string;
641
+ /** The path of the issue, if any. */
642
+ readonly path?: ReadonlyArray<PathSegment | PropertyKey> | undefined;
643
+ }
644
+ /** The path segment interface of the issue. */
645
+ interface PathSegment {
646
+ /** The key representing a path segment. */
647
+ readonly key: PropertyKey;
648
+ }
649
+ /** The Standard types interface. */
650
+ type Types<Input = unknown, Output = Input> = StandardTypedV1.Types<Input, Output>;
651
+ /** Infers the input type of a Standard. */
652
+ type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
653
+ /** Infers the output type of a Standard. */
654
+ type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
566
655
  }
567
656
  //#endregion
568
- //#region src/plugins.d.ts
569
- type PluginSetupContext<TCallApiContext extends CallApiContext = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
570
- initURL: string;
571
- };
572
- type PluginInitResult<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Partial<Omit<PluginSetupContext<TCallApiContext>, "initURL" | "request"> & {
573
- initURL: InitURLOrURLObject;
574
- request: CallApiRequestOptions;
575
- }>;
576
- type PluginHooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> = HooksOrHooksArray<OverrideCallApiContext<TCallApiContext, {
577
- Data: DefaultDataType extends TCallApiContext["Data"] ? never : TCallApiContext["Data"];
578
- ErrorData: DefaultDataType extends TCallApiContext["ErrorData"] ? never : TCallApiContext["ErrorData"];
579
- }>>;
580
- interface CallApiPlugin<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
581
- /**
582
- * Defines additional options that can be passed to callApi
583
- */
584
- defineExtraOptions?: (...params: never[]) => unknown;
585
- /**
586
- * A description for the plugin
587
- */
588
- description?: string;
589
- /**
590
- * Hooks for the plugin
591
- */
592
- hooks?: PluginHooks<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginHooks<TCallApiContext>>);
593
- /**
594
- * A unique id for the plugin
595
- */
596
- id: string;
597
- /**
598
- * Middlewares that for the plugin
599
- */
600
- middlewares?: Middlewares<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<Middlewares<TCallApiContext>>);
601
- /**
602
- * A name for the plugin
603
- */
604
- name: string;
657
+ //#region src/validation.d.ts
658
+ type ResultVariant = "infer-input" | "infer-output";
659
+ type InferSchemaResult<TSchema, TFallbackResult, TResultVariant extends ResultVariant> = undefined extends TSchema ? TFallbackResult : TSchema extends StandardSchemaV1 ? TResultVariant extends "infer-input" ? StandardSchemaV1.InferInput<TSchema> : StandardSchemaV1.InferOutput<TSchema> : TSchema extends AnyFunction$1<infer TResult> ? Awaited<TResult> : TFallbackResult;
660
+ type InferSchemaOutput<TSchema, TFallbackResult = unknown> = InferSchemaResult<TSchema, TFallbackResult, "infer-output">;
661
+ type BooleanObject = { [Key in keyof CallApiSchema]: boolean };
662
+ interface CallApiSchemaConfig {
605
663
  /**
606
- * Base schema for the client.
664
+ * The base url of the schema. By default it's the baseURL of the callApi instance.
607
665
  */
608
- schema?: BaseCallApiSchemaAndConfig;
666
+ baseURL?: "" | AnyString;
609
667
  /**
610
- * A function that will be called when the plugin is initialized. This will be called before the any of the other internal functions.
668
+ * Disables runtime validation for the schema.
611
669
  */
612
- setup?: (context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginInitResult<TCallApiContext>> | Awaitable<void>;
670
+ disableRuntimeValidation?: boolean | BooleanObject;
613
671
  /**
614
- * A version for the plugin
672
+ * If `true`, the original input value will be used instead of the transformed/validated output.
673
+ *
674
+ * When true, the original input is returned unchanged after validation, ignoring any schema-level
675
+ * transformations such as type coercion, default values, or field mapping. Only the validation
676
+ * step is executed; the resulting value is discarded in favor of the raw input.
615
677
  */
616
- version?: string;
617
- }
618
- //#endregion
619
- //#region src/types/default-types.d.ts
620
- type DefaultDataType = unknown;
621
- type DefaultPluginArray = CallApiPlugin[];
622
- type DefaultThrowOnError = boolean;
623
- type DefaultMetaObject = Record<string, unknown>;
624
- type DefaultCallApiContext = Omit<Required<CallApiContext>, "Meta"> & {
625
- Meta: GlobalMeta;
626
- };
627
- //#endregion
628
- //#region src/hooks.d.ts
629
- interface Hooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
678
+ disableRuntimeValidationTransform?: boolean | BooleanObject;
630
679
  /**
631
- * Hook called when any error occurs within the request/response lifecycle.
632
- *
633
- * This is a unified error handler that catches both request errors (network failures,
634
- * timeouts, etc.) and response errors (HTTP error status codes). It's essentially
635
- * a combination of `onRequestError` and `onResponseError` hooks.
680
+ * Optional url prefix that will be substituted for the `baseURL` of the schemaConfig at runtime.
636
681
  *
637
- * @param context - Error context containing error details, request info, and response (if available)
638
- * @returns Promise or void - Hook can be async or sync
682
+ * Enables a short, stable prefix for routes while keeping the full `baseURL` centralized in config.
683
+ * Keeps route definitions concise and shields them from changes to the underlying base URL.
639
684
  */
640
- onError?: (context: ErrorContext<TCallApiContext>) => Awaitable<unknown>;
685
+ prefix?: "" | AnyString;
641
686
  /**
642
- * Hook called before the HTTP request is sent and before any internal processing of the request object begins.
643
- *
644
- * This is the ideal place to modify request headers, add authentication,
645
- * implement request logging, or perform any setup before the network call.
687
+ * Controls the strictness of API route validation.
646
688
  *
647
- * @param context - Request context with mutable request object and configuration
648
- * @returns Promise or void - Hook can be async or sync
689
+ * When true:
690
+ * - Only routes explicitly defined in the schema will be considered valid to typescript and the runtime.
691
+ * - Attempting to call routes not defined in the schema will result in both type errors and runtime validation errors.
692
+ * - Useful for ensuring API calls conform exactly to your schema definition
649
693
  *
694
+ * When false or undefined (default):
695
+ * - All routes will be allowed, whether they are defined in the schema or not
650
696
  */
651
- onRequest?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
697
+ strict?: boolean;
698
+ }
699
+ interface CallApiSchema {
700
+ auth?: StandardSchemaV1<AuthOption | undefined> | ((auth: AuthOption) => Awaitable<AuthOption | undefined>);
652
701
  /**
653
- * Hook called when an error occurs during the fetch request itself.
654
- *
655
- * This handles network-level errors like connection failures, timeouts,
656
- * DNS resolution errors, or other issues that prevent getting an HTTP response.
657
- * Note that HTTP error status codes (4xx, 5xx) are handled by `onResponseError`.
658
- *
659
- * @param context - Request error context with error details and null response
660
- * @returns Promise or void - Hook can be async or sync
702
+ * The schema to use for validating the request body.
661
703
  */
662
- onRequestError?: (context: RequestErrorContext<TCallApiContext>) => Awaitable<unknown>;
704
+ body?: StandardSchemaV1<Body | undefined> | ((body: Body) => Awaitable<Body | undefined>);
663
705
  /**
664
- * Hook called just before the HTTP request is sent and after the request has been processed.
665
- *
666
- * @param context - Request context with mutable request object and configuration
706
+ * The schema to use for validating the response data.
667
707
  */
668
- onRequestReady?: (context: RequestContext<TCallApiContext>) => Awaitable<unknown>;
708
+ data?: StandardSchemaV1 | ((data: unknown) => unknown);
669
709
  /**
670
- * Hook called during upload stream progress tracking.
671
- *
672
- * This hook is triggered when uploading data (like file uploads) and provides
673
- * progress information about the upload. Useful for implementing progress bars
674
- * or upload status indicators.
675
- *
676
- * @param context - Request stream context with progress event and request instance
677
- * @returns Promise or void - Hook can be async or sync
678
- *
710
+ * The schema to use for validating the response error data.
679
711
  */
680
- onRequestStream?: (context: RequestStreamContext<TCallApiContext>) => Awaitable<unknown>;
712
+ errorData?: StandardSchemaV1 | ((errorData: unknown) => unknown);
681
713
  /**
682
- * Hook called when any HTTP response is received from the API.
683
- *
684
- * This hook is triggered for both successful (2xx) and error (4xx, 5xx) responses.
685
- * It's useful for response logging, metrics collection, or any processing that
686
- * should happen regardless of response status.
687
- *
688
- * @param context - Response context with either success data or error information
689
- * @returns Promise or void - Hook can be async or sync
690
- *
714
+ * The schema to use for validating the request headers.
691
715
  */
692
- onResponse?: (context: ResponseContext<TCallApiContext>) => Awaitable<unknown>;
716
+ headers?: StandardSchemaV1<HeadersOption | undefined> | ((headers: HeadersOption) => Awaitable<HeadersOption | undefined>);
693
717
  /**
694
- * Hook called when an HTTP error response (4xx, 5xx) is received from the API.
695
- *
696
- * This handles server-side errors where an HTTP response was successfully received
697
- * but indicates an error condition. Different from `onRequestError` which handles
698
- * network-level failures.
699
- *
700
- * @param context - Response error context with HTTP error details and response
701
- * @returns Promise or void - Hook can be async or sync
718
+ * The schema to use for validating the meta option.
702
719
  */
703
- onResponseError?: (context: ResponseErrorContext<TCallApiContext>) => Awaitable<unknown>;
720
+ meta?: StandardSchemaV1<GlobalMeta | undefined> | ((meta: GlobalMeta) => Awaitable<GlobalMeta | undefined>);
704
721
  /**
705
- * Hook called during download stream progress tracking.
706
- *
707
- * This hook is triggered when downloading data (like file downloads) and provides
708
- * progress information about the download. Useful for implementing progress bars
709
- * or download status indicators.
710
- *
711
- * @param context - Response stream context with progress event and response
712
- * @returns Promise or void - Hook can be async or sync
713
- *
722
+ * The schema to use for validating the request method.
714
723
  */
715
- onResponseStream?: (context: ResponseStreamContext<TCallApiContext>) => Awaitable<unknown>;
724
+ method?: StandardSchemaV1<MethodUnion | undefined> | ((method: MethodUnion) => Awaitable<MethodUnion | undefined>);
716
725
  /**
717
- * Hook called when a request is being retried.
718
- *
719
- * This hook is triggered before each retry attempt, providing information about
720
- * the previous failure and the current retry attempt number. Useful for implementing
721
- * custom retry logic, exponential backoff, or retry logging.
722
- *
723
- * @param context - Retry context with error details and retry attempt count
724
- * @returns Promise or void - Hook can be async or sync
725
- *
726
+ * The schema to use for validating the request url parameters.
726
727
  */
727
- onRetry?: (response: RetryContext<TCallApiContext>) => Awaitable<unknown>;
728
+ params?: StandardSchemaV1<Params | undefined> | ((params: Params) => Awaitable<Params | undefined>);
728
729
  /**
729
- * Hook called when a successful response (2xx status) is received from the API.
730
- *
731
- * This hook is triggered only for successful responses and provides access to
732
- * the parsed response data. Ideal for success logging, caching, or post-processing
733
- * of successful API responses.
734
- *
735
- * @param context - Success context with parsed response data and response object
736
- * @returns Promise or void - Hook can be async or sync
737
- *
730
+ * The schema to use for validating the request url queries.
738
731
  */
739
- onSuccess?: (context: SuccessContext<TCallApiContext>) => Awaitable<unknown>;
732
+ query?: StandardSchemaV1<Query | undefined> | ((query: Query) => Awaitable<Query | undefined>);
733
+ }
734
+ declare const routeKeyMethods: readonly ["delete", "get", "patch", "post", "put"];
735
+ type RouteKeyMethods = (typeof routeKeyMethods)[number];
736
+ type RouteKeyMethodsURLUnion = `@${RouteKeyMethods}/`;
737
+ type BaseSchemaRouteKeyPrefixes = FallBackRouteSchemaKey | RouteKeyMethodsURLUnion;
738
+ type BaseCallApiSchemaRoutes = Partial<Record<AnyString | BaseSchemaRouteKeyPrefixes, CallApiSchema>>;
739
+ type BaseCallApiSchemaAndConfig = {
740
+ config?: CallApiSchemaConfig;
741
+ routes: BaseCallApiSchemaRoutes;
742
+ };
743
+ //#endregion
744
+ //#region src/url.d.ts
745
+ type AllowedQueryParamValues = UnmaskType<boolean | number | string>;
746
+ type RecordStyleParams = UnmaskType<Record<string, AllowedQueryParamValues>>;
747
+ type TupleStyleParams = UnmaskType<AllowedQueryParamValues[]>;
748
+ type Params = UnmaskType<RecordStyleParams | TupleStyleParams>;
749
+ type Query = UnmaskType<Record<string, AllowedQueryParamValues>>;
750
+ type InitURLOrURLObject = AnyString | RouteKeyMethodsURLUnion | URL;
751
+ interface URLOptions {
740
752
  /**
741
- * Hook called when a validation error occurs.
753
+ * Base URL for all API requests. Will only be prepended to relative URLs.
742
754
  *
743
- * This hook is triggered when request or response data fails validation against
744
- * a defined schema. It provides access to the validation error details and can
745
- * be used for custom error handling, logging, or fallback behavior.
755
+ * Absolute URLs (starting with http/https) will not be prepended by the baseURL.
746
756
  *
747
- * @param context - Validation error context with error details and response (if available)
748
- * @returns Promise or void - Hook can be async or sync
757
+ * @example
758
+ * ```ts
759
+ * // Set base URL for all requests
760
+ * baseURL: "https://api.example.com/v1"
761
+ *
762
+ * // Then use relative URLs in requests
763
+ * callApi("/users") // → https://api.example.com/v1/users
764
+ * callApi("/posts/123") // → https://api.example.com/v1/posts/123
749
765
  *
766
+ * // Environment-specific base URLs
767
+ * baseURL: process.env.NODE_ENV === "production"
768
+ * ? "https://api.example.com"
769
+ * : "http://localhost:3000/api"
770
+ * ```
750
771
  */
751
- onValidationError?: (context: ValidationErrorContext<TCallApiContext>) => Awaitable<unknown>;
752
- }
753
- type HooksOrHooksArray<TCallApiContext extends NoInfer<CallApiContext> = DefaultCallApiContext> = { [Key in keyof Hooks<TCallApiContext>]: Hooks<TCallApiContext>[Key] | Array<Hooks<TCallApiContext>[Key]> };
754
- interface HookConfigOptions {
772
+ baseURL?: string;
755
773
  /**
756
- * Controls the execution mode of all composed hooks (main + plugin hooks).
757
- *
758
- * - **"parallel"**: All hooks execute simultaneously via Promise.all() for better performance
759
- * - **"sequential"**: All hooks execute one by one in registration order via await in a loop
774
+ * Resolved request URL after processing baseURL, parameters, and query strings (readonly)
760
775
  *
761
- * This affects how ALL hooks execute together, regardless of their source (main or plugin).
776
+ * This is the final URL that will be used for the HTTP request, computed from
777
+ * baseURL, initURL, params, and query parameters.
762
778
  *
763
- * @default "parallel"
764
779
  */
765
- hooksExecutionMode?: "parallel" | "sequential";
766
- }
767
- type RequestContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = {
780
+ readonly fullURL?: string;
768
781
  /**
769
- * Base configuration object passed to createFetchClient.
782
+ * The original URL string passed to the callApi instance (readonly)
770
783
  *
771
- * Contains the foundational configuration that applies to all requests
772
- * made by this client instance, such as baseURL, default headers, and
773
- * global options.
774
- */
775
- baseConfig: Exclude<BaseCallApiConfig, AnyFunction$1>;
776
- /**
777
- * Instance-specific configuration object passed to the callApi instance.
784
+ * This preserves the original URL as provided, including any method modifiers like "@get/" or "@post/".
778
785
  *
779
- * Contains configuration specific to this particular API call, which
780
- * can override or extend the base configuration.
781
786
  */
782
- config: CallApiConfig;
787
+ readonly initURL?: string;
783
788
  /**
784
- * Merged options combining base config, instance config, and default options.
789
+ * The URL string after normalization, with method modifiers removed(readonly)
785
790
  *
786
- * This is the final resolved configuration that will be used for the request,
787
- * with proper precedence applied (instance > base > defaults).
788
- */
789
- options: CallApiExtraOptionsForHooks<TCallApiContext>;
790
- /**
791
- * Merged request object ready to be sent.
791
+ * Method modifiers like "@get/", "@post/" are stripped to create a clean URL
792
+ * for parameter substitution and final URL construction.
792
793
  *
793
- * Contains the final request configuration including URL, method, headers,
794
- * body, and other fetch options. This object can be modified in onRequest
795
- * hooks to customize the outgoing request.
796
- */
797
- request: CallApiRequestOptionsForHooks;
798
- };
799
- type ValidationErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
800
- error: PossibleValidationError;
801
- response: Response | null;
802
- };
803
- type SuccessContext<TCallApiContext extends Pick<CallApiContext, "Data" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
804
- data: TCallApiContext["Data"];
805
- response: Response;
806
- };
807
- type ResponseContext<TCallApiContext extends Pick<CallApiContext, "Data" | "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & (Prettify<CallApiResultSuccessVariant<TCallApiContext["Data"]>> | Prettify<Extract<CallApiResultErrorVariant<TCallApiContext["ErrorData"]>, {
808
- error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
809
- }>>);
810
- type RequestErrorContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
811
- error: PossibleJavaScriptError;
812
- response: null;
813
- };
814
- type ErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & ({
815
- error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
816
- response: Response;
817
- } | {
818
- error: PossibleJavaScriptOrValidationError;
819
- response: Response | null;
820
- });
821
- type ResponseErrorContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = Extract<ErrorContext<TCallApiContext>, {
822
- error: PossibleHTTPError<TCallApiContext["ErrorData"]>;
823
- }> & RequestContext<TCallApiContext>;
824
- type RetryContext<TCallApiContext extends Pick<CallApiContext, "ErrorData" | "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = ErrorContext<TCallApiContext> & {
825
- retryAttemptCount: number;
826
- };
827
- type RequestStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
828
- event: StreamProgressEvent;
829
- requestInstance: Request;
830
- };
831
- type ResponseStreamContext<TCallApiContext extends Pick<CallApiContext, "InferredExtraOptions" | "Meta"> = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
832
- event: StreamProgressEvent;
833
- response: Response;
834
- };
835
- //#endregion
836
- //#region src/dedupe.d.ts
837
- type DedupeStrategyUnion = UnmaskType<"cancel" | "defer" | "none">;
838
- type DedupeOptionKeys = Exclude<keyof DedupeOptions, "dedupe">;
839
- type InnerDedupeOptions = { [Key in DedupeOptionKeys as RemovePrefix<"dedupe", Key>]?: DedupeOptions[Key] };
840
- type DedupeOptions = {
841
- /**
842
- * All dedupe options in a single object instead of separate properties
843
794
  */
844
- dedupe?: InnerDedupeOptions;
795
+ readonly initURLNormalized?: string;
845
796
  /**
846
- * Controls the scope of request deduplication caching.
847
- *
848
- * - `"global"`: Shares deduplication cache across all `createFetchClient` instances with the same `dedupeCacheScopeKey`.
849
- * Useful for applications with multiple API clients that should share deduplication state.
850
- * - `"local"`: Limits deduplication to requests within the same `createFetchClient` instance.
851
- * Provides better isolation and is recommended for most use cases.
852
- *
797
+ * Parameters to be substituted into URL path segments.
853
798
  *
854
- * **Real-world Scenarios:**
855
- * - Use `"global"` when you have multiple API clients (user service, auth service, etc.) that might make overlapping requests
856
- * - Use `"local"` (default) for single-purpose clients or when you want strict isolation between different parts of your app
799
+ * Supports both object-style (named parameters) and array-style (positional parameters)
800
+ * for flexible URL parameter substitution.
857
801
  *
858
802
  * @example
859
- * ```ts
860
- * // Local scope - each client has its own deduplication cache
861
- * const userClient = createFetchClient({ baseURL: "/api/users" });
862
- * const postClient = createFetchClient({ baseURL: "/api/posts" });
863
- * // These clients won't share deduplication state
803
+ * ```typescript
804
+ * // Object-style parameters (recommended)
805
+ * const namedParams: URLOptions = {
806
+ * initURL: "/users/:userId/posts/:postId",
807
+ * params: { userId: "123", postId: "456" }
808
+ * };
809
+ * // Results in: /users/123/posts/456
864
810
  *
865
- * // Global scope - share cache across related clients
866
- * const userClient = createFetchClient({
867
- * baseURL: "/api/users",
868
- * dedupeCacheScope: "global",
869
- * });
870
- * const postClient = createFetchClient({
871
- * baseURL: "/api/posts",
872
- * dedupeCacheScope: "global",
873
- * });
874
- * // These clients will share deduplication state
875
- * ```
811
+ * // Array-style parameters (positional)
812
+ * const positionalParams: URLOptions = {
813
+ * initURL: "/users/:userId/posts/:postId",
814
+ * params: ["123", "456"] // Maps in order: userId=123, postId=456
815
+ * };
816
+ * // Results in: /users/123/posts/456
876
817
  *
877
- * @default "local"
818
+ * // Single parameter
819
+ * const singleParam: URLOptions = {
820
+ * initURL: "/users/:id",
821
+ * params: { id: "user-123" }
822
+ * };
823
+ * // Results in: /users/user-123
824
+ * ```
878
825
  */
879
- dedupeCacheScope?: "global" | "local";
826
+ params?: Params;
880
827
  /**
881
- * Unique namespace for the global deduplication cache when using `dedupeCacheScope: "global"`.
882
- *
883
- * This creates logical groupings of deduplication caches. All instances with the same key
884
- * will share the same cache namespace, allowing fine-grained control over which clients
885
- * share deduplication state.
886
- *
887
- * **Best Practices:**
888
- * - Use descriptive names that reflect the logical grouping (e.g., "user-service", "analytics-api")
889
- * - Keep scope keys consistent across related API clients
890
- * - Consider using different scope keys for different environments (dev, staging, prod)
891
- * - Avoid overly broad scope keys that might cause unintended cache sharing
828
+ * Query parameters to append to the URL as search parameters.
892
829
  *
893
- * **Cache Management:**
894
- * - Each scope key maintains its own independent cache
895
- * - Caches are automatically cleaned up when no references remain
896
- * - Consider the memory implications of multiple global scopes
830
+ * These will be serialized into the URL query string using standard
831
+ * URL encoding practices.
897
832
  *
898
833
  * @example
899
- * ```ts
900
- * // Group related API clients together
901
- * const userClient = createFetchClient({
902
- * baseURL: "/api/users",
903
- * dedupeCacheScope: "global",
904
- * dedupeCacheScopeKey: "user-service"
905
- * });
906
- * const profileClient = createFetchClient({
907
- * baseURL: "/api/profiles",
908
- * dedupeCacheScope: "global",
909
- * dedupeCacheScopeKey: "user-service" // Same scope - will share cache
910
- * });
911
- *
912
- * // Separate analytics client with its own cache
913
- * const analyticsClient = createFetchClient({
914
- * baseURL: "/api/analytics",
915
- * dedupeCacheScope: "global",
916
- * dedupeCacheScopeKey: "analytics-service" // Different scope
917
- * });
834
+ * ```typescript
835
+ * // Basic query parameters
836
+ * const queryOptions: URLOptions = {
837
+ * initURL: "/users",
838
+ * query: {
839
+ * page: 1,
840
+ * limit: 10,
841
+ * search: "john doe",
842
+ * active: true
843
+ * }
844
+ * };
845
+ * // Results in: /users?page=1&limit=10&search=john%20doe&active=true
918
846
  *
919
- * // Environment-specific scoping
920
- * const apiClient = createFetchClient({
921
- * dedupeCacheScope: "global",
922
- * dedupeCacheScopeKey: `api-${process.env.NODE_ENV}` // "api-development", "api-production", etc.
923
- * });
847
+ * // Filtering and sorting
848
+ * const filterOptions: URLOptions = {
849
+ * initURL: "/products",
850
+ * query: {
851
+ * category: "electronics",
852
+ * minPrice: 100,
853
+ * maxPrice: 500,
854
+ * sortBy: "price",
855
+ * order: "asc"
856
+ * }
857
+ * };
858
+ * // Results in: /products?category=electronics&minPrice=100&maxPrice=500&sortBy=price&order=asc
924
859
  * ```
925
- *
926
- * @default "default"
927
860
  */
928
- dedupeCacheScopeKey?: "default" | AnyString | ((context: RequestContext) => string | undefined);
861
+ query?: Query;
862
+ }
863
+ //#endregion
864
+ //#region src/types/conditional-types.d.ts
865
+ /**
866
+ * @description Makes a type partial if the output type of TSchema is not provided or has undefined in the union, otherwise makes it required
867
+ */
868
+ type MakeSchemaOptionRequiredIfDefined<TSchemaOption extends CallApiSchema[keyof CallApiSchema], TObject> = undefined extends InferSchemaOutput<TSchemaOption, undefined> ? TObject : Required<TObject>;
869
+ type MergePrefixWithRouteKey<TPrefix extends string, TRouteKey extends string> = TRouteKey extends `@${infer TMethod extends RouteKeyMethods}/${infer TRestOfRoutKey}` ? `@${TMethod}/${TPrefix extends `/${infer TPrefixWithoutSlash}` ? TPrefixWithoutSlash : TPrefix}${TRestOfRoutKey}` : `${TPrefix}${TRouteKey}`;
870
+ type ApplyURLBasedConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["prefix"] extends string ? MergePrefixWithRouteKey<TSchemaConfig["prefix"], TSchemaRouteKeys> : TSchemaConfig["baseURL"] extends string ? MergePrefixWithRouteKey<TSchemaConfig["baseURL"], TSchemaRouteKeys> : TSchemaRouteKeys;
871
+ type ApplyStrictConfig<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = TSchemaConfig["strict"] extends true ? TSchemaRouteKeys :
872
+ // eslint-disable-next-line perfectionist/sort-union-types -- Don't sort union types
873
+ TSchemaRouteKeys | Exclude<InitURLOrURLObject, RouteKeyMethodsURLUnion>;
874
+ type ApplySchemaConfiguration<TSchemaConfig extends CallApiSchemaConfig, TSchemaRouteKeys extends string> = ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, TSchemaRouteKeys>>;
875
+ type InferAllRouteKeys<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = ApplySchemaConfiguration<TSchemaConfig, Exclude<Extract<keyof TBaseSchemaRoutes, string>, FallBackRouteSchemaKey>>;
876
+ type InferInitURL<TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TSchemaConfig extends CallApiSchemaConfig> = keyof TBaseSchemaRoutes extends never ? InitURLOrURLObject : InferAllRouteKeys<TBaseSchemaRoutes, TSchemaConfig>;
877
+ 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;
878
+ type JsonPrimitive = boolean | number | string | null | undefined;
879
+ type SerializableObject = Record<PropertyKey, unknown>;
880
+ type SerializableArray = Array<JsonPrimitive | SerializableObject> | ReadonlyArray<JsonPrimitive | SerializableObject>;
881
+ type Body = UnmaskType<Exclude<RequestInit["body"], undefined> | SerializableArray | SerializableObject>;
882
+ type InferBodyOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["body"], {
883
+ /**
884
+ * Body of the request, can be a object or any other supported body type.
885
+ */
886
+ body?: InferSchemaOutput<TSchema["body"], Body>;
887
+ }>;
888
+ type MethodUnion = UnmaskType<"CONNECT" | "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT" | "TRACE" | AnyString>;
889
+ type InferMethodFromURL<TInitURL> = string extends TInitURL ? MethodUnion : TInitURL extends `@${infer TMethod extends RouteKeyMethods}/${string}` ? Uppercase<TMethod> : MethodUnion;
890
+ type InferMethodOption<TSchema extends CallApiSchema, TInitURL> = MakeSchemaOptionRequiredIfDefined<TSchema["method"], {
929
891
  /**
930
- * Custom key generator for request deduplication.
931
- *
932
- * Override the default key generation strategy to control exactly which requests
933
- * are considered duplicates. The default key combines URL, method, body, and
934
- * relevant headers (excluding volatile ones like 'Date', 'Authorization', etc.).
935
- *
936
- * **Default Key Generation:**
937
- * The auto-generated key includes:
938
- * - Full request URL (including query parameters)
939
- * - HTTP method (GET, POST, etc.)
940
- * - Request body (for POST/PUT/PATCH requests)
941
- * - Stable headers (excludes Date, Authorization, User-Agent, etc.)
942
- *
943
- * **Custom Key Best Practices:**
944
- * - Include only the parts of the request that should affect deduplication
945
- * - Avoid including volatile data (timestamps, random IDs, etc.)
946
- * - Consider performance - simpler keys are faster to compute and compare
947
- * - Ensure keys are deterministic for the same logical request
948
- * - Use consistent key formats across your application
892
+ * HTTP method for the request.
893
+ * @default "GET"
894
+ */
895
+ method?: InferSchemaOutput<TSchema["method"], InferMethodFromURL<TInitURL>>;
896
+ }>;
897
+ type HeadersOption = UnmaskType<Headers | Record<"Authorization", CommonAuthorizationHeaders | undefined> | Record<"Content-Type", CommonContentTypes | undefined> | Record<CommonRequestHeaders, string | undefined> | Record<string, string | undefined> | Array<[string, string]>>;
898
+ type InferHeadersOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["headers"], {
899
+ /**
900
+ * Headers to be used in the request.
901
+ */
902
+ headers?: InferSchemaOutput<TSchema["headers"], HeadersOption> | ((context: {
903
+ baseHeaders: Extract<HeadersOption, Record<string, unknown>>;
904
+ }) => InferSchemaOutput<TSchema["headers"], HeadersOption>);
905
+ }>;
906
+ type InferRequestOptions<TSchema extends CallApiSchema, TInitURL extends InferInitURL<BaseCallApiSchemaRoutes, CallApiSchemaConfig>> = InferBodyOption<TSchema> & InferHeadersOption<TSchema> & InferMethodOption<TSchema, TInitURL>;
907
+ type InferMetaOption<TSchema extends CallApiSchema, TCallApiContext extends CallApiContext> = MakeSchemaOptionRequiredIfDefined<TSchema["meta"], {
908
+ /**
909
+ * - An optional field you can fill with additional information,
910
+ * to associate with the request, typically used for logging or tracing.
949
911
  *
950
- * **Performance Considerations:**
951
- * - Function-based keys are computed on every request - keep them lightweight
952
- * - String keys are fastest but least flexible
953
- * - Consider caching expensive key computations if needed
912
+ * - A good use case for this, would be to use the info to handle specific cases in any of the shared interceptors.
954
913
  *
955
914
  * @example
956
915
  * ```ts
957
- * import { callApi } from "@zayne-labs/callapi";
958
- *
959
- * // Simple static key - useful for singleton requests
960
- * const config = callApi("/api/config", {
961
- * dedupeKey: "app-config",
962
- * dedupeStrategy: "defer" // Share the same config across all requests
963
- * });
964
- *
965
- * // URL and method only - ignore headers and body
966
- * const userData = callApi("/api/user/123", {
967
- * dedupeKey: (context) => `${context.options.method}:${context.options.fullURL}`
968
- * });
969
- *
970
- * // Include specific headers in deduplication
971
- * const apiCall = callApi("/api/data", {
972
- * dedupeKey: (context) => {
973
- * const authHeader = context.request.headers.get("Authorization");
974
- * return `${context.options.fullURL}-${authHeader}`;
975
- * }
976
- * });
977
- *
978
- * // User-specific deduplication
979
- * const userSpecificCall = callApi("/api/dashboard", {
980
- * dedupeKey: (context) => {
981
- * const userId = context.options.fullURL.match(/user\/(\d+)/)?.[1];
982
- * return `dashboard-${userId}`;
983
- * }
916
+ * const callMainApi = callApi.create({
917
+ * baseURL: "https://main-api.com",
918
+ * onResponseError: ({ response, options }) => {
919
+ * if (options.meta?.userId) {
920
+ * console.error(`User ${options.meta.userId} made an error`);
921
+ * }
922
+ * },
984
923
  * });
985
924
  *
986
- * // Ignore certain query parameters
987
- * const searchCall = callApi("/api/search?q=test&timestamp=123456", {
988
- * dedupeKey: (context) => {
989
- * const url = new URL(context.options.fullURL);
990
- * url.searchParams.delete("timestamp"); // Remove volatile param
991
- * return `search:${url.toString()}`;
992
- * }
925
+ * const response = await callMainApi({
926
+ * url: "https://example.com/api/data",
927
+ * meta: { userId: "123" },
993
928
  * });
994
929
  * ```
995
- *
996
- * @default Auto-generated from request details
997
930
  */
998
- dedupeKey?: string | ((context: RequestContext) => string | undefined);
931
+ meta?: InferSchemaOutput<TSchema["meta"], TCallApiContext["Meta"]>;
932
+ }>;
933
+ type InferAuthOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["auth"], {
999
934
  /**
1000
- * Strategy for handling duplicate requests. Can be a static string or callback function.
935
+ * Automatically add an Authorization header value.
1001
936
  *
1002
- * **Available Strategies:**
1003
- * - `"cancel"`: Cancel previous request when new one starts (good for search)
1004
- * - `"defer"`: Share response between duplicate requests (good for config loading)
1005
- * - `"none"`: No deduplication, all requests execute independently
937
+ * Supports multiple authentication patterns:
938
+ * - String: Direct authorization header value
939
+ * - Auth object: Structured authentication configuration
1006
940
  *
1007
941
  * @example
1008
942
  * ```ts
1009
- * // Static strategies
1010
- * const searchClient = createFetchClient({
1011
- * dedupeStrategy: "cancel" // Cancel previous searches
943
+ * // Bearer auth
944
+ * const response = await callMainApi({
945
+ * url: "https://example.com/api/data",
946
+ * auth: "123456",
1012
947
  * });
1013
948
  *
1014
- * const configClient = createFetchClient({
1015
- * dedupeStrategy: "defer" // Share config across components
1016
- * });
949
+ * // Bearer auth
950
+ * const response = await callMainApi({
951
+ * url: "https://example.com/api/data",
952
+ * auth: {
953
+ * type: "Bearer",
954
+ * value: "123456",
955
+ * },
956
+ })
1017
957
  *
1018
- * // Dynamic strategy based on request
1019
- * const smartClient = createFetchClient({
1020
- * dedupeStrategy: (context) => {
1021
- * return context.options.method === "GET" ? "defer" : "cancel";
1022
- * }
958
+ * // Token auth
959
+ * const response = await callMainApi({
960
+ * url: "https://example.com/api/data",
961
+ * auth: {
962
+ * type: "Token",
963
+ * value: "123456",
964
+ * },
1023
965
  * });
1024
966
  *
1025
- * // Search-as-you-type with cancel strategy
1026
- * const handleSearch = async (query: string) => {
1027
- * try {
1028
- * const { data } = await callApi("/api/search", {
1029
- * method: "POST",
1030
- * body: { query },
1031
- * dedupeStrategy: "cancel",
1032
- * dedupeKey: "search" // Cancel previous searches, only latest one goes through
1033
- * });
1034
- *
1035
- * updateSearchResults(data);
1036
- * } catch (error) {
1037
- * if (error.name === "AbortError") {
1038
- * // Previous search cancelled - (expected behavior)
1039
- * return;
1040
- * }
1041
- * console.error("Search failed:", error);
1042
- * }
1043
- * };
967
+ * // Basic auth
968
+ * const response = await callMainApi({
969
+ * url: "https://example.com/api/data",
970
+ * auth: {
971
+ * type: "Basic",
972
+ * username: "username",
973
+ * password: "password",
974
+ * },
975
+ * });
1044
976
  *
1045
977
  * ```
1046
- *
1047
- * @default "cancel"
1048
978
  */
1049
- dedupeStrategy?: DedupeStrategyUnion | ((context: RequestContext) => DedupeStrategyUnion);
979
+ auth?: InferSchemaOutput<TSchema["auth"], AuthOption>;
980
+ }>;
981
+ type InferQueryOption<TSchema extends CallApiSchema> = MakeSchemaOptionRequiredIfDefined<TSchema["query"], {
982
+ /**
983
+ * Parameters to be appended to the URL (i.e: /:id)
984
+ */
985
+ query?: InferSchemaOutput<TSchema["query"], Query>;
986
+ }>;
987
+ type EmptyString = "";
988
+ type EmptyTuple = readonly [];
989
+ type StringTuple = readonly string[];
990
+ type PossibleParamNamePatterns = `${string}:${string}` | `${string}{${string}}${"" | AnyString}`;
991
+ 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;
992
+ type ConvertParamNamesToRecord<TParamNames extends StringTuple> = Prettify<TParamNames extends (readonly [infer TFirstParamName extends string, ...infer TRemainingParamNames extends StringTuple]) ? Record<TFirstParamName, AllowedQueryParamValues> & ConvertParamNamesToRecord<TRemainingParamNames> : NonNullable<unknown>>;
993
+ type ConvertParamNamesToTuple<TParamNames extends StringTuple> = TParamNames extends readonly [string, ...infer TRemainingParamNames extends StringTuple] ? [AllowedQueryParamValues, ...ConvertParamNamesToTuple<TRemainingParamNames>] : [];
994
+ type InferParamsFromRoute<TCurrentRoute> = ExtractRouteParamNames<TCurrentRoute> extends StringTuple ? ExtractRouteParamNames<TCurrentRoute> extends EmptyTuple ? Params : ConvertParamNamesToRecord<ExtractRouteParamNames<TCurrentRoute>> | ConvertParamNamesToTuple<ExtractRouteParamNames<TCurrentRoute>> : Params;
995
+ 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>;
996
+ type InferParamsOption<TSchema extends CallApiSchema, TBaseSchemaRoutes extends BaseCallApiSchemaRoutes, TCurrentRouteSchemaKey extends string> = MakeParamsOptionRequired<TSchema["params"], TBaseSchemaRoutes, TCurrentRouteSchemaKey, {
997
+ /**
998
+ * Parameters to be appended to the URL (i.e: /:id)
999
+ */
1000
+ params?: InferSchemaOutput<TSchema["params"], InferParamsFromRoute<TCurrentRouteSchemaKey>>;
1001
+ }>;
1002
+ 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>;
1003
+ type ResultModeOption<TErrorData, TResultMode extends ResultModeType> = TErrorData extends false ? {
1004
+ resultMode: "onlyData";
1005
+ } : TErrorData extends false | undefined ? {
1006
+ resultMode?: "onlyData";
1007
+ } : {
1008
+ resultMode?: TResultMode;
1009
+ };
1010
+ type ThrowOnErrorUnion = boolean;
1011
+ type ThrowOnErrorType<TErrorData, TThrowOnError extends ThrowOnErrorUnion> = TThrowOnError | ((context: ErrorContext<{
1012
+ ErrorData: TErrorData;
1013
+ }>) => TThrowOnError);
1014
+ type ThrowOnErrorOption<TErrorData, TThrowOnError extends ThrowOnErrorUnion> = TErrorData extends false ? {
1015
+ throwOnError: true;
1016
+ } : TErrorData extends false | undefined ? {
1017
+ throwOnError?: true;
1018
+ } : {
1019
+ throwOnError?: ThrowOnErrorType<TErrorData, TThrowOnError>;
1050
1020
  };
1051
1021
  //#endregion
1052
1022
  //#region src/retry.d.ts
@@ -1197,7 +1167,7 @@ type CallApiRequestOptions = Prettify<{
1197
1167
  type CallApiRequestOptionsForHooks = Omit<CallApiRequestOptions, "headers"> & {
1198
1168
  headers: Record<string, string | undefined>;
1199
1169
  };
1200
- type SharedExtraOptions<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorUnion = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedMergedPluginExtraOptions = Partial<InferPluginExtraOptions<TPluginArray> & TCallApiContext["InferredExtraOptions"]>, TComputedCallApiContext extends CallApiContext = OverrideCallApiContext<TCallApiContext, {
1170
+ type SharedExtraOptions<TCallApiContext extends CallApiContext = DefaultCallApiContext, TData = DefaultDataType, TErrorData = DefaultDataType, TResultMode extends ResultModeType = ResultModeType, TThrowOnError extends ThrowOnErrorUnion = DefaultThrowOnError, TResponseType extends ResponseTypeType = ResponseTypeType, TPluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedMergedPluginExtraOptions = Partial<InferPluginExtraOptions<TPluginArray> & InferSchemaOutput<TCallApiContext["InferredExtraOptions"], TCallApiContext["InferredExtraOptions"]>>, TComputedCallApiContext extends CallApiContext = OverrideCallApiContext<TCallApiContext, {
1201
1171
  Data: TData;
1202
1172
  ErrorData: TErrorData;
1203
1173
  InferredExtraOptions: TComputedMergedPluginExtraOptions;
@@ -1782,6 +1752,74 @@ type ResultModePlaceholder = null;
1782
1752
  type ResultModeUnion = keyof ResultModeMap;
1783
1753
  type ResultModeType = ResultModePlaceholder | ResultModeUnion;
1784
1754
  //#endregion
1755
+ //#region src/plugins.d.ts
1756
+ type PluginSetupContext<TCallApiContext extends CallApiContext = DefaultCallApiContext> = RequestContext<TCallApiContext> & {
1757
+ initURL: string;
1758
+ };
1759
+ type PluginInitResult<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Partial<Omit<PluginSetupContext<TCallApiContext>, "initURL" | "request"> & {
1760
+ initURL: InitURLOrURLObject;
1761
+ request: CallApiRequestOptions;
1762
+ }>;
1763
+ type GetDefaultDataTypeForPlugins<TData> = DefaultDataType extends TData ? never : TData;
1764
+ type PluginHooks<TCallApiContext extends CallApiContext = DefaultCallApiContext> = HooksOrHooksArray<OverrideCallApiContext<TCallApiContext, {
1765
+ Data: GetDefaultDataTypeForPlugins<TCallApiContext["Data"]>;
1766
+ ErrorData: GetDefaultDataTypeForPlugins<TCallApiContext["ErrorData"]>;
1767
+ }>>;
1768
+ type PluginMiddlewares<TCallApiContext extends CallApiContext = DefaultCallApiContext> = Middlewares<OverrideCallApiContext<TCallApiContext, {
1769
+ Data: GetDefaultDataTypeForPlugins<TCallApiContext["Data"]>;
1770
+ ErrorData: GetDefaultDataTypeForPlugins<TCallApiContext["ErrorData"]>;
1771
+ }>>;
1772
+ interface CallApiPlugin<TCallApiContext extends CallApiContext = DefaultCallApiContext> {
1773
+ /**
1774
+ * Defines additional options that can be passed to callApi
1775
+ */
1776
+ defineExtraOptions?: () => TCallApiContext["InferredExtraOptions"];
1777
+ /**
1778
+ * A description for the plugin
1779
+ */
1780
+ description?: string;
1781
+ /**
1782
+ * Hooks for the plugin
1783
+ */
1784
+ hooks?: PluginHooks<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginHooks<TCallApiContext>>);
1785
+ /**
1786
+ * A unique id for the plugin
1787
+ */
1788
+ id: string;
1789
+ /**
1790
+ * Middlewares that for the plugin
1791
+ */
1792
+ middlewares?: PluginMiddlewares<TCallApiContext> | ((context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginMiddlewares<TCallApiContext>>);
1793
+ /**
1794
+ * A name for the plugin
1795
+ */
1796
+ name: string;
1797
+ /**
1798
+ * Base schema for the client.
1799
+ */
1800
+ schema?: BaseCallApiSchemaAndConfig;
1801
+ /**
1802
+ * A function that will be called when the plugin is initialized. This will be called before the any of the other internal functions.
1803
+ */
1804
+ setup?: (context: PluginSetupContext<TCallApiContext>) => Awaitable<PluginInitResult<TCallApiContext>> | Awaitable<void>;
1805
+ /**
1806
+ * A version for the plugin
1807
+ */
1808
+ version?: string;
1809
+ }
1810
+ type InferPluginExtraOptions<TPluginArray extends CallApiPlugin[]> = UnionToIntersection<TPluginArray extends Array<infer TPlugin> ? TPlugin extends CallApiPlugin ? TPlugin["defineExtraOptions"] extends AnyFunction$1<infer TResult> ? InferSchemaOutput<TResult, TResult> : never : never : never>;
1811
+ //#endregion
1812
+ //#region src/types/default-types.d.ts
1813
+ type DefaultDataType = unknown;
1814
+ type DefaultPluginArray = CallApiPlugin[];
1815
+ type DefaultThrowOnError = boolean;
1816
+ type DefaultMetaObject = Record<string, unknown>;
1817
+ type DefaultCallApiContext = Prettify<Required<Omit<CallApiContext, "Meta">> & {
1818
+ Meta: GlobalMeta;
1819
+ }>;
1820
+ //#endregion
1821
+ //#region src/createFetchClient.d.ts
1822
+ //#endregion
1785
1823
  //#region src/plugins/logger/logger.d.ts
1786
1824
  type ConsoleLikeObject = {
1787
1825
  error: AnyFunction<void>;
@@ -1820,32 +1858,80 @@ declare const loggerPlugin: (options?: LoggerOptions) => {
1820
1858
  name: "Logger";
1821
1859
  version: "1.1.0";
1822
1860
  hooks: {
1823
- onRequest: (ctx: RequestContext<Omit<DefaultCallApiContext, "Data" | "ErrorData"> & {
1861
+ onRequest: (ctx: RequestContext<Omit<{
1862
+ Data: unknown;
1863
+ ErrorData: unknown;
1864
+ InferredExtraOptions: unknown;
1865
+ ResultMode: ResultModeType;
1866
+ Meta: {
1867
+ [x: string]: unknown;
1868
+ };
1869
+ }, "Data" | "ErrorData"> & {
1824
1870
  Data: never;
1825
1871
  ErrorData: never;
1826
1872
  }>) => void;
1827
- onRequestError: (ctx: RequestContext<Omit<DefaultCallApiContext, "Data" | "ErrorData"> & {
1873
+ onRequestError: (ctx: RequestContext<Omit<{
1874
+ Data: unknown;
1875
+ ErrorData: unknown;
1876
+ InferredExtraOptions: unknown;
1877
+ ResultMode: ResultModeType;
1878
+ Meta: {
1879
+ [x: string]: unknown;
1880
+ };
1881
+ }, "Data" | "ErrorData"> & {
1828
1882
  Data: never;
1829
1883
  ErrorData: never;
1830
1884
  }> & {
1831
1885
  error: PossibleJavaScriptError;
1832
1886
  response: null;
1833
1887
  }) => void;
1834
- onResponseError: (ctx: ResponseErrorContext<Omit<DefaultCallApiContext, "Data" | "ErrorData"> & {
1888
+ onResponseError: (ctx: ResponseErrorContext<Omit<{
1889
+ Data: unknown;
1890
+ ErrorData: unknown;
1891
+ InferredExtraOptions: unknown;
1892
+ ResultMode: ResultModeType;
1893
+ Meta: {
1894
+ [x: string]: unknown;
1895
+ };
1896
+ }, "Data" | "ErrorData"> & {
1835
1897
  Data: never;
1836
1898
  ErrorData: never;
1837
1899
  }>) => void;
1838
- onRetry: (ctx: ErrorContext<Omit<DefaultCallApiContext, "Data" | "ErrorData"> & {
1900
+ onRetry: (ctx: ErrorContext<Omit<{
1901
+ Data: unknown;
1902
+ ErrorData: unknown;
1903
+ InferredExtraOptions: unknown;
1904
+ ResultMode: ResultModeType;
1905
+ Meta: {
1906
+ [x: string]: unknown;
1907
+ };
1908
+ }, "Data" | "ErrorData"> & {
1839
1909
  Data: never;
1840
1910
  ErrorData: never;
1841
1911
  }> & {
1842
1912
  retryAttemptCount: number;
1843
1913
  }) => void;
1844
- onSuccess: (ctx: SuccessContext<Omit<DefaultCallApiContext, "Data" | "ErrorData"> & {
1914
+ onSuccess: (ctx: SuccessContext<Omit<{
1915
+ Data: unknown;
1916
+ ErrorData: unknown;
1917
+ InferredExtraOptions: unknown;
1918
+ ResultMode: ResultModeType;
1919
+ Meta: {
1920
+ [x: string]: unknown;
1921
+ };
1922
+ }, "Data" | "ErrorData"> & {
1845
1923
  Data: never;
1846
1924
  ErrorData: never;
1847
1925
  }>) => void;
1848
- onValidationError: (ctx: RequestContext<Omit<DefaultCallApiContext, "Data" | "ErrorData"> & {
1926
+ onValidationError: (ctx: RequestContext<Omit<{
1927
+ Data: unknown;
1928
+ ErrorData: unknown;
1929
+ InferredExtraOptions: unknown;
1930
+ ResultMode: ResultModeType;
1931
+ Meta: {
1932
+ [x: string]: unknown;
1933
+ };
1934
+ }, "Data" | "ErrorData"> & {
1849
1935
  Data: never;
1850
1936
  ErrorData: never;
1851
1937
  }> & {
@@ -1856,4 +1942,4 @@ declare const loggerPlugin: (options?: LoggerOptions) => {
1856
1942
  };
1857
1943
  //#endregion
1858
1944
  export { defaultConsoleObject as n, loggerPlugin as r, LoggerOptions as t };
1859
- //# sourceMappingURL=index-BuFffnAC.d.ts.map
1945
+ //# sourceMappingURL=index-DsoJDLKd.d.ts.map