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