@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.js CHANGED
@@ -2774,22 +2774,22 @@ async function getUserName(storageService) {
2774
2774
  * @param storageService - Storage service to access localStorage
2775
2775
  * @returns boolean indicating if token has expired
2776
2776
  */
2777
- async function isJwtTokenExpired(storageService) {
2777
+ async function isDmTokenExpired(storageService) {
2778
2778
  try {
2779
2779
  const dmTokenExpires = await storageService.getItem(LOCAL_STORAGE_KEYS.DM_TOKEN_EXPIRES);
2780
2780
  if (!dmTokenExpires) {
2781
- return false; // not expired if dm_token_expires not found
2781
+ return true;
2782
2782
  }
2783
2783
  const expiryTime = new Date(dmTokenExpires).getTime();
2784
2784
  const currentTime = Date.now();
2785
2785
  if (currentTime >= expiryTime) {
2786
- console.warn("[AuthProvider] JWT token has expired");
2786
+ console.warn("[AuthProvider] DM token has expired");
2787
2787
  return true;
2788
2788
  }
2789
2789
  return false;
2790
2790
  }
2791
2791
  catch (error) {
2792
- console.error("[AuthProvider] Error checking JWT token expiry:", error);
2792
+ console.error("[AuthProvider] Error checking DM token expiry:", error);
2793
2793
  return true; // Treat as expired on error
2794
2794
  }
2795
2795
  }
@@ -2842,12 +2842,22 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
2842
2842
  return;
2843
2843
  }
2844
2844
  isRedirectingRef.current = true;
2845
- // Stop polling immediately to prevent stale callbacks
2846
- if (cookieCheckIntervalRef.current) {
2847
- clearInterval(cookieCheckIntervalRef.current);
2848
- cookieCheckIntervalRef.current = null;
2849
- }
2845
+ // NOTE: we intentionally do NOT clear the interval here.
2846
+ // The isRedirectingRef guard prevents redundant redirects while navigation
2847
+ // is in progress. Clearing the interval preemptively can permanently stop
2848
+ // cross-SPA sync if the consuming app suppresses the redirect (e.g. during
2849
+ // its own concurrent tenant switch), leaving the page with no active poller.
2850
2850
  redirectToAuthSpa(...args);
2851
+ // Safety recovery: if navigation was suppressed by the consuming app
2852
+ // (e.g. _suppressAuthRedirect was true), reset the guard after a short delay
2853
+ // so the interval can resume cross-SPA sync on its next tick.
2854
+ // If navigation did occur, the page will have unloaded before this fires.
2855
+ setTimeout(() => {
2856
+ if (isRedirectingRef.current) {
2857
+ console.log("[AuthProvider] safeRedirectToAuthSpa: navigation did not occur, resetting redirect guard");
2858
+ isRedirectingRef.current = false;
2859
+ }
2860
+ }, 3000);
2851
2861
  };
2852
2862
  /**
2853
2863
  * Sync cookies to localStorage on mount and periodically (web only)
@@ -2998,12 +3008,12 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
2998
3008
  }
2999
3009
  // Check JWT token expiry and force logout for protected routes
3000
3010
  if (isProtectedRoute && storageService) {
3001
- const jwtExpired = await isJwtTokenExpired(storageService);
3002
- if (jwtExpired) {
3003
- console.log("[auth-redirect] JWT token has expired, forcing logout");
3011
+ const dmTokenExpired = await isDmTokenExpired(storageService);
3012
+ if (dmTokenExpired) {
3013
+ console.log("[auth-redirect] DM token has expired, forcing logout");
3004
3014
  // Clear all auth-related storage keys
3005
3015
  clearAuthCookies();
3006
- const reason = "JWT token expired";
3016
+ const reason = "DM token expired";
3007
3017
  onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(reason);
3008
3018
  safeRedirectToAuthSpa(undefined, undefined, true);
3009
3019
  return;
@@ -5924,6 +5934,7 @@ const initialState$3 = {
5924
5934
  href: "",
5925
5935
  },
5926
5936
  pageContent: "",
5937
+ metadata: null,
5927
5938
  },
5928
5939
  documentFilter: null,
5929
5940
  token: null,
@@ -8067,6 +8078,7 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
8067
8078
  messageData = {
8068
8079
  ...messageData,
8069
8080
  page_content: iframeContext.pageContent,
8081
+ metadata: iframeContext.metadata,
8070
8082
  };
8071
8083
  }
8072
8084
  if (documentFilter) {
@@ -13599,6 +13611,7 @@ const buildEndpointFromService = (service, serviceFn) => {
13599
13611
  return { data };
13600
13612
  }
13601
13613
  catch (err) {
13614
+ console.error('[data-layer] API error:', JSON.stringify(err, Object.getOwnPropertyNames(err)));
13602
13615
  if (Object.prototype.hasOwnProperty.call(Config.httpErrorHandlers, err === null || err === void 0 ? void 0 : err.status)) {
13603
13616
  Config.httpErrorHandlers[err === null || err === void 0 ? void 0 : err.status]({ ...((err === null || err === void 0 ? void 0 : err.data) || {}) });
13604
13617
  }
@@ -13669,7 +13682,7 @@ const invokeHttpErrorHandler = (status, error) => {
13669
13682
  }
13670
13683
  };
13671
13684
  /** HTTP status codes that should not be retried (e.g., 402 Payment Required). */
