@zayne-labs/callapi 1.8.21 → 1.9.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.
- package/README.md +3 -1
- package/dist/esm/{common-C-kIzPcz.d.ts → common-Byku1Sji.d.ts} +83 -129
- package/dist/esm/index.d.ts +5 -5
- package/dist/esm/index.js +59 -93
- 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/dist/esm/{utils-DZe23qYR.js → utils-DUbIqsYh.js} +28 -44
- package/dist/esm/utils-DUbIqsYh.js.map +1 -0
- package/package.json +5 -5
- package/dist/esm/utils-DZe23qYR.js.map +0 -1
package/dist/esm/index.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { HTTPError, ValidationError, createCombinedSignal, createTimeoutSignal,
|
1
|
+
import { HTTPError, ValidationError, createCombinedSignal, createTimeoutSignal, defineEnum, deterministicHashFn, extraOptionDefaults, getBody, getFetchImpl, getHeaders, isArray, isFunction, isHTTPErrorInstance, isObject, isPlainObject, isReadableStream, isString, isValidationErrorInstance, requestOptionDefaults, splitBaseConfig, splitConfig, toQueryString, waitFor } from "./utils-DUbIqsYh.js";
|
2
2
|
|
3
3
|
//#region src/result.ts
|
4
4
|
const getResponseType = (response, parser) => ({
|
@@ -13,8 +13,8 @@ const getResponseType = (response, parser) => ({
|
|
13
13
|
text: () => response.text()
|
14
14
|
});
|
15
15
|
const resolveResponseData = (response, responseType, parser) => {
|
16
|
-
const selectedParser = parser ??
|
17
|
-
const selectedResponseType = responseType ??
|
16
|
+
const selectedParser = parser ?? extraOptionDefaults().responseParser;
|
17
|
+
const selectedResponseType = responseType ?? extraOptionDefaults().responseType;
|
18
18
|
const RESPONSE_TYPE_LOOKUP = getResponseType(response, selectedParser);
|
19
19
|
if (!Object.hasOwn(RESPONSE_TYPE_LOOKUP, selectedResponseType)) throw new Error(`Invalid response type: ${responseType}`);
|
20
20
|
return RESPONSE_TYPE_LOOKUP[selectedResponseType]();
|
@@ -241,71 +241,25 @@ const toStreamableResponse = async (context) => {
|
|
241
241
|
|
242
242
|
//#endregion
|
243
243
|
//#region src/dedupe.ts
|
244
|
-
const resolveDedupeKey = (dedupeKey, context) => {
|
245
|
-
if (isFunction(dedupeKey)) return dedupeKey(context);
|
246
|
-
return dedupeKey ?? null;
|
247
|
-
};
|
248
|
-
const getAbortErrorMessage = (dedupeKey, context) => {
|
249
|
-
if (dedupeKey) return `Duplicate request detected - Aborted previous request with key '${resolveDedupeKey(dedupeKey, context)}' as a new request was initiated`;
|
250
|
-
return `Duplicate request detected - Aborted previous request to '${context.options.fullURL}' as a new request with identical options was initiated`;
|
251
|
-
};
|
252
|
-
/**
|
253
|
-
* @description Creates and manages the deduplication strategy for a request.
|
254
|
-
*
|
255
|
-
* This is the core function that implements request deduplication logic. It handles
|
256
|
-
* cache management, key generation, and provides strategy-specific handlers for
|
257
|
-
* cancel and defer operations.
|
258
|
-
*
|
259
|
-
* **Key Responsibilities:**
|
260
|
-
* - Generates or resolves deduplication keys
|
261
|
-
* - Manages cache scope (local vs global)
|
262
|
-
* - Provides strategy handlers (cancel, defer)
|
263
|
-
* - Handles cache cleanup operations
|
264
|
-
*
|
265
|
-
* **Performance Optimizations:**
|
266
|
-
* - Includes a small delay (0.1ms) for simultaneous request handling
|
267
|
-
* - Lazy cache creation for global scopes
|
268
|
-
* - Efficient cache key resolution
|
269
|
-
*
|
270
|
-
* **Internal Usage:**
|
271
|
-
* This function is used internally by the CallApi system and is not intended
|
272
|
-
* for direct use in application code. It's called automatically during request
|
273
|
-
* processing when deduplication is enabled.
|
274
|
-
*
|
275
|
-
* @param context - Extended request context with deduplication-specific properties
|
276
|
-
* @returns Promise resolving to deduplication strategy handlers and metadata
|
277
|
-
*
|
278
|
-
* @example
|
279
|
-
* ```ts
|
280
|
-
* // This is used internally, but conceptually:
|
281
|
-
* const strategy = await createDedupeStrategy({
|
282
|
-
* ...requestContext,
|
283
|
-
* $GlobalRequestInfoCache: globalCache,
|
284
|
-
* $LocalRequestInfoCache: localCache,
|
285
|
-
* newFetchController: new AbortController()
|
286
|
-
* });
|
287
|
-
*
|
288
|
-
* // Use the returned strategy
|
289
|
-
* await strategy.handleRequestCancelStrategy();
|
290
|
-
* const response = await strategy.handleRequestDeferStrategy({ options, request });
|
291
|
-
* strategy.removeDedupeKeyFromCache();
|
292
|
-
* ```
|
293
|
-
*/
|
294
244
|
const createDedupeStrategy = async (context) => {
|
295
245
|
const { $GlobalRequestInfoCache: $GlobalRequestInfoCache$1, $LocalRequestInfoCache, baseConfig, config, newFetchController, options: globalOptions, request: globalRequest } = context;
|
296
|
-
const dedupeStrategy = globalOptions.dedupeStrategy ??
|
246
|
+
const dedupeStrategy = globalOptions.dedupeStrategy ?? extraOptionDefaults().dedupeStrategy;
|
247
|
+
const resolvedDedupeStrategy = isFunction(dedupeStrategy) ? dedupeStrategy(context) : dedupeStrategy;
|
297
248
|
const getDedupeKey = () => {
|
298
|
-
const shouldHaveDedupeKey =
|
249
|
+
const shouldHaveDedupeKey = resolvedDedupeStrategy === "cancel" || resolvedDedupeStrategy === "defer";
|
299
250
|
if (!shouldHaveDedupeKey) return null;
|
300
|
-
if (globalOptions.dedupeKey)
|
251
|
+
if (globalOptions.dedupeKey) {
|
252
|
+
const resolvedDedupeKey = isFunction(globalOptions.dedupeKey) ? globalOptions.dedupeKey(context) : globalOptions.dedupeKey;
|
253
|
+
return resolvedDedupeKey;
|
254
|
+
}
|
301
255
|
return `${globalOptions.fullURL}-${deterministicHashFn({
|
302
256
|
options: globalOptions,
|
303
257
|
request: globalRequest
|
304
258
|
})}`;
|
305
259
|
};
|
306
260
|
const dedupeKey = getDedupeKey();
|
307
|
-
const dedupeCacheScope = globalOptions.dedupeCacheScope ??
|
308
|
-
const dedupeCacheScopeKey = globalOptions.dedupeCacheScopeKey ??
|
261
|
+
const dedupeCacheScope = globalOptions.dedupeCacheScope ?? extraOptionDefaults().dedupeCacheScope;
|
262
|
+
const dedupeCacheScopeKey = globalOptions.dedupeCacheScopeKey ?? extraOptionDefaults().dedupeCacheScopeKey;
|
309
263
|
if (dedupeCacheScope === "global" && !$GlobalRequestInfoCache$1.has(dedupeCacheScopeKey)) $GlobalRequestInfoCache$1.set(dedupeCacheScopeKey, /* @__PURE__ */ new Map());
|
310
264
|
const $RequestInfoCache = dedupeCacheScope === "global" ? $GlobalRequestInfoCache$1.get(dedupeCacheScopeKey) : $LocalRequestInfoCache;
|
311
265
|
const $RequestInfoCacheOrNull = dedupeKey !== null ? $RequestInfoCache : null;
|
@@ -315,10 +269,14 @@ const createDedupeStrategy = async (context) => {
|
|
315
269
|
******/
|
316
270
|
if (dedupeKey !== null) await waitFor(.1);
|
317
271
|
const prevRequestInfo = $RequestInfoCacheOrNull?.get(dedupeKey);
|
272
|
+
const getAbortErrorMessage = () => {
|
273
|
+
if (globalOptions.dedupeKey) return `Duplicate request detected - Aborted previous request with key '${dedupeKey}' as a new request was initiated`;
|
274
|
+
return `Duplicate request detected - Aborted previous request to '${globalOptions.fullURL}' as a new request with identical options was initiated`;
|
275
|
+
};
|
318
276
|
const handleRequestCancelStrategy = () => {
|
319
|
-
const shouldCancelRequest = prevRequestInfo &&
|
277
|
+
const shouldCancelRequest = prevRequestInfo && resolvedDedupeStrategy === "cancel";
|
320
278
|
if (!shouldCancelRequest) return;
|
321
|
-
const message = getAbortErrorMessage(
|
279
|
+
const message = getAbortErrorMessage();
|
322
280
|
const reason = new DOMException(message, "AbortError");
|
323
281
|
prevRequestInfo.controller.abort(reason);
|
324
282
|
return Promise.resolve();
|
@@ -326,7 +284,7 @@ const createDedupeStrategy = async (context) => {
|
|
326
284
|
const handleRequestDeferStrategy = async (deferContext) => {
|
327
285
|
const { options: localOptions, request: localRequest } = deferContext;
|
328
286
|
const fetchApi = getFetchImpl(localOptions.customFetchImpl);
|
329
|
-
const shouldUsePromiseFromCache = prevRequestInfo &&
|
287
|
+
const shouldUsePromiseFromCache = prevRequestInfo && resolvedDedupeStrategy === "defer";
|
330
288
|
const streamableContext = {
|
331
289
|
baseConfig,
|
332
290
|
config,
|
@@ -349,10 +307,11 @@ const createDedupeStrategy = async (context) => {
|
|
349
307
|
$RequestInfoCacheOrNull?.delete(dedupeKey);
|
350
308
|
};
|
351
309
|
return {
|
352
|
-
|
310
|
+
getAbortErrorMessage,
|
353
311
|
handleRequestCancelStrategy,
|
354
312
|
handleRequestDeferStrategy,
|
355
|
-
removeDedupeKeyFromCache
|
313
|
+
removeDedupeKeyFromCache,
|
314
|
+
resolvedDedupeStrategy
|
356
315
|
};
|
357
316
|
};
|
358
317
|
|
@@ -534,7 +493,7 @@ const initializePlugins = async (context) => {
|
|
534
493
|
clonedHookRegistries[key].add(pluginHook);
|
535
494
|
}
|
536
495
|
};
|
537
|
-
const hookRegistrationOrder = options.hooksRegistrationOrder ??
|
496
|
+
const hookRegistrationOrder = options.hooksRegistrationOrder ?? extraOptionDefaults().hooksRegistrationOrder;
|
538
497
|
if (hookRegistrationOrder === "mainFirst") addMainHooks();
|
539
498
|
const { currentRouteSchemaKey, mainInitURL } = getCurrentRouteSchemaKeyAndMainInitURL({
|
540
499
|
baseExtraOptions: baseConfig,
|
@@ -545,9 +504,9 @@ const initializePlugins = async (context) => {
|
|
545
504
|
let resolvedInitURL = mainInitURL;
|
546
505
|
let resolvedOptions = options;
|
547
506
|
let resolvedRequestOptions = request;
|
548
|
-
const
|
549
|
-
if (!
|
550
|
-
const initResult = await
|
507
|
+
const executePluginSetupFn = async (pluginSetupFn) => {
|
508
|
+
if (!pluginSetupFn) return;
|
509
|
+
const initResult = await pluginSetupFn({
|
551
510
|
baseConfig,
|
552
511
|
config,
|
553
512
|
initURL,
|
@@ -573,7 +532,7 @@ const initializePlugins = async (context) => {
|
|
573
532
|
options
|
574
533
|
});
|
575
534
|
for (const plugin of resolvedPlugins) {
|
576
|
-
await
|
535
|
+
await executePluginSetupFn(plugin.setup);
|
577
536
|
if (!plugin.hooks) continue;
|
578
537
|
addPluginHooks(plugin.hooks);
|
579
538
|
}
|
@@ -583,7 +542,7 @@ const initializePlugins = async (context) => {
|
|
583
542
|
if (hookRegistry.size === 0) continue;
|
584
543
|
const flattenedHookArray = [...hookRegistry].flat();
|
585
544
|
if (flattenedHookArray.length === 0) continue;
|
586
|
-
const hooksExecutionMode = options.hooksExecutionMode ??
|
545
|
+
const hooksExecutionMode = options.hooksExecutionMode ?? extraOptionDefaults().hooksExecutionMode;
|
587
546
|
const composedHook = composeAllHooks(flattenedHookArray, hooksExecutionMode);
|
588
547
|
resolvedHooks[key] = composedHook;
|
589
548
|
}
|
@@ -600,20 +559,20 @@ const initializePlugins = async (context) => {
|
|
600
559
|
//#region src/retry.ts
|
601
560
|
const getLinearDelay = (currentAttemptCount, options) => {
|
602
561
|
const retryDelay = options.retryDelay ?? options.retry?.delay;
|
603
|
-
const resolveRetryDelay = (isFunction(retryDelay) ? retryDelay(currentAttemptCount) : retryDelay) ??
|
562
|
+
const resolveRetryDelay = (isFunction(retryDelay) ? retryDelay(currentAttemptCount) : retryDelay) ?? extraOptionDefaults().retryDelay;
|
604
563
|
return resolveRetryDelay;
|
605
564
|
};
|
606
565
|
const getExponentialDelay = (currentAttemptCount, options) => {
|
607
|
-
const retryDelay = options.retryDelay ?? options.retry?.delay ??
|
566
|
+
const retryDelay = options.retryDelay ?? options.retry?.delay ?? extraOptionDefaults().retryDelay;
|
608
567
|
const resolvedRetryDelay = isFunction(retryDelay) ? retryDelay(currentAttemptCount) : retryDelay;
|
609
|
-
const maxDelay = options.retryMaxDelay ?? options.retry?.maxDelay ??
|
568
|
+
const maxDelay = options.retryMaxDelay ?? options.retry?.maxDelay ?? extraOptionDefaults().retryMaxDelay;
|
610
569
|
const exponentialDelay = resolvedRetryDelay * 2 ** currentAttemptCount;
|
611
570
|
return Math.min(exponentialDelay, maxDelay);
|
612
571
|
};
|
613
572
|
const createRetryStrategy = (ctx) => {
|
614
573
|
const { options } = ctx;
|
615
574
|
const currentAttemptCount = options["~retryAttemptCount"] ?? 1;
|
616
|
-
const retryStrategy = options.retryStrategy ?? options.retry?.strategy ??
|
575
|
+
const retryStrategy = options.retryStrategy ?? options.retry?.strategy ?? extraOptionDefaults().retryStrategy;
|
617
576
|
const getDelay = () => {
|
618
577
|
switch (retryStrategy) {
|
619
578
|
case "exponential": return getExponentialDelay(currentAttemptCount, options);
|
@@ -622,14 +581,13 @@ const createRetryStrategy = (ctx) => {
|
|
622
581
|
}
|
623
582
|
};
|
624
583
|
const shouldAttemptRetry = async () => {
|
625
|
-
const retryCondition = options.retryCondition ?? options.retry?.condition ??
|
626
|
-
const maximumRetryAttempts = options.retryAttempts ?? options.retry?.attempts ??
|
584
|
+
const retryCondition = options.retryCondition ?? options.retry?.condition ?? extraOptionDefaults().retryCondition;
|
585
|
+
const maximumRetryAttempts = options.retryAttempts ?? options.retry?.attempts ?? extraOptionDefaults().retryAttempts;
|
627
586
|
const customRetryCondition = await retryCondition(ctx);
|
628
587
|
const baseShouldRetry = maximumRetryAttempts >= currentAttemptCount && customRetryCondition;
|
629
588
|
if (!baseShouldRetry) return false;
|
630
|
-
const retryMethods = new Set(options.retryMethods ?? options.retry?.methods ??
|
631
|
-
const
|
632
|
-
const includesMethod = retryMethods.has(resolvedMethod);
|
589
|
+
const retryMethods = new Set(options.retryMethods ?? options.retry?.methods ?? extraOptionDefaults().retryMethods);
|
590
|
+
const includesMethod = retryMethods.has(ctx.request.method);
|
633
591
|
const retryStatusCodes = new Set(options.retryStatusCodes ?? options.retry?.statusCodes ?? []);
|
634
592
|
const includesStatusCodes = Boolean(ctx.response?.status) && (retryStatusCodes.size > 0 ? retryStatusCodes.has(ctx.response.status) : true);
|
635
593
|
const shouldRetry = includesMethod && includesStatusCodes;
|
@@ -645,19 +603,28 @@ const createRetryStrategy = (ctx) => {
|
|
645
603
|
//#endregion
|
646
604
|
//#region src/url.ts
|
647
605
|
const slash = "/";
|
648
|
-
const
|
606
|
+
const colon = ":";
|
607
|
+
const openBrace = "{";
|
608
|
+
const closeBrace = "}";
|
649
609
|
const mergeUrlWithParams = (url, params) => {
|
650
610
|
if (!params) return url;
|
651
611
|
let newUrl = url;
|
652
612
|
if (isArray(params)) {
|
653
|
-
const
|
654
|
-
|
613
|
+
const urlParts = newUrl.split(slash);
|
614
|
+
const matchedParamsArray = urlParts.filter((part) => part.startsWith(colon) || part.startsWith(openBrace) && part.endsWith(closeBrace));
|
615
|
+
for (const [index, matchedParam] of matchedParamsArray.entries()) {
|
655
616
|
const realParam = params[index];
|
656
617
|
newUrl = newUrl.replace(matchedParam, realParam);
|
657
618
|
}
|
658
619
|
return newUrl;
|
659
620
|
}
|
660
|
-
for (const [key, value] of Object.entries(params))
|
621
|
+
for (const [key, value] of Object.entries(params)) {
|
622
|
+
const stringValue = String(value);
|
623
|
+
const colonPattern = `${colon}${key}`;
|
624
|
+
const bracePattern = `${openBrace}${key}${closeBrace}`;
|
625
|
+
if (newUrl.includes(colonPattern)) newUrl = newUrl.replace(colonPattern, stringValue);
|
626
|
+
else if (newUrl.includes(bracePattern)) newUrl = newUrl.replace(bracePattern, stringValue);
|
627
|
+
}
|
661
628
|
return newUrl;
|
662
629
|
};
|
663
630
|
const questionMark = "?";
|
@@ -708,10 +675,9 @@ const extractMethodFromURL = (initURL) => {
|
|
708
675
|
if (!method || !routeKeyMethods.includes(method)) return;
|
709
676
|
return method;
|
710
677
|
};
|
711
|
-
const getMethod = (
|
712
|
-
const { initURL, method
|
713
|
-
|
714
|
-
return method?.toUpperCase() ?? extractMethodFromURL(initURL)?.toUpperCase() ?? requestOptionDefaults.method;
|
678
|
+
const getMethod = (ctx) => {
|
679
|
+
const { initURL, method } = ctx;
|
680
|
+
return method?.toUpperCase() ?? extractMethodFromURL(initURL)?.toUpperCase() ?? requestOptionDefaults().method;
|
715
681
|
};
|
716
682
|
const normalizeURL = (initURL) => {
|
717
683
|
const methodFromURL = extractMethodFromURL(initURL);
|
@@ -724,8 +690,8 @@ const getFullAndNormalizedURL = (options) => {
|
|
724
690
|
const normalizedInitURL = normalizeURL(initURL);
|
725
691
|
const urlWithMergedParams = mergeUrlWithParams(normalizedInitURL, params);
|
726
692
|
const urlWithMergedQueryAndParams = mergeUrlWithQuery(urlWithMergedParams, query);
|
727
|
-
const
|
728
|
-
const fullURL =
|
693
|
+
const shouldPrependBaseURL = !urlWithMergedQueryAndParams.startsWith("http") && baseURL;
|
694
|
+
const fullURL = shouldPrependBaseURL ? `${baseURL}${urlWithMergedQueryAndParams}` : urlWithMergedQueryAndParams;
|
729
695
|
return {
|
730
696
|
fullURL,
|
731
697
|
normalizedInitURL
|
@@ -786,7 +752,7 @@ const createFetchClient = (initBaseConfig = {}) => {
|
|
786
752
|
...resolvedRequestOptions,
|
787
753
|
signal: combinedSignal
|
788
754
|
};
|
789
|
-
const {
|
755
|
+
const { getAbortErrorMessage, handleRequestCancelStrategy, handleRequestDeferStrategy, removeDedupeKeyFromCache, resolvedDedupeStrategy } = await createDedupeStrategy({
|
790
756
|
$GlobalRequestInfoCache,
|
791
757
|
$LocalRequestInfoCache,
|
792
758
|
baseConfig,
|
@@ -839,7 +805,7 @@ const createFetchClient = (initBaseConfig = {}) => {
|
|
839
805
|
options,
|
840
806
|
request
|
841
807
|
});
|
842
|
-
const shouldCloneResponse =
|
808
|
+
const shouldCloneResponse = resolvedDedupeStrategy === "defer" || options.cloneResponse;
|
843
809
|
if (!response.ok) {
|
844
810
|
const errorData = await resolveResponseData(shouldCloneResponse ? response.clone() : response, options.responseType, options.responseParser);
|
845
811
|
const validErrorData = await handleValidation(resolvedSchema?.errorData, {
|
@@ -937,7 +903,7 @@ const createFetchClient = (initBaseConfig = {}) => {
|
|
937
903
|
}
|
938
904
|
let message = error?.message;
|
939
905
|
if (error instanceof DOMException && error.name === "AbortError") {
|
940
|
-
message = getAbortErrorMessage(
|
906
|
+
message = getAbortErrorMessage();
|
941
907
|
!shouldThrowOnError && console.error(`${error.name}:`, message);
|
942
908
|
}
|
943
909
|
if (error instanceof DOMException && error.name === "TimeoutError") {
|
@@ -958,8 +924,8 @@ const callApi = createFetchClient();
|
|
958
924
|
//#region src/defineHelpers.ts
|
959
925
|
const defineSchema = (routes, config) => {
|
960
926
|
return {
|
961
|
-
config,
|
962
|
-
routes
|
927
|
+
config: defineSchemaConfig(config),
|
928
|
+
routes: defineSchemaRoutes(routes)
|
963
929
|
};
|
964
930
|
};
|
965
931
|
const defineSchemaConfig = (config) => {
|