@iblai/web-utils 1.6.9 → 1.6.10

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.d.ts CHANGED
@@ -1438,8 +1438,9 @@ type Props$7 = {
1438
1438
  isOffline?: boolean;
1439
1439
  onOfflineWithoutLocalLLM?: () => void;
1440
1440
  isPublicRoute?: boolean;
1441
+ initialPrompt?: string;
1441
1442
  };
1442
- declare function useAdvancedChat({ tenantKey, mentorId, username, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, onOAuthRequired, onOAuthResolved, isOffline, onOfflineWithoutLocalLLM, isPublicRoute, }: Props$7): {
1443
+ declare function useAdvancedChat({ tenantKey, mentorId, username, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, onOAuthRequired, onOAuthResolved, isOffline, onOfflineWithoutLocalLLM, isPublicRoute, initialPrompt, }: Props$7): {
1443
1444
  messages: Message$1[];
1444
1445
  isStreaming: boolean;
1445
1446
  status: ChatStatus;
@@ -2025,7 +2026,7 @@ type Props$2 = {
2025
2026
  * 4. Handles redirects to auth SPA when needed
2026
2027
  * 5. Manages public route access state
2027
2028
  */
2028
- declare function AuthProvider({ children, fallback, middleware, onAuthSuccess, onAuthFailure, redirectToAuthSpa, hasNonExpiredAuthToken, username, pathname, storageService, token, enableStorageSync, skip, }: Props$2): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
2029
+ declare function AuthProvider({ children, fallback, middleware, onAuthSuccess, onAuthFailure, redirectToAuthSpa, hasNonExpiredAuthToken, username, pathname, storageService, token, enableStorageSync, skip, }: Props$2): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
2029
2030
 
2030
2031
  /**
2031
2032
  * Props for the MentorProvider component
@@ -2058,7 +2059,7 @@ type Props$1 = {
2058
2059
  * 3. Manages redirection based on mentor availability
2059
2060
  * 4. Integrates with tenant context for access control
2060
2061
  */
2061
- declare function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redirectToAuthSpa, redirectToMentor, onLoadMentorsPermissions, redirectToNoMentorsPage, redirectToCreateMentor, username, isAdmin, mainTenantKey, requestedMentorId, handleMentorNotFound, forceDetermineMentor, onComplete, skip, }: Props$1): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
2062
+ declare function MentorProvider({ children, fallback, onAuthSuccess, onAuthFailure, redirectToAuthSpa, redirectToMentor, onLoadMentorsPermissions, redirectToNoMentorsPage, redirectToCreateMentor, username, isAdmin, mainTenantKey, requestedMentorId, handleMentorNotFound, forceDetermineMentor, onComplete, skip, }: Props$1): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
2062
2063
 
2063
2064
  /**
2064
2065
  * Type definition for the tenant context
@@ -2122,7 +2123,7 @@ type Props = {
2122
2123
  * 4. Handles tenant-specific domain redirects
2123
2124
  * 5. Maintains tenant access state
2124
2125
  */
2125
- declare function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, currentTenant, requestedTenant, saveCurrentTenant, saveUserTenants, handleTenantSwitch, saveVisitingTenant, removeVisitingTenant, saveUserTokens, saveTenant, onAutoJoinUserToTenant, redirectToAuthSpa, username, isIframed, setUseMentorProvider, skip, onLoadPlatformPermissions, skipCustomDomainCheck, onTenantMismatch, }: Props): string | number | bigint | boolean | react_jsx_runtime.JSX.Element | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | null | undefined;
2126
+ declare function TenantProvider({ children, fallback, onAuthSuccess, onAuthFailure, currentTenant, requestedTenant, saveCurrentTenant, saveUserTenants, handleTenantSwitch, saveVisitingTenant, removeVisitingTenant, saveUserTokens, saveTenant, onAutoJoinUserToTenant, redirectToAuthSpa, username, isIframed, setUseMentorProvider, skip, onLoadPlatformPermissions, skipCustomDomainCheck, onTenantMismatch, }: Props): string | number | bigint | boolean | Iterable<React__default.ReactNode> | Promise<string | number | bigint | boolean | React__default.ReactPortal | React__default.ReactElement<unknown, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined> | react_jsx_runtime.JSX.Element | null | undefined;
2126
2127
 
