@iblai/web-utils 0.2.1 → 1.0.0

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.
Files changed (38) hide show
  1. package/README.md +504 -0
  2. package/dist/data-layer/src/features/chat-files/api-slice.d.ts +185 -0
  3. package/dist/data-layer/src/features/chat-files/types.d.ts +32 -0
  4. package/dist/data-layer/src/features/core/api-slice.d.ts +419 -61
  5. package/dist/data-layer/src/features/core/constants.d.ts +3 -0
  6. package/dist/data-layer/src/features/core/custom-api-slice.d.ts +50 -50
  7. package/dist/data-layer/src/features/core/custom-public-image-asset-api-slice.d.ts +333 -0
  8. package/dist/data-layer/src/features/core/types.d.ts +33 -0
  9. package/dist/data-layer/src/features/credentials/api-slice.d.ts +62 -39
  10. package/dist/data-layer/src/features/mentor/api-slice.d.ts +980 -188
  11. package/dist/data-layer/src/features/notifications/constants.d.ts +6 -0
  12. package/dist/data-layer/src/features/notifications/custom-api-slice.d.ts +43 -43
  13. package/dist/data-layer/src/features/notifications/types.d.ts +25 -2
  14. package/dist/data-layer/src/index.d.ts +3 -0
  15. package/dist/index.d.ts +425 -66
  16. package/dist/index.esm.js +999 -109
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +1030 -107
  19. package/dist/index.js.map +1 -1
  20. package/dist/package.json +4 -2
  21. package/dist/web-utils/src/constants/chat.d.ts +8 -0
  22. package/dist/web-utils/src/features/files/filesSlice.d.ts +20 -0
  23. package/dist/web-utils/src/features/index.d.ts +1 -0
  24. package/dist/web-utils/src/hooks/chat/use-advanced-chat.d.ts +6 -4
  25. package/dist/web-utils/src/hooks/chat/use-chat-v2.d.ts +11 -1
  26. package/dist/web-utils/src/index.d.ts +2 -0
  27. package/dist/web-utils/src/index.web.d.ts +14 -12
  28. package/dist/web-utils/src/providers/auth-provider.d.ts +9 -1
  29. package/dist/web-utils/src/providers/tenant-provider.d.ts +2 -1
  30. package/dist/web-utils/src/services/__tests__/file-upload.test.d.ts +1 -0
  31. package/dist/web-utils/src/services/file-upload.d.ts +60 -0
  32. package/dist/web-utils/src/services/index.d.ts +1 -0
  33. package/dist/web-utils/src/types/file-upload.d.ts +62 -0
  34. package/dist/web-utils/src/types/index.d.ts +1 -0
  35. package/dist/web-utils/src/utils/auth.d.ts +180 -0
  36. package/dist/web-utils/src/utils/index.d.ts +1 -0
  37. package/dist/web-utils/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +3 -3
package/dist/index.esm.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { useCallback, useDebugValue, useMemo, useState, useEffect, useRef, useLayoutEffect, createContext, useContext } from 'react';
3
3
  import { OpenAPI, CoreService, ReportsService, AiAnalyticsService, AiMentorService, SearchService, AiPromptService, AiIndexService, CatalogService, SkillsService, AiAccountService, CredentialsService, FeaturesService, CommerceService, NotificationsService } from '@iblai/iblai-api';
4
- import { jsx, Fragment } from 'react/jsx-runtime';
4
+ import { jsx } from 'react/jsx-runtime';
5
+ import axios from 'axios';
5
6
 
6
7
  const SUBSCRIPTION_TRIGGERS = {
7
8
  PRICING_MODAL: "TRIGGER_PRICING_MODAL",
@@ -3380,7 +3381,7 @@ function updateMutationSubstateIfExists(state, id, update) {
3380
3381
  update(substate);
3381
3382
  }
3382
3383
  }
