@iblai/web-utils 1.2.7 → 1.2.9

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/dist/index.esm.js CHANGED
@@ -2754,22 +2754,22 @@ async function getUserName(storageService) {
2754
2754
  * @param storageService - Storage service to access localStorage
2755
2755
  * @returns boolean indicating if token has expired
2756
2756
  */
2757
- async function isJwtTokenExpired(storageService) {
2757
+ async function isDmTokenExpired(storageService) {
2758
2758
  try {
2759
2759
  const dmTokenExpires = await storageService.getItem(LOCAL_STORAGE_KEYS.DM_TOKEN_EXPIRES);
2760
2760
  if (!dmTokenExpires) {
2761
- return false; // not expired if dm_token_expires not found
2761
+ return true;
2762
2762
  }
2763
2763
  const expiryTime = new Date(dmTokenExpires).getTime();
2764
2764
  const currentTime = Date.now();
2765
2765
  if (currentTime >= expiryTime) {
2766
- console.warn("[AuthProvider] JWT token has expired");
2766
+ console.warn("[AuthProvider] DM token has expired");
2767
2767
  return true;
2768
2768
  }
2769
2769
  return false;
2770
2770
  }
2771
2771
  catch (error) {
2772
- console.error("[AuthProvider] Error checking JWT token expiry:", error);
2772
+ console.error("[AuthProvider] Error checking DM token expiry:", error);
2773
2773
  return true; // Treat as expired on error
2774
2774
  }
2775
2775
  }
@@ -2822,12 +2822,22 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
2822
2822
  return;
2823
2823
  }
2824
2824
  isRedirectingRef.current = true;
2825
- // Stop polling immediately to prevent stale callbacks
2826
- if (cookieCheckIntervalRef.current) {
2827
- clearInterval(cookieCheckIntervalRef.current);
2828
- cookieCheckIntervalRef.current = null;
2829
- }
2825
+ // NOTE: we intentionally do NOT clear the interval here.
2826
+ // The isRedirectingRef guard prevents redundant redirects while navigation
2827
+ // is in progress. Clearing the interval preemptively can permanently stop
2828
+ // cross-SPA sync if the consuming app suppresses the redirect (e.g. during
2829
+ // its own concurrent tenant switch), leaving the page with no active poller.
2830
2830
  redirectToAuthSpa(...args);
2831
+ // Safety recovery: if navigation was suppressed by the consuming app
2832
+ // (e.g. _suppressAuthRedirect was true), reset the guard after a short delay
2833
+ // so the interval can resume cross-SPA sync on its next tick.
2834
+ // If navigation did occur, the page will have unloaded before this fires.
2835
+ setTimeout(() => {
2836
+ if (isRedirectingRef.current) {
2837
+ console.log("[AuthProvider] safeRedirectToAuthSpa: navigation did not occur, resetting redirect guard");
2838
+ isRedirectingRef.current = false;
2839
+ }
2840
+ }, 3000);
2831
2841
  };
2832
2842
  /**
2833
2843
  * Sync cookies to localStorage on mount and periodically (web only)
@@ -2978,12 +2988,12 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
2978
2988
  }
2979
2989
  // Check JWT token expiry and force logout for protected routes
2980
2990
  if (isProtectedRoute && storageService) {
2981
- const jwtExpired = await isJwtTokenExpired(storageService);
2982
- if (jwtExpired) {
2983
- console.log("[auth-redirect] JWT token has expired, forcing logout");
2991
+ const dmTokenExpired = await isDmTokenExpired(storageService);
2992
+ if (dmTokenExpired) {
2993
+ console.log("[auth-redirect] DM token has expired, forcing logout");
2984
2994
  // Clear all auth-related storage keys
2985
2995
  clearAuthCookies();
2986
- const reason = "JWT token expired";
2996
+ const reason = "DM token expired";
2987
2997
  onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(reason);
2988
2998
  safeRedirectToAuthSpa(undefined, undefined, true);
2989
2999
  return;
@@ -5904,6 +5914,7 @@ const initialState$3 = {
5904
5914
  href: "",
5905
5915
  },
5906
5916
  pageContent: "",
5917
+ metadata: null,
5907
5918
  },
5908
5919
  documentFilter: null,
5909
5920
  token: null,
@@ -8047,6 +8058,7 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
8047
8058
  messageData = {
8048
8059
  ...messageData,
8049
8060
  page_content: iframeContext.pageContent,
8061
+ metadata: iframeContext.metadata,
8050
8062
  };
8051
8063
  }
8052
8064
  if (documentFilter) {
@@ -13579,6 +13591,7 @@ const buildEndpointFromService = (service, serviceFn) => {
13579
13591
  return { data };
13580
13592
  }
13581
13593
  catch (err) {
13594
+ console.error('[data-layer] API error:', JSON.stringify(err, Object.getOwnPropertyNames(err)));
13582
13595
  if (Object.prototype.hasOwnProperty.call(Config.httpErrorHandlers, err === null || err === void 0 ? void 0 : err.status)) {
13583
13596
  Config.httpErrorHandlers[err === null || err === void 0 ? void 0 : err.status]({ ...((err === null || err === void 0 ? void 0 : err.data) || {}) });
13584
13597
  }
@@ -13649,7 +13662,7 @@ const invokeHttpErrorHandler = (status, error) => {
13649
13662
  }
13650
13663
  };
13651
13664
  /** HTTP status codes that should not be retried (e.g., 402 Payment Required). */
