@nuskin/react-search-sdk 1.0.1-code-cleanup.1 → 1.1.0-pa-1255.1

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.js CHANGED
@@ -3,11 +3,11 @@
3
3
  var react = require('react');
4
4
  var configurationSdk = require('@nuskin/configuration-sdk');
5
5
  var reactSearchComponents = require('@nuskin/react-search-components');
6
+ var jsxRuntime = require('react/jsx-runtime');
6
7
  var reactNuiChatSdk = require('@nuskin/react-nui-chat-sdk');
7
8
  require('@nuskin/react-nui-chat-sdk/styles.css');
8
9
  var reactProductSdk = require('@nuskin/react-product-sdk');
9
10
  var styled = require('@emotion/styled');
10
- var jsxRuntime = require('react/jsx-runtime');
11
11
 
12
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
13
 
@@ -23,36 +23,53 @@ var CATALOG_SUGGESTIONS_PATH = "/storefront/catalogs/search/suggestions";
23
23
  var RECENT_SEARCHES_PREFIX = "nuskin:header:recentSearches";
24
24
  var RECENT_SEARCHES_DEFAULT_LIMIT = 10;
25
25
  var DEFAULT_SUGGESTION_PAGE = 1;
26
- var DEFAULT_SUGGESTION_SIZE = 6;
27
26
  var DEFAULT_SEARCH_PATH = "/search";
28
27
  var DEBOUNCE_MS = 220;
29
28
  var MINIMUM_SEARCH_CHAR_LENGTH = 2;
30
- var MAX_VISIBLE_SUGGESTIONS = 6;
29
+ var DEFAULT_MAX_SUGGESTIONS = 6;
30
+ var MAX_SUGGESTIONS_CEILING = 20;
31
31
  var EQUINOX_MARKETS_MAP = "Equinox_Markets";
32
- var DEFAULT_EQUINOX_CLIENT_ID = "63056848d7532f9923f3d08cb48c423b";
32
+ var COMMERCE_SEARCH_MAP = "Commerce_Search";
33
33
 
34
34
  // src/config/commerceSearchConfig.ts
35
35
  var COMMERCE_SEARCH_CONFIG_DEFAULTS = Object.freeze({
36
- minimumSearchLength: MINIMUM_SEARCH_CHAR_LENGTH
36
+ minimumSearchLength: MINIMUM_SEARCH_CHAR_LENGTH,
37
+ maxSuggestions: DEFAULT_MAX_SUGGESTIONS,
38
+ agentEnabledFor: Object.freeze({
39
+ guest: false,
40
+ brandAffiliate: false,
41
+ preferredCustomer: false,
42
+ retailCustomer: false
43
+ })
37
44
  });
