@iblai/web-utils 1.10.11 → 1.11.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.
package/dist/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import React__default, { useState, useRef, useEffect, useLayoutEffect, useCallback, createContext, useContext, useMemo } from 'react';
2
+ import React__default, { useRef, useEffect, useState, useLayoutEffect, useCallback, createContext, useContext, useMemo } from 'react';
3
3
  import { useRenewSubscriptionMutation, useLazyGetUserAppsQuery, useCreateStripeCustomerPortalMutation, useLazyGetFreeUsageCountQuery, useGetTenantMetadataQuery, useLazyGetStripeContextQuery, useUpdateUserTrialStatusMutation, useLazyGetAccountBillingInfoQuery, useLazyRefreshJwtTokenQuery, useLazyGetUserTenantsQuery, useJoinTenantMutation, useLazyGetTenantMetadataQuery, useGetAppTokensMutation, useGetCustomDomainsQuery, useGetRbacPermissionsMutation, useLazyGetMentorsQuery, useLazySeedMentorsQuery, useLazyGetMentorPublicSettingsQuery, useLazyGetRecentlyAccessedMentorsQuery, useGetMentorSettingsQuery, useGetMentorPublicSettingsQuery, useGetMemsearchStatusQuery, useCreateSessionIdMutation, useLazyGetSessionIdQuery, useLazyGetSharedSessionIdQuery, useEditSessionMutation, useGetToolsQuery, useGetPromptsSearchQuery, useUpdateUserAccountMutation, useGetUserMetadataQuery, useUploadProfileImageMutation, useRemoveProfileImageMutation, useLazyGetStripePricingPageSessionQuery, useGetFileUploadUrlMutation, useGetDisclaimersQuery, useAgreeToDisclaimerMutation, useLazyGetGuidedPromptsQuery, useAudioToTextMutation } from '@iblai/data-layer';
4
4
  import { toast } from 'sonner';
5
5
  import { platform } from '@tauri-apps/plugin-os';
@@ -7,6 +7,535 @@ import { jsx, Fragment } from 'react/jsx-runtime';
7
7
  import { MentorVisibilityEnum } from '@iblai/iblai-api';
8
8
  import axios from 'axios';
9
9
 