13652
- const NON_RETRYABLE_STATUS_CODES = [402];
13665
+ const NON_RETRYABLE_STATUS_CODES = [402, 401];
13653
13666
  /**
13654
13667
  * Wraps a base query to skip retries for non-retryable HTTP status codes.
13655
13668
  * Uses `retry.fail()` to immediately bail out of the retry loop.
@@ -13704,7 +13717,6 @@ const iblFetchBaseQuery = async (args, api, extraOptions) => {
13704
13717
  error: e,
13705
13718
  }));
13706
13719
  // Extract status from error and invoke HTTP error handlers
13707
- console.log('[MONETIZATION e]', { e });
13708
13720
  const err = e;
13709
13721
  const errorStatus = typeof e === 'object' && e !== null && 'status' in e
13710
13722
  ? e.status
@@ -14387,7 +14399,7 @@ createApi({
14387
14399
  });
14388
14400
 
14389
14401
  const featureTags = {
14390
- PLATFORM_USERS: "PLATFORM_USERS",
14402
+ PLATFORM_USERS: 'PLATFORM_USERS',
14391
14403
  };
14392
14404
 
14393
14405
  createApi({
@@ -14699,10 +14711,10 @@ createApi({
14699
14711
  credentials: 'omit',
14700
14712
  });
14701
14713
  if (!response.ok) {
14702
- const error = new Error('Failed to fetch RBAC group details');
14703
- error.status = response.status;
14704
- error.body = await response.text();
14705
- throw error;
14714
+ throw Object.assign(new Error('Failed to fetch RBAC group details'), {
14715
+ status: response.status,
14716
+ body: await response.text(),
14717
+ });
14706
14718
  }
14707
14719
  return response.json();
14708
14720
  }),
@@ -14736,10 +14748,10 @@ createApi({
14736
14748
  credentials: 'omit',
14737
14749
  });
14738
14750
  if (!response.ok) {
14739
- const error = new Error('Failed to fetch RBAC policy details');
14740
- error.status = response.status;
14741
- error.body = await response.text();
14742
- throw error;
14751
+ throw Object.assign(new Error('Failed to fetch RBAC policy details'), {
14752
+ status: response.status,
14753
+ body: await response.text(),
14754
+ });
14743
14755
  }
14744
14756
  return response.json();
14745
14757
  }),
@@ -14777,15 +14789,33 @@ createApi({
14777
14789
  credentials: 'omit',
14778
14790
  });
14779
14791
  if (!response.ok) {
14780
- const error = new Error('Failed to fetch RBAC role details');
14781
- error.status = response.status;
14782
- error.body = await response.text();
14783
- throw error;
14792
+ throw Object.assign(new Error('Failed to fetch RBAC role details'), {
14793
+ status: response.status,
14794
+ body: await response.text(),
14795
+ });
14784
14796
  }
14785
14797
  return response.json();
14786
14798
  }),
14787
14799
  providesTags: ['RbacRoles'],
14788
14800
  }),
14801
+ getDepartmentMemberCheck: builder.query({
14802
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
14803
+ const queryParams = new URLSearchParams({ platform_key: args.platform_key });
14804
+ const url = `${OpenAPI.BASE}/api/core/departments/members/check/?${queryParams.toString()}`;
14805
+ const response = await fetch(url, {
14806
+ method: 'GET',
14807
+ headers: OpenAPI.HEADERS,
14808
+ credentials: 'omit',
14809
+ });
14810
+ if (!response.ok) {
14811
+ throw Object.assign(new Error('Failed to fetch department member check'), {
14812
+ status: response.status,
14813
+ body: await response.text(),
14814
+ });
14815
+ }
14816
+ return response.json();
14817
+ }),
14818
+ }),
14789
14819
  getRbacMentorAccessList: builder.query({
14790
14820
  ...buildEndpointFromDmService(CoreService.coreRbacMentorAccessList),
14791
14821
  }),
@@ -16060,6 +16090,7 @@ createApi({
16060
16090
  'catalog-roles',
16061
16091
  'catalog-invitations-course',
16062
16092
  'catalog-invitations-program',
16093
+ 'user-assigned-programs',
16063
16094
  ],
16064
16095
  endpoints: (builder) => ({
16065
16096
  getUserReportedSkills: builder.query({
@@ -16167,15 +16198,56 @@ createApi({
16167
16198
  credentials: 'omit',
16168
16199
  });
16169
16200
  if (!response.ok) {
16170
- const error = new Error('Failed to fetch program invitations');
16171
- error.status = response.status;
16172
- error.body = await response.text();
16173
- throw error;
16201
+ throw Object.assign(new Error('Failed to fetch program invitations'), {
16202
+ status: response.status,
16203
+ body: await response.text(),
16204
+ });
16174
16205
  }
16175
16206
  return response.json();
16176
16207
  }),
16177
16208
  providesTags: ['catalog-invitations-program'],
16178
16209
  }),
16210
+ getUserCatalogPathways: builder.query({
16211
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
16212
+ const params = new URLSearchParams({
16213
+ username: args.username,
16214
+ platform_key: args.platform_key,
16215
+ });
16216
+ const url = `${OpenAPI.BASE}/api/catalog/pathways/?${params.toString()}`;
16217
+ const response = await fetch(url, {
16218
+ method: 'GET',
16219
+ headers: OpenAPI.HEADERS,
16220
+ credentials: 'omit',
16221
+ });
16222
+ if (!response.ok) {
16223
+ throw Object.assign(new Error('Failed to fetch user catalog pathways'), {
16224
+ status: response.status,
16225
+ body: await response.text(),
16226
+ });
16227
+ }
16228
+ return response.json();
16229
+ }),
16230
+ providesTags: ['user-catalog-pathways'],
16231
+ }),
16232
+ getAssignedPrograms: builder.query({
16233
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
16234
+ const params = new URLSearchParams({ user_id: String(args.user_id) });
16235
+ const url = `${OpenAPI.BASE}/api/catalog/suggestions/program/user/?${params.toString()}`;
16236
+ const response = await fetch(url, {
16237
+ method: 'GET',
16238
+ headers: OpenAPI.HEADERS,
16239
+ credentials: 'omit',
16240
+ });
16241
+ if (!response.ok) {
16242
+ throw Object.assign(new Error('Failed to fetch assigned programs'), {
16243
+ status: response.status,
16244
+ body: await response.text(),
16245
+ });
16246
+ }
16247
+ return response.json();
16248
+ }),
16249
+ providesTags: ['user-assigned-programs'],
16250
+ }),
16179
16251
  }),
16180
16252
  });
16181
16253
 
@@ -16197,6 +16269,114 @@ createApi({
16197
16269
  }),
16198
16270
  });
16199
16271
 
16272
+ createApi({
16273
+ reducerPath: 'coursesApiSlice',
16274
+ baseQuery: iblFetchBaseQuery,
16275
+ tagTypes: ['user-enrolled-courses', 'user-assigned-courses'],
16276
+ endpoints: (builder) => ({
16277
+ getUserEnrolledCourses: builder.query({
16278
+ query: ({ username, query }) => {
16279
+ const params = new URLSearchParams();
16280
+ params.set('username', username);
16281
+ if (query) {
16282
+ for (const [k, v] of Object.entries(query)) {
16283
+ if (v !== undefined && v !== null)
16284
+ params.set(k, String(v));
16285
+ }
16286
+ }
16287
+ return {
16288
+ url: `/api/catalog/enrollment/courses/search/?${params.toString()}`,
16289
+ service: SERVICES.DM,
16290
+ method: 'GET',
16291
+ };
16292
+ },
16293
+ providesTags: ['user-enrolled-courses'],
16294
+ }),
16295
+ getUserAssignedCourses: builder.query({
16296
+ query: ({ user_id, query }) => {
16297
+ const params = new URLSearchParams();
16298
+ params.set('user_id', String(user_id));
16299
+ if (query) {
16300
+ for (const [k, v] of Object.entries(query)) {
16301
+ if (v !== undefined && v !== null)
16302
+ params.set(k, String(v));
16303
+ }
16304
+ }
16305
+ return {
16306
+ url: `/api/catalog/suggestions/course/user/?${params.toString()}`,
16307
+ service: SERVICES.DM,
16308
+ method: 'GET',
16309
+ };
16310
+ },
16311
+ providesTags: ['user-assigned-courses'],
16312
+ }),
16313
+ }),
16314
+ });
16315
+
16316
+ createApi({
16317
+ reducerPath: 'courseMetadataApiSlice',
16318
+ baseQuery: iblFetchBaseQuery,
16319
+ endpoints: (builder) => ({
16320
+ getCourseMetaData: builder.query({
16321
+ query: ({ courseKey }) => ({
16322
+ url: `/api/ibl/v1/course_metadata?course_key=${encodeURIComponent(courseKey)}`,
16323
+ service: SERVICES.LMS,
16324
+ method: 'GET',
16325
+ }),
16326
+ }),
16327
+ getCourseCompletionOutlines: builder.query({
16328
+ query: ({ courseKey }) => ({
16329
+ url: `/api/ibl/completion/course_outline/${courseKey}?course_id=${encodeURIComponent(courseKey)}`,
16330
+ service: SERVICES.LMS,
16331
+ method: 'GET',
16332
+ }),
16333
+ }),
16334
+ getCourseEligibility: builder.query({
16335
+ query: ({ courseKey }) => ({
16336
+ url: `/api/ibl/enrollment/enroll_status?course_id=${encodeURIComponent(courseKey)}`,
16337
+ service: SERVICES.LMS,
16338
+ method: 'GET',
16339
+ }),
16340
+ }),
16341
+ createCourseEnrollment: builder.mutation({
16342
+ query: (body) => ({
16343
+ url: `/api/enrollment/v1/enrollment`,
16344
+ service: SERVICES.LMS,
16345
+ method: 'POST',
16346
+ body,
16347
+ }),
16348
+ }),
16349
+ getCourseProgress: builder.query({
16350
+ query: ({ courseKey }) => ({
16351
+ url: `/api/course_home/progress/${courseKey}`,
16352
+ service: SERVICES.LMS,
16353
+ method: 'GET',
16354
+ }),
16355
+ }),
16356
+ getCourseCompletion: builder.query({
16357
+ query: ({ courseKey, userID }) => ({
16358
+ url: `/api/catalog/milestones/completions/course/manage/?course_id=${courseKey}&user_id=${userID}`,
16359
+ service: SERVICES.DM,
16360
+ method: 'GET',
16361
+ }),
16362
+ }),
16363
+ }),
16364
+ });
16365
+
16366
+ createApi({
16367
+ reducerPath: 'edxSsoApiSlice',
16368
+ baseQuery: iblFetchBaseQuery,
16369
+ endpoints: (builder) => ({
16370
+ getEdxSSOToken: builder.query({
16371
+ query: ({ username, redirectUrl }) => ({
16372
+ url: `/ibl/ai/sso/backend/edx/sso-auth-token/generate?username=${encodeURIComponent(username)}&redirect_url=${encodeURIComponent(redirectUrl)}`,
16373
+ service: SERVICES.LMS,
16374
+ method: 'GET',
16375
+ }),
16376
+ }),
16377
+ }),
16378
+ });
16379
+
16200
16380
  createApi({
16201
16381
  reducerPath: 'searchApiSlice',
16202
16382
  baseQuery: iblFakeBaseQuery,
@@ -16870,6 +17050,7 @@ const MEMORY_QUERY_KEYS = {
16870
17050
  MEMSEARCH_MENTOR_MEMORIES: () => ['MEMSEARCH_MENTOR_MEMORIES'],
16871
17051
  MEMSEARCH_MEMORY_CATEGORIES: () => ['MEMSEARCH_MEMORY_CATEGORIES'],
16872
17052
  MEMSEARCH_PLATFORM_CONFIG: () => ['MEMSEARCH_PLATFORM_CONFIG'],
17053
+ MEMSEARCH_STATUS: () => ['MEMSEARCH_STATUS'],
16873
17054
  };
16874
17055
  const MEMSEARCH_ENDPOINTS = {
16875
17056
  // User Memory Settings
@@ -16937,6 +17118,11 @@ const MEMSEARCH_ENDPOINTS = {
16937
17118
  service: SERVICES.AXD,
16938
17119
  path: (org, userId) => `/api/ai-mentor/orgs/${org}/users/${userId}/memsearch-config/`,
16939
17120
  },
17121
+ // Memsearch Status (available to students and admins)
17122
+ GET_MEMSEARCH_STATUS: {
17123
+ service: SERVICES.AXD,
17124
+ path: (org, userId) => `/api/ai-mentor/orgs/${org}/users/${userId}/memsearch-status/`,
17125
+ },
16940
17126
  };
16941
17127
 
16942
17128
  createApi({
@@ -16947,6 +17133,7 @@ createApi({
16947
17133
  ...MEMORY_QUERY_KEYS.MEMSEARCH_MENTOR_MEMORIES(),
16948
17134
  ...MEMORY_QUERY_KEYS.MEMSEARCH_MEMORY_CATEGORIES(),
16949
17135
  ...MEMORY_QUERY_KEYS.MEMSEARCH_PLATFORM_CONFIG(),
17136
+ ...MEMORY_QUERY_KEYS.MEMSEARCH_STATUS(),
16950
17137
  ],
16951
17138
  baseQuery: iblFetchBaseQuery,
16952
17139
  endpoints: (builder) => ({
@@ -17079,6 +17266,14 @@ createApi({
17079
17266
  }),
17080
17267
  invalidatesTags: MEMORY_QUERY_KEYS.MEMSEARCH_PLATFORM_CONFIG(),
17081
17268
  }),
17269
+ // Memsearch Status (student + admin accessible)
17270
+ getMemsearchStatus: builder.query({
17271
+ query: (args) => ({
17272
+ url: MEMSEARCH_ENDPOINTS.GET_MEMSEARCH_STATUS.path(args.org, args.userId),
17273
+ service: MEMSEARCH_ENDPOINTS.GET_MEMSEARCH_STATUS.service,
17274
+ }),
17275
+ providesTags: MEMORY_QUERY_KEYS.MEMSEARCH_STATUS(),
17276
+ }),
17082
17277
  }),
17083
17278
  });
17084
17279
 
@@ -18059,6 +18254,33 @@ createApi({
18059
18254
  }),
18060
18255
  });
18061
18256
 
18257
+ const AUDIT_LOGS_REDUCER_PATH = 'auditLogsApiSlice';
18258
+ const AUDIT_LOGS_QUERY_KEYS = {
18259
+ GET_AUDIT_LOGS: () => ['AUDIT_LOGS'],
18260
+ };
18261
+ const AUDIT_LOGS_ENDPOINTS = {
18262
+ GET_AUDIT_LOGS: {
18263
+ service: SERVICES.AXD,
18264
+ path: (org, userId) => `/api/ai-mentor/orgs/${org}/users/${userId}/mentors/audit-logs/`,
18265
+ },
18266
+ };
18267
+
18268
+ createApi({
18269
+ reducerPath: AUDIT_LOGS_REDUCER_PATH,
18270
+ tagTypes: [...AUDIT_LOGS_QUERY_KEYS.GET_AUDIT_LOGS()],
18271
+ baseQuery: iblFetchBaseQuery,
18272
+ endpoints: (builder) => ({
18273
+ getAuditLogs: builder.query({
18274
+ query: (args) => ({
18275
+ url: AUDIT_LOGS_ENDPOINTS.GET_AUDIT_LOGS.path(args.org, args.userId),
18276
+ service: AUDIT_LOGS_ENDPOINTS.GET_AUDIT_LOGS.service,
18277
+ params: args.params,
18278
+ }),
18279
+ providesTags: AUDIT_LOGS_QUERY_KEYS.GET_AUDIT_LOGS(),
18280
+ }),
18281
+ }),
18282
+ });
18283
+
18062
18284
  function useMentorSettings({ mentorId, tenantKey, username, isPublicRoute, }) {
18063
18285
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
18064
18286
  const isLoggedIn = username !== ANONYMOUS_USERNAME;