38
- var TEST_ENVIRONMENT_VALUES = {
39
- agent: {
40
- appKey: "pk_commerce_product_advisor_624aea4305b09240e8c8f41d0f6527b2",
41
- apiBaseUrl: "https://nui.tst.nuskin.ai/"
42
- },
43
- // Open to visitors who are not signed in, so the handoff can be tried where signing in is not
44
- // possible (for example local development). Sign-in stays required everywhere else.
45
- allowGuestAgent: true
45
+ var FIELDS = {
46
+ minimumLength: "commerceSearchMinimumLength",
47
+ maxSuggestions: "commerceSearchMaxSuggestions",
48
+ agentAppKey: "commerceSearchAgentAppKey",
49
+ agentApiBaseUrl: "commerceSearchAgentApiBaseUrl",
50
+ enabledGuest: "commerceSearchAgentEnabledGuest",
51
+ enabledBrandAffiliate: "commerceSearchAgentEnabledBrandAffiliate",
52
+ enabledPreferredCustomer: "commerceSearchAgentEnabledPreferredCustomer",
53
+ enabledRetailCustomer: "commerceSearchAgentEnabledRetailCustomer"
46
54
  };
47
55
  function nonEmptyString(value) {
48
56
  return typeof value === "string" ? value.trim() : "";
49
57
  }
58
+ function wholeNumber(value) {
59
+ const number = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
60
+ return typeof number === "number" && Number.isInteger(number) ? number : void 0;
61
+ }
62
+ function positiveWholeNumber(value) {
63
+ const number = wholeNumber(value);
64
+ return number !== void 0 && number >= 1 ? number : void 0;
65
+ }
50
66
  function normalizeCommerceSearchConfig(raw, { language } = {}) {
51
- const minimumSearchLength = Number(raw?.minimumSearchLength);
52
- const appKey = nonEmptyString(raw?.agent?.appKey);
53
- const apiBaseUrl = nonEmptyString(raw?.agent?.apiBaseUrl);
67
+ const appKey = nonEmptyString(raw?.[FIELDS.agentAppKey]);
68
+ const apiBaseUrl = nonEmptyString(raw?.[FIELDS.agentApiBaseUrl]);
69
+ const maxSuggestions = positiveWholeNumber(raw?.[FIELDS.maxSuggestions]);
54
70
  return {
55
- minimumSearchLength: Number.isInteger(minimumSearchLength) && minimumSearchLength >= 1 ? minimumSearchLength : COMMERCE_SEARCH_CONFIG_DEFAULTS.minimumSearchLength,
71
+ minimumSearchLength: positiveWholeNumber(raw?.[FIELDS.minimumLength]) ?? COMMERCE_SEARCH_CONFIG_DEFAULTS.minimumSearchLength,
72
+ maxSuggestions: maxSuggestions === void 0 ? COMMERCE_SEARCH_CONFIG_DEFAULTS.maxSuggestions : Math.min(maxSuggestions, MAX_SUGGESTIONS_CEILING),
56
73
  ...appKey && apiBaseUrl ? {
57
74
  agent: {
58
75
  appKey,
@@ -60,13 +77,36 @@ function normalizeCommerceSearchConfig(raw, { language } = {}) {
60
77
  ...language ? { language } : {}
61
78
  }
62
79
  } : {},
63
- ...raw?.allowGuestAgent === true ? { allowGuestAgent: true } : {}
80
+ agentEnabledFor: {
81
+ guest: raw?.[FIELDS.enabledGuest] === true,
82
+ brandAffiliate: raw?.[FIELDS.enabledBrandAffiliate] === true,
83
+ preferredCustomer: raw?.[FIELDS.enabledPreferredCustomer] === true,
84
+ retailCustomer: raw?.[FIELDS.enabledRetailCustomer] === true
85
+ }
64
86
  };
65
87
  }
66
88
  async function fetchCommerceSearchConfig({
67
- language
68
- } = {}) {
69
- const raw = getEnvironmentCode() === "test" ? TEST_ENVIRONMENT_VALUES : {};
89
+ country,
90
+ language,
91
+ clientId
92
+ }) {
93
+ const configuration = await getConfiguration([COMMERCE_SEARCH_MAP], {
94
+ country: country.trim().toUpperCase(),
95
+ ...clientId ? { clientId } : {}
96
+ });
97
+ const raw = configuration?.[COMMERCE_SEARCH_MAP];
98
+ if (!raw || Object.keys(raw).length === 0) {
99
+ throw new Error(
100
+ `the configuration service returned no ${COMMERCE_SEARCH_MAP} for market ${country}`
101
+ );
102
+ }
103
+ const hasKey = nonEmptyString(raw[FIELDS.agentAppKey]) !== "";
104
+ const hasUrl = nonEmptyString(raw[FIELDS.agentApiBaseUrl]) !== "";
105
+ if (hasKey !== hasUrl) {
106
+ console.warn(
107
+ `[search] The agent is half configured: ${FIELDS.agentAppKey} and ${FIELDS.agentApiBaseUrl} must both be set, or both empty. Using plain search.`
108
+ );
109
+ }
70
110
  return normalizeCommerceSearchConfig(raw, language ? { language } : {});
71
111
  }
72
112
 
@@ -74,30 +114,39 @@ async function fetchCommerceSearchConfig({
74
114
  function useCommerceSearchConfig({
75
115
  country,
76
116
  language,
77
- enabled
117
+ clientId,
118
+ isOpen
78
119
  }) {
79
- const [config, setConfig] = react.useState(
80
- COMMERCE_SEARCH_CONFIG_DEFAULTS
81
- );
120
+ const market = `${country}|${language}|${clientId ?? ""}`;
121
+ const [loaded, setLoaded] = react.useState(null);
122
+ const currentMarket = react.useRef(market);
82
123
  react.useEffect(() => {
83
- if (!enabled) return void 0;
84
- let isStale = false;
124
+ currentMarket.current = market;
125
+ }, [market]);
126
+ const reading = react.useRef(null);
127
+ react.useEffect(() => {
128
+ if (!isOpen || loaded?.market === market || reading.current === market) {
129
+ return;
130
+ }
131
+ reading.current = market;
85
132
  fetchCommerceSearchConfig({
86
- ...country ? { country } : {},
87
- ...language ? { language } : {}
88
- }).then((next) => {
89
- if (!isStale) setConfig(next);
133
+ country,
134
+ language,
135
+ ...clientId ? { clientId } : {}
136
+ }).then((config) => {
137
+ if (currentMarket.current === market) {
138
+ setLoaded({ market, config });
139
+ }
90
140
  }).catch((error) => {
91
141
  console.warn(
92
142
  "[search] Could not load the product search configuration:",
93
143
  error
94
144
  );
145
+ }).finally(() => {
146
+ if (reading.current === market) reading.current = null;
95
147
  });
96
- return () => {
97
- isStale = true;
98
- };
99
- }, [enabled, country, language]);
100
- return config;
148
+ }, [isOpen, market, loaded, country, language, clientId]);
149
+ return loaded?.market === market ? loaded.config : COMMERCE_SEARCH_CONFIG_DEFAULTS;
101
150
  }
102
151
 
103
152
  // src/copy/searchPanelCopy.ts
@@ -180,25 +229,38 @@ function useSearchPanelCopy({
180
229
  }
181
230
 
182
231
  // src/services/localeParts.ts
183
- function normalizeLocaleParts({
232
+ var TWO_LETTERS = /^[A-Za-z]{2}$/;
233
+ function marketProblem(country, language) {
234
+ const countryText = String(country ?? "").trim();
235
+ const languageText = String(language ?? "").trim();
236
+ if (TWO_LETTERS.test(countryText) && TWO_LETTERS.test(languageText)) {
237
+ return void 0;
238
+ }
239
+ return `the market is not valid (country ${JSON.stringify(countryText)}, language ${JSON.stringify(languageText)}): each must be two letters, for example US and en`;
240
+ }
241
+ function isValidMarket(country, language) {
242
+ return marketProblem(country, language) === void 0;
243
+ }
244
+ function createLocaleParts({
184
245
  country,
185
- language,
186
- locale
246
+ language
187
247
  }) {
188
- const normalizedCountry = String(country ?? "").trim().toUpperCase();
189
- const normalizedLanguage = String(language ?? "").trim().toLowerCase();
190
- const normalizedLocale = String(locale ?? "").trim() || "UNKNOWN";
248
+ const problem = marketProblem(country, language);
249
+ if (problem) throw new Error(problem);
250
+ const normalizedCountry = country.trim().toUpperCase();
251
+ const normalizedLanguage = language.trim().toLowerCase();
191
252
  return {
192
- country: normalizedCountry || "UNKNOWN",
193
- language: normalizedLanguage || "unknown",
194
- locale: normalizedLocale
253
+ country: normalizedCountry,
254
+ language: normalizedLanguage,
255
+ locale: `${normalizedCountry}-${normalizedLanguage}`
195
256
  };
196
257
  }
197
- function toCatalogLocale(localeParts) {
198
- const language = String(localeParts.language ?? "en").toLowerCase();
199
- const country = String(localeParts.country ?? "US").toUpperCase();
258
+ function toCatalogLocale({ country, language }) {
200
259
  return `${language}_${country}`;
201
260
  }
261
+ function toPathPrefix({ country, language }) {
262
+ return `/${country.toLowerCase()}/${language}`;
263
+ }
202
264
 
203
265
  // src/services/catalogRequest.ts
204
266
  function resolveApiGatewayPath(apiGatewayPath, apiName = STOREFRONT_SHOPPING_CONTEXT) {
@@ -223,13 +285,14 @@ function buildCatalogSuggestionsUrl(apiBaseUrl) {
223
285
  function buildSuggestionsRequestUrl({
224
286
  suggestionsUrl,
225
287
  searchTerm,
288
+ size,
226
289
  storeId,
227
290
  localeParts
228
291
  }) {
229
292
  const queryParams = new URLSearchParams({
230
293
  searchTerm,
231
294
  page: String(DEFAULT_SUGGESTION_PAGE),
232
- size: String(DEFAULT_SUGGESTION_SIZE),
295
+ size: String(size),
233
296
  locale: toCatalogLocale(localeParts),
234
297
  storeId
235
298
  });
@@ -241,12 +304,21 @@ async function resolveCatalogRequestConfig({
241
304
  getMarketConfig
242
305
  }) {
243
306
  const marketConfig = getMarketConfig ? await getMarketConfig({ locationLike, country }) ?? null : null;
244
- return {
245
- suggestionsUrl: buildCatalogSuggestionsUrl(
246
- resolveCatalogApiBaseUrl(marketConfig)
247
- ),
248
- storeId: resolveCatalogStoreId(marketConfig)
249
- };
307
+ const suggestionsUrl = buildCatalogSuggestionsUrl(
308
+ resolveCatalogApiBaseUrl(marketConfig)
309
+ );
310
+ if (!suggestionsUrl) {
311
+ throw new Error(
312
+ `no suggestions API for market ${country}: the market configuration has no gateway`
313
+ );
314
+ }
315
+ const storeId = resolveCatalogStoreId(marketConfig);
316
+ if (!storeId) {
317
+ throw new Error(
318
+ `no suggestions API for market ${country}: the market configuration has no store`
319
+ );
320
+ }
321
+ return { suggestionsUrl, storeId };
250
322
  }
251
323
  function resolveCustomerTypeHeaders(getCustomerType) {
252
324
  const customerType = getCustomerType?.()?.trim();
@@ -291,7 +363,7 @@ function getSessionStorage(providedStorage) {
291
363
  return null;
292
364
  }
293
365
  function getRecentHeaderSearchesStorageKey(localeParts) {
294
- const { country, language, locale } = normalizeLocaleParts(localeParts);
366
+ const { country, language, locale } = localeParts;
295
367
  return `${RECENT_SEARCHES_PREFIX}:${locale}:${country}:${language}`;
296
368
  }
297
369
  function getRecentHeaderSearches(localeParts, storage) {
@@ -354,13 +426,14 @@ function buildSearchResultsPath(query, localeParts) {
354
426
  /%2D/gi,
355
427
  "-"
356
428
  );
357
- const country = localeParts.country.toLowerCase();
358
- const language = localeParts.language.toLowerCase();
359
- return `/${country}/${language}${DEFAULT_SEARCH_PATH}/${encodedQuery}`;
429
+ return `${toPathPrefix(localeParts)}${DEFAULT_SEARCH_PATH}/${encodedQuery}`;
360
430
  }
431
+ var SCHEME = /^[a-z][a-z0-9+.-]*:/i;
361
432
  function normalizeOptionUrl(rawValue) {
362
433
  if (typeof rawValue !== "string") return "";
363
- return rawValue.trim();
434
+ const value = rawValue.trim();
435
+ if (!value || value.startsWith("/") || SCHEME.test(value)) return value;
436
+ return `/${value}`;
364
437
  }
365
438
  function safeDecodeURIComponent(value) {
366
439
  try {
@@ -390,16 +463,14 @@ function slugifyPathSegment(value) {
390
463
  function buildProductSlugPath(slugValue, localeParts) {
391
464
  const slug = slugifyPathSegment(slugValue);
392
465
  if (!slug) return "";
393
- const country = String(localeParts.country ?? "US").toLowerCase();
394
- const language = String(localeParts.language ?? "en").toLowerCase();
395
- return `/${country}/${language}/product/${slug}`;
466
+ return `${toPathPrefix(localeParts)}/product/${slug}`;
396
467
  }
397
468
  function isNavigableUrl(value) {
398
469
  if (!value) return false;
399
470
  return value.startsWith("/") || /^https?:\/\//i.test(value);
400
471
  }
401
472
  function stripLocalePrefix(path, localeParts) {
402
- const prefix = `/${localeParts.country.toLowerCase()}/${localeParts.language.toLowerCase()}`;
473
+ const prefix = toPathPrefix(localeParts);
403
474
  return path.startsWith(prefix) ? path.slice(prefix.length) || "/" : path;
404
475
  }
405
476
  function resolveOptionHref(option, localeParts) {
@@ -453,26 +524,21 @@ function getBrowserLocation(providedLocation) {
453
524
  return window.location;
454
525
  return null;
455
526
  }
456
- function resolveFetch(fetchImplOption) {
457
- if (fetchImplOption !== void 0) return fetchImplOption;
458
- return typeof fetch === "function" ? fetch.bind(globalThis) : null;
459
- }
460
- function createSearchService(options = {}) {
527
+ function createSearchService(options) {
461
528
  const {
462
529
  fetchImpl: fetchImplOption,
463
530
  location: providedLocation,
464
531
  storage: providedStorage,
465
532
  country,
466
533
  language,
467
- locale,
468
534
  getMarketConfig,
469
535
  getCustomerType,
470
- minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH
536
+ minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH,
537
+ maxSuggestions = DEFAULT_MAX_SUGGESTIONS
471
538
  } = options;
472
- const fetchImpl = resolveFetch(fetchImplOption);
473
539
  const locationLike = getBrowserLocation(providedLocation);
474
540
  const storage = getSessionStorage(providedStorage);
475
- const localeParts = normalizeLocaleParts({ country, language, locale });
541
+ const localeParts = createLocaleParts({ country, language });
476
542
  let catalogRequestConfigPromise = null;
477
543
  function loadCatalogRequestConfig() {
478
544
  if (!catalogRequestConfigPromise) {
@@ -494,16 +560,16 @@ function createSearchService(options = {}) {
494
560
  const service = {
495
561
  async fetchSuggestions(searchTerm) {
496
562
  const trimmedSearchTerm = String(searchTerm ?? "").trim();
497
- if (!trimmedSearchTerm || trimmedSearchTerm.length < minimumSearchLength || !fetchImpl) {
563
+ if (!trimmedSearchTerm || trimmedSearchTerm.length < minimumSearchLength) {
498
564
  return emptySuggestions();
499
565
  }
500
566
  const { suggestionsUrl, storeId } = await loadCatalogRequestConfig();
501
- if (!suggestionsUrl || !storeId) return emptySuggestions();
502
567
  const responsePayload = await fetchJson(
503
- fetchImpl,
568
+ fetchImplOption ?? fetch.bind(globalThis),
504
569
  buildSuggestionsRequestUrl({
505
570
  suggestionsUrl,
506
571
  searchTerm: trimmedSearchTerm,
572
+ size: maxSuggestions,
507
573
  storeId,
508
574
  localeParts
509
575
  }),
@@ -553,12 +619,15 @@ function createSearchService(options = {}) {
553
619
  }
554
620
 
555
621
  // src/services/marketConfig.ts
556
- var fetchMarketConfig = async ({ country }) => {
557
- const countryCode = String(country ?? "").trim().toUpperCase() || "US";
622
+ var fetchMarketConfig = async ({
623
+ country,
624
+ clientId
625
+ }) => {
626
+ const countryCode = country.trim().toUpperCase();
558
627
  const configuration = await getConfiguration([EQUINOX_MARKETS_MAP], {
559
628
  country: countryCode,
560
629
  ...getEnvironmentCode() ? { environment: getEnvironmentCode() } : {},
561
- clientId: DEFAULT_EQUINOX_CLIENT_ID,
630
+ ...clientId ? { clientId } : {},
562
631
  shoppingContext: STOREFRONT_SHOPPING_CONTEXT
563
632
  });
564
633
  return configuration?.[EQUINOX_MARKETS_MAP] ?? null;
@@ -577,14 +646,25 @@ var SEARCH_GTM_USER_STATUS = {
577
646
  var GTM_FALLBACK_NA = "NA";
578
647
 
579
648
  // src/utils/navigationTarget.ts
649
+ var EXTERNAL_URL = /^https?:\/\//i;
650
+ var INTERNAL_PATH = /^\/(?![/\\])[^\t\n\r]*$/;
580
651
  function toNavigationTarget(path) {
581
- return /^https?:\/\//i.test(path) ? { type: "external", url: path } : { type: "internal", path };
652
+ if (EXTERNAL_URL.test(path)) return { type: "external", url: path };
653
+ if (INTERNAL_PATH.test(path)) return { type: "internal", path };
654
+ return void 0;
582
655
  }
583
656
 
584
657
  // src/utils/productEnvironment.ts
585
- var PRODUCT_ENVIRONMENTS = ["dev", "test", "prod"];
658
+ var PRODUCT_ENVIRONMENTS = {
659
+ dev: true,
660
+ test: true,
661
+ prod: true
662
+ };
663
+ function isProductEnvironment(code) {
664
+ return Object.keys(PRODUCT_ENVIRONMENTS).includes(code);
665
+ }
586
666
  function toProductEnvironment(environmentCode) {
587
- return environmentCode && PRODUCT_ENVIRONMENTS.includes(environmentCode) ? environmentCode : void 0;
667
+ return environmentCode !== void 0 && isProductEnvironment(environmentCode) ? environmentCode : void 0;
588
668
  }
589
669
  function getProductEnvironment() {
590
670
  try {
@@ -593,6 +673,47 @@ function getProductEnvironment() {
593
673
  return void 0;
594
674
  }
595
675
  }
676
+ var NOTHING = () => void 0;
677
+ function CommerceSearchUnavailable({
678
+ isOpen,
679
+ onClose,
680
+ country,
681
+ language,
682
+ logo
683
+ }) {
684
+ react.useEffect(() => {
685
+ console.error(
686
+ `[search] Search is unavailable: ${marketProblem(country, language)}`
687
+ );
688
+ }, [country, language]);
689
+ return /* @__PURE__ */ jsxRuntime.jsx(
690
+ reactSearchComponents.SearchPanel,
691
+ {
692
+ isOpen,
693
+ onClose,
694
+ copy: SEARCH_PANEL_COPY_EN,
695
+ logo,
696
+ input: { value: "", onChange: NOTHING, onSubmit: NOTHING },
697
+ suggestions: {
698
+ items: [],
699
+ loading: false,
700
+ showNoResults: false,
701
+ showDidYouMean: false,
702
+ hasMore: false,
703
+ query: "",
704
+ onSelect: NOTHING,
705
+ onRunAction: NOTHING,
706
+ onSeeMore: NOTHING
707
+ },
708
+ recentSearches: {
709
+ items: [],
710
+ onSelect: NOTHING,
711
+ onClear: NOTHING
712
+ },
713
+ agentPanel: /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", children: "Search is unavailable right now." })
714
+ }
715
+ );
716
+ }
596
717
 
597
718
  // src/utils/nuiSearchGate.ts
598
719
  var NUI_SEARCH_FLAG = "enableNuiSearch";
@@ -624,29 +745,23 @@ function resolveNuiSearchFlag(env = {}) {
624
745
  return parseFlagValue(new URLSearchParams(search).get(NUI_SEARCH_FLAG)) ?? readCookieFlag(cookie);
625
746
  }
626
747
  function isNuiSearchEnabled({
627
- isAuthenticated,
628
748
  flag,
629
- allowGuests = false
749
+ enabledForCustomer
630
750
  }) {
631
- return flag === true && (isAuthenticated === true || allowGuests);
751
+ return flag === true && enabledForCustomer;
632
752
  }
633
753
 
634
754
  // src/hooks/useNuiAgentGate.ts
635
755
  function useNuiAgentGate({
636
756
  hasAgent,
637
757
  isOpen,
638
- isAuthenticated,
639
- allowGuestAgent
758
+ enabledForCustomer
640
759
  }) {
641
760
  const [flag, setFlag] = react.useState(void 0);
642
761
  react.useEffect(() => {
643
762
  if (isOpen) setFlag(resolveNuiSearchFlag());
644
763
  }, [isOpen]);
645
- return hasAgent && isNuiSearchEnabled({
646
- isAuthenticated,
647
- flag,
648
- allowGuests: allowGuestAgent
649
- });
764
+ return hasAgent && isNuiSearchEnabled({ flag, enabledForCustomer });
650
765
  }
651
766
  function uniqueRecentSearches(queries) {
652
767
  const seen = /* @__PURE__ */ new Set();
@@ -980,7 +1095,7 @@ var FETCH_FAILED = {
980
1095
  emptyFetched: true,
981
1096
  failed: true
982
1097
  };
983
- function toSettledResults(response) {
1098
+ function toSettledResults(response, maxSuggestions) {
984
1099
  const { suggestions: allSuggestions, didYouMean } = normalizeSuggestionsResponse(response);
985
1100
  if (didYouMean.length > 0) {
986
1101
  return {
@@ -995,18 +1110,19 @@ function toSettledResults(response) {
995
1110
  resolveTotalResultsCount2(response, allSuggestions.length),
996
1111
  allSuggestions.length
997
1112
  );
998
- const suggestions = allSuggestions.slice(0, MAX_VISIBLE_SUGGESTIONS);
1113
+ const suggestions = allSuggestions.slice(0, maxSuggestions);
999
1114
  return {
1000
1115
  suggestions,
1001
1116
  didYouMean,
1002
- hasMoreResults: total > MAX_VISIBLE_SUGGESTIONS,
1117
+ hasMoreResults: total > maxSuggestions,
1003
1118
  emptyFetched: suggestions.length === 0,
1004
1119
  failed: false
1005
1120
  };
1006
1121
  }
1007
1122
  function useSearchSuggestions({
1008
1123
  service,
1009
- enabled = true
1124
+ enabled = true,
1125
+ maxSuggestions = DEFAULT_MAX_SUGGESTIONS
1010
1126
  }) {
1011
1127
  const [query, setQuery] = react.useState("");
1012
1128
  const [suggestions, setSuggestions] = react.useState([]);
@@ -1063,7 +1179,8 @@ function useSearchSuggestions({
1063
1179
  let results;
1064
1180
  try {
1065
1181
  results = toSettledResults(
1066
- await service.fetchSuggestions(trimmedQuery)
1182
+ await service.fetchSuggestions(trimmedQuery),
1183
+ maxSuggestions
1067
1184
  );
1068
1185
  } catch (error) {
1069
1186
  console.warn("[search] Could not load suggestions:", error);
@@ -1073,9 +1190,10 @@ function useSearchSuggestions({
1073
1190
  settle(results, trimmedQuery);
1074
1191
  setLoading(false);
1075
1192
  },
1076
- [enabled, service, resetSuggestionsState, settle]
1193
+ [enabled, maxSuggestions, service, resetSuggestionsState, settle]
1077
1194
  );
1078
1195
  react.useEffect(() => {
1196
+ latestRequestId.current += 1;
1079
1197
  if (!enabled) {
1080
1198
  resetSuggestionsState();
1081
1199
  return void 0;
@@ -1083,7 +1201,6 @@ function useSearchSuggestions({
1083
1201
  if (fetchDebounced.current) {
1084
1202
  clearTimeout(fetchDebounced.current);
1085
1203
  }
1086
- latestRequestId.current += 1;
1087
1204
  fetchDebounced.current = setTimeout(() => {
1088
1205
  void runFetch(query);
1089
1206
  }, DEBOUNCE_MS);
@@ -1365,11 +1482,11 @@ function useHandoff({
1365
1482
  onUnavailableRef.current = onUnavailable;
1366
1483
  });
1367
1484
  react.useEffect(() => {
1368
- let cancelled = false;
1485
+ let canceled = false;
1369
1486
  let timer;
1370
1487
  const startedAt = Date.now();
1371
1488
  const tryOpen = () => {
1372
- if (cancelled) return;
1489
+ if (canceled) return;
1373
1490
  const handle = getSalesAIHandle();
1374
1491
  if (handle) {
1375
1492
  handle.open({
@@ -1389,7 +1506,7 @@ function useHandoff({
1389
1506
  };
1390
1507
  tryOpen();
1391
1508
  return () => {
1392
- cancelled = true;
1509
+ canceled = true;
1393
1510
  if (timer !== void 0) clearTimeout(timer);
1394
1511
  };
1395
1512
  }, [query]);
@@ -1430,12 +1547,12 @@ function CommerceSearchView({
1430
1547
  onClose,
1431
1548
  service,
1432
1549
  navigate,
1433
- isAuthenticated,
1434
1550
  agent,
1435
- allowGuestAgent = false,
1551
+ agentEnabledForCustomer,
1436
1552
  agentProducts,
1437
1553
  logo,
1438
1554
  minimumSearchLength = MINIMUM_SEARCH_CHAR_LENGTH,
1555
+ maxSuggestions = DEFAULT_MAX_SUGGESTIONS,
1439
1556
  gtmContext
1440
1557
  }) {
1441
1558
  const [handoff, setHandoff] = react.useState(null);
@@ -1443,6 +1560,7 @@ function CommerceSearchView({
1443
1560
  const [pendingTerm, setPendingTerm] = react.useState(null);
1444
1561
  const search = useSearchSuggestions({
1445
1562
  service,
1563
+ maxSuggestions,
1446
1564
  enabled: isOpen && agentQuery === null
1447
1565
  });
1448
1566
  const {
@@ -1458,8 +1576,7 @@ function CommerceSearchView({
1458
1576
  const agentEnabled = useNuiAgentGate({
1459
1577
  hasAgent: agent !== void 0,
1460
1578
  isOpen,
1461
- isAuthenticated,
1462
- allowGuestAgent
1579
+ enabledForCustomer: agentEnabledForCustomer
1463
1580
  });
1464
1581
  const { recentSearches, clearRecentSearches } = useRecentSearches({
1465
1582
  service,
@@ -1617,43 +1734,89 @@ function CommerceSearchView({
1617
1734
  );
1618
1735
  }
1619
1736
  var GUEST = "guest";
1620
- function CommerceSearch({
1737
+ var AGENT_SWITCH_FOR = {
1738
+ guest: "guest",
1739
+ "Brand Affiliate": "brandAffiliate",
1740
+ "Retail Customer": "retailCustomer",
1741
+ "Preferred Customer/Member": "preferredCustomer"
1742
+ };
1743
+ var AGENT_SWITCHES = new Map(
1744
+ Object.entries(AGENT_SWITCH_FOR)
1745
+ );
1746
+ function CommerceSearch(props) {
1747
+ if (!isValidMarket(props.country, props.language)) {
1748
+ return /* @__PURE__ */ jsxRuntime.jsx(
1749
+ CommerceSearchUnavailable,
1750
+ {
1751
+ isOpen: props.isOpen,
1752
+ onClose: props.onClose,
1753
+ country: props.country,
1754
+ language: props.language,
1755
+ logo: props.logo
1756
+ }
1757
+ );
1758
+ }
1759
+ return /* @__PURE__ */ jsxRuntime.jsx(ValidMarketSearch, { ...props });
1760
+ }
1761
+ function ValidMarketSearch({
1621
1762
  isOpen,
1622
1763
  onClose,
1623
1764
  navigate,
1624
1765
  customer,
1625
1766
  country,
1626
1767
  language,
1768
+ clientId,
1627
1769
  logo
1628
1770
  }) {
1629
- const [hasOpened, setHasOpened] = react.useState(false);
1630
- react.useEffect(() => {
1631
- if (isOpen) setHasOpened(true);
1632
- }, [isOpen]);
1633
1771
  const copy = useSearchPanelCopy({ language });
1634
- const { minimumSearchLength, agent, allowGuestAgent } = useCommerceSearchConfig({
1772
+ const { minimumSearchLength, maxSuggestions, agent, agentEnabledFor } = useCommerceSearchConfig({
1635
1773
  country,
1636
1774
  language,
1637
- enabled: hasOpened
1775
+ ...clientId ? { clientId } : {},
1776
+ isOpen
1638
1777
  });
1639
1778
  const customerType = customer.isAuthenticated ? customer.accountType ?? GUEST : GUEST;
1640
1779
  const customerTypeRef = react.useRef(customerType);
1641
1780
  react.useEffect(() => {
1642
1781
  customerTypeRef.current = customerType;
1643
1782
  }, [customerType]);
1783
+ const agentEnabledForCustomer = agentEnabledFor[AGENT_SWITCHES.get(customerType) ?? "guest"];
1784
+ const getMarketConfig = react.useCallback(
1785
+ (context) => fetchMarketConfig({
1786
+ ...context,
1787
+ ...clientId ? { clientId } : {}
1788
+ }),
1789
+ [clientId]
1790
+ );
1644
1791
  const service = react.useMemo(
1645
1792
  () => createSearchService({
1646
1793
  country,
1647
1794
  language,
1648
- locale: `${country.toUpperCase()}-${language.toLowerCase()}`,
1649
- getMarketConfig: fetchMarketConfig,
1795
+ getMarketConfig,
1650
1796
  minimumSearchLength,
1797
+ maxSuggestions,
1651
1798
  getCustomerType: () => customerTypeRef.current
1652
1799
  }),
1653
- [country, language, minimumSearchLength]
1800
+ [
1801
+ country,
1802
+ language,
1803
+ getMarketConfig,
1804
+ minimumSearchLength,
1805
+ maxSuggestions
1806
+ ]
1654
1807
  );
1655
1808
  const navigateToPath = react.useCallback(
1656
- (path) => navigate(toNavigationTarget(path)),
1809
+ (path) => {
1810
+ const target = toNavigationTarget(path);
1811
+ if (target) {
1812
+ navigate(target);
1813
+ } else {
1814
+ console.warn(
1815
+ "[search] Not navigating to a path the SDK does not recognize:",
1816
+ path
1817
+ );
1818
+ }
1819
+ },
1657
1820
  [navigate]
1658
1821
  );
1659
1822
  const { isAuthenticated, accountType, userId } = customer;
@@ -1683,13 +1846,13 @@ function CommerceSearch({
1683
1846
  onClose,
1684
1847
  service,
1685
1848
  navigate: navigateToPath,
1686
- isAuthenticated,
1687
1849
  ...agent ? { agent } : {},
1688
- ...allowGuestAgent ? { allowGuestAgent } : {},
1850
+ agentEnabledForCustomer,
1689
1851
  ...agentProducts ? { agentProducts } : {},
1690
1852
  copy,
1691
1853
  logo,
1692
1854
  minimumSearchLength,
1855
+ maxSuggestions,
1693
1856
  gtmContext
1694
1857
  }
1695
1858
  );