@zayne-labs/callapi 1.7.4 → 1.7.6

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,964 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- HTTPError: () => HTTPError,
24
- callApi: () => callApi,
25
- createFetchClient: () => createFetchClient,
26
- defineParameters: () => defineParameters,
27
- definePlugin: () => definePlugin,
28
- getDefaultOptions: () => getDefaultOptions
29
- });
30
- module.exports = __toCommonJS(index_exports);
31
-
32
- // src/hooks.ts
33
- var hookRegistries = {
34
- onError: /* @__PURE__ */ new Set(),
35
- onRequest: /* @__PURE__ */ new Set(),
36
- onRequestError: /* @__PURE__ */ new Set(),
37
- onRequestStream: /* @__PURE__ */ new Set(),
38
- onResponse: /* @__PURE__ */ new Set(),
39
- onResponseError: /* @__PURE__ */ new Set(),
40
- onResponseStream: /* @__PURE__ */ new Set(),
41
- onRetry: /* @__PURE__ */ new Set(),
42
- onSuccess: /* @__PURE__ */ new Set()
43
- };
44
- var composeTwoHooks = (hooks, mergedHooksExecutionMode) => {
45
- if (hooks.length === 0) return;
46
- const mergedHook = async (ctx) => {
47
- if (mergedHooksExecutionMode === "sequential") {
48
- for (const hook of hooks) {
49
- await hook?.(ctx);
50
- }
51
- return;
52
- }
53
- if (mergedHooksExecutionMode === "parallel") {
54
- const hookArray = [...hooks];
55
- await Promise.all(hookArray.map((uniqueHook) => uniqueHook?.(ctx)));
56
- }
57
- };
58
- return mergedHook;
59
- };
60
- var executeHooks = (...hooks) => Promise.all(hooks);
61
-
62
- // src/auth.ts
63
- var getValue = (value) => {
64
- return isFunction(value) ? value() : value;
65
- };
66
- var getAuthHeader = (auth) => {
67
- if (auth === void 0) return;
68
- if (isString(auth) || auth === null) {
69
- return { Authorization: `Bearer ${auth}` };
70
- }
71
- switch (auth.type) {
72
- case "Basic": {
73
- const username = getValue(auth.username);
74
- const password = getValue(auth.password);
75
- if (username === void 0 || password === void 0) return;
76
- return {
77
- Authorization: `Basic ${globalThis.btoa(`${username}:${password}`)}`
78
- };
79
- }
80
- case "Custom": {
81
- const value = getValue(auth.value);
82
- if (value === void 0) return;
83
- const prefix = getValue(auth.prefix);
84
- return {
85
- Authorization: `${prefix} ${value}`
86
- };
87
- }
88
- default: {
89
- const bearer = getValue(auth.bearer);
90
- const token = getValue(auth.token);
91
- if ("token" in auth && token !== void 0) {
92
- return { Authorization: `Token ${token}` };
93
- }
94
- return bearer !== void 0 && { Authorization: `Bearer ${bearer}` };
95
- }
96
- }
97
- };
98
-
99
- // src/utils/type-helpers.ts
100
- var defineEnum = (value) => value;
101
-
102
- // src/types/common.ts
103
- var optionsEnumToOmitFromBase = defineEnum(["dedupeKey"]);
104
-
105
- // src/utils/constants.ts
106
- var fetchSpecificKeys = defineEnum([
107
- "body",
108
- "integrity",
109
- "duplex",
110
- "method",
111
- "headers",
112
- "signal",
113
- "cache",
114
- "redirect",
115
- "window",
116
- "credentials",
117
- "keepalive",
118
- "referrer",
119
- "priority",
120
- "mode",
121
- "referrerPolicy"
122
- ]);
123
- var retryStatusCodesLookup = defineEnum({
124
- 408: "Request Timeout",
125
- 409: "Conflict",
126
- 425: "Too Early",
127
- 429: "Too Many Requests",
128
- 500: "Internal Server Error",
129
- 502: "Bad Gateway",
130
- 503: "Service Unavailable",
131
- 504: "Gateway Timeout"
132
- });
133
- var defaultRetryMethods = ["GET", "POST"];
134
- var defaultRetryStatusCodes = Object.keys(retryStatusCodesLookup).map(Number);
135
- var defaultExtraOptions = {
136
- baseURL: "",
137
- bodySerializer: JSON.stringify,
138
- dedupeStrategy: "cancel",
139
- defaultErrorMessage: "Failed to fetch data from server!",
140
- mergedHooksExecutionMode: "parallel",
141
- mergedHooksExecutionOrder: "mainHooksAfterPlugins",
142
- responseType: "json",
143
- resultMode: "all",
144
- retryAttempts: 0,
145
- retryDelay: 1e3,
146
- retryMaxDelay: 1e4,
147
- retryMethods: defaultRetryMethods,
148
- retryStatusCodes: defaultRetryStatusCodes,
149
- retryStrategy: "linear"
150
- };
151
- var defaultRequestOptions = {
152
- method: "GET"
153
- };
154
- var getDefaultOptions = () => defaultExtraOptions;
155
-
156
- // src/utils/common.ts
157
- var omitKeys = (initialObject, keysToOmit) => {
158
- const updatedObject = {};
159
- const keysToOmitSet = new Set(keysToOmit);
160
- for (const [key, value] of Object.entries(initialObject)) {
161
- if (!keysToOmitSet.has(key)) {
162
- updatedObject[key] = value;
163
- }
164
- }
165
- return updatedObject;
166
- };
167
- var pickKeys = (initialObject, keysToPick) => {
168
- const updatedObject = {};
169
- const keysToPickSet = new Set(keysToPick);
170
- for (const [key, value] of Object.entries(initialObject)) {
171
- if (keysToPickSet.has(key)) {
172
- updatedObject[key] = value;
173
- }
174
- }
175
- return updatedObject;
176
- };
177
- var splitBaseConfig = (baseConfig) => [
178
- pickKeys(baseConfig, fetchSpecificKeys),
179
- omitKeys(baseConfig, [
180
- ...fetchSpecificKeys,
181
- ...optionsEnumToOmitFromBase
182
- ])
183
- ];
184
- var splitConfig = (config) => [
185
- pickKeys(config, fetchSpecificKeys),
186
- omitKeys(config, fetchSpecificKeys)
187
- ];
188
- var toQueryString = (params) => {
189
- if (!params) {
190
- console.error("toQueryString:", "No query params provided!");
191
- return null;
192
- }
193
- return new URLSearchParams(params).toString();
194
- };
195
- var objectifyHeaders = (headers) => {
196
- if (!headers || isPlainObject(headers)) {
197
- return headers;
198
- }
199
- return Object.fromEntries(headers);
200
- };
201
- var mergeAndResolveHeaders = (options) => {
202
- const { auth, baseHeaders, body, headers } = options;
203
- const shouldResolveHeaders = Boolean(baseHeaders || headers || body || auth);
204
- if (!shouldResolveHeaders) return;
205
- const headersObject = {
206
- ...getAuthHeader(auth),
207
- ...objectifyHeaders(baseHeaders),
208
- ...objectifyHeaders(headers)
209
- };
210
- if (isQueryString(body)) {
211
- headersObject["Content-Type"] = "application/x-www-form-urlencoded";
212
- return headersObject;
213
- }
214
- if (isSerializable(body) || isJsonString(body)) {
215
- headersObject["Content-Type"] = "application/json";
216
- headersObject.Accept = "application/json";
217
- }
218
- return headersObject;
219
- };
220
- var getFetchImpl = (customFetchImpl) => {
221
- if (customFetchImpl) {
222
- return customFetchImpl;
223
- }
224
- if (typeof globalThis !== "undefined" && isFunction(globalThis.fetch)) {
225
- return globalThis.fetch;
226
- }
227
- throw new Error("No fetch implementation found");
228
- };
229
- var PromiseWithResolvers = () => {
230
- let reject;
231
- let resolve;
232
- const promise = new Promise((res, rej) => {
233
- resolve = res;
234
- reject = rej;
235
- });
236
- return { promise, reject, resolve };
237
- };
238
- var waitUntil = (delay) => {
239
- if (delay === 0) return;
240
- const { promise, resolve } = PromiseWithResolvers();
241
- setTimeout(resolve, delay);
242
- return promise;
243
- };
244
- var createCombinedSignal = (...signals) => AbortSignal.any(signals.filter(Boolean));
245
- var createTimeoutSignal = (milliseconds) => AbortSignal.timeout(milliseconds);
246
-
247
- // src/error.ts
248
- var resolveErrorResult = (info) => {
249
- const { cloneResponse, defaultErrorMessage, error, message: customErrorMessage, resultMode } = info;
250
- let apiDetails = {
251
- data: null,
252
- error: {
253
- errorData: error,
254
- message: customErrorMessage ?? error.message,
255
- name: error.name
256
- },
257
- response: null
258
- };
259
- if (isHTTPErrorInstance(error)) {
260
- const { errorData, message = defaultErrorMessage, name, response } = error;
261
- apiDetails = {
262
- data: null,
263
- error: {
264
- errorData,
265
- message,
266
- name
267
- },
268
- response: cloneResponse ? response.clone() : response
269
- };
270
- }
271
- const resultModeMap = {
272
- all: apiDetails,
273
- allWithException: apiDetails,
274
- allWithoutResponse: omitKeys(apiDetails, ["response"]),
275
- onlyError: apiDetails.error,
276
- onlyResponse: apiDetails.response,
277
- onlyResponseWithException: apiDetails.response,
278
- onlySuccess: apiDetails.data,
279
- onlySuccessWithException: apiDetails.data
280
- };
281
- const getErrorResult = (customErrorInfo) => {
282
- const errorVariantResult = resultModeMap[resultMode ?? "all"];
283
- return customErrorInfo ? {
284
- ...errorVariantResult,
285
- error: {
286
- ...errorVariantResult.error,
287
- ...customErrorInfo
288
- }
289
- } : errorVariantResult;
290
- };
291
- return { apiDetails, getErrorResult };
292
- };
293
- var HTTPError = class {
294
- cause;
295
- errorData;
296
- isHTTPError = true;
297
- message;
298
- name = "HTTPError";
299
- response;
300
- constructor(errorDetails, errorOptions) {
301
- const { defaultErrorMessage, errorData, response } = errorDetails;
302
- this.message = errorData?.message ?? defaultErrorMessage;
303
- errorOptions?.cause && (this.cause = errorOptions.cause);
304
- this.errorData = errorData;
305
- this.response = response;
306
- Error.captureStackTrace(this, this.constructor);
307
- }
308
- };
309
-
310
- // src/utils/guards.ts
311
- var isHTTPErrorInstance = (error) => {
312
- return (
313
- // prettier-ignore
314
- error instanceof HTTPError || isPlainObject(error) && error.name === "HTTPError" && error.isHTTPError === true
315
- );
316
- };
317
- var isArray = (value) => Array.isArray(value);
318
- var isObject = (value) => typeof value === "object" && value !== null;
319
- var hasObjectPrototype = (value) => {
320
- return Object.prototype.toString.call(value) === "[object Object]";
321
- };
322
- var isPlainObject = (value) => {
323
- if (!hasObjectPrototype(value)) {
324
- return false;
325
- }
326
- const constructor = value?.constructor;
327
- if (constructor === void 0) {
328
- return true;
329
- }
330
- const prototype = constructor.prototype;
331
- if (!hasObjectPrototype(prototype)) {
332
- return false;
333
- }
334
- if (!Object.hasOwn(prototype, "isPrototypeOf")) {
335
- return false;
336
- }
337
- if (Object.getPrototypeOf(value) !== Object.prototype) {
338
- return false;
339
- }
340
- return true;
341
- };
342
- var isJsonString = (value) => {
343
- if (!isString(value)) {
344
- return false;
345
- }
346
- try {
347
- JSON.parse(value);
348
- return true;
349
- } catch {
350
- return false;
351
- }
352
- };
353
- var isSerializable = (value) => {
354
- return isPlainObject(value) || isArray(value) || typeof value?.toJSON === "function";
355
- };
356
- var isFunction = (value) => typeof value === "function";
357
- var isQueryString = (value) => isString(value) && value.includes("=");
358
- var isString = (value) => typeof value === "string";
359
- var isReadableStream = (value) => {
360
- return value instanceof ReadableStream;
361
- };
362
-
363
- // src/stream.ts
364
- var createProgressEvent = (options) => {
365
- const { chunk, totalBytes, transferredBytes } = options;
366
- return {
367
- chunk,
368
- progress: Math.round(transferredBytes / totalBytes * 100) || 0,
369
- totalBytes,
370
- transferredBytes
371
- };
372
- };
373
- var calculateTotalBytesFromBody = async (requestBody, existingTotalBytes) => {
374
- let totalBytes = existingTotalBytes;
375
- if (!requestBody) {
376
- return totalBytes;
377
- }
378
- for await (const chunk of requestBody) {
379
- totalBytes += chunk.byteLength;
380
- }
381
- return totalBytes;
382
- };
383
- var toStreamableRequest = async (context) => {
384
- const { baseConfig, config, options, request, requestInstance } = context;
385
- if (!options.onRequestStream || !requestInstance.body) return;
386
- const contentLength = requestInstance.headers.get("content-length") ?? new Headers(request.headers).get("content-length") ?? request.body?.size;
387
- let totalBytes = Number(contentLength ?? 0);
388
- const shouldForceContentLengthCalc = isObject(options.forceCalculateStreamSize) ? options.forceCalculateStreamSize.request : options.forceCalculateStreamSize;
389
- if (!contentLength && shouldForceContentLengthCalc) {
390
- totalBytes = await calculateTotalBytesFromBody(requestInstance.clone().body, totalBytes);
391
- }
392
- let transferredBytes = 0;
393
- await executeHooks(
394
- options.onRequestStream({
395
- baseConfig,
396
- config,
397
- event: createProgressEvent({ chunk: new Uint8Array(), totalBytes, transferredBytes }),
398
- options,
399
- request,
400
- requestInstance
401
- })
402
- );
403
- const body = requestInstance.body;
404
- void new ReadableStream({
405
- start: async (controller) => {
406
- if (!body) return;
407
- for await (const chunk of body) {
408
- transferredBytes += chunk.byteLength;
409
- totalBytes = Math.max(totalBytes, transferredBytes);
410
- await executeHooks(
411
- options.onRequestStream?.({
412
- baseConfig,
413
- config,
414
- event: createProgressEvent({ chunk, totalBytes, transferredBytes }),
415
- options,
416
- request,
417
- requestInstance
418
- })
419
- );
420
- controller.enqueue(chunk);
421
- }
422
- controller.close();
423
- }
424
- });
425
- };
426
- var toStreamableResponse = async (context) => {
427
- const { baseConfig, config, options, request, response } = context;
428
- if (!options.onResponseStream || !response.body) {
429
- return response;
430
- }
431
- const contentLength = response.headers.get("content-length");
432
- let totalBytes = Number(contentLength ?? 0);
433
- const shouldForceContentLengthCalc = isObject(options.forceCalculateStreamSize) ? options.forceCalculateStreamSize.response : options.forceCalculateStreamSize;
434
- if (!contentLength && shouldForceContentLengthCalc) {
435
- totalBytes = await calculateTotalBytesFromBody(response.clone().body, totalBytes);
436
- }
437
- let transferredBytes = 0;
438
- await executeHooks(
439
- options.onResponseStream({
440
- baseConfig,
441
- config,
442
- event: createProgressEvent({ chunk: new Uint8Array(), totalBytes, transferredBytes }),
443
- options,
444
- request,
445
- response
446
- })
447
- );
448
- const body = response.body;
449
- const stream = new ReadableStream({
450
- start: async (controller) => {
451
- if (!body) return;
452
- for await (const chunk of body) {
453
- transferredBytes += chunk.byteLength;
454
- totalBytes = Math.max(totalBytes, transferredBytes);
455
- await executeHooks(
456
- options.onResponseStream?.({
457
- baseConfig,
458
- config,
459
- event: createProgressEvent({ chunk, totalBytes, transferredBytes }),
460
- options,
461
- request,
462
- response
463
- })
464
- );
465
- controller.enqueue(chunk);
466
- }
467
- controller.close();
468
- }
469
- });
470
- return new Response(stream, response);
471
- };
472
-
473
- // src/dedupe.ts
474
- var generateDedupeKey = (options, request) => {
475
- const shouldHaveDedupeKey = options.dedupeStrategy === "cancel" || options.dedupeStrategy === "defer";
476
- if (!shouldHaveDedupeKey) {
477
- return null;
478
- }
479
- return `${options.fullURL}-${JSON.stringify({ options, request })}`;
480
- };
481
- var createDedupeStrategy = async (context) => {
482
- const { $RequestInfoCache, baseConfig, config, newFetchController, options, request } = context;
483
- const dedupeKey = options.dedupeKey ?? generateDedupeKey(options, request);
484
- const $RequestInfoCacheOrNull = dedupeKey !== null ? $RequestInfoCache : null;
485
- if (dedupeKey !== null) {
486
- await waitUntil(0.1);
487
- }
488
- const prevRequestInfo = $RequestInfoCacheOrNull?.get(dedupeKey);
489
- const handleRequestCancelStrategy = () => {
490
- const shouldCancelRequest = prevRequestInfo && options.dedupeStrategy === "cancel";
491
- if (!shouldCancelRequest) return;
492
- const message = options.dedupeKey ? `Duplicate request detected - Aborting previous request with key '${options.dedupeKey}' as a new request was initiated` : `Duplicate request detected - Aborting previous request to '${options.fullURL}' as a new request with identical options was initiated`;
493
- const reason = new DOMException(message, "AbortError");
494
- prevRequestInfo.controller.abort(reason);
495
- return Promise.resolve();
496
- };
497
- const handleRequestDeferStrategy = async () => {
498
- const fetchApi = getFetchImpl(options.customFetchImpl);
499
- const shouldUsePromiseFromCache = prevRequestInfo && options.dedupeStrategy === "defer";
500
- const requestInstance = new Request(
501
- options.fullURL,
502
- isReadableStream(request.body) && !request.duplex ? { ...request, duplex: "half" } : request
503
- );
504
- await toStreamableRequest({
505
- baseConfig,
506
- config,
507
- options,
508
- request,
509
- requestInstance: requestInstance.clone()
510
- });
511
- const getFetchApiPromise = () => {
512
- if (isReadableStream(request.body)) {
513
- return fetchApi(requestInstance.clone());
514
- }
515
- return fetchApi(options.fullURL, request);
516
- };
517
- const responsePromise = shouldUsePromiseFromCache ? prevRequestInfo.responsePromise : getFetchApiPromise();
518
- $RequestInfoCacheOrNull?.set(dedupeKey, { controller: newFetchController, responsePromise });
519
- const streamableResponse = toStreamableResponse({
520
- baseConfig,
521
- config,
522
- options,
523
- request,
524
- response: await responsePromise
525
- });
526
- return streamableResponse;
527
- };
528
- const removeDedupeKeyFromCache = () => {
529
- $RequestInfoCacheOrNull?.delete(dedupeKey);
530
- };
531
- return {
532
- handleRequestCancelStrategy,
533
- handleRequestDeferStrategy,
534
- removeDedupeKeyFromCache
535
- };
536
- };
537
-
538
- // src/plugins.ts
539
- var definePlugin = (plugin) => {
540
- return plugin;
541
- };
542
- var resolvePluginArray = (plugins, basePlugins) => {
543
- if (!plugins) {
544
- return [];
545
- }
546
- if (isFunction(plugins)) {
547
- return plugins({ basePlugins: basePlugins ?? [] });
548
- }
549
- return plugins;
550
- };
551
- var initializePlugins = async (context) => {
552
- const { baseConfig, config, initURL, options, request } = context;
553
- const clonedHookRegistries = structuredClone(hookRegistries);
554
- const addMainHooks = () => {
555
- for (const key of Object.keys(clonedHookRegistries)) {
556
- const mainHook = options[key];
557
- clonedHookRegistries[key].add(mainHook);
558
- }
559
- };
560
- const addPluginHooks = (pluginHooks) => {
561
- for (const key of Object.keys(clonedHookRegistries)) {
562
- const pluginHook = pluginHooks[key];
563
- clonedHookRegistries[key].add(pluginHook);
564
- }
565
- };
566
- if (options.mergedHooksExecutionOrder === "mainHooksBeforePlugins") {
567
- addMainHooks();
568
- }
569
- const resolvedPlugins = resolvePluginArray(options.plugins, baseConfig.plugins);
570
- let resolvedUrl = initURL;
571
- let resolvedOptions = options;
572
- let resolvedRequestOptions = request;
573
- const executePluginInit = async (pluginInit) => {
574
- if (!pluginInit) return;
575
- const initResult = await pluginInit({
576
- baseConfig,
577
- config,
578
- initURL,
579
- options,
580
- request
581
- });
582
- if (!isPlainObject(initResult)) return;
583
- if (isString(initResult.initURL)) {
584
- resolvedUrl = initResult.initURL;
585
- }
586
- if (isPlainObject(initResult.request)) {
587
- resolvedRequestOptions = initResult.request;
588
- }
589
- if (isPlainObject(initResult.options)) {
590
- resolvedOptions = initResult.options;
591
- }
592
- };
593
- for (const plugin of resolvedPlugins) {
594
- await executePluginInit(plugin.init);
595
- if (!plugin.hooks) continue;
596
- addPluginHooks(plugin.hooks);
597
- }
598
- if (!options.mergedHooksExecutionOrder || options.mergedHooksExecutionOrder === "mainHooksAfterPlugins") {
599
- addMainHooks();
600
- }
601
- const resolvedHooks = {};
602
- for (const [key, hookRegistry] of Object.entries(clonedHookRegistries)) {
603
- const flattenedHookArray = [...hookRegistry].flat().filter(Boolean);
604
- const composedHook = composeTwoHooks(flattenedHookArray, options.mergedHooksExecutionMode);
605
- resolvedHooks[key] = composedHook;
606
- }
607
- return {
608
- resolvedHooks,
609
- resolvedOptions,
610
- resolvedRequestOptions,
611
- url: resolvedUrl?.toString()
612
- };
613
- };
614
-
615
- // src/response.ts
616
- var getResponseType = (response, parser) => ({
617
- arrayBuffer: () => response.arrayBuffer(),
618
- blob: () => response.blob(),
619
- formData: () => response.formData(),
620
- json: async () => {
621
- if (parser) {
622
- const text = await response.text();
623
- return parser(text);
624
- }
625
- return response.json();
626
- },
627
- stream: () => response.body,
628
- text: () => response.text()
629
- });
630
- var resolveResponseData = async (response, responseType, parser) => {
631
- const RESPONSE_TYPE_LOOKUP = getResponseType(response, parser);
632
- if (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, responseType)) {
633
- throw new Error(`Invalid response type: ${responseType}`);
634
- }
635
- const responseData = await RESPONSE_TYPE_LOOKUP[responseType]();
636
- return responseData;
637
- };
638
- var resolveSuccessResult = (info) => {
639
- const { data, response, resultMode } = info;
640
- const apiDetails = { data, error: null, response };
641
- if (!resultMode) {
642
- return apiDetails;
643
- }
644
- const resultModeMap = {
645
- all: apiDetails,
646
- allWithException: apiDetails,
647
- allWithoutResponse: omitKeys(apiDetails, ["response"]),
648
- onlyError: apiDetails.error,
649
- onlyResponse: apiDetails.response,
650
- onlyResponseWithException: apiDetails.response,
651
- onlySuccess: apiDetails.data,
652
- onlySuccessWithException: apiDetails.data
653
- };
654
- return resultModeMap[resultMode];
655
- };
656
-
657
- // src/retry.ts
658
- var getLinearDelay = (options) => options.retryDelay ?? 1e3;
659
- var getExponentialDelay = (currentAttemptCount, options) => {
660
- const maxDelay = options.retryMaxDelay ?? 1e4;
661
- const exponentialDelay = (options.retryDelay ?? 1e3) * 2 ** currentAttemptCount;
662
- return Math.min(exponentialDelay, maxDelay);
663
- };
664
- var createRetryStrategy = (ctx) => {
665
- const { options } = ctx;
666
- const currentRetryCount = options["~retryCount"] ?? 0;
667
- const getDelay = () => {
668
- if (options.retryStrategy === "exponential") {
669
- return getExponentialDelay(currentRetryCount, options);
670
- }
671
- return getLinearDelay(options);
672
- };
673
- const shouldAttemptRetry = async () => {
674
- const customRetryCondition = await options.retryCondition?.(ctx) ?? true;
675
- const maxRetryAttempts = options.retryAttempts ?? 0;
676
- const baseRetryCondition = maxRetryAttempts > currentRetryCount && customRetryCondition;
677
- if (ctx.error.name !== "HTTPError") {
678
- return baseRetryCondition;
679
- }
680
- const includesMethod = (
681
- // eslint-disable-next-line no-implicit-coercion -- Boolean doesn't narrow
682
- !!ctx.request.method && options.retryMethods?.includes(ctx.request.method)
683
- );
684
- const includesCodes = (
685
- // eslint-disable-next-line no-implicit-coercion -- Boolean doesn't narrow
686
- !!ctx.response?.status && options.retryStatusCodes?.includes(ctx.response.status)
687
- );
688
- return includesCodes && includesMethod && baseRetryCondition;
689
- };
690
- const executeRetryHook = async (shouldThrowOnError) => {
691
- try {
692
- return await executeHooks(options.onRetry?.(ctx));
693
- } catch (error) {
694
- const { apiDetails } = resolveErrorResult({
695
- cloneResponse: options.cloneResponse,
696
- defaultErrorMessage: options.defaultErrorMessage,
697
- error,
698
- resultMode: options.resultMode
699
- });
700
- if (shouldThrowOnError) {
701
- throw error;
702
- }
703
- return apiDetails;
704
- }
705
- };
706
- return {
707
- executeRetryHook,
708
- getDelay,
709
- shouldAttemptRetry
710
- };
711
- };
712
-
713
- // src/url.ts
714
- var slash = "/";
715
- var column = ":";
716
- var mergeUrlWithParams = (url, params) => {
717
- if (!params) {
718
- return url;
719
- }
720
- let newUrl = url;
721
- if (isArray(params)) {
722
- const matchedParamArray = newUrl.split(slash).filter((param) => param.startsWith(column));
723
- for (const [index, matchedParam] of matchedParamArray.entries()) {
724
- const realParam = params[index];
725
- newUrl = newUrl.replace(matchedParam, realParam);
726
- }
727
- return newUrl;
728
- }
729
- for (const [key, value] of Object.entries(params)) {
730
- newUrl = newUrl.replace(`${column}${key}`, String(value));
731
- }
732
- return newUrl;
733
- };
734
- var questionMark = "?";
735
- var ampersand = "&";
736
- var mergeUrlWithQuery = (url, query) => {
737
- if (!query) {
738
- return url;
739
- }
740
- const queryString = toQueryString(query);
741
- if (queryString?.length === 0) {
742
- return url;
743
- }
744
- if (url.endsWith(questionMark)) {
745
- return `${url}${queryString}`;
746
- }
747
- if (url.includes(questionMark)) {
748
- return `${url}${ampersand}${queryString}`;
749
- }
750
- return `${url}${questionMark}${queryString}`;
751
- };
752
- var mergeUrlWithParamsAndQuery = (url, params, query) => {
753
- if (!url) return;
754
- const urlWithMergedParams = mergeUrlWithParams(url, params);
755
- return mergeUrlWithQuery(urlWithMergedParams, query);
756
- };
757
-
758
- // src/validation.ts
759
- var standardSchemaParser = async (schema, inputData) => {
760
- const result = await schema["~standard"].validate(inputData);
761
- if (result.issues) {
762
- throw new Error(JSON.stringify(result.issues, null, 2), { cause: result.issues });
763
- }
764
- return result.value;
765
- };
766
- var handleValidation = async (responseData, schema, validator) => {
767
- const validResponseData = validator ? validator(responseData) : responseData;
768
- const schemaValidResponseData = schema ? await standardSchemaParser(schema, validResponseData) : validResponseData;
769
- return schemaValidResponseData;
770
- };
771
-
772
- // src/createFetchClient.ts
773
- var createFetchClient = (initBaseConfig = {}) => {
774
- const $RequestInfoCache = /* @__PURE__ */ new Map();
775
- const callApi2 = async (...parameters) => {
776
- const [initURL, initConfig = {}] = parameters;
777
- const [fetchOptions, extraOptions] = splitConfig(initConfig);
778
- const resolvedBaseConfig = isFunction(initBaseConfig) ? initBaseConfig({ initURL: initURL.toString(), options: extraOptions, request: fetchOptions }) : initBaseConfig;
779
- const [baseFetchOptions, baseExtraOptions] = splitBaseConfig(resolvedBaseConfig);
780
- const mergedExtraOptions = {
781
- ...defaultExtraOptions,
782
- ...baseExtraOptions,
783
- ...baseExtraOptions.skipAutoMergeFor !== "all" && baseExtraOptions.skipAutoMergeFor !== "options" && extraOptions
784
- };
785
- const mergedRequestOptions = {
786
- ...defaultRequestOptions,
787
- ...baseFetchOptions,
788
- ...baseExtraOptions.skipAutoMergeFor !== "all" && baseExtraOptions.skipAutoMergeFor !== "request" && fetchOptions
789
- };
790
- const baseConfig = resolvedBaseConfig;
791
- const config = initConfig;
792
- const { resolvedHooks, resolvedOptions, resolvedRequestOptions, url } = await initializePlugins({
793
- baseConfig,
794
- config,
795
- initURL,
796
- options: mergedExtraOptions,
797
- request: mergedRequestOptions
798
- });
799
- const fullURL = `${resolvedOptions.baseURL}${mergeUrlWithParamsAndQuery(url, resolvedOptions.params, resolvedOptions.query)}`;
800
- const options = {
801
- ...resolvedOptions,
802
- ...resolvedHooks,
803
- fullURL,
804
- initURL: initURL.toString()
805
- };
806
- const newFetchController = new AbortController();
807
- const timeoutSignal = options.timeout != null ? createTimeoutSignal(options.timeout) : null;
808
- const combinedSignal = createCombinedSignal(
809
- resolvedRequestOptions.signal,
810
- timeoutSignal,
811
- newFetchController.signal
812
- );
813
- const request = {
814
- ...resolvedRequestOptions,
815
- body: isSerializable(resolvedRequestOptions.body) ? options.bodySerializer(resolvedRequestOptions.body) : resolvedRequestOptions.body,
816
- headers: mergeAndResolveHeaders({
817
- auth: options.auth,
818
- baseHeaders: baseFetchOptions.headers,
819
- body: resolvedRequestOptions.body,
820
- headers: fetchOptions.headers
821
- }),
822
- signal: combinedSignal
823
- };
824
- const { handleRequestCancelStrategy, handleRequestDeferStrategy, removeDedupeKeyFromCache } = await createDedupeStrategy({
825
- $RequestInfoCache,
826
- baseConfig,
827
- config,
828
- newFetchController,
829
- options,
830
- request
831
- });
832
- await handleRequestCancelStrategy();
833
- try {
834
- await executeHooks(options.onRequest?.({ baseConfig, config, options, request }));
835
- request.headers = mergeAndResolveHeaders({
836
- auth: options.auth,
837
- body: request.body,
838
- headers: request.headers
839
- });
840
- const response = await handleRequestDeferStrategy();
841
- const shouldCloneResponse = options.dedupeStrategy === "defer" || options.cloneResponse;
842
- const schemas = isFunction(options.schemas) ? options.schemas({ baseSchemas: baseExtraOptions.schemas ?? {} }) : options.schemas;
843
- const validators = isFunction(options.validators) ? options.validators({ baseValidators: baseExtraOptions.validators ?? {} }) : options.validators;
844
- if (!response.ok) {
845
- const errorData = await resolveResponseData(
846
- shouldCloneResponse ? response.clone() : response,
847
- options.responseType,
848
- options.responseParser
849
- );
850
- const validErrorData = await handleValidation(
851
- errorData,
852
- schemas?.errorData,
853
- validators?.errorData
854
- );
855
- throw new HTTPError({
856
- defaultErrorMessage: options.defaultErrorMessage,
857
- errorData: validErrorData,
858
- response
859
- });
860
- }
861
- const successData = await resolveResponseData(
862
- shouldCloneResponse ? response.clone() : response,
863
- options.responseType,
864
- options.responseParser
865
- );
866
- const validSuccessData = await handleValidation(successData, schemas?.data, validators?.data);
867
- const successContext = {
868
- baseConfig,
869
- config,
870
- data: validSuccessData,
871
- options,
872
- request,
873
- response
874
- };
875
- await executeHooks(
876
- options.onSuccess?.(successContext),
877
- options.onResponse?.({ ...successContext, error: null })
878
- );
879
- return await resolveSuccessResult({
880
- data: successContext.data,
881
- response: successContext.response,
882
- resultMode: options.resultMode
883
- });
884
- } catch (error) {
885
- const { apiDetails, getErrorResult } = resolveErrorResult({
886
- cloneResponse: options.cloneResponse,
887
- defaultErrorMessage: options.defaultErrorMessage,
888
- error,
889
- resultMode: options.resultMode
890
- });
891
- const errorContext = {
892
- baseConfig,
893
- config,
894
- error: apiDetails.error,
895
- options,
896
- request,
897
- response: apiDetails.response
898
- };
899
- const shouldThrowOnError = isFunction(options.throwOnError) ? options.throwOnError(errorContext) : options.throwOnError;
900
- const handleRetryOrGetResult = async (customInfo) => {
901
- const { executeRetryHook, getDelay, shouldAttemptRetry } = createRetryStrategy(errorContext);
902
- const shouldRetry = !combinedSignal.aborted && await shouldAttemptRetry();
903
- if (shouldRetry) {
904
- await executeRetryHook(shouldThrowOnError);
905
- const delay = getDelay();
906
- await waitUntil(delay);
907
- const updatedOptions = {
908
- ...config,
909
- "~retryCount": (options["~retryCount"] ?? 0) + 1
910
- };
911
- return callApi2(initURL, updatedOptions);
912
- }
913
- if (shouldThrowOnError) {
914
- throw error;
915
- }
916
- return customInfo ? getErrorResult(customInfo) : getErrorResult();
917
- };
918
- if (isHTTPErrorInstance(error)) {
919
- await executeHooks(
920
- options.onResponseError?.(errorContext),
921
- options.onError?.(errorContext),
922
- options.onResponse?.({ ...errorContext, data: null })
923
- );
924
- return await handleRetryOrGetResult();
925
- }
926
- if (error instanceof DOMException && error.name === "AbortError") {
927
- const { message, name } = error;
928
- !shouldThrowOnError && console.error(`${name}:`, message);
929
- return await handleRetryOrGetResult();
930
- }
931
- if (error instanceof DOMException && error.name === "TimeoutError") {
932
- const message = `Request timed out after ${options.timeout}ms`;
933
- !shouldThrowOnError && console.error(`${error.name}:`, message);
934
- return await handleRetryOrGetResult({ message });
935
- }
936
- await executeHooks(
937
- // == At this point only the request errors exist, so the request error hook is called
938
- options.onRequestError?.(errorContext),
939
- // == Also call the onError hook
940
- options.onError?.(errorContext)
941
- );
942
- return await handleRetryOrGetResult();
943
- } finally {
944
- removeDedupeKeyFromCache();
945
- }
946
- };
947
- return callApi2;
948
- };
949
- var callApi = createFetchClient();
950
-
951
- // src/defineParameters.ts
952
- var defineParameters = (...parameters) => {
953
- return parameters;
954
- };
955
- // Annotate the CommonJS export names for ESM import in node:
956
- 0 && (module.exports = {
957
- HTTPError,
958
- callApi,
959
- createFetchClient,
960
- defineParameters,
961
- definePlugin,
962
- getDefaultOptions
963
- });
964
- //# sourceMappingURL=index.cjs.map