10
+ /**
11
+ * Authentication and Authorization Utilities
12
+ *
13
+ * This module provides utility functions for handling authentication flows,
14
+ * including redirects to auth SPA, cookie management, and session handling.
15
+ */
16
+ /**
17
+ * Check if the current window is inside an iframe
18
+ * @returns {boolean} True if running in an iframe, false otherwise
19
+ */
20
+ function isInIframe() {
21
+ if (typeof window === "undefined") {
22
+ return false;
23
+ }
24
+ return (window === null || window === void 0 ? void 0 : window.self) !== (window === null || window === void 0 ? void 0 : window.top);
25
+ }
26
+ /**
27
+ * Send a message to the parent website (when in iframe)
28
+ * @param payload - The data to send to parent window
29
+ */
30
+ function sendMessageToParentWebsite(payload) {
31
+ window.parent.postMessage(payload, "*");
32
+ }
33
+ /**
34
+ * Delete a cookie with specific name, path, and domain
35
+ * @param name - Cookie name
36
+ * @param path - Cookie path
37
+ * @param domain - Cookie domain
38
+ */
39
+ function deleteCookie(name, path, domain) {
40
+ const expires = "expires=Thu, 01 Jan 1970 00:00:00 UTC;";
41
+ const cookieValue = `${name}=;`;
42
+ const pathValue = path ? `path=${path};` : "";
43
+ const domainValue = domain ? `domain=${domain};` : "";
44
+ document.cookie = cookieValue + expires + pathValue + domainValue;
45
+ }
46
+ /**
47
+ * Get all possible domain parts for cookie deletion
48
+ * @param domain - The domain to split
49
+ * @returns Array of domain parts
50
+ * @example
51
+ * getDomainParts('app.example.com') // returns ['app.example.com', 'example.com', 'com']
52
+ */
53
+ function getDomainParts(domain) {
54
+ const parts = domain.split(".");
55
+ const domains = [];
56
+ for (let i = parts.length - 1; i >= 0; i--) {
57
+ domains.push(parts.slice(i).join("."));
58
+ }
59
+ return domains;
60
+ }
61
+ /**
62
+ * Delete a cookie across all possible domain variations
63
+ * @param name - Cookie name to delete
64
+ * @param childDomain - The current domain
65
+ */
66
+ function deleteCookieOnAllDomains(name, childDomain) {
67
+ getDomainParts(childDomain).forEach((domainPart) => {
68
+ deleteCookie(name, "/", domainPart);
69
+ deleteCookie(name, "", domainPart);
70
+ });
71
+ }
72
+ /**
73
+ * Get the parent domain from a given domain
74
+ * @param domain - The domain to process
75
+ * @returns The parent domain (e.g., 'app.example.com' → '.example.com')
76
+ */
77
+ function getParentDomain(domain) {
78
+ if (!domain) {
79
+ return "";
80
+ }
81
+ const parts = domain.split(".");
82
+ return parts.length > 1 ? `.${parts.slice(-2).join(".")}` : domain;
83
+ }
84
+ /**
85
+ * Set a cookie for authentication with cross-domain support
86
+ * @param name - Cookie name
87
+ * @param value - Cookie value
88
+ * @param days - Number of days until expiration (default: 365)
89
+ */
90
+ function setCookieForAuth(name, value, days = 365) {
91
+ const expires = new Date();
92
+ expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
93
+ const hostname = window.location.hostname;
94
+ let baseDomain = hostname;
95
+ // Calculate base domain (skip for localhost and IP addresses)
96
+ if (hostname !== "localhost" && !/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
97
+ const parts = hostname.split(".");
98
+ if (parts.length > 2) {
99
+ baseDomain = `.${parts.slice(-2).join(".")}`;
100
+ }
101
+ }
102
+ const domainAttr = baseDomain ? `;domain=${baseDomain}` : "";
103
+ document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/;SameSite=None;Secure${domainAttr}`;
104
+ }
105
+ /**
106
+ * Clear all cookies for the current domain
107
+ */
108
+ function clearCookies() {
109
+ const cookies = document.cookie.split(";");
110
+ for (let i = 0; i < cookies.length; i++) {
111
+ const cookie = cookies[i];
112
+ const eqPos = cookie.indexOf("=");
113
+ const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
114
+ deleteCookieOnAllDomains(name, window.location.hostname);
115
+ document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;Domain=${getParentDomain(window.location.hostname)}`;
116
+ }
117
+ }
118
+ /**
119
+ * Get the value of a cookie by name
120
+ * @param name - Cookie name
121
+ * @returns The cookie value or null if not found
122
+ */
123
+ function getCookieValue(name) {
124
+ const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
125
+ return match ? decodeURIComponent(match[1]) : null;
126
+ }
127
+ /**
128
+ * Check if user is currently logged in
129
+ * @param tokenKey - The localStorage key for the auth token (default: 'axd_token')
130
+ * @returns True if logged in, false otherwise
131
+ */
132
+ function isLoggedIn(tokenKey = "axd_token") {
133
+ return !!localStorage.getItem(tokenKey);
134
+ }
135
+ /**
136
+ * Extract platform/tenant key from URL
137
+ * @param url - The URL to parse
138
+ * @param pattern - RegExp pattern to match platform key (default matches /platform/[key]/...)
139
+ * @returns The platform key or null if not found
140
+ */
141
+ function getPlatformKey(url, pattern = /\/platform\/([^/]+)/) {
142
+ const match = url.match(pattern);
143
+ return match ? match[1] : null;
144
+ }
145
+ /**
146
+ * Redirect to authentication SPA for login/logout
147
+ *
148
+ * This function handles the complete authentication flow:
149
+ * - Clears localStorage and cookies on logout
150
+ * - Saves redirect path for post-auth return
151
+ * - Handles iframe scenarios
152
+ * - Syncs logout across multiple SPAs via cookies
153
+ *
154
+ * @param options - Configuration options for the redirect
155
+ *
156
+ * @example
157
+ * ```tsx
158
+ * // Basic usage
159
+ * redirectToAuthSpa({
160
+ * authUrl: 'https://auth.example.com',
161
+ * appName: 'mentor',
162
+ * });
163
+ *
164
+ * // With logout
165
+ * redirectToAuthSpa({
166
+ * authUrl: 'https://auth.example.com',
167
+ * appName: 'mentor',
168
+ * logout: true,
169
+ * platformKey: 'my-tenant',
170
+ * });
171
+ *
172
+ * // With custom redirect
173
+ * redirectToAuthSpa({
174
+ * authUrl: 'https://auth.example.com',
175
+ * appName: 'mentor',
176
+ * redirectTo: '/dashboard',
177
+ * platformKey: 'my-tenant',
178
+ * });
179
+ * ```
180
+ */
181
+ async function redirectToAuthSpa(options) {
182
+ const { redirectTo, platformKey, logout = false, saveRedirect = true, authUrl, appName, queryParams = {
183
+ app: "app",
184
+ redirectTo: "redirect-to",
185
+ tenant: "tenant",
186
+ }, redirectPathStorageKey = "redirect_to", cookieNames = {
187
+ currentTenant: "ibl_current_tenant",
188
+ userData: "ibl_user_data",
189
+ tenant: "ibl_tenant",
190
+ logoutTimestamp: "ibl_logout_timestamp",
191
+ loginTimestamp: "ibl_login_timestamp",
192
+ tenantSwitching: "ibl_tenant_switching",
193
+ }, hasNonExpiredAuthToken, isOffline, preserveTokenKey, authRedirectProxy, isNativeApp, scheme = "iblai-mentor", forceRedirect = false, } = options;
194
+ console.log("[redirectToAuthSpa] starting redirect to auth spa", redirectTo, platformKey, logout, saveRedirect, forceRedirect);
195
+ // Skip if a tenant switch is already in progress
196
+ if (!forceRedirect &&
197
+ cookieNames.tenantSwitching &&
198
+ document.cookie.includes(cookieNames.tenantSwitching)) {
199
+ console.log("[redirectToAuthSpa] Tenant switch in progress, skipping redirect");
200
+ return;
201
+ }
202
+ // Skip if a login occurred after the last logout (login takes precedence)
203
+ // but only if this app actually has a valid auth token
204
+ if (!forceRedirect &&
205
+ hasNonExpiredAuthToken &&
206
+ cookieNames.loginTimestamp &&
207
+ cookieNames.logoutTimestamp) {
208
+ const loginTs = getCookieValue(cookieNames.loginTimestamp);
209
+ const logoutTs = getCookieValue(cookieNames.logoutTimestamp);
210
+ const hasValidToken = hasNonExpiredAuthToken();
211
+ console.log("[redirectToAuthSpa] Login/logout timestamp check", {
212
+ loginTs,
213
+ logoutTs,
214
+ hasValidToken,
215
+ loginAhead: loginTs && logoutTs ? Number(loginTs) > Number(logoutTs) : false,
216
+ });
217
+ if (!forceRedirect &&
218
+ hasValidToken &&
219
+ loginTs &&
220
+ logoutTs &&
221
+ Number(loginTs) > Number(logoutTs)) {
222
+ console.log("[redirectToAuthSpa] Login timestamp is ahead of logout timestamp, skipping redirect", { loginTs, logoutTs });
223
+ return;
224
+ }
225
+ }
226
+ // Skip redirect in offline mode
227
+ if (isOffline === null || isOffline === void 0 ? void 0 : isOffline()) {
228
+ console.log("[redirectToAuthSpa] Skipping redirect - offline mode");
229
+ return;
230
+ }
231
+ // Preserve a token before clearing localStorage if requested
232
+ const preservedToken = preserveTokenKey
233
+ ? window.localStorage.getItem(preserveTokenKey)
234
+ : null;
235
+ console.log("[redirectToAuthSpa] clearing local storage");
236
+ localStorage.clear();
237
+ if (logout || isInIframe()) {
238
+ // Delete authentication cookies for cross-SPA synchronization
239
+ const currentDomain = window.location.hostname;
240
+ if (cookieNames.currentTenant) {
241
+ deleteCookieOnAllDomains(cookieNames.currentTenant, currentDomain);
242
+ }
243
+ if (cookieNames.userData) {
244
+ deleteCookieOnAllDomains(cookieNames.userData, currentDomain);
245
+ }
246
+ if (cookieNames.tenant) {
247
+ deleteCookieOnAllDomains(cookieNames.tenant, currentDomain);
248
+ }
249
+ // Set logout timestamp cookie to trigger logout on other SPAs
250
+ if (cookieNames.logoutTimestamp && !isInIframe()) {
251
+ setCookieForAuth(cookieNames.logoutTimestamp, Date.now().toString());
252
+ }
253
+ }
254
+ // Handle iframe scenario
255
+ if (isInIframe()) {
256
+ console.log("[redirectToAuthSpa]: sending authExpired to parent");
257
+ sendMessageToParentWebsite({ authExpired: true });
258
+ return;
259
+ }
260
+ const redirectPath = redirectTo !== null && redirectTo !== void 0 ? redirectTo : `${window.location.pathname}${window.location.search}`;
261
+ // Never save sso-login routes as redirect paths
262
+ if (!redirectPath.startsWith("/sso-login") &&
263
+ !redirectPath.startsWith("/sso-login-complete") &&
264
+ saveRedirect) {
265
+ window.localStorage.setItem(redirectPathStorageKey, redirectPath);
266
+ }
267
+ const platform = platformKey !== null && platformKey !== void 0 ? platformKey : getPlatformKey(redirectPath);
268
+ const redirectToUrl = window.location.origin;
269
+ let authRedirectUrl = `${authUrl}/login?${queryParams.app}=${appName}`;
270
+ authRedirectUrl += `&${queryParams.redirectTo}=${redirectToUrl}`;
271
+ if (platform) {
272
+ authRedirectUrl += `&${queryParams.tenant}=${platform}`;
273
+ }
274
+ if (logout) {
275
+ authRedirectUrl += "&logout=1";
276
+ }
277
+ if ((isNativeApp === null || isNativeApp === void 0 ? void 0 : isNativeApp()) && scheme) {
278
+ authRedirectUrl += `&scheme=${scheme}`;
279
+ }
280
+ // Small delay for any pending operations
281
+ await new Promise((resolve) => setTimeout(resolve, 100));
282
+ if (isNativeApp === null || isNativeApp === void 0 ? void 0 : isNativeApp()) {
283
+ // On native apps (e.g., Tauri), pass preserved token and navigate directly
284
+ if (preservedToken && preserveTokenKey) {
285
+ authRedirectUrl += `&token=${encodeURIComponent(preservedToken)}`;
286
+ console.log("[redirectToAuthSpa] Added preserved token to auth URL");
287
+ }
288
+ console.log("[redirectToAuthSpa] Native app, navigating to auth URL:", authRedirectUrl);
289
+ window.location.href = authRedirectUrl;
290
+ }
291
+ else if (authRedirectProxy) {
292
+ // Use auth redirect proxy endpoint
293
+ window.location.href = `${authRedirectProxy}?to=${encodeURIComponent(authRedirectUrl)}`;
294
+ }
295
+ else {
296
+ window.location.href = authRedirectUrl;
297
+ }
298
+ }
299
+ /**
300
+ * Get the URL for joining a tenant (sign up flow)
301
+ * @param authUrl - Auth SPA base URL
302
+ * @param tenantKey - Tenant to join
303
+ * @param redirectUrl - URL to redirect to after joining (defaults to current URL)
304
+ * @returns The join URL
305
+ */
306
+ function getAuthSpaJoinUrl(authUrl, tenantKey, redirectUrl) {
307
+ const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
308
+ if (!resolvedTenant) {
309
+ return "";
310
+ }
311
+ const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
312
+ const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
313
+ return joinUrl;
314
+ }
315
+ /**
316
+ * Redirect to auth SPA's join/signup page for a specific tenant
317
+ * @param authUrl - Auth SPA base URL
318
+ * @param tenantKey - Tenant to join
319
+ * @param redirectUrl - URL to redirect to after joining (defaults to current URL)
320
+ */
321
+ function redirectToAuthSpaJoinTenant(authUrl, tenantKey, redirectUrl) {
322
+ const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
323
+ if (!resolvedTenant) {
324
+ console.log("[auth-redirect] Missing tenant key for join", {
325
+ tenantKey,
326
+ redirectUrl,
327
+ });
328
+ redirectToAuthSpa({
329
+ authUrl,
330
+ appName: "app", // Will need to be configured
331
+ redirectTo: redirectUrl,
332
+ });
333
+ return;
334
+ }
335
+ const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
336
+ const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
337
+ window.location.href = joinUrl;
338
+ }
339
+ /**
340
+ * Handle user logout
341
+ *
342
+ * This function:
343
+ * - Clears localStorage (preserving tenant)
344
+ * - Sets logout timestamp cookie for cross-SPA sync
345
+ * - Clears all cookies
346
+ * - Redirects to auth SPA logout endpoint
347
+ *
348
+ * @param options - Logout configuration options
349
+ *
350
+ * @example
351
+ * ```tsx
352
+ * handleLogout({
353
+ * authUrl: 'https://auth.example.com',
354
+ * redirectUrl: 'https://app.example.com',
355
+ * });
356
+ * ```
357
+ */
358
+ function handleLogout(options) {
359
+ const { redirectUrl = window.location.origin, authUrl, tenantStorageKey = "tenant", logoutTimestampCookie = "ibl_logout_timestamp", callback, } = options;
360
+ const tenant = window.localStorage.getItem(tenantStorageKey);
361
+ window.localStorage.clear();
362
+ if (tenant) {
363
+ window.localStorage.setItem(tenantStorageKey, tenant);
364
+ }
365
+ // Set logout timestamp cookie to trigger logout on other SPAs
366
+ setCookieForAuth(logoutTimestampCookie, Date.now().toString());
367
+ clearCookies();
368
+ callback === null || callback === void 0 ? void 0 : callback();
369
+ if (!isInIframe()) {
370
+ window.location.href = `${authUrl}/logout?redirect-to=${redirectUrl}${tenant ? "&tenant=" + tenant : ""}`;
371
+ }
372
+ }
373
+ // ── Tenant-switch coordination ─────────────────────────────────────────────
374
+ // Cross-tab tenant switching: a TTL lock (shared via localStorage) plus a
375
+ // BroadcastChannel signal let multiple tabs coordinate a switch and prevent the
376
+ // "ping-pong" where a stale tab reverts the session to its own route's tenant.
377
+ const TENANT_SWITCH_CHANNEL = "ibl-tenant-switch";
378
+ const TENANT_SWITCH_LOCK_KEY = "ibl_tenant_switch_lock";
379
+ const DEFAULT_TENANT_SWITCH_LOCK_TTL_MS = 8000;
380
+ let _tabId = null;
381
+ /**
382
+ * Stable per-tab id. Uses sessionStorage so it survives same-tab reloads while
383
+ * staying unique per tab/window.
384
+ */
385
+ function getTabId() {
386
+ var _a, _b;
387
+ if (_tabId)
388
+ return _tabId;
389
+ try {
390
+ const existing = sessionStorage.getItem("ibl_tab_id");
391
+ if (existing)
392
+ return (_tabId = existing);
393
+ const id = (_b = (_a = crypto === null || crypto === void 0 ? void 0 : crypto.randomUUID) === null || _a === void 0 ? void 0 : _a.call(crypto)) !== null && _b !== void 0 ? _b : `${Date.now()}-${Math.random()}`;
394
+ sessionStorage.setItem("ibl_tab_id", id);
395
+ return (_tabId = id);
396
+ }
397
+ catch (_c) {
398
+ return (_tabId = `${Date.now()}-${Math.random()}`);
399
+ }
400
+ }
401
+ /** The active (non-expired) tenant-switch lock, or null. */
402
+ function readTenantSwitchLock(ttlMs = DEFAULT_TENANT_SWITCH_LOCK_TTL_MS) {
403
+ try {
404
+ const raw = localStorage.getItem(TENANT_SWITCH_LOCK_KEY);
405
+ if (!raw)
406
+ return null;
407
+ const lock = JSON.parse(raw);
408
+ if (typeof (lock === null || lock === void 0 ? void 0 : lock.ts) !== "number")
409
+ return null;
410
+ if (Date.now() - lock.ts > ttlMs)
411
+ return null;
412
+ return lock;
413
+ }
414
+ catch (_a) {
415
+ return null;
416
+ }
417
+ }
418
+ /** Write a fresh lock for `tenant` (newest intent). Returns the lock written. */
419
+ function writeTenantSwitchLock(tenant) {
420
+ const lock = {
421
+ tenant,
422
+ token: `${Date.now()}-${getTabId()}`,
423
+ ts: Date.now(),
424
+ senderId: getTabId(),
425
+ };
426
+ try {
427
+ localStorage.setItem(TENANT_SWITCH_LOCK_KEY, JSON.stringify(lock));
428
+ }
429
+ catch (_a) { }
430
+ return lock;
431
+ }
432
+ /** Extend an existing lock's window from "now" (no-op when no active lock). */
433
+ function refreshTenantSwitchLock() {
434
+ const lock = readTenantSwitchLock();
435
+ if (!lock)
436
+ return;
437
+ try {
438
+ localStorage.setItem(TENANT_SWITCH_LOCK_KEY, JSON.stringify({ ...lock, ts: Date.now() }));
439
+ }
440
+ catch (_a) { }
441
+ }
442
+ function clearTenantSwitchLock() {
443
+ try {
444
+ localStorage.removeItem(TENANT_SWITCH_LOCK_KEY);
445
+ }
446
+ catch (_a) { }
447
+ }
448
+ /** True while a tenant switch is in flight — gate automatic reverts on this. */
449
+ function isTenantSwitchInProgress(ttlMs) {
450
+ return readTenantSwitchLock(ttlMs) !== null;
451
+ }
452
+ /**
453
+ * Switch the active tenant. Coordinates across tabs (TTL lock + BroadcastChannel),
454
+ * clears storage, then redirects through the auth SPA's `/login/complete`.
455
+ *
456
+ * @example
457
+ * await handleTenantSwitch("acme", {
458
+ * authUrl: config.authUrl(),
459
+ * clearCurrentTenantCookie,
460
+ * });
461
+ */
462
+ async function handleTenantSwitch(tenant, options) {
463
+ var _a, _b, _c;
464
+ const { authUrl, redirectPathStorageKey = "redirect-to", preserveTokenKey = "edx_jwt_token", tenantStorageKey = "tenant", tenantSwitchingCookie = "ibl_tenant_switching", queryParams = {}, saveRedirect = false, redirectUrl, broadcast = true, clearCurrentTenantCookie, } = options;
465
+ const tenantParam = (_a = queryParams.tenant) !== null && _a !== void 0 ? _a : "tenant";
466
+ const redirectToParam = (_b = queryParams.redirectTo) !== null && _b !== void 0 ? _b : "redirect-to";
467
+ // Guard: don't start a new user switch while one is already in progress.
468
+ if (broadcast && document.cookie.includes(tenantSwitchingCookie))
469
+ return;
470
+ // Guard: already on the target tenant.
471
+ if (tenant === localStorage.getItem(tenantStorageKey))
472
+ return;
473
+ if (broadcast) {
474
+ setCookieForAuth(tenantSwitchingCookie, "true");
475
+ writeTenantSwitchLock(tenant);
476
+ if (typeof BroadcastChannel !== "undefined") {
477
+ const channel = new BroadcastChannel(TENANT_SWITCH_CHANNEL);
478
+ const lock = readTenantSwitchLock(options.lockTtlMs);
479
+ channel.postMessage({
480
+ type: "TENANT_SWITCHING",
481
+ tenant,
482
+ senderId: (_c = lock === null || lock === void 0 ? void 0 : lock.senderId) !== null && _c !== void 0 ? _c : getTabId(),
483
+ token: lock === null || lock === void 0 ? void 0 : lock.token,
484
+ });
485
+ channel.close();
486
+ }
487
+ clearCurrentTenantCookie === null || clearCurrentTenantCookie === void 0 ? void 0 : clearCurrentTenantCookie();
488
+ }
489
+ const currentPath = `${window.location.pathname}${window.location.search}`;
490
+ const jwtToken = localStorage.getItem(preserveTokenKey);
491
+ if (broadcast)
492
+ localStorage.clear();
493
+ const params = {
494
+ [tenantParam]: tenant,
495
+ [redirectToParam]: redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.origin,
496
+ };
497
+ if (jwtToken)
498
+ params.token = jwtToken;
499
+ localStorage.setItem(tenantStorageKey, tenant);
500
+ if (saveRedirect)
501
+ localStorage.setItem(redirectPathStorageKey, currentPath);
502
+ // Re-persist: localStorage.clear() above wiped the pre-broadcast lock; this
503
+ // copy survives the redirect so stale tabs honor the switch window.
504
+ if (broadcast)
505
+ writeTenantSwitchLock(tenant);
506
+ await new Promise((resolve) => setTimeout(resolve, 100));
507
+ window.location.href = `${authUrl}/login/complete?${new URLSearchParams(params)}`;
508
+ }
509
+
510
+ /**
511
+ * Subscribe to cross-tab tenant-switch broadcasts. Ignores this tab's own echo
512
+ * (BroadcastChannel delivers to sibling instances in the same tab) and refreshes
513
+ * the shared switch lock so this tab won't auto-revert during the switch window.
514
+ *
515
+ * @example
516
+ * useTenantSwitchSync();
517
+ */
518
+ function useTenantSwitchSync(options = {}) {
519
+ const onRemoteSwitchRef = useRef(options.onRemoteSwitch);
520
+ onRemoteSwitchRef.current = options.onRemoteSwitch;
521
+ useEffect(() => {
522
+ if (typeof BroadcastChannel === "undefined")
523
+ return;
524
+ const channel = new BroadcastChannel(TENANT_SWITCH_CHANNEL);
525
+ channel.onmessage = (event) => {
526
+ var _a, _b;
527
+ const { type, tenant, senderId } = (_a = event.data) !== null && _a !== void 0 ? _a : {};
528
+ if (type !== "TENANT_SWITCHING")
529
+ return;
530
+ if (senderId && senderId === getTabId())
531
+ return; // ignore our own echo
532
+ refreshTenantSwitchLock();
533
+ (_b = onRemoteSwitchRef.current) === null || _b === void 0 ? void 0 : _b.call(onRemoteSwitchRef, tenant);
534
+ };
535
+ return () => channel.close();
536
+ }, []);
537
+ }
538
+
10
539
  const SUBSCRIPTION_TRIGGERS = {
11
540
  PRICING_MODAL: "TRIGGER_PRICING_MODAL",
12
541
  SUBSCRIBE_USER: "TRIGGER_SUBSCRIBE_USER",
@@ -800,568 +1329,204 @@ const TOOL_NAME_MAP = {
800
1329
  vector_search: "Searching knowledge base",
801
1330
  calculator: "Calculating",
802
1331
  file_reader: "Reading file",
803
- code_executor: "Running code",
804
- wikipedia: "Searching Wikipedia",
805
- };
806
- const REQUIRED_ACTIONS_FOR_GROUPS = {
807
- NOTIFICATIONS: "Ibl.Notifications/Notification/action",
808
- };
809
- const MENTOR_VISIBILITY_VALUES = {
810
- ADMINISTRATORS: "viewable_by_tenant_admins",
811
- STUDENTS: "viewable_by_tenant_students",
812
- ANYONE: "viewable_by_anyone",
813
- };
814
- const MENTOR_VISIBILITY = [
815
- {
816
- label: "Administrators",
817
- value: MENTOR_VISIBILITY_VALUES.ADMINISTRATORS,
818
- },
819
- {
820
- label: "Students",
821
- value: MENTOR_VISIBILITY_VALUES.STUDENTS,
822
- },
823
- {
824
- label: "Anyone",
825
- value: MENTOR_VISIBILITY_VALUES.ANYONE,
826
- },
827
- ];
828
-
829
- /**
830
- * Reads the cached user_nicename from the LOCAL_STORAGE_KEYS.USER_DATA blob.
831
- */
832
- const getUserName = () => {
833
- var _a;
834
- if (typeof window === "undefined" ||
835
- typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== "function")
836
- return null;
837
- try {
838
- return (_a = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.USER_DATA))) === null || _a === void 0 ? void 0 : _a.user_nicename;
839
- }
840
- catch (_b) {
841
- return null;
842
- }
843
- };
844
- /**
845
- * Reads the cached user_email from the LOCAL_STORAGE_KEYS.USER_DATA blob.
846
- */
847
- const getUserEmail = () => {
848
- var _a;
849
- if (typeof window === "undefined" ||
850
- typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== "function")
851
- return null;
852
- try {
853
- return (_a = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.USER_DATA))) === null || _a === void 0 ? void 0 : _a.user_email;
854
- }
855
- catch (_b) {
856
- return null;
857
- }
858
- };
859
- /**
860
- * Whether Stripe-backed billing is active for the given tenant. The Stripe
861
- * feature flag is a host-side config value; pass it in. Tenants opt in via
862
- * `show_paywall` or by being the "main" platform tenant.
863
- */
864
- function isStripeActivated(currentTenant, stripeEnabled) {
865
- return Boolean(stripeEnabled &&
866
- ((currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.show_paywall) || (currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.key) === "main"));
867
- }
868
- /**
869
- * Checks whether a file matches any of the accepted type specifiers.
870
- * Supports MIME types (image/png), wildcard MIME (image/*), and extensions (.pdf).
871
- */
872
- function isFileAccepted(file, acceptedTypes) {
873
- if (acceptedTypes.length === 0)
874
- return true;
875
- return acceptedTypes.some((type) => {
876
- if (type.startsWith(".")) {
877
- return file.name.toLowerCase().endsWith(type.toLowerCase());
878
- }
879
- if (type.endsWith("/*")) {
880
- const category = type.split("/")[0];
881
- return file.type.startsWith(category + "/");
882
- }
883
- return file.type === type;
884
- });
885
- }
886
- const isJSON = (text) => {
887
- if (typeof text !== "string") {
888
- return false;
889
- }
890
- try {
891
- JSON.parse(text);
892
- return true;
893
- }
894
- catch (_a) {
895
- return false;
896
- }
897
- };
898
- const getInitials = (fullName) => {
899
- const names = fullName.split(" ");
900
- if (names.length === 1) {
901
- return names[0].substring(0, 2).toUpperCase();
902
- }
903
- return names
904
- .slice(0, 2)
905
- .map((name) => name.charAt(0).toUpperCase())
906
- .join("");
907
- };
908
- const ALPHANUMERIC_32_REGEX = /^[a-zA-Z0-9]{32}$/;
909
- const isAlphaNumeric32 = (text) => {
910
- return ALPHANUMERIC_32_REGEX.test(text);
911
- };
912
- const addProtocolToUrl = (url) => {
913
- if (!url.startsWith("http")) {
914
- return `https://${url}`;
915
- }
916
- return url;
917
- };
918
- function getTimeAgo(createdAt) {
919
- const now = new Date();
920
- const created = new Date(createdAt);
921
- const diffInMilliseconds = now.getTime() - created.getTime();
922
- const diffInMinutes = Math.floor(diffInMilliseconds / (1000 * 60));
923
- const diffInHours = Math.floor(diffInMilliseconds / (1000 * 60 * 60));
924
- const diffInDays = Math.floor(diffInHours / 24);
925
- if (diffInMinutes < 60) {
926
- return `${diffInMinutes} min ago`;
927
- }
928
- else if (diffInHours < 24) {
929
- return `${diffInHours} hrs ago`;
930
- }
931
- else {
932
- return `${diffInDays} ${diffInDays === 1 ? "day" : "days"} ago`;
933
- }
934
- }
935
- // Utility function to format relative time
936
- const formatRelativeTime = (timestamp) => {
937
- const now = new Date();
938
- const date = new Date(timestamp);
939
- const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
940
- if (diffInSeconds < 60) {
941
- return "Just now";
942
- }
943
- const diffInMinutes = Math.floor(diffInSeconds / 60);
944
- if (diffInMinutes < 60) {
945
- return `${diffInMinutes} minute${diffInMinutes === 1 ? "" : "s"} ago`;
946
- }
947
- const diffInHours = Math.floor(diffInMinutes / 60);
948
- if (diffInHours < 24) {
949
- return `${diffInHours} hour${diffInHours === 1 ? "" : "s"} ago`;
950
- }
951
- const diffInDays = Math.floor(diffInHours / 24);
952
- if (diffInDays < 7) {
953
- return `${diffInDays} day${diffInDays === 1 ? "" : "s"} ago`;
954
- }
955
- const diffInWeeks = Math.floor(diffInDays / 7);
956
- if (diffInWeeks < 4) {
957
- return `${diffInWeeks} week${diffInWeeks === 1 ? "" : "s"} ago`;
958
- }
959
- const diffInMonths = Math.floor(diffInDays / 30);
960
- if (diffInMonths < 12) {
961
- return `${diffInMonths} month${diffInMonths === 1 ? "" : "s"} ago`;
962
- }
963
- const diffInYears = Math.floor(diffInDays / 365);
964
- return `${diffInYears} year${diffInYears === 1 ? "" : "s"} ago`;
965
- };
966
- /**
967
- * Convert markdown text to plain text by removing markdown formatting
968
- * @param text - The markdown text to convert
969
- * @returns Plain text without markdown formatting
970
- */
971
- const markdownToPlainText = (text) => {
972
- return (text
973
- // Remove code blocks first (before other replacements)
974
- .replace(/```[\s\S]*?```/g, "")
975
- // Remove headers (# ## ### etc)
976
- .replace(/^#{1,6}\s+/gm, "")
977
- // Remove images ![alt](url) - must come before links
978
- .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1")
979
- // Remove links [text](url) - keep only the text
980
- .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
981
- // Remove horizontal rules (---, ***, ___) - must come before bold/italic
982
- .replace(/^[-*_]{3,}\s*$/gm, "")
983
- // Remove bold (**text** or __text__)
984
- .replace(/(\*\*|__)(.*?)\1/g, "$2")
985
- // Remove italic (*text* or _text_) - only single * or _
986
- .replace(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g, "$1")
987
- .replace(/(?<!_)_(?!_)([^_]+)_(?!_)/g, "$1")
988
- // Remove strikethrough (~~text~~)
989
- .replace(/~~(.*?)~~/g, "$1")
990
- // Remove inline code (`code`)
991
- .replace(/`([^`]+)`/g, "$1")
992
- // Remove blockquotes (> text)
993
- .replace(/^>\s+/gm, "")
994
- // Remove list markers (- * + 1. 2. etc)
995
- .replace(/^[\s]*[-*+]\s+/gm, "")
996
- .replace(/^[\s]*\d+\.\s+/gm, "")
997
- // Clean up multiple spaces
998
- .replace(/\s+/g, " ")
999
- .trim());
1332
+ code_executor: "Running code",
1333
+ wikipedia: "Searching Wikipedia",
1334
+ };
1335
+ const REQUIRED_ACTIONS_FOR_GROUPS = {
1336
+ NOTIFICATIONS: "Ibl.Notifications/Notification/action",
1337
+ };
1338
+ const MENTOR_VISIBILITY_VALUES = {
1339
+ ADMINISTRATORS: "viewable_by_tenant_admins",
1340
+ STUDENTS: "viewable_by_tenant_students",
1341
+ ANYONE: "viewable_by_anyone",
1000
1342
  };
1343
+ const MENTOR_VISIBILITY = [
1344
+ {
1345
+ label: "Administrators",
1346
+ value: MENTOR_VISIBILITY_VALUES.ADMINISTRATORS,
1347
+ },
1348
+ {
1349
+ label: "Users",
1350
+ value: MENTOR_VISIBILITY_VALUES.STUDENTS,
1351
+ },
1352
+ {
1353
+ label: "Anyone",
1354
+ value: MENTOR_VISIBILITY_VALUES.ANYONE,
1355
+ },
1356
+ ];
1001
1357
 
1002
1358
  /**
1003
- * Authentication and Authorization Utilities
1004
- *
1005
- * This module provides utility functions for handling authentication flows,
1006
- * including redirects to auth SPA, cookie management, and session handling.
1007
- */
1008
- /**
1009
- * Check if the current window is inside an iframe
1010
- * @returns {boolean} True if running in an iframe, false otherwise
1011
- */
1012
- function isInIframe() {
1013
- if (typeof window === "undefined") {
1014
- return false;
1015
- }
1016
- return (window === null || window === void 0 ? void 0 : window.self) !== (window === null || window === void 0 ? void 0 : window.top);
1017
- }
1018
- /**
1019
- * Send a message to the parent website (when in iframe)
1020
- * @param payload - The data to send to parent window
1021
- */
1022
- function sendMessageToParentWebsite(payload) {
1023
- window.parent.postMessage(payload, "*");
1024
- }
1025
- /**
1026
- * Delete a cookie with specific name, path, and domain
1027
- * @param name - Cookie name
1028
- * @param path - Cookie path
1029
- * @param domain - Cookie domain
1030
- */
1031
- function deleteCookie(name, path, domain) {
1032
- const expires = "expires=Thu, 01 Jan 1970 00:00:00 UTC;";
1033
- const cookieValue = `${name}=;`;
1034
- const pathValue = path ? `path=${path};` : "";
1035
- const domainValue = domain ? `domain=${domain};` : "";
1036
- document.cookie = cookieValue + expires + pathValue + domainValue;
1037
- }
1038
- /**
1039
- * Get all possible domain parts for cookie deletion
1040
- * @param domain - The domain to split
1041
- * @returns Array of domain parts
1042
- * @example
1043
- * getDomainParts('app.example.com') // returns ['app.example.com', 'example.com', 'com']
1044
- */
1045
- function getDomainParts(domain) {
1046
- const parts = domain.split(".");
1047
- const domains = [];
1048
- for (let i = parts.length - 1; i >= 0; i--) {
1049
- domains.push(parts.slice(i).join("."));
1050
- }
1051
- return domains;
1052
- }
1053
- /**
1054
- * Delete a cookie across all possible domain variations
1055
- * @param name - Cookie name to delete
1056
- * @param childDomain - The current domain
1057
- */
1058
- function deleteCookieOnAllDomains(name, childDomain) {
1059
- getDomainParts(childDomain).forEach((domainPart) => {
1060
- deleteCookie(name, "/", domainPart);
1061
- deleteCookie(name, "", domainPart);
1062
- });
1063
- }
1064
- /**
1065
- * Get the parent domain from a given domain
1066
- * @param domain - The domain to process
1067
- * @returns The parent domain (e.g., 'app.example.com' → '.example.com')
1068
- */
1069
- function getParentDomain(domain) {
1070
- if (!domain) {
1071
- return "";
1072
- }
1073
- const parts = domain.split(".");
1074
- return parts.length > 1 ? `.${parts.slice(-2).join(".")}` : domain;
1075
- }
1076
- /**
1077
- * Set a cookie for authentication with cross-domain support
1078
- * @param name - Cookie name
1079
- * @param value - Cookie value
1080
- * @param days - Number of days until expiration (default: 365)
1359
+ * Reads the cached user_nicename from the LOCAL_STORAGE_KEYS.USER_DATA blob.
1081
1360
  */
1082
- function setCookieForAuth(name, value, days = 365) {
1083
- const expires = new Date();
1084
- expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
1085
- const hostname = window.location.hostname;
1086
- let baseDomain = hostname;
1087
- // Calculate base domain (skip for localhost and IP addresses)
1088
- if (hostname !== "localhost" && !/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
1089
- const parts = hostname.split(".");
1090
- if (parts.length > 2) {
1091
- baseDomain = `.${parts.slice(-2).join(".")}`;
1092
- }
1361
+ const getUserName = () => {
1362
+ var _a;
1363
+ if (typeof window === "undefined" ||
1364
+ typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== "function")
1365
+ return null;
1366
+ try {
1367
+ return (_a = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.USER_DATA))) === null || _a === void 0 ? void 0 : _a.user_nicename;
1093
1368
  }
1094
- const domainAttr = baseDomain ? `;domain=${baseDomain}` : "";
1095
- document.cookie = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/;SameSite=None;Secure${domainAttr}`;
1096
- }
1097
- /**
1098
- * Clear all cookies for the current domain
1099
- */
1100
- function clearCookies() {
1101
- const cookies = document.cookie.split(";");
1102
- for (let i = 0; i < cookies.length; i++) {
1103
- const cookie = cookies[i];
1104
- const eqPos = cookie.indexOf("=");
1105
- const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
1106
- deleteCookieOnAllDomains(name, window.location.hostname);
1107
- document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;Domain=${getParentDomain(window.location.hostname)}`;
1369
+ catch (_b) {
1370
+ return null;
1108
1371
  }
1109
- }
1110
- /**
1111
- * Get the value of a cookie by name
1112
- * @param name - Cookie name
1113
- * @returns The cookie value or null if not found
1114
- */
1115
- function getCookieValue(name) {
1116
- const match = document.cookie.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
1117
- return match ? decodeURIComponent(match[1]) : null;
1118
- }
1119
- /**
1120
- * Check if user is currently logged in
1121
- * @param tokenKey - The localStorage key for the auth token (default: 'axd_token')
1122
- * @returns True if logged in, false otherwise
1123
- */
1124
- function isLoggedIn(tokenKey = "axd_token") {
1125
- return !!localStorage.getItem(tokenKey);
1126
- }
1127
- /**
1128
- * Extract platform/tenant key from URL
1129
- * @param url - The URL to parse
1130
- * @param pattern - RegExp pattern to match platform key (default matches /platform/[key]/...)
1131
- * @returns The platform key or null if not found
1132
- */
1133
- function getPlatformKey(url, pattern = /\/platform\/([^/]+)/) {
1134
- const match = url.match(pattern);
1135
- return match ? match[1] : null;
1136
- }
1372
+ };
1137
1373
  /**
1138
- * Redirect to authentication SPA for login/logout
1139
- *
1140
- * This function handles the complete authentication flow:
1141
- * - Clears localStorage and cookies on logout
1142
- * - Saves redirect path for post-auth return
1143
- * - Handles iframe scenarios
1144
- * - Syncs logout across multiple SPAs via cookies
1145
- *
1146
- * @param options - Configuration options for the redirect
1147
- *
1148
- * @example
1149
- * ```tsx
1150
- * // Basic usage
1151
- * redirectToAuthSpa({
1152
- * authUrl: 'https://auth.example.com',
1153
- * appName: 'mentor',
1154
- * });
1155
- *
1156
- * // With logout
1157
- * redirectToAuthSpa({
1158
- * authUrl: 'https://auth.example.com',
1159
- * appName: 'mentor',
1160
- * logout: true,
1161
- * platformKey: 'my-tenant',
1162
- * });
1163
- *
1164
- * // With custom redirect
1165
- * redirectToAuthSpa({
1166
- * authUrl: 'https://auth.example.com',
1167
- * appName: 'mentor',
1168
- * redirectTo: '/dashboard',
1169
- * platformKey: 'my-tenant',
1170
- * });
1171
- * ```
1374
+ * Reads the cached user_email from the LOCAL_STORAGE_KEYS.USER_DATA blob.
1172
1375
  */
1173
- async function redirectToAuthSpa(options) {
1174
- const { redirectTo, platformKey, logout = false, saveRedirect = true, authUrl, appName, queryParams = {
1175
- app: "app",
1176
- redirectTo: "redirect-to",
1177
- tenant: "tenant",
1178
- }, redirectPathStorageKey = "redirect_to", cookieNames = {
1179
- currentTenant: "ibl_current_tenant",
1180
- userData: "ibl_user_data",
1181
- tenant: "ibl_tenant",
1182
- logoutTimestamp: "ibl_logout_timestamp",
1183
- loginTimestamp: "ibl_login_timestamp",
1184
- tenantSwitching: "ibl_tenant_switching",
1185
- }, hasNonExpiredAuthToken, isOffline, preserveTokenKey, authRedirectProxy, isNativeApp, scheme = "iblai-mentor", forceRedirect = false, } = options;
1186
- console.log("[redirectToAuthSpa] starting redirect to auth spa", redirectTo, platformKey, logout, saveRedirect, forceRedirect);
1187
- // Skip if a tenant switch is already in progress
1188
- if (!forceRedirect &&
1189
- cookieNames.tenantSwitching &&
1190
- document.cookie.includes(cookieNames.tenantSwitching)) {
1191
- console.log("[redirectToAuthSpa] Tenant switch in progress, skipping redirect");
1192
- return;
1193
- }
1194
- // Skip if a login occurred after the last logout (login takes precedence)
1195
- // but only if this app actually has a valid auth token
1196
- if (!forceRedirect &&
1197
- hasNonExpiredAuthToken &&
1198
- cookieNames.loginTimestamp &&
1199
- cookieNames.logoutTimestamp) {
1200
- const loginTs = getCookieValue(cookieNames.loginTimestamp);
1201
- const logoutTs = getCookieValue(cookieNames.logoutTimestamp);
1202
- const hasValidToken = hasNonExpiredAuthToken();
1203
- console.log("[redirectToAuthSpa] Login/logout timestamp check", {
1204
- loginTs,
1205
- logoutTs,
1206
- hasValidToken,
1207
- loginAhead: loginTs && logoutTs ? Number(loginTs) > Number(logoutTs) : false,
1208
- });
1209
- if (!forceRedirect &&
1210
- hasValidToken &&
1211
- loginTs &&
1212
- logoutTs &&
1213
- Number(loginTs) > Number(logoutTs)) {
1214
- console.log("[redirectToAuthSpa] Login timestamp is ahead of logout timestamp, skipping redirect", { loginTs, logoutTs });
1215
- return;
1216
- }
1217
- }
1218
- // Skip redirect in offline mode
1219
- if (isOffline === null || isOffline === void 0 ? void 0 : isOffline()) {
1220
- console.log("[redirectToAuthSpa] Skipping redirect - offline mode");
1221
- return;
1376
+ const getUserEmail = () => {
1377
+ var _a;
1378
+ if (typeof window === "undefined" ||
1379
+ typeof (localStorage === null || localStorage === void 0 ? void 0 : localStorage.getItem) !== "function")
1380
+ return null;
1381
+ try {
1382
+ return (_a = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.USER_DATA))) === null || _a === void 0 ? void 0 : _a.user_email;
1222
1383
  }
1223
- // Preserve a token before clearing localStorage if requested
1224
- const preservedToken = preserveTokenKey
1225
- ? window.localStorage.getItem(preserveTokenKey)
1226
- : null;
1227
- console.log("[redirectToAuthSpa] clearing local storage");
1228
- localStorage.clear();
1229
- if (logout || isInIframe()) {
1230
- // Delete authentication cookies for cross-SPA synchronization
1231
- const currentDomain = window.location.hostname;
1232
- if (cookieNames.currentTenant) {
1233
- deleteCookieOnAllDomains(cookieNames.currentTenant, currentDomain);
1234
- }
1235
- if (cookieNames.userData) {
1236
- deleteCookieOnAllDomains(cookieNames.userData, currentDomain);
1237
- }
1238
- if (cookieNames.tenant) {
1239
- deleteCookieOnAllDomains(cookieNames.tenant, currentDomain);
1384
+ catch (_b) {
1385
+ return null;
1386
+ }
1387
+ };
1388
+ /**
1389
+ * Whether Stripe-backed billing is active for the given tenant. The Stripe
1390
+ * feature flag is a host-side config value; pass it in. Tenants opt in via
1391
+ * `show_paywall` or by being the "main" platform tenant.
1392
+ */
1393
+ function isStripeActivated(currentTenant, stripeEnabled) {
1394
+ return Boolean(stripeEnabled &&
1395
+ ((currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.show_paywall) || (currentTenant === null || currentTenant === void 0 ? void 0 : currentTenant.key) === "main"));
1396
+ }
1397
+ /**
1398
+ * Checks whether a file matches any of the accepted type specifiers.
1399
+ * Supports MIME types (image/png), wildcard MIME (image/*), and extensions (.pdf).
1400
+ */
1401
+ function isFileAccepted(file, acceptedTypes) {
1402
+ if (acceptedTypes.length === 0)
1403
+ return true;
1404
+ return acceptedTypes.some((type) => {
1405
+ if (type.startsWith(".")) {
1406
+ return file.name.toLowerCase().endsWith(type.toLowerCase());
1240
1407
  }
1241
- // Set logout timestamp cookie to trigger logout on other SPAs
1242
- if (cookieNames.logoutTimestamp && !isInIframe()) {
1243
- setCookieForAuth(cookieNames.logoutTimestamp, Date.now().toString());
1408
+ if (type.endsWith("/*")) {
1409
+ const category = type.split("/")[0];
1410
+ return file.type.startsWith(category + "/");
1244
1411
  }
1412
+ return file.type === type;
1413
+ });
1414
+ }
1415
+ const isJSON = (text) => {
1416
+ if (typeof text !== "string") {
1417
+ return false;
1245
1418
  }
1246
- // Handle iframe scenario
1247
- if (isInIframe()) {
1248
- console.log("[redirectToAuthSpa]: sending authExpired to parent");
1249
- sendMessageToParentWebsite({ authExpired: true });
1250
- return;
1251
- }
1252
- const redirectPath = redirectTo !== null && redirectTo !== void 0 ? redirectTo : `${window.location.pathname}${window.location.search}`;
1253
- // Never save sso-login routes as redirect paths
1254
- if (!redirectPath.startsWith("/sso-login") &&
1255
- !redirectPath.startsWith("/sso-login-complete") &&
1256
- saveRedirect) {
1257
- window.localStorage.setItem(redirectPathStorageKey, redirectPath);
1419
+ try {
1420
+ JSON.parse(text);
1421
+ return true;
1258
1422
  }
1259
- const platform = platformKey !== null && platformKey !== void 0 ? platformKey : getPlatformKey(redirectPath);
1260
- const redirectToUrl = window.location.origin;
1261
- let authRedirectUrl = `${authUrl}/login?${queryParams.app}=${appName}`;
1262
- authRedirectUrl += `&${queryParams.redirectTo}=${redirectToUrl}`;
1263
- if (platform) {
1264
- authRedirectUrl += `&${queryParams.tenant}=${platform}`;
1423
+ catch (_a) {
1424
+ return false;
1265
1425
  }
1266
- if (logout) {
1267
- authRedirectUrl += "&logout=1";
1426
+ };
1427
+ const getInitials = (fullName) => {
1428
+ const names = fullName.split(" ");
1429
+ if (names.length === 1) {
1430
+ return names[0].substring(0, 2).toUpperCase();
1268
1431
  }
1269
- if ((isNativeApp === null || isNativeApp === void 0 ? void 0 : isNativeApp()) && scheme) {
1270
- authRedirectUrl += `&scheme=${scheme}`;
1432
+ return names
1433
+ .slice(0, 2)
1434
+ .map((name) => name.charAt(0).toUpperCase())
1435
+ .join("");
1436
+ };
1437
+ const ALPHANUMERIC_32_REGEX = /^[a-zA-Z0-9]{32}$/;
1438
+ const isAlphaNumeric32 = (text) => {
1439
+ return ALPHANUMERIC_32_REGEX.test(text);
1440
+ };
1441
+ const addProtocolToUrl = (url) => {
1442
+ if (!url.startsWith("http")) {
1443
+ return `https://${url}`;
1271
1444
  }
1272
- // Small delay for any pending operations
1273
- await new Promise((resolve) => setTimeout(resolve, 100));
1274
- if (isNativeApp === null || isNativeApp === void 0 ? void 0 : isNativeApp()) {
1275
- // On native apps (e.g., Tauri), pass preserved token and navigate directly
1276
- if (preservedToken && preserveTokenKey) {
1277
- authRedirectUrl += `&token=${encodeURIComponent(preservedToken)}`;
1278
- console.log("[redirectToAuthSpa] Added preserved token to auth URL");
1279
- }
1280
- console.log("[redirectToAuthSpa] Native app, navigating to auth URL:", authRedirectUrl);
1281
- window.location.href = authRedirectUrl;
1445
+ return url;
1446
+ };
1447
+ function getTimeAgo(createdAt) {
1448
+ const now = new Date();
1449
+ const created = new Date(createdAt);
1450
+ const diffInMilliseconds = now.getTime() - created.getTime();
1451
+ const diffInMinutes = Math.floor(diffInMilliseconds / (1000 * 60));
1452
+ const diffInHours = Math.floor(diffInMilliseconds / (1000 * 60 * 60));
1453
+ const diffInDays = Math.floor(diffInHours / 24);
1454
+ if (diffInMinutes < 60) {
1455
+ return `${diffInMinutes} min ago`;
1282
1456
  }
1283
- else if (authRedirectProxy) {
1284
- // Use auth redirect proxy endpoint
1285
- window.location.href = `${authRedirectProxy}?to=${encodeURIComponent(authRedirectUrl)}`;
1457
+ else if (diffInHours < 24) {
1458
+ return `${diffInHours} hrs ago`;
1286
1459
  }
1287
1460
  else {
1288
- window.location.href = authRedirectUrl;
1461
+ return `${diffInDays} ${diffInDays === 1 ? "day" : "days"} ago`;
1289
1462
  }
1290
1463
  }
1291
- /**
1292
- * Get the URL for joining a tenant (sign up flow)
1293
- * @param authUrl - Auth SPA base URL
1294
- * @param tenantKey - Tenant to join
1295
- * @param redirectUrl - URL to redirect to after joining (defaults to current URL)
1296
- * @returns The join URL
1297
- */
1298
- function getAuthSpaJoinUrl(authUrl, tenantKey, redirectUrl) {
1299
- const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
1300
- if (!resolvedTenant) {
1301
- return "";
1464
+ // Utility function to format relative time
1465
+ const formatRelativeTime = (timestamp) => {
1466
+ const now = new Date();
1467
+ const date = new Date(timestamp);
1468
+ const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
1469
+ if (diffInSeconds < 60) {
1470
+ return "Just now";
1302
1471
  }
1303
- const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
1304
- const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
1305
- return joinUrl;
1306
- }
1307
- /**
1308
- * Redirect to auth SPA's join/signup page for a specific tenant
1309
- * @param authUrl - Auth SPA base URL
1310
- * @param tenantKey - Tenant to join
1311
- * @param redirectUrl - URL to redirect to after joining (defaults to current URL)
1312
- */
1313
- function redirectToAuthSpaJoinTenant(authUrl, tenantKey, redirectUrl) {
1314
- const resolvedTenant = tenantKey || getPlatformKey(window.location.pathname) || "";
1315
- if (!resolvedTenant) {
1316
- console.log("[auth-redirect] Missing tenant key for join", {
1317
- tenantKey,
1318
- redirectUrl,
1319
- });
1320
- redirectToAuthSpa({
1321
- authUrl,
1322
- appName: "app", // Will need to be configured
1323
- redirectTo: redirectUrl,
1324
- });
1325
- return;
1472
+ const diffInMinutes = Math.floor(diffInSeconds / 60);
1473
+ if (diffInMinutes < 60) {
1474
+ return `${diffInMinutes} minute${diffInMinutes === 1 ? "" : "s"} ago`;
1326
1475
  }
1327
- const targetUrl = redirectUrl !== null && redirectUrl !== void 0 ? redirectUrl : window.location.href;
1328
- const joinUrl = `${authUrl}/join?tenant=${encodeURIComponent(resolvedTenant)}&redirect-to=${encodeURIComponent(targetUrl)}`;
1329
- window.location.href = joinUrl;
1330
- }
1331
- /**
1332
- * Handle user logout
1333
- *
1334
- * This function:
1335
- * - Clears localStorage (preserving tenant)
1336
- * - Sets logout timestamp cookie for cross-SPA sync
1337
- * - Clears all cookies
1338
- * - Redirects to auth SPA logout endpoint
1339
- *
1340
- * @param options - Logout configuration options
1341
- *
1342
- * @example
1343
- * ```tsx
1344
- * handleLogout({
1345
- * authUrl: 'https://auth.example.com',
1346
- * redirectUrl: 'https://app.example.com',
1347
- * });
1348
- * ```
1349
- */
1350
- function handleLogout(options) {
1351
- const { redirectUrl = window.location.origin, authUrl, tenantStorageKey = "tenant", logoutTimestampCookie = "ibl_logout_timestamp", callback, } = options;
1352
- const tenant = window.localStorage.getItem(tenantStorageKey);
1353
- window.localStorage.clear();
1354
- if (tenant) {
1355
- window.localStorage.setItem(tenantStorageKey, tenant);
1476
+ const diffInHours = Math.floor(diffInMinutes / 60);
1477
+ if (diffInHours < 24) {
1478
+ return `${diffInHours} hour${diffInHours === 1 ? "" : "s"} ago`;
1356
1479
  }
1357
- // Set logout timestamp cookie to trigger logout on other SPAs
1358
- setCookieForAuth(logoutTimestampCookie, Date.now().toString());
1359
- clearCookies();
1360
- callback === null || callback === void 0 ? void 0 : callback();
1361
- if (!isInIframe()) {
1362
- window.location.href = `${authUrl}/logout?redirect-to=${redirectUrl}${tenant ? "&tenant=" + tenant : ""}`;
1480
+ const diffInDays = Math.floor(diffInHours / 24);
1481
+ if (diffInDays < 7) {
1482
+ return `${diffInDays} day${diffInDays === 1 ? "" : "s"} ago`;
1363
1483
  }
1364
- }
1484
+ const diffInWeeks = Math.floor(diffInDays / 7);
1485
+ if (diffInWeeks < 4) {
1486
+ return `${diffInWeeks} week${diffInWeeks === 1 ? "" : "s"} ago`;
1487
+ }
1488
+ const diffInMonths = Math.floor(diffInDays / 30);
1489
+ if (diffInMonths < 12) {
1490
+ return `${diffInMonths} month${diffInMonths === 1 ? "" : "s"} ago`;
1491
+ }
1492
+ const diffInYears = Math.floor(diffInDays / 365);
1493
+ return `${diffInYears} year${diffInYears === 1 ? "" : "s"} ago`;
1494
+ };
1495
+ /**
1496
+ * Convert markdown text to plain text by removing markdown formatting
1497
+ * @param text - The markdown text to convert
1498
+ * @returns Plain text without markdown formatting
1499
+ */
1500
+ const markdownToPlainText = (text) => {
1501
+ return (text
1502
+ // Remove code blocks first (before other replacements)
1503
+ .replace(/```[\s\S]*?```/g, "")
1504
+ // Remove headers (# ## ### etc)
1505
+ .replace(/^#{1,6}\s+/gm, "")
1506
+ // Remove images ![alt](url) - must come before links
1507
+ .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1")
1508
+ // Remove links [text](url) - keep only the text
1509
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
1510
+ // Remove horizontal rules (---, ***, ___) - must come before bold/italic
1511
+ .replace(/^[-*_]{3,}\s*$/gm, "")
1512
+ // Remove bold (**text** or __text__)
1513
+ .replace(/(\*\*|__)(.*?)\1/g, "$2")
1514
+ // Remove italic (*text* or _text_) - only single * or _
1515
+ .replace(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g, "$1")
1516
+ .replace(/(?<!_)_(?!_)([^_]+)_(?!_)/g, "$1")
1517
+ // Remove strikethrough (~~text~~)
1518
+ .replace(/~~(.*?)~~/g, "$1")
1519
+ // Remove inline code (`code`)
1520
+ .replace(/`([^`]+)`/g, "$1")
1521
+ // Remove blockquotes (> text)
1522
+ .replace(/^>\s+/gm, "")
1523
+ // Remove list markers (- * + 1. 2. etc)
1524
+ .replace(/^[\s]*[-*+]\s+/gm, "")
1525
+ .replace(/^[\s]*\d+\.\s+/gm, "")
1526
+ // Clean up multiple spaces
1527
+ .replace(/\s+/g, " ")
1528
+ .trim());
1529
+ };
1365
1530
 
1366
1531
  // Platform detection utility
1367
1532
  const isReactNative = () => {
@@ -18208,5 +18373,5 @@ const checkRbacPermission = (rbacPermissions, rbacResource, enableRBAC = true) =
18208
18373
  return checkRbacPermissionInternal(rbacPermissions, rbacResource);
18209
18374
  };
18210
18375
 
18211
- export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, CHAT_AREA_SIZE, CacheKeys, DEFAULT_DISCLAIMER_CONTENT, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, MENTOR_VISIBILITY, MENTOR_VISIBILITY_VALUES, METADATAS, MentorProvider, REQUIRED_ACTIONS_FOR_GROUPS, RemoteEvents, STREAMING_CONTENT_BUFFER_THRESHOLD, STREAMING_CONTENT_FLUSH_INTERVAL, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_PACKAGES_V2, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, ServiceWorkerProvider, SubscriptionFlow, SubscriptionFlowV2, TOOLS, TOOL_NAME_MAP, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, WithFormPermissions, WithPermissions, addFiles, addMessage, addProtocolToUrl, advancedTabs, advancedTabsProperties, appleRestrictionReducer, appleRestrictionSlice, chatActions, chatInputSlice, chatInputSliceActions, chatInputSliceReducer, chatInputSliceSelectors, chatSliceReducerShared, checkModelAvailable, checkOllamaHealth, checkRbacPermission, clearAllCaches, clearApiCache, clearAuthCookies, clearCookies, clearCurrentTenantCookie, clearFiles, clearMessages, combineCSVData, convertToOllamaMessages, createFileReference, createMultipleFileReferences, csvDataToText, defaultSessionIds, deleteCookie, deleteCookieOnAllDomains, enableChatActionsPopup, eventBus, fetchWithCache, filesReducer, filesSlice, formatRelativeTime, getAuthSpaJoinUrl, getCacheStatus, getCachedApiResponse, getCookieValue, getDomainParts, getFileInfo, getInitials, getLocalLLMSystemPrompt, getNextNavigation, getParentDomain, getPlatform, getPlatformKey, getServiceWorkerStatus, getStoredUserName, getTimeAgo, getUserEmail, getUserName, handleLogout, hasNonExpiredAuthToken, hostChatReducer, hostChatSlice, initServiceWorker, isAlphaNumeric32, isExpo, isFileAccepted, isInIframe, isJSON, isLoggedIn, isNode, isReactNative, isSafariBrowser, isServiceWorkerSupported, isStripeActivated, isTauri, isTauriOfflineMode, isWeb$2 as isWeb, loadMetadataConfig, markdownToPlainText, monetizationSlice, onStatusChange, onUpdate, parseCSV, preCacheMentorData, rbacReducer, redirectToAuthSpa, redirectToAuthSpaJoinTenant, registerServiceWorker, removeFile, requestPresignedUrl, safeRequire, selectActiveChatMessages, selectActiveTab, selectArtifactsEnabled, selectAttachedFiles, selectChats, selectCurrentStreamingArtifact, selectCurrentStreamingMessage, selectDocumentFilter, selectEnableChatActionsPopup, selectIframeContext, selectIsError, selectIsPending, selectIsReasoning, selectIsStopped, selectIsTyping, selectLastArtifactContentFlushTime, selectMetadata, selectNumberOfActiveChatMessages, selectRbacPermissions, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectStreamingArtifactContentBuffer, selectStreamingArtifactFullContent, selectStreamingReasoningContent, selectStreamingToolCalls, selectToken, selectTokenEnabled, selectTools, sendMessageToParentWebsite, setAccessCheckResponse, setAdvancedDisplayMonetizationCheckoutModal, setCachedApiResponse, setCookieForAuth, setDisplayMonetizationCheckoutModal, setError402Detected, setFreeTrialUsageOptions, setOfflineStatus, setOpenAppleRestrictionModal, setOpenPricingModal, setPricingModalData, setSubscriptionStatus, setTauriMode, setTopBannerOptions, setupNetworkListeners, showMonetizationCheckoutModal, skipWaiting, streamOllamaChat, subscriptionReducer, subscriptionSlice, syncAuthToCookies, tenantKeySchema, tenantSchema, topBannerReducer, topBannerSlice, translatePrompt, unregisterServiceWorker, updateFileMetadata, updateFileProgress, updateFileRetryCount, updateFileStatus, updateFileUrl, updateFileUrlFromWebSocket, updateRbacPermissions, uploadToS3, use402ErrorCheck, useAccessingPublicRoute, useAdvancedChat, useAuthContext, useAuthProvider, useAxdToken, useCachedSessionId, useChat, useChatFileUpload, useCurrentTenant, useDayJs, useDmToken, useEmbedMode, useEventCallback, useEventListener, useExternalPricingPlan, useFileDragDrop, useIsAdmin, useIsomorphicLayoutEffect, useLocalStorage, useMentorSettings, useMentorTools, useModelFileUploadCapabilities, useOS, useProfileImageUpload, useResponsive, useServiceWorker, useShowAttachment, useShowFreeTrialDialog, useShowVoiceCall, useShowVoiceRecorder, useStripeUpgrade, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTimeTracker, useTimeTrackerNative, useTimer, useUserAgreement, useUserData, useUserProfileUpdate, useUserTenants, useUsername, useVisitingTenant, useVoiceChat, useWelcome as useWelcomeMessage, userDataSchema, validateFile };
18376
+ export { ALPHANUMERIC_32_REGEX, ANONYMOUS_USERNAME, AuthContext, AuthContextProvider, AuthProvider, CHAT_AREA_SIZE, CacheKeys, DEFAULT_DISCLAIMER_CONTENT, DEFAULT_TENANT_SWITCH_LOCK_TTL_MS, LOCAL_STORAGE_KEYS, MAX_INITIAL_WEBSOCKET_CONNECTION_ATTEMPTS, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, MENTOR_VISIBILITY, MENTOR_VISIBILITY_VALUES, METADATAS, MentorProvider, REQUIRED_ACTIONS_FOR_GROUPS, RemoteEvents, STREAMING_CONTENT_BUFFER_THRESHOLD, STREAMING_CONTENT_FLUSH_INTERVAL, SUBSCRIPTION_MESSAGES, SUBSCRIPTION_PACKAGES, SUBSCRIPTION_PACKAGES_V2, SUBSCRIPTION_TRIGGERS, SUBSCRIPTION_V2_TRIGGERS, ServiceWorkerProvider, SubscriptionFlow, SubscriptionFlowV2, TENANT_SWITCH_CHANNEL, TENANT_SWITCH_LOCK_KEY, TOOLS, TOOL_NAME_MAP, TenantContext, TenantContextProvider, TenantProvider, TimeTracker, WithFormPermissions, WithPermissions, addFiles, addMessage, addProtocolToUrl, advancedTabs, advancedTabsProperties, appleRestrictionReducer, appleRestrictionSlice, chatActions, chatInputSlice, chatInputSliceActions, chatInputSliceReducer, chatInputSliceSelectors, chatSliceReducerShared, checkModelAvailable, checkOllamaHealth, checkRbacPermission, clearAllCaches, clearApiCache, clearAuthCookies, clearCookies, clearCurrentTenantCookie, clearFiles, clearMessages, clearTenantSwitchLock, combineCSVData, convertToOllamaMessages, createFileReference, createMultipleFileReferences, csvDataToText, defaultSessionIds, deleteCookie, deleteCookieOnAllDomains, enableChatActionsPopup, eventBus, fetchWithCache, filesReducer, filesSlice, formatRelativeTime, getAuthSpaJoinUrl, getCacheStatus, getCachedApiResponse, getCookieValue, getDomainParts, getFileInfo, getInitials, getLocalLLMSystemPrompt, getNextNavigation, getParentDomain, getPlatform, getPlatformKey, getServiceWorkerStatus, getStoredUserName, getTabId, getTimeAgo, getUserEmail, getUserName, handleLogout, handleTenantSwitch, hasNonExpiredAuthToken, hostChatReducer, hostChatSlice, initServiceWorker, isAlphaNumeric32, isExpo, isFileAccepted, isInIframe, isJSON, isLoggedIn, isNode, isReactNative, isSafariBrowser, isServiceWorkerSupported, isStripeActivated, isTauri, isTauriOfflineMode, isTenantSwitchInProgress, isWeb$2 as isWeb, loadMetadataConfig, markdownToPlainText, monetizationSlice, onStatusChange, onUpdate, parseCSV, preCacheMentorData, rbacReducer, readTenantSwitchLock, redirectToAuthSpa, redirectToAuthSpaJoinTenant, refreshTenantSwitchLock, registerServiceWorker, removeFile, requestPresignedUrl, safeRequire, selectActiveChatMessages, selectActiveTab, selectArtifactsEnabled, selectAttachedFiles, selectChats, selectCurrentStreamingArtifact, selectCurrentStreamingMessage, selectDocumentFilter, selectEnableChatActionsPopup, selectIframeContext, selectIsError, selectIsPending, selectIsReasoning, selectIsStopped, selectIsTyping, selectLastArtifactContentFlushTime, selectMetadata, selectNumberOfActiveChatMessages, selectRbacPermissions, selectSessionId, selectSessionIds, selectShouldStartNewChat, selectShowingSharedChat, selectStatus, selectStreaming, selectStreamingArtifactContentBuffer, selectStreamingArtifactFullContent, selectStreamingReasoningContent, selectStreamingToolCalls, selectToken, selectTokenEnabled, selectTools, sendMessageToParentWebsite, setAccessCheckResponse, setAdvancedDisplayMonetizationCheckoutModal, setCachedApiResponse, setCookieForAuth, setDisplayMonetizationCheckoutModal, setError402Detected, setFreeTrialUsageOptions, setOfflineStatus, setOpenAppleRestrictionModal, setOpenPricingModal, setPricingModalData, setSubscriptionStatus, setTauriMode, setTopBannerOptions, setupNetworkListeners, showMonetizationCheckoutModal, skipWaiting, streamOllamaChat, subscriptionReducer, subscriptionSlice, syncAuthToCookies, tenantKeySchema, tenantSchema, topBannerReducer, topBannerSlice, translatePrompt, unregisterServiceWorker, updateFileMetadata, updateFileProgress, updateFileRetryCount, updateFileStatus, updateFileUrl, updateFileUrlFromWebSocket, updateRbacPermissions, uploadToS3, use402ErrorCheck, useAccessingPublicRoute, useAdvancedChat, useAuthContext, useAuthProvider, useAxdToken, useCachedSessionId, useChat, useChatFileUpload, useCurrentTenant, useDayJs, useDmToken, useEmbedMode, useEventCallback, useEventListener, useExternalPricingPlan, useFileDragDrop, useIsAdmin, useIsomorphicLayoutEffect, useLocalStorage, useMentorSettings, useMentorTools, useModelFileUploadCapabilities, useOS, useProfileImageUpload, useResponsive, useServiceWorker, useShowAttachment, useShowFreeTrialDialog, useShowVoiceCall, useShowVoiceRecorder, useStripeUpgrade, useSubscriptionHandler, useSubscriptionHandlerV2, useTenantContext, useTenantMetadata, useTenantSwitchSync, useTimeTracker, useTimeTrackerNative, useTimer, useUserAgreement, useUserData, useUserProfileUpdate, useUserTenants, useUsername, useVisitingTenant, useVoiceChat, useWelcome as useWelcomeMessage, userDataSchema, validateFile, writeTenantSwitchLock };
18212
18377
  //# sourceMappingURL=index.esm.js.map