@nuskin/react-search-sdk 1.0.0-comx-2233.1 → 1.0.0-comx-2233.2

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.mts CHANGED
@@ -292,10 +292,14 @@ interface ChatServiceOptions {
292
292
  conversationsUrl?: string;
293
293
  /** POST endpoint for sending a message (SSE response). */
294
294
  messagesUrl?: string;
295
- /** X-Tenant-Id header value (used for conversations call). */
296
- tenantId?: string;
297
- /** Returns the current Bearer token, or null if unauthenticated. */
298
- getAuthToken?: () => string | null;
295
+ /** X-Sales-AI-App-Key header value (from CS configurations). */
296
+ salesAiAppKey?: string;
297
+ /** X-Sales-AI-Parent-Origin header value (from CS configurations). */
298
+ salesAiParentOrigin?: string;
299
+ /** Market code (e.g., "US", "JP") for scope_context in message requests. */
300
+ market?: string;
301
+ /** Language code (e.g., "en", "ja") for scope_context in message requests. */
302
+ language?: string;
299
303
  /** Override fetch for testing. */
300
304
  fetchImpl?: typeof fetch | null;
301
305
  }
@@ -385,6 +389,7 @@ interface SpeechRecognitionErrorEventLike {
385
389
  interface UseVoiceSearchOptions {
386
390
  lang?: string;
387
391
  onResult: (transcript: string) => void;
392
+ onInterimResult?: (transcript: string) => void;
388
393
  onError?: (error: SpeechRecognitionErrorEventLike) => void;
389
394
  }
390
395
  interface UseVoiceSearchResult {
@@ -393,7 +398,7 @@ interface UseVoiceSearchResult {
393
398
  start: () => void;
394
399
  stop: () => void;
395
400
  }
396
- declare function useVoiceSearch({ lang, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
401
+ declare function useVoiceSearch({ lang, onInterimResult, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
397
402
 
398
403
  /**
399
404
  * Catalog-product normalisation helpers — Phase 0 lift-and-shift of the
@@ -502,12 +507,12 @@ declare const TRIGGER_PLACEHOLDER = "Product Search";
502
507
  /**
503
508
  * Full path segment for the AI commerce chat SSE endpoint.
504
509
  * Appended to the consumer-provided `chatBaseUrl`.
505
- * Source: https://admin.tst.nuskin.ai/api/analytics/v1/commerce/chat
510
+ * Source: https://admin.tst.nuskin.ai/api/analytics/v1/embed
506
511
  */
507
- declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat";
512
+ declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
508
513
  /** Default POST endpoint for creating an AI chat conversation. */
509
- declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat/conversations";
514
+ declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/conversations";
510
515
  /** Default POST endpoint for sending AI chat messages. */
511
- declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat/messages";
516
+ declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/messages";
512
517
 
513
518
  export { CATALOG_SUGGESTIONS_PATH, type CatalogSuggestionItem, type ChatMessageResult, type ChatProduct, type ChatService, type ChatServiceOptions, DEBOUNCE_MS, DEFAULT_AI_CHAT_BASE_URL, DEFAULT_AI_CHAT_CONVERSATIONS_URL, DEFAULT_AI_CHAT_MESSAGES_URL, DEFAULT_SEARCH_PATH, DEFAULT_SUGGESTION_PAGE, DEFAULT_SUGGESTION_SIZE, type DidYouMeanItem, type GetMarketConfig, type LocaleParts, type LocationLike, MAX_VISIBLE_SUGGESTIONS, type MarketConfig, type ProductType, RECENT_SEARCHES_DEFAULT_LIMIT, RECENT_SEARCHES_PREFIX, SEARCH_PLACEHOLDER, STOREFRONT_SHOPPING_CONTEXT, type SearchChromeCopy, type SearchHistoryEntry, type SearchHistoryState, type SearchHistoryEntry as SearchHistoryStoreEntry, type SearchService, type SearchServiceOptions, type SuggestionOption, type SuggestionsResponse, TRIGGER_PLACEHOLDER, type UseSearchHistoryResult, type UseSearchSuggestionsOptions, type UseSearchSuggestionsResult, type UseVoiceSearchOptions, type UseVoiceSearchResult, createChatService, createSearchService, getImageFromProperty, getIngredientTerms, getSeeMoreResultsPayload, getVisibleSuggestions, isCartActionDisabled, isSellableProductStatus, isSuggestionDisabled, mapCatalogProductToSuggestion, normalizeDidYouMean, resolveApiGatewayPath, resolveSuggestionImageSrc, useSearchHistory, useSearchHistoryStore, useSearchSuggestions, useVoiceSearch };
package/dist/index.d.ts CHANGED
@@ -292,10 +292,14 @@ interface ChatServiceOptions {
292
292
  conversationsUrl?: string;
293
293
  /** POST endpoint for sending a message (SSE response). */
294
294
  messagesUrl?: string;
295
- /** X-Tenant-Id header value (used for conversations call). */
296
- tenantId?: string;
297
- /** Returns the current Bearer token, or null if unauthenticated. */
298
- getAuthToken?: () => string | null;
295
+ /** X-Sales-AI-App-Key header value (from CS configurations). */
296
+ salesAiAppKey?: string;
297
+ /** X-Sales-AI-Parent-Origin header value (from CS configurations). */
298
+ salesAiParentOrigin?: string;
299
+ /** Market code (e.g., "US", "JP") for scope_context in message requests. */
300
+ market?: string;
301
+ /** Language code (e.g., "en", "ja") for scope_context in message requests. */
302
+ language?: string;
299
303
  /** Override fetch for testing. */
300
304
  fetchImpl?: typeof fetch | null;
301
305
  }
@@ -385,6 +389,7 @@ interface SpeechRecognitionErrorEventLike {
385
389
  interface UseVoiceSearchOptions {
386
390
  lang?: string;
387
391
  onResult: (transcript: string) => void;
392
+ onInterimResult?: (transcript: string) => void;
388
393
  onError?: (error: SpeechRecognitionErrorEventLike) => void;
389
394
  }
390
395
  interface UseVoiceSearchResult {
@@ -393,7 +398,7 @@ interface UseVoiceSearchResult {
393
398
  start: () => void;
394
399
  stop: () => void;
395
400
  }
396
- declare function useVoiceSearch({ lang, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
401
+ declare function useVoiceSearch({ lang, onInterimResult, onResult, onError, }: UseVoiceSearchOptions): UseVoiceSearchResult;
397
402
 
398
403
  /**
399
404
  * Catalog-product normalisation helpers — Phase 0 lift-and-shift of the
@@ -502,12 +507,12 @@ declare const TRIGGER_PLACEHOLDER = "Product Search";
502
507
  /**
503
508
  * Full path segment for the AI commerce chat SSE endpoint.
504
509
  * Appended to the consumer-provided `chatBaseUrl`.
505
- * Source: https://admin.tst.nuskin.ai/api/analytics/v1/commerce/chat
510
+ * Source: https://admin.tst.nuskin.ai/api/analytics/v1/embed
506
511
  */
507
- declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat";
512
+ declare const DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
508
513
  /** Default POST endpoint for creating an AI chat conversation. */
509
- declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat/conversations";
514
+ declare const DEFAULT_AI_CHAT_CONVERSATIONS_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/conversations";
510
515
  /** Default POST endpoint for sending AI chat messages. */
511
- declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat/messages";
516
+ declare const DEFAULT_AI_CHAT_MESSAGES_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed/messages";
512
517
 
513
518
  export { CATALOG_SUGGESTIONS_PATH, type CatalogSuggestionItem, type ChatMessageResult, type ChatProduct, type ChatService, type ChatServiceOptions, DEBOUNCE_MS, DEFAULT_AI_CHAT_BASE_URL, DEFAULT_AI_CHAT_CONVERSATIONS_URL, DEFAULT_AI_CHAT_MESSAGES_URL, DEFAULT_SEARCH_PATH, DEFAULT_SUGGESTION_PAGE, DEFAULT_SUGGESTION_SIZE, type DidYouMeanItem, type GetMarketConfig, type LocaleParts, type LocationLike, MAX_VISIBLE_SUGGESTIONS, type MarketConfig, type ProductType, RECENT_SEARCHES_DEFAULT_LIMIT, RECENT_SEARCHES_PREFIX, SEARCH_PLACEHOLDER, STOREFRONT_SHOPPING_CONTEXT, type SearchChromeCopy, type SearchHistoryEntry, type SearchHistoryState, type SearchHistoryEntry as SearchHistoryStoreEntry, type SearchService, type SearchServiceOptions, type SuggestionOption, type SuggestionsResponse, TRIGGER_PLACEHOLDER, type UseSearchHistoryResult, type UseSearchSuggestionsOptions, type UseSearchSuggestionsResult, type UseVoiceSearchOptions, type UseVoiceSearchResult, createChatService, createSearchService, getImageFromProperty, getIngredientTerms, getSeeMoreResultsPayload, getVisibleSuggestions, isCartActionDisabled, isSellableProductStatus, isSuggestionDisabled, mapCatalogProductToSuggestion, normalizeDidYouMean, resolveApiGatewayPath, resolveSuggestionImageSrc, useSearchHistory, useSearchHistoryStore, useSearchSuggestions, useVoiceSearch };
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ var DEBOUNCE_MS = 220;
18
18
  var MAX_VISIBLE_SUGGESTIONS = 6;
19
19
  var SEARCH_PLACEHOLDER = "Search by product name, ingredient, benefits, or SKU";
20
20
  var TRIGGER_PLACEHOLDER = "Product Search";
21
- var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat";
21
+ var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
22
22
  var DEFAULT_AI_CHAT_CONVERSATIONS_URL = `${DEFAULT_AI_CHAT_BASE_URL}/conversations`;
23
23
  var DEFAULT_AI_CHAT_MESSAGES_URL = `${DEFAULT_AI_CHAT_BASE_URL}/messages`;
24
24
 
@@ -769,6 +769,19 @@ function findClosingFence(content, fromIndex) {
769
769
  }
770
770
  return -1;
771
771
  }
772
+ function isRecord(value) {
773
+ return value !== null && typeof value === "object";
774
+ }
775
+ function getRenderProductListItems(event) {
776
+ if (event["type"] !== "render" || event["pattern"] !== "product_list") {
777
+ return [];
778
+ }
779
+ const data = event["data"];
780
+ if (!isRecord(data) || !Array.isArray(data["items"])) {
781
+ return [];
782
+ }
783
+ return data["items"].filter(isRecord);
784
+ }
772
785
  function extractProductsFromMarkdown(content) {
773
786
  const products = [];
774
787
  const summaryParts = [];
@@ -797,7 +810,8 @@ function extractProductsFromMarkdown(content) {
797
810
  if (Array.isArray(parsed)) {
798
811
  products.push(...parsed);
799
812
  }
800
- } catch {
813
+ } catch (e) {
814
+ console.error("Failed to parse products block:", e);
801
815
  }
802
816
  summaryParts.push(content.slice(summaryCursor, start));
803
817
  summaryCursor = end + MARKDOWN_CLOSING_FENCE.length;
@@ -810,8 +824,10 @@ function createChatService(options) {
810
824
  const {
811
825
  conversationsUrl = DEFAULT_AI_CHAT_CONVERSATIONS_URL,
812
826
  messagesUrl = DEFAULT_AI_CHAT_MESSAGES_URL,
813
- tenantId,
814
- getAuthToken,
827
+ salesAiAppKey,
828
+ salesAiParentOrigin,
829
+ market,
830
+ language,
815
831
  fetchImpl: fetchImplOption
816
832
  } = options;
817
833
  let fetchImpl;
@@ -824,22 +840,30 @@ function createChatService(options) {
824
840
  throw new Error("ChatService: no fetch implementation available");
825
841
  };
826
842
  }
827
- function buildHeaders(extra) {
843
+ function buildHeaders(extra, includeJsonContentType = true) {
828
844
  const headers = {
829
- "Content-Type": "application/json",
830
845
  ...extra
831
846
  };
832
- if (tenantId) headers["X-Tenant-Id"] = tenantId;
833
- const token = getAuthToken?.();
834
- if (token) headers["Authorization"] = `Bearer ${token}`;
847
+ if (includeJsonContentType) {
848
+ headers["Content-Type"] = "application/json";
849
+ }
850
+ const accessToken = typeof window !== "undefined" ? window.localStorage?.getItem("accessToken") : null;
851
+ if (accessToken) {
852
+ headers["Authorization"] = `Bearer ${accessToken}`;
853
+ }
854
+ if (salesAiAppKey) {
855
+ headers["X-Sales-AI-App-Key"] = salesAiAppKey;
856
+ }
857
+ if (salesAiParentOrigin) {
858
+ headers["X-Sales-AI-Parent-Origin"] = salesAiParentOrigin;
859
+ }
835
860
  return headers;
836
861
  }
837
862
  return {
838
- async createConversation(title = "Search Session") {
863
+ async createConversation() {
839
864
  const res = await fetchImpl(conversationsUrl, {
840
865
  method: "POST",
841
- headers: buildHeaders(),
842
- body: JSON.stringify({ title })
866
+ headers: buildHeaders(void 0, false)
843
867
  });
844
868
  if (!res.ok) {
845
869
  throw new Error(
@@ -856,14 +880,26 @@ function createChatService(options) {
856
880
  return id;
857
881
  },
858
882
  async sendMessage(conversationId, content, onChunk) {
883
+ const requestBody = {
884
+ conversation_id: conversationId,
885
+ content
886
+ };
887
+ if (market || language) {
888
+ requestBody.scope_context = {
889
+ market: market ?? "US",
890
+ language: language ?? "en"
891
+ };
892
+ }
893
+ requestBody.client_capabilities = {
894
+ patterns: {
895
+ product_list: [1],
896
+ follow_up: [1]
897
+ }
898
+ };
859
899
  const res = await fetchImpl(messagesUrl, {
860
900
  method: "POST",
861
- headers: buildHeaders({ Accept: "application/json" }),
862
- body: JSON.stringify({
863
- conversation_id: conversationId,
864
- content,
865
- compact: false
866
- })
901
+ headers: buildHeaders({ Accept: "text/event-stream" }),
902
+ body: JSON.stringify(requestBody)
867
903
  });
868
904
  if (!res.ok) {
869
905
  throw new Error(
@@ -871,6 +907,7 @@ function createChatService(options) {
871
907
  );
872
908
  }
873
909
  let accumulated = "";
910
+ const renderedProducts = [];
874
911
  const parser = createParser({
875
912
  onEvent(event) {
876
913
  const data = event.data;
@@ -879,6 +916,9 @@ function createChatService(options) {
879
916
  const parsed = JSON.parse(data);
880
917
  const parsedType = parsed["type"];
881
918
  const type = typeof parsedType === "string" ? parsedType : "";
919
+ renderedProducts.push(
920
+ ...getRenderProductListItems(parsed)
921
+ );
882
922
  if (type === "content" && typeof parsed["text"] === "string") {
883
923
  const text = parsed["text"];
884
924
  accumulated += text;
@@ -906,7 +946,12 @@ function createChatService(options) {
906
946
  parser.feed(rawBody);
907
947
  }
908
948
  const { summary, products } = extractProductsFromMarkdown(accumulated);
909
- return { summary, products, rawContent: accumulated };
949
+ const finalProducts = renderedProducts.length > 0 ? renderedProducts : products;
950
+ return {
951
+ summary,
952
+ products: finalProducts,
953
+ rawContent: accumulated
954
+ };
910
955
  }
911
956
  };
912
957
  }
@@ -1653,6 +1698,7 @@ function getSpeechRecognition() {
1653
1698
  }
1654
1699
  function useVoiceSearch({
1655
1700
  lang,
1701
+ onInterimResult,
1656
1702
  onResult,
1657
1703
  onError
1658
1704
  }) {
@@ -1670,11 +1716,24 @@ function useVoiceSearch({
1670
1716
  stop();
1671
1717
  const rec = new SR();
1672
1718
  rec.lang = lang ?? navigator.language ?? "en-US";
1673
- rec.interimResults = false;
1719
+ rec.interimResults = true;
1674
1720
  rec.maxAlternatives = 1;
1675
1721
  rec.onresult = (event) => {
1676
- const transcript = event.results[0]?.[0]?.transcript ?? "";
1677
- if (transcript) onResult(transcript);
1722
+ let finalTranscript = "";
1723
+ let interimTranscript = "";
1724
+ const startIndex = event.resultIndex ?? 0;
1725
+ for (let index = startIndex; index < event.results.length; index += 1) {
1726
+ const result = event.results[index];
1727
+ const transcript = result?.[0]?.transcript ?? "";
1728
+ if (!transcript) continue;
1729
+ if (result?.isFinal) {
1730
+ finalTranscript += transcript;
1731
+ } else {
1732
+ interimTranscript += transcript;
1733
+ }
1734
+ }
1735
+ if (interimTranscript) onInterimResult?.(interimTranscript);
1736
+ if (finalTranscript) onResult(finalTranscript);
1678
1737
  };
1679
1738
  rec.onerror = (event) => {
1680
1739
  onError?.(event);
@@ -1684,7 +1743,7 @@ function useVoiceSearch({
1684
1743
  recognitionRef.current = rec;
1685
1744
  rec.start();
1686
1745
  setListening(true);
1687
- }, [lang, onResult, onError, stop]);
1746
+ }, [lang, onInterimResult, onResult, onError, stop]);
1688
1747
  return { listening, supported, start, stop };
1689
1748
  }
1690
1749