@nuskin/react-search-sdk 1.0.0-comx-2233.4 → 1.0.0-trying-again.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.mjs CHANGED
@@ -1,4 +1,10 @@
1
- import React, { useState, useRef, useCallback, useEffect } from 'react';
1
+ import { lazy, useState, useEffect, useRef, useMemo, useCallback, Suspense, Component } from 'react';
2
+ import configurationSdk from '@nuskin/configuration-sdk';
3
+ import { SearchModal } from '@nuskin/react-search-components';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/components/CommerceSearch/CommerceSearch.tsx
7
+ var { getConfiguration, getEnvironmentCode } = configurationSdk;
2
8
 
3
9
  // src/constants.ts
4
10
  var STOREFRONT_SHOPPING_CONTEXT = "storefront";
@@ -9,16 +15,77 @@ var DEFAULT_SUGGESTION_PAGE = 1;
9
15
  var DEFAULT_SUGGESTION_SIZE = 6;
10
16
  var DEFAULT_SEARCH_PATH = "/search";
11
17
  var DEBOUNCE_MS = 220;
18
+ var MINIMUM_SEARCH_CHAR_LENGTH = 2;
12
19
  var MAX_VISIBLE_SUGGESTIONS = 6;
13
- var SEARCH_PLACEHOLDER = "Search by product name, ingredient, benefits, or SKU";
14
- var TRIGGER_PLACEHOLDER = "Product Search";
15
- var DEFAULT_AI_CHAT_BASE_URL = "https://admin.tst.nuskin.ai/api/analytics/v1/embed";
16
- var DEFAULT_AI_CHAT_CONVERSATIONS_URL = `${DEFAULT_AI_CHAT_BASE_URL}/conversations`;
17
- var DEFAULT_AI_CHAT_MESSAGES_URL = `${DEFAULT_AI_CHAT_BASE_URL}/messages`;
20
+ var EQUINOX_MARKETS_MAP = "Equinox_Markets";
21
+ var DEFAULT_EQUINOX_CLIENT_ID = "63056848d7532f9923f3d08cb48c423b";
22
+
23
+ // src/config/commerceSearchConfig.ts
24
+ var COMMERCE_SEARCH_CONFIG_DEFAULTS = Object.freeze({
25
+ minimumSearchLength: MINIMUM_SEARCH_CHAR_LENGTH
26
+ });
27
+ var TEST_ENVIRONMENT_VALUES = {
28
+ agent: {
29
+ appKey: "pk_commerce_product_advisor_624aea4305b09240e8c8f41d0f6527b2",
30
+ apiBaseUrl: "https://nui.tst.nuskin.ai/"
31
+ }
32
+ };
33
+ function nonEmptyString(value) {
34
+ return typeof value === "string" ? value.trim() : "";
35
+ }
36
+ function normalizeCommerceSearchConfig(raw, { language } = {}) {
37
+ const minimumSearchLength = Number(raw?.minimumSearchLength);
38
+ const appKey = nonEmptyString(raw?.agent?.appKey);
39
+ const apiBaseUrl = nonEmptyString(raw?.agent?.apiBaseUrl);
40
+ return {
41
+ minimumSearchLength: Number.isInteger(minimumSearchLength) && minimumSearchLength >= 1 ? minimumSearchLength : COMMERCE_SEARCH_CONFIG_DEFAULTS.minimumSearchLength,
42
+ ...appKey && apiBaseUrl ? {
43
+ agent: {
44
+ appKey,
45
+ apiBaseUrl,
46
+ ...language ? { language } : {}
47
+ }
48
+ } : {}
49
+ };
50
+ }
51
+ async function fetchCommerceSearchConfig({
52
+ language
53
+ } = {}) {
54
+ const raw = getEnvironmentCode() === "test" ? TEST_ENVIRONMENT_VALUES : {};
55
+ return normalizeCommerceSearchConfig(raw, language ? { language } : {});
56
+ }
57
+
58
+ // src/hooks/useCommerceSearchConfig.ts
59
+ function useCommerceSearchConfig({
60
+ country,
61
+ language,
62
+ enabled
63
+ }) {
64
+ const [config, setConfig] = useState(
65
+ COMMERCE_SEARCH_CONFIG_DEFAULTS
66
+ );
67
+ useEffect(() => {
68
+ if (!enabled) return void 0;
69
+ let isStale = false;
70
+ fetchCommerceSearchConfig({
71
+ ...country ? { country } : {},
72
+ ...language ? { language } : {}
73
+ }).then((next) => {
74
+ if (!isStale) setConfig(next);
75
+ }).catch((error) => {
76
+ console.warn(
77
+ "[search] Could not load the product search configuration:",
78
+ error
79
+ );
80
+ });
81
+ return () => {
82
+ isStale = true;
83
+ };
84
+ }, [enabled, country, language]);
85
+ return config;
86
+ }
18
87
 
19
88
  // src/services/SearchApiService.ts
20
- var DEFAULT_SEARCH_PLACEHOLDER = TRIGGER_PLACEHOLDER;
21
- var DEFAULT_INPUT_ARIA_LABEL = SEARCH_PLACEHOLDER;
22
89
  function canUseStorage(storage) {
23
90
  return Boolean(storage) && typeof storage.getItem === "function";
24
91
  }
@@ -37,7 +104,7 @@ function normalizeLocaleParts({
37
104
  country,
38
105
  language,
39
106
  locale
40
- } = {}) {
107
+ }) {
41
108
  const normalizedCountry = String(country ?? "").trim().toUpperCase();
42
109
  const normalizedLanguage = String(language ?? "").trim().toLowerCase();
43
110
  const normalizedLocale = String(locale ?? "").trim() || "UNKNOWN";
@@ -228,14 +295,18 @@ function resolveOptionHref(option, localeParts) {
228
295
  }
229
296
  return "";
230
297
  }
