@askdialog/dialog-sdk 2.15.1 → 2.17.0

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.
Files changed (33) hide show
  1. package/README.md +16 -9
  2. package/dist/__tests__/dialogSearchAnalytics.spec.js +1 -1
  3. package/dist/__tests__/publicTypes.spec.js +7 -1
  4. package/dist/__tests__/searchController.spec.js +15 -15
  5. package/dist/__tests__/searchControllerAttribution.spec.js +23 -13
  6. package/dist/__tests__/searchImpressions.spec.js +1 -1
  7. package/dist/__tests__/searchService.spec.js +9 -10
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/searchController.d.ts +1 -1
  12. package/dist/searchController.d.ts.map +1 -1
  13. package/dist/searchController.js +10 -16
  14. package/dist/services/search.d.ts +2 -2
  15. package/dist/services/search.d.ts.map +1 -1
  16. package/dist/services/search.js +5 -5
  17. package/dist/types/search.d.ts +74 -16
  18. package/dist/types/search.d.ts.map +1 -1
  19. package/dist/types/search.js +1 -1
  20. package/dist/types/searchAnalytics.d.ts +1 -1
  21. package/dist/types/searchAnalytics.d.ts.map +1 -1
  22. package/dist/types/searchController.d.ts +10 -6
  23. package/dist/types/searchController.d.ts.map +1 -1
  24. package/dist/utils/searchControllerAnalytics.d.ts +4 -4
  25. package/dist/utils/searchControllerAnalytics.d.ts.map +1 -1
  26. package/dist/utils/searchControllerAnalytics.js +2 -1
  27. package/dist/utils/searchPrice.d.ts +9 -0
  28. package/dist/utils/searchPrice.d.ts.map +1 -0
  29. package/dist/utils/searchPrice.js +30 -0
  30. package/dist/utils/searchRequests.d.ts +8 -7
  31. package/dist/utils/searchRequests.d.ts.map +1 -1
  32. package/dist/utils/searchRequests.js +15 -5
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -260,9 +260,9 @@ Example of expected result:
260
260
 
261
261
  - Search products
262
262
 
263
- `client.search()` sends a multi-index request to the public search API. Build names with `searchIndexName(index, language, currency)`: `<index>_<lang>_<currency>`, e.g. `products_fr_eur`. Supported indices: `products`, `collections`, `articles`, `pages`. Language must be a lowercase ISO 639-1 code (`fr`, `en`). Regional locales such as `fr-FR` are rejected. The ISO 4217 currency is lowercased in the index name.
263
+ `client.search()` sends a multi-index request to the public search API. Build names with `searchIndexName(index, language, country)`: `<index>_<lang>_<country>`, e.g. `products_fr_be`. Supported indices: `products`, `collections`, `articles`, `pages`. Language must be a lowercase ISO 639-1 code (`fr`, `en`). Regional locales such as `fr-FR` are rejected. The country is the shopper's ISO 3166-1 alpha-2 code, lowercased in the index name.
264
264
 
265
- Currency is required and independent of language: `fr` with `USD` produces `products_fr_usd`. Names without a currency suffix return 404.
265
+ The country is required and independent of language: `fr` with `CA` produces `products_fr_ca`. Product prices are in the base currency of the market that sells to that country. Names without a country suffix return 404.
266
266
 
267
267
  ```typescript
268
268
  import { Dialog, DialogSearchError, searchIndexName } from '@askdialog/dialog-sdk';
@@ -273,7 +273,7 @@ const client = new Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr-FR', currency: '
273
273
  const response: SearchResponse = await client.search({
274
274
  requests: [
275
275
  {
276
- indexName: searchIndexName('products', 'fr', client.currency), // "products_fr_eur"
276
+ indexName: searchIndexName('products', 'fr', 'BE'), // "products_fr_be"
277
277
  query: 'shampoo',
278
278
  page: 0, // optional, zero-indexed (default 0)
279
279
  hitsPerPage: 20, // optional, 1-100 (default 20)
@@ -282,8 +282,12 @@ const response: SearchResponse = await client.search({
282
282
  });
283
283
  // response.results[n]: { index, hits, nbHits, page, nbPages, hitsPerPage,
284
284
  // processingTimeMS, query, queryID }
285
- // response.results[n].hits[m]: { objectID, title?, url?, handle?, imageUrl?,
286
- // priceRange? }
285
+ // A products hit is Algolia's Shopify record (`SearchProductHit`), plus `url`
286
+ // and `currency`: { objectID (variant id), id (product id), title?, handle?,
287
+ // url?, image?, variants_min_price?, price?, compare_at_price?, ... }
288
+ // Collection hits are `SearchCollectionHit`, Algolia's collection record:
289
+ // { objectID, title?, handle?, url?, body_html_safe?, image?, products_count?, ... }
290
+ // Article and page hits are `SearchHit`: { objectID, title?, url?, handle?, imageUrl? }
287
291
  ```
288
292
 
289
293
  With the IIFE bundle the results are plain runtime JSON (same shape, no types):
@@ -293,7 +297,7 @@ With the IIFE bundle the results are plain runtime JSON (same shape, no types):
293
297
  <script>
294
298
  const client = new window.DialogSDK.Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr-FR', currency: 'EUR' });
295
299
  client
296
- .search({ requests: [{ indexName: 'products_fr_eur', query: 'shampoo' }] })
300
+ .search({ requests: [{ indexName: 'products_fr_be', query: 'shampoo' }] })
297
301
  .then((response) => console.log(response.results[0].hits));
298
302
  </script>
299
303
  ```
@@ -316,13 +320,14 @@ const client = new Dialog({ apiKey: 'YOUR_API_KEY', locale: 'fr-FR', currency: '
316
320
  const controller = createSearchController({
317
321
  search: (request, options) => client.search(request, options),
318
322
  language: 'fr',
319
- currency: client.currency,
323
+ country: 'BE', // shopper's country
320
324
  analytics: {
321
325
  surface: 'search_page', // where results are displayed
322
326
  trackViewSearchResults: (params) => client.trackViewSearchResults(params),
323
327
  trackSelectSearchResult: (params) => client.trackSelectSearchResult(params),
324
328
  },
325
329
  navigate: (url) => router.push(url), // optional platform routing adapter
330
+ buildProductUrl: (hit) => `/products/${hit.handle}`, // optional, for hits without `url`
326
331
  debounceMs: 250, // optional (default 250)
327
332
  hitsPerPage: 12, // optional (default 12)
328
333
  sections: [{ index: 'collections', hitsPerPage: 5 }], // optional, first page only; a function is resolved per request
@@ -346,9 +351,11 @@ retryButton.onclick = () => controller.retry();
346
351
  controller.dispose();
347
352
  ```
