@zayne-labs/callapi 1.6.17 → 1.6.18

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.
@@ -102,7 +102,7 @@ interface CallApiSchemas {
102
102
  */
103
103
  query?: StandardSchemaV1<Query>;
104
104
  }
105
- interface CallApiValidators<TData = never, TErrorData = never> {
105
+ interface CallApiValidators<TData = unknown, TErrorData = unknown> {
106
106
  /**
107
107
  * Custom function to validate the response data.
108
108
  */
@@ -133,7 +133,7 @@ interface UrlOptions<TSchemas extends CallApiSchemas> {
133
133
  query?: InferSchemaResult<TSchemas["query"], Query>;
134
134
  }
135
135
 
136
- type RetryCondition<TErrorData> = (context: ErrorContext<TErrorData>) => boolean | Promise<boolean>;
136
+ type RetryCondition<TErrorData> = (context: ErrorContext<TErrorData>) => Awaitable<boolean>;
137
137
  interface RetryOptions<TErrorData> {
138
138
  /**
139
139
  * Keeps track of the number of times the request has already been retried
@@ -163,12 +163,12 @@ interface RetryOptions<TErrorData> {
163
163
  * HTTP methods that are allowed to retry
164
164
  * @default ["GET", "POST"]
165
165
  */
166
- retryMethods?: Method[];
166
+ retryMethods?: Method[] | ((context: ErrorContext<TErrorData>) => Method[]);
167
167
  /**
168
168
  * HTTP status codes that trigger a retry
169
169
  * @default [409, 425, 429, 500, 502, 503, 504]
170
170
  */
171
- retryStatusCodes?: Array<409 | 425 | 429 | 500 | 502 | 503 | 504 | AnyNumber>;
171
+ retryStatusCodes?: Array<409 | 425 | 429 | 500 | 502 | 503 | 504 | AnyNumber> | ((context: ErrorContext<TErrorData>) => Array<409 | 425 | 429 | 500 | 502 | 503 | 504 | AnyNumber>);
172
172
  /**
173
173
  * Strategy to use when retrying
174
174
  * @default "linear"
@@ -203,6 +203,7 @@ type InferPluginOptions<TPluginArray extends CallApiPlugin[]> = UnionToIntersect
203
203
  type PluginInitContext<TMoreOptions = DefaultMoreOptions> = WithMoreOptions<TMoreOptions> & {
204
204
  baseConfig: BaseCallApiExtraOptions & CallApiRequestOptions;
205
205
  config: CallApiExtraOptions & CallApiRequestOptions;
206
+ defaultOptions: CallApiExtraOptions;
206
207
  initURL: InitURL | undefined;
207
208
  options: CombinedCallApiExtraOptions;
208
209
  request: CallApiRequestOptionsForHooks;
@@ -244,8 +245,6 @@ declare const definePlugin: <TPlugin extends CallApiPlugin | AnyFunction<CallApi
244
245
  type Plugins<TPluginArray extends CallApiPlugin[]> = TPluginArray;
245
246
 
246
247
  declare const fetchSpecificKeys: ("body" | "cache" | "credentials" | "headers" | "integrity" | "keepalive" | "method" | "mode" | "priority" | "redirect" | "referrer" | "referrerPolicy" | "signal" | "window")[];
247
- declare const defaultRetryMethods: ("GET" | "POST")[];
248
- declare const defaultRetryStatusCodes: Required<BaseCallApiExtraOptions>["retryStatusCodes"];
249
248
 
250
249
  /**
251
250
  * @description Makes a type required if TSchema type is undefined or if the output type of TSchema contains undefined, otherwise keeps it as is
@@ -466,6 +465,7 @@ declare const optionsEnumToOmitFromBase: ("dedupeKey" | "extend")[];
466
465
  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]>;
467
466
  type CombinedCallApiExtraOptions = BaseCallApiExtraOptions & CallApiExtraOptions;
468
467
  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: {
468
+ defaultOptions: CallApiExtraOptions;
469
469
  initURL: string;
470
470
  options: CallApiExtraOptions;
471
471
  request: CallApiRequestOptions;
@@ -576,4 +576,4 @@ declare class HTTPError<TErrorResponse = Record<string, unknown>> extends Error
576
576
  constructor(errorDetails: ErrorDetails<TErrorResponse>, errorOptions?: ErrorOptions);
577
577
  }
578
578
 
579
- export { defaultRetryMethods as A, type BaseCallApiConfig as B, type CallApiPlugin as C, type DefaultPluginArray as D, type ErrorContext as E, defaultRetryStatusCodes as F, HTTPError as H, type InferSchemaResult as I, type PluginInitContext as P, type ResultModeUnion as R, type SuccessContext as S, type ResponseTypeUnion as a, type CallApiSchemas as b, type CallApiConfig as c, type CallApiResult as d, type DefaultDataType as e, type DefaultThrowOnError as f, type DefaultMoreOptions as g, type CallApiParameters as h, definePlugin as i, type BaseCallApiExtraOptions as j, type CallApiExtraOptions as k, type PossibleJavaScriptError as l, type PossibleHTTPError as m, type CallApiRequestOptions as n, type CallApiRequestOptionsForHooks as o, type CallApiResultErrorVariant as p, type CallApiResultSuccessVariant as q, type CombinedCallApiExtraOptions as r, type Interceptors as s, type InterceptorsOrInterceptorArray as t, type PossibleJavascriptErrorNames as u, type Register as v, type RequestContext as w, type RequestErrorContext as x, type ResponseContext as y, type ResponseErrorContext as z };
579
+ export { type ResponseErrorContext as A, type BaseCallApiConfig as B, type CallApiPlugin as C, type DefaultPluginArray as D, type ErrorContext as E, HTTPError as H, type InferSchemaResult as I, type PluginInitContext as P, type ResultModeUnion as R, type SuccessContext as S, type ResponseTypeUnion as a, type CallApiSchemas as b, type CallApiConfig as c, type CallApiResult as d, type DefaultDataType as e, type DefaultThrowOnError as f, type DefaultMoreOptions as g, type CallApiParameters as h, definePlugin as i, type RetryOptions as j, type BaseCallApiExtraOptions as k, type CallApiExtraOptions as l, type PossibleJavaScriptError as m, type PossibleHTTPError as n, type CallApiRequestOptions as o, type CallApiRequestOptionsForHooks as p, type CallApiResultErrorVariant as q, type CallApiResultSuccessVariant as r, type CombinedCallApiExtraOptions as s, type Interceptors as t, type InterceptorsOrInterceptorArray as u, type PossibleJavascriptErrorNames as v, type Register as w, type RequestContext as x, type RequestErrorContext as y, type ResponseContext as z };
@@ -205,6 +205,22 @@ var retryStatusCodesLookup = defineEnum({
205
205
  });
206
206
  var defaultRetryMethods = ["GET", "POST"];
207
207
  var defaultRetryStatusCodes = Object.keys(retryStatusCodesLookup).map(Number);
208
+ var defaultExtraOptions = {
209
+ baseURL: "",
210
+ bodySerializer: JSON.stringify,
211
+ dedupeStrategy: "cancel",
212
+ defaultErrorMessage: "Failed to fetch data from server!",
213
+ mergedHooksExecutionMode: "parallel",
214
+ mergedHooksExecutionOrder: "mainHooksAfterPlugins",
215
+ responseType: "json",
216
+ resultMode: "all",
217
+ retryAttempts: 0,
218
+ retryDelay: 1e3,
219
+ retryMaxDelay: 1e4,
220
+ retryMethods: defaultRetryMethods,
221
+ retryStatusCodes: defaultRetryStatusCodes,
222
+ retryStrategy: "linear"
223
+ };
208
224
 
209
225
  // src/utils/common.ts
210
226
  var omitKeys = (initialObject, keysToOmit) => {
@@ -375,7 +391,7 @@ var getPluginArray = (plugins) => {
375
391
  return plugins;
376
392
  };
377
393
  var initializePlugins = async (context) => {
378
- const { baseConfig, config, initURL, options, request } = context;
394
+ const { baseConfig, config, defaultOptions, initURL, options, request } = context;
379
395
  const hookRegistries = structuredClone(hooksEnum);
380
396
  const addMainHooks = () => {
381
397
  for (const key of Object.keys(hooksEnum)) {
@@ -404,6 +420,7 @@ var initializePlugins = async (context) => {
404
420
  const initResult = await pluginInit({
405
421
  baseConfig,
406
422
  config,
423
+ defaultOptions,
407
424
  initURL,
408
425
  options,
409
426
  request
@@ -490,28 +507,30 @@ var getExponentialDelay = (currentAttemptCount, options) => {
490
507
  const exponentialDelay = (options.retryDelay ?? 1e3) * 2 ** currentAttemptCount;
491
508
  return Math.min(exponentialDelay, maxDelay);
492
509
  };
493
- var createRetryStrategy = (options, ctx) => {
494
- const currentRetryCount = options["~retryCount"] ?? 0;
510
+ var createRetryStrategy = (ctx) => {
511
+ const currentRetryCount = ctx.options["~retryCount"] ?? 0;
495
512
  const getDelay = () => {
496
- if (options.retryStrategy === "exponential") {
497
- return getExponentialDelay(currentRetryCount, options);
513
+ if (ctx.options.retryStrategy === "exponential") {
514
+ return getExponentialDelay(currentRetryCount, ctx.options);
498
515
  }
499
- return getLinearDelay(options);
516
+ return getLinearDelay(ctx.options);
500
517
  };
501
518
  const shouldAttemptRetry = async () => {
502
- const customRetryCondition = await options.retryCondition?.(ctx) ?? true;
503
- const maxRetryAttempts = options.retryAttempts ?? 0;
519
+ const customRetryCondition = await ctx.options.retryCondition?.(ctx) ?? true;
520
+ const maxRetryAttempts = ctx.options.retryAttempts ?? 0;
504
521
  const baseRetryCondition = maxRetryAttempts > currentRetryCount && customRetryCondition;
505
522
  if (ctx.error.name !== "HTTPError") {
506
523
  return baseRetryCondition;
507
524
  }
525
+ const resolvedRetryMethods = isFunction(ctx.options.retryMethods) ? ctx.options.retryMethods(ctx) : ctx.options.retryMethods;
526
+ const resolvedRetryStatusCodes = isFunction(ctx.options.retryStatusCodes) ? ctx.options.retryStatusCodes(ctx) : ctx.options.retryStatusCodes;
508
527
  const includesMethod = (
509
528
  // eslint-disable-next-line no-implicit-coercion -- Boolean doesn't narrow
510
- !!ctx.request.method && options.retryMethods?.includes(ctx.request.method)
529
+ !!ctx.request.method && resolvedRetryMethods?.includes(ctx.request.method)
511
530
  );
512
531
  const includesCodes = (
513
532
  // eslint-disable-next-line no-implicit-coercion -- Boolean doesn't narrow
514
- !!ctx.response?.status && options.retryStatusCodes?.includes(ctx.response.status)
533
+ !!ctx.response?.status && resolvedRetryStatusCodes?.includes(ctx.response.status)
515
534
  );
516
535
  return includesCodes && includesMethod && baseRetryCondition;
517
536
  };
@@ -595,7 +614,12 @@ var createFetchClient = (baseConfig = {}) => {
595
614
  const callApi2 = async (...parameters) => {
596
615
  const [initURL, config = {}] = parameters;
597
616
  const [fetchOptions, extraOptions] = splitConfig(config);
598
- const resolvedBaseConfig = isFunction(baseConfig) ? baseConfig({ initURL: initURL.toString(), options: extraOptions, request: fetchOptions }) : baseConfig;
617
+ const resolvedBaseConfig = isFunction(baseConfig) ? baseConfig({
618
+ defaultOptions: defaultExtraOptions,
619
+ initURL: initURL.toString(),
620
+ options: extraOptions,
621
+ request: fetchOptions
622
+ }) : baseConfig;
599
623
  const [baseFetchOptions, baseExtraOptions] = splitBaseConfig(resolvedBaseConfig);
600
624
  const initCombinedHooks = {};
601
625
  for (const key of Object.keys(hooksEnum)) {
@@ -605,35 +629,22 @@ var createFetchClient = (baseConfig = {}) => {
605
629
  );
606
630
  initCombinedHooks[key] = combinedHook;
607
631
  }
608
- const defaultExtraOptions = {
609
- baseURL: "",
610
- bodySerializer: JSON.stringify,
611
- dedupeStrategy: "cancel",
612
- defaultErrorMessage: "Failed to fetch data from server!",
613
- mergedHooksExecutionMode: "parallel",
614
- mergedHooksExecutionOrder: "mainHooksAfterPlugins",
615
- responseType: "json",
616
- resultMode: "all",
617
- retryAttempts: 0,
618
- retryDelay: 1e3,
619
- retryMaxDelay: 1e4,
620
- retryMethods: defaultRetryMethods,
621
- retryStatusCodes: defaultRetryStatusCodes,
622
- retryStrategy: "linear",
632
+ const mergedExtraOptions = {
633
+ ...defaultExtraOptions,
623
634
  ...baseExtraOptions,
624
- ...extraOptions,
625
- ...initCombinedHooks
635
+ ...extraOptions
626
636
  };
627
- const defaultRequestOptions = {
637
+ const mergedRequestOptions = {
628
638
  ...baseFetchOptions,
629
639
  ...fetchOptions
630
640
  };
631
641
  const { resolvedHooks, resolvedOptions, resolvedRequestOptions, url } = await initializePlugins({
632
642
  baseConfig: resolvedBaseConfig,
633
643
  config,
644
+ defaultOptions: defaultExtraOptions,
634
645
  initURL,
635
- options: defaultExtraOptions,
636
- request: defaultRequestOptions
646
+ options: mergedExtraOptions,
647
+ request: mergedRequestOptions
637
648
  });
638
649
  const fullURL = `${resolvedOptions.baseURL}${mergeUrlWithParamsAndQuery(url, resolvedOptions.params, resolvedOptions.query)}`;
639
650
  const options = {
@@ -694,11 +705,7 @@ var createFetchClient = (baseConfig = {}) => {
694
705
  options.responseType,
695
706
  options.responseParser
696
707
  );
697
- const validSuccessData = await handleValidation(
698
- successData,
699
- schemas?.data,
700
- validators?.data
701
- );
708
+ const validSuccessData = await handleValidation(successData, schemas?.data, validators?.data);
702
709
  const successContext = {
703
710
  data: validSuccessData,
704
711
  options,
@@ -727,9 +734,11 @@ var createFetchClient = (baseConfig = {}) => {
727
734
  request,
728
735
  response: apiDetails.response
729
736
  };
730
- const { getDelay, shouldAttemptRetry } = createRetryStrategy(options, errorContext);
731
- const shouldRetry = !combinedSignal.aborted && await shouldAttemptRetry();
732
- if (shouldRetry) {
737
+ const shouldThrowOnError = isFunction(options.throwOnError) ? options.throwOnError(errorContext) : options.throwOnError;
738
+ const handleRetry = async () => {
739
+ const { getDelay, shouldAttemptRetry } = createRetryStrategy(errorContext);
740
+ const shouldRetry = !combinedSignal.aborted && await shouldAttemptRetry();
741
+ if (!shouldRetry) return;
733
742
  await executeHooks(options.onRetry(errorContext));
734
743
  const delay = getDelay();
735
744
  await waitUntil(delay);
@@ -737,9 +746,8 @@ var createFetchClient = (baseConfig = {}) => {
737
746
  ...config,
738
747
  "~retryCount": (options["~retryCount"] ?? 0) + 1
739
748
  };
740
- return await callApi2(initURL, updatedOptions);
741
- }
742
- const shouldThrowOnError = isFunction(options.throwOnError) ? options.throwOnError(errorContext) : options.throwOnError;
749
+ return callApi2(initURL, updatedOptions);
750
+ };
743
751
  const handleThrowOnError = () => {
744
752
  if (!shouldThrowOnError) return;
745
753
  throw apiDetails.error;
@@ -750,18 +758,21 @@ var createFetchClient = (baseConfig = {}) => {
750
758
  options.onError(errorContext),
751
759
  options.onResponse({ ...errorContext, data: null })
752
760
  );
761
+ await handleRetry();
753
762
  handleThrowOnError();
754
763
  return getErrorResult();
755
764
  }
756
765
  if (error instanceof DOMException && error.name === "AbortError") {
757
766
  const { message, name } = error;
758
767
  console.error(`${name}:`, message);
768
+ await handleRetry();
759
769
  handleThrowOnError();
760
770
  return getErrorResult();
761
771
  }
762
772
  if (error instanceof DOMException && error.name === "TimeoutError") {
763
773
  const message = `Request timed out after ${options.timeout}ms`;
764
774
  console.error(`${error.name}:`, message);
775
+ await handleRetry();
765
776
  handleThrowOnError();
766
777
  return getErrorResult({ message });
767
778
  }
@@ -771,6 +782,7 @@ var createFetchClient = (baseConfig = {}) => {
771
782
  // == Also call the onError interceptor
772
783
  options.onError(errorContext)
773
784
  );
785
+ await handleRetry();
774
786
  handleThrowOnError();
775
787
  return getErrorResult();
776
788
  } finally {