231
- async function fetchJsonFromCandidates(fetchImpl, requestUrls) {
298
+ function resolveCustomerTypeHeaders(getCustomerType) {
299
+ const customerType = getCustomerType?.()?.trim();
300
+ return customerType ? { "X-Customer-Type": customerType } : void 0;
301
+ }
302
+ async function fetchJsonFromCandidates(fetchImpl, requestUrls, requestHeaders = {}) {
232
303
  let lastError;
233
304
  for (const requestUrl of requestUrls) {
234
305
  try {
235
306
  const response = await fetchImpl(requestUrl, {
236
307
  method: "GET",
237
308
  credentials: "include",
238
- headers: { Accept: "application/json" }
309
+ headers: { Accept: "application/json", ...requestHeaders }
239
310
  });
240
311
  if (!response.ok) {
241
312
  lastError = new Error(
@@ -260,9 +331,9 @@ function createSearchService(options = {}) {
260
331
  country,
261
332
  language,
262
333
  locale,
263
- searchPlaceholder = DEFAULT_SEARCH_PLACEHOLDER,
264
- inputAriaLabel = DEFAULT_INPUT_ARIA_LABEL,
265
- getMarketConfig
334
+ getMarketConfig,
335
+ getCustomerType,
336
+ minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH
266
337
  } = options;
267
338
  const fetchImpl = fetchImplOption !== void 0 ? fetchImplOption : typeof fetch === "function" ? fetch.bind(globalThis) : null;
268
339
  const locationLike = getBrowserLocation(providedLocation);
@@ -282,15 +353,9 @@ function createSearchService(options = {}) {
282
353
  return catalogRequestConfigPromise;
283
354
  }
284
355
  const service = {
285
- async getSearchChromeCopy() {
286
- return {
287
- placeholder: searchPlaceholder || DEFAULT_SEARCH_PLACEHOLDER,
288
- inputAriaLabel: inputAriaLabel || DEFAULT_INPUT_ARIA_LABEL
289
- };
290
- },
291
356
  async fetchSuggestions(searchTerm) {
292
357
  const trimmedSearchTerm = String(searchTerm ?? "").trim();
293
- if (!trimmedSearchTerm || !fetchImpl) {
358
+ if (!trimmedSearchTerm || trimmedSearchTerm.length < minimumSearchLength || !fetchImpl) {
294
359
  return {
295
360
  suggestions: [],
296
361
  didYouMean: [],
@@ -315,7 +380,8 @@ function createSearchService(options = {}) {
315
380
  const requestUrls = [`${suggestionsUrl}?${queryParams.toString()}`];
316
381
  const responsePayload = await fetchJsonFromCandidates(
317
382
  fetchImpl,
318
- requestUrls
383
+ requestUrls,
384
+ resolveCustomerTypeHeaders(getCustomerType)
319
385
  );
320
386
  const suggestionsField = Array.isArray(responsePayload?.suggestions) ? responsePayload?.suggestions : [];
321
387
  const productField = Array.isArray(responsePayload?.product) ? responsePayload?.product : null;
@@ -347,33 +413,6 @@ function createSearchService(options = {}) {
347
413
  return stripLocalePrefix(targetPath, localeParts);
348
414
  },
349
415
  activateSuggestionAction(option) {
350
- if (options.onSuggestionAction) {
351
- const result = options.onSuggestionAction(option);
352
- if (result instanceof Promise) {
353
- result.then((handled) => {
354
- if (!handled) {
355
- const optionHref2 = resolveOptionHref(
356
- option,
357
- localeParts
358
- );
359
- if (!optionHref2) {
360
- const optionQuery2 = option?.value ?? option?.label ?? option?.identifier ?? "";
361
- service.submitProductSearch(
362
- String(optionQuery2)
363
- );
364
- }
365
- }
366
- }).catch((error) => {
367
- console.error(
368
- "Error in onSuggestionAction:",
369
- error
370
- );
371
- });
372
- return "action-pending";
373
- } else if (result === true) {
374
- return "action-handled";
375
- }
376
- }
377
416
  const optionHref = resolveOptionHref(option, localeParts);
378
417
  if (optionHref) {
379
418
  return stripLocalePrefix(optionHref, localeParts);
@@ -400,547 +439,33 @@ function createSearchService(options = {}) {
400
439
  return service;
401
440
  }
402
441
 
403
- // node_modules/eventsource-parser/dist/errors.js
404
- var ParseError = class extends Error {
405
- constructor(message, options) {
406
- super(message);
407
- this.name = "ParseError";
408
- this.type = options.type;
409
- this.field = options.field;
410
- this.value = options.value;
411
- this.line = options.line;
412
- }
442
+ // src/services/marketConfig.ts
443
+ var fetchMarketConfig = async ({ country }) => {
444
+ const countryCode = String(country ?? "").trim().toUpperCase() || "US";
445
+ const configuration = await getConfiguration([EQUINOX_MARKETS_MAP], {
446
+ country: countryCode,
447
+ ...getEnvironmentCode() ? { environment: getEnvironmentCode() } : {},
448
+ clientId: DEFAULT_EQUINOX_CLIENT_ID,
449
+ shoppingContext: STOREFRONT_SHOPPING_CONTEXT
450
+ });
451
+ return configuration?.[EQUINOX_MARKETS_MAP] ?? null;
413
452
  };
414
453
 
415
- // node_modules/eventsource-parser/dist/parse.js
416
- var LF = 10;
417
- var CR = 13;
418
- var SPACE = 32;
419
- var MAX_FIELD_PREFIX_LENGTH = 6;
420
- function noop(_arg) {
421
- }
422
- function createParser(config) {
423
- if (typeof config === "function") {
424
- throw new TypeError("`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?");
425
- }
426
- const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config;
427
- const pendingFragments = [];
428
- let pendingFragmentsLength = 0;
429
- let isFirstChunk = true;
430
- let id;
431
- let data = "";
432
- let dataLines = 0;
433
- let eventType;
434
- let terminated = false;
435
- let skippingLine = false;
436
- let skipNextLineFeed = false;
437
- function feed(chunk) {
438
- if (terminated) {
439
- throw new Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");
440
- }
441
- if (isFirstChunk) {
442
- isFirstChunk = false;
443
- if (chunk.charCodeAt(0) === 65279) {
444
- chunk = chunk.slice(1);
445
- } else if (chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191) {
446
- chunk = chunk.slice(3);
447
- }
448
- }
449
- if (skippingLine || skipNextLineFeed) {
450
- chunk = resumeAfterSkip(chunk);
451
- if (!chunk) {
452
- return;
453
- }
454
- }
455
- if (!pendingFragments.length) {
456
- const trailing = processLines(chunk);
457
- if (trailing !== "") {
458
- storeTrailing(trailing);
459
- }
460
- checkBufferSize();
461
- return;
462
- }
463
- if (chunk.indexOf("\n") === -1 && chunk.indexOf("\r") === -1) {
464
- if (pendingFragmentsLength < MAX_FIELD_PREFIX_LENGTH) {
465
- const head = pendingFragments.join("") + chunk.slice(0, MAX_FIELD_PREFIX_LENGTH - pendingFragmentsLength);
466
- if (!shouldBufferTrailing(head)) {
467
- pendingFragments.length = 0;
468
- pendingFragmentsLength = 0;
469
- skippingLine = true;
470
- return;
471
- }
472
- }
473
- pendingFragments.push(chunk);
474
- pendingFragmentsLength += chunk.length;
475
- checkBufferSize();
476
- return;
477
- }
478
- pendingFragments.push(chunk);
479
- const input = pendingFragments.join("");
480
- pendingFragments.length = 0;
481
- pendingFragmentsLength = 0;
482
- storeTrailing(processLines(input));
483
- checkBufferSize();
484
- }
485
- function resumeAfterSkip(chunk) {
486
- if (chunk.length === 0) {
487
- return chunk;
488
- }
489
- if (skipNextLineFeed) {
490
- skipNextLineFeed = false;
491
- return chunk.charCodeAt(0) === LF ? chunk.slice(1) : chunk;
492
- }
493
- const crIndex = chunk.indexOf("\r");
494
- const lfIndex = chunk.indexOf("\n");
495
- const lineEnd = crIndex === -1 ? lfIndex : lfIndex === -1 ? crIndex : crIndex < lfIndex ? crIndex : lfIndex;
496
- if (lineEnd === -1) {
497
- return "";
498
- }
499
- if (lineEnd === chunk.length - 1 && chunk.charCodeAt(lineEnd) === CR) {
500
- skippingLine = false;
501
- skipNextLineFeed = true;
502
- return "";
503
- }
504
- skippingLine = false;
505
- return chunk.slice(lineEnd + (chunk.charCodeAt(lineEnd) === CR && chunk.charCodeAt(lineEnd + 1) === LF ? 2 : 1));
506
- }
507
- function storeTrailing(trailing) {
508
- if (!trailing)
509
- return;
510
- if (trailing.charCodeAt(trailing.length - 1) === CR) {
511
- parseLine(trailing, 0, trailing.length - 1);
512
- skipNextLineFeed = true;
513
- return;
514
- }
515
- if (shouldBufferTrailing(trailing)) {
516
- pendingFragments.push(trailing);
517
- pendingFragmentsLength = trailing.length;
518
- return;
519
- }
520
- skippingLine = true;
521
- }
522
- function shouldBufferTrailing(trailing) {
523
- const firstCharCode = trailing.charCodeAt(0);
524
- return firstCharCode === 58 && !!onComment || firstCharCode === 100 && isPotentialField(trailing, "data") || firstCharCode === 101 && isPotentialField(trailing, "event") || firstCharCode === 105 && isPotentialField(trailing, "id") || firstCharCode === 114 && isPotentialField(trailing, "retry");
525
- }
526
- function checkBufferSize() {
527
- if (maxBufferSize === void 0)
528
- return;
529
- if (pendingFragmentsLength + data.length <= maxBufferSize)
530
- return;
531
- terminated = true;
532
- pendingFragments.length = 0;
533
- pendingFragmentsLength = 0;
534
- id = void 0;
535
- data = "";
536
- dataLines = 0;
537
- eventType = void 0;
538
- skippingLine = false;
539
- skipNextLineFeed = false;
540
- onError(new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {
541
- type: "max-buffer-size-exceeded"
542
- }));
543
- }
544
- function processLines(chunk) {
545
- let searchIndex = 0;
546
- if (chunk.indexOf("\r") === -1) {
547
- let lfIndex = chunk.indexOf("\n", searchIndex);
548
- while (lfIndex !== -1) {
549
- if (searchIndex === lfIndex) {
550
- if (dataLines > 0) {
551
- onEvent({ id, event: eventType, data });
552
- }
553
- id = void 0;
554
- data = "";
555
- dataLines = 0;
556
- eventType = void 0;
557
- searchIndex = lfIndex + 1;
558
- lfIndex = chunk.indexOf("\n", searchIndex);
559
- continue;
560
- }
561
- const firstCharCode = chunk.charCodeAt(searchIndex);
562
- if (isDataPrefix(chunk, searchIndex, firstCharCode)) {
563
- const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5;
564
- const value = chunk.slice(valueStart, lfIndex);
565
- if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {
566
- onEvent({ id, event: eventType, data: value });
567
- id = void 0;
568
- data = "";
569
- eventType = void 0;
570
- searchIndex = lfIndex + 2;
571
- lfIndex = chunk.indexOf("\n", searchIndex);
572
- continue;
573
- }
574
- data = dataLines === 0 ? value : `${data}
575
- ${value}`;
576
- dataLines++;
577
- } else if (isEventPrefix(chunk, searchIndex, firstCharCode)) {
578
- eventType = chunk.slice(chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6, lfIndex) || void 0;
579
- } else {
580
- parseLine(chunk, searchIndex, lfIndex);
581
- }
582
- searchIndex = lfIndex + 1;
583
- lfIndex = chunk.indexOf("\n", searchIndex);
584
- }
585
- return chunk.slice(searchIndex);
586
- }
587
- while (searchIndex < chunk.length) {
588
- const crIndex = chunk.indexOf("\r", searchIndex);
589
- const lfIndex = chunk.indexOf("\n", searchIndex);
590
- let lineEnd = -1;
591
- if (crIndex !== -1 && lfIndex !== -1) {
592
- lineEnd = crIndex < lfIndex ? crIndex : lfIndex;
593
- } else if (crIndex !== -1) {
594
- if (crIndex === chunk.length - 1) {
595
- lineEnd = -1;
596
- } else {
597
- lineEnd = crIndex;
598
- }
599
- } else if (lfIndex !== -1) {
600
- lineEnd = lfIndex;
601
- }
602
- if (lineEnd === -1) {
603
- break;
604
- }
605
- parseLine(chunk, searchIndex, lineEnd);
606
- searchIndex = lineEnd + 1;
607
- if (chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF) {
608
- searchIndex++;
609
- }
610
- }
611
- return chunk.slice(searchIndex);
612
- }
613
- function parseLine(chunk, start, end) {
614
- if (start === end) {
615
- dispatchEvent();
616
- return;
617
- }
618
- const firstCharCode = chunk.charCodeAt(start);
619
- if (isDataPrefix(chunk, start, firstCharCode)) {
620
- const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5;
621
- const value2 = chunk.slice(valueStart, end);
622
- data = dataLines === 0 ? value2 : `${data}
623
- ${value2}`;
624
- dataLines++;
625
- return;
626
- }
627
- if (isEventPrefix(chunk, start, firstCharCode)) {
628
- eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;
629
- return;
630
- }
631
- if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {
632
- const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);
633
- if (!value2.includes("\0"))
634
- id = value2;
635
- return;
636
- }
637
- if (firstCharCode === 58) {
638
- if (onComment) {
639
- const line2 = chunk.slice(start, end);
640
- onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));
641
- }
642
- return;
643
- }
644
- const line = chunk.slice(start, end);
645
- const fieldSeparatorIndex = line.indexOf(":");
646
- if (fieldSeparatorIndex === -1) {
647
- processField(line, "", line);
648
- return;
649
- }
650
- const field = line.slice(0, fieldSeparatorIndex);
651
- const offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1;
652
- const value = line.slice(fieldSeparatorIndex + offset);
653
- processField(field, value, line);
654
- }
655
- function processField(field, value, line) {
656
- switch (field) {
657
- case "event":
658
- eventType = value || void 0;
659
- break;
660
- case "data":
661
- data = dataLines === 0 ? value : `${data}
662
- ${value}`;
663
- dataLines++;
664
- break;
665
- case "id":
666
- if (!value.includes("\0"))
667
- id = value;
668
- break;
669
- case "retry":
670
- if (/^\d+$/.test(value)) {
671
- onRetry(parseInt(value, 10));
672
- } else {
673
- onError(new ParseError(`Invalid \`retry\` value: "${value}"`, {
674
- type: "invalid-retry",
675
- value,
676
- line
677
- }));
678
- }
679
- break;
680
- default:
681
- onError(new ParseError(`Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`, { type: "unknown-field", field, value, line }));
682
- break;
683
- }
684
- }
685
- function dispatchEvent() {
686
- if (dataLines > 0) {
687
- onEvent({
688
- id,
689
- event: eventType,
690
- data
691
- });
692
- }
693
- id = void 0;
694
- data = "";
695
- dataLines = 0;
696
- eventType = void 0;
697
- }
698
- function reset(options = {}) {
699
- if (options.consume && pendingFragments.length > 0) {
700
- const incompleteLine = pendingFragments.join("");
701
- parseLine(incompleteLine, 0, incompleteLine.length);
702
- }
703
- isFirstChunk = true;
704
- id = void 0;
705
- data = "";
706
- dataLines = 0;
707
- eventType = void 0;
708
- pendingFragments.length = 0;
709
- pendingFragmentsLength = 0;
710
- terminated = false;
711
- skippingLine = false;
712
- skipNextLineFeed = false;
713
- }
714
- return { feed, reset };
715
- }
716
- function isDataPrefix(chunk, i, firstCharCode) {
717
- return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;
718
- }
719
- function isEventPrefix(chunk, i, firstCharCode) {
720
- return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;
721
- }
722
- function isPotentialField(line, field) {
723
- let i = 1;
724
- while (i < line.length && i < field.length) {
725
- if (line.charCodeAt(i) !== field.charCodeAt(i)) {
726
- return false;
727
- }
728
- i++;
729
- }
730
- return line.length <= field.length || line.charCodeAt(field.length) === 58;
731
- }
454
+ // src/utils/gtmConstants.ts
455
+ var SEARCH_GTM_EVENTS = {
456
+ CHAT_ENTRY_POINT: "Begin_chat",
457
+ PRODUCT_SEARCH: "ProductSearch"};
458
+ var SEARCH_GTM_SEARCH_TYPES = {
459
+ SEMANTIC: "semantic search"};
460
+ var SEARCH_GTM_USER_STATUS = {
461
+ GUEST: "guest",
462
+ LOGGED_IN: "logged_in"
463
+ };
464
+ var GTM_FALLBACK_NA = "NA";
732
465
 
733
- // src/services/ChatService.ts
734
- var PRODUCTS_OPENING_FENCE = "```products";
735
- var MARKDOWN_CLOSING_FENCE = "\n```";
736
- function findProductsBlockStart(content, openingFenceIndex) {
737
- let blockStart = openingFenceIndex + PRODUCTS_OPENING_FENCE.length;
738
- while (blockStart < content.length && (content[blockStart] === " " || content[blockStart] === " " || content[blockStart] === "\r")) {
739
- blockStart += 1;
740
- }
741
- return content[blockStart] === "\n" ? blockStart + 1 : null;
742
- }
743
- function isStandaloneClosingFence(content, fenceEnd) {
744
- const lineEnd = content.indexOf("\n", fenceEnd);
745
- const suffixEnd = lineEnd === -1 ? content.length : lineEnd;
746
- return content.slice(fenceEnd, suffixEnd).trim() === "";
747
- }
748
- function findClosingFence(content, fromIndex) {
749
- let candidate = content.indexOf(MARKDOWN_CLOSING_FENCE, fromIndex);
750
- while (candidate !== -1) {
751
- const fenceEnd = candidate + MARKDOWN_CLOSING_FENCE.length;
752
- if (isStandaloneClosingFence(content, fenceEnd)) {
753
- return candidate;
754
- }
755
- candidate = content.indexOf(MARKDOWN_CLOSING_FENCE, fenceEnd);
756
- }
757
- return -1;
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
- }
772
- function extractProductsFromMarkdown(content) {
773
- const products = [];
774
- const summaryParts = [];
775
- let summaryCursor = 0;
776
- let searchCursor = 0;
777
- while (searchCursor < content.length) {
778
- const start = content.indexOf(PRODUCTS_OPENING_FENCE, searchCursor);
779
- if (start === -1) break;
780
- const blockStart = findProductsBlockStart(content, start);
781
- if (blockStart === null) {
782
- searchCursor = start + PRODUCTS_OPENING_FENCE.length;
783
- continue;
784
- }
785
- const nextOpeningFence = content.indexOf(
786
- PRODUCTS_OPENING_FENCE,
787
- blockStart
788
- );
789
- const end = findClosingFence(content, blockStart);
790
- if (end === -1) break;
791
- if (nextOpeningFence !== -1 && nextOpeningFence < end) {
792
- searchCursor = nextOpeningFence;
793
- continue;
794
- }
795
- try {
796
- const parsed = JSON.parse(content.slice(blockStart, end));
797
- if (Array.isArray(parsed)) {
798
- products.push(...parsed);
799
- }
800
- } catch (e) {
801
- console.error("Failed to parse products block:", e);
802
- }
803
- summaryParts.push(content.slice(summaryCursor, start));
804
- summaryCursor = end + MARKDOWN_CLOSING_FENCE.length;
805
- searchCursor = summaryCursor;
806
- }
807
- summaryParts.push(content.slice(summaryCursor));
808
- return { summary: summaryParts.join("").trim(), products };
809
- }
810
- function createChatService(options) {
811
- const {
812
- conversationsUrl = DEFAULT_AI_CHAT_CONVERSATIONS_URL,
813
- messagesUrl = DEFAULT_AI_CHAT_MESSAGES_URL,
814
- salesAiAppKey,
815
- salesAiParentOrigin,
816
- market,
817
- language,
818
- fetchImpl: fetchImplOption
819
- } = options;
820
- let fetchImpl;
821
- if (fetchImplOption !== void 0) {
822
- fetchImpl = fetchImplOption;
823
- } else if (typeof fetch === "function") {
824
- fetchImpl = fetch.bind(globalThis);
825
- } else {
826
- fetchImpl = () => {
827
- throw new Error("ChatService: no fetch implementation available");
828
- };
829
- }
830
- function buildHeaders(extra, includeJsonContentType = true) {
831
- const headers = {
832
- ...extra
833
- };
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
- }
847
- return headers;
848
- }
849
- return {
850
- async createConversation() {
851
- const res = await fetchImpl(conversationsUrl, {
852
- method: "POST",
853
- headers: buildHeaders(void 0, false)
854
- });
855
- if (!res.ok) {
856
- throw new Error(
857
- `ChatService.createConversation: ${res.status} ${res.statusText}`
858
- );
859
- }
860
- const data = await res.json();
861
- const id = data["id"] ?? data["conversation_id"];
862
- if (typeof id !== "number") {
863
- throw new TypeError(
864
- "ChatService.createConversation: response missing numeric id"
865
- );
866
- }
867
- return id;
868
- },
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
- };
886
- const res = await fetchImpl(messagesUrl, {
887
- method: "POST",
888
- headers: buildHeaders({ Accept: "text/event-stream" }),
889
- body: JSON.stringify(requestBody)
890
- });
891
- if (!res.ok) {
892
- throw new Error(
893
- `ChatService.sendMessage: ${res.status} ${res.statusText}`
894
- );
895
- }
896
- let accumulated = "";
897
- const renderedProducts = [];
898
- const parser = createParser({
899
- onEvent(event) {
900
- const data = event.data;
901
- if (!data || data === "[DONE]") return;
902
- try {
903
- const parsed = JSON.parse(data);
904
- const parsedType = parsed["type"];
905
- const type = typeof parsedType === "string" ? parsedType : "";
906
- renderedProducts.push(
907
- ...getRenderProductListItems(parsed)
908
- );
909
- if (type === "content" && typeof parsed["text"] === "string") {
910
- const text = parsed["text"];
911
- accumulated += text;
912
- onChunk?.(type, text);
913
- } else if (type === "status" && typeof parsed["message"] === "string") {
914
- const message = parsed["message"];
915
- onChunk?.(type, message);
916
- }
917
- } catch {
918
- }
919
- }
920
- });
921
- const body = res.body;
922
- if (body && typeof body.getReader === "function") {
923
- const reader = body.getReader();
924
- const decoder = new TextDecoder();
925
- while (true) {
926
- const { value, done } = await reader.read();
927
- if (done) break;
928
- parser.feed(decoder.decode(value, { stream: true }));
929
- }
930
- parser.feed(decoder.decode());
931
- } else {
932
- const rawBody = await res.text();
933
- parser.feed(rawBody);
934
- }
935
- const { summary, products } = extractProductsFromMarkdown(accumulated);
936
- const finalProducts = renderedProducts.length > 0 ? renderedProducts : products;
937
- return {
938
- summary,
939
- products: finalProducts,
940
- rawContent: accumulated
941
- };
942
- }
943
- };
466
+ // src/utils/navigationTarget.ts
467
+ function toNavigationTarget(path) {
468
+ return /^https?:\/\//i.test(path) ? { type: "external", url: path } : { type: "internal", path };
944
469
  }
945
470
 
946
471
  // src/utils/catalogHelpers.ts
@@ -958,9 +483,6 @@ var CART_DISABLED_PRODUCT_STATUSES = /* @__PURE__ */ new Set([
958
483
  function normalizeProductStatusValue(value) {
959
484
  return String(value ?? "").trim().toLowerCase().replace(/\s+/g, "");
960
485
  }
961
- function isSellableProductStatus(productStatus) {
962
- return normalizeProductStatusValue(productStatus) === "sellable";
963
- }
964
486
  function resolveSkuStatusValues(suggestionOption) {
965
487
  return [
966
488
  suggestionOption["productStatus"],
@@ -1205,14 +727,6 @@ function getVisibleSuggestions(opts) {
1205
727
  }
1206
728
  return opts.suggestions;
1207
729
  }
1208
- function getSeeMoreResultsPayload(query, totalResultsCount, suggestions) {
1209
- return {
1210
- query,
1211
- source: "autocomplete-footer",
1212
- totalResultsCount,
1213
- visibleResultsCount: suggestions.length
1214
- };
1215
- }
1216
730
 
1217
731
  // src/hooks/useSearchSuggestions.ts
1218
732
  function normalizeSuggestionsResponse(response) {
@@ -1251,6 +765,7 @@ function useSearchSuggestions({
1251
765
  const [suggestionFetchFailed, setSuggestionFetchFailed] = useState(false);
1252
766
  const [hasMoreResults, setHasMoreResults] = useState(false);
1253
767
  const [totalResultsCount, setTotalResultsCount] = useState(0);
768
+ const [settledQuery, setSettledQuery] = useState("");
1254
769
  const fetchDebounced = useRef(null);
1255
770
  const latestRequestId = useRef(0);
1256
771
  const resetSuggestionsState = useCallback(() => {
@@ -1260,6 +775,7 @@ function useSearchSuggestions({
1260
775
  setSuggestionFetchFailed(false);
1261
776
  setHasMoreResults(false);
1262
777
  setTotalResultsCount(0);
778
+ setSettledQuery("");
1263
779
  setLoading(false);
1264
780
  }, []);
1265
781
  const updateQuery = useCallback(
@@ -1310,6 +826,7 @@ function useSearchSuggestions({
1310
826
  );
1311
827
  setTotalResultsCount(effectiveTotal);
1312
828
  setSuggestionFetchFailed(false);
829
+ setSettledQuery(trimmedQuery);
1313
830
  setEmptyFetched(
1314
831
  hasDidYouMean ? false : visibleSuggestions.length === 0 && suggestedTerms.length === 0
1315
832
  );
@@ -1323,6 +840,7 @@ function useSearchSuggestions({
1323
840
  setSuggestionFetchFailed(true);
1324
841
  setHasMoreResults(false);
1325
842
  setTotalResultsCount(0);
843
+ setSettledQuery(trimmedQuery);
1326
844
  } finally {
1327
845
  if (requestId === latestRequestId.current) {
1328
846
  setLoading(false);
@@ -1357,6 +875,7 @@ function useSearchSuggestions({
1357
875
  didYouMean,
1358
876
  loading,
1359
877
  query,
878
+ settledQuery,
1360
879
  setQuery,
1361
880
  suggestionFetchFailed,
1362
881
  suggestions,
@@ -1365,382 +884,6 @@ function useSearchSuggestions({
1365
884
  };
1366
885
  }
1367
886
 
1368
- // node_modules/zustand/esm/vanilla.mjs
1369
- var createStoreImpl = (createState) => {
1370
- let state;
1371
- const listeners = /* @__PURE__ */ new Set();
1372
- const setState = (partial, replace) => {
1373
- const nextState = typeof partial === "function" ? partial(state) : partial;
1374
- if (!Object.is(nextState, state)) {
1375
- const previousState = state;
1376
- state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
1377
- listeners.forEach((listener) => listener(state, previousState));
1378
- }
1379
- };
1380
- const getState = () => state;
1381
- const getInitialState = () => initialState;
1382
- const subscribe = (listener) => {
1383
- listeners.add(listener);
1384
- return () => listeners.delete(listener);
1385
- };
1386
- const api = { setState, getState, getInitialState, subscribe };
1387
- const initialState = state = createState(setState, getState, api);
1388
- return api;
1389
- };
1390
- var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
1391
- var identity = (arg) => arg;
1392
- function useStore(api, selector = identity) {
1393
- const slice = React.useSyncExternalStore(
1394
- api.subscribe,
1395
- () => selector(api.getState()),
1396
- () => selector(api.getInitialState())
1397
- );
1398
- React.useDebugValue(slice);
1399
- return slice;
1400
- }
1401
- var createImpl = (createState) => {
1402
- const api = createStore(createState);
1403
- const useBoundStore = (selector) => useStore(api, selector);
1404
- Object.assign(useBoundStore, api);
1405
- return useBoundStore;
1406
- };
1407
- var create = (createState) => createImpl;
1408
-
1409
- // node_modules/zustand/esm/middleware.mjs
1410
- function createJSONStorage(getStorage, options) {
1411
- let storage;
1412
- try {
1413
- storage = getStorage();
1414
- } catch (e) {
1415
- return;
1416
- }
1417
- const persistStorage = {
1418
- getItem: (name) => {
1419
- var _a;
1420
- const parse = (str2) => {
1421
- if (str2 === null) {
1422
- return null;
1423
- }
1424
- return JSON.parse(str2, void 0 );
1425
- };
1426
- const str = (_a = storage.getItem(name)) != null ? _a : null;
1427
- if (str instanceof Promise) {
1428
- return str.then(parse);
1429
- }
1430
- return parse(str);
1431
- },
1432
- setItem: (name, newValue) => storage.setItem(
1433
- name,
1434
- JSON.stringify(newValue, void 0 )
1435
- ),
1436
- removeItem: (name) => storage.removeItem(name)
1437
- };
1438
- return persistStorage;
1439
- }
1440
- var toThenable = (fn) => (input) => {
1441
- try {
1442
- const result = fn(input);
1443
- if (result instanceof Promise) {
1444
- return result;
1445
- }
1446
- return {
1447
- then(onFulfilled) {
1448
- return toThenable(onFulfilled)(result);
1449
- },
1450
- catch(_onRejected) {
1451
- return this;
1452
- }
1453
- };
1454
- } catch (e) {
1455
- return {
1456
- then(_onFulfilled) {
1457
- return this;
1458
- },
1459
- catch(onRejected) {
1460
- return toThenable(onRejected)(e);
1461
- }
1462
- };
1463
- }
1464
- };
1465
- var persistImpl = (config, baseOptions) => (set, get, api) => {
1466
- let options = {
1467
- storage: createJSONStorage(() => localStorage),
1468
- partialize: (state) => state,
1469
- version: 0,
1470
- merge: (persistedState, currentState) => ({
1471
- ...currentState,
1472
- ...persistedState
1473
- }),
1474
- ...baseOptions
1475
- };
1476
- let hasHydrated = false;
1477
- const hydrationListeners = /* @__PURE__ */ new Set();
1478
- const finishHydrationListeners = /* @__PURE__ */ new Set();
1479
- let storage = options.storage;
1480
- if (!storage) {
1481
- return config(
1482
- (...args) => {
1483
- console.warn(
1484
- `[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`
1485
- );
1486
- set(...args);
1487
- },
1488
- get,
1489
- api
1490
- );
1491
- }
1492
- const setItem = () => {
1493
- const state = options.partialize({ ...get() });
1494
- return storage.setItem(options.name, {
1495
- state,
1496
- version: options.version
1497
- });
1498
- };
1499
- const savedSetState = api.setState;
1500
- api.setState = (state, replace) => {
1501
- savedSetState(state, replace);
1502
- void setItem();
1503
- };
1504
- const configResult = config(
1505
- (...args) => {
1506
- set(...args);
1507
- void setItem();
1508
- },
1509
- get,
1510
- api
1511
- );
1512
- api.getInitialState = () => configResult;
1513
- let stateFromStorage;
1514
- const hydrate = () => {
1515
- var _a, _b;
1516
- if (!storage) return;
1517
- hasHydrated = false;
1518
- hydrationListeners.forEach((cb) => {
1519
- var _a2;
1520
- return cb((_a2 = get()) != null ? _a2 : configResult);
1521
- });
1522
- const postRehydrationCallback = ((_b = options.onRehydrateStorage) == null ? void 0 : _b.call(options, (_a = get()) != null ? _a : configResult)) || void 0;
1523
- return toThenable(storage.getItem.bind(storage))(options.name).then((deserializedStorageValue) => {
1524
- if (deserializedStorageValue) {
1525
- if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) {
1526
- if (options.migrate) {
1527
- return [
1528
- true,
1529
- options.migrate(
1530
- deserializedStorageValue.state,
1531
- deserializedStorageValue.version
1532
- )
1533
- ];
1534
- }
1535
- console.error(
1536
- `State loaded from storage couldn't be migrated since no migrate function was provided`
1537
- );
1538
- } else {
1539
- return [false, deserializedStorageValue.state];
1540
- }
1541
- }
1542
- return [false, void 0];
1543
- }).then((migrationResult) => {
1544
- var _a2;
1545
- const [migrated, migratedState] = migrationResult;
1546
- stateFromStorage = options.merge(
1547
- migratedState,
1548
- (_a2 = get()) != null ? _a2 : configResult
1549
- );
1550
- set(stateFromStorage, true);
1551
- if (migrated) {
1552
- return setItem();
1553
- }
1554
- }).then(() => {
1555
- postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0);
1556
- stateFromStorage = get();
1557
- hasHydrated = true;
1558
- finishHydrationListeners.forEach((cb) => cb(stateFromStorage));
1559
- }).catch((e) => {
1560
- postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e);
1561
- });
1562
- };
1563
- api.persist = {
1564
- setOptions: (newOptions) => {
1565
- options = {
1566
- ...options,
1567
- ...newOptions
1568
- };
1569
- if (newOptions.storage) {
1570
- storage = newOptions.storage;
1571
- }
1572
- },
1573
- clearStorage: () => {
1574
- storage == null ? void 0 : storage.removeItem(options.name);
1575
- },
1576
- getOptions: () => options,
1577
- rehydrate: () => hydrate(),
1578
- hasHydrated: () => hasHydrated,
1579
- onHydrate: (cb) => {
1580
- hydrationListeners.add(cb);
1581
- return () => {
1582
- hydrationListeners.delete(cb);
1583
- };
1584
- },
1585
- onFinishHydration: (cb) => {
1586
- finishHydrationListeners.add(cb);
1587
- return () => {
1588
- finishHydrationListeners.delete(cb);
1589
- };
1590
- }
1591
- };
1592
- if (!options.skipHydration) {
1593
- hydrate();
1594
- }
1595
- return stateFromStorage || configResult;
1596
- };
1597
- var persist = persistImpl;
1598
-
1599
- // src/store/searchHistoryStore.ts
1600
- var MAX_HISTORY = 10;
1601
- var useSearchHistoryStore = create()(
1602
- persist(
1603
- (set) => ({
1604
- entries: [],
1605
- addEntry: (entry) => {
1606
- const trimmed = entry.query.trim();
1607
- if (!trimmed) return;
1608
- set((state) => {
1609
- const deduped = state.entries.filter(
1610
- (e) => e.query.toLowerCase() !== trimmed.toLowerCase()
1611
- );
1612
- const newEntry = {
1613
- ...entry,
1614
- query: trimmed,
1615
- timestamp: Date.now()
1616
- };
1617
- return {
1618
- entries: [newEntry, ...deduped].slice(0, MAX_HISTORY)
1619
- };
1620
- });
1621
- },
1622
- addQuery: (query) => {
1623
- const trimmed = query.trim();
1624
- if (!trimmed) return;
1625
- set((state) => {
1626
- const deduped = state.entries.filter(
1627
- (e) => e.query.toLowerCase() !== trimmed.toLowerCase()
1628
- );
1629
- return {
1630
- entries: [
1631
- {
1632
- query: trimmed,
1633
- isAI: false,
1634
- timestamp: Date.now()
1635
- },
1636
- ...deduped
1637
- ].slice(0, MAX_HISTORY)
1638
- };
1639
- });
1640
- },
1641
- removeEntry: (query) => set((state) => ({
1642
- entries: state.entries.filter((e) => e.query !== query)
1643
- })),
1644
- clearAll: () => set({ entries: [] })
1645
- }),
1646
- {
1647
- name: "ai_search_recent_queries",
1648
- version: 1,
1649
- storage: createJSONStorage(
1650
- () => typeof window !== "undefined" ? window.sessionStorage : {}
1651
- ),
1652
- // Migrate from v0 (queries: string[]) to v1 (entries: SearchHistoryEntry[])
1653
- migrate(persistedState, version) {
1654
- if (version === 0) {
1655
- const old = persistedState;
1656
- if (Array.isArray(old?.queries)) {
1657
- return {
1658
- entries: old.queries.map((q) => ({
1659
- query: q,
1660
- isAI: false,
1661
- timestamp: Date.now()
1662
- }))
1663
- };
1664
- }
1665
- }
1666
- return persistedState;
1667
- }
1668
- }
1669
- )
1670
- );
1671
-
1672
- // src/hooks/useSearchHistory.ts
1673
- function useSearchHistory() {
1674
- const entries = useSearchHistoryStore((s) => s.entries);
1675
- const addEntry = useSearchHistoryStore((s) => s.addEntry);
1676
- const addQuery = useSearchHistoryStore((s) => s.addQuery);
1677
- const removeEntry = useSearchHistoryStore((s) => s.removeEntry);
1678
- const clearAll = useSearchHistoryStore((s) => s.clearAll);
1679
- return {
1680
- entries,
1681
- queries: entries.map((e) => e.query),
1682
- addEntry,
1683
- addQuery,
1684
- removeEntry,
1685
- clearAll
1686
- };
1687
- }
1688
- function getSpeechRecognition() {
1689
- if (typeof window === "undefined") return null;
1690
- const w = window;
1691
- return w.SpeechRecognition ?? w.webkitSpeechRecognition ?? null;
1692
- }
1693
- function useVoiceSearch({
1694
- lang,
1695
- onInterimResult,
1696
- onResult,
1697
- onError
1698
- }) {
1699
- const [listening, setListening] = useState(false);
1700
- const recognitionRef = useRef(null);
1701
- const supported = getSpeechRecognition() !== null;
1702
- const stop = useCallback(() => {
1703
- recognitionRef.current?.stop();
1704
- recognitionRef.current = null;
1705
- setListening(false);
1706
- }, []);
1707
- const start = useCallback(() => {
1708
- const SR = getSpeechRecognition();
1709
- if (!SR) return;
1710
- stop();
1711
- const rec = new SR();
1712
- rec.lang = lang ?? navigator.language ?? "en-US";
1713
- rec.interimResults = true;
1714
- rec.maxAlternatives = 1;
1715
- rec.onresult = (event) => {
1716
- let finalTranscript = "";
1717
- let interimTranscript = "";
1718
- const startIndex = event.resultIndex ?? 0;
1719
- for (let index = startIndex; index < event.results.length; index += 1) {
1720
- const result = event.results[index];
1721
- const transcript = result?.[0]?.transcript ?? "";
1722
- if (!transcript) continue;
1723
- if (result?.isFinal) {
1724
- finalTranscript += transcript;
1725
- } else {
1726
- interimTranscript += transcript;
1727
- }
1728
- }
1729
- if (interimTranscript) onInterimResult?.(interimTranscript);
1730
- if (finalTranscript) onResult(finalTranscript);
1731
- };
1732
- rec.onerror = (event) => {
1733
- onError?.(event);
1734
- setListening(false);
1735
- };
1736
- rec.onend = () => setListening(false);
1737
- recognitionRef.current = rec;
1738
- rec.start();
1739
- setListening(true);
1740
- }, [lang, onInterimResult, onResult, onError, stop]);
1741
- return { listening, supported, start, stop };
1742
- }
1743
-
1744
887
  // src/utils/commonUtils.ts
1745
888
  var IS_BROWSER = typeof window !== "undefined";
1746
889
  function toEventString(value, fallback = " ") {
@@ -1748,119 +891,24 @@ function toEventString(value, fallback = " ") {
1748
891
  const str = String(value).trim();
1749
892
  return str.length > 0 ? str : fallback;
1750
893
  }
1751
- function getLocaleFromUrl(pathname) {
1752
- const path = pathname ?? (IS_BROWSER ? window.location.pathname : "");
1753
- const [country, language] = path.split("/").filter(Boolean);
1754
- if (!/^[a-z]{2}$/i.test(country ?? "") || !/^[a-z]{2}$/i.test(language ?? "")) {
1755
- return {};
1756
- }
1757
- return {
1758
- countryCode: country.toUpperCase(),
1759
- language: language.toLowerCase()
1760
- };
1761
- }
1762
- function safeParse(raw) {
1763
- try {
1764
- return JSON.parse(raw);
1765
- } catch {
1766
- return raw;
1767
- }
1768
- }
1769
- function getLocalStorage(key) {
1770
- if (!IS_BROWSER) return void 0;
1771
- try {
1772
- const raw = window.localStorage.getItem(key);
1773
- return raw === null ? void 0 : safeParse(raw);
1774
- } catch {
1775
- return void 0;
1776
- }
1777
- }
1778
- function getSessionStorage2(key) {
1779
- if (!IS_BROWSER) return void 0;
1780
- try {
1781
- const raw = window.sessionStorage.getItem(key);
1782
- return raw === null ? void 0 : safeParse(raw);
1783
- } catch {
1784
- return void 0;
1785
- }
1786
- }
1787
-
1788
- // src/utils/gtmConstants.ts
1789
- var SEARCH_GTM_EVENTS = {
1790
- CHAT_ENTRY_POINT: "Begin_chat",
1791
- SEARCH_TYPE: "Search_Type",
1792
- PRODUCT_SEARCH: "ProductSearch",
1793
- ERROR_POPUP: "Error_popup"
1794
- };
1795
- var SEARCH_GTM_SEARCH_TYPES = {
1796
- SEMANTIC: "semantic search",
1797
- AI_AGENT: "AI agent"
1798
- };
1799
- var SEARCH_GTM_USER_STATUS = {
1800
- GUEST: "guest",
1801
- LOGGED_IN: "logged_in"
1802
- };
1803
- var AI_CHAT_CONVERSATION_ID_KEY = "ai_chat_conversation_id";
1804
- var IS_AUTHENTICATED_KEY = "IsAuthenticated";
1805
- var ACCOUNT_TYPE_KEY = "accountType";
1806
- var NUSKIN_ACCOUNT_DATA_KEY = "nuskin.account.data";
1807
- var GTM_FALLBACK_NA = "NA";
1808
894
 
1809
895
  // src/utils/gtmTracking.ts
1810
- function readSessionChatId() {
1811
- const raw = getSessionStorage2(AI_CHAT_CONVERSATION_ID_KEY);
1812
- return typeof raw === "string" || typeof raw === "number" ? String(raw) : void 0;
1813
- }
1814
- function readUserStatusFromStorage() {
1815
- const value = getLocalStorage(IS_AUTHENTICATED_KEY);
1816
- if (value === void 0) return void 0;
1817
- if (value === SEARCH_GTM_USER_STATUS.GUEST || value === SEARCH_GTM_USER_STATUS.LOGGED_IN) {
1818
- return value;
1819
- }
1820
- const isAuthenticated = value === true || typeof value === "string" && ["true", "1", "yes", "y"].includes(value.toLowerCase().trim());
1821
- return isAuthenticated ? SEARCH_GTM_USER_STATUS.LOGGED_IN : SEARCH_GTM_USER_STATUS.GUEST;
1822
- }
1823
- function readAccountTypeFromStorage() {
1824
- const value = getLocalStorage(ACCOUNT_TYPE_KEY);
1825
- return typeof value === "string" && value.trim().length > 0 ? value : void 0;
1826
- }
1827
- function readUserIdFromStorage() {
1828
- const raw = getLocalStorage(NUSKIN_ACCOUNT_DATA_KEY);
1829
- if (!raw || typeof raw !== "object") return void 0;
1830
- const user = raw.user;
1831
- if (!user || typeof user !== "object") return void 0;
1832
- const id = user.id;
1833
- return typeof id === "string" && id.trim().length > 0 ? id : void 0;
1834
- }
1835
896
  function resolveUserId(explicit) {
1836
- return toEventString(explicit ?? readUserIdFromStorage(), GTM_FALLBACK_NA);
897
+ return toEventString(explicit, GTM_FALLBACK_NA);
1837
898
  }
1838
899
  function baseEventPayload(ctx) {
1839
- const urlLocale = getLocaleFromUrl();
1840
- const chatId = ctx.chatId ?? readSessionChatId();
1841
- const userStatus = ctx.userStatus ?? readUserStatusFromStorage();
1842
- const accountType = ctx.accountType ?? readAccountTypeFromStorage();
1843
- const resolvedUserStatus = userStatus ?? SEARCH_GTM_USER_STATUS.GUEST;
1844
- const documentTitle = IS_BROWSER && typeof document?.title === "string" ? document.title : void 0;
1845
900
  return {
1846
- Chat_ID: toEventString(chatId, GTM_FALLBACK_NA),
1847
- page_type: toEventString(
1848
- ctx.pageType ?? documentTitle,
1849
- GTM_FALLBACK_NA
1850
- ),
1851
- country_code: toEventString(
1852
- ctx.countryCode ?? urlLocale.countryCode,
1853
- GTM_FALLBACK_NA
1854
- ),
1855
- language: toEventString(
1856
- ctx.language ?? urlLocale.language,
1857
- GTM_FALLBACK_NA
1858
- ),
901
+ Chat_ID: toEventString(ctx.chatId, GTM_FALLBACK_NA),
902
+ // TODO: COMX-3277 puts `page_type` in every event but defines no values. Send `NA` until
903
+ // the data team says what it should hold (perhaps the page path).
904
+ page_type: GTM_FALLBACK_NA,
905
+ country_code: toEventString(ctx.countryCode, GTM_FALLBACK_NA),
906
+ language: toEventString(ctx.language, GTM_FALLBACK_NA),
1859
907
  user_status: toEventString(
1860
- resolvedUserStatus,
908
+ ctx.userStatus ?? SEARCH_GTM_USER_STATUS.GUEST,
1861
909
  SEARCH_GTM_USER_STATUS.GUEST
1862
910
  ),
1863
- account_type: toEventString(accountType, GTM_FALLBACK_NA)
911
+ account_type: toEventString(ctx.accountType, GTM_FALLBACK_NA)
1864
912
  };
1865
913
  }
1866
914
  function serializeProductResults(productResultsData, productResults) {
@@ -1893,15 +941,6 @@ function trackChatEntryPoint(ctx) {
1893
941
  ...baseEventPayload(ctx)
1894
942
  });
1895
943
  }
1896
- function trackSearchType(input) {
1897
- pushGtmEvent({
1898
- event: SEARCH_GTM_EVENTS.SEARCH_TYPE,
1899
- ...baseEventPayload(input),
1900
- id: resolveUserId(input.userId),
1901
- type: input.searchType ?? SEARCH_GTM_SEARCH_TYPES.SEMANTIC,
1902
- search_response: toEventString(input.searchResponse, GTM_FALLBACK_NA)
1903
- });
1904
- }
1905
944
  function trackProductSearch(input) {
1906
945
  pushGtmEvent({
1907
946
  event: SEARCH_GTM_EVENTS.PRODUCT_SEARCH,
@@ -1914,15 +953,471 @@ function trackProductSearch(input) {
1914
953
  )
1915
954
  });
1916
955
  }
1917
- function trackErrorPopup(input) {
1918
- pushGtmEvent({
1919
- event: SEARCH_GTM_EVENTS.ERROR_POPUP,
1920
- ...baseEventPayload(input),
1921
- id: resolveUserId(input.userId),
1922
- search_type: input.searchType ?? SEARCH_GTM_SEARCH_TYPES.SEMANTIC
956
+
957
+ // src/utils/nuiSearchGate.ts
958
+ var NUI_SEARCH_FLAG = "enableNuiSearch";
959
+ function parseFlagValue(raw) {
960
+ const value = raw?.trim().toLowerCase();
961
+ if (value === "true" || value === "1") return true;
962
+ if (value === "false" || value === "0") return false;
963
+ return void 0;
964
+ }
965
+ function readCookieFlag(cookie) {
966
+ for (const part of cookie.split(";")) {
967
+ const separator = part.indexOf("=");
968
+ if (separator < 0) continue;
969
+ if (part.slice(0, separator).trim() !== NUI_SEARCH_FLAG) continue;
970
+ const rawValue = part.slice(separator + 1);
971
+ try {
972
+ return parseFlagValue(decodeURIComponent(rawValue));
973
+ } catch {
974
+ return void 0;
975
+ }
976
+ }
977
+ return void 0;
978
+ }
979
+ function resolveNuiSearchFlag(env = {}) {
980
+ const inBrowser = typeof window !== "undefined" && typeof document !== "undefined";
981
+ const search = env.search ?? (inBrowser ? window.location.search : "");
982
+ const cookie = env.cookie ?? (inBrowser ? document.cookie : "");
983
+ const setCookie = env.setCookie ?? (inBrowser ? (value) => {
984
+ document.cookie = value;
985
+ } : void 0);
986
+ const fromQuery = parseFlagValue(
987
+ new URLSearchParams(search).get(NUI_SEARCH_FLAG)
988
+ );
989
+ if (fromQuery !== void 0) {
990
+ const secure = inBrowser && window.location.protocol === "https:" ? "; Secure" : "";
991
+ setCookie?.(
992
+ `${NUI_SEARCH_FLAG}=${fromQuery}; path=/; SameSite=Lax${secure}`
993
+ );
994
+ return fromQuery;
995
+ }
996
+ return readCookieFlag(cookie);
997
+ }
998
+ function isNuiSearchEnabled({
999
+ isAuthenticated,
1000
+ flag
1001
+ }) {
1002
+ return isAuthenticated === true && flag === true;
1003
+ }
1004
+
1005
+ // src/utils/searchRouting.ts
1006
+ function decideSearchRoute({
1007
+ enabled,
1008
+ snapshot
1009
+ }) {
1010
+ const term = snapshot.query.trim();
1011
+ if (!enabled || !term) return "search";
1012
+ if (snapshot.loading || snapshot.settledQuery !== term) return "wait";
1013
+ if (snapshot.suggestionFetchFailed) return "search";
1014
+ return snapshot.suggestions.length > 0 ? "search" : "handoff";
1015
+ }
1016
+ var HANDLE_POLL_MS = 50;
1017
+ var HANDLE_TIMEOUT_MS = 15e3;
1018
+ var SalesAIChatWidget = lazy(async () => {
1019
+ const [widget] = await Promise.all([
1020
+ import('@nuskin/react-nui-chat-sdk'),
1021
+ import('@nuskin/react-nui-chat-sdk/styles.css')
1022
+ ]);
1023
+ return { default: widget.SalesAIChatWidget };
1024
+ });
1025
+ function getSalesAIHandle() {
1026
+ return window.SalesAI;
1027
+ }
1028
+ var AgentErrorBoundary = class extends Component {
1029
+ constructor() {
1030
+ super(...arguments);
1031
+ this.state = { failed: false };
1032
+ }
1033
+ static getDerivedStateFromError() {
1034
+ return { failed: true };
1035
+ }
1036
+ componentDidCatch(_error, _info) {
1037
+ this.props.onError();
1038
+ }
1039
+ render() {
1040
+ return this.state.failed ? null : this.props.children;
1041
+ }
1042
+ };
1043
+ function useHandoff({
1044
+ query,
1045
+ handoffContext,
1046
+ agentContext,
1047
+ onUnavailable
1048
+ }) {
1049
+ useEffect(() => {
1050
+ let cancelled = false;
1051
+ let timer;
1052
+ const startedAt = Date.now();
1053
+ const tryOpen = () => {
1054
+ if (cancelled) return;
1055
+ const handle = getSalesAIHandle();
1056
+ if (handle) {
1057
+ handle.open({
1058
+ query,
1059
+ context: { ...agentContext, ...handoffContext }
1060
+ });
1061
+ return;
1062
+ }
1063
+ if (Date.now() - startedAt >= HANDLE_TIMEOUT_MS) {
1064
+ onUnavailable();
1065
+ return;
1066
+ }
1067
+ timer = setTimeout(tryOpen, HANDLE_POLL_MS);
1068
+ };
1069
+ tryOpen();
1070
+ return () => {
1071
+ cancelled = true;
1072
+ if (timer !== void 0) clearTimeout(timer);
1073
+ };
1074
+ }, [query]);
1075
+ }
1076
+ function NuiAgentPanel({
1077
+ agent,
1078
+ query,
1079
+ handoffContext,
1080
+ onUnavailable
1081
+ }) {
1082
+ useHandoff({
1083
+ query,
1084
+ handoffContext,
1085
+ agentContext: agent.context,
1086
+ onUnavailable
1087
+ });
1088
+ return /* @__PURE__ */ jsx(AgentErrorBoundary, { onError: onUnavailable, children: /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
1089
+ SalesAIChatWidget,
1090
+ {
1091
+ appKey: agent.appKey,
1092
+ apiBaseUrl: agent.apiBaseUrl,
1093
+ theme: agent.theme ?? "nui-light",
1094
+ defaultOpen: true,
1095
+ hideFab: true,
1096
+ placement: "host",
1097
+ ...agent.language === void 0 ? {} : { language: agent.language },
1098
+ ...agent.renderProduct === void 0 ? {} : { renderProduct: agent.renderProduct }
1099
+ }
1100
+ ) }) });
1101
+ }
1102
+ var SUBMIT_WAIT_TIMEOUT_MS = 2e3;
1103
+ function uniqueRecentSearches(queries) {
1104
+ const seen = /* @__PURE__ */ new Set();
1105
+ return queries.filter((query) => {
1106
+ const key = query.trim().toLowerCase();
1107
+ if (!key || seen.has(key)) return false;
1108
+ seen.add(key);
1109
+ return true;
1110
+ });
1111
+ }
1112
+ function CommerceSearchView({
1113
+ isOpen,
1114
+ onClose,
1115
+ service,
1116
+ navigate,
1117
+ isAuthenticated,
1118
+ agent,
1119
+ logo,
1120
+ minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH,
1121
+ gtmContext
1122
+ }) {
1123
+ const [flag, setFlag] = useState(void 0);
1124
+ const [handoff, setHandoff] = useState(null);
1125
+ const agentQuery = handoff?.query ?? null;
1126
+ const [pendingTerm, setPendingTerm] = useState(null);
1127
+ const [recentSearches, setRecentSearches] = useState([]);
1128
+ const search = useSearchSuggestions({
1129
+ service,
1130
+ enabled: isOpen && agentQuery === null
1131
+ });
1132
+ const {
1133
+ clearQuery,
1134
+ didYouMean,
1135
+ emptyFetched,
1136
+ hasMoreResults,
1137
+ loading,
1138
+ query,
1139
+ settledQuery,
1140
+ suggestionFetchFailed,
1141
+ suggestions,
1142
+ updateQuery
1143
+ } = search;
1144
+ const enabled = agent !== void 0 && isNuiSearchEnabled({ isAuthenticated, flag });
1145
+ const items = getVisibleSuggestions({
1146
+ didYouMean,
1147
+ suggestions
1923
1148
  });
1149
+ const showDidYouMean = didYouMean.length > 0;
1150
+ useEffect(() => {
1151
+ if (isOpen) setFlag(resolveNuiSearchFlag());
1152
+ }, [isOpen]);
1153
+ useEffect(() => {
1154
+ if (isOpen) {
1155
+ setRecentSearches(
1156
+ uniqueRecentSearches(service.getRecentSearches())
1157
+ );
1158
+ }
1159
+ }, [isOpen, service, agentQuery]);
1160
+ const close = useCallback(() => {
1161
+ setHandoff(null);
1162
+ setPendingTerm(null);
1163
+ clearQuery();
1164
+ onClose();
1165
+ }, [clearQuery, onClose]);
1166
+ useEffect(() => {
1167
+ if (!isOpen) {
1168
+ setHandoff(null);
1169
+ setPendingTerm(null);
1170
+ clearQuery();
1171
+ }
1172
+ }, [isOpen, clearQuery]);
1173
+ const navigateTo = useCallback(
1174
+ (path) => {
1175
+ if (!path) return;
1176
+ navigate(path);
1177
+ close();
1178
+ },
1179
+ [navigate, close]
1180
+ );
1181
+ const trackSemantic = useCallback(
1182
+ (productResultsData) => {
1183
+ trackProductSearch({
1184
+ ...gtmContext,
1185
+ searchType: SEARCH_GTM_SEARCH_TYPES.SEMANTIC,
1186
+ productResultsData
1187
+ });
1188
+ },
1189
+ [gtmContext]
1190
+ );
1191
+ const runSearch = useCallback(
1192
+ (term) => {
1193
+ trackSemantic(items.length > 0 ? items : { query: term });
1194
+ navigateTo(service.submitProductSearch(term));
1195
+ },
1196
+ [items, navigateTo, service, trackSemantic]
1197
+ );
1198
+ const decide = useCallback(
1199
+ (term) => decideSearchRoute({
1200
+ enabled,
1201
+ snapshot: {
1202
+ query: term,
1203
+ loading,
1204
+ settledQuery,
1205
+ suggestions,
1206
+ suggestionFetchFailed
1207
+ }
1208
+ }),
1209
+ [enabled, loading, settledQuery, suggestions, suggestionFetchFailed]
1210
+ );
1211
+ const act = useCallback(
1212
+ (term, route) => {
1213
+ setPendingTerm(null);
1214
+ if (route === "handoff") {
1215
+ trackChatEntryPoint(gtmContext ?? {});
1216
+ setHandoff({
1217
+ query: term,
1218
+ didYouMean: didYouMean.map((item) => item.value)
1219
+ });
1220
+ return;
1221
+ }
1222
+ runSearch(term);
1223
+ },
1224
+ [didYouMean, gtmContext, runSearch]
1225
+ );
1226
+ const handleSubmit = useCallback(
1227
+ (value) => {
1228
+ const term = value.trim();
1229
+ if (!term) return;
1230
+ if (term.length < minimumSearchLength) {
1231
+ runSearch(term);
1232
+ return;
1233
+ }
1234
+ const route = decide(term);
1235
+ if (route === "wait") {
1236
+ setPendingTerm(term);
1237
+ return;
1238
+ }
1239
+ act(term, route);
1240
+ },
1241
+ [act, decide, minimumSearchLength, runSearch]
1242
+ );
1243
+ const latest = useRef({ act, decide });
1244
+ latest.current = { act, decide };
1245
+ useEffect(() => {
1246
+ if (pendingTerm === null) return void 0;
1247
+ const route = latest.current.decide(pendingTerm);
1248
+ if (route !== "wait") {
1249
+ latest.current.act(pendingTerm, route);
1250
+ return void 0;
1251
+ }
1252
+ const timer = setTimeout(
1253
+ () => latest.current.act(pendingTerm, "search"),
1254
+ SUBMIT_WAIT_TIMEOUT_MS
1255
+ );
1256
+ return () => clearTimeout(timer);
1257
+ }, [
1258
+ pendingTerm,
1259
+ loading,
1260
+ settledQuery,
1261
+ suggestions,
1262
+ suggestionFetchFailed
1263
+ ]);
1264
+ const handleSelect = useCallback(
1265
+ (option) => {
1266
+ if (isSuggestionDisabled(
1267
+ option
1268
+ )) {
1269
+ return;
1270
+ }
1271
+ const term = String(option["value"] ?? option["label"] ?? "");
1272
+ if (term.trim()) trackSemantic([option]);
1273
+ const suggestionOption = option;
1274
+ const href = service.getSuggestionHref(suggestionOption);
1275
+ navigateTo(
1276
+ href || service.submitProductSearch(term, suggestionOption)
1277
+ );
1278
+ },
1279
+ [navigateTo, service, trackSemantic]
1280
+ );
1281
+ const handleRunAction = useCallback(
1282
+ (option) => {
1283
+ if (isSuggestionDisabled(
1284
+ option
1285
+ )) {
1286
+ return;
1287
+ }
1288
+ const result = service.activateSuggestionAction(
1289
+ option
1290
+ );
1291
+ void Promise.resolve(result).then(navigateTo).catch(() => void 0);
1292
+ },
1293
+ [navigateTo, service]
1294
+ );
1295
+ const handleSeeMore = useCallback(() => {
1296
+ const term = query.trim();
1297
+ if (term) navigateTo(service.viewMoreResults({ query: term }));
1298
+ }, [navigateTo, query, service]);
1299
+ const handleRecentSelect = useCallback(
1300
+ (recentQuery) => {
1301
+ trackSemantic({ query: recentQuery });
1302
+ navigateTo(service.submitProductSearch(recentQuery));
1303
+ },
1304
+ [navigateTo, service, trackSemantic]
1305
+ );
1306
+ const handleClearRecent = useCallback(() => {
1307
+ service.clearRecentSearches();
1308
+ setRecentSearches([]);
1309
+ }, [service]);
1310
+ const handleAgentUnavailable = useCallback(() => {
1311
+ if (agentQuery !== null)
1312
+ navigateTo(service.submitProductSearch(agentQuery));
1313
+ }, [agentQuery, navigateTo, service]);
1314
+ return /* @__PURE__ */ jsx(
1315
+ SearchModal,
1316
+ {
1317
+ isOpen,
1318
+ onClose: close,
1319
+ logo,
1320
+ input: {
1321
+ value: query,
1322
+ onChange: updateQuery,
1323
+ onSubmit: handleSubmit
1324
+ },
1325
+ suggestions: {
1326
+ items,
1327
+ loading,
1328
+ showNoResults: !loading && emptyFetched && items.length === 0,
1329
+ showDidYouMean,
1330
+ hasMore: hasMoreResults,
1331
+ query,
1332
+ onSelect: handleSelect,
1333
+ onRunAction: handleRunAction,
1334
+ onSeeMore: handleSeeMore
1335
+ },
1336
+ recentSearches: {
1337
+ items: recentSearches,
1338
+ onSelect: handleRecentSelect,
1339
+ onClear: handleClearRecent
1340
+ },
1341
+ agentPanel: handoff === null || agent === void 0 ? void 0 : /* @__PURE__ */ jsx(
1342
+ NuiAgentPanel,
1343
+ {
1344
+ agent,
1345
+ query: handoff.query,
1346
+ handoffContext: {
1347
+ source: "storefront_search",
1348
+ trigger: "zero_results",
1349
+ query: handoff.query,
1350
+ did_you_mean: handoff.didYouMean
1351
+ },
1352
+ onUnavailable: handleAgentUnavailable
1353
+ }
1354
+ )
1355
+ }
1356
+ );
1357
+ }
1358
+ var GUEST = "guest";
1359
+ function CommerceSearch({
1360
+ isOpen,
1361
+ onClose,
1362
+ navigate,
1363
+ customer,
1364
+ country,
1365
+ language,
1366
+ logo
1367
+ }) {
1368
+ const [hasOpened, setHasOpened] = useState(false);
1369
+ useEffect(() => {
1370
+ if (isOpen) setHasOpened(true);
1371
+ }, [isOpen]);
1372
+ const { minimumSearchLength, agent } = useCommerceSearchConfig({
1373
+ country,
1374
+ language,
1375
+ enabled: hasOpened
1376
+ });
1377
+ const customerTypeRef = useRef(GUEST);
1378
+ customerTypeRef.current = customer.isAuthenticated ? customer.accountType ?? GUEST : GUEST;
1379
+ const service = useMemo(
1380
+ () => createSearchService({
1381
+ country,
1382
+ language,
1383
+ locale: `${country.toUpperCase()}-${language.toLowerCase()}`,
1384
+ getMarketConfig: fetchMarketConfig,
1385
+ minimumSearchLength,
1386
+ getCustomerType: () => customerTypeRef.current
1387
+ }),
1388
+ [country, language, minimumSearchLength]
1389
+ );
1390
+ const navigateToPath = useCallback(
1391
+ (path) => navigate(toNavigationTarget(path)),
1392
+ [navigate]
1393
+ );
1394
+ const { isAuthenticated, accountType, userId } = customer;
1395
+ const gtmContext = useMemo(
1396
+ () => ({
1397
+ countryCode: country,
1398
+ language,
1399
+ userStatus: isAuthenticated ? SEARCH_GTM_USER_STATUS.LOGGED_IN : SEARCH_GTM_USER_STATUS.GUEST,
1400
+ ...isAuthenticated && accountType ? { accountType } : {},
1401
+ ...isAuthenticated && userId ? { userId } : {}
1402
+ }),
1403
+ [country, language, isAuthenticated, accountType, userId]
1404
+ );
1405
+ return /* @__PURE__ */ jsx(
1406
+ CommerceSearchView,
1407
+ {
1408
+ isOpen,
1409
+ onClose,
1410
+ service,
1411
+ navigate: navigateToPath,
1412
+ isAuthenticated,
1413
+ ...agent ? { agent } : {},
1414
+ logo,
1415
+ minimumSearchLength,
1416
+ gtmContext
1417
+ }
1418
+ );
1924
1419
  }
1925
1420
 
1926
- export { CATALOG_SUGGESTIONS_PATH, 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, MAX_VISIBLE_SUGGESTIONS, RECENT_SEARCHES_DEFAULT_LIMIT, RECENT_SEARCHES_PREFIX, SEARCH_GTM_EVENTS, SEARCH_GTM_SEARCH_TYPES, SEARCH_GTM_USER_STATUS, SEARCH_PLACEHOLDER, STOREFRONT_SHOPPING_CONTEXT, TRIGGER_PLACEHOLDER, createChatService, createSearchService, getImageFromProperty, getIngredientTerms, getLocaleFromUrl, getSeeMoreResultsPayload, getVisibleSuggestions, isCartActionDisabled, isSellableProductStatus, isSuggestionDisabled, mapCatalogProductToSuggestion, normalizeDidYouMean, pushGtmEvent, resolveApiGatewayPath, resolveSuggestionImageSrc, trackChatEntryPoint, trackErrorPopup, trackProductSearch, trackSearchType, useSearchHistory, useSearchHistoryStore, useSearchSuggestions, useVoiceSearch };
1421
+ export { CommerceSearch };
1927
1422
  //# sourceMappingURL=index.mjs.map
1928
1423
  //# sourceMappingURL=index.mjs.map