3383
- var initialState$1 = {};
3384
+ var initialState$2 = {};
3384
3385
  function buildSlice({
3385
3386
  reducerPath,
3386
3387
  queryThunk,
@@ -3456,7 +3457,7 @@ function buildSlice({
3456
3457
  }
3457
3458
  const querySlice = createSlice$1({
3458
3459
  name: `${reducerPath}/queries`,
3459
- initialState: initialState$1,
3460
+ initialState: initialState$2,
3460
3461
  reducers: {
3461
3462
  removeQueryResult: {
3462
3463
  reducer(draft, {
@@ -3590,7 +3591,7 @@ function buildSlice({
3590
3591
  });
3591
3592
  const mutationSlice = createSlice$1({
3592
3593
  name: `${reducerPath}/mutations`,
3593
- initialState: initialState$1,
3594
+ initialState: initialState$2,
3594
3595
  reducers: {
3595
3596
  removeMutationResult: {
3596
3597
  reducer(draft, {
@@ -3759,7 +3760,7 @@ function buildSlice({
3759
3760
  }
3760
3761
  const subscriptionSlice = createSlice$1({
3761
3762
  name: `${reducerPath}/subscriptions`,
3762
- initialState: initialState$1,
3763
+ initialState: initialState$2,
3763
3764
  reducers: {
3764
3765
  updateSubscriptionOptions(d, a) {
3765
3766
  },
@@ -3771,7 +3772,7 @@ function buildSlice({
3771
3772
  });
3772
3773
  const internalSubscriptionsSlice = createSlice$1({
3773
3774
  name: `${reducerPath}/internalSubscriptions`,
3774
- initialState: initialState$1,
3775
+ initialState: initialState$2,
3775
3776
  reducers: {
3776
3777
  subscriptionsUpdated: {
3777
3778
  reducer(state, action) {
@@ -6441,6 +6442,25 @@ createApi({
6441
6442
  }),
6442
6443
  });
6443
6444
 
6445
+ createApi({
6446
+ reducerPath: 'chatFilesApiSlice',
6447
+ baseQuery: iblFetchBaseQuery,
6448
+ endpoints: (builder) => ({
6449
+ /**
6450
+ * Get presigned S3 URL for file upload
6451
+ * POST /api/ai-mentor/orgs/{org}/users/{userId}/chat/files/upload-url/
6452
+ */
6453
+ getFileUploadUrl: builder.mutation({
6454
+ query: ({ org, userId, requestBody, }) => ({
6455
+ url: `/api/ai-mentor/orgs/${org}/users/${userId}/chat/files/upload-url/`,
6456
+ method: 'POST',
6457
+ body: requestBody,
6458
+ service: SERVICES.DM,
6459
+ }),
6460
+ }),
6461
+ }),
6462
+ });
6463
+
6444
6464
  createApi({
6445
6465
  reducerPath: 'llmsApiSlice',
6446
6466
  baseQuery: iblFakeBaseQuery,
@@ -6461,6 +6481,7 @@ const mentorApiSlice = createApi({
6461
6481
  'mentorPublicSettings',
6462
6482
  'shareableLinks',
6463
6483
  'publicMentors',
6484
+ 'moderationLogs',
6464
6485
  ],
6465
6486
  endpoints: (builder) => ({
6466
6487
  createMentor: builder.mutation({
@@ -6526,7 +6547,7 @@ const mentorApiSlice = createApi({
6526
6547
  }));
6527
6548
  }
6528
6549
  catch (error) {
6529
- console.error(error);
6550
+ console.error(JSON.stringify(error));
6530
6551
  }
6531
6552
  },
6532
6553
  }),
@@ -6582,9 +6603,17 @@ const mentorApiSlice = createApi({
6582
6603
  ...buildEndpointFromDmService(AiMentorService.aiMentorOrgsUsersMentorsForkCreate),
6583
6604
  invalidatesTags: ['mentors'],
6584
6605
  }),
6606
+ getRecentlyAccessedMentors: builder.query({
6607
+ ...buildEndpointFromDmService(AiMentorService.aiMentorOrgsUsersRecentlyAccessedMentorsList),
6608
+ providesTags: ['mentors'],
6609
+ }),
6610
+ getModerationLogs: builder.query({
6611
+ ...buildEndpointFromDmService(AiMentorService.aiMentorOrgsUsersModerationLogsList),
6612
+ providesTags: ['moderationLogs'],
6613
+ }),
6585
6614
  }),
6586
6615
  });
6587
- const { useLazyGetMentorsQuery, useLazySeedMentorsQuery, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetFreeUsageCountQuery} = mentorApiSlice;
6616
+ const { useLazyGetMentorsQuery, useLazySeedMentorsQuery, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetFreeUsageCountQuery, useLazyGetRecentlyAccessedMentorsQuery} = mentorApiSlice;
6588
6617
 
6589
6618
  createApi({
6590
6619
  reducerPath: 'mentorCategoriesApiSlice',
@@ -7051,7 +7080,20 @@ const coreApiSlice = createApi({
7051
7080
  invalidatesTags: ['RbacGroups'],
7052
7081
  }),
7053
7082
  getRbacGroupDetails: builder.query({
7054
- ...buildEndpointFromDmService(CoreService.coreRbacGroupsRetrieve),
7083
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
7084
+ const url = `${OpenAPI.BASE}/api/core/rbac/groups/${args.id}/`;
7085
+ const response = await fetch(url, {
7086
+ method: 'GET',
7087
+ headers: OpenAPI.HEADERS,
7088
+ });
7089
+ if (!response.ok) {
7090
+ const error = new Error('Failed to fetch RBAC group details');
7091
+ error.status = response.status;
7092
+ error.body = await response.text();
7093
+ throw error;
7094
+ }
7095
+ return response.json();
7096
+ }),
7055
7097
  providesTags: ['RbacGroups'],
7056
7098
  }),
7057
7099
  //RBAC POLICIES
@@ -7072,7 +7114,22 @@ const coreApiSlice = createApi({
7072
7114
  invalidatesTags: ['RbacPolicies'],
7073
7115
  }),
7074
7116
  getRbacPolicyDetails: builder.query({
7075
- ...buildEndpointFromDmService(CoreService.coreRbacPoliciesRetrieve),
7117
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
7118
+ const queryParams = new URLSearchParams({ platform_key: args.platform_key });
7119
+ const queryString = queryParams.toString();
7120
+ const url = `${OpenAPI.BASE}/api/core/rbac/policies/${args.id}/?${queryString}`;
7121
+ const response = await fetch(url, {
7122
+ method: 'GET',
7123
+ headers: OpenAPI.HEADERS,
7124
+ });
7125
+ if (!response.ok) {
7126
+ const error = new Error('Failed to fetch RBAC policy details');
7127
+ error.status = response.status;
7128
+ error.body = await response.text();
7129
+ throw error;
7130
+ }
7131
+ return response.json();
7132
+ }),
7076
7133
  providesTags: ['RbacPolicies'],
7077
7134
  }),
7078
7135
  //RBAC ROLES
@@ -7097,14 +7154,36 @@ const coreApiSlice = createApi({
7097
7154
  invalidatesTags: ['RbacRoles'],
7098
7155
  }),
7099
7156
  getRbacRoleDetails: builder.query({
7100
- ...buildEndpointFromDmService(CoreService.coreRbacRolesRetrieve),
7157
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
7158
+ const queryParams = new URLSearchParams({ platform_key: args.platform_key });
7159
+ const queryString = queryParams.toString();
7160
+ const url = `${OpenAPI.BASE}/api/core/rbac/roles/${args.id}/?${queryString}`;
7161
+ const response = await fetch(url, {
7162
+ method: 'GET',
7163
+ headers: OpenAPI.HEADERS,
7164
+ });
7165
+ if (!response.ok) {
7166
+ const error = new Error('Failed to fetch RBAC role details');
7167
+ error.status = response.status;
7168
+ error.body = await response.text();
7169
+ throw error;
7170
+ }
7171
+ return response.json();
7172
+ }),
7101
7173
  providesTags: ['RbacRoles'],
7102
7174
  }),
7175
+ getRbacMentorAccessList: builder.query({
7176
+ ...buildEndpointFromDmService(CoreService.coreRbacMentorAccessList),
7177
+ }),
7178
+ updateRbacMentorAccess: builder.mutation({
7179
+ ...buildEndpointFromDmService(CoreService.coreRbacMentorAccessCreate),
7180
+ }),
7103
7181
  }),
7104
7182
  });
7105
7183
  const { useGetRbacPermissionsMutation} = coreApiSlice;
7106
7184
 
7107
7185
  const CORE_CUSTOM_REDUCER_PATH = 'coreCustomApiSlice';
7186
+ const CORE_FAKE_CUSTOM_REDUCER_PATH = 'coreFakeCustomApiSlice';
7108
7187
  const PLATFORM_MEMBERSHIP_TAG = 'PlatformMembership';
7109
7188
  const CORE_CUSTOM_ENDPOINTS = {
7110
7189
  GET_PLATFORM_MEMBERSHIP: {
@@ -7138,7 +7217,13 @@ const CORE_CUSTOM_ENDPOINTS = {
7138
7217
  DELETE_PLATFORM_IMAGE_ASSET: {
7139
7218
  path: (platform_key, asset_id) => `/api/core/platforms/${platform_key}/public-image-assets/${asset_id}/`,
7140
7219
  service: SERVICES.DM,
7141
- }};
7220
+ },
7221
+ GET_PUBLIC_PLATFORM_IMAGE_ASSET_FILE_URL: {
7222
+ path: (platform_key, asset_id) => `/api/core/platforms/${platform_key}/public-image-assets/${asset_id}/file/`,
7223
+ baseUrl: getServiceUrl(SERVICES.DM),
7224
+ service: SERVICES.DM,
7225
+ },
7226
+ };
7142
7227
 
7143
7228
  createApi({
7144
7229
  // TODO: replace to catalogSlice
@@ -7210,7 +7295,13 @@ createApi({
7210
7295
  ...buildEndpointFromDmService(AiAccountService.aiAccountOrgsIntegrationCredentialPartialUpdate),
7211
7296
  }),
7212
7297
  getConnectedServiceAuthUrl: builder.query({
7213
- ...buildEndpointFromDmService(AiAccountService.aiAccountConnectedServicesOrgsUsersRetrieve),
7298
+ queryFn: async (queryParams, api, extraOptions) => {
7299
+ const url = `/api/ai-account/connected-services/orgs/${queryParams.org}/users/${queryParams.userId}/${queryParams.provider}/${queryParams.service}/`;
7300
+ return iblFetchBaseQuery({
7301
+ url,
7302
+ service: SERVICES.DM,
7303
+ }, api, extraOptions);
7304
+ },
7214
7305
  }),
7215
7306
  connectedServicesCallback: builder.query({
7216
7307
  queryFn: async (queryParams, api, extraOptions) => {
@@ -7342,7 +7433,7 @@ const sessionApiSlice = createApi({
7342
7433
  }),
7343
7434
  }),
7344
7435
  });
7345
- const { useCreateSessionIdMutation, useEditSessionMutation, } = sessionApiSlice;
7436
+ const { useCreateSessionIdMutation, useLazyGetSessionIdQuery, useEditSessionMutation, } = sessionApiSlice;
7346
7437
 
7347
7438
  createApi({
7348
7439
  reducerPath: 'datasetsApiSlice',
@@ -8279,6 +8370,12 @@ createApi({
8279
8370
  });
8280
8371
 
8281
8372
  const NOTIFICATIONS_CUSTOM_REDUCER_PATH = 'notificationsCustomApiSlice';
8373
+ const NOTIFICATIONS_CUSTOM_TAGS = {
8374
+ TEMPLATES: 'NOTIFICATIONS_CUSTOM_TEMPLATES',
8375
+ TEMPLATE_DETAILS: 'NOTIFICATIONS_CUSTOM_TEMPLATE_DETAILS',
8376
+ UPDATE_TEMPLATE: 'NOTIFICATIONS_CUSTOM_UPDATE_TEMPLATE',
8377
+ TOGGLE_TEMPLATE: 'NOTIFICATIONS_CUSTOM_TOGGLE_TEMPLATE',
8378
+ };
8282
8379
  const NOTIFICATIONS_CUSTOM_ENDPOINTS = {
8283
8380
  GET_TEMPLATES: {
8284
8381
  service: SERVICES.DM,
@@ -8300,6 +8397,7 @@ const NOTIFICATIONS_CUSTOM_ENDPOINTS = {
8300
8397
 
8301
8398
  createApi({
8302
8399
  reducerPath: NOTIFICATIONS_CUSTOM_REDUCER_PATH,
8400
+ tagTypes: [...Object.values(NOTIFICATIONS_CUSTOM_TAGS)],
8303
8401
  baseQuery: iblFetchBaseQuery,
8304
8402
  endpoints: (builder) => ({
8305
8403
  getTemplates: builder.query({
@@ -8309,6 +8407,7 @@ createApi({
8309
8407
  service: NOTIFICATIONS_CUSTOM_ENDPOINTS.GET_TEMPLATES.service,
8310
8408
  };
8311
8409
  },
8410
+ providesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATES],
8312
8411
  }),
8313
8412
  getTemplateDetails: builder.query({
8314
8413
  query: (args) => {
@@ -8317,6 +8416,7 @@ createApi({
8317
8416
  service: NOTIFICATIONS_CUSTOM_ENDPOINTS.GET_TEMPLATE_DETAILS.service,
8318
8417
  };
8319
8418
  },
8419
+ providesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATE_DETAILS],
8320
8420
  }),
8321
8421
  updateTemplate: builder.mutation({
8322
8422
  query: (args) => {
@@ -8327,6 +8427,7 @@ createApi({
8327
8427
  body: args.template,
8328
8428
  };
8329
8429
  },
8430
+ invalidatesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATE_DETAILS],
8330
8431
  }),
8331
8432
  toggleTemplate: builder.mutation({
8332
8433
  query: (args) => {
@@ -8339,6 +8440,7 @@ createApi({
8339
8440
  },
8340
8441
  };
8341
8442
  },
8443
+ invalidatesTags: [NOTIFICATIONS_CUSTOM_TAGS.TEMPLATES],
8342
8444
  }),
8343
8445
  }),
8344
8446
  });
@@ -8619,7 +8721,7 @@ const CUSTOM_DOMAIN_QUERY_KEYS = {
8619
8721
  CREATE_CUSTOM_DOMAIN: () => ['CREATE_CUSTOM_DOMAIN'],
8620
8722
  };
8621
8723
 
8622
- createApi({
8724
+ const customDomainApiSlice = createApi({
8623
8725
  reducerPath: CUSTOM_DOMAIN_REDUCER_PATH,
8624
8726
  baseQuery: iblFetchBaseQuery,
8625
8727
  tagTypes: [...CUSTOM_DOMAIN_QUERY_KEYS.GET_CUSTOM_DOMAINS()],
@@ -8658,6 +8760,7 @@ createApi({
8658
8760
  }),
8659
8761
  }),
8660
8762
  });
8763
+ const { useGetCustomDomainsQuery} = customDomainApiSlice;
8661
8764
 
8662
8765
  const PLATFORM_CUSTOM_REDUCER_PATH = "platformCustomApiSlice";
8663
8766
  const PLATFORM_CONFIGURATION_TAG = "PlatformConfiguration";
@@ -8720,7 +8823,7 @@ createApi({
8720
8823
  const coreCustomApiSlice = createApi({
8721
8824
  reducerPath: CORE_CUSTOM_REDUCER_PATH,
8722
8825
  baseQuery: iblFetchBaseQuery,
8723
- tagTypes: [PLATFORM_MEMBERSHIP_TAG],
8826
+ tagTypes: [PLATFORM_MEMBERSHIP_TAG, 'PlatformImageAssetFileUrl'],
8724
8827
  endpoints: (builder) => ({
8725
8828
  getPlatformMembership: builder.query({
8726
8829
  query: ({ platform_key }) => ({
@@ -8793,6 +8896,19 @@ const coreCustomApiSlice = createApi({
8793
8896
  });
8794
8897
  const { useJoinTenantMutation} = coreCustomApiSlice;
8795
8898
 
8899
+ createApi({
8900
+ reducerPath: CORE_FAKE_CUSTOM_REDUCER_PATH,
8901
+ baseQuery: iblFakeBaseQuery,
8902
+ endpoints: (builder) => ({
8903
+ getPublicPlatformImageAssetFileUrl: builder.query({
8904
+ ...buildEndpointFromService(SERVICES.DM, async (args) => {
8905
+ const url = `${OpenAPI.BASE}${CORE_CUSTOM_ENDPOINTS.GET_PUBLIC_PLATFORM_IMAGE_ASSET_FILE_URL.path(args.platform_key, args.asset_id)}`;
8906
+ return url;
8907
+ }),
8908
+ }),
8909
+ }),
8910
+ });
8911
+
8796
8912
  const EDX_PROCTORING_ENDPOINTS = {
8797
8913
  GET_EXAM_INFO: {
8798
8914
  service: SERVICES.LMS,
@@ -10305,6 +10421,299 @@ const formatRelativeTime = (timestamp) => {
10305
10421
  return `${diffInYears} year${diffInYears === 1 ? "" : "s"} ago`;
10306
10422
  };
10307
10423
 
10424
+ /**
10425
+ * Authentication and Authorization Utilities
10426
+ *
10427
+ * This module provides utility functions for handling authentication flows,
10428
+ * including redirects to auth SPA, cookie management, and session handling.
10429
+ */
10430
+ /**
10431
+ * Check if the current window is inside an iframe
10432
+ * @returns {boolean} True if running in an iframe, false otherwise
10433
+ */
10434
+ function isInIframe() {
10435
+ if (typeof window === "undefined") {
10436
+ return false;
10437
+ }
10438
+ return (window === null || window === void 0 ? void 0 : window.self) !== (window === null || window === void 0 ? void 0 : window.top);
10439
+ }
10440
+ /**
10441
+ * Send a message to the parent website (when in iframe)
10442
+ * @param payload - The data to send to parent window
10443
+ */
10444
+ function sendMessageToParentWebsite(payload) {
10445
+ window.parent.postMessage(payload, "*");
10446
+ }
10447
+ /**
10448
+ * Delete a cookie with specific name, path, and domain
10449
+ * @param name - Cookie name
10450
+ * @param path - Cookie path
10451
+ * @param domain - Cookie domain
10452
+ */
10453
+ function deleteCookie(name, path, domain) {
10454
+ const expires = "expires=Thu, 01 Jan 1970 00:00:00 UTC;";
10455
+ const cookieValue = `${name}=;`;
10456
+ const pathValue = path ? `path=${path};` : "";
10457
+ const domainValue = domain ? `domain=${domain};` : "";
10458
+ document.cookie = cookieValue + expires + pathValue + domainValue;
10459
+ }
10460
+ /**
10461
+ * Get all possible domain parts for cookie deletion
10462
+ * @param domain - The domain to split
10463
+ * @returns Array of domain parts
10464
+ * @example
10465
+ * getDomainParts('app.example.com') // returns ['app.example.com', 'example.com', 'com']
10466
+ */
10467
+ function getDomainParts(domain) {
10468
+ const parts = domain.split(".");
10469
+ const domains = [];
10470
+ for (let i = parts.length - 1; i >= 0; i--) {
10471
+ domains.push(parts.slice(i).join("."));
10472
+ }
10473
+ return domains;
10474
+ }
10475
+ /**
10476
+ * Delete a cookie across all possible domain variations
10477
+ * @param name - Cookie name to delete
10478
+ * @param childDomain - The current domain
10479
+ */
10480
+ function deleteCookieOnAllDomains(name, childDomain) {
10481
+ getDomainParts(childDomain).forEach((domainPart) => {
10482
+ deleteCookie(name, "/", domainPart);
10483
+ deleteCookie(name, "", domainPart);
10484
+ });
10485
+ }
10486
+ /**
10487
+ * Get the parent domain from a given domain
10488
+ * @param domain - The domain to process
10489
+ * @returns The parent domain (e.g., 'app.example.com' → '.example.com')
10490
+ */
10491
+ function getParentDomain(domain) {
10492
+ if (!domain) {
10493
+ return "";
10494
+ }
10495
+ const parts = domain.split(".");
10496
+ return parts.length > 1 ? `.${parts.slice(-2).join(".")}` : domain;
10497
+ }
10498
+ /**
10499
+ * Set a cookie for authentication with cross-domain support
10500
+ * @param name - Cookie name
10501
+ * @param value - Cookie value
10502
+ * @param days - Number of days until expiration (default: 365)
10503
+ */
10504
+ function setCookieForAuth(name, value, days = 365) {
10505
+ const expires = new Date();
10506
+ expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
10507
+ const hostname = window.location.hostname;
10508
+ let baseDomain = hostname;
10509
+ // Calculate base domain (skip for localhost and IP addresses)
10510
+ if (hostname !== "localhost" && !/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
10511
+ const parts = hostname.split(".");
10512
+ if (parts.length > 2) {
10513
+ baseDomain = `.${parts.slice(-2).join(".")}`;
10514
+ }
10515
+ }
10516
+ const domainAttr = baseDomain ? `;domain=${baseDomain}` : "";
10517
+ document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/;SameSite=None;Secure${domainAttr}`;
10518
+ }
10519
+ /**
10520
+ * Clear all cookies for the current domain
10521
+ */
10522
+ function clearCookies() {
10523
+ const cookies = document.cookie.split(";");
10524
+ for (let i = 0; i < cookies.length; i++) {
10525
+ const cookie = cookies[i];
10526
+ const eqPos = cookie.indexOf("=");
10527
+ const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
10528
+ deleteCookieOnAllDomains(name, window.location.hostname);
10529
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;Domain=${getParentDomain(window.location.hostname)}`;
10530
+ }
10531
+ }
10532
+ /**
10533
+ * Check if user is currently logged in
10534
+ * @param tokenKey - The localStorage key for the auth token (default: 'axd_token')
10535
+ * @returns True if logged in, false otherwise
10536
+ */
10537
+ function isLoggedIn(tokenKey = "axd_token") {
10538
+ return !!localStorage.getItem(tokenKey);
10539
+ }
10540
+ /**
10541
+ * Extract platform/tenant key from URL
10542
+ * @param url - The URL to parse
10543
+ * @param pattern - RegExp pattern to match platform key (default matches /platform/[key]/...)
10544
+ * @returns The platform key or null if not found
10545
+ */
10546
+ function getPlatformKey(url, pattern = /\/platform\/([^/]+)/) {
10547
+ const match = url.match(pattern);
10548
+ return match ? match[1] : null;
10549
+ }
10550
+ /**
10551
+ * Redirect to authentication SPA for login/logout
10552
+ *
10553
+ * This function handles the complete authentication flow:
10554
+ * - Clears localStorage and cookies on logout
10555
+ * - Saves redirect path for post-auth return
10556
+ * - Handles iframe scenarios
10557
+ * - Syncs logout across multiple SPAs via cookies
10558
+ *
10559
+ * @param options - Configuration options for the redirect
10560
+ *
10561
+ * @example
10562
+ * ```tsx
10563
+ * // Basic usage
10564
+ * redirectToAuthSpa({
10565
+ * authUrl: 'https://auth.example.com',
10566
+ * appName: 'mentor',
10567
+ * });
10568
+ *
10569
+ * // With logout
10570
+ * redirectToAuthSpa({
10571
+ * authUrl: 'https://auth.example.com',
10572
+ * appName: 'mentor',
10573
+ * logout: true,
10574
+ * platformKey: 'my-tenant',
10575
+ * });
10576
+ *
10577
+ * // With custom redirect
10578
+ * redirectToAuthSpa({
10579
+ * authUrl: 'https://auth.example.com',
10580
+ * appName: 'mentor',
10581
+ * redirectTo: '/dashboard',
10582
+ * platformKey: 'my-tenant',
10583
+ * });
10584
+ * ```
10585
+ */
10586
+ async function redirectToAuthSpa(options) {
10587
+ const { redirectTo, platformKey, logout = false, saveRedirect = true, authUrl, appName, queryParams = {
10588
+ app: "app",
10589
+ redirectTo: "redirect-to",
10590
+ tenant: "tenant",
10591
+ }, redirectPathStorageKey = "redirect_to", cookieNames = {
10592
+ currentTenant: "ibl_current_tenant",
10593
+ userData: "ibl_user_data",
10594
+ tenant: "ibl_tenant",
10595
+ logoutTimestamp: "ibl_logout_timestamp",
10596
+ }, } = options;
10597
+ // Clear localStorage
10598
+ localStorage.clear();
10599
+ if (logout) {
10600
+ // Delete authentication cookies for cross-SPA synchronization
10601
+ const currentDomain = window.location.hostname;
10602
+ if (cookieNames.currentTenant) {
10603
+ deleteCookieOnAllDomains(cookieNames.currentTenant, currentDomain);
10604
+ }
10605
+ if (cookieNames.userData) {
10606
+ deleteCookieOnAllDomains(cookieNames.userData, currentDomain);
10607
+ }
10608
+ if (cookieNames.tenant) {
10609
+ deleteCookieOnAllDomains(cookieNames.tenant, currentDomain);
10610
+ }
10611
+ // Set logout timestamp cookie to trigger logout on other SPAs
10612
+ if (cookieNames.logoutTimestamp) {
10613
+ setCookieForAuth(cookieNames.logoutTimestamp, Date.now().toString());
10614
+ }
10615
+ }
10616
+ // Handle iframe scenario
10617
+ if (isInIframe()) {
10618
+ console.log("[redirectToAuthSpa]: sending authExpired to parent");
10619
+ sendMessageToParentWebsite({ authExpired: true });
10620
+ return;
10621
+ }
10622
+ const redirectPath = redirectTo !== null && redirectTo !== void 0 ? redirectTo : `${window.location.pathname}${window.location.search}`;
10623
+ // Never save sso-login routes as redirect paths
10624
+ if (!redirectPath.startsWith("/sso-login") && saveRedirect) {
10625
+ window.localStorage.setItem(redirectPathStorageKey, redirectPath);
10626
+ }
10627
+ const platform = platformKey !== null && platformKey !== void 0 ? platformKey : getPlatformKey(redirectPath);
10628
+ const redirectToUrl = `${window.location.origin}`;
10629
+ let authRedirectUrl = `${authUrl}/login?${queryParams.app}=${appName}`;
10630
+ authRedirectUrl += `&${queryParams.redirectTo}=${redirectToUrl}`;
10631
+ if (platform) {
10632
+ authRedirectUrl += `&${queryParams.tenant}=${platform}`;
10633
+ }
10634
+ if (logout) {
10635
+ authRedirectUrl += "&logout=1";
10636
+ }
10637
+ // Small delay for any pending operations
10638
+ await new Promise((resolve) => setTimeout(resolve, 100));
10639
+ // Redirect to auth SPA
10640
+ window.location.href = authRedirectUrl;
10641
+ }
10642
+ /**
10643
+ * Get the URL for joining a tenant (sign up flow)
10644
+ * @param authUrl - Auth SPA base URL
10645
+ * @param tenantKey - Tenant to join
10646
+ * @param redirectUrl - URL to redirect to after joining (defaults to current URL)
10647
+ * @returns The join URL
10648
+ */
10649
+ function getAuthSpaJoinUrl(authUrl, tenantKey, redirectUrl) {
10650
+ const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
10651
+ if (!resolvedTenant) {
10652
+ return "";
10653
+ }
10654
+ const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
10655
+ const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
10656
+ return joinUrl;
10657
+ }
10658
+ /**
10659
+ * Redirect to auth SPA's join/signup page for a specific tenant
10660
+ * @param authUrl - Auth SPA base URL
10661
+ * @param tenantKey - Tenant to join
10662
+ * @param redirectUrl - URL to redirect to after joining (defaults to current URL)
10663
+ */
10664
+ function redirectToAuthSpaJoinTenant(authUrl, tenantKey, redirectUrl) {
10665
+ const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
10666
+ if (!resolvedTenant) {
10667
+ console.log("[auth-redirect] Missing tenant key for join", {
10668
+ tenantKey,
10669
+ redirectUrl,
10670
+ });
10671
+ redirectToAuthSpa({
10672
+ authUrl,
10673
+ appName: "app", // Will need to be configured
10674
+ redirectTo: redirectUrl,
10675
+ });
10676
+ return;
10677
+ }
10678
+ const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
10679
+ const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
10680
+ window.location.href = joinUrl;
10681
+ }
10682
+ /**
10683
+ * Handle user logout
10684
+ *
10685
+ * This function:
10686
+ * - Clears localStorage (preserving tenant)
10687
+ * - Sets logout timestamp cookie for cross-SPA sync
10688
+ * - Clears all cookies
10689
+ * - Redirects to auth SPA logout endpoint
10690
+ *
10691
+ * @param options - Logout configuration options
10692
+ *
10693
+ * @example
10694
+ * ```tsx
10695
+ * handleLogout({
10696
+ * authUrl: 'https://auth.example.com',
10697
+ * redirectUrl: 'https://app.example.com',
10698
+ * });
10699
+ * ```
10700
+ */
10701
+ function handleLogout(options) {
10702
+ const { redirectUrl = window.location.origin, authUrl, tenantStorageKey = "tenant", logoutTimestampCookie = "ibl_logout_timestamp", callback, } = options;
10703
+ const tenant = window.localStorage.getItem(tenantStorageKey);
10704
+ window.localStorage.clear();
10705
+ if (tenant) {
10706
+ window.localStorage.setItem(tenantStorageKey, tenant);
10707
+ }
10708
+ // Set logout timestamp cookie to trigger logout on other SPAs
10709
+ setCookieForAuth(logoutTimestampCookie, Date.now().toString());
10710
+ clearCookies();
10711
+ callback === null || callback === void 0 ? void 0 : callback();
10712
+ if (!isInIframe()) {
10713
+ window.location.href = `${authUrl}/logout?redirect-to=${redirectUrl}${tenant ? "&tenant=" + tenant : ""}`;
10714
+ }
10715
+ }
10716
+
10308
10717
  const useExternalPricingPlan = ({ pricingModalData, userEmail, }) => {
10309
10718
  const pricingBoxIframeRef = useRef(null);
10310
10719
  const getIFrameReadyData = async () => {
@@ -10436,6 +10845,8 @@ function jwtDecode(token, options) {
10436
10845
  * - Polls for cookie changes every 2 seconds
10437
10846
  * - Refreshes the page when cross-SPA changes are detected
10438
10847
  * - Listens for storage events to update cookies when localStorage changes
10848
+ * - Monitors logout timestamp cookie to trigger cross-SPA logout
10849
+ * - Automatically logs out when another SPA initiates logout
10439
10850
  *
10440
10851
  * For React Native:
10441
10852
  * - Relies on AsyncStorage only (handled by StorageService)
@@ -10445,7 +10856,7 @@ function jwtDecode(token, options) {
10445
10856
  /**
10446
10857
  * Check if we're running in a web browser environment
10447
10858
  */
10448
- const isWeb = () => {
10859
+ const isWeb$1 = () => {
10449
10860
  return typeof window !== "undefined" && typeof document !== "undefined";
10450
10861
  };
10451
10862
  /**
@@ -10455,6 +10866,7 @@ const COOKIE_KEYS = {
10455
10866
  CURRENT_TENANT: "ibl_current_tenant",
10456
10867
  USER_DATA: "ibl_user_data",
10457
10868
  TENANT: "ibl_tenant",
10869
+ LOGOUT_TIMESTAMP: "ibl_logout_timestamp",
10458
10870
  };
10459
10871
  /**
10460
10872
  * Get the base domain for cookie sharing
@@ -10465,7 +10877,7 @@ const COOKIE_KEYS = {
10465
10877
  */
10466
10878
  const getBaseDomain = () => {
10467
10879
  // Must check for window existence before accessing it
10468
- if (!isWeb())
10880
+ if (!isWeb$1())
10469
10881
  return "";
10470
10882
  const hostname = window.location.hostname;
10471
10883
  // For localhost or IP addresses, use as-is
@@ -10494,7 +10906,7 @@ const CookieUtils = {
10494
10906
  * Uses SameSite=None for cross-subdomain sharing and base domain
10495
10907
  */
10496
10908
  set(name, value, days = 365) {
10497
- if (!isWeb())
10909
+ if (!isWeb$1())
10498
10910
  return;
10499
10911
  const expires = new Date();
10500
10912
  expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
@@ -10506,7 +10918,7 @@ const CookieUtils = {
10506
10918
  * Get a cookie value by name
10507
10919
  */
10508
10920
  get(name) {
10509
- if (!isWeb())
10921
+ if (!isWeb$1())
10510
10922
  return null;
10511
10923
  const nameEQ = name + "=";
10512
10924
  const cookies = document.cookie.split(";");
@@ -10525,7 +10937,7 @@ const CookieUtils = {
10525
10937
  * Uses same domain logic as set() to ensure proper deletion
10526
10938
  */
10527
10939
  delete(name) {
10528
- if (!isWeb())
10940
+ if (!isWeb$1())
10529
10941
  return;
10530
10942
  const baseDomain = getBaseDomain();
10531
10943
  const domainAttr = baseDomain ? `;domain=${baseDomain}` : "";
@@ -10538,8 +10950,7 @@ const CookieUtils = {
10538
10950
  * On React Native, this is a no-op
10539
10951
  */
10540
10952
  async function syncAuthToCookies(storageService) {
10541
- console.log("##################### syncing auth to cookie");
10542
- if (!isWeb())
10953
+ if (!isWeb$1())
10543
10954
  return;
10544
10955
  try {
10545
10956
  const currentTenant = await storageService.getItem(LOCAL_STORAGE_KEYS.CURRENT_TENANT);
@@ -10568,13 +10979,23 @@ async function syncAuthToCookies(storageService) {
10568
10979
  console.error("[syncAuthToCookies] Error syncing to cookies:", error);
10569
10980
  }
10570
10981
  }
10982
+ /**
10983
+ * Clear current tenant cookie only (web only)
10984
+ * Should be called before tenant switching
10985
+ * On React Native, this is a no-op
10986
+ */
10987
+ function clearCurrentTenantCookie() {
10988
+ if (!isWeb$1())
10989
+ return;
10990
+ CookieUtils.delete(COOKIE_KEYS.CURRENT_TENANT);
10991
+ }
10571
10992
  /**
10572
10993
  * Clear all authentication cookies (web only)
10573
10994
  * Should be called on logout
10574
10995
  * On React Native, this is a no-op
10575
10996
  */
10576
10997
  function clearAuthCookies() {
10577
- if (!isWeb())
10998
+ if (!isWeb$1())
10578
10999
  return;
10579
11000
  CookieUtils.delete(COOKIE_KEYS.CURRENT_TENANT);
10580
11001
  CookieUtils.delete(COOKIE_KEYS.USER_DATA);
@@ -10586,14 +11007,30 @@ function clearAuthCookies() {
10586
11007
  * On React Native, always returns false (no-op)
10587
11008
  */
10588
11009
  async function syncCookiesToLocalStorage(storageService) {
10589
- if (!isWeb())
11010
+ if (!isWeb$1())
10590
11011
  return false;
10591
11012
  try {
10592
11013
  let needsRefresh = false;
10593
11014
  // Check current_tenant
10594
11015
  const cookieCurrentTenant = CookieUtils.get(COOKIE_KEYS.CURRENT_TENANT);
11016
+ console.log("[syncCookiesToLocalStorage] cookieCurrentTenant", cookieCurrentTenant);
10595
11017
  const localCurrentTenant = await storageService.getItem(LOCAL_STORAGE_KEYS.CURRENT_TENANT);
10596
- if (cookieCurrentTenant !== localCurrentTenant) {
11018
+ console.log("[syncCookiesToLocalStorage] localCurrentTenant", localCurrentTenant);
11019
+ // Compare current tenant objects by key field only to avoid false positives from extra fields
11020
+ let currentTenantIsDifferent = false;
11021
+ if (cookieCurrentTenant && localCurrentTenant) {
11022
+ try {
11023
+ const cookieTenant = JSON.parse(cookieCurrentTenant);
11024
+ const localTenant = JSON.parse(localCurrentTenant);
11025
+ // Compare by the tenant key only
11026
+ currentTenantIsDifferent = cookieTenant.key !== localTenant.key;
11027
+ }
11028
+ catch (e) {
11029
+ // If parsing fails, fall back to string comparison
11030
+ currentTenantIsDifferent = cookieCurrentTenant !== localCurrentTenant;
11031
+ }
11032
+ }
11033
+ if (currentTenantIsDifferent) {
10597
11034
  if (cookieCurrentTenant) {
10598
11035
  await storageService.setItem(LOCAL_STORAGE_KEYS.CURRENT_TENANT, cookieCurrentTenant);
10599
11036
  }
@@ -10605,8 +11042,24 @@ async function syncCookiesToLocalStorage(storageService) {
10605
11042
  }
10606
11043
  // Check userData
10607
11044
  const cookieUserData = CookieUtils.get(COOKIE_KEYS.USER_DATA);
11045
+ console.log("[syncCookiesToLocalStorage] cookieUserData", cookieUserData);
10608
11046
  const localUserData = await storageService.getItem(LOCAL_STORAGE_KEYS.USER_DATA);
10609
- if (cookieUserData !== localUserData) {
11047
+ console.log("[syncCookiesToLocalStorage] localUserData", localUserData);
11048
+ // Compare userData objects by user_id to avoid false positives from extra fields
11049
+ let userDataIsDifferent = false;
11050
+ if (cookieUserData && localUserData) {
11051
+ try {
11052
+ const cookieUser = JSON.parse(cookieUserData);
11053
+ const localUser = JSON.parse(localUserData);
11054
+ // Compare by user_id - the primary identifier
11055
+ userDataIsDifferent = cookieUser.user_id !== localUser.user_id;
11056
+ }
11057
+ catch (e) {
11058
+ // If parsing fails, fall back to string comparison
11059
+ userDataIsDifferent = cookieUserData !== localUserData;
11060
+ }
11061
+ }
11062
+ if (userDataIsDifferent) {
10610
11063
  if (cookieUserData) {
10611
11064
  await storageService.setItem(LOCAL_STORAGE_KEYS.USER_DATA, cookieUserData);
10612
11065
  }
@@ -10618,8 +11071,37 @@ async function syncCookiesToLocalStorage(storageService) {
10618
11071
  }
10619
11072
  // Check tenant
10620
11073
  const cookieTenant = CookieUtils.get(COOKIE_KEYS.TENANT);
11074
+ console.log("[syncCookiesToLocalStorage] cookieTenant", cookieTenant);
10621
11075
  const localTenant = await storageService.getItem(LOCAL_STORAGE_KEYS.TENANTS);
10622
- if (cookieTenant !== localTenant) {
11076
+ console.log("[syncCookiesToLocalStorage] localTenant", localTenant);
11077
+ // Compare tenant arrays by checking length and tenant keys
11078
+ let tenantsAreDifferent = false;
11079
+ if (cookieTenant &&
11080
+ localTenant &&
11081
+ cookieTenant !== "[]" &&
11082
+ localTenant !== "[]") {
11083
+ try {
11084
+ const cookieTenantsArray = JSON.parse(cookieTenant);
11085
+ const localTenantsArray = JSON.parse(localTenant);
11086
+ // Check if arrays have same length
11087
+ if (cookieTenantsArray.length !== localTenantsArray.length) {
11088
+ tenantsAreDifferent = true;
11089
+ }
11090
+ else {
11091
+ // Check if all tenant keys match
11092
+ const cookieKeys = new Set(cookieTenantsArray.map((t) => t.key));
11093
+ const localKeys = new Set(localTenantsArray.map((t) => t.key));
11094
+ tenantsAreDifferent =
11095
+ cookieKeys.size !== localKeys.size ||
11096
+ [...cookieKeys].some((key) => !localKeys.has(key));
11097
+ }
11098
+ }
11099
+ catch (e) {
11100
+ // If parsing fails, fall back to string comparison
11101
+ tenantsAreDifferent = cookieTenant !== localTenant;
11102
+ }
11103
+ }
11104
+ if (tenantsAreDifferent) {
10623
11105
  if (cookieTenant) {
10624
11106
  await storageService.setItem(LOCAL_STORAGE_KEYS.TENANTS, cookieTenant);
10625
11107
  }
@@ -10674,6 +11156,7 @@ async function isJwtTokenExpired(storageService) {
10674
11156
  async function validateJwtToken(storageService) {
10675
11157
  try {
10676
11158
  const edxJwtToken = await storageService.getItem(LOCAL_STORAGE_KEYS.EDX_TOKEN_KEY);
11159
+ console.log("[AuthProvider] JWT token ", edxJwtToken);
10677
11160
  const userData = await storageService.getItem(LOCAL_STORAGE_KEYS.USER_DATA);
10678
11161
  if (!edxJwtToken || !userData) {
10679
11162
  return false;
@@ -10696,7 +11179,9 @@ async function validateJwtToken(storageService) {
10696
11179
  function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure, redirectToAuthSpa, hasNonExpiredAuthToken, username, pathname, storageService, skipAuthCheck, token, }) {
10697
11180
  const [isAuthenticating, setIsAuthenticating] = useState(true);
10698
11181
  const [userIsAccessingPublicRoute, setUserIsAccessingPublicRoute] = useState(false);
11182
+ const [initialSyncComplete, setInitialSyncComplete] = useState(false);
10699
11183
  const cookieCheckIntervalRef = useRef(null);
11184
+ const lastLogoutTimestampRef = useRef(null);
10700
11185
  // RTK Query hook for refreshing JWT token
10701
11186
  const [refreshJwtToken] = useLazyRefreshJwtTokenQuery();
10702
11187
  /**
@@ -10705,24 +11190,65 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
10705
11190
  * On React Native, this is a no-op
10706
11191
  */
10707
11192
  useEffect(() => {
10708
- console.log("################******* useEffect sync auth");
10709
- if (!storageService || !isWeb())
11193
+ if (!storageService || !isWeb$1()) {
11194
+ // If no storage service or not web, mark sync as complete immediately
11195
+ setInitialSyncComplete(true);
10710
11196
  return;
11197
+ }
10711
11198
  // Initial sync on mount
10712
11199
  async function initialSync() {
10713
- const needsRefresh = await syncCookiesToLocalStorage(storageService);
10714
- if (needsRefresh) {
10715
- console.log("[AuthProvider] Cookie sync detected changes, refreshing page...");
10716
- window.location.reload();
11200
+ try {
11201
+ // Initialize last known logout timestamp
11202
+ lastLogoutTimestampRef.current = CookieUtils.get(COOKIE_KEYS.LOGOUT_TIMESTAMP);
11203
+ const needsRefresh = await syncCookiesToLocalStorage(storageService);
11204
+ if (needsRefresh) {
11205
+ console.log("[auth-redirect] Cookie sync detected changes, refreshing page");
11206
+ redirectToAuthSpa(undefined, undefined, false, false);
11207
+ }
11208
+ else {
11209
+ await syncAuthToCookies(storageService);
11210
+ }
11211
+ }
11212
+ finally {
11213
+ // Mark initial sync as complete
11214
+ setInitialSyncComplete(true);
10717
11215
  }
10718
11216
  }
10719
11217
  initialSync();
10720
11218
  // Poll for cookie changes every 2 seconds to detect cross-SPA updates
10721
11219
  cookieCheckIntervalRef.current = setInterval(async () => {
11220
+ // Skip cookie sync checks if user is completing SSO at /sso-login
11221
+ const completingSso = new RegExp("^\/sso-login").test(pathname);
11222
+ if (completingSso) {
11223
+ console.log("[AuthProvider] Skipping cookie sync check for public route");
11224
+ return;
11225
+ }
11226
+ // Check for logout timestamp changes (cross-SPA logout detection)
11227
+ const currentLogoutTimestamp = CookieUtils.get(COOKIE_KEYS.LOGOUT_TIMESTAMP);
11228
+ if (currentLogoutTimestamp &&
11229
+ lastLogoutTimestampRef.current &&
11230
+ currentLogoutTimestamp !== lastLogoutTimestampRef.current) {
11231
+ console.log("[auth-redirect] Logout detected from another SPA", {
11232
+ previousTimestamp: lastLogoutTimestampRef.current,
11233
+ newTimestamp: currentLogoutTimestamp,
11234
+ });
11235
+ lastLogoutTimestampRef.current = currentLogoutTimestamp;
11236
+ redirectToAuthSpa(undefined, undefined, true);
11237
+ return;
11238
+ }
10722
11239
  const needsRefresh = await syncCookiesToLocalStorage(storageService);
10723
11240
  if (needsRefresh) {
10724
- console.log("[AuthProvider] Cookie sync detected changes from another SPA, refreshing page...");
10725
- window.location.reload();
11241
+ console.log("[auth-redirect] Cookie sync detected changes from another SPA, refreshing page");
11242
+ let cookieTenantKey;
11243
+ try {
11244
+ const cookieCurrentTenant = JSON.parse(CookieUtils.get(COOKIE_KEYS.CURRENT_TENANT));
11245
+ cookieTenantKey = cookieCurrentTenant.key;
11246
+ }
11247
+ catch (_a) { }
11248
+ redirectToAuthSpa(undefined, cookieTenantKey, false, false);
11249
+ }
11250
+ else {
11251
+ await syncAuthToCookies(storageService);
10726
11252
  }
10727
11253
  }, 2000);
10728
11254
  // Cleanup interval on unmount
@@ -10731,15 +11257,14 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
10731
11257
  clearInterval(cookieCheckIntervalRef.current);
10732
11258
  }
10733
11259
  };
10734
- }, [storageService]);
11260
+ }, [storageService, middleware, pathname]);
10735
11261
  /**
10736
11262
  * Listen for storage events from other tabs/windows (web only)
10737
11263
  * This catches same-SPA changes in different tabs
10738
11264
  * On React Native, this is a no-op
10739
11265
  */
10740
11266
  useEffect(() => {
10741
- console.log("################ useEffect sync auth");
10742
- if (!storageService || !isWeb())
11267
+ if (!storageService || !isWeb$1())
10743
11268
  return;
10744
11269
  const handleStorageChange = async (event) => {
10745
11270
  // Only handle changes to our auth-related keys
@@ -10770,14 +11295,15 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
10770
11295
  if (!hasNonExpiredAuthToken()) {
10771
11296
  const reason = "Auth token expired";
10772
11297
  onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(reason);
10773
- redirectToAuthSpa();
11298
+ console.log("[auth-redirect] Auth token expired");
11299
+ redirectToAuthSpa(undefined, undefined, true);
10774
11300
  return;
10775
11301
  }
10776
11302
  // Check JWT token expiry and force logout for protected routes
10777
11303
  if (isProtectedRoute && storageService) {
10778
11304
  const jwtExpired = await isJwtTokenExpired(storageService);
10779
11305
  if (jwtExpired) {
10780
- console.log("[AuthProvider] JWT token has expired, forcing logout...");
11306
+ console.log("[auth-redirect] JWT token has expired, forcing logout");
10781
11307
  // Clear all auth-related storage keys
10782
11308
  clearAuthCookies();
10783
11309
  const reason = "JWT token expired";
@@ -10799,11 +11325,18 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
10799
11325
  console.log("[AuthProvider] JWT token refreshed successfully");
10800
11326
  }
10801
11327
  else if (result.error) {
10802
- console.error("[AuthProvider] Failed to refresh JWT token:", result.error);
11328
+ console.log("[auth-redirect] Failed to refresh JWT token", {
11329
+ error: result.error,
11330
+ });
11331
+ redirectToAuthSpa(undefined, undefined, true);
11332
+ return;
10803
11333
  }
10804
11334
  }
10805
11335
  catch (refreshError) {
10806
- console.error("[AuthProvider] Failed to refresh JWT token:", refreshError);
11336
+ console.log("[auth-redirect] JWT token refresh error", {
11337
+ error: refreshError,
11338
+ });
11339
+ redirectToAuthSpa(undefined, undefined, true);
10807
11340
  // Continue with auth check even if JWT refresh fails
10808
11341
  }
10809
11342
  }
@@ -10815,12 +11348,18 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
10815
11348
  setIsAuthenticating(false);
10816
11349
  }
10817
11350
  catch (error) {
11351
+ console.error("[AuthProvider] Error performing auth check:", error);
10818
11352
  const errorMessage = error instanceof Error ? error.message : String(error);
10819
11353
  onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(`Unexpected error: ${errorMessage}`);
10820
11354
  redirectToAuthSpa();
10821
11355
  }
10822
11356
  }
10823
11357
  useEffect(() => {
11358
+ // Wait for initial sync to complete before performing auth check
11359
+ if (!initialSyncComplete) {
11360
+ console.log("[useAuthProvider] Waiting for initial sync to complete...");
11361
+ return;
11362
+ }
10824
11363
  async function checkAuth() {
10825
11364
  setIsAuthenticating(true);
10826
11365
  const authRequired = (await determineAuthRequired(middleware, pathname)) && !token;
@@ -10841,7 +11380,7 @@ function useAuthProvider({ middleware = new Map(), onAuthSuccess, onAuthFailure,
10841
11380
  }
10842
11381
  console.log("[useAuthProvider] performing auth check for ", username);
10843
11382
  checkAuth();
10844
- }, [username]);
11383
+ }, [username, initialSyncComplete]);
10845
11384
  return {
10846
11385
  isAuthenticating,
10847
11386
  userIsAccessingPublicRoute,
@@ -10909,7 +11448,7 @@ function AuthProvider({ children, fallback, middleware = new Map(), onAuthSucces
10909
11448
  if (isAuthenticating) {
10910
11449
  return fallback;
10911
11450
  }
10912
- return (jsx(AuthContextProvider, { value: { userIsAccessingPublicRoute, setUserIsAccessingPublicRoute }, children: jsx(Fragment, {}) }));
11451
+ return (jsx(AuthContextProvider, { value: { userIsAccessingPublicRoute, setUserIsAccessingPublicRoute }, children: children }));
10913
11452
  }
10914
11453
 
10915
11454
  /**
@@ -10922,6 +11461,12 @@ function AuthProvider({ children, fallback, middleware = new Map(), onAuthSucces
10922
11461
  * - Tenant metadata retrieval
10923
11462
  * - User-tenant relationship verification
10924
11463
  */
11464
+ /**
11465
+ * Check if we're running in a web browser environment
11466
+ */
11467
+ const isWeb = () => {
11468
+ return typeof window !== "undefined" && typeof document !== "undefined";
11469
+ };
10925
11470
  const TenantContext = createContext(undefined);
10926
11471
  /**
10927
11472
  * Context Provider component that wraps children with tenant context
@@ -10942,7 +11487,7 @@ const useTenantContext = () => useContext(TenantContext);
10942
11487
  * 4. Handles tenant-specific domain redirects
10943
11488
  * 5. Maintains tenant access state
10944
11489
  */
10945
- function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, currentTenant, requestedTenant, saveCurrentTenant, saveUserTenants, handleTenantSwitch, saveVisitingTenant, removeVisitingTenant, saveUserTokens, saveTenant, onAutoJoinUserToTenant, }) {
11490
+ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, currentTenant, requestedTenant, saveCurrentTenant, saveUserTenants, handleTenantSwitch, saveVisitingTenant, removeVisitingTenant, saveUserTokens, saveTenant, onAutoJoinUserToTenant, redirectToAuthSpa, }) {
10946
11491
  const { userIsAccessingPublicRoute, setUserIsAccessingPublicRoute } = useAuthContext();
10947
11492
  const [determineUserPath, setDetermineUserPath] = useState(false);
10948
11493
  const [isLoading, setIsLoading] = React__default.useState(true);
@@ -10953,6 +11498,26 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
10953
11498
  const [getAppToken] = useGetAppTokensMutation();
10954
11499
  const [tenantKey, setTenantKey] = useState(currentTenant);
10955
11500
  const [metadata, setMetadata] = useState({});
11501
+ // Fetch custom domain to get platform_key
11502
+ const currentDomain = typeof window !== "undefined" ? window.location.hostname : "";
11503
+ const { data: customDomainData, isLoading: isLoadingCustomDomain, isError: isCustomDomainError, } = useGetCustomDomainsQuery({ params: { domain: currentDomain } }, { skip: !isWeb() || !currentDomain });
11504
+ // Extract platform_key from custom domain response to use as requestedTenant
11505
+ const customDomainPlatformKey = React__default.useMemo(() => {
11506
+ if (customDomainData &&
11507
+ typeof customDomainData === "object" &&
11508
+ "custom_domains" in customDomainData) {
11509
+ const domains = customDomainData.custom_domains;
11510
+ if (Array.isArray(domains) &&
11511
+ domains.length > 0 &&
11512
+ domains[0].platform_key) {
11513
+ console.log("[TenantProvider] Using platform_key from custom domain:", domains[0].platform_key);
11514
+ return domains[0].platform_key;
11515
+ }
11516
+ }
11517
+ return undefined;
11518
+ }, [customDomainData]);
11519
+ // Use custom domain platform_key as requested tenant if available, otherwise use provided requestedTenant
11520
+ const effectiveRequestedTenant = customDomainPlatformKey || requestedTenant;
10956
11521
  /**
10957
11522
  * Helper function to enhance tenants with platform metadata from user apps
10958
11523
  */
@@ -11012,24 +11577,32 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
11012
11577
  const otherTenant = tenants.find((t) => t.key !== MAIN_TENANT_KEY);
11013
11578
  if (mainTenant &&
11014
11579
  otherTenant &&
11015
- requestedTenant &&
11016
- requestedTenant !== otherTenant.key) {
11580
+ effectiveRequestedTenant &&
11581
+ effectiveRequestedTenant !== otherTenant.key) {
11017
11582
  handleTenantSwitch(otherTenant.key, true);
11018
11583
  console.log("TenantProvider: Triggering tenant switch to newly joined tenant:", otherTenant.key);
11019
11584
  }
11020
11585
  }
11021
- // Enhance tenants with platform metadata
11586
+ // Enhance tenants with platform metadatax
11022
11587
  let enhancedTenants = await enhanceTenants(tenants);
11588
+ // if there is customDomainPlatformKey and user doesn't belong to that tenant, trigger logout
11589
+ if (customDomainPlatformKey) {
11590
+ if (!(enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === customDomainPlatformKey))) {
11591
+ redirectToAuthSpa === null || redirectToAuthSpa === void 0 ? void 0 : redirectToAuthSpa(undefined, undefined, true);
11592
+ return;
11593
+ }
11594
+ }
11023
11595
  saveUserTenants(enhancedTenants);
11024
11596
  // Find requested tenant or fallback to current tenant
11025
- console.log("checking requested tenant", { requestedTenant });
11026
- let tenant = enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === requestedTenant);
11027
- console.log("requested tenant", { tenant });
11597
+ console.log("checking requested tenant", JSON.stringify({
11598
+ requestedTenant: effectiveRequestedTenant,
11599
+ customDomainPlatformKey,
11600
+ }));
11601
+ let tenant = enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === effectiveRequestedTenant);
11602
+ console.log("requested tenant", tenant);
11028
11603
  if (!tenant && !userIsAccessingPublicRoute) {
11029
11604
  setDetermineUserPath(true);
11030
- console.log("no requested tenant, checking current tenant", {
11031
- currentTenant,
11032
- });
11605
+ console.log("no requested tenant, checking current tenant", currentTenant);
11033
11606
  tenant = enhancedTenants === null || enhancedTenants === void 0 ? void 0 : enhancedTenants.find((tenant) => tenant.key === currentTenant);
11034
11607
  console.log("current tenant", { tenant });
11035
11608
  }
@@ -11041,7 +11614,7 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
11041
11614
  tenant = enhancedTenants[0];
11042
11615
  }
11043
11616
  // Fetch and validate tenant metadata
11044
- console.log("fetching tenant metadata", { tenant });
11617
+ console.log("fetching tenant metadata", JSON.stringify(tenant));
11045
11618
  const { data: tenantMetadata } = await fetchTenantMetadata([
11046
11619
  {
11047
11620
  org: (tenant === null || tenant === void 0 ? void 0 : tenant.key) || currentTenant,
@@ -11183,28 +11756,44 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
11183
11756
  }
11184
11757
  // Effect to handle tenant determination when auth state changes
11185
11758
  React__default.useEffect(() => {
11759
+ // Wait for custom domain query to complete (unless skipped or error)
11760
+ const customDomainQuerySkipped = !isWeb() || !currentDomain;
11761
+ if (!customDomainQuerySkipped && isLoadingCustomDomain) {
11762
+ console.log("[TenantProvider] Waiting for custom domain query to complete...");
11763
+ return;
11764
+ }
11765
+ if (isCustomDomainError) {
11766
+ console.warn("[TenantProvider] Custom domain query failed, proceeding without custom domain platform_key");
11767
+ }
11186
11768
  // NOTE: currentTenant comes from local storage.
11187
11769
  // it's the tenant the user is currently signed into.
11188
11770
  console.log("determineWhichTenantToUse", {
11189
11771
  userIsAccessingPublicRoute,
11190
11772
  currentTenant,
11191
11773
  requestedTenant,
11774
+ effectiveRequestedTenant,
11775
+ customDomainPlatformKey,
11192
11776
  });
11193
- if (userIsAccessingPublicRoute && requestedTenant.length === 0) {
11777
+ if (userIsAccessingPublicRoute && effectiveRequestedTenant.length === 0) {
11194
11778
  setIsLoading(false);
11195
11779
  return;
11196
11780
  }
11197
- if (userIsAccessingPublicRoute && requestedTenant.length > 0) {
11781
+ if (userIsAccessingPublicRoute && effectiveRequestedTenant.length > 0) {
11198
11782
  setIsLoading(true);
11199
- setTenantKey(requestedTenant);
11200
- handleLoadTenantMetadata(requestedTenant);
11783
+ setTenantKey(effectiveRequestedTenant);
11784
+ handleLoadTenantMetadata(effectiveRequestedTenant);
11201
11785
  }
11202
11786
  else {
11203
11787
  setIsLoading(true);
11204
11788
  removeVisitingTenant === null || removeVisitingTenant === void 0 ? void 0 : removeVisitingTenant();
11205
11789
  determineWhichTenantToUse();
11206
11790
  }
11207
- }, [userIsAccessingPublicRoute, requestedTenant]);
11791
+ }, [
11792
+ userIsAccessingPublicRoute,
11793
+ effectiveRequestedTenant,
11794
+ isLoadingCustomDomain,
11795
+ isCustomDomainError,
11796
+ ]);
11208
11797
  // Show fallback component during tenant determination
11209
11798
  if (isLoading) {
11210
11799
  return fallback;
@@ -11240,21 +11829,14 @@ function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, curr
11240
11829
  function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redirectToAuthSpa, redirectToMentor, onLoadMentorsPermissions, redirectToNoMentorsPage, redirectToCreateMentor, username, isAdmin, mainTenantKey, requestedMentorId, handleMentorNotFound, forceDetermineMentor = false, }) {
11241
11830
  const [isLoading, setIsLoading] = useState(true);
11242
11831
  const { userIsAccessingPublicRoute } = useAuthContext();
11243
- const { determineUserPath, tenantKey } = useTenantContext();
11832
+ const { determineUserPath, tenantKey, metadata } = useTenantContext();
11244
11833
  const isMainTenant = tenantKey === mainTenantKey;
11245
- console.log("MentorProvider initialized", {
11246
- username,
11247
- isAdmin,
11248
- tenantKey,
11249
- mainTenantKey,
11250
- isMainTenant,
11251
- requestedMentorId,
11252
- determineUserPath,
11253
- });
11834
+ console.log("MentorProvider initialized", username, isAdmin, tenantKey, mainTenantKey, isMainTenant, requestedMentorId, determineUserPath);
11254
11835
  const [fetchMentors] = useLazyGetMentorsQuery();
11255
11836
  const [fetchSeedMentors] = useLazySeedMentorsQuery();
11256
11837
  const [getMentorPublicSettings] = useLazyGetMentorPublicSettingsQuery();
11257
11838
  const [getRbacPermissions] = useGetRbacPermissionsMutation();
11839
+ const [getRecentlyAccessedMentors] = useLazyGetRecentlyAccessedMentorsQuery();
11258
11840
  const QUERY_LIMIT = 10;
11259
11841
  const loadMentorsPermissions = async (mentorDbId) => {
11260
11842
  try {
@@ -11281,21 +11863,69 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11281
11863
  */
11282
11864
  async function determineMentorToRedirectTo() {
11283
11865
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
11284
- console.log("starting mentor determination process", {
11866
+ console.log("starting mentor determination process", JSON.stringify({
11285
11867
  tenantKey,
11286
11868
  username,
11287
11869
  isAdmin,
11288
11870
  isMainTenant,
11289
- });
11871
+ }));
11872
+ console.log("###################### metadata is ", metadata, typeof metadata);
11290
11873
  setIsLoading(true);
11291
11874
  try {
11292
11875
  // Get the user's recent mentors
11293
- console.log("fetching recent mentors", {
11876
+ console.log("fetching recent mentors", JSON.stringify({
11294
11877
  tenantKey,
11295
11878
  username,
11296
11879
  limit: QUERY_LIMIT,
11297
11880
  orderBy: "recently_accessed_at",
11881
+ }));
11882
+ if (typeof metadata === "object" &&
11883
+ metadata.skills_embedded_mentor_name) {
11884
+ const defaultMentor = JSON.parse(metadata.skills_embedded_mentor_name);
11885
+ if (defaultMentor === null || defaultMentor === void 0 ? void 0 : defaultMentor.unique_id) {
11886
+ if (defaultMentor === null || defaultMentor === void 0 ? void 0 : defaultMentor.id) {
11887
+ const rbacPermissions = await loadMentorsPermissions(defaultMentor === null || defaultMentor === void 0 ? void 0 : defaultMentor.id);
11888
+ onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
11889
+ }
11890
+ redirectToMentor(tenantKey, defaultMentor.unique_id);
11891
+ onAuthSuccess === null || onAuthSuccess === void 0 ? void 0 : onAuthSuccess();
11892
+ return;
11893
+ }
11894
+ }
11895
+ // Check for recently accessed mentors (second choice)
11896
+ console.log("checking recently accessed mentors", JSON.stringify({
11897
+ tenantKey,
11898
+ username,
11899
+ }));
11900
+ const recentlyAccessedResult = await getRecentlyAccessedMentors({
11901
+ org: tenantKey,
11902
+ // @ts-ignore
11903
+ userId: username,
11298
11904
  });
11905
+ // @ts-ignore
11906
+ const recentlyAccessedMentors = recentlyAccessedResult.data;
11907
+ const starredMentors = recentlyAccessedMentors === null || recentlyAccessedMentors === void 0 ? void 0 : recentlyAccessedMentors.starred_mentors;
11908
+ let recentMentors = recentlyAccessedMentors === null || recentlyAccessedMentors === void 0 ? void 0 : recentlyAccessedMentors.recent_mentors;
11909
+ if (starredMentors.length > 0) {
11910
+ const starredMentor = starredMentors[0];
11911
+ if (starredMentor === null || starredMentor === void 0 ? void 0 : starredMentor.unique_id) {
11912
+ const rbacPermissions = await loadMentorsPermissions(starredMentor === null || starredMentor === void 0 ? void 0 : starredMentor.id);
11913
+ onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
11914
+ redirectToMentor(tenantKey, starredMentor.unique_id);
11915
+ onAuthSuccess === null || onAuthSuccess === void 0 ? void 0 : onAuthSuccess();
11916
+ return;
11917
+ }
11918
+ }
11919
+ if (recentMentors.length > 0) {
11920
+ const recentMentor = recentMentors[0];
11921
+ if (recentMentor === null || recentMentor === void 0 ? void 0 : recentMentor.unique_id) {
11922
+ const rbacPermissions = await loadMentorsPermissions(recentMentor === null || recentMentor === void 0 ? void 0 : recentMentor.id);
11923
+ onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
11924
+ redirectToMentor(tenantKey, recentMentor.unique_id);
11925
+ onAuthSuccess === null || onAuthSuccess === void 0 ? void 0 : onAuthSuccess();
11926
+ return;
11927
+ }
11928
+ }
11299
11929
  const recentMentorsResult = await fetchMentors({
11300
11930
  // @ts-ignore
11301
11931
  org: tenantKey,
@@ -11303,20 +11933,20 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11303
11933
  limit: QUERY_LIMIT,
11304
11934
  orderBy: "recently_accessed_at",
11305
11935
  });
11306
- const recentMentors = ((_a = recentMentorsResult.data) === null || _a === void 0 ? void 0 : _a.results) || [];
11307
- console.log("recent mentors fetched", {
11936
+ recentMentors = ((_a = recentMentorsResult.data) === null || _a === void 0 ? void 0 : _a.results) || [];
11937
+ console.log("recent mentors fetched", JSON.stringify({
11308
11938
  count: recentMentors.length,
11309
11939
  mentorIds: recentMentors.map((m) => m === null || m === void 0 ? void 0 : m.unique_id),
11310
- });
11940
+ }));
11311
11941
  // Check if we found recent mentors for the tenant
11312
11942
  if (recentMentors.length > 0) {
11313
11943
  const selectedMentorId = ((_b = recentMentors[0]) === null || _b === void 0 ? void 0 : _b.unique_id) || "";
11314
11944
  const selectedMentorDbId = ((_c = recentMentors[0]) === null || _c === void 0 ? void 0 : _c.id) || "";
11315
- console.log("redirecting to recent mentor", {
11945
+ console.log("redirecting to recent mentor", JSON.stringify({
11316
11946
  selectedMentorId,
11317
11947
  mentorName: (_d = recentMentors[0]) === null || _d === void 0 ? void 0 : _d.name,
11318
11948
  tenantKey,
11319
- });
11949
+ }));
11320
11950
  const rbacPermissions = await loadMentorsPermissions(selectedMentorDbId);
11321
11951
  // Select the most recent mentor as current mentor
11322
11952
  onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
@@ -11518,6 +12148,9 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11518
12148
  isAdmin,
11519
12149
  });
11520
12150
  onAuthFailure === null || onAuthFailure === void 0 ? void 0 : onAuthFailure(`Unexpected error: ${errorMessage}`);
12151
+ console.log("[auth-redirect] Unexpected error in mentor provider", {
12152
+ error: errorMessage,
12153
+ });
11521
12154
  redirectToAuthSpa();
11522
12155
  }
11523
12156
  finally {
@@ -11525,11 +12158,7 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11525
12158
  }
11526
12159
  }
11527
12160
  async function mentorExistsInTenant(tenantKey, requestedMentorId) {
11528
- console.log("checking if mentor exists in tenant", {
11529
- tenantKey,
11530
- requestedMentorId,
11531
- username,
11532
- });
12161
+ console.log("checking if mentor exists in tenant", tenantKey, requestedMentorId, username);
11533
12162
  try {
11534
12163
  const response = await getMentorPublicSettings({
11535
12164
  // @ts-ignore
@@ -11558,11 +12187,7 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11558
12187
  // Effect to handle mentor determination when tenant path is determined
11559
12188
  React__default.useEffect(() => {
11560
12189
  async function checkMentor() {
11561
- console.log("starting mentor check process", {
11562
- determineUserPath,
11563
- requestedMentorId,
11564
- tenantKey,
11565
- });
12190
+ console.log("starting mentor check process", determineUserPath, requestedMentorId, tenantKey);
11566
12191
  if (userIsAccessingPublicRoute && !requestedMentorId) {
11567
12192
  setIsLoading(false);
11568
12193
  return;
@@ -11582,11 +12207,11 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11582
12207
  const [mentorExists, requestedMentorDbId] = await mentorExistsInTenant(tenantKey, requestedMentorId);
11583
12208
  // If the mentor does not exist in the tenant, redirect the user to the correct mentor
11584
12209
  if (!mentorExists) {
11585
- console.log("requested mentor not found in tenant", {
12210
+ console.log("requested mentor not found in tenant", JSON.stringify({
11586
12211
  requestedMentorId,
11587
12212
  tenantKey,
11588
12213
  hasCustomHandler: !!handleMentorNotFound,
11589
- });
12214
+ }));
11590
12215
  if (handleMentorNotFound) {
11591
12216
  console.log("calling custom mentor not found handler");
11592
12217
  await handleMentorNotFound();
@@ -11598,9 +12223,7 @@ function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redi
11598
12223
  return;
11599
12224
  }
11600
12225
  else {
11601
- console.log("requested mentor exists in tenant, proceeding", {
11602
- requestedMentorId,
11603
- });
12226
+ console.log("requested mentor exists in tenant, proceeding", requestedMentorId);
11604
12227
  if (requestedMentorDbId) {
11605
12228
  const rbacPermissions = await loadMentorsPermissions(requestedMentorDbId);
11606
12229
  onLoadMentorsPermissions === null || onLoadMentorsPermissions === void 0 ? void 0 : onLoadMentorsPermissions(rbacPermissions);
@@ -12072,7 +12695,7 @@ function formatProdErrorMessage(code) {
12072
12695
 
12073
12696
  const defaultSessionIds = Object.fromEntries(Object.keys(advancedTabsProperties).map((key) => [key, ""]));
12074
12697
  const defaultChatState = Object.fromEntries(Object.keys(advancedTabsProperties).map((key) => [key, []]));
12075
- const initialState = {
12698
+ const initialState$1 = {
12076
12699
  chats: defaultChatState,
12077
12700
  isTyping: false,
12078
12701
  streaming: false,
@@ -12100,7 +12723,7 @@ const initialState = {
12100
12723
  };
12101
12724
  const chatSlice = createSlice({
12102
12725
  name: "chatSliceShared",
12103
- initialState,
12726
+ initialState: initialState$1,
12104
12727
  reducers: {
12105
12728
  setChats: (state, action) => {
12106
12729
  state.chats = action.payload;
@@ -12204,6 +12827,25 @@ const chatSlice = createSlice({
12204
12827
  setShowingSharedChat: (state, action) => {
12205
12828
  state.showingSharedChat = action.payload;
12206
12829
  },
12830
+ updateFileUrlInMessage: (state, action) => {
12831
+ const { fileId, fileUrl } = action.payload;
12832
+ // Update all tabs' messages that have this file
12833
+ Object.keys(state.chats).forEach((tab) => {
12834
+ state.chats[tab] = state.chats[tab].map((message) => {
12835
+ if (message.fileAttachments && message.fileAttachments.length > 0) {
12836
+ const updatedAttachments = message.fileAttachments.map((attachment) => {
12837
+ // Match by fileId
12838
+ if (attachment.fileId === fileId) {
12839
+ return { ...attachment, uploadUrl: fileUrl };
12840
+ }
12841
+ return attachment;
12842
+ });
12843
+ return { ...message, fileAttachments: updatedAttachments };
12844
+ }
12845
+ return message;
12846
+ });
12847
+ });
12848
+ },
12207
12849
  },
12208
12850
  });
12209
12851
  const chatActions = chatSlice.actions;
@@ -12487,6 +13129,61 @@ function useTimeTrackerNative(config) {
12487
13129
  };
12488
13130
  }
12489
13131
 
13132
+ const initialState = {
13133
+ attachedFiles: [],
13134
+ };
13135
+ const filesSlice = createSlice({
13136
+ name: "files",
13137
+ initialState,
13138
+ reducers: {
13139
+ addFiles: (state, action) => {
13140
+ state.attachedFiles = [...state.attachedFiles, ...action.payload];
13141
+ },
13142
+ updateFileProgress: (state, action) => {
13143
+ state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
13144
+ ? { ...file, uploadProgress: action.payload.progress }
13145
+ : file);
13146
+ },
13147
+ updateFileStatus: (state, action) => {
13148
+ state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
13149
+ ? { ...file, uploadStatus: action.payload.status }
13150
+ : file);
13151
+ },
13152
+ updateFileUrl: (state, action) => {
13153
+ state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
13154
+ ? { ...file, uploadUrl: action.payload.uploadUrl }
13155
+ : file);
13156
+ },
13157
+ updateFileMetadata: (state, action) => {
13158
+ state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
13159
+ ? {
13160
+ ...file,
13161
+ fileKey: action.payload.fileKey,
13162
+ fileId: action.payload.fileId,
13163
+ }
13164
+ : file);
13165
+ },
13166
+ updateFileRetryCount: (state, action) => {
13167
+ state.attachedFiles = state.attachedFiles.map((file) => file.id === action.payload.id
13168
+ ? { ...file, retryCount: action.payload.retryCount }
13169
+ : file);
13170
+ },
13171
+ updateFileUrlFromWebSocket: (state, action) => {
13172
+ state.attachedFiles = state.attachedFiles.map((file) => file.fileId === action.payload.fileId
13173
+ ? { ...file, fileUrl: action.payload.fileUrl }
13174
+ : file);
13175
+ },
13176
+ removeFile: (state, action) => {
13177
+ state.attachedFiles = state.attachedFiles.filter((file) => file.id !== action.payload);
13178
+ },
13179
+ clearFiles: (state) => {
13180
+ state.attachedFiles = [];
13181
+ },
13182
+ },
13183
+ });
13184
+ const { addFiles, removeFile, clearFiles, updateFileProgress, updateFileStatus, updateFileUrl, updateFileMetadata, updateFileRetryCount, updateFileUrlFromWebSocket, } = filesSlice.actions;
13185
+ const filesReducer = filesSlice.reducer;
13186
+
12490
13187
  const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, enableHaptics = false, hapticFeedback, store, errorHandler, onStatusChange, onStreamingChange, onStreamingMessageUpdate, WebSocketImpl = WebSocket, redirectToAuthSpa, sendMessageToParentWebsite, on402Error, }) => {
12491
13188
  const dispatch = useDispatch();
12492
13189
  const isWebSocketPaused = useRef(false);
@@ -12522,8 +13219,8 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12522
13219
  }
12523
13220
  if (!wsToken) {
12524
13221
  if (!userIsAccessingPublicRoute) {
12525
- console.error("Token is missing for non-anonymous user");
12526
- redirectToAuthSpa();
13222
+ console.log("[auth-redirect] WebSocket token is missing for non-anonymous user");
13223
+ redirectToAuthSpa(undefined, undefined, true);
12527
13224
  return;
12528
13225
  }
12529
13226
  }
@@ -12578,6 +13275,16 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12578
13275
  onStreamingChange(!!messageData.isTyping);
12579
13276
  return;
12580
13277
  }
13278
+ // Handle file processing success
13279
+ if (messageData.type === "file_processing_success") {
13280
+ console.log("🟣 File processing success:", messageData);
13281
+ dispatch(chatActions.updateFileUrlInMessage({
13282
+ fileId: messageData.file_id,
13283
+ fileName: messageData.file_name,
13284
+ fileUrl: messageData.file_url,
13285
+ }));
13286
+ return;
13287
+ }
12581
13288
  // Handle start of new streaming session
12582
13289
  if (messageData.generation_id &&
12583
13290
  !messageData.data &&
@@ -12673,19 +13380,31 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12673
13380
  };
12674
13381
  };
12675
13382
  const sendMessage = useCallback(async (tab, text, options) => {
12676
- var _a;
13383
+ var _a, _b;
12677
13384
  dispatch(chatActions.setShowingSharedChat(false));
12678
- if (!text.trim())
13385
+ // Allow sending if there's text OR file references
13386
+ if (!text.trim() &&
13387
+ (!(options === null || options === void 0 ? void 0 : options.fileReferences) || options.fileReferences.length === 0)) {
12679
13388
  return;
13389
+ }
12680
13390
  onStatusChange("pending");
12681
13391
  isWebSocketPaused.current = false;
12682
13392
  await triggerHapticFeedback();
13393
+ // Create file attachments array from file references if present
13394
+ const fileAttachments = (_a = options === null || options === void 0 ? void 0 : options.fileReferences) === null || _a === void 0 ? void 0 : _a.map((ref) => ({
13395
+ fileName: ref.file_name,
13396
+ fileType: ref.content_type,
13397
+ fileSize: ref.file_size,
13398
+ uploadUrl: ref.upload_url, // Include URL for display
13399
+ fileId: ref.file_id, // Include fileId for matching WebSocket updates
13400
+ }));
12683
13401
  const userMessage = {
12684
13402
  id: `user-${Date.now()}`,
12685
13403
  role: "user",
12686
13404
  content: text,
12687
13405
  timestamp: new Date().toISOString(),
12688
- visible: (_a = options === null || options === void 0 ? void 0 : options.visible) !== null && _a !== void 0 ? _a : true,
13406
+ visible: (_b = options === null || options === void 0 ? void 0 : options.visible) !== null && _b !== void 0 ? _b : true,
13407
+ fileAttachments,
12689
13408
  };
12690
13409
  // Notify parent to add user message
12691
13410
  // onAddUserMessage?.(tab, userMessage);
@@ -12697,8 +13416,14 @@ const useChat = ({ wsUrl, wsToken, flowConfig, sessionId, stopGenerationWsUrl, e
12697
13416
  flow: flowConfig,
12698
13417
  session_id: sessionId,
12699
13418
  token: wsToken,
12700
- prompt: text,
13419
+ prompt: text || "", // Allow empty prompt when sending files
12701
13420
  };
13421
+ if ((options === null || options === void 0 ? void 0 : options.fileReferences) && options.fileReferences.length > 0) {
13422
+ messageData = {
13423
+ ...messageData,
13424
+ file_references: options.fileReferences,
13425
+ };
13426
+ }
12702
13427
  if (iframeContext.pageContent) {
12703
13428
  messageData = {
12704
13429
  ...messageData,
@@ -12883,7 +13608,7 @@ function useMentorSettings({ mentorId, tenantKey, username }) {
12883
13608
  };
12884
13609
  }
12885
13610
 
12886
- function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, }) {
13611
+ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, }) {
12887
13612
  var _a, _b, _c, _d;
12888
13613
  const dispatch = useDispatch();
12889
13614
  const [createSessionId, { isLoading: isLoadingSessionIds }] = useCreateSessionIdMutation();
@@ -12922,7 +13647,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
12922
13647
  username,
12923
13648
  pathway: mentorId,
12924
13649
  },
12925
- sessionId: sessionIds[activeTab],
13650
+ sessionId: cachedSessionId !== null && cachedSessionId !== void 0 ? cachedSessionId : sessionIds[activeTab],
12926
13651
  activeTab,
12927
13652
  wsToken: token,
12928
13653
  errorHandler,
@@ -12933,6 +13658,49 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
12933
13658
  onStatusChange,
12934
13659
  on402Error,
12935
13660
  });
13661
+ const [getSessionChats, { isError: isErrorGettingSessionChats }] = useLazyGetSessionIdQuery();
13662
+ useEffect(() => {
13663
+ const getChats = async () => {
13664
+ var _a;
13665
+ try {
13666
+ const { data } = await getSessionChats({
13667
+ sessionId: cachedSessionId !== null && cachedSessionId !== void 0 ? cachedSessionId : "",
13668
+ org: tenantKey,
13669
+ share: true,
13670
+ });
13671
+ let previousChats = [];
13672
+ try {
13673
+ previousChats =
13674
+ ((_a = data === null || data === void 0 ? void 0 : data.results) === null || _a === void 0 ? void 0 : _a.map((result) => {
13675
+ return {
13676
+ ...result,
13677
+ role: result.type === "human" ? "user" : "assistant",
13678
+ visible: true,
13679
+ id: new Date().getTime(),
13680
+ content: typeof result.content === "object" &&
13681
+ result.content.length > 0
13682
+ ? result.content[0].text
13683
+ : result.content,
13684
+ };
13685
+ }).reverse()) || [];
13686
+ }
13687
+ catch (error) {
13688
+ console.error(JSON.stringify(error));
13689
+ }
13690
+ dispatch(chatActions.setNewMessages(previousChats));
13691
+ }
13692
+ catch (error) {
13693
+ console.error(JSON.stringify(error));
13694
+ }
13695
+ };
13696
+ if (cachedSessionId) {
13697
+ dispatch(chatActions.updateSessionIds(cachedSessionId));
13698
+ getChats();
13699
+ }
13700
+ }, [cachedSessionId]);
13701
+ useEffect(() => {
13702
+ }, [isErrorGettingSessionChats]);
13703
+ useEffect(() => { }, []);
12936
13704
  const startNewChat = React__default.useCallback(async () => {
12937
13705
  // Reset all chat state
12938
13706
  if (!showingSharedChat) {
@@ -12942,6 +13710,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
12942
13710
  dispatch(chatActions.setStreaming(false));
12943
13711
  dispatch(chatActions.resetCurrentStreamingMessage(undefined));
12944
13712
  dispatch(chatActions.setTools([]));
13713
+ dispatch(clearFiles(undefined));
12945
13714
  if (isPreviewMode) {
12946
13715
  return;
12947
13716
  }
@@ -12952,6 +13721,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
12952
13721
  // @ts-ignore
12953
13722
  requestBody["shareable_link_token"] = mentorShareableToken;
12954
13723
  }
13724
+ console.log("[startNewChat] requestBody", tenantKey, username, JSON.stringify(requestBody));
12955
13725
  const response = await createSessionId({
12956
13726
  org: tenantKey,
12957
13727
  // @ts-ignore
@@ -12959,10 +13729,12 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
12959
13729
  requestBody,
12960
13730
  }).unwrap();
12961
13731
  dispatch(chatActions.updateSessionIds(response.session_id));
12962
- // dispatch(chatActions.setShouldStartNewChat(false));
13732
+ onStartNewChat === null || onStartNewChat === void 0 ? void 0 : onStartNewChat(response.session_id);
12963
13733
  }
12964
13734
  catch (error) {
12965
- errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler("Failed to start new chat");
13735
+ errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler(`Failed to start new chat: ${JSON.stringify(error)}`);
13736
+ console.log("[auth-redirect] Failed to start new chat", JSON.stringify({ error }));
13737
+ redirectToAuthSpa(undefined, undefined, true);
12966
13738
  }
12967
13739
  }, [
12968
13740
  isPreviewMode,
@@ -12976,7 +13748,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
12976
13748
  ]);
12977
13749
  React__default.useEffect(() => {
12978
13750
  if (!showingSharedChat || mentorSettings.allowAnonymous) {
12979
- if (mentorSettings.allowAnonymous !== undefined) {
13751
+ if (mentorSettings.allowAnonymous !== undefined && !cachedSessionId) {
12980
13752
  startNewChat();
12981
13753
  }
12982
13754
  }
@@ -13028,7 +13800,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13028
13800
  }
13029
13801
  }
13030
13802
  catch (error) {
13031
- errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler("Failed to start new chat");
13803
+ errorHandler === null || errorHandler === void 0 ? void 0 : errorHandler(`Failed to start new chat: ${error}`);
13032
13804
  return;
13033
13805
  }
13034
13806
  }
@@ -17918,5 +18690,123 @@ const tenantSchema = z.object({
17918
18690
  });
17919
18691
  const tenantKeySchema = z.string().min(1);
17920
18692
 
17921
- export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, METADATAS, MentorProvider, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, SubscriptionFlow, SubscriptionFlowV2, TOOLS, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, addProtocolToUrl, advancedTabs, advancedTabsProperties, chatActions, chatSliceReducerShared, clearAuthCookies, defaultSessionIds, formatRelativeTime, getInitials, getTimeAgo, getUserName, isAlphaNumeric32, isJSON, loadMetadataConfig, selectActiveChatMessages, selectActiveTab, selectChats, selectCurrentStreamingMessage, selectDocumentFilter, selectIframeContext, selectIsError, selectIsPending, selectIsStopped, selectIsTyping, selectNumberOfActiveChatMessages, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectToken, selectTokenEnabled, selectTools, syncAuthToCookies, tenantKeySchema, tenantSchema, translatePrompt, useAdvancedChat, useAuthContext, useAuthProvider, useChat, useDayJs, useExternalPricingPlan, useMentorSettings, useMentorTools, useProfileImageUpload, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTimeTracker, useTimeTrackerNative, useUserProfileUpdate, userDataSchema };
18693
+ /**
18694
+ * Chat area size constants
18695
+ */
18696
+ const CHAT_AREA_SIZE = {
18697
+ DEFAULT: 672,
18698
+ MIN: 672,
18699
+ MAX: 1024,
18700
+ };
18701
+
18702
+ /**
18703
+ * Extract file information from File object
18704
+ */
18705
+ function getFileInfo(file) {
18706
+ const fileName = file.name;
18707
+ const contentType = file.type || "application/octet-stream";
18708
+ const fileSize = file.size;
18709
+ return {
18710
+ fileName,
18711
+ contentType,
18712
+ fileSize,
18713
+ };
18714
+ }
18715
+ /**
18716
+ * Request presigned S3 URL from backend
18717
+ * Note: This function should be called via the RTK Query mutation hook
18718
+ * This is a helper to show the data flow
18719
+ */
18720
+ async function requestPresignedUrl(sessionId, file, getUploadUrlFn, org, userId) {
18721
+ const { fileName, contentType, fileSize } = getFileInfo(file);
18722
+ return await getUploadUrlFn({
18723
+ org,
18724
+ userId,
18725
+ requestBody: {
18726
+ session_id: sessionId,
18727
+ file_name: fileName,
18728
+ content_type: contentType,
18729
+ file_size: fileSize,
18730
+ },
18731
+ });
18732
+ }
18733
+ /**
18734
+ * Upload file directly to S3 using presigned URL
18735
+ * Uses axios for cross-platform support (web + React Native)
18736
+ */
18737
+ async function uploadToS3(presignedUrl, file, contentType, onProgress) {
18738
+ await axios.put(presignedUrl, file, {
18739
+ headers: {
18740
+ "Content-Type": contentType,
18741
+ },
18742
+ onUploadProgress: (progressEvent) => {
18743
+ if (onProgress && progressEvent.total) {
18744
+ const progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
18745
+ onProgress(progress);
18746
+ }
18747
+ },
18748
+ });
18749
+ }
18750
+ /**
18751
+ * Complete file upload flow:
18752
+ * 1. Request presigned URL
18753
+ * 2. Upload to S3
18754
+ * 3. Return file reference
18755
+ */
18756
+ async function createFileReference(file, sessionId, getUploadUrlFn, org, userId, onProgress) {
18757
+ // Step 1: Get presigned URL
18758
+ const presignedResponse = await requestPresignedUrl(sessionId, file, getUploadUrlFn, org, userId);
18759
+ // Step 2: Upload to S3
18760
+ const { fileName, contentType, fileSize } = getFileInfo(file);
18761
+ await uploadToS3(presignedResponse.upload_url, file, contentType, onProgress);
18762
+ // Step 3: Return file reference
18763
+ return {
18764
+ file_id: presignedResponse.file_id,
18765
+ file_key: presignedResponse.file_key,
18766
+ file_name: fileName,
18767
+ content_type: contentType,
18768
+ file_size: fileSize,
18769
+ };
18770
+ }
18771
+ /**
18772
+ * Upload multiple files and return their references
18773
+ */
18774
+ async function createMultipleFileReferences(files, sessionId, getUploadUrlFn, org, userId, onFileProgress) {
18775
+ const fileReferences = [];
18776
+ for (let i = 0; i < files.length; i++) {
18777
+ const file = files[i];
18778
+ const onProgress = onFileProgress
18779
+ ? (progress) => onFileProgress(i, progress)
18780
+ : undefined;
18781
+ const fileReference = await createFileReference(file, sessionId, getUploadUrlFn, org, userId, onProgress);
18782
+ fileReferences.push(fileReference);
18783
+ }
18784
+ return fileReferences;
18785
+ }
18786
+ /**
18787
+ * Validate file before upload
18788
+ * Returns error message if invalid, null if valid
18789
+ */
18790
+ function validateFile(file, maxSizeBytes, allowedTypes) {
18791
+ var _a;
18792
+ // Check file size
18793
+ if (maxSizeBytes && file.size > maxSizeBytes) {
18794
+ const maxSizeMB = (maxSizeBytes / (1024 * 1024)).toFixed(2);
18795
+ const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
18796
+ return `File size (${fileSizeMB}MB) exceeds maximum allowed size (${maxSizeMB}MB)`;
18797
+ }
18798
+ // Check file type
18799
+ if (allowedTypes && allowedTypes.length > 0) {
18800
+ const fileExtension = (_a = file.name.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
18801
+ const fileType = file.type.toLowerCase();
18802
+ const isTypeAllowed = allowedTypes.some((allowed) => fileType.includes(allowed.toLowerCase()) ||
18803
+ fileExtension === allowed.toLowerCase().replace(".", ""));
18804
+ if (!isTypeAllowed) {
18805
+ return `File type not allowed. Allowed types: ${allowedTypes.join(", ")}`;
18806
+ }
18807
+ }
18808
+ return null;
18809
+ }
18810
+
18811
+ export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, CHAT_AREA_SIZE, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, METADATAS, MentorProvider, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, SubscriptionFlow, SubscriptionFlowV2, TOOLS, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, addFiles, addProtocolToUrl, advancedTabs, advancedTabsProperties, chatActions, chatSliceReducerShared, clearAuthCookies, clearCookies, clearCurrentTenantCookie, clearFiles, createFileReference, createMultipleFileReferences, defaultSessionIds, deleteCookie, deleteCookieOnAllDomains, filesReducer, filesSlice, formatRelativeTime, getAuthSpaJoinUrl, getDomainParts, getFileInfo, getInitials, getParentDomain, getPlatformKey, getTimeAgo, getUserName, handleLogout, isAlphaNumeric32, isInIframe, isJSON, isLoggedIn, loadMetadataConfig, redirectToAuthSpa, redirectToAuthSpaJoinTenant, removeFile, requestPresignedUrl, selectActiveChatMessages, selectActiveTab, selectChats, selectCurrentStreamingMessage, selectDocumentFilter, selectIframeContext, selectIsError, selectIsPending, selectIsStopped, selectIsTyping, selectNumberOfActiveChatMessages, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectToken, selectTokenEnabled, selectTools, sendMessageToParentWebsite, setCookieForAuth, syncAuthToCookies, tenantKeySchema, tenantSchema, translatePrompt, updateFileMetadata, updateFileProgress, updateFileRetryCount, updateFileStatus, updateFileUrl, updateFileUrlFromWebSocket, uploadToS3, useAdvancedChat, useAuthContext, useAuthProvider, useChat, useDayJs, useExternalPricingPlan, useMentorSettings, useMentorTools, useProfileImageUpload, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTimeTracker, useTimeTrackerNative, useUserProfileUpdate, userDataSchema, validateFile };
17922
18812
  //# sourceMappingURL=index.esm.js.map