@zayne-labs/callapi 1.8.20 → 1.8.21

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,22 +1,22 @@
1
- import { AnyFunction, AnyString, ApplyStrictConfig, ApplyURLBasedConfig, BaseCallApiConfig, BaseCallApiExtraOptions, BaseCallApiSchemaAndConfig, BaseCallApiSchemaRoutes, CallApiConfig, CallApiExtraOptions, CallApiExtraOptionsForHooks, CallApiParameters, CallApiPlugin, CallApiRequestOptions, CallApiRequestOptionsForHooks, CallApiResult, CallApiResultErrorVariant, CallApiResultSuccessVariant, CallApiSchema, CallApiSchemaConfig, DedupeOptions, DefaultDataType, DefaultPluginArray, DefaultThrowOnError, ErrorContext, GetCurrentRouteSchema, GetCurrentRouteSchemaKey, HTTPError, Hooks, HooksOrHooksArray, InferInitURL, InferParamsFromRoute, InferSchemaResult, PluginExtraOptions, PluginHooks, PluginHooksWithMoreOptions, PluginInitContext, PossibleHTTPError, PossibleJavaScriptError, PossibleJavaScriptOrValidationError, PossibleValidationError, Register, RequestContext, RequestStreamContext, ResponseContext, ResponseErrorContext, ResponseStreamContext, ResponseTypeUnion, ResultModeUnion, RetryOptions, SuccessContext, ThrowOnErrorUnion, URLOptions, ValidationError, Writeable } from "./common-Vd9i_nPc.js";
1
+ import { AnyFunction, AnyString, ApplyStrictConfig, ApplyURLBasedConfig, BaseCallApiConfig, BaseCallApiExtraOptions, BaseCallApiSchemaAndConfig, BaseCallApiSchemaRoutes, CallApiConfig, CallApiExtraOptions, CallApiExtraOptionsForHooks, CallApiParameters, CallApiPlugin, CallApiRequestOptions, CallApiRequestOptionsForHooks, CallApiResult, CallApiResultErrorVariant, CallApiResultSuccessVariant, CallApiSchema, CallApiSchemaConfig, DedupeOptions, DefaultDataType, DefaultPluginArray, DefaultThrowOnError, ErrorContext, GetCurrentRouteSchema, GetCurrentRouteSchemaKey, HTTPError, Hooks, HooksOrHooksArray, InferInitURL, InferParamsFromRoute, InferSchemaResult, PluginExtraOptions, PluginHooks, PluginHooksWithMoreOptions, PluginInitContext, PossibleHTTPError, PossibleJavaScriptError, PossibleJavaScriptOrValidationError, PossibleValidationError, Register, RequestContext, RequestStreamContext, ResponseContext, ResponseErrorContext, ResponseStreamContext, ResponseTypeUnion, ResultModeUnion, RetryOptions, SuccessContext, ThrowOnErrorUnion, URLOptions, ValidationError, Writeable } from "./common-C-kIzPcz.js";
2
2
 
3
3
  //#region src/createFetchClient.d.ts
4
4
 