348
353
 
349
- The client uses a BCP-47 `locale` such as `fr-FR` for assistant localization. Search controllers and React/Vue hooks require explicit `language` (ISO 639-1) and `currency` (ISO 4217), independently of the client locale. Pass `client.currency` to reuse its configured currency.
354
+ The client uses a BCP-47 `locale` such as `fr-FR` for assistant localization. Search controllers and React/Vue hooks require explicit `language` (ISO 639-1) and `country` (the shopper's ISO 3166-1 alpha-2 country), independently of the client locale.
350
355
 
351
- For Shopify, use `window.Shopify.currency.active` as the currency. Controller options are fixed at creation; recreate the controller to change language or currency.
356
+ For Shopify, use `window.Shopify.country` as the country. Controller options are fixed at creation; recreate the controller to change language or country.
357
+
358
+ Analytics send each product hit's `id` (the product id) as `product_id`; `navigate` receives `controller.productUrl(hit)`: the hit's `url`, else `buildProductUrl`'s result.
352
359
 
353
360
  Framework integrations:
354
361
 
@@ -17,7 +17,7 @@ const buildDialog = () => {
17
17
  };
18
18
  const envelope = {
19
19
  query_id: "query-1",
20
- index: "products_fr_eur",
20
+ index: "products_fr_fr",
21
21
  surface: "search_page",
22
22
  search_type: "lexical",
23
23
  page: 2,
@@ -19,7 +19,13 @@ describe("public API types", () => {
19
19
  expectTypeOf().toEqualTypeOf();
20
20
  expectTypeOf().toEqualTypeOf();
21
21
  expectTypeOf().toEqualTypeOf();
22
- expectTypeOf().toMatchTypeOf();
22
+ expectTypeOf().toEqualTypeOf();
23
+ expectTypeOf().toEqualTypeOf();
24
+ expectTypeOf().toEqualTypeOf();
25
+ expectTypeOf().toEqualTypeOf();
26
+ expectTypeOf().toEqualTypeOf();
27
+ expectTypeOf().toEqualTypeOf();
28
+ expectTypeOf().toEqualTypeOf();
23
29
  const error = new DialogSearchError({ status: 404, message: "not found" });
24
30
  expectTypeOf(error.status).toEqualTypeOf();
25
31
  expectTypeOf(error.code).toEqualTypeOf();
@@ -15,7 +15,7 @@ const DEBOUNCE_MS = 250;
15
15
  const response = (overrides = {}) => ({
16
16
  results: [
17
17
  {
18
- index: "products_fr_eur",
18
+ index: "products_fr_fr",
19
19
  hits: [{ objectID: "p1" }, { objectID: "p2" }],
20
20
  nbHits: 2,
21
21
  page: 0,
@@ -50,7 +50,7 @@ const createController = () => {
50
50
  const controller = createSearchController({
51
51
  search,
52
52
  language: "fr",
53
- currency: "EUR",
53
+ country: "FR",
54
54
  analytics: {
55
55
  surface: "search_page",
56
56
  trackViewSearchResults,
@@ -94,24 +94,24 @@ describe("createSearchController", () => {
94
94
  });
95
95
  expect(controller.getState().status).toBe(SearchStatus.SUCCESS);
96
96
  });
97
- it("sends one products entry with independent language and currency", async () => {
97
+ it("sends one products entry with the language and the shopper's country", async () => {
98
98
  const controller = createSearchController({
99
99
  search,
100
100
  language: "fr",
101
- currency: "USD",
101
+ country: "US",
102
102
  analytics: {
103
103
  surface: "search_page",
104
104
  trackViewSearchResults,
105
105
  trackSelectSearchResult,
106
106
  },
107
107
  });
108
- search.mockResolvedValue(response({ query: "shoes", index: "products_fr_usd" }));
108
+ search.mockResolvedValue(response({ query: "shoes", index: "products_fr_us" }));
109
109
  controller.submit("shoes");
110
110
  await settle();
111
111
  expect(search.mock.calls[0][0]).toEqual({
112
112
  requests: [
113
113
  {
114
- indexName: "products_fr_usd",
114
+ indexName: "products_fr_us",
115
115
  query: "shoes",
116
116
  page: 0,
117
117
  hitsPerPage: 12,
@@ -305,7 +305,7 @@ describe("createSearchController", () => {
305
305
  const controller = createSearchController({
306
306
  search,
307
307
  language: "fr",
308
- currency: "USD",
308
+ country: "US",
309
309
  analytics: {
310
310
  surface: "search_page",
311
311
  trackViewSearchResults,
@@ -315,30 +315,30 @@ describe("createSearchController", () => {
315
315
  });
316
316
  const collections = {
317
317
  ...response().results[0],
318
- index: "collections_fr_usd",
318
+ index: "collections_fr_us",
319
319
  hits: [{ objectID: "c1" }],
320
320
  nbHits: 1,
321
321
  };
322
322
  search.mockResolvedValueOnce({
323
- results: [...response({ index: "products_fr_usd" }).results, collections],
323
+ results: [...response({ index: "products_fr_us" }).results, collections],
324
324
  });
325
325
  controller.submit("shoes");
326
326
  await settle();
327
327
  expect(search.mock.calls[0][0].requests).toEqual([
328
328
  {
329
- indexName: "products_fr_usd",
329
+ indexName: "products_fr_us",
330
330
  query: "shoes",
331
331
  page: 0,
332
332
  hitsPerPage: 12,
333
333
  },
334
334
  {
335
- indexName: "collections_fr_usd",
335
+ indexName: "collections_fr_us",
336
336
  query: "shoes",
337
337
  page: 0,
338
338
  hitsPerPage: 5,
339
339
  },
340
340
  ]);
341
- expect(controller.getState().response?.index).toBe("products_fr_usd");
341
+ expect(controller.getState().response?.index).toBe("products_fr_us");
342
342
  expect(controller.getState().sections).toEqual({ collections });
343
343
  });
344
344
  it("resolves a sections function per request and leaves a missing entry out", async () => {
@@ -346,7 +346,7 @@ describe("createSearchController", () => {
346
346
  const controller = createSearchController({
347
347
  search,
348
348
  language: "fr",
349
- currency: "EUR",
349
+ country: "FR",
350
350
  analytics: {
351
351
  surface: "search_page",
352
352
  trackViewSearchResults,
@@ -363,7 +363,7 @@ describe("createSearchController", () => {
363
363
  expect(search.mock.calls[0][0].requests).toHaveLength(1);
364
364
  expect(search.mock.calls[1][0].requests).toHaveLength(2);
365
365
  expect(search.mock.calls[1][0].requests[1]).toMatchObject({
366
- indexName: "collections_fr_eur",
366
+ indexName: "collections_fr_fr",
367
367
  hitsPerPage: 12,
368
368
  });
369
369
  expect(controller.getState().sections).toEqual({});
@@ -373,7 +373,7 @@ describe("createSearchController", () => {
373
373
  const controller = createSearchController({
374
374
  search,
375
375
  language: "fr",
376
- currency: "EUR",
376
+ country: "FR",
377
377
  analytics: {
378
378
  surface: "search_page",
379
379
  trackViewSearchResults,
@@ -1,4 +1,4 @@
1
- /* eslint max-lines: ["error", 300] */
1
+ /* eslint max-lines: ["error", 310] */
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import { createSearchController } from "../searchController";
4
4
  const tracker = vi.hoisted(() => ({
@@ -11,13 +11,23 @@ const tracker = vi.hoisted(() => ({
11
11
  vi.mock("../utils/searchImpressions", () => ({
12
12
  createSearchImpressionTracker: () => tracker,
13
13
  }));
14
+ // `objectID` is the variant id, `id` the product id the events carry.
15
+ const productHit = (objectID, id, url) => ({
16
+ objectID,
17
+ id,
18
+ url,
19
+ tags: [],
20
+ options: {},
21
+ option_names: [],
22
+ inventory_available: true,
23
+ });
14
24
  const response = (overrides = {}) => ({
15
25
  results: [
16
26
  {
17
- index: "products_fr_eur",
27
+ index: "products_fr_fr",
18
28
  hits: [
19
- { objectID: "p1", url: "https://shop.example/p1" },
20
- { objectID: "p2" },
29
+ productHit("v11", 101, "https://shop.example/p1"),
30
+ productHit("v22", 102),
21
31
  ],
22
32
  nbHits: 30,
23
33
  page: 0,
@@ -37,7 +47,7 @@ const navigate = vi.fn();
37
47
  const createController = () => createSearchController({
38
48
  search,
39
49
  language: "fr",
40
- currency: "EUR",
50
+ country: "FR",
41
51
  analytics: {
42
52
  surface: "search_page",
43
53
  trackViewSearchResults,
@@ -76,7 +86,7 @@ describe("search controller attribution", () => {
76
86
  await settle();
77
87
  expect(tracker.setContext).toHaveBeenCalledWith({
78
88
  query_id: "qid-1",
79
- index: "products_fr_eur",
89
+ index: "products_fr_fr",
80
90
  surface: "search_page",
81
91
  search_type: "lexical",
82
92
  page: 2,
@@ -106,7 +116,7 @@ describe("search controller attribution", () => {
106
116
  await settle();
107
117
  expect(trackViewSearchResults).toHaveBeenCalledWith({
108
118
  query_id: "qid-1",
109
- index: "products_fr_eur",
119
+ index: "products_fr_fr",
110
120
  surface: "search_page",
111
121
  search_type: "lexical",
112
122
  page: 1,
@@ -123,7 +133,7 @@ describe("search controller attribution", () => {
123
133
  await settle();
124
134
  controller.observeResult(element, 1);
125
135
  expect(tracker.observe).toHaveBeenCalledWith(element, {
126
- product_id: "p2",
136
+ product_id: "102",
127
137
  position: 2 * 12 + 2,
128
138
  });
129
139
  });
@@ -147,18 +157,18 @@ describe("search controller attribution", () => {
147
157
  await settle();
148
158
  controller.selectResult(0);
149
159
  expect(tracker.forceImpression).toHaveBeenCalledWith({
150
- product_id: "p1",
160
+ product_id: "101",
151
161
  position: 1,
152
162
  });
153
163
  expect(trackSelectSearchResult).toHaveBeenCalledWith({
154
164
  query_id: "qid-1",
155
- index: "products_fr_eur",
165
+ index: "products_fr_fr",
156
166
  surface: "search_page",
157
167
  search_type: "lexical",
158
168
  page: 1,
159
169
  total_hits: 30,
160
170
  query_length: 5,
161
- items: [{ product_id: "p1", position: 1 }],
171
+ items: [{ product_id: "101", position: 1 }],
162
172
  });
163
173
  expect(navigate).toHaveBeenCalledWith("https://shop.example/p1", landed.results[0].hits[0]);
164
174
  expect(order).toEqual(["impression", "select", "navigate"]);
@@ -169,7 +179,7 @@ describe("search controller attribution", () => {
169
179
  controller.submit("shoes");
170
180
  await settle();
171
181
  controller.selectResult(1);
172
- expect(trackSelectSearchResult).toHaveBeenCalledWith(expect.objectContaining({ items: [{ product_id: "p2", position: 2 }] }));
182
+ expect(trackSelectSearchResult).toHaveBeenCalledWith(expect.objectContaining({ items: [{ product_id: "102", position: 2 }] }));
173
183
  expect(navigate).not.toHaveBeenCalled();
174
184
  });
175
185
  it("returns true when the navigate adapter handles the transition", async () => {
@@ -203,7 +213,7 @@ describe("search controller attribution", () => {
203
213
  const controller = createSearchController({
204
214
  search,
205
215
  language: "fr",
206
- currency: "EUR",
216
+ country: "FR",
207
217
  analytics: {
208
218
  surface: "search_page",
209
219
  trackViewSearchResults,
@@ -3,7 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import { createSearchImpressionTracker } from "../utils/searchImpressions";
4
4
  const envelope = {
5
5
  query_id: "query-1",
6
- index: "products_fr_eur",
6
+ index: "products_fr_fr",
7
7
  surface: "search_page",
8
8
  search_type: "lexical",
9
9
  page: 1,
@@ -41,17 +41,16 @@ afterEach(() => {
41
41
  });
42
42
  describe("searchIndexName", () => {
43
43
  it.each([
44
- ["fr", "EUR", "products_fr_eur"],
45
- ["pt", "BRL", "products_pt_brl"],
46
- ["fr", "CAD", "products_fr_cad"],
47
- ["en", "EUR", "products_en_eur"],
48
- ])("builds an index name from language %s and currency", (locale, currency, name) => {
49
- expect(searchIndexName("products", locale, currency)).toBe(name);
44
+ ["fr", "BE", "products_fr_be"],
45
+ ["pt", "br", "products_pt_br"],
46
+ ["fr", "CA", "products_fr_ca"],
47
+ ])("builds an index name from language %s and the shopper's country", (language, country, name) => {
48
+ expect(searchIndexName("products", language, country)).toBe(name);
50
49
  });
51
- it("names every logical index", () => {
52
- expect(searchIndexName("collections", "en", "USD")).toBe("collections_en_usd");
53
- expect(() => searchIndexName("products", "fr-FR", "EUR")).toThrow(/ISO 639-1/);
54
- expect(() => searchIndexName("products", "fr", "")).toThrow(/ISO 4217/);
50
+ it("names every logical index and rejects invalid codes", () => {
51
+ expect(searchIndexName("collections", "en", "US")).toBe("collections_en_us");
52
+ expect(() => searchIndexName("products", "fr-FR", "FR")).toThrow(/ISO 639-1/);
53
+ expect(() => searchIndexName("products", "fr", "EUR")).toThrow(/ISO 3166-1/);
55
54
  });
56
55
  });
57
56
  describe("searchLexical", () => {
package/dist/index.d.ts CHANGED
@@ -6,5 +6,6 @@ export * from "./services/search";
6
6
  export * from "./types";
7
7
  export { isRtlLanguageCode, resolveTextDirection, type TextDirection, } from "./utils/localization";
8
8
  export * from "./utils/searchImpressions";
9
+ export * from "./utils/searchPrice";
9
10
  export * from "./windowAudit";
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,aAAa,GACnB,MAAM,sBAAsB,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -6,4 +6,5 @@ export * from "./services/search";
6
6
  export * from "./types";
7
7
  export { isRtlLanguageCode, resolveTextDirection, } from "./utils/localization";
8
8
  export * from "./utils/searchImpressions";
9
+ export * from "./utils/searchPrice";
9
10
  export * from "./windowAudit";
@@ -1,3 +1,3 @@
1
1
  import { SearchController, SearchControllerOptions } from "./types/searchController";
2
- export declare function createSearchController({ search, language, currency, analytics, navigate, debounceMs, hitsPerPage, sections, }: SearchControllerOptions): SearchController;
2
+ export declare function createSearchController({ search, language, country, analytics, navigate, buildProductUrl, debounceMs, hitsPerPage, sections, }: SearchControllerOptions): SearchController;
3
3
  //# sourceMappingURL=searchController.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,0BAA0B,CAAC;AAiBlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,UAAgB,EAChB,WAAgB,EAChB,QAAa,GACd,EAAE,uBAAuB,GAAG,gBAAgB,CAuK5C"}
1
+ {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../src/searchController.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,0BAA0B,CAAC;AASlC,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,QAAQ,EACR,OAAO,EACP,SAAS,EACT,QAAQ,EACR,eAAe,EACf,UAAgB,EAChB,WAAgB,EAChB,QAAa,GACd,EAAE,uBAAuB,GAAG,gBAAgB,CA4K5C"}
@@ -1,23 +1,15 @@
1
1
  import { searchIndexName } from "./services/search";
2
2
  import { SearchStatus, } from "./types/searchController";
3
3
  import { createControllerAnalytics } from "./utils/searchControllerAnalytics";
4
- import { buildSearchRequest, normalizeQuery, readSearchResults, } from "./utils/searchRequests";
5
- const INITIAL_STATE = {
6
- status: SearchStatus.IDLE,
7
- query: "",
8
- page: 0,
9
- response: undefined,
10
- sections: undefined,
11
- error: undefined,
12
- };
13
- export function createSearchController({ search, language, currency, analytics, navigate, debounceMs = 250, hitsPerPage = 12, sections = [], }) {
4
+ import { buildSearchRequest, INITIAL_SEARCH_STATE, normalizeQuery, readSearchResults, } from "./utils/searchRequests";
5
+ export function createSearchController({ search, language, country, analytics, navigate, buildProductUrl, debounceMs = 250, hitsPerPage = 12, sections = [], }) {
14
6
  const requestConfig = {
15
- indexName: searchIndexName("products", language, currency),
7
+ indexName: searchIndexName("products", language, country),
16
8
  language,
17
- currency,
9
+ country,
18
10
  hitsPerPage,
19
11
  };
20
- let state = INITIAL_STATE;
12
+ let state = INITIAL_SEARCH_STATE;
21
13
  const listeners = new Set();
22
14
  const controllerAnalytics = createControllerAnalytics(analytics);
23
15
  let debounceTimer;
@@ -31,6 +23,7 @@ export function createSearchController({ search, language, currency, analytics,
31
23
  listener(state);
32
24
  }
33
25
  };
26
+ const productUrl = (hit) => hit.url !== undefined && hit.url !== "" ? hit.url : buildProductUrl?.(hit);
34
27
  const cancelPendingSearch = () => {
35
28
  clearTimeout(debounceTimer);
36
29
  pendingQuery = undefined;
@@ -85,7 +78,7 @@ export function createSearchController({ search, language, currency, analytics,
85
78
  cancelPendingSearch();
86
79
  const query = normalizeQuery(rawQuery);
87
80
  if (query === undefined) {
88
- setState(INITIAL_STATE);
81
+ setState(INITIAL_SEARCH_STATE);
89
82
  return;
90
83
  }
91
84
  pendingQuery = query;
@@ -98,7 +91,7 @@ export function createSearchController({ search, language, currency, analytics,
98
91
  cancelPendingSearch();
99
92
  const query = normalizeQuery(rawQuery);
100
93
  if (query === undefined) {
101
- setState(INITIAL_STATE);
94
+ setState(INITIAL_SEARCH_STATE);
102
95
  return;
103
96
  }
104
97
  void run(query, 0);
@@ -135,7 +128,7 @@ export function createSearchController({ search, language, currency, analytics,
135
128
  return false;
136
129
  }
137
130
  controllerAnalytics.select(response, index);
138
- const url = response.hits[index].url;
131
+ const url = productUrl(response.hits[index]);
139
132
  const runAdapter = options?.navigate ?? true;
140
133
  if (runAdapter && navigate !== undefined && url !== undefined) {
141
134
  navigate(url, response.hits[index]);
@@ -143,6 +136,7 @@ export function createSearchController({ search, language, currency, analytics,
143
136
  }
144
137
  return false;
145
138
  },
139
+ productUrl,
146
140
  subscribe(listener) {
147
141
  listeners.add(listener);
148
142
  return () => {
@@ -1,6 +1,6 @@
1
1
  import { SearchIndex, SearchOptions, SearchRequest, SearchResponse } from "../types/search";
2
- /** Build an index name from an ISO 639-1 language and ISO 4217 currency. */
3
- export declare const searchIndexName: (index: SearchIndex, language: string, currency: string) => string;
2
+ /** Build an index name from an ISO 639-1 language and the shopper's ISO 3166-1 alpha-2 country. */
3
+ export declare const searchIndexName: (index: SearchIndex, language: string, country: string) => string;
4
4
  /** Send one request. The caller controls cancellation through `options.signal`. */
5
5
  export declare const searchLexical: (apiKey: string, request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
6
6
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/services/search.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,iBAAiB,CAAC;AAKzB,4EAA4E;AAC5E,eAAO,MAAM,eAAe,GAC1B,OAAO,WAAW,EAClB,UAAU,MAAM,EAChB,UAAU,MAAM,KACf,MASF,CAAC;AAoBF,mFAAmF;AACnF,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,SAAS,aAAa,EACtB,UAAU,aAAa,KACtB,OAAO,CAAC,cAAc,CAgBxB,CAAC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/services/search.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,iBAAiB,CAAC;AAKzB,mGAAmG;AACnG,eAAO,MAAM,eAAe,GAC1B,OAAO,WAAW,EAClB,UAAU,MAAM,EAChB,SAAS,MAAM,KACd,MASF,CAAC;AAoBF,mFAAmF;AACnF,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,SAAS,aAAa,EACtB,UAAU,aAAa,KACtB,OAAO,CAAC,cAAc,CAgBxB,CAAC"}
@@ -2,15 +2,15 @@ import { config } from "../config";
2
2
  import { DialogSearchError } from "../DialogSearchError";
3
3
  const SEARCH_PATH = "/public/search/lexical";
4
4
  const API_KEY_HEADER = "x-dialog-api-key";
5
- /** Build an index name from an ISO 639-1 language and ISO 4217 currency. */
6
- export const searchIndexName = (index, language, currency) => {
5
+ /** Build an index name from an ISO 639-1 language and the shopper's ISO 3166-1 alpha-2 country. */
6
+ export const searchIndexName = (index, language, country) => {
7
7
  if (!/^[a-z]{2}$/.test(language)) {
8
8
  throw new Error("Search language must be a lowercase ISO 639-1 code.");
9
9
  }
10
- if (!/^[A-Za-z]{3}$/.test(currency)) {
11
- throw new Error("Search currency must be an ISO 4217 code.");
10
+ if (!/^[A-Za-z]{2}$/.test(country)) {
11
+ throw new Error("Search country must be an ISO 3166-1 alpha-2 code.");
12
12
  }
13
- return `${index}_${language}_${currency.toLowerCase()}`;
13
+ return `${index}_${language}_${country.toLowerCase()}`;
14
14
  };
15
15
  const toSearchError = async (response) => {
16
16
  let body;
@@ -1,10 +1,10 @@
1
- /** Supported indices before adding language and currency. */
1
+ /** Supported indices before adding language and country. */
2
2
  export declare const SEARCH_INDICES: readonly ["products", "collections", "articles", "pages"];
3
3
  export type SearchIndex = (typeof SEARCH_INDICES)[number];
4
4
  export interface SearchQuery {
5
5
  /**
6
- * `<index>_<lang>_<currency>`, e.g. `products_fr_eur`.
7
- * Use the same language and lowercase ISO 4217 currency for every entry.
6
+ * `<index>_<lang>_<country>`, e.g. `products_fr_be`.
7
+ * Use the same language and lowercase ISO 3166-1 alpha-2 country for every entry.
8
8
  * Unsupported index names return 404.
9
9
  */
10
10
  indexName: string;
@@ -22,28 +22,82 @@ export interface SearchOptions {
22
22
  /** Passed to fetch; cancellation rejects with `AbortError`. */
23
23
  signal?: AbortSignal;
24
24
  }
25
- export interface SearchPrice {
26
- /** Decimal string, e.g. "24.90". */
27
- amount: string;
28
- /** Currency code, if provided by the index. */
29
- currencyCode?: string;
30
- }
31
- export interface SearchPriceRange {
32
- min: SearchPrice;
33
- max: SearchPrice;
25
+ /** Algolia's collection record, plus `url`. */
26
+ export interface SearchCollectionHit {
27
+ /** Collection id, the last segment of the Shopify GID. */
28
+ objectID: string;
29
+ title?: string;
30
+ handle?: string;
31
+ url?: string;
32
+ body_html?: string;
33
+ body_html_safe?: string;
34
+ image?: string;
35
+ products_count?: number;
36
+ template_suffix?: string;
37
+ updated_at?: string;
34
38
  }
35
- /** Indexed record. Display fields are optional; only products have `priceRange`. */
39
+ /** An article or page hit. */
36
40
  export interface SearchHit {
37
41
  objectID: string;
38
42
  title?: string;
39
43
  url?: string;
40
44
  handle?: string;
41
45
  imageUrl?: string;
42
- priceRange?: SearchPriceRange;
43
46
  }
44
- export interface SearchResult {
47
+ /**
48
+ * A product hit: Algolia's Shopify record, plus `url` and `currency`.
49
+ * Prices are numbers in the base currency of the shopper's market (`currency`);
50
+ * a price the market lacks is absent.
51
+ */
52
+ export interface SearchProductHit {
53
+ /** Variant id, the last segment of the Shopify GID. */
54
+ objectID: string;
55
+ /** Product id, a number when numeric. */
56
+ id: number | string;
57
+ title?: string;
58
+ handle?: string;
59
+ url?: string;
60
+ variant_title?: string;
61
+ position?: number;
62
+ image?: string;
63
+ product_image?: string;
64
+ body_html_safe?: string;
65
+ vendor?: string;
66
+ product_type?: string;
67
+ tags: string[];
68
+ sku?: string;
69
+ barcode?: string;
70
+ option1?: string;
71
+ option2?: string;
72
+ option3?: string;
73
+ /** Lowercase option name to selected value. */
74
+ options: Record<string, string>;
75
+ option_names: string[];
76
+ price?: number;
77
+ compare_at_price?: number;
78
+ variants_min_price?: number;
79
+ variants_max_price?: number;
80
+ variants_compare_at_price_min?: number;
81
+ variants_compare_at_price_max?: number;
82
+ /** ISO 4217 base currency of the shopper's market. */
83
+ currency?: string;
84
+ variants_count?: number;
85
+ inventory_quantity?: number;
86
+ inventory_available: boolean;
87
+ created_at?: string;
88
+ updated_at?: string;
89
+ published_at?: string;
90
+ }
91
+ /** The hit each index answers. */
92
+ export interface SearchHitByIndex {
93
+ products: SearchProductHit;
94
+ collections: SearchCollectionHit;
95
+ articles: SearchHit;
96
+ pages: SearchHit;
97
+ }
98
+ export interface SearchResult<THit = SearchHitByIndex[SearchIndex]> {
45
99
  index: string;
46
- hits: SearchHit[];
100
+ hits: THit[];
47
101
  nbHits: number;
48
102
  page: number;
49
103
  nbPages: number;
@@ -52,6 +106,10 @@ export interface SearchResult {
52
106
  query: string;
53
107
  queryID: string;
54
108
  }
109
+ /** Results of the sections searched next to products, keyed by index. */
110
+ export type SearchSections = {
111
+ [Index in Exclude<SearchIndex, "products">]?: SearchResult<SearchHitByIndex[Index]>;
112
+ };
55
113
  /** Results in request order. */
56
114
  export interface SearchResponse {
57
115
  results: SearchResult[];
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,eAAO,MAAM,cAAc,2DAKjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,WAAW,CAAC;IACjB,GAAG,EAAE,WAAW,CAAC;CAClB;AAED,oFAAoF;AACpF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- /** Supported indices before adding language and currency. */
1
+ /** Supported indices before adding language and country. */
2
2
  export const SEARCH_INDICES = [
3
3
  "products",
4
4
  "collections",
@@ -4,7 +4,7 @@ export declare const SEARCH_TYPES: readonly ["lexical"];
4
4
  export type SearchType = (typeof SEARCH_TYPES)[number];
5
5
  export interface SearchAnalyticsEnvelope {
6
6
  query_id: string;
7
- /** Index returned by the API, e.g. `products_fr_eur`. */
7
+ /** Index returned by the API, e.g. `products_fr_be`. */
8
8
  index: string;
9
9
  /** UI surface displaying the results. */
10
10
  surface: SearchSurface;
@@ -1 +1 @@
1
- {"version":3,"file":"searchAnalytics.d.ts","sourceRoot":"","sources":["../../src/types/searchAnalytics.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,uEAKlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY,sBAAuB,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,EAAE,aAAa,CAAC;IACvB,6CAA6C;IAC7C,WAAW,EAAE,UAAU,CAAC;IACxB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,qCAAqC;AACrC,MAAM,WAAW,wBAAyB,SAAQ,uBAAuB;IACvE,KAAK,EAAE,CAAC,gBAAgB,CAAC,CAAC;CAC3B;AAGD,MAAM,WAAW,6BAA8B,SAAQ,uBAAuB;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,8BACf,SAAQ,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"searchAnalytics.d.ts","sourceRoot":"","sources":["../../src/types/searchAnalytics.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe,uEAKlB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY,sBAAuB,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,OAAO,EAAE,aAAa,CAAC;IACvB,6CAA6C;IAC7C,WAAW,EAAE,UAAU,CAAC;IACxB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAED,qCAAqC;AACrC,MAAM,WAAW,wBAAyB,SAAQ,uBAAuB;IACvE,KAAK,EAAE,CAAC,gBAAgB,CAAC,CAAC;CAC3B;AAGD,MAAM,WAAW,6BAA8B,SAAQ,uBAAuB;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,8BACf,SAAQ,wBAAwB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -1,4 +1,4 @@
1
- import { SearchHit, SearchIndex, SearchOptions, SearchRequest, SearchResponse, SearchResult } from "./search";
1
+ import { SearchIndex, SearchOptions, SearchProductHit, SearchRequest, SearchResponse, SearchResult, SearchSections } from "./search";
2
2
  import { SearchSurface, SelectSearchResultParams, ViewSearchResultsParams } from "./searchAnalytics";
3
3
  export declare const SearchStatus: {
4
4
  readonly IDLE: "idle";
@@ -20,8 +20,8 @@ export interface SearchControllerState {
20
20
  /** Zero-based page of the last request. */
21
21
  page: number;
22
22
  /** Last products result; retained while loading and cleared on error or reset. */
23
- response?: SearchResult;
24
- sections?: Partial<Record<SearchIndex, SearchResult>>;
23
+ response?: SearchResult<SearchProductHit>;
24
+ sections?: SearchSections;
25
25
  error?: unknown;
26
26
  }
27
27
  export type SearchFunction = (request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
@@ -36,11 +36,13 @@ export interface SearchControllerOptions {
36
36
  search: SearchFunction;
37
37
  /** Lowercase ISO 639-1 language code, e.g. `fr`. */
38
38
  language: string;
39
- /** ISO 4217 currency, independent of language. */
40
- currency: string;
39
+ /** Shopper's ISO 3166-1 alpha-2 country, e.g. `BE`; prices follow its market. */
40
+ country: string;
41
41
  analytics: SearchControllerAnalytics;
42
42
  /** Navigate after recording selection. Omit to use native link navigation. */
43
- navigate?: (url: string, hit: SearchHit) => void;
43
+ navigate?: (url: string, hit: SearchProductHit) => void;
44
+ /** Build a product's link when its hit has no `url`, e.g. `/products/${hit.handle}`. */
45
+ buildProductUrl?: (hit: SearchProductHit) => string | undefined;
44
46
  debounceMs?: number;
45
47
  hitsPerPage?: number;
46
48
  sections?: readonly SearchSection[] | (() => readonly SearchSection[]);
@@ -65,6 +67,8 @@ export interface SearchController {
65
67
  selectResult(index: number, options?: {
66
68
  navigate?: boolean;
67
69
  }): boolean;
70
+ /** The hit's `url`, else `buildProductUrl`'s result; undefined when neither gives one. */
71
+ productUrl(hit: SearchProductHit): string | undefined;
68
72
  /** Subscribe to state changes; returns an unsubscribe function. */
69
73
  subscribe(listener: (state: SearchControllerState) => void): () => void;
70
74
  getState(): SearchControllerState;
@@ -1 +1 @@
1
- {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACb,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,aAAa,EACb,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,YAAY;;;;IAIvB,yCAAyC;;;CAGjC,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAC3B,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,qFAAqF;AACrF,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,aAAa,CAAC;IACvB,sBAAsB,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAClE,uBAAuB,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;CACrE;AAED,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,MAAM,EAAE,cAAc,CAAC;IACvB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,yBAAyB,CAAC;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,CAAC,MAAM,SAAS,aAAa,EAAE,CAAC,CAAC;CACxE;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0BAA0B;IAC1B,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gFAAgF;IAChF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,wDAAwD;IACxD,KAAK,IAAI,IAAI,CAAC;IACd,6EAA6E;IAC7E,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrD;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;IACvE,mEAAmE;IACnE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxE,QAAQ,IAAI,qBAAqB,CAAC;IAClC,kEAAkE;IAClE,OAAO,IAAI,IAAI,CAAC;CACjB"}
1
+ {"version":3,"file":"searchController.d.ts","sourceRoot":"","sources":["../../src/types/searchController.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { SearchResult } from "../types/search";
1
+ import { SearchProductHit, SearchResult } from "../types/search";
2
2
  import { SearchControllerAnalytics } from "../types/searchController";
3
3
  /** Internal binding for search impressions and selections. */
4
4
  export interface ControllerAnalyticsBinding {
5
- onResponse(result: SearchResult): void;
6
- observeResult(element: Element, result: SearchResult, index: number): void;
7
- select(result: SearchResult, index: number): void;
5
+ onResponse(result: SearchResult<SearchProductHit>): void;
6
+ observeResult(element: Element, result: SearchResult<SearchProductHit>, index: number): void;
7
+ select(result: SearchResult<SearchProductHit>, index: number): void;
8
8
  dispose(): void;
9
9
  }
10
10
  export declare function createControllerAnalytics(analytics: SearchControllerAnalytics): ControllerAnalyticsBinding;
@@ -1 +1 @@
1
- {"version":3,"file":"searchControllerAnalytics.d.ts","sourceRoot":"","sources":["../../src/utils/searchControllerAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAK/C,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAMtE,8DAA8D;AAC9D,MAAM,WAAW,0BAA0B;IACzC,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3E,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAClD,OAAO,IAAI,IAAI,CAAC;CACjB;AAQD,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,yBAAyB,GACnC,0BAA0B,CAgD5B"}
1
+ {"version":3,"file":"searchControllerAnalytics.d.ts","sourceRoot":"","sources":["../../src/utils/searchControllerAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKjE,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAMtE,8DAA8D;AAC9D,MAAM,WAAW,0BAA0B;IACzC,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IACzD,aAAa,CACX,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,YAAY,CAAC,gBAAgB,CAAC,EACtC,KAAK,EAAE,MAAM,GACZ,IAAI,CAAC;IACR,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACpE,OAAO,IAAI,IAAI,CAAC;CACjB;AAYD,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,yBAAyB,GACnC,0BAA0B,CAgD5B"}
@@ -1,6 +1,7 @@
1
1
  import { createSearchImpressionTracker, } from "./searchImpressions";
2
2
  const resultItem = (result, index) => ({
3
- product_id: result.hits[index].objectID,
3
+ // `objectID` is the variant id; analytics count products.
4
+ product_id: String(result.hits[index].id),
4
5
  // One-based position across all pages.
5
6
  position: result.page * result.hitsPerPage + index + 1,
6
7
  });
@@ -0,0 +1,9 @@
1
+ import type { SearchProductHit } from "../types/search";
2
+ /** The lowest variant price only, as on the Shopify storefront search. */
3
+ export declare const formatSearchPrice: ({ variants_min_price: amount, currency }: SearchProductHit, locale?: string) => string;
4
+ /**
5
+ * The lowest compare-at price, struck next to the price, only when it is
6
+ * higher than a shown price; it may belong to another variant.
7
+ */
8
+ export declare const formatSearchCompareAtPrice: (hit: SearchProductHit, locale?: string) => string;
9
+ //# sourceMappingURL=searchPrice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searchPrice.d.ts","sourceRoot":"","sources":["../../src/utils/searchPrice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAsBxD,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,GAC5B,0CAA0C,gBAAgB,EAC1D,SAAS,MAAM,KACd,MAAgD,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GACrC,KAAK,gBAAgB,EACrB,SAAS,MAAM,KACd,MAeF,CAAC"}
@@ -0,0 +1,30 @@
1
+ // Hide a missing or invalid amount; show a bare amount when currency is absent.
2
+ const formatAmount = (amount, currency, locale) => {
3
+ if (amount === undefined || !Number.isFinite(amount)) {
4
+ return "";
5
+ }
6
+ try {
7
+ return currency === undefined
8
+ ? new Intl.NumberFormat(locale).format(amount)
9
+ : new Intl.NumberFormat(locale, { style: "currency", currency }).format(amount);
10
+ }
11
+ catch {
12
+ return "";
13
+ }
14
+ };
15
+ /** The lowest variant price only, as on the Shopify storefront search. */
16
+ export const formatSearchPrice = ({ variants_min_price: amount, currency }, locale) => formatAmount(amount, currency, locale);
17
+ /**
18
+ * The lowest compare-at price, struck next to the price, only when it is
19
+ * higher than a shown price; it may belong to another variant.
20
+ */
21
+ export const formatSearchCompareAtPrice = (hit, locale) => {
22
+ const { variants_min_price: price, variants_compare_at_price_min: compareAt, } = hit;
23
+ if (price === undefined ||
24
+ compareAt === undefined ||
25
+ !(compareAt > price) ||
26
+ formatSearchPrice(hit, locale) === "") {
27
+ return "";
28
+ }
29
+ return formatAmount(compareAt, hit.currency, locale);
30
+ };
@@ -1,17 +1,18 @@
1
- import { SearchIndex, SearchRequest, SearchResponse, SearchResult } from "../types/search";
2
- import { SearchSection } from "../types/searchController";
1
+ import { SearchProductHit, SearchRequest, SearchResponse, SearchResult, SearchSections } from "../types/search";
2
+ import { SearchControllerState, SearchSection } from "../types/searchController";
3
+ export declare const INITIAL_SEARCH_STATE: SearchControllerState;
3
4
  export declare const normalizeQuery: (rawQuery: string) => string | undefined;
4
5
  interface SearchRequestConfig {
5
6
  indexName: string;
6
7
  language: string;
7
- currency: string;
8
+ country: string;
8
9
  hitsPerPage: number;
9
10
  }
10
11
  interface SearchResults {
11
- response: SearchResult;
12
- sections: Partial<Record<SearchIndex, SearchResult>> | undefined;
12
+ response: SearchResult<SearchProductHit>;
13
+ sections: SearchSections | undefined;
13
14
  }
14
- export declare const buildSearchRequest: (query: string, page: number, sections: readonly SearchSection[], { indexName, language, currency, hitsPerPage }: SearchRequestConfig) => SearchRequest;
15
- export declare const readSearchResults: (result: SearchResponse, requested: readonly SearchSection[], { indexName, language, currency }: SearchRequestConfig) => SearchResults;
15
+ export declare const buildSearchRequest: (query: string, page: number, sections: readonly SearchSection[], { indexName, language, country, hitsPerPage }: SearchRequestConfig) => SearchRequest;
16
+ export declare const readSearchResults: (result: SearchResponse, requested: readonly SearchSection[], { indexName, language, country }: SearchRequestConfig) => SearchResults;
16
17
  export {};
17
18
  //# sourceMappingURL=searchRequests.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"searchRequests.d.ts","sourceRoot":"","sources":["../../src/utils/searchRequests.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,aAAa,EACb,cAAc,EACd,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK1D,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,GAAG,SAI1D,CAAC;AAEF,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,GAAG,SAAS,CAAC;CAClE;AAED,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,MAAM,MAAM,EACZ,UAAU,SAAS,aAAa,EAAE,EAClC,gDAAgD,mBAAmB,KAClE,aAUD,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,cAAc,EACtB,WAAW,SAAS,aAAa,EAAE,EACnC,mCAAmC,mBAAmB,KACrD,aAwBF,CAAC"}
1
+ {"version":3,"file":"searchRequests.d.ts","sourceRoot":"","sources":["../../src/utils/searchRequests.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,cAAc,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,qBAAqB,EACrB,aAAa,EAEd,MAAM,2BAA2B,CAAC;AAEnC,eAAO,MAAM,oBAAoB,EAAE,qBAOlC,CAAC;AAKF,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,MAAM,GAAG,SAI1D,CAAC;AAEF,UAAU,mBAAmB;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC;IACzC,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;CACtC;AAED,eAAO,MAAM,kBAAkB,GAC7B,OAAO,MAAM,EACb,MAAM,MAAM,EACZ,UAAU,SAAS,aAAa,EAAE,EAClC,+CAA+C,mBAAmB,KACjE,aAUD,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,cAAc,EACtB,WAAW,SAAS,aAAa,EAAE,EACnC,kCAAkC,mBAAmB,KACpD,aAyBF,CAAC"}
@@ -1,36 +1,46 @@
1
1
  import { searchIndexName } from "../services/search";
2
+ import { SearchStatus, } from "../types/searchController";
3
+ export const INITIAL_SEARCH_STATE = {
4
+ status: SearchStatus.IDLE,
5
+ query: "",
6
+ page: 0,
7
+ response: undefined,
8
+ sections: undefined,
9
+ error: undefined,
10
+ };
2
11
  // Match the API minimum query length.
3
12
  const MIN_QUERY_CODE_POINTS = 2;
4
13
  export const normalizeQuery = (rawQuery) => {
5
14
  const query = rawQuery.trim();
6
15
  return [...query].length >= MIN_QUERY_CODE_POINTS ? query : undefined;
7
16
  };
8
- export const buildSearchRequest = (query, page, sections, { indexName, language, currency, hitsPerPage }) => ({
17
+ export const buildSearchRequest = (query, page, sections, { indexName, language, country, hitsPerPage }) => ({
9
18
  requests: [
10
19
  { indexName, query, page, hitsPerPage },
11
20
  ...sections.map((section) => ({
12
- indexName: searchIndexName(section.index, language, currency),
21
+ indexName: searchIndexName(section.index, language, country),
13
22
  query,
14
23
  page: 0,
15
24
  hitsPerPage: section.hitsPerPage ?? hitsPerPage,
16
25
  })),
17
26
  ],
18
27
  });
19
- export const readSearchResults = (result, requested, { indexName, language, currency }) => {
28
+ export const readSearchResults = (result, requested, { indexName, language, country }) => {
20
29
  const response = result.results.find((entry) => entry.index === indexName);
21
30
  if (response === undefined) {
22
31
  throw new Error(`Dialog search returned no ${indexName} entry`);
23
32
  }
24
33
  const sections = {};
25
34
  for (const section of requested) {
26
- const requestedIndexName = searchIndexName(section.index, language, currency);
35
+ const requestedIndexName = searchIndexName(section.index, language, country);
27
36
  const entry = result.results.find((candidate) => candidate.index === requestedIndexName);
28
37
  if (entry !== undefined) {
29
38
  sections[section.index] = entry;
30
39
  }
31
40
  }
32
41
  return {
33
- response,
42
+ // Each index answers its own record type.
43
+ response: response,
34
44
  sections: requested.length === 0 ? undefined : sections,
35
45
  };
36
46
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdialog/dialog-sdk",
3
- "version": "2.15.1",
3
+ "version": "2.17.0",
4
4
  "private": false,
5
5
  "description": "Dialog SDK",
6
6
  "main": "dist/index.js",