@zayne-labs/callapi 1.6.24 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/dist/cjs/{error-DIHsfUiJ.d.cts → error-B0rQm6WQ.d.cts} +70 -5
- package/dist/cjs/index.cjs +162 -34
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -2
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/cjs/utils/index.cjs.map +1 -1
- package/dist/cjs/utils/index.d.cts +1 -1
- package/dist/esm/{chunk-UA7QPQFE.js → chunk-MMZRXEXE.js} +9 -4
- package/dist/esm/chunk-MMZRXEXE.js.map +1 -0
- package/dist/esm/{error-DIHsfUiJ.d.ts → error-B0rQm6WQ.d.ts} +70 -5
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +158 -35
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/package.json +6 -6
- package/dist/esm/chunk-UA7QPQFE.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<h1 align="center">CallApi - Advanced Fetch Client</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://github.com/zayne-labs/call-api/blob/main/packages/callapi/assets/logo.png" alt="CallApi Logo" width="30%">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<!-- <a href="https://deno.bundlejs.com/badge?q=@zayne-labs/callapi,@zayne-labs/callapi&treeshake=%5B*%5D,%5B%7B+createFetchClient+%7D%5D&config=%7B%22compression%22:%7B%22type%22:%22brotli%22,%22quality%22:11%7D%7D"><img src="https://deno.bundlejs.com/badge?q=@zayne-labs/callapi,@zayne-labs/callapi&treeshake=%5B*%5D,%5B%7B+createFetchClient+%7D%5D&config=%7B%22compression%22:%7B%22type%22:%22brotli%22,%22quality%22:11%7D%7D" alt="bundle size"></a> -->
|
|
9
|
+
<a href="https://www.npmjs.com/package/@zayne-labs/callapi"><img src="https://img.shields.io/npm/v/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="npm version"></a>
|
|
10
|
+
<a href="https://github.com/zayne-labs/call-api/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="license"></a>
|
|
11
|
+
<a href="https://github.com/zayne-labs/call-api/graphs/commit-activity"><img src="https://img.shields.io/github/commit-activity/m/zayne-labs/call-api?style=flat&color=EFBA5F" alt="commit activity"></a>
|
|
12
|
+
<a href="https://www.npmjs.com/package/@zayne-labs/callapi"><img src="https://img.shields.io/npm/dm/@zayne-labs/callapi?style=flat&color=EFBA5F" alt="downloads per month"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
CallApi Fetch is an extra-lightweight wrapper over fetch that provides quality of life improvements beyond the bare fetch api, while keeping the API familiar.</p>
|
|
6
17
|
|
|
7
18
|
It takes in a url and a request options object, just like fetch, but with some additional options to make your life easier. Check out the [API Reference](https://zayne-labs-callapi.netlify.app/docs/latest/all-options) for a quick look at each option.
|
|
8
19
|
|
|
@@ -197,6 +197,42 @@ type ResponseTypeMap<TResponse> = {
|
|
|
197
197
|
};
|
|
198
198
|
type GetResponseType<TResponse, TResponseType extends ResponseTypeUnion, TComputedMap extends ResponseTypeMap<TResponse> = ResponseTypeMap<TResponse>> = undefined extends TResponseType ? TComputedMap["json"] : TResponseType extends NonNullable<ResponseTypeUnion> ? TComputedMap[TResponseType] : never;
|
|
199
199
|
|
|
200
|
+
type StreamProgressEvent = {
|
|
201
|
+
/**
|
|
202
|
+
* Current chunk of data being streamed
|
|
203
|
+
*/
|
|
204
|
+
chunk: Uint8Array;
|
|
205
|
+
/**
|
|
206
|
+
* Progress in percentage
|
|
207
|
+
*/
|
|
208
|
+
progress: number;
|
|
209
|
+
/**
|
|
210
|
+
* Total size of data in bytes
|
|
211
|
+
*/
|
|
212
|
+
totalBytes: number;
|
|
213
|
+
/**
|
|
214
|
+
* Amount of data transferred so far
|
|
215
|
+
*/
|
|
216
|
+
transferredBytes: number;
|
|
217
|
+
};
|
|
218
|
+
type RequestStreamContext = {
|
|
219
|
+
event: StreamProgressEvent;
|
|
220
|
+
options: CombinedCallApiExtraOptions;
|
|
221
|
+
request: CallApiRequestOptionsForHooks;
|
|
222
|
+
requestInstance: Request;
|
|
223
|
+
};
|
|
224
|
+
type ResponseStreamContext = {
|
|
225
|
+
event: StreamProgressEvent;
|
|
226
|
+
options: CombinedCallApiExtraOptions;
|
|
227
|
+
request: CallApiRequestOptionsForHooks;
|
|
228
|
+
response: Response;
|
|
229
|
+
};
|
|
230
|
+
declare global {
|
|
231
|
+
interface ReadableStream<R> {
|
|
232
|
+
[Symbol.asyncIterator]: () => AsyncIterableIterator<R>;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
200
236
|
type UnionToIntersection<TUnion> = (TUnion extends unknown ? (param: TUnion) => void : never) extends (param: infer TParam) => void ? TParam : never;
|
|
201
237
|
type InferSchema<TResult> = TResult extends StandardSchemaV1 ? InferSchemaResult<TResult, NonNullable<unknown>> : TResult;
|
|
202
238
|
type InferPluginOptions<TPluginArray extends CallApiPlugin[]> = UnionToIntersection<InferSchema<ReturnType<NonNullable<TPluginArray[number]["createExtraOptions"]>>>>;
|
|
@@ -243,7 +279,10 @@ interface CallApiPlugin<TData = never, TErrorData = never> {
|
|
|
243
279
|
declare const definePlugin: <TPlugin extends CallApiPlugin | AnyFunction<CallApiPlugin>>(plugin: TPlugin) => TPlugin;
|
|
244
280
|
type Plugins<TPluginArray extends CallApiPlugin[]> = TPluginArray;
|
|
245
281
|
|
|
246
|
-
|
|
282
|
+
type ModifiedRequestInit = RequestInit & {
|
|
283
|
+
duplex?: "full" | "half" | "none";
|
|
284
|
+
};
|
|
285
|
+
declare const fetchSpecificKeys: ("body" | "cache" | "credentials" | "headers" | "integrity" | "keepalive" | "method" | "mode" | "priority" | "redirect" | "referrer" | "referrerPolicy" | "signal" | "window" | "duplex")[];
|
|
247
286
|
declare const getDefaultOptions: () => {
|
|
248
287
|
baseURL: string;
|
|
249
288
|
bodySerializer: {
|
|
@@ -335,7 +374,7 @@ type ResultModeOption<TErrorData, TResultMode extends ResultModeUnion> = TErrorD
|
|
|
335
374
|
};
|
|
336
375
|
|
|
337
376
|
type FetchSpecificKeysUnion = Exclude<(typeof fetchSpecificKeys)[number], "body" | "headers" | "method">;
|
|
338
|
-
type CallApiRequestOptions<TSchemas extends CallApiSchemas = DefaultMoreOptions> = BodyOption<TSchemas> & HeadersOption<TSchemas> & MethodOption<TSchemas> & Pick<
|
|
377
|
+
type CallApiRequestOptions<TSchemas extends CallApiSchemas = DefaultMoreOptions> = BodyOption<TSchemas> & HeadersOption<TSchemas> & MethodOption<TSchemas> & Pick<ModifiedRequestInit, FetchSpecificKeysUnion>;
|
|
339
378
|
type CallApiRequestOptionsForHooks<TSchemas extends CallApiSchemas = DefaultMoreOptions> = Omit<CallApiRequestOptions<TSchemas>, "headers"> & {
|
|
340
379
|
headers?: Record<string, string | undefined>;
|
|
341
380
|
};
|
|
@@ -356,6 +395,10 @@ interface Interceptors<TData = DefaultDataType, TErrorData = DefaultDataType, TM
|
|
|
356
395
|
* Interceptor that will be called when an error occurs during the fetch request.
|
|
357
396
|
*/
|
|
358
397
|
onRequestError?: (context: RequestErrorContext & WithMoreOptions<TMoreOptions>) => Awaitable<unknown>;
|
|
398
|
+
/**
|
|
399
|
+
* Interceptor that will be called when upload stream progress is tracked
|
|
400
|
+
*/
|
|
401
|
+
onRequestStream?: (context: RequestStreamContext & WithMoreOptions<TMoreOptions>) => Awaitable<unknown>;
|
|
359
402
|
/**
|
|
360
403
|
* Interceptor that will be called when any response is received from the api, whether successful or not
|
|
361
404
|
*/
|
|
@@ -364,6 +407,10 @@ interface Interceptors<TData = DefaultDataType, TErrorData = DefaultDataType, TM
|
|
|
364
407
|
* Interceptor that will be called when an error response is received from the api.
|
|
365
408
|
*/
|
|
366
409
|
onResponseError?: (context: ResponseErrorContext<TErrorData> & WithMoreOptions<TMoreOptions>) => Awaitable<unknown>;
|
|
410
|
+
/**
|
|
411
|
+
* Interceptor that will be called when download stream progress is tracked
|
|
412
|
+
*/
|
|
413
|
+
onResponseStream?: (context: ResponseStreamContext & WithMoreOptions<TMoreOptions>) => Awaitable<unknown>;
|
|
367
414
|
/**
|
|
368
415
|
* Interceptor that will be called when a request is retried.
|
|
369
416
|
*/
|
|
@@ -418,6 +465,14 @@ type ExtraOptions<TData = DefaultDataType, TErrorData = DefaultDataType, TResult
|
|
|
418
465
|
* @default "Failed to fetch data from server!"
|
|
419
466
|
*/
|
|
420
467
|
defaultErrorMessage?: string;
|
|
468
|
+
/**
|
|
469
|
+
* If true, forces the calculation of the total byte size from the request or response body, in case the content-length header is not present or is incorrect.
|
|
470
|
+
* @default false
|
|
471
|
+
*/
|
|
472
|
+
forceStreamSizeCalc?: boolean | {
|
|
473
|
+
request?: boolean;
|
|
474
|
+
response?: boolean;
|
|
475
|
+
};
|
|
421
476
|
/**
|
|
422
477
|
* Resolved request URL
|
|
423
478
|
*/
|
|
@@ -480,8 +535,18 @@ type CallApiExtraOptions<TData = DefaultDataType, TErrorData = DefaultDataType,
|
|
|
480
535
|
extend?: Pick<ExtraOptions<TData, TErrorData, TResultMode, TThrowOnError, TResponseType, TPluginArray, TSchemas>, (typeof optionsEnumToExtendFromBase)[number]>;
|
|
481
536
|
};
|
|
482
537
|
declare const optionsEnumToOmitFromBase: ("dedupeKey" | "extend")[];
|
|
483
|
-
type BaseCallApiExtraOptions<TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeUnion = ResultModeUnion, TBaseThrowOnError extends boolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeUnion = ResponseTypeUnion, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemas extends CallApiSchemas = DefaultMoreOptions> = Omit<Partial<CallApiExtraOptions<TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBasePluginArray, TBaseSchemas>>, (typeof optionsEnumToOmitFromBase)[number]
|
|
484
|
-
|
|
538
|
+
type BaseCallApiExtraOptions<TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeUnion = ResultModeUnion, TBaseThrowOnError extends boolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeUnion = ResponseTypeUnion, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemas extends CallApiSchemas = DefaultMoreOptions> = Omit<Partial<CallApiExtraOptions<TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBasePluginArray, TBaseSchemas>>, (typeof optionsEnumToOmitFromBase)[number]> & {
|
|
539
|
+
/**
|
|
540
|
+
* If true, the base options will not be merged with the main options by default.
|
|
541
|
+
*
|
|
542
|
+
* It's recommended to set this to true when you want to handle the options merge manually from the createFetchClient config function signature.
|
|
543
|
+
*
|
|
544
|
+
* This helps prevent main options from overriding base options by default.
|
|
545
|
+
* @default false
|
|
546
|
+
*/
|
|
547
|
+
mergeMainOptionsManuallyFromBase?: boolean;
|
|
548
|
+
};
|
|
549
|
+
type CombinedCallApiExtraOptions = Interceptors & Omit<BaseCallApiExtraOptions & CallApiExtraOptions, keyof Interceptors>;
|
|
485
550
|
type BaseCallApiConfig<TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeUnion = ResultModeUnion, TBaseThrowOnError extends boolean = DefaultThrowOnError, TBaseResponseType extends ResponseTypeUnion = ResponseTypeUnion, TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TBaseSchemas extends CallApiSchemas = DefaultMoreOptions> = (CallApiRequestOptions<TBaseSchemas> & BaseCallApiExtraOptions<TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBasePluginArray, TBaseSchemas>) | ((context: {
|
|
486
551
|
initURL: string;
|
|
487
552
|
options: CallApiExtraOptions;
|
|
@@ -569,7 +634,7 @@ type ResultModeMap<TData = DefaultDataType, TErrorData = DefaultDataType, TRespo
|
|
|
569
634
|
allWithoutResponse: CallApiResultSuccessVariant<TComputedData>["data" | "error"] | CallApiResultErrorVariant<TComputedErrorData>["data" | "error"];
|
|
570
635
|
onlyError: CallApiResultSuccessVariant<TComputedData>["error"] | CallApiResultErrorVariant<TComputedErrorData>["error"];
|
|
571
636
|
onlyResponse: CallApiResultErrorVariant<TComputedErrorData>["response"] | CallApiResultSuccessVariant<TComputedData>["response"];
|
|
572
|
-
onlyResponseWithException: CallApiResultSuccessVariant<
|
|
637
|
+
onlyResponseWithException: CallApiResultSuccessVariant<TComputedData>["response"];
|
|
573
638
|
onlySuccess: CallApiResultErrorVariant<TComputedErrorData>["data"] | CallApiResultSuccessVariant<TComputedData>["data"];
|
|
574
639
|
onlySuccessWithException: CallApiResultSuccessVariant<TComputedData>["data"];
|
|
575
640
|
}>;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -89,7 +89,7 @@ var HTTPError = class extends Error {
|
|
|
89
89
|
}
|
|
90
90
|
};
|
|
91
91
|
|
|
92
|
-
// src/utils/
|
|
92
|
+
// src/utils/guards.ts
|
|
93
93
|
var isHTTPErrorInstance = (error) => {
|
|
94
94
|
return (
|
|
95
95
|
// prettier-ignore
|
|
@@ -97,6 +97,7 @@ var isHTTPErrorInstance = (error) => {
|
|
|
97
97
|
);
|
|
98
98
|
};
|
|
99
99
|
var isArray = (value) => Array.isArray(value);
|
|
100
|
+
var isObject = (value) => typeof value === "object" && value !== null;
|
|
100
101
|
var hasObjectPrototype = (value) => {
|
|
101
102
|
return Object.prototype.toString.call(value) === "[object Object]";
|
|
102
103
|
};
|
|
@@ -137,6 +138,9 @@ var isSerializable = (value) => {
|
|
|
137
138
|
var isFunction = (value) => typeof value === "function";
|
|
138
139
|
var isQueryString = (value) => isString(value) && value.includes("=");
|
|
139
140
|
var isString = (value) => typeof value === "string";
|
|
141
|
+
var isReadableStream = (value) => {
|
|
142
|
+
return value instanceof ReadableStream;
|
|
143
|
+
};
|
|
140
144
|
|
|
141
145
|
// src/auth.ts
|
|
142
146
|
var getValue = (value) => {
|
|
@@ -186,6 +190,7 @@ var optionsEnumToOmitFromBase = defineEnum(["extend", "dedupeKey"]);
|
|
|
186
190
|
var fetchSpecificKeys = defineEnum([
|
|
187
191
|
"body",
|
|
188
192
|
"integrity",
|
|
193
|
+
"duplex",
|
|
189
194
|
"method",
|
|
190
195
|
"headers",
|
|
191
196
|
"signal",
|
|
@@ -328,6 +333,108 @@ var waitUntil = (delay) => {
|
|
|
328
333
|
return promise;
|
|
329
334
|
};
|
|
330
335
|
|
|
336
|
+
// src/stream.ts
|
|
337
|
+
var createProgressEvent = (options) => {
|
|
338
|
+
const { chunk, totalBytes, transferredBytes } = options;
|
|
339
|
+
return {
|
|
340
|
+
chunk,
|
|
341
|
+
progress: Math.round(transferredBytes / totalBytes * 100) || 0,
|
|
342
|
+
totalBytes,
|
|
343
|
+
transferredBytes
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
var calculateTotalBytesFromBody = async (requestBody, existingTotalBytes) => {
|
|
347
|
+
let totalBytes = existingTotalBytes;
|
|
348
|
+
if (!requestBody) {
|
|
349
|
+
return totalBytes;
|
|
350
|
+
}
|
|
351
|
+
for await (const chunk of requestBody) {
|
|
352
|
+
totalBytes += chunk.byteLength;
|
|
353
|
+
}
|
|
354
|
+
return totalBytes;
|
|
355
|
+
};
|
|
356
|
+
var toStreamableRequest = async (context) => {
|
|
357
|
+
const { options, request, requestInstance } = context;
|
|
358
|
+
if (!options.onRequestStream || !requestInstance.body) return;
|
|
359
|
+
const contentLength = requestInstance.headers.get("content-length") ?? new Headers(request.headers).get("content-length") ?? request.body?.size;
|
|
360
|
+
let totalBytes = Number(contentLength ?? 0);
|
|
361
|
+
const shouldForceContentLengthCalc = isObject(options.forceStreamSizeCalc) ? options.forceStreamSizeCalc.request : options.forceStreamSizeCalc;
|
|
362
|
+
if (!contentLength && shouldForceContentLengthCalc) {
|
|
363
|
+
totalBytes = await calculateTotalBytesFromBody(requestInstance.clone().body, totalBytes);
|
|
364
|
+
}
|
|
365
|
+
let transferredBytes = 0;
|
|
366
|
+
await executeHooks(
|
|
367
|
+
options.onRequestStream({
|
|
368
|
+
event: createProgressEvent({ chunk: new Uint8Array(), totalBytes, transferredBytes }),
|
|
369
|
+
options,
|
|
370
|
+
request,
|
|
371
|
+
requestInstance
|
|
372
|
+
})
|
|
373
|
+
);
|
|
374
|
+
const body = requestInstance.body;
|
|
375
|
+
void new ReadableStream({
|
|
376
|
+
start: async (controller) => {
|
|
377
|
+
if (!body) return;
|
|
378
|
+
for await (const chunk of body) {
|
|
379
|
+
transferredBytes += chunk.byteLength;
|
|
380
|
+
totalBytes = Math.max(totalBytes, transferredBytes);
|
|
381
|
+
await executeHooks(
|
|
382
|
+
options.onRequestStream?.({
|
|
383
|
+
event: createProgressEvent({ chunk, totalBytes, transferredBytes }),
|
|
384
|
+
options,
|
|
385
|
+
request,
|
|
386
|
+
requestInstance
|
|
387
|
+
})
|
|
388
|
+
);
|
|
389
|
+
controller.enqueue(chunk);
|
|
390
|
+
}
|
|
391
|
+
controller.close();
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
};
|
|
395
|
+
var toStreamableResponse = async (context) => {
|
|
396
|
+
const { options, request, response } = context;
|
|
397
|
+
if (!options.onResponseStream || !response.body) {
|
|
398
|
+
return response;
|
|
399
|
+
}
|
|
400
|
+
const contentLength = response.headers.get("content-length");
|
|
401
|
+
let totalBytes = Number(contentLength ?? 0);
|
|
402
|
+
const shouldForceContentLengthCalc = isObject(options.forceStreamSizeCalc) ? options.forceStreamSizeCalc.response : options.forceStreamSizeCalc;
|
|
403
|
+
if (!contentLength && shouldForceContentLengthCalc) {
|
|
404
|
+
totalBytes = await calculateTotalBytesFromBody(response.clone().body, totalBytes);
|
|
405
|
+
}
|
|
406
|
+
let transferredBytes = 0;
|
|
407
|
+
await executeHooks(
|
|
408
|
+
options.onResponseStream({
|
|
409
|
+
event: createProgressEvent({ chunk: new Uint8Array(), totalBytes, transferredBytes }),
|
|
410
|
+
options,
|
|
411
|
+
request,
|
|
412
|
+
response
|
|
413
|
+
})
|
|
414
|
+
);
|
|
415
|
+
const body = response.body;
|
|
416
|
+
const stream = new ReadableStream({
|
|
417
|
+
start: async (controller) => {
|
|
418
|
+
if (!body) return;
|
|
419
|
+
for await (const chunk of body) {
|
|
420
|
+
transferredBytes += chunk.byteLength;
|
|
421
|
+
totalBytes = Math.max(totalBytes, transferredBytes);
|
|
422
|
+
await executeHooks(
|
|
423
|
+
options.onResponseStream?.({
|
|
424
|
+
event: createProgressEvent({ chunk, totalBytes, transferredBytes }),
|
|
425
|
+
options,
|
|
426
|
+
request,
|
|
427
|
+
response
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
controller.enqueue(chunk);
|
|
431
|
+
}
|
|
432
|
+
controller.close();
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
return new Response(stream, response);
|
|
436
|
+
};
|
|
437
|
+
|
|
331
438
|
// src/dedupe.ts
|
|
332
439
|
var createDedupeStrategy = async (context) => {
|
|
333
440
|
const { $RequestInfoCache, newFetchController, options, request } = context;
|
|
@@ -352,12 +459,29 @@ var createDedupeStrategy = async (context) => {
|
|
|
352
459
|
prevRequestInfo.controller.abort(reason);
|
|
353
460
|
return Promise.resolve();
|
|
354
461
|
};
|
|
355
|
-
const handleRequestDeferStrategy = () => {
|
|
462
|
+
const handleRequestDeferStrategy = async () => {
|
|
356
463
|
const fetchApi = getFetchImpl(options.customFetchImpl);
|
|
357
464
|
const shouldUsePromiseFromCache = prevRequestInfo && options.dedupeStrategy === "defer";
|
|
358
|
-
const
|
|
465
|
+
const requestInstance = new Request(
|
|
466
|
+
options.fullURL,
|
|
467
|
+
isReadableStream(request.body) && !request.duplex ? { ...request, duplex: "half" } : request
|
|
468
|
+
);
|
|
469
|
+
void toStreamableRequest({
|
|
470
|
+
options,
|
|
471
|
+
request,
|
|
472
|
+
requestInstance: requestInstance.clone()
|
|
473
|
+
});
|
|
474
|
+
const responsePromise = shouldUsePromiseFromCache ? prevRequestInfo.responsePromise : (
|
|
475
|
+
// eslint-disable-next-line unicorn/no-nested-ternary -- Allow
|
|
476
|
+
isReadableStream(request.body) ? fetchApi(requestInstance.clone()) : fetchApi(options.fullURL, request)
|
|
477
|
+
);
|
|
359
478
|
$RequestInfoCacheOrNull?.set(dedupeKey, { controller: newFetchController, responsePromise });
|
|
360
|
-
|
|
479
|
+
const streamableResponse = toStreamableResponse({
|
|
480
|
+
options,
|
|
481
|
+
request,
|
|
482
|
+
response: await responsePromise
|
|
483
|
+
});
|
|
484
|
+
return streamableResponse;
|
|
361
485
|
};
|
|
362
486
|
const removeDedupeKeyFromCache = () => $RequestInfoCacheOrNull?.delete(dedupeKey);
|
|
363
487
|
return {
|
|
@@ -372,7 +496,8 @@ var definePlugin = (plugin) => {
|
|
|
372
496
|
return plugin;
|
|
373
497
|
};
|
|
374
498
|
var createMergedHook = (hooks, mergedHooksExecutionMode) => {
|
|
375
|
-
|
|
499
|
+
if (hooks.length === 0) return;
|
|
500
|
+
const mergedHook = async (ctx) => {
|
|
376
501
|
if (mergedHooksExecutionMode === "sequential") {
|
|
377
502
|
for (const hook of hooks) {
|
|
378
503
|
await hook?.(ctx);
|
|
@@ -384,13 +509,16 @@ var createMergedHook = (hooks, mergedHooksExecutionMode) => {
|
|
|
384
509
|
await Promise.all(hookArray.map((uniqueHook) => uniqueHook?.(ctx)));
|
|
385
510
|
}
|
|
386
511
|
};
|
|
512
|
+
return mergedHook;
|
|
387
513
|
};
|
|
388
514
|
var hooksEnum = {
|
|
389
515
|
onError: /* @__PURE__ */ new Set(),
|
|
390
516
|
onRequest: /* @__PURE__ */ new Set(),
|
|
391
517
|
onRequestError: /* @__PURE__ */ new Set(),
|
|
518
|
+
onRequestStream: /* @__PURE__ */ new Set(),
|
|
392
519
|
onResponse: /* @__PURE__ */ new Set(),
|
|
393
520
|
onResponseError: /* @__PURE__ */ new Set(),
|
|
521
|
+
onResponseStream: /* @__PURE__ */ new Set(),
|
|
394
522
|
onRetry: /* @__PURE__ */ new Set(),
|
|
395
523
|
onSuccess: /* @__PURE__ */ new Set()
|
|
396
524
|
};
|
|
@@ -455,7 +583,7 @@ var initializePlugins = async (context) => {
|
|
|
455
583
|
}
|
|
456
584
|
const resolvedHooks = {};
|
|
457
585
|
for (const [key, hookRegistry] of Object.entries(hookRegistries)) {
|
|
458
|
-
const flattenedHookArray = [...hookRegistry].flat();
|
|
586
|
+
const flattenedHookArray = [...hookRegistry].flat().filter(Boolean);
|
|
459
587
|
const mergedHook = createMergedHook(flattenedHookArray, options.mergedHooksExecutionMode);
|
|
460
588
|
resolvedHooks[key] = mergedHook;
|
|
461
589
|
}
|
|
@@ -517,45 +645,45 @@ var getExponentialDelay = (currentAttemptCount, options) => {
|
|
|
517
645
|
return Math.min(exponentialDelay, maxDelay);
|
|
518
646
|
};
|
|
519
647
|
var createRetryStrategy = (ctx) => {
|
|
520
|
-
const
|
|
648
|
+
const { options } = ctx;
|
|
649
|
+
const currentRetryCount = options["~retryCount"] ?? 0;
|
|
521
650
|
const getDelay = () => {
|
|
522
|
-
if (
|
|
523
|
-
return getExponentialDelay(currentRetryCount,
|
|
651
|
+
if (options.retryStrategy === "exponential") {
|
|
652
|
+
return getExponentialDelay(currentRetryCount, options);
|
|
524
653
|
}
|
|
525
|
-
return getLinearDelay(
|
|
654
|
+
return getLinearDelay(options);
|
|
526
655
|
};
|
|
527
656
|
const shouldAttemptRetry = async () => {
|
|
528
|
-
const customRetryCondition = await
|
|
529
|
-
const maxRetryAttempts =
|
|
657
|
+
const customRetryCondition = await options.retryCondition?.(ctx) ?? true;
|
|
658
|
+
const maxRetryAttempts = options.retryAttempts ?? 0;
|
|
530
659
|
const baseRetryCondition = maxRetryAttempts > currentRetryCount && customRetryCondition;
|
|
531
660
|
if (ctx.error.name !== "HTTPError") {
|
|
532
661
|
return baseRetryCondition;
|
|
533
662
|
}
|
|
534
663
|
const includesMethod = (
|
|
535
664
|
// eslint-disable-next-line no-implicit-coercion -- Boolean doesn't narrow
|
|
536
|
-
!!ctx.request.method &&
|
|
665
|
+
!!ctx.request.method && options.retryMethods?.includes(ctx.request.method)
|
|
537
666
|
);
|
|
538
667
|
const includesCodes = (
|
|
539
668
|
// eslint-disable-next-line no-implicit-coercion -- Boolean doesn't narrow
|
|
540
|
-
!!ctx.response?.status &&
|
|
669
|
+
!!ctx.response?.status && options.retryStatusCodes?.includes(ctx.response.status)
|
|
541
670
|
);
|
|
542
671
|
return includesCodes && includesMethod && baseRetryCondition;
|
|
543
672
|
};
|
|
544
673
|
const executeRetryHook = async (shouldThrowOnError) => {
|
|
545
674
|
try {
|
|
546
|
-
|
|
547
|
-
await executeHooks(ctx.options.onRetry(ctx));
|
|
675
|
+
return await executeHooks(options.onRetry?.(ctx));
|
|
548
676
|
} catch (error) {
|
|
549
|
-
const {
|
|
550
|
-
cloneResponse:
|
|
551
|
-
defaultErrorMessage:
|
|
677
|
+
const { apiDetails } = resolveErrorResult({
|
|
678
|
+
cloneResponse: options.cloneResponse,
|
|
679
|
+
defaultErrorMessage: options.defaultErrorMessage,
|
|
552
680
|
error,
|
|
553
|
-
resultMode:
|
|
681
|
+
resultMode: options.resultMode
|
|
554
682
|
});
|
|
555
683
|
if (shouldThrowOnError) {
|
|
556
684
|
throw error;
|
|
557
685
|
}
|
|
558
|
-
return
|
|
686
|
+
return apiDetails;
|
|
559
687
|
}
|
|
560
688
|
};
|
|
561
689
|
return {
|
|
@@ -656,7 +784,7 @@ var createFetchClient = (baseConfig = {}) => {
|
|
|
656
784
|
const mergedExtraOptions = {
|
|
657
785
|
...defaultExtraOptions,
|
|
658
786
|
...baseExtraOptions,
|
|
659
|
-
|
|
787
|
+
...!baseExtraOptions.mergeMainOptionsManuallyFromBase && extraOptions
|
|
660
788
|
};
|
|
661
789
|
const mergedRequestOptions = {
|
|
662
790
|
...defaultRequestOptions,
|
|
@@ -698,7 +826,7 @@ var createFetchClient = (baseConfig = {}) => {
|
|
|
698
826
|
const { handleRequestCancelStrategy, handleRequestDeferStrategy, removeDedupeKeyFromCache } = await createDedupeStrategy({ $RequestInfoCache, newFetchController, options, request });
|
|
699
827
|
await handleRequestCancelStrategy();
|
|
700
828
|
try {
|
|
701
|
-
await executeHooks(options.onRequest({ options, request }));
|
|
829
|
+
await executeHooks(options.onRequest?.({ options, request }));
|
|
702
830
|
request.headers = mergeAndResolveHeaders({
|
|
703
831
|
auth: options.auth,
|
|
704
832
|
body: request.body,
|
|
@@ -734,11 +862,11 @@ var createFetchClient = (baseConfig = {}) => {
|
|
|
734
862
|
data: validSuccessData,
|
|
735
863
|
options,
|
|
736
864
|
request,
|
|
737
|
-
response
|
|
865
|
+
response
|
|
738
866
|
};
|
|
739
867
|
await executeHooks(
|
|
740
|
-
options.onSuccess(successContext),
|
|
741
|
-
options.onResponse({ ...successContext, error: null })
|
|
868
|
+
options.onSuccess?.(successContext),
|
|
869
|
+
options.onResponse?.({ ...successContext, error: null })
|
|
742
870
|
);
|
|
743
871
|
return await resolveSuccessResult({
|
|
744
872
|
data: successContext.data,
|
|
@@ -773,33 +901,33 @@ var createFetchClient = (baseConfig = {}) => {
|
|
|
773
901
|
return callApi2(initURL, updatedOptions);
|
|
774
902
|
}
|
|
775
903
|
if (shouldThrowOnError) {
|
|
776
|
-
throw
|
|
904
|
+
throw error;
|
|
777
905
|
}
|
|
778
906
|
return customInfo ? getErrorResult(customInfo) : getErrorResult();
|
|
779
907
|
};
|
|
780
908
|
if (isHTTPErrorInstance(error)) {
|
|
781
909
|
await executeHooks(
|
|
782
|
-
options.onResponseError(errorContext),
|
|
783
|
-
options.onError(errorContext),
|
|
784
|
-
options.onResponse({ ...errorContext, data: null })
|
|
910
|
+
options.onResponseError?.(errorContext),
|
|
911
|
+
options.onError?.(errorContext),
|
|
912
|
+
options.onResponse?.({ ...errorContext, data: null })
|
|
785
913
|
);
|
|
786
914
|
return await handleRetryOrGetResult();
|
|
787
915
|
}
|
|
788
916
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
789
917
|
const { message, name } = error;
|
|
790
|
-
console.error(`${name}:`, message);
|
|
918
|
+
!shouldThrowOnError && console.error(`${name}:`, message);
|
|
791
919
|
return await handleRetryOrGetResult();
|
|
792
920
|
}
|
|
793
921
|
if (error instanceof DOMException && error.name === "TimeoutError") {
|
|
794
922
|
const message = `Request timed out after ${options.timeout}ms`;
|
|
795
|
-
console.error(`${error.name}:`, message);
|
|
923
|
+
!shouldThrowOnError && console.error(`${error.name}:`, message);
|
|
796
924
|
return await handleRetryOrGetResult({ message });
|
|
797
925
|
}
|
|
798
926
|
await executeHooks(
|
|
799
927
|
// == At this point only the request errors exist, so the request error interceptor is called
|
|
800
|
-
options.onRequestError(errorContext),
|
|
928
|
+
options.onRequestError?.(errorContext),
|
|
801
929
|
// == Also call the onError interceptor
|
|
802
|
-
options.onError(errorContext)
|
|
930
|
+
options.onError?.(errorContext)
|
|
803
931
|
);
|
|
804
932
|
return await handleRetryOrGetResult();
|
|
805
933
|
} finally {
|