5
5
  declare const createFetchClient: <TBaseData = DefaultDataType, TBaseErrorData = DefaultDataType, TBaseResultMode extends ResultModeUnion = ResultModeUnion, TBaseThrowOnError extends ThrowOnErrorUnion = DefaultThrowOnError, TBaseResponseType extends ResponseTypeUnion = ResponseTypeUnion, const TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig = BaseCallApiSchemaAndConfig, const TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray, TComputedBaseSchemaConfig extends CallApiSchemaConfig = NonNullable<Writeable<TBaseSchemaAndConfig["config"], "deep">>, TComputedBaseSchemaRoutes extends BaseCallApiSchemaRoutes = Writeable<TBaseSchemaAndConfig["routes"], "deep">>(initBaseConfig?: BaseCallApiConfig<TBaseData, TBaseErrorData, TBaseResultMode, TBaseThrowOnError, TBaseResponseType, TBaseSchemaAndConfig, TBasePluginArray>) => <TData = TBaseData, TErrorData = TBaseErrorData, TResultMode extends ResultModeUnion = TBaseResultMode, TThrowOnError extends ThrowOnErrorUnion = TBaseThrowOnError, TResponseType extends ResponseTypeUnion = TBaseResponseType, const TSchemaConfig extends CallApiSchemaConfig = TComputedBaseSchemaConfig, TInitURL extends InferInitURL<TComputedBaseSchemaRoutes, TSchemaConfig> = InferInitURL<TComputedBaseSchemaRoutes, TSchemaConfig>, TCurrentRouteSchemaKey extends GetCurrentRouteSchemaKey<TSchemaConfig, TInitURL> = GetCurrentRouteSchemaKey<TSchemaConfig, TInitURL>, const TSchema extends CallApiSchema = GetCurrentRouteSchema<TComputedBaseSchemaRoutes, TCurrentRouteSchemaKey>, const TPluginArray extends CallApiPlugin[] = TBasePluginArray>(initURL: TInitURL, config?: CallApiConfig<InferSchemaResult<TSchema["data"], TData>, InferSchemaResult<TSchema["errorData"], TErrorData>, TResultMode, TThrowOnError, TResponseType, TComputedBaseSchemaRoutes, TSchema, TComputedBaseSchemaConfig, TSchemaConfig, TInitURL, TCurrentRouteSchemaKey, TBasePluginArray, TPluginArray> | undefined) => CallApiResult<InferSchemaResult<TSchema["data"], TData>, InferSchemaResult<TSchema["errorData"], TErrorData>, TResultMode, TThrowOnError, TResponseType>;
