@askdialog/dialog-sdk 2.11.0 → 2.13.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.
- package/README.md +31 -20
- package/dist/Dialog.d.ts +16 -22
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Dialog.js +44 -56
- package/dist/__tests__/dialogChangeCartQuantity.spec.d.ts +2 -0
- package/dist/__tests__/dialogChangeCartQuantity.spec.d.ts.map +1 -0
- package/dist/__tests__/dialogChangeCartQuantity.spec.js +46 -0
- package/dist/__tests__/dialogSearchAnalytics.spec.js +2 -4
- package/dist/__tests__/publicTypes.spec.js +3 -0
- package/dist/__tests__/searchController.spec.js +46 -20
- package/dist/__tests__/searchControllerAttribution.spec.js +8 -6
- package/dist/__tests__/searchImpressions.spec.js +2 -3
- package/dist/__tests__/searchService.spec.js +13 -11
- package/dist/searchController.d.ts +1 -1
- package/dist/searchController.d.ts.map +1 -1
- package/dist/searchController.js +41 -64
- package/dist/services/search.d.ts +3 -5
- package/dist/services/search.d.ts.map +1 -1
- package/dist/services/search.js +11 -6
- package/dist/types/constructor.d.ts +9 -3
- package/dist/types/constructor.d.ts.map +1 -1
- package/dist/types/events.d.ts +5 -0
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/search.d.ts +14 -14
- package/dist/types/search.d.ts.map +1 -1
- package/dist/types/search.js +1 -1
- package/dist/types/searchAnalytics.d.ts +12 -6
- package/dist/types/searchAnalytics.d.ts.map +1 -1
- package/dist/types/searchAnalytics.js +1 -4
- package/dist/types/searchController.d.ts +22 -51
- package/dist/types/searchController.d.ts.map +1 -1
- package/dist/types/searchController.js +1 -1
- package/dist/utils/localization.d.ts +0 -1
- package/dist/utils/localization.d.ts.map +1 -1
- package/dist/utils/localization.js +0 -11
- package/dist/utils/searchControllerAnalytics.d.ts +1 -4
- package/dist/utils/searchControllerAnalytics.d.ts.map +1 -1
- package/dist/utils/searchControllerAnalytics.js +5 -8
- package/dist/utils/searchImpressions.d.ts +4 -4
- package/dist/utils/searchImpressions.d.ts.map +1 -1
- package/dist/utils/searchImpressions.js +4 -11
- package/dist/utils/searchRequests.d.ts +17 -0
- package/dist/utils/searchRequests.d.ts.map +1 -0
- package/dist/utils/searchRequests.js +36 -0
- package/package.json +1 -1
- package/dist/utils/searchSections.d.ts +0 -5
- package/dist/utils/searchSections.d.ts.map +0 -1
- package/dist/utils/searchSections.js +0 -21
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint max-lines: ["error",
|
|
1
|
+
/* eslint max-lines: ["error", 490] */
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { createSearchController } from "../searchController";
|
|
4
4
|
import { SearchStatus } from "../types/searchController";
|
|
@@ -15,7 +15,7 @@ const DEBOUNCE_MS = 250;
|
|
|
15
15
|
const response = (overrides = {}) => ({
|
|
16
16
|
results: [
|
|
17
17
|
{
|
|
18
|
-
index: "
|
|
18
|
+
index: "products_fr_eur",
|
|
19
19
|
hits: [{ objectID: "p1" }, { objectID: "p2" }],
|
|
20
20
|
nbHits: 2,
|
|
21
21
|
page: 0,
|
|
@@ -37,7 +37,7 @@ const deferred = () => {
|
|
|
37
37
|
});
|
|
38
38
|
return { promise, resolve, reject };
|
|
39
39
|
};
|
|
40
|
-
//
|
|
40
|
+
// Flush the search promise continuation.
|
|
41
41
|
const settle = async () => {
|
|
42
42
|
await Promise.resolve();
|
|
43
43
|
await Promise.resolve();
|
|
@@ -49,12 +49,13 @@ const states = [];
|
|
|
49
49
|
const createController = () => {
|
|
50
50
|
const controller = createSearchController({
|
|
51
51
|
search,
|
|
52
|
+
language: "fr",
|
|
53
|
+
currency: "EUR",
|
|
52
54
|
analytics: {
|
|
53
55
|
surface: "search_page",
|
|
54
56
|
trackViewSearchResults,
|
|
55
57
|
trackSelectSearchResult,
|
|
56
58
|
},
|
|
57
|
-
locale: "fr",
|
|
58
59
|
});
|
|
59
60
|
controller.subscribe((state) => states.push(state));
|
|
60
61
|
return controller;
|
|
@@ -93,22 +94,28 @@ describe("createSearchController", () => {
|
|
|
93
94
|
});
|
|
94
95
|
expect(controller.getState().status).toBe(SearchStatus.SUCCESS);
|
|
95
96
|
});
|
|
96
|
-
it("sends one products entry
|
|
97
|
+
it("sends one products entry with independent language and currency", async () => {
|
|
97
98
|
const controller = createSearchController({
|
|
98
99
|
search,
|
|
100
|
+
language: "fr",
|
|
101
|
+
currency: "USD",
|
|
99
102
|
analytics: {
|
|
100
103
|
surface: "search_page",
|
|
101
104
|
trackViewSearchResults,
|
|
102
105
|
trackSelectSearchResult,
|
|
103
106
|
},
|
|
104
|
-
locale: "fr-FR",
|
|
105
107
|
});
|
|
106
|
-
search.mockResolvedValue(response({ query: "shoes" }));
|
|
108
|
+
search.mockResolvedValue(response({ query: "shoes", index: "products_fr_usd" }));
|
|
107
109
|
controller.submit("shoes");
|
|
108
110
|
await settle();
|
|
109
111
|
expect(search.mock.calls[0][0]).toEqual({
|
|
110
112
|
requests: [
|
|
111
|
-
{
|
|
113
|
+
{
|
|
114
|
+
indexName: "products_fr_usd",
|
|
115
|
+
query: "shoes",
|
|
116
|
+
page: 0,
|
|
117
|
+
hitsPerPage: 12,
|
|
118
|
+
},
|
|
112
119
|
],
|
|
113
120
|
});
|
|
114
121
|
controller.dispose();
|
|
@@ -125,7 +132,7 @@ describe("createSearchController", () => {
|
|
|
125
132
|
});
|
|
126
133
|
expect(controller.getState().status).toBe(SearchStatus.SUCCESS);
|
|
127
134
|
});
|
|
128
|
-
it("
|
|
135
|
+
it("invalidates the previous request as soon as new input is queued", async () => {
|
|
129
136
|
const controller = createController();
|
|
130
137
|
const first = deferred();
|
|
131
138
|
const second = deferred();
|
|
@@ -134,8 +141,14 @@ describe("createSearchController", () => {
|
|
|
134
141
|
.mockReturnValueOnce(second.promise);
|
|
135
142
|
controller.submit("shoes");
|
|
136
143
|
const firstSignal = search.mock.calls[0][1]?.signal;
|
|
137
|
-
controller.
|
|
144
|
+
controller.setQuery("boots");
|
|
145
|
+
first.resolve(response({ query: "shoes" }));
|
|
146
|
+
await settle();
|
|
138
147
|
expect(firstSignal?.aborted).toBe(true);
|
|
148
|
+
expect(search).toHaveBeenCalledTimes(1);
|
|
149
|
+
expect(controller.getState().response).toBeUndefined();
|
|
150
|
+
expect(controller.getState().status).toBe(SearchStatus.LOADING);
|
|
151
|
+
vi.advanceTimersByTime(DEBOUNCE_MS);
|
|
139
152
|
second.resolve(response({ query: "boots", queryID: "qid-2" }));
|
|
140
153
|
await settle();
|
|
141
154
|
expect(controller.getState().response?.query).toBe("boots");
|
|
@@ -151,7 +164,7 @@ describe("createSearchController", () => {
|
|
|
151
164
|
controller.submit("boots");
|
|
152
165
|
fresh.resolve(response({ query: "boots", queryID: "qid-2" }));
|
|
153
166
|
await settle();
|
|
154
|
-
//
|
|
167
|
+
// Simulate a late response from a transport that ignores cancellation.
|
|
155
168
|
stale.resolve(response({ query: "shoes", queryID: "qid-1" }));
|
|
156
169
|
await settle();
|
|
157
170
|
expect(controller.getState().status).toBe(SearchStatus.SUCCESS);
|
|
@@ -291,42 +304,54 @@ describe("createSearchController", () => {
|
|
|
291
304
|
it("requests the sections on their first page with the products and exposes their entries", async () => {
|
|
292
305
|
const controller = createSearchController({
|
|
293
306
|
search,
|
|
307
|
+
language: "fr",
|
|
308
|
+
currency: "USD",
|
|
294
309
|
analytics: {
|
|
295
310
|
surface: "search_page",
|
|
296
311
|
trackViewSearchResults,
|
|
297
312
|
trackSelectSearchResult,
|
|
298
313
|
},
|
|
299
|
-
locale: "fr",
|
|
300
314
|
sections: [{ index: "collections", hitsPerPage: 5 }],
|
|
301
315
|
});
|
|
302
316
|
const collections = {
|
|
303
317
|
...response().results[0],
|
|
304
|
-
index: "
|
|
318
|
+
index: "collections_fr_usd",
|
|
305
319
|
hits: [{ objectID: "c1" }],
|
|
306
320
|
nbHits: 1,
|
|
307
321
|
};
|
|
308
322
|
search.mockResolvedValueOnce({
|
|
309
|
-
results: [...response().results, collections],
|
|
323
|
+
results: [...response({ index: "products_fr_usd" }).results, collections],
|
|
310
324
|
});
|
|
311
325
|
controller.submit("shoes");
|
|
312
326
|
await settle();
|
|
313
327
|
expect(search.mock.calls[0][0].requests).toEqual([
|
|
314
|
-
{
|
|
315
|
-
|
|
328
|
+
{
|
|
329
|
+
indexName: "products_fr_usd",
|
|
330
|
+
query: "shoes",
|
|
331
|
+
page: 0,
|
|
332
|
+
hitsPerPage: 12,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
indexName: "collections_fr_usd",
|
|
336
|
+
query: "shoes",
|
|
337
|
+
page: 0,
|
|
338
|
+
hitsPerPage: 5,
|
|
339
|
+
},
|
|
316
340
|
]);
|
|
317
|
-
expect(controller.getState().response?.index).toBe("
|
|
341
|
+
expect(controller.getState().response?.index).toBe("products_fr_usd");
|
|
318
342
|
expect(controller.getState().sections).toEqual({ collections });
|
|
319
343
|
});
|
|
320
344
|
it("resolves a sections function per request and leaves a missing entry out", async () => {
|
|
321
345
|
let enabled = false;
|
|
322
346
|
const controller = createSearchController({
|
|
323
347
|
search,
|
|
348
|
+
language: "fr",
|
|
349
|
+
currency: "EUR",
|
|
324
350
|
analytics: {
|
|
325
351
|
surface: "search_page",
|
|
326
352
|
trackViewSearchResults,
|
|
327
353
|
trackSelectSearchResult,
|
|
328
354
|
},
|
|
329
|
-
locale: "fr",
|
|
330
355
|
sections: () => (enabled ? [{ index: "collections" }] : []),
|
|
331
356
|
});
|
|
332
357
|
search.mockResolvedValue(response());
|
|
@@ -338,7 +363,7 @@ describe("createSearchController", () => {
|
|
|
338
363
|
expect(search.mock.calls[0][0].requests).toHaveLength(1);
|
|
339
364
|
expect(search.mock.calls[1][0].requests).toHaveLength(2);
|
|
340
365
|
expect(search.mock.calls[1][0].requests[1]).toMatchObject({
|
|
341
|
-
indexName: "
|
|
366
|
+
indexName: "collections_fr_eur",
|
|
342
367
|
hitsPerPage: 12,
|
|
343
368
|
});
|
|
344
369
|
expect(controller.getState().sections).toEqual({});
|
|
@@ -347,12 +372,13 @@ describe("createSearchController", () => {
|
|
|
347
372
|
const failure = new Error("entitlement lookup failed");
|
|
348
373
|
const controller = createSearchController({
|
|
349
374
|
search,
|
|
375
|
+
language: "fr",
|
|
376
|
+
currency: "EUR",
|
|
350
377
|
analytics: {
|
|
351
378
|
surface: "search_page",
|
|
352
379
|
trackViewSearchResults,
|
|
353
380
|
trackSelectSearchResult,
|
|
354
381
|
},
|
|
355
|
-
locale: "fr",
|
|
356
382
|
sections: () => {
|
|
357
383
|
throw failure;
|
|
358
384
|
},
|
|
@@ -14,7 +14,7 @@ vi.mock("../utils/searchImpressions", () => ({
|
|
|
14
14
|
const response = (overrides = {}) => ({
|
|
15
15
|
results: [
|
|
16
16
|
{
|
|
17
|
-
index: "
|
|
17
|
+
index: "products_fr_eur",
|
|
18
18
|
hits: [
|
|
19
19
|
{ objectID: "p1", url: "https://shop.example/p1" },
|
|
20
20
|
{ objectID: "p2" },
|
|
@@ -36,13 +36,14 @@ const trackSelectSearchResult = vi.fn();
|
|
|
36
36
|
const navigate = vi.fn();
|
|
37
37
|
const createController = () => createSearchController({
|
|
38
38
|
search,
|
|
39
|
+
language: "fr",
|
|
40
|
+
currency: "EUR",
|
|
39
41
|
analytics: {
|
|
40
42
|
surface: "search_page",
|
|
41
43
|
trackViewSearchResults,
|
|
42
44
|
trackSelectSearchResult,
|
|
43
45
|
},
|
|
44
46
|
navigate,
|
|
45
|
-
locale: "fr",
|
|
46
47
|
});
|
|
47
48
|
beforeEach(() => {
|
|
48
49
|
vi.useFakeTimers();
|
|
@@ -75,7 +76,7 @@ describe("search controller attribution", () => {
|
|
|
75
76
|
await settle();
|
|
76
77
|
expect(tracker.setContext).toHaveBeenCalledWith({
|
|
77
78
|
query_id: "qid-1",
|
|
78
|
-
index: "
|
|
79
|
+
index: "products_fr_eur",
|
|
79
80
|
surface: "search_page",
|
|
80
81
|
search_type: "lexical",
|
|
81
82
|
page: 2,
|
|
@@ -105,7 +106,7 @@ describe("search controller attribution", () => {
|
|
|
105
106
|
await settle();
|
|
106
107
|
expect(trackViewSearchResults).toHaveBeenCalledWith({
|
|
107
108
|
query_id: "qid-1",
|
|
108
|
-
index: "
|
|
109
|
+
index: "products_fr_eur",
|
|
109
110
|
surface: "search_page",
|
|
110
111
|
search_type: "lexical",
|
|
111
112
|
page: 1,
|
|
@@ -151,7 +152,7 @@ describe("search controller attribution", () => {
|
|
|
151
152
|
});
|
|
152
153
|
expect(trackSelectSearchResult).toHaveBeenCalledWith({
|
|
153
154
|
query_id: "qid-1",
|
|
154
|
-
index: "
|
|
155
|
+
index: "products_fr_eur",
|
|
155
156
|
surface: "search_page",
|
|
156
157
|
search_type: "lexical",
|
|
157
158
|
page: 1,
|
|
@@ -201,12 +202,13 @@ describe("search controller attribution", () => {
|
|
|
201
202
|
it("returns false when no navigate adapter is configured", async () => {
|
|
202
203
|
const controller = createSearchController({
|
|
203
204
|
search,
|
|
205
|
+
language: "fr",
|
|
206
|
+
currency: "EUR",
|
|
204
207
|
analytics: {
|
|
205
208
|
surface: "search_page",
|
|
206
209
|
trackViewSearchResults,
|
|
207
210
|
trackSelectSearchResult,
|
|
208
211
|
},
|
|
209
|
-
locale: "fr",
|
|
210
212
|
});
|
|
211
213
|
search.mockResolvedValue(response());
|
|
212
214
|
controller.submit("shoes");
|
|
@@ -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: "
|
|
6
|
+
index: "products_fr_eur",
|
|
7
7
|
surface: "search_page",
|
|
8
8
|
search_type: "lexical",
|
|
9
9
|
page: 1,
|
|
@@ -184,8 +184,7 @@ describe("createSearchImpressionTracker", () => {
|
|
|
184
184
|
vi.advanceTimersByTime(300);
|
|
185
185
|
windowListeners.get("pageshow")?.({ persisted: true });
|
|
186
186
|
intersect(card, 0.6);
|
|
187
|
-
//
|
|
188
|
-
// at 1700ms; a fresh dwell records at 500ms and flushes at 2000ms.
|
|
187
|
+
// Fresh dwell: record at 500ms and flush at 2000ms, not at 200ms and 1700ms.
|
|
189
188
|
vi.advanceTimersByTime(1700);
|
|
190
189
|
expect(emit).not.toHaveBeenCalled();
|
|
191
190
|
vi.advanceTimersByTime(300);
|
|
@@ -5,7 +5,7 @@ const API_KEY = "pk_test_abcdef";
|
|
|
5
5
|
const request = {
|
|
6
6
|
requests: [
|
|
7
7
|
{
|
|
8
|
-
indexName: "
|
|
8
|
+
indexName: "products_fr_eur",
|
|
9
9
|
query: "running shoes",
|
|
10
10
|
page: 2,
|
|
11
11
|
hitsPerPage: 50,
|
|
@@ -15,7 +15,7 @@ const request = {
|
|
|
15
15
|
const emptyResponse = {
|
|
16
16
|
results: [
|
|
17
17
|
{
|
|
18
|
-
index: "
|
|
18
|
+
index: "products_fr_eur",
|
|
19
19
|
hits: [],
|
|
20
20
|
nbHits: 0,
|
|
21
21
|
page: 0,
|
|
@@ -41,15 +41,17 @@ afterEach(() => {
|
|
|
41
41
|
});
|
|
42
42
|
describe("searchIndexName", () => {
|
|
43
43
|
it.each([
|
|
44
|
-
["fr
|
|
45
|
-
["pt
|
|
46
|
-
["fr", "
|
|
47
|
-
["
|
|
48
|
-
])("
|
|
49
|
-
expect(searchIndexName("products", locale)).toBe(name);
|
|
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);
|
|
50
50
|
});
|
|
51
51
|
it("names every logical index", () => {
|
|
52
|
-
expect(searchIndexName("collections", "en
|
|
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/);
|
|
53
55
|
});
|
|
54
56
|
});
|
|
55
57
|
describe("searchLexical", () => {
|
|
@@ -69,11 +71,11 @@ describe("searchLexical", () => {
|
|
|
69
71
|
it("keeps optional per-entry fields the caller did not provide off the wire", async () => {
|
|
70
72
|
fetchMock.mockResolvedValue(jsonResponse(emptyResponse));
|
|
71
73
|
await searchLexical(API_KEY, {
|
|
72
|
-
requests: [{ indexName: "
|
|
74
|
+
requests: [{ indexName: "products_fr_eur", query: "running shoes" }],
|
|
73
75
|
});
|
|
74
76
|
const [, init] = fetchMock.mock.calls[0];
|
|
75
77
|
expect(JSON.parse(init.body)).toEqual({
|
|
76
|
-
requests: [{ indexName: "
|
|
78
|
+
requests: [{ indexName: "products_fr_eur", query: "running shoes" }],
|
|
77
79
|
});
|
|
78
80
|
});
|
|
79
81
|
it("resolves the typed response, including empty results", async () => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { SearchController, SearchControllerOptions } from "./types/searchController";
|
|
2
|
-
export declare function createSearchController({ search, analytics, navigate, debounceMs, hitsPerPage,
|
|
2
|
+
export declare function createSearchController({ search, language, currency, analytics, navigate, 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":"
|
|
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"}
|
package/dist/searchController.js
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
|
-
// This controller is one cohesive unit: `selectResult` reports whether the
|
|
2
|
-
// navigate adapter handled the transition and the response is split into the
|
|
3
|
-
// products entry plus its sections, pushing the file past the default 200-line
|
|
4
|
-
// cap — raised modestly rather than split artificially.
|
|
5
|
-
/* eslint max-lines: ["error", 240] */
|
|
6
1
|
import { searchIndexName } from "./services/search";
|
|
7
2
|
import { SearchStatus, } from "./types/searchController";
|
|
8
3
|
import { createControllerAnalytics } from "./utils/searchControllerAnalytics";
|
|
9
|
-
import {
|
|
10
|
-
const DEFAULT_DEBOUNCE_MS = 250;
|
|
11
|
-
const DEFAULT_HITS_PER_PAGE = 12;
|
|
12
|
-
// `SearchRequest` rejects queries under two visible characters (code points).
|
|
13
|
-
const MIN_QUERY_CODE_POINTS = 2;
|
|
4
|
+
import { buildSearchRequest, normalizeQuery, readSearchResults, } from "./utils/searchRequests";
|
|
14
5
|
const INITIAL_STATE = {
|
|
15
6
|
status: SearchStatus.IDLE,
|
|
16
7
|
query: "",
|
|
@@ -19,18 +10,19 @@ const INITIAL_STATE = {
|
|
|
19
10
|
sections: undefined,
|
|
20
11
|
error: undefined,
|
|
21
12
|
};
|
|
22
|
-
export function createSearchController({ search, analytics, navigate, debounceMs =
|
|
23
|
-
const
|
|
24
|
-
|
|
13
|
+
export function createSearchController({ search, language, currency, analytics, navigate, debounceMs = 250, hitsPerPage = 12, sections = [], }) {
|
|
14
|
+
const requestConfig = {
|
|
15
|
+
indexName: searchIndexName("products", language, currency),
|
|
16
|
+
language,
|
|
17
|
+
currency,
|
|
18
|
+
hitsPerPage,
|
|
19
|
+
};
|
|
25
20
|
let state = INITIAL_STATE;
|
|
26
21
|
const listeners = new Set();
|
|
27
22
|
const controllerAnalytics = createControllerAnalytics(analytics);
|
|
28
23
|
let debounceTimer;
|
|
29
|
-
// Query typed but not yet run: `state.query` only holds committed queries, so
|
|
30
|
-
// without this, `setPage()` would cancel the debounce and paginate stale results.
|
|
31
24
|
let pendingQuery;
|
|
32
25
|
let abortController;
|
|
33
|
-
// Monotonic token: a response only lands if it belongs to the latest request.
|
|
34
26
|
let requestId = 0;
|
|
35
27
|
let disposed = false;
|
|
36
28
|
const setState = (patch) => {
|
|
@@ -39,45 +31,40 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
39
31
|
listener(state);
|
|
40
32
|
}
|
|
41
33
|
};
|
|
42
|
-
const
|
|
34
|
+
const cancelPendingSearch = () => {
|
|
43
35
|
clearTimeout(debounceTimer);
|
|
36
|
+
pendingQuery = undefined;
|
|
37
|
+
requestId += 1;
|
|
44
38
|
abortController?.abort();
|
|
45
39
|
abortController = undefined;
|
|
46
40
|
};
|
|
47
|
-
const buildRequest = (query, page, requested) => ({
|
|
48
|
-
requests: [
|
|
49
|
-
{ indexName, query, page, hitsPerPage },
|
|
50
|
-
...buildSectionQueries(requested, query, locale, hitsPerPage),
|
|
51
|
-
],
|
|
52
|
-
});
|
|
53
41
|
const run = async (query, page) => {
|
|
54
|
-
|
|
42
|
+
cancelPendingSearch();
|
|
55
43
|
abortController = new AbortController();
|
|
56
|
-
const id =
|
|
44
|
+
const id = requestId;
|
|
57
45
|
setState({ status: SearchStatus.LOADING, query, page });
|
|
58
46
|
try {
|
|
59
|
-
const requested =
|
|
60
|
-
const
|
|
47
|
+
const requested = typeof sections === "function" ? sections() : sections;
|
|
48
|
+
const request = buildSearchRequest(query, page, requested, requestConfig);
|
|
49
|
+
const response = await search(request, {
|
|
61
50
|
signal: abortController.signal,
|
|
62
51
|
});
|
|
63
52
|
if (id !== requestId) {
|
|
64
|
-
return; //
|
|
65
|
-
}
|
|
66
|
-
const result = response.results.find((entry) => entry.index === indexName);
|
|
67
|
-
if (result === undefined) {
|
|
68
|
-
throw new Error(`Dialog search returned no ${indexName} entry`);
|
|
53
|
+
return; // Ignore a superseded request.
|
|
69
54
|
}
|
|
70
|
-
|
|
55
|
+
const results = readSearchResults(response, requested, requestConfig);
|
|
56
|
+
controllerAnalytics.onResponse(results.response);
|
|
71
57
|
setState({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
...results,
|
|
59
|
+
status: results.response.nbHits === 0
|
|
60
|
+
? SearchStatus.EMPTY
|
|
61
|
+
: SearchStatus.SUCCESS,
|
|
75
62
|
error: undefined,
|
|
76
63
|
});
|
|
77
64
|
}
|
|
78
65
|
catch (error) {
|
|
79
66
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
80
|
-
return; //
|
|
67
|
+
return; // Cancellation is not a search error.
|
|
81
68
|
}
|
|
82
69
|
if (id !== requestId) {
|
|
83
70
|
return;
|
|
@@ -90,33 +77,28 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
90
77
|
});
|
|
91
78
|
}
|
|
92
79
|
};
|
|
93
|
-
// Shared by `setQuery`/`submit`: undefined = reset to idle (query too short).
|
|
94
|
-
const prepare = (rawQuery) => {
|
|
95
|
-
const query = rawQuery.trim();
|
|
96
|
-
cancelInFlight();
|
|
97
|
-
pendingQuery = undefined;
|
|
98
|
-
if ([...query].length < MIN_QUERY_CODE_POINTS) {
|
|
99
|
-
requestId += 1; // Invalidate anything in flight.
|
|
100
|
-
setState(INITIAL_STATE);
|
|
101
|
-
return undefined;
|
|
102
|
-
}
|
|
103
|
-
return query;
|
|
104
|
-
};
|
|
105
80
|
return {
|
|
106
81
|
setQuery(rawQuery) {
|
|
107
|
-
|
|
82
|
+
if (disposed) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
cancelPendingSearch();
|
|
86
|
+
const query = normalizeQuery(rawQuery);
|
|
108
87
|
if (query === undefined) {
|
|
88
|
+
setState(INITIAL_STATE);
|
|
109
89
|
return;
|
|
110
90
|
}
|
|
111
91
|
pendingQuery = query;
|
|
112
|
-
debounceTimer = setTimeout(() =>
|
|
113
|
-
pendingQuery = undefined;
|
|
114
|
-
void run(query, 0);
|
|
115
|
-
}, debounceMs);
|
|
92
|
+
debounceTimer = setTimeout(() => void run(query, 0), debounceMs);
|
|
116
93
|
},
|
|
117
94
|
submit(rawQuery) {
|
|
118
|
-
|
|
95
|
+
if (disposed) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
cancelPendingSearch();
|
|
99
|
+
const query = normalizeQuery(rawQuery);
|
|
119
100
|
if (query === undefined) {
|
|
101
|
+
setState(INITIAL_STATE);
|
|
120
102
|
return;
|
|
121
103
|
}
|
|
122
104
|
void run(query, 0);
|
|
@@ -126,15 +108,12 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
126
108
|
return;
|
|
127
109
|
}
|
|
128
110
|
if (pendingQuery !== undefined) {
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
const query = pendingQuery;
|
|
132
|
-
pendingQuery = undefined;
|
|
133
|
-
void run(query, 0);
|
|
111
|
+
// Submit pending input on page zero before allowing pagination.
|
|
112
|
+
void run(pendingQuery, 0);
|
|
134
113
|
return;
|
|
135
114
|
}
|
|
136
115
|
if (state.query === "") {
|
|
137
|
-
return; //
|
|
116
|
+
return; // No query to paginate.
|
|
138
117
|
}
|
|
139
118
|
void run(state.query, page);
|
|
140
119
|
},
|
|
@@ -173,9 +152,7 @@ export function createSearchController({ search, analytics, navigate, debounceMs
|
|
|
173
152
|
getState: () => state,
|
|
174
153
|
dispose() {
|
|
175
154
|
disposed = true;
|
|
176
|
-
|
|
177
|
-
requestId += 1;
|
|
178
|
-
pendingQuery = undefined;
|
|
155
|
+
cancelPendingSearch();
|
|
179
156
|
listeners.clear();
|
|
180
157
|
controllerAnalytics.dispose();
|
|
181
158
|
},
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { SearchIndex, SearchOptions, SearchRequest, SearchResponse } from "../types/search";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* caller owns cancellation through `options.signal`.
|
|
6
|
-
*/
|
|
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;
|
|
4
|
+
/** Send one request. The caller controls cancellation through `options.signal`. */
|
|
7
5
|
export declare const searchLexical: (apiKey: string, request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
|
|
8
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;
|
|
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"}
|
package/dist/services/search.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { config } from "../config";
|
|
2
2
|
import { DialogSearchError } from "../DialogSearchError";
|
|
3
|
-
import { toIso639LanguageCode } from "../utils/localization";
|
|
4
3
|
const SEARCH_PATH = "/public/search/lexical";
|
|
5
4
|
const API_KEY_HEADER = "x-dialog-api-key";
|
|
6
|
-
|
|
5
|
+
/** Build an index name from an ISO 639-1 language and ISO 4217 currency. */
|
|
6
|
+
export const searchIndexName = (index, language, currency) => {
|
|
7
|
+
if (!/^[a-z]{2}$/.test(language)) {
|
|
8
|
+
throw new Error("Search language must be a lowercase ISO 639-1 code.");
|
|
9
|
+
}
|
|
10
|
+
if (!/^[A-Za-z]{3}$/.test(currency)) {
|
|
11
|
+
throw new Error("Search currency must be an ISO 4217 code.");
|
|
12
|
+
}
|
|
13
|
+
return `${index}_${language}_${currency.toLowerCase()}`;
|
|
14
|
+
};
|
|
7
15
|
const toSearchError = async (response) => {
|
|
8
16
|
let body;
|
|
9
17
|
try {
|
|
@@ -18,10 +26,7 @@ const toSearchError = async (response) => {
|
|
|
18
26
|
: response.statusText;
|
|
19
27
|
return new DialogSearchError({ status: response.status, code, message });
|
|
20
28
|
};
|
|
21
|
-
/**
|
|
22
|
-
* One POST per invocation — no debounce, cache, retry or request state; the
|
|
23
|
-
* caller owns cancellation through `options.signal`.
|
|
24
|
-
*/
|
|
29
|
+
/** Send one request. The caller controls cancellation through `options.signal`. */
|
|
25
30
|
export const searchLexical = async (apiKey, request, options) => {
|
|
26
31
|
const response = await fetch(`${config.monolithApiUrl}${SEARCH_PATH}`, {
|
|
27
32
|
method: "POST",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { AddToCartInput } from "./events";
|
|
1
|
+
import { AddToCartInput, ChangeCartQuantityInput } from "./events";
|
|
2
2
|
import { SimplifiedProduct } from "./product";
|
|
3
3
|
import { Theme } from "./theme";
|
|
4
4
|
export interface DialogCallbacks {
|
|
5
5
|
addToCart: (input: AddToCartInput) => Promise<void>;
|
|
6
6
|
getProduct: (productId: string, variantId?: string) => Promise<SimplifiedProduct>;
|
|
7
|
+
changeCartQuantity?: (input: ChangeCartQuantityInput) => Promise<void>;
|
|
7
8
|
}
|
|
8
9
|
export interface CurrentProduct {
|
|
9
10
|
/** The catalog product id — must match the id in the Dialog product feed. */
|
|
@@ -12,7 +13,10 @@ export interface CurrentProduct {
|
|
|
12
13
|
}
|
|
13
14
|
export interface DialogConstructor {
|
|
14
15
|
apiKey: string;
|
|
16
|
+
/** BCP-47 locale, e.g. `fr-FR`. */
|
|
15
17
|
locale: string;
|
|
18
|
+
/** Currency (ISO 4217), independent of language. */
|
|
19
|
+
currency: string;
|
|
16
20
|
/**
|
|
17
21
|
* Optional ISO 3166 country code used to format prices (e.g. 'FR', 'US').
|
|
18
22
|
* When provided it takes precedence over the country derived from `locale`.
|
|
@@ -21,8 +25,10 @@ export interface DialogConstructor {
|
|
|
21
25
|
countryCode?: string;
|
|
22
26
|
/**
|
|
23
27
|
* Commerce callbacks. Optional: a search-only integration can omit them —
|
|
24
|
-
* the assistant runtime still loads, and
|
|
25
|
-
* throw a configuration error when their callback is
|
|
28
|
+
* the assistant runtime still loads, and `getProduct()`, `addToCart()` and
|
|
29
|
+
* `changeCartQuantity()` throw a configuration error when their callback is
|
|
30
|
+
* absent. `changeCartQuantity` is optional within the set: check
|
|
31
|
+
* `canChangeCartQuantity()` before calling it.
|
|
26
32
|
*/
|
|
27
33
|
callbacks?: DialogCallbacks;
|
|
28
34
|
theme?: Partial<Theme>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constructor.d.ts","sourceRoot":"","sources":["../../src/types/constructor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"constructor.d.ts","sourceRoot":"","sources":["../../src/types/constructor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,UAAU,EAAE,CACV,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,KACf,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAED,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B"}
|
package/dist/types/events.d.ts
CHANGED
|
@@ -51,6 +51,11 @@ export interface AddToCartInput extends AddToCartProductDetails {
|
|
|
51
51
|
currency?: string;
|
|
52
52
|
variantId?: string;
|
|
53
53
|
}
|
|
54
|
+
export interface ChangeCartQuantityInput {
|
|
55
|
+
productId: string;
|
|
56
|
+
variantId?: string;
|
|
57
|
+
quantity: number;
|
|
58
|
+
}
|
|
54
59
|
export interface TrackEventPayload extends AddToCartProductDetails {
|
|
55
60
|
userId?: string;
|
|
56
61
|
productId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/types/events.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/types/events.ts"],"names":[],"mappings":"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"}
|