13672
- const NON_RETRYABLE_STATUS_CODES = [402];
13685
+ const NON_RETRYABLE_STATUS_CODES = [402, 401];
13673
13686
  /**
13674
13687
  * Wraps a base query to skip retries for non-retryable HTTP status codes.
13675
13688
  * Uses `retry.fail()` to immediately bail out of the retry loop.
@@ -13724,7 +13737,6 @@ const iblFetchBaseQuery = async (args, api, extraOptions) => {
13724
13737
  error: e,
13725
13738
  }));
13726
13739
  // Extract status from error and invoke HTTP error handlers
13727
- console.log('[MONETIZATION e]', { e });
13728
13740
  const err = e;
13729
13741
  const errorStatus = typeof e === 'object' && e !== null && 'status' in e
13730
13742
  ? e.status
@@ -14407,7 +14419,7 @@ createApi({
14407
14419
  });
14408
14420
 
14409
14421
  const featureTags = {
14410
- PLATFORM_USERS: "PLATFORM_USERS",
14422
+ PLATFORM_USERS: 'PLATFORM_USERS',
14411
14423
  };
14412
14424
 
14413
14425
  createApi({
@@ -14719,10 +14731,10 @@ createApi({
14719
14731
  credentials: 'omit',
14720
14732
  });
14721
14733
  if (!response.ok) {
14722
- const error = new Error('Failed to fetch RBAC group details');
14723
- error.status = response.status;
14724
- error.body = await response.text();
14725
- throw error;
14734
+ throw Object.assign(new Error('Failed to fetch RBAC group details'), {
14735
+ status: response.status,
14736
+ body: await response.text(),
14737
+ });
14726
14738
  }
14727
14739
  return response.json();
14728
14740
  }),
@@ -14756,10 +14768,10 @@ createApi({
14756
14768
  credentials: 'omit',
14757
14769
  });
14758
14770
  if (!response.ok) {
14759
- const error = new Error('Failed to fetch RBAC policy details');
14760
- error.status = response.status;
14761
- error.body = await response.text();
14762
- throw error;
14771
+ throw Object.assign(new Error('Failed to fetch RBAC policy details'), {
14772
+ status: response.status,
14773
+ body: await response.text(),
14774
+ });
14763
14775
  }
14764
14776
  return response.json();
14765
14777
  }),
@@ -14797,15 +14809,33 @@ createApi({
14797
14809
  credentials: 'omit',
14798
14810
  });
14799
14811
  if (!response.ok) {
14800
- const error = new Error('Failed to fetch RBAC role details');
14801
- error.status = response.status;
14802
- error.body = await response.text();
14803
- throw error;
14812
+ throw Object.assign(new Error('Failed to fetch RBAC role details'), {
14813
+ status: response.status,
14814
+ body: await response.text(),
14815
+ });
14804
14816
  }
14805
14817
  return response.json();
14806
14818
  }),
14807
14819
  providesTags: ['RbacRoles'],
14808
14820
  }),
14821
+ getDepartmentMemberCheck: builder.query({
14822
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
14823
+ const queryParams = new URLSearchParams({ platform_key: args.platform_key });
14824
+ const url = `${iblaiApi.OpenAPI.BASE}/api/core/departments/members/check/?${queryParams.toString()}`;
14825
+ const response = await fetch(url, {
14826
+ method: 'GET',
14827
+ headers: iblaiApi.OpenAPI.HEADERS,
14828
+ credentials: 'omit',
14829
+ });
14830
+ if (!response.ok) {
14831
+ throw Object.assign(new Error('Failed to fetch department member check'), {
14832
+ status: response.status,
14833
+ body: await response.text(),
14834
+ });
14835
+ }
14836
+ return response.json();
14837
+ }),
14838
+ }),
14809
14839
  getRbacMentorAccessList: builder.query({
14810
14840
  ...buildEndpointFromDmService(iblaiApi.CoreService.coreRbacMentorAccessList),
14811
14841
  }),
@@ -16080,6 +16110,7 @@ createApi({
16080
16110
  'catalog-roles',
16081
16111
  'catalog-invitations-course',
16082
16112
  'catalog-invitations-program',
16113
+ 'user-assigned-programs',
16083
16114
  ],
16084
16115
  endpoints: (builder) => ({
16085
16116
  getUserReportedSkills: builder.query({
@@ -16187,15 +16218,56 @@ createApi({
16187
16218
  credentials: 'omit',
16188
16219
  });
16189
16220
  if (!response.ok) {
16190
- const error = new Error('Failed to fetch program invitations');
16191
- error.status = response.status;
16192
- error.body = await response.text();
16193
- throw error;
16221
+ throw Object.assign(new Error('Failed to fetch program invitations'), {
16222
+ status: response.status,
16223
+ body: await response.text(),
16224
+ });
16194
16225
  }
16195
16226
  return response.json();
16196
16227
  }),
16197
16228
  providesTags: ['catalog-invitations-program'],
16198
16229
  }),
16230
+ getUserCatalogPathways: builder.query({
16231
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
16232
+ const params = new URLSearchParams({
16233
+ username: args.username,
16234
+ platform_key: args.platform_key,
16235
+ });
16236
+ const url = `${iblaiApi.OpenAPI.BASE}/api/catalog/pathways/?${params.toString()}`;
16237
+ const response = await fetch(url, {
16238
+ method: 'GET',
16239
+ headers: iblaiApi.OpenAPI.HEADERS,
16240
+ credentials: 'omit',
16241
+ });
16242
+ if (!response.ok) {
16243
+ throw Object.assign(new Error('Failed to fetch user catalog pathways'), {
16244
+ status: response.status,
16245
+ body: await response.text(),
16246
+ });
16247
+ }
16248
+ return response.json();
16249
+ }),
16250
+ providesTags: ['user-catalog-pathways'],
16251
+ }),
16252
+ getAssignedPrograms: builder.query({
16253
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
16254
+ const params = new URLSearchParams({ user_id: String(args.user_id) });
16255
+ const url = `${iblaiApi.OpenAPI.BASE}/api/catalog/suggestions/program/user/?${params.toString()}`;
16256
+ const response = await fetch(url, {
16257
+ method: 'GET',
16258
+ headers: iblaiApi.OpenAPI.HEADERS,
16259
+ credentials: 'omit',
16260
+ });
16261
+ if (!response.ok) {
16262
+ throw Object.assign(new Error('Failed to fetch assigned programs'), {
16263
+ status: response.status,
16264
+ body: await response.text(),
16265
+ });
16266
+ }
16267
+ return response.json();
16268
+ }),
16269
+ providesTags: ['user-assigned-programs'],
16270
+ }),
16199
16271
  }),
16200
16272
  });
16201
16273
 
@@ -16217,6 +16289,114 @@ createApi({
16217
16289
  }),
16218
16290
  });
16219
16291
 
16292
+ createApi({
16293
+ reducerPath: 'coursesApiSlice',
16294
+ baseQuery: iblFetchBaseQuery,
16295
+ tagTypes: ['user-enrolled-courses', 'user-assigned-courses'],
16296
+ endpoints: (builder) => ({
16297
+ getUserEnrolledCourses: builder.query({
16298
+ query: ({ username, query }) => {
16299
+ const params = new URLSearchParams();
16300
+ params.set('username', username);
16301
+ if (query) {
16302
+ for (const [k, v] of Object.entries(query)) {
16303
+ if (v !== undefined && v !== null)
16304
+ params.set(k, String(v));
16305
+ }
16306
+ }
16307
+ return {
16308
+ url: `/api/catalog/enrollment/courses/search/?${params.toString()}`,
16309
+ service: SERVICES.DM,
16310
+ method: 'GET',
16311
+ };
16312
+ },
16313
+ providesTags: ['user-enrolled-courses'],
16314
+ }),
16315
+ getUserAssignedCourses: builder.query({
16316
+ query: ({ user_id, query }) => {
16317
+ const params = new URLSearchParams();
16318
+ params.set('user_id', String(user_id));
16319
+ if (query) {
16320
+ for (const [k, v] of Object.entries(query)) {
16321
+ if (v !== undefined && v !== null)
16322
+ params.set(k, String(v));
16323
+ }
16324
+ }
16325
+ return {
16326
+ url: `/api/catalog/suggestions/course/user/?${params.toString()}`,
16327
+ service: SERVICES.DM,
16328
+ method: 'GET',
16329
+ };
16330
+ },
16331
+ providesTags: ['user-assigned-courses'],
16332
+ }),
16333
+ }),
16334
+ });
16335
+
16336
+ createApi({
16337
+ reducerPath: 'courseMetadataApiSlice',
16338
+ baseQuery: iblFetchBaseQuery,
16339
+ endpoints: (builder) => ({
16340
+ getCourseMetaData: builder.query({
16341
+ query: ({ courseKey }) => ({
16342
+ url: `/api/ibl/v1/course_metadata?course_key=${encodeURIComponent(courseKey)}`,
16343
+ service: SERVICES.LMS,
16344
+ method: 'GET',
16345
+ }),
16346
+ }),
16347
+ getCourseCompletionOutlines: builder.query({
16348
+ query: ({ courseKey }) => ({
16349
+ url: `/api/ibl/completion/course_outline/${courseKey}?course_id=${encodeURIComponent(courseKey)}`,
16350
+ service: SERVICES.LMS,
16351
+ method: 'GET',
16352
+ }),
16353
+ }),
16354
+ getCourseEligibility: builder.query({
16355
+ query: ({ courseKey }) => ({
16356
+ url: `/api/ibl/enrollment/enroll_status?course_id=${encodeURIComponent(courseKey)}`,
16357
+ service: SERVICES.LMS,
16358
+ method: 'GET',
16359
+ }),
16360
+ }),
16361
+ createCourseEnrollment: builder.mutation({
16362
+ query: (body) => ({
16363
+ url: `/api/enrollment/v1/enrollment`,
16364
+ service: SERVICES.LMS,
16365
+ method: 'POST',
16366
+ body,
16367
+ }),
16368
+ }),
16369
+ getCourseProgress: builder.query({
16370
+ query: ({ courseKey }) => ({
16371
+ url: `/api/course_home/progress/${courseKey}`,
16372
+ service: SERVICES.LMS,
16373
+ method: 'GET',
16374
+ }),
16375
+ }),
16376
+ getCourseCompletion: builder.query({
16377
+ query: ({ courseKey, userID }) => ({
16378
+ url: `/api/catalog/milestones/completions/course/manage/?course_id=${courseKey}&user_id=${userID}`,
16379
+ service: SERVICES.DM,
16380
+ method: 'GET',
16381
+ }),
16382
+ }),
16383
+ }),
16384
+ });
16385
+
16386
+ createApi({
16387
+ reducerPath: 'edxSsoApiSlice',
16388
+ baseQuery: iblFetchBaseQuery,
16389
+ endpoints: (builder) => ({
16390
+ getEdxSSOToken: builder.query({
16391
+ query: ({ username, redirectUrl }) => ({
16392
+ url: `/ibl/ai/sso/backend/edx/sso-auth-token/generate?username=${encodeURIComponent(username)}&redirect_url=${encodeURIComponent(redirectUrl)}`,
16393
+ service: SERVICES.LMS,
16394
+ method: 'GET',
16395
+ }),
16396
+ }),
16397
+ }),
16398
+ });
16399
+
16220
16400
  createApi({
16221
16401
  reducerPath: 'searchApiSlice',
16222
16402
  baseQuery: iblFakeBaseQuery,
@@ -16890,6 +17070,7 @@ const MEMORY_QUERY_KEYS = {
16890
17070
  MEMSEARCH_MENTOR_MEMORIES: () => ['MEMSEARCH_MENTOR_MEMORIES'],
16891
17071
  MEMSEARCH_MEMORY_CATEGORIES: () => ['MEMSEARCH_MEMORY_CATEGORIES'],
16892
17072
  MEMSEARCH_PLATFORM_CONFIG: () => ['MEMSEARCH_PLATFORM_CONFIG'],
17073
+ MEMSEARCH_STATUS: () => ['MEMSEARCH_STATUS'],
16893
17074
  };
16894
17075
  const MEMSEARCH_ENDPOINTS = {
16895
17076
  // User Memory Settings
@@ -16957,6 +17138,11 @@ const MEMSEARCH_ENDPOINTS = {
16957
17138
  service: SERVICES.AXD,
16958
17139
  path: (org, userId) => `/api/ai-mentor/orgs/${org}/users/${userId}/memsearch-config/`,
16959
17140
  },
17141
+ // Memsearch Status (available to students and admins)
17142
+ GET_MEMSEARCH_STATUS: {
17143
+ service: SERVICES.AXD,
17144
+ path: (org, userId) => `/api/ai-mentor/orgs/${org}/users/${userId}/memsearch-status/`,
17145
+ },
16960
17146
  };
16961
17147
 
16962
17148
  createApi({
@@ -16967,6 +17153,7 @@ createApi({
16967
17153
  ...MEMORY_QUERY_KEYS.MEMSEARCH_MENTOR_MEMORIES(),
16968
17154
  ...MEMORY_QUERY_KEYS.MEMSEARCH_MEMORY_CATEGORIES(),
16969
17155
  ...MEMORY_QUERY_KEYS.MEMSEARCH_PLATFORM_CONFIG(),
17156
+ ...MEMORY_QUERY_KEYS.MEMSEARCH_STATUS(),
16970
17157
  ],
16971
17158
  baseQuery: iblFetchBaseQuery,
16972
17159
  endpoints: (builder) => ({
@@ -17099,6 +17286,14 @@ createApi({
17099
17286
  }),
17100
17287
  invalidatesTags: MEMORY_QUERY_KEYS.MEMSEARCH_PLATFORM_CONFIG(),
17101
17288
  }),
17289
+ // Memsearch Status (student + admin accessible)
17290
+ getMemsearchStatus: builder.query({
17291
+ query: (args) => ({
17292
+ url: MEMSEARCH_ENDPOINTS.GET_MEMSEARCH_STATUS.path(args.org, args.userId),
17293
+ service: MEMSEARCH_ENDPOINTS.GET_MEMSEARCH_STATUS.service,
17294
+ }),
17295
+ providesTags: MEMORY_QUERY_KEYS.MEMSEARCH_STATUS(),
17296
+ }),
17102
17297
  }),
17103
17298
  });
17104
17299
 
@@ -18079,6 +18274,33 @@ createApi({
18079
18274
  }),
18080
18275
  });
18081
18276
 
18277
+ const AUDIT_LOGS_REDUCER_PATH = 'auditLogsApiSlice';
18278
+ const AUDIT_LOGS_QUERY_KEYS = {
18279
+ GET_AUDIT_LOGS: () => ['AUDIT_LOGS'],
18280
+ };
18281
+ const AUDIT_LOGS_ENDPOINTS = {
18282
+ GET_AUDIT_LOGS: {
18283
+ service: SERVICES.AXD,
18284
+ path: (org, userId) => `/api/ai-mentor/orgs/${org}/users/${userId}/mentors/audit-logs/`,
18285
+ },
18286
+ };
18287
+
18288
+ createApi({
18289
+ reducerPath: AUDIT_LOGS_REDUCER_PATH,
18290
+ tagTypes: [...AUDIT_LOGS_QUERY_KEYS.GET_AUDIT_LOGS()],
18291
+ baseQuery: iblFetchBaseQuery,
18292
+ endpoints: (builder) => ({
18293
+ getAuditLogs: builder.query({
18294
+ query: (args) => ({
18295
+ url: AUDIT_LOGS_ENDPOINTS.GET_AUDIT_LOGS.path(args.org, args.userId),
18296
+ service: AUDIT_LOGS_ENDPOINTS.GET_AUDIT_LOGS.service,
18297
+ params: args.params,
18298
+ }),
18299
+ providesTags: AUDIT_LOGS_QUERY_KEYS.GET_AUDIT_LOGS(),
18300
+ }),
18301
+ }),
18302
+ });
18303
+
18082
18304
  function useMentorSettings({ mentorId, tenantKey, username, isPublicRoute, }) {
18083
18305
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
18084
18306
  const isLoggedIn = username !== ANONYMOUS_USERNAME;