6
- declare const callApi: <TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = ResultModeUnion, TThrowOnError extends ThrowOnErrorUnion = boolean, TResponseType extends ResponseTypeUnion = ResponseTypeUnion, const TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TInitURL extends ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, AnyString | "delete/" | "get/" | "patch/" | "post/" | "put/">> = ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, AnyString | "delete/" | "get/" | "patch/" | "post/" | "put/">>, TCurrentRouteSchemaKey extends GetCurrentRouteSchemaKey<TSchemaConfig, TInitURL> = GetCurrentRouteSchemaKey<TSchemaConfig, TInitURL>, const TSchema extends CallApiSchema = GetCurrentRouteSchema<{
6
+ declare const callApi: <TData = unknown, TErrorData = unknown, TResultMode extends ResultModeUnion = ResultModeUnion, TThrowOnError extends ThrowOnErrorUnion = boolean, TResponseType extends ResponseTypeUnion = ResponseTypeUnion, const TSchemaConfig extends CallApiSchemaConfig = CallApiSchemaConfig, TInitURL extends ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, AnyString | "@delete/" | "@get/" | "@patch/" | "@post/" | "@put/">> = ApplyStrictConfig<TSchemaConfig, ApplyURLBasedConfig<TSchemaConfig, AnyString | "@delete/" | "@get/" | "@patch/" | "@post/" | "@put/">>, TCurrentRouteSchemaKey extends GetCurrentRouteSchemaKey<TSchemaConfig, TInitURL> = GetCurrentRouteSchemaKey<TSchemaConfig, TInitURL>, const TSchema extends CallApiSchema = GetCurrentRouteSchema<{
7
7
  [x: AnyString]: CallApiSchema | undefined;
8
- "delete/"?: CallApiSchema | undefined;
9
- "get/"?: CallApiSchema | undefined;
10
- "patch/"?: CallApiSchema | undefined;
11
- "post/"?: CallApiSchema | undefined;
12
- "put/"?: CallApiSchema | undefined;
8
+ "@delete/"?: CallApiSchema | undefined;
9
+ "@get/"?: CallApiSchema | undefined;
10
+ "@patch/"?: CallApiSchema | undefined;
11
+ "@post/"?: CallApiSchema | undefined;
12
+ "@put/"?: CallApiSchema | undefined;
13
13
  }, TCurrentRouteSchemaKey>, const TPluginArray extends CallApiPlugin[] = DefaultPluginArray>(initURL: TInitURL, config?: CallApiConfig<InferSchemaResult<TSchema["data"], TData>, InferSchemaResult<TSchema["errorData"], TErrorData>, TResultMode, TThrowOnError, TResponseType, {
14
14
  [x: AnyString]: CallApiSchema | undefined;
15
- "delete/"?: CallApiSchema | undefined;
16
- "get/"?: CallApiSchema | undefined;
17
- "patch/"?: CallApiSchema | undefined;
18
- "post/"?: CallApiSchema | undefined;
19
- "put/"?: CallApiSchema | undefined;
15
+ "@delete/"?: CallApiSchema | undefined;
16
+ "@get/"?: CallApiSchema | undefined;
17
+ "@patch/"?: CallApiSchema | undefined;
18
+ "@post/"?: CallApiSchema | undefined;
19
+ "@put/"?: CallApiSchema | undefined;
20
20
  }, TSchema, CallApiSchemaConfig, TSchemaConfig, TInitURL, TCurrentRouteSchemaKey, DefaultPluginArray, TPluginArray> | undefined) => CallApiResult<InferSchemaResult<TSchema["data"], TData>, InferSchemaResult<TSchema["errorData"], TErrorData>, TResultMode, TThrowOnError, TResponseType>;
21
21
  //#endregion
22
22
  //#region src/defineHelpers.d.ts
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { HTTPError, ValidationError, createCombinedSignal, createTimeoutSignal, dedupeDefaults, defineEnum, deterministicHashFn, getBody, getFetchImpl, getHeaders, hookDefaults, isArray, isFunction, isHTTPErrorInstance, isObject, isPlainObject, isReadableStream, isString, isValidationErrorInstance, requestOptionDefaults, responseDefaults, retryDefaults, splitBaseConfig, splitConfig, toQueryString, waitFor } from "./utils-C4H57FX_.js";
1
+ import { HTTPError, ValidationError, createCombinedSignal, createTimeoutSignal, dedupeDefaults, defineEnum, deterministicHashFn, getBody, getFetchImpl, getHeaders, hookDefaults, isArray, isFunction, isHTTPErrorInstance, isObject, isPlainObject, isReadableStream, isString, isValidationErrorInstance, requestOptionDefaults, responseDefaults, retryDefaults, splitBaseConfig, splitConfig, toQueryString, waitFor } from "./utils-DZe23qYR.js";
2
2
 
3
3
  //#region src/result.ts
4
4
  const getResponseType = (response, parser) => ({
@@ -107,13 +107,13 @@ const hookRegistries = {
107
107
  onSuccess: /* @__PURE__ */ new Set(),
108
108
  onValidationError: /* @__PURE__ */ new Set()
109
109
  };
110
- const composeAllHooks = (hooksArray, mergedHooksExecutionMode) => {
110
+ const composeAllHooks = (hooksArray, hooksExecutionMode) => {
111
111
  const mergedHook = async (ctx) => {
112
- if (mergedHooksExecutionMode === "sequential") {
112
+ if (hooksExecutionMode === "sequential") {
113
113
  for (const hook of hooksArray) await hook?.(ctx);
114
114
  return;
115
115
  }
116
- if (mergedHooksExecutionMode === "parallel") await Promise.all(hooksArray.map((uniqueHook) => uniqueHook?.(ctx)));
116
+ if (hooksExecutionMode === "parallel") await Promise.all(hooksArray.map((uniqueHook) => uniqueHook?.(ctx)));
117
117
  };
118
118
  return mergedHook;
119
119
  };
@@ -241,21 +241,69 @@ const toStreamableResponse = async (context) => {
241
241
 
242
242
  //#endregion
243
243
  //#region src/dedupe.ts
244
- const getAbortErrorMessage = (dedupeKey, fullURL) => {
245
- return dedupeKey ? `Duplicate request detected - Aborted previous request with key '${dedupeKey}' as a new request was initiated` : `Duplicate request detected - Aborted previous request to '${fullURL}' as a new request with identical options was initiated`;
244
+ const resolveDedupeKey = (dedupeKey, context) => {
245
+ if (isFunction(dedupeKey)) return dedupeKey(context);
246
+ return dedupeKey ?? null;
246
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
+ */
247
294
  const createDedupeStrategy = async (context) => {
248
295
  const { $GlobalRequestInfoCache: $GlobalRequestInfoCache$1, $LocalRequestInfoCache, baseConfig, config, newFetchController, options: globalOptions, request: globalRequest } = context;
249
296
  const dedupeStrategy = globalOptions.dedupeStrategy ?? dedupeDefaults.dedupeStrategy;
250
- const generateDedupeKey = () => {
297
+ const getDedupeKey = () => {
251
298
  const shouldHaveDedupeKey = dedupeStrategy === "cancel" || dedupeStrategy === "defer";
252
299
  if (!shouldHaveDedupeKey) return null;
300
+ if (globalOptions.dedupeKey) return resolveDedupeKey(globalOptions.dedupeKey, context);
253
301
  return `${globalOptions.fullURL}-${deterministicHashFn({
254
302
  options: globalOptions,
255
303
  request: globalRequest
256
304
  })}`;
257
305
  };
258
- const dedupeKey = globalOptions.dedupeKey ?? generateDedupeKey();
306
+ const dedupeKey = getDedupeKey();
259
307
  const dedupeCacheScope = globalOptions.dedupeCacheScope ?? dedupeDefaults.dedupeCacheScope;
260
308
  const dedupeCacheScopeKey = globalOptions.dedupeCacheScopeKey ?? dedupeDefaults.dedupeCacheScopeKey;
261
309
  if (dedupeCacheScope === "global" && !$GlobalRequestInfoCache$1.has(dedupeCacheScopeKey)) $GlobalRequestInfoCache$1.set(dedupeCacheScopeKey, /* @__PURE__ */ new Map());
@@ -270,7 +318,7 @@ const createDedupeStrategy = async (context) => {
270
318
  const handleRequestCancelStrategy = () => {
271
319
  const shouldCancelRequest = prevRequestInfo && dedupeStrategy === "cancel";
272
320
  if (!shouldCancelRequest) return;
273
- const message = getAbortErrorMessage(globalOptions.dedupeKey, globalOptions.fullURL);
321
+ const message = getAbortErrorMessage(globalOptions.dedupeKey, context);
274
322
  const reason = new DOMException(message, "AbortError");
275
323
  prevRequestInfo.controller.abort(reason);
276
324
  return Promise.resolve();
@@ -486,8 +534,8 @@ const initializePlugins = async (context) => {
486
534
  clonedHookRegistries[key].add(pluginHook);
487
535
  }
488
536
  };
489
- const mergedHooksExecutionOrder = options.mergedHooksExecutionOrder ?? hookDefaults.mergedHooksExecutionOrder;
490
- if (mergedHooksExecutionOrder === "mainHooksBeforePlugins") addMainHooks();
537
+ const hookRegistrationOrder = options.hooksRegistrationOrder ?? hookDefaults.hooksRegistrationOrder;
538
+ if (hookRegistrationOrder === "mainFirst") addMainHooks();
491
539
  const { currentRouteSchemaKey, mainInitURL } = getCurrentRouteSchemaKeyAndMainInitURL({
492
540
  baseExtraOptions: baseConfig,
493
541
  extraOptions: config,
@@ -529,14 +577,14 @@ const initializePlugins = async (context) => {
529
577
  if (!plugin.hooks) continue;
530
578
  addPluginHooks(plugin.hooks);
531
579
  }
532
- if (mergedHooksExecutionOrder === "mainHooksAfterPlugins") addMainHooks();
580
+ if (hookRegistrationOrder === "pluginsFirst") addMainHooks();
533
581
  const resolvedHooks = {};
534
582
  for (const [key, hookRegistry] of Object.entries(clonedHookRegistries)) {
535
583
  if (hookRegistry.size === 0) continue;
536
584
  const flattenedHookArray = [...hookRegistry].flat();
537
585
  if (flattenedHookArray.length === 0) continue;
538
- const mergedHooksExecutionMode = options.mergedHooksExecutionMode ?? hookDefaults.mergedHooksExecutionMode;
539
- const composedHook = composeAllHooks(flattenedHookArray, mergedHooksExecutionMode);
586
+ const hooksExecutionMode = options.hooksExecutionMode ?? hookDefaults.hooksExecutionMode;
587
+ const composedHook = composeAllHooks(flattenedHookArray, hooksExecutionMode);
540
588
  resolvedHooks[key] = composedHook;
541
589
  }
542
590
  return {
@@ -623,14 +671,39 @@ const mergeUrlWithQuery = (url, query) => {
623
671
  return `${url}${questionMark}${queryString}`;
624
672
  };
625
673
  /**
626
- * @description
627
- * Extracts the method from the URL if it is a schema modifier.
674
+ * @description Extracts the HTTP method from method-prefixed route patterns.
675
+ *
676
+ * Analyzes URLs that start with method modifiers (e.g., "@get/", "@post/") and extracts
677
+ * the HTTP method for use in API requests. This enables method specification directly
678
+ * in route definitions.
679
+ *
680
+ * @param initURL - The URL string to analyze for method modifiers
681
+ * @returns The extracted HTTP method (lowercase) if found, otherwise undefined
682
+ *
683
+ * @example
684
+ * ```typescript
685
+ * // Method extraction from prefixed routes
686
+ * extractMethodFromURL("@get/users"); // Returns: "get"
687
+ * extractMethodFromURL("@post/users"); // Returns: "post"
688
+ * extractMethodFromURL("@put/users/:id"); // Returns: "put"
689
+ * extractMethodFromURL("@delete/users/:id"); // Returns: "delete"
690
+ * extractMethodFromURL("@patch/users/:id"); // Returns: "patch"
691
+ *
692
+ * // No method modifier
693
+ * extractMethodFromURL("/users"); // Returns: undefined
694
+ * extractMethodFromURL("users"); // Returns: undefined
695
+ *
696
+ * // Invalid or unsupported methods
697
+ * extractMethodFromURL("@invalid/users"); // Returns: undefined
698
+ * extractMethodFromURL("@/users"); // Returns: undefined
628
699
  *
629
- * @param initURL - The URL to extract the method from.
630
- * @returns The method if it is a schema modifier, otherwise undefined.
700
+ * // Edge cases
701
+ * extractMethodFromURL(undefined); // Returns: undefined
702
+ * extractMethodFromURL(""); // Returns: undefined
703
+ * ```
631
704
  */
632
705
  const extractMethodFromURL = (initURL) => {
633
- if (!initURL?.startsWith("@")) return;
706
+ if (!initURL || initURL.startsWith("@")) return;
634
707
  const method = initURL.split("@")[1]?.split("/")[0];
635
708
  if (!method || !routeKeyMethods.includes(method)) return;
636
709
  return method;
@@ -646,7 +719,7 @@ const normalizeURL = (initURL) => {
646
719
  const normalizedURL = initURL.replace(`@${methodFromURL}/`, "/");
647
720
  return normalizedURL;
648
721
  };
649
- const getFullURL = (options) => {
722
+ const getFullAndNormalizedURL = (options) => {
650
723
  const { baseURL, initURL, params, query } = options;
651
724
  const normalizedInitURL = normalizeURL(initURL);
652
725
  const urlWithMergedParams = mergeUrlWithParams(normalizedInitURL, params);
@@ -693,7 +766,7 @@ const createFetchClient = (initBaseConfig = {}) => {
693
766
  options: mergedExtraOptions,
694
767
  request: mergedRequestOptions
695
768
  });
696
- const { fullURL, normalizedInitURL } = getFullURL({
769
+ const { fullURL, normalizedInitURL } = getFullAndNormalizedURL({
697
770
  baseURL: resolvedOptions.baseURL,
698
771
  initURL: resolvedInitURL,
699
772
  params: resolvedOptions.params,
@@ -864,7 +937,7 @@ const createFetchClient = (initBaseConfig = {}) => {
864
937
  }
865
938
  let message = error?.message;
866
939
  if (error instanceof DOMException && error.name === "AbortError") {
867
- message = getAbortErrorMessage(options.dedupeKey, options.fullURL);
940
+ message = getAbortErrorMessage(options.dedupeKey, errorContext);
868
941
  !shouldThrowOnError && console.error(`${error.name}:`, message);
869
942
  }
870
943
  if (error instanceof DOMException && error.name === "TimeoutError") {