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

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.mjs CHANGED
@@ -12,7 +12,7 @@ var DEBOUNCE_MS = 220;
12
12
  var MAX_VISIBLE_SUGGESTIONS = 6;
13
13
  var SEARCH_PLACEHOLDER = "Search by product name, ingredient, benefits, or SKU";
14
14
  var TRIGGER_PLACEHOLDER = "Product Search";
15
- var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/chat";
15
+ var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
16
16
  var DEFAULT_AI_CHAT_CONVERSATIONS_URL = `${DEFAULT_AI_CHAT_BASE_URL}/conversations`;
17
17
  var DEFAULT_AI_CHAT_MESSAGES_URL = `${DEFAULT_AI_CHAT_BASE_URL}/messages`;
18
18
 
@@ -337,17 +337,13 @@ function createSearchService(options = {}) {
337
337
  if (!trimmedQuery) return void 0;
338
338
  addRecentHeaderSearch(localeParts, trimmedQuery, storage);
339
339
  const optionHref = resolveOptionHref(option, localeParts);
340
- if (optionHref && locationLike && typeof locationLike.assign === "function") {
341
- locationLike.assign(optionHref);
340
+ if (optionHref) {
342
341
  return stripLocalePrefix(optionHref, localeParts);
343
342
  }
344
343
  const targetPath = buildSearchResultsPath(
345
344
  trimmedQuery,
346
345
  localeParts
347
346
  );
348
- if (locationLike && typeof locationLike.assign === "function") {
349
- locationLike.assign(targetPath);
350
- }
351
347
  return stripLocalePrefix(targetPath, localeParts);
352
348
  },
353
349
  activateSuggestionAction(option) {
@@ -360,9 +356,7 @@ function createSearchService(options = {}) {
360
356
  option,
361
357
  localeParts
362
358
  );
363
- if (optionHref2 && locationLike && typeof locationLike.assign === "function") {
364
- locationLike.assign(optionHref2);
365
- } else {
359
+ if (!optionHref2) {
366
360
  const optionQuery2 = option?.value ?? option?.label ?? option?.identifier ?? "";
367
361
  service.submitProductSearch(
368
362
  String(optionQuery2)
@@ -381,9 +375,8 @@ function createSearchService(options = {}) {
381
375
  }
382
376
  }
383
377
  const optionHref = resolveOptionHref(option, localeParts);
384
- if (optionHref && locationLike && typeof locationLike.assign === "function") {
385
- locationLike.assign(optionHref);
386
- return optionHref;
378
+ if (optionHref) {
379
+ return stripLocalePrefix(optionHref, localeParts);
387
380
  }
388
381
  const optionQuery = option?.value ?? option?.label ?? option?.identifier ?? "";
389
382
  return service.submitProductSearch(String(optionQuery));
@@ -763,6 +756,19 @@ function findClosingFence(content, fromIndex) {
763
756
  }
764
757
  return -1;
765
758
  }
759
+ function isRecord(value) {
760
+ return value !== null && typeof value === "object";
761
+ }
762
+ function getRenderProductListItems(event) {
763
+ if (event["type"] !== "render" || event["pattern"] !== "product_list") {
764
+ return [];
765
+ }
766
+ const data = event["data"];
767
+ if (!isRecord(data) || !Array.isArray(data["items"])) {
768
+ return [];
769
+ }
770
+ return data["items"].filter(isRecord);
771
+ }
766
772
  function extractProductsFromMarkdown(content) {
767
773
  const products = [];
768
774
  const summaryParts = [];
@@ -791,7 +797,8 @@ function extractProductsFromMarkdown(content) {
791
797
  if (Array.isArray(parsed)) {
792
798
  products.push(...parsed);
793
799
  }
794
- } catch {
800
+ } catch (e) {
801
+ console.error("Failed to parse products block:", e);
795
802
  }
796
803
  summaryParts.push(content.slice(summaryCursor, start));
797
804
  summaryCursor = end + MARKDOWN_CLOSING_FENCE.length;
@@ -804,8 +811,10 @@ function createChatService(options) {
804
811
  const {
805
812
  conversationsUrl = DEFAULT_AI_CHAT_CONVERSATIONS_URL,
806
813
  messagesUrl = DEFAULT_AI_CHAT_MESSAGES_URL,
807
- tenantId,
808
- getAuthToken,
814
+ salesAiAppKey,
815
+ salesAiParentOrigin,
816
+ market,
817
+ language,
809
818
  fetchImpl: fetchImplOption
810
819
  } = options;
811
820
  let fetchImpl;
@@ -818,22 +827,30 @@ function createChatService(options) {
818
827
  throw new Error("ChatService: no fetch implementation available");
819
828
  };
820
829
  }
821
- function buildHeaders(extra) {
830
+ function buildHeaders(extra, includeJsonContentType = true) {
822
831
  const headers = {
823
- "Content-Type": "application/json",
824
832
  ...extra
825
833
  };
826
- if (tenantId) headers["X-Tenant-Id"] = tenantId;
827
- const token = getAuthToken?.();
828
- if (token) headers["Authorization"] = `Bearer ${token}`;
834
+ if (includeJsonContentType) {
835
+ headers["Content-Type"] = "application/json";
836
+ }
837
+ const accessToken = typeof window !== "undefined" ? window.localStorage?.getItem("accessToken") : null;
838
+ if (accessToken) {
839
+ headers["Authorization"] = `Bearer ${accessToken}`;
840
+ }
841
+ if (salesAiAppKey) {
842
+ headers["X-Sales-AI-App-Key"] = salesAiAppKey;
843
+ }
844
+ if (salesAiParentOrigin) {
845
+ headers["X-Sales-AI-Parent-Origin"] = salesAiParentOrigin;
846
+ }
829
847
  return headers;
830
848
  }
831
849
  return {
832
- async createConversation(title = "Search Session") {
850
+ async createConversation() {
833
851
  const res = await fetchImpl(conversationsUrl, {
834
852
  method: "POST",
835
- headers: buildHeaders(),
836
- body: JSON.stringify({ title })
853
+ headers: buildHeaders(void 0, false)
837
854
  });
838
855
  if (!res.ok) {
839
856
  throw new Error(
@@ -850,14 +867,26 @@ function createChatService(options) {
850
867
  return id;
851
868
  },
852
869
  async sendMessage(conversationId, content, onChunk) {
870
+ const requestBody = {
871
+ conversation_id: conversationId,
872
+ content
873
+ };
874
+ if (market || language) {
875
+ requestBody.scope_context = {
876
+ market: market ?? "US",
877
+ language: language ?? "en"
878
+ };
879
+ }
880
+ requestBody.client_capabilities = {
881
+ patterns: {
882
+ product_list: [1],
883
+ follow_up: [1]
884
+ }
885
+ };
853
886
  const res = await fetchImpl(messagesUrl, {
854
887
  method: "POST",
855
- headers: buildHeaders({ Accept: "application/json" }),
856
- body: JSON.stringify({
857
- conversation_id: conversationId,
858
- content,
859
- compact: false
860
- })
888
+ headers: buildHeaders({ Accept: "text/event-stream" }),
889
+ body: JSON.stringify(requestBody)
861
890
  });
862
891
  if (!res.ok) {
863
892
  throw new Error(
@@ -865,6 +894,7 @@ function createChatService(options) {
865
894
  );
866
895
  }
867
896
  let accumulated = "";
897
+ const renderedProducts = [];
868
898
  const parser = createParser({
869
899
  onEvent(event) {
870
900
  const data = event.data;
@@ -873,6 +903,9 @@ function createChatService(options) {
873
903
  const parsed = JSON.parse(data);
874
904
  const parsedType = parsed["type"];
875
905
  const type = typeof parsedType === "string" ? parsedType : "";
906
+ renderedProducts.push(
907
+ ...getRenderProductListItems(parsed)
908
+ );
876
909
  if (type === "content" && typeof parsed["text"] === "string") {
877
910
  const text = parsed["text"];
878
911
  accumulated += text;
@@ -900,7 +933,12 @@ function createChatService(options) {
900
933
  parser.feed(rawBody);
901
934
  }
902
935
  const { summary, products } = extractProductsFromMarkdown(accumulated);
903
- return { summary, products, rawContent: accumulated };
936
+ const finalProducts = renderedProducts.length > 0 ? renderedProducts : products;
937
+ return {
938
+ summary,
939
+ products: finalProducts,
940
+ rawContent: accumulated
941
+ };
904
942
  }
905
943
  };
906
944
  }
@@ -1647,6 +1685,7 @@ function getSpeechRecognition() {
1647
1685
  }
1648
1686
  function useVoiceSearch({
1649
1687
  lang,
1688
+ onInterimResult,
1650
1689
  onResult,
1651
1690
  onError
1652
1691
  }) {
@@ -1664,11 +1703,24 @@ function useVoiceSearch({
1664
1703
  stop();
1665
1704
  const rec = new SR();
1666
1705
  rec.lang = lang ?? navigator.language ?? "en-US";
1667
- rec.interimResults = false;
1706
+ rec.interimResults = true;
1668
1707
  rec.maxAlternatives = 1;
1669
1708
  rec.onresult = (event) => {
1670
- const transcript = event.results[0]?.[0]?.transcript ?? "";
1671
- if (transcript) onResult(transcript);
1709
+ let finalTranscript = "";
1710
+ let interimTranscript = "";
1711
+ const startIndex = event.resultIndex ?? 0;
1712
+ for (let index = startIndex; index < event.results.length; index += 1) {
1713
+ const result = event.results[index];
1714
+ const transcript = result?.[0]?.transcript ?? "";
1715
+ if (!transcript) continue;
1716
+ if (result?.isFinal) {
1717
+ finalTranscript += transcript;
1718
+ } else {
1719
+ interimTranscript += transcript;
1720
+ }
1721
+ }
1722
+ if (interimTranscript) onInterimResult?.(interimTranscript);
1723
+ if (finalTranscript) onResult(finalTranscript);
1672
1724
  };
1673
1725
  rec.onerror = (event) => {
1674
1726
  onError?.(event);
@@ -1678,7 +1730,7 @@ function useVoiceSearch({
1678
1730
  recognitionRef.current = rec;
1679
1731
  rec.start();
1680
1732
  setListening(true);
1681
- }, [lang, onResult, onError, stop]);
1733
+ }, [lang, onInterimResult, onResult, onError, stop]);
1682
1734
  return { listening, supported, start, stop };
1683
1735
  }
1684
1736