2127
2128
  /**
2128
2129
  * Service Worker Registration Helper
package/dist/index.esm.js CHANGED
@@ -11020,8 +11020,16 @@ const chatSlice = createSlice({
11020
11020
  state.chats = action.payload;
11021
11021
  },
11022
11022
  appendMessageToActiveTab: (state) => {
11023
- const lastMessageInActiveTabMessages = state.chats[state.activeTab][state.chats[state.activeTab].length - 1];
11024
- if (lastMessageInActiveTabMessages.id !== state.currentStreamingMessage.id) {
11023
+ const activeMessages = state.chats[state.activeTab];
11024
+ // Defensive: if the active tab has no messages yet (e.g. a stale
11025
+ // `setNewMessages([])` raced ahead of the user-message dispatch),
11026
+ // skip the lookup that would throw on `undefined.id` and rely on
11027
+ // the no-last-message branch below to append the streaming message.
11028
+ const lastMessageInActiveTabMessages = activeMessages && activeMessages.length > 0
11029
+ ? activeMessages[activeMessages.length - 1]
11030
+ : undefined;
11031
+ if (!lastMessageInActiveTabMessages ||
11032
+ lastMessageInActiveTabMessages.id !== state.currentStreamingMessage.id) {
11025
11033
  const temp = {
11026
11034
  id: state.currentStreamingMessage.id,
11027
11035
  role: "assistant",
@@ -13530,7 +13538,7 @@ function useMentorSettings({ mentorId, tenantKey, isPublicRoute = false, mainTen
13530
13538
  };
13531
13539
  }
13532
13540
 
13533
- function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, onOAuthRequired, onOAuthResolved, isOffline = false, onOfflineWithoutLocalLLM, isPublicRoute, }) {
13541
+ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, token, wsUrl, stopGenerationWsUrl, redirectToAuthSpa, errorHandler, sendMessageToParentWebsite, isPreviewMode, mentorShareableToken, on402Error, cachedSessionId, onStartNewChat, onOAuthRequired, onOAuthResolved, isOffline = false, onOfflineWithoutLocalLLM, isPublicRoute, initialPrompt, }) {
13534
13542
  var _a, _b, _c, _d;
13535
13543
  const dispatch = useDispatch();
13536
13544
  const [createSessionId, { isLoading: isLoadingSessionIds }] = useCreateSessionIdMutation();
@@ -13585,10 +13593,41 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13585
13593
  onOfflineWithoutLocalLLM,
13586
13594
  });
13587
13595
  const [isLoadingChats, setIsLoadingChats] = useState(true);
13596
+ // Tracks whether the optional `initialPrompt` prop has already been
13597
+ // auto-submitted for this hook instance. Set to `true` the moment the
13598
+ // effect decides to act (either to send or because the prompt is already
13599
+ // the most recent user message) so subsequent re-renders never re-fire.
13600
+ const initialPromptInjectedRef = React__default.useRef(false);
13601
+ // Tracks whether a `startNewChat()` call is already mid-flight. The
13602
+ // auto-start `useEffect` below re-runs whenever any of its deps change
13603
+ // (`shouldStartNewChat` in particular toggles after mount in many
13604
+ // consumers), and the existing `!cachedSessionId?.[mentorId]` guard is
13605
+ // not enough on its own because `cachedSessionId` only updates AFTER
13606
+ // `createSessionId` resolves. Without this ref, dep churn during the
13607
+ // pending API call triggers a second `startNewChat`, producing two
13608
+ // sessions, two `getChats` calls, and an extra `setNewMessages([])`
13609
+ // that wipes anything dispatched (like `initialPrompt`'s
13610
+ // `addUserMessage`) between the two `getChats` resolutions.
13611
+ const startNewChatInFlightRef = React__default.useRef(false);
13612
+ // Synchronous mirror of `isLoadingChats`. The state setter is async, so a
13613
+ // consumer effect running in the SAME render cycle as `getChats()`
13614
+ // invocation sees a stale `isLoadingChats=false` in its closure even
13615
+ // though `setIsLoadingChats(true)` has been scheduled. The ref flips
13616
+ // immediately (before `getChats` hits its first `await`), giving the
13617
+ // `initialPrompt` injection effect a reliable "fetch in progress" signal
13618
+ // it can read synchronously inside its body.
13619
+ const isLoadingChatsRef = React__default.useRef(true);
13588
13620
  const [getSessionChats] = useLazyGetSessionIdQuery();
13589
13621
  const [getSharedSessionChats] = useLazyGetSharedSessionIdQuery();
13590
13622
  const getChats = React__default.useCallback(async () => {
13591
13623
  var _a, _b, _c, _d;
13624
+ // Mark the fetch as in-flight so consumers gated on `isLoadingChats`
13625
+ // (e.g. the `initialPrompt` injection effect) defer until the resulting
13626
+ // `setNewMessages` has landed. The state setter alone is not enough —
13627
+ // it's async and the consumer effect may run in the same render cycle
13628
+ // with the stale closure — so flip the synchronous ref FIRST.
13629
+ isLoadingChatsRef.current = true;
13630
+ setIsLoadingChats(true);
13592
13631
  let data;
13593
13632
  try {
13594
13633
  if (showingSharedChat) {
@@ -13673,6 +13712,7 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13673
13712
  }
13674
13713
  finally {
13675
13714
  setIsLoadingChats(false);
13715
+ isLoadingChatsRef.current = false;
13676
13716
  }
13677
13717
  }, [
13678
13718
  showingSharedChat,
@@ -13684,21 +13724,30 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13684
13724
  getSessionChats,
13685
13725
  dispatch,
13686
13726
  ]);
13727
+ // Depend on the indexed string session id, not the whole `cachedSessionId`
13728
+ // object. Consumers (e.g. `useLocalStorage`) often re-emit a new object
13729
+ // reference on every render even when the underlying value is unchanged,
13730
+ // which would otherwise cause this effect to re-fire `getChats()` per
13731
+ // render — and each `setNewMessages` would wipe any user message
13732
+ // dispatched in between (the URL `initialPrompt` race).
13733
+ const currentMentorSessionId = cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId];
13687
13734
  useEffect(() => {
13688
- if (cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId]) {
13689
- dispatch(chatActions.updateSessionIds(cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId]));
13735
+ if (currentMentorSessionId) {
13736
+ dispatch(chatActions.updateSessionIds(currentMentorSessionId));
13690
13737
  // Skip fetching previous chats when offline
13691
13738
  if (!isOffline) {
13692
13739
  getChats();
13693
13740
  }
13694
13741
  else {
13695
13742
  setIsLoadingChats(false);
13743
+ isLoadingChatsRef.current = false;
13696
13744
  }
13697
13745
  }
13698
13746
  else {
13699
13747
  setIsLoadingChats(false);
13748
+ isLoadingChatsRef.current = false;
13700
13749
  }
13701
- }, [cachedSessionId, isOffline]);
13750
+ }, [currentMentorSessionId, isOffline]);
13702
13751
  const startNewChat = React__default.useCallback(async () => {
13703
13752
  // Reset all chat state
13704
13753
  if (!showingSharedChat) {
@@ -13761,10 +13810,15 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13761
13810
  errorHandler,
13762
13811
  ]);
13763
13812
  React__default.useEffect(() => {
13813
+ if (startNewChatInFlightRef.current)
13814
+ return;
13764
13815
  if (!showingSharedChat || mentorSettings.allowAnonymous) {
13765
13816
  if (mentorSettings.allowAnonymous !== undefined &&
13766
13817
  !(cachedSessionId === null || cachedSessionId === void 0 ? void 0 : cachedSessionId[mentorId])) {
13767
- startNewChat();
13818
+ startNewChatInFlightRef.current = true;
13819
+ startNewChat().finally(() => {
13820
+ startNewChatInFlightRef.current = false;
13821
+ });
13768
13822
  }
13769
13823
  }
13770
13824
  }, [shouldStartNewChat, showingSharedChat, mentorSettings.allowAnonymous]);
@@ -13775,6 +13829,76 @@ function useAdvancedChat({ tenantKey, mentorId, username = ANONYMOUS_USERNAME, t
13775
13829
  resetConnection();
13776
13830
  }
13777
13831
  }, [sessionIds, activeTab, sessionId, resetConnection]);
13832
+ // Auto-submit `initialPrompt` exactly once per mount after the chat
13833
+ // lifecycle has stabilized. Matches the existing proactive-prompt
13834
+ // precedent in `changeTab` (sendMessage(tab, content)) but is gated on
13835
+ // URL-driven deep-link signals instead of tab transitions.
13836
+ React__default.useEffect(() => {
13837
+ var _a;
13838
+ if (initialPromptInjectedRef.current)
13839
+ return;
13840
+ const trimmedPrompt = initialPrompt === null || initialPrompt === void 0 ? void 0 : initialPrompt.trim();
13841
+ if (!trimmedPrompt)
13842
+ return;
13843
+ // Bail while any state would make auto-submission unsafe or premature.
13844
+ if (isPreviewMode)
13845
+ return;
13846
+ if (showingSharedChat)
13847
+ return;
13848
+ if (isOffline)
13849
+ return;
13850
+ // Two-layer guard: the state catches re-runs after `getChats` settles
13851
+ // (which schedule a new render). The ref catches the in-render race
13852
+ // where this effect runs in the same cycle that `getChats` was invoked
13853
+ // — the state setter is async and the closure has a stale `false`.
13854
+ if (isLoadingChats)
13855
+ return;
13856
+ if (isLoadingChatsRef.current)
13857
+ return;
13858
+ if (!sessionIds[activeTab])
13859
+ return;
13860
+ if (streaming)
13861
+ return;
13862
+ if (isPending)
13863
+ return;
13864
+ // Dedup: scan back through the loaded history for the most recent
13865
+ // user message and compare its content. If it matches, this session
13866
+ // already has the prompt as a user turn (typical case: reload of a
13867
+ // `?prompt=` URL — the cached history ends with the assistant reply,
13868
+ // so the matching user message is one step back).
13869
+ const messages = chats[activeTab];
13870
+ let lastUserMessage;
13871
+ if (messages && messages.length > 0) {
13872
+ for (let i = messages.length - 1; i >= 0; i--) {
13873
+ if (((_a = messages[i]) === null || _a === void 0 ? void 0 : _a.role) === "user") {
13874
+ lastUserMessage = messages[i];
13875
+ break;
13876
+ }
13877
+ }
13878
+ }
13879
+ if (lastUserMessage &&
13880
+ typeof lastUserMessage.content === "string" &&
13881
+ lastUserMessage.content === trimmedPrompt) {
13882
+ initialPromptInjectedRef.current = true;
13883
+ return;
13884
+ }
13885
+ // Mark BEFORE dispatching: sendMessage triggers state updates that may
13886
+ // re-run this effect, and we want the guard active before any re-fire.
13887
+ initialPromptInjectedRef.current = true;
13888
+ sendMessage(activeTab, trimmedPrompt);
13889
+ }, [
13890
+ initialPrompt,
13891
+ isPreviewMode,
13892
+ showingSharedChat,
13893
+ isOffline,
13894
+ isLoadingChats,
13895
+ sessionIds,
13896
+ activeTab,
13897
+ chats,
13898
+ streaming,
13899
+ isPending,
13900
+ sendMessage,
13901
+ ]);
13778
13902
  async function changeTab(tab) {
13779
13903
  var _a, _b, _c, _d;
13780
13904
  // while responding to a message, do not change the tab