@iblai/web-utils 1.6.2 → 1.6.3

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.
@@ -17,20 +17,28 @@ export declare const getHeaders: (service: SERVICES) => Promise<{
17
17
  } | undefined>;
18
18
  /**
19
19
  * Build a generic RTK Query endpoint from a service function.
20
+ * Includes built-in retry with exponential backoff (since RTK Query
21
+ * skips baseQuery retry for queryFn endpoints).
20
22
  */
21
- export declare const buildEndpointFromService: <Args extends Record<string, unknown>, Result>(service: SERVICES, serviceFn: (args: Args) => Promise<Result>) => {
23
+ export declare const buildEndpointFromService: <Args extends Record<string, unknown>, Result>(service: SERVICES, serviceFn: (args: Args) => Promise<Result>, options?: {
24
+ maxRetries?: number;
25
+ }) => {
22
26
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
23
27
  };
24
28
  /**
25
29
  * Shortcut for building an endpoint using the DM service.
26
30
  */
27
- export declare const buildEndpointFromDmService: <Args extends Record<string, unknown>, Result>(serviceFn: (args: Args) => Promise<Result>) => {
31
+ export declare const buildEndpointFromDmService: <Args extends Record<string, unknown>, Result>(serviceFn: (args: Args) => Promise<Result>, options?: {
32
+ maxRetries?: number;
33
+ }) => {
28
34
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
29
35
  };
30
36
  /**
31
37
  * Shortcut for building an endpoint using the AXD service.
32
38
  */
33
- export declare const buildEndpointFromAxdService: <Args extends Record<string, unknown>, Result>(serviceFn: (args: Args) => Promise<Result>) => {
39
+ export declare const buildEndpointFromAxdService: <Args extends Record<string, unknown>, Result>(serviceFn: (args: Args) => Promise<Result>, options?: {
40
+ maxRetries?: number;
41
+ }) => {
34
42
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
35
43
  };
36
44
  export interface CustomQueryArgs extends Omit<FetchArgs, 'url'> {
@@ -50,20 +58,27 @@ export type ExtendedFetchBaseQueryError = FetchBaseQueryError & {
50
58
  };
51
59
  export declare const iblFetchBaseQuery: BaseQueryFn<CustomQueryArgs, unknown, ExtendedFetchBaseQueryError, Record<string, unknown>, FetchBaseQueryMeta>;
52
60
  /**
53
- * Build a generic RTK Query endpoint from a service function.
61
+ * Build a generic RTK Query endpoint from a service function (legacy variant).
62
+ * Includes built-in retry with exponential backoff.
54
63
  */
55
- export declare const buildEndpointFromServiceLegacy: <Args extends any[], Result>(service: SERVICES, serviceFn: (...args: Args) => Promise<Result>) => {
64
+ export declare const buildEndpointFromServiceLegacy: <Args extends any[], Result>(service: SERVICES, serviceFn: (...args: Args) => Promise<Result>, options?: {
65
+ maxRetries?: number;
66
+ }) => {
56
67
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
57
68
  };
58
69
  /**
59
70
  * Shortcut for building an endpoint using the DM service.
60
71
  */
61
- export declare const buildEndpointFromDmServiceLegacy: <Args extends any[], Result>(serviceFn: (...args: Args) => Promise<Result>) => {
72
+ export declare const buildEndpointFromDmServiceLegacy: <Args extends any[], Result>(serviceFn: (...args: Args) => Promise<Result>, options?: {
73
+ maxRetries?: number;
74
+ }) => {
62
75
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
63
76
  };
64
77
  /**
65
78
  * Shortcut for building an endpoint using the AXD service.
66
79
  */
67
- export declare const buildEndpointFromAxdServiceLegacy: <Args extends any[], Result>(serviceFn: (...args: Args) => Promise<Result>) => {
80
+ export declare const buildEndpointFromAxdServiceLegacy: <Args extends any[], Result>(serviceFn: (...args: Args) => Promise<Result>, options?: {
81
+ maxRetries?: number;
82
+ }) => {
68
83
  queryFn: BaseQueryFn<Args, Result, FetchBaseQueryError | CustomError>;
69
84
  };
package/dist/index.esm.js CHANGED
@@ -3073,6 +3073,7 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
3073
3073
  if (isProtectedRoute && storageService) {
3074
3074
  const dmTokenInLocalStorage = await storageService.getItem(LOCAL_STORAGE_KEYS.DM_TOKEN_KEY);
3075
3075
  console.log("[auth-redirect] local storage keys ", LOCAL_STORAGE_KEYS);
3076
+ console.log("[auth-redirect] local storage ", JSON.stringify(localStorage));
3076
3077
  const dmTokenExpired = await isDmTokenExpired(storageService);
3077
3078
  if (!dmTokenInLocalStorage || dmTokenExpired) {
3078
3079
  if (!!dmTokenInLocalStorage) {
@@ -13724,44 +13725,71 @@ const getHeaders = async (service) => {
13724
13725
  }
13725
13726
  return token ? { Authorization: `${prefix} ${token}` } : undefined;
13726
13727
  };
13728
+ /** Status codes that should not be retried for queryFn endpoints. */
13729
+ const QUERYFN_NON_RETRYABLE_STATUSES = [401, 403, 500];
13730
+ /** Default max retries for queryFn endpoints. */
13731
+ const QUERYFN_MAX_RETRIES = 5;
13732
+ /** Base delay in ms for exponential backoff. */
13733
+ const QUERYFN_BASE_DELAY_MS = 1000;
13727
13734
  /**
13728
13735
  * Build a generic RTK Query endpoint from a service function.
13736
+ * Includes built-in retry with exponential backoff (since RTK Query
13737
+ * skips baseQuery retry for queryFn endpoints).
13729
13738
  */
13730
- const buildEndpointFromService = (service, serviceFn) => {
13739
+ const buildEndpointFromService = (service, serviceFn, options) => {
13740
+ var _a;
13741
+ const maxRetries = (_a = void 0 ) !== null && _a !== void 0 ? _a : QUERYFN_MAX_RETRIES;
13731
13742
  return {
13732
13743
  async queryFn(args) {
13733
- try {
13734
- OpenAPI.BASE = getServiceUrl(service);
13735
- OpenAPI.HEADERS = await getHeaders(service);
13736
- OpenAPI.CREDENTIALS = service === SERVICES.LEGACY_LMS ? 'include' : 'omit';
13737
- // API request initiated
13738
- const data = await serviceFn(args);
13739
- // API response received
13740
- return { data };
13741
- }
13742
- catch (err) {
13743
- console.error('[data-layer] API error:', JSON.stringify(err, Object.getOwnPropertyNames(err)));
13744
- if (Object.prototype.hasOwnProperty.call(Config.httpErrorHandlers, err === null || err === void 0 ? void 0 : err.status)) {
13745
- Config.httpErrorHandlers[err === null || err === void 0 ? void 0 : err.status]({ ...((err === null || err === void 0 ? void 0 : err.data) || {}) });
13744
+ let lastError;
13745
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
13746
+ try {
13747
+ OpenAPI.BASE = getServiceUrl(service);
13748
+ OpenAPI.HEADERS = await getHeaders(service);
13749
+ OpenAPI.CREDENTIALS = service === SERVICES.LEGACY_LMS ? 'include' : 'omit';
13750
+ const data = await serviceFn(args);
13751
+ return { data };
13746
13752
  }
13747
- return {
13748
- error: {
13749
- status: (err === null || err === void 0 ? void 0 : err.status) || 500,
13750
- data: (err === null || err === void 0 ? void 0 : err.body) || (err === null || err === void 0 ? void 0 : err.data) || (err === null || err === void 0 ? void 0 : err.message) || 'Unknown error',
13751
- },
13752
- };
13753
+ catch (err) {
13754
+ lastError = err;
13755
+ const status = err === null || err === void 0 ? void 0 : err.status;
13756
+ // Don't retry auth errors
13757
+ if (QUERYFN_NON_RETRYABLE_STATUSES.includes(status)) {
13758
+ break;
13759
+ }
13760
+ // If we have retries left, wait with exponential backoff
13761
+ if (attempt < maxRetries) {
13762
+ const delay = QUERYFN_BASE_DELAY_MS * Math.pow(2, attempt);
13763
+ console.warn(`[data-layer] Retry ${attempt + 1}/${maxRetries}:`, `service=${service}, fn=${serviceFn.name || 'anonymous'}`);
13764
+ await new Promise((resolve) => setTimeout(resolve, delay));
13765
+ continue;
13766
+ }
13767
+ }
13768
+ }
13769
+ // All retries exhausted or non-retryable error
13770
+ console.error('[data-layer] API error:', `service=${service}, fn=${serviceFn.name || 'anonymous'}`, JSON.stringify(lastError, Object.getOwnPropertyNames(lastError)));
13771
+ if (Object.prototype.hasOwnProperty.call(Config.httpErrorHandlers, lastError === null || lastError === void 0 ? void 0 : lastError.status)) {
13772
+ Config.httpErrorHandlers[lastError === null || lastError === void 0 ? void 0 : lastError.status]({
13773
+ ...((lastError === null || lastError === void 0 ? void 0 : lastError.data) || {}),
13774
+ });
13753
13775
  }
13776
+ return {
13777
+ error: {
13778
+ status: (lastError === null || lastError === void 0 ? void 0 : lastError.status) || 500,
13779
+ data: (lastError === null || lastError === void 0 ? void 0 : lastError.body) || (lastError === null || lastError === void 0 ? void 0 : lastError.data) || (lastError === null || lastError === void 0 ? void 0 : lastError.message) || 'Unknown error',
13780
+ },
13781
+ };
13754
13782
  },
13755
13783
  };
13756
13784
  };
13757
13785
  /**
13758
13786
  * Shortcut for building an endpoint using the DM service.
13759
13787
  */
13760
- const buildEndpointFromDmService = (serviceFn) => buildEndpointFromService(SERVICES.DM, serviceFn);
13788
+ const buildEndpointFromDmService = (serviceFn, options) => buildEndpointFromService(SERVICES.DM, serviceFn);
13761
13789
  /**
13762
13790
  * Shortcut for building an endpoint using the AXD service.
13763
13791
  */
13764
- const buildEndpointFromAxdService = (serviceFn) => buildEndpointFromService(SERVICES.AXD, serviceFn);
13792
+ const buildEndpointFromAxdService = (serviceFn, options) => buildEndpointFromService(SERVICES.AXD, serviceFn);
13765
13793
  const isErrorObject = (data) => {
13766
13794
  return (typeof data === 'object' &&
13767
13795
  data !== null &&
@@ -13893,40 +13921,57 @@ const iblFetchBaseQuery = async (args, api, extraOptions) => {
13893
13921
  }
13894
13922
  };
13895
13923
  /**
13896
- * Build a generic RTK Query endpoint from a service function.
13924
+ * Build a generic RTK Query endpoint from a service function (legacy variant).
13925
+ * Includes built-in retry with exponential backoff.
13897
13926
  */
13898
- const buildEndpointFromServiceLegacy = (service, serviceFn) => {
13927
+ const buildEndpointFromServiceLegacy = (service, serviceFn, options) => {
13928
+ var _a;
13929
+ const maxRetries = (_a = void 0 ) !== null && _a !== void 0 ? _a : QUERYFN_MAX_RETRIES;
13899
13930
  return {
13900
13931
  async queryFn(args) {
13901
- try {
13902
- OpenAPI.BASE = getServiceUrl(service);
13903
- OpenAPI.HEADERS = await getHeaders(service);
13904
- OpenAPI.CREDENTIALS = service === SERVICES.LEGACY_LMS ? 'include' : 'omit';
13905
- const data = await serviceFn(...args);
13906
- return { data };
13907
- }
13908
- catch (err) {
13909
- if (Object.prototype.hasOwnProperty.call(Config.httpErrorHandlers, err === null || err === void 0 ? void 0 : err.status)) {
13910
- Config.httpErrorHandlers[err === null || err === void 0 ? void 0 : err.status]({
13911
- status: err === null || err === void 0 ? void 0 : err.status,
13912
- body: err === null || err === void 0 ? void 0 : err.body,
13913
- message: err === null || err === void 0 ? void 0 : err.message,
13914
- });
13932
+ let lastError;
13933
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
13934
+ try {
13935
+ OpenAPI.BASE = getServiceUrl(service);
13936
+ OpenAPI.HEADERS = await getHeaders(service);
13937
+ OpenAPI.CREDENTIALS = service === SERVICES.LEGACY_LMS ? 'include' : 'omit';
13938
+ const data = await serviceFn(...args);
13939
+ return { data };
13940
+ }
13941
+ catch (err) {
13942
+ lastError = err;
13943
+ const status = err === null || err === void 0 ? void 0 : err.status;
13944
+ if (QUERYFN_NON_RETRYABLE_STATUSES.includes(status)) {
13945
+ break;
13946
+ }
13947
+ if (attempt < maxRetries) {
13948
+ const delay = QUERYFN_BASE_DELAY_MS * Math.pow(2, attempt);
13949
+ console.warn(`[data-layer] Retry ${attempt + 1}/${maxRetries}:`, `service=${service}, fn=${serviceFn.name || 'anonymous'}`);
13950
+ await new Promise((resolve) => setTimeout(resolve, delay));
13951
+ continue;
13952
+ }
13915
13953
  }
13916
- return {
13917
- error: {
13918
- status: (err === null || err === void 0 ? void 0 : err.status) || 500,
13919
- data: (err === null || err === void 0 ? void 0 : err.body) || (err === null || err === void 0 ? void 0 : err.message) || 'Unknown error',
13920
- },
13921
- };
13922
13954
  }
13955
+ if (Object.prototype.hasOwnProperty.call(Config.httpErrorHandlers, lastError === null || lastError === void 0 ? void 0 : lastError.status)) {
13956
+ Config.httpErrorHandlers[lastError === null || lastError === void 0 ? void 0 : lastError.status]({
13957
+ status: lastError === null || lastError === void 0 ? void 0 : lastError.status,
13958
+ body: lastError === null || lastError === void 0 ? void 0 : lastError.body,
13959
+ message: lastError === null || lastError === void 0 ? void 0 : lastError.message,
13960
+ });
13961
+ }
13962
+ return {
13963
+ error: {
13964
+ status: (lastError === null || lastError === void 0 ? void 0 : lastError.status) || 500,
13965
+ data: (lastError === null || lastError === void 0 ? void 0 : lastError.body) || (lastError === null || lastError === void 0 ? void 0 : lastError.message) || 'Unknown error',
13966
+ },
13967
+ };
13923
13968
  },
13924
13969
  };
13925
13970
  };
13926
13971
  /**
13927
13972
  * Shortcut for building an endpoint using the DM service.
13928
13973
  */
13929
- const buildEndpointFromDmServiceLegacy = (serviceFn) => buildEndpointFromServiceLegacy(SERVICES.DM, serviceFn);
13974
+ const buildEndpointFromDmServiceLegacy = (serviceFn, options) => buildEndpointFromServiceLegacy(SERVICES.DM, serviceFn);
13930
13975
 
13931
13976
  createApi({
13932
13977
  reducerPath: 'apiKeysApiSlice',