@askdialog/dialog-sdk 2.11.0 → 2.12.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 +12 -21
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Dialog.js +25 -51
- package/dist/__tests__/dialogSearchAnalytics.spec.js +2 -4
- package/dist/__tests__/publicTypes.spec.js +1 -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 +3 -0
- package/dist/types/constructor.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
|
@@ -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",
|
|
@@ -12,7 +12,10 @@ export interface CurrentProduct {
|
|
|
12
12
|
}
|
|
13
13
|
export interface DialogConstructor {
|
|
14
14
|
apiKey: string;
|
|
15
|
+
/** BCP-47 locale, e.g. `fr-FR`. */
|
|
15
16
|
locale: string;
|
|
17
|
+
/** Currency (ISO 4217), independent of language. */
|
|
18
|
+
currency: string;
|
|
16
19
|
/**
|
|
17
20
|
* Optional ISO 3166 country code used to format prices (e.g. 'FR', 'US').
|
|
18
21
|
* When provided it takes precedence over the country derived from `locale`.
|
|
@@ -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;AAC1C,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;CACjC;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,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;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"}
|
|
1
|
+
{"version":3,"file":"constructor.d.ts","sourceRoot":"","sources":["../../src/types/constructor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,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;CACjC;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;;;;OAIG;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/search.d.ts
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Supported indices before adding language and currency. */
|
|
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.
|
|
8
|
+
* Unsupported index names return 404.
|
|
9
|
+
*/
|
|
6
10
|
indexName: string;
|
|
7
|
-
/**
|
|
11
|
+
/** At least two code points after server-side trimming. */
|
|
8
12
|
query: string;
|
|
9
|
-
/** Zero-
|
|
13
|
+
/** Zero-based page; server default: 0. */
|
|
10
14
|
page?: number;
|
|
11
|
-
/**
|
|
15
|
+
/** Page size from 1 to 100; server default: 20. */
|
|
12
16
|
hitsPerPage?: number;
|
|
13
17
|
}
|
|
14
18
|
export interface SearchRequest {
|
|
15
19
|
requests: SearchQuery[];
|
|
16
20
|
}
|
|
17
21
|
export interface SearchOptions {
|
|
18
|
-
/**
|
|
22
|
+
/** Passed to fetch; cancellation rejects with `AbortError`. */
|
|
19
23
|
signal?: AbortSignal;
|
|
20
24
|
}
|
|
21
25
|
export interface SearchPrice {
|
|
22
|
-
/** Decimal
|
|
26
|
+
/** Decimal string, e.g. "24.90". */
|
|
23
27
|
amount: string;
|
|
24
|
-
/**
|
|
28
|
+
/** Currency code, if provided by the index. */
|
|
25
29
|
currencyCode?: string;
|
|
26
30
|
}
|
|
27
31
|
export interface SearchPriceRange {
|
|
28
32
|
min: SearchPrice;
|
|
29
33
|
max: SearchPrice;
|
|
30
34
|
}
|
|
31
|
-
/**
|
|
32
|
-
* A flat, storefront-ready record: `objectID` plus the record's attributes.
|
|
33
|
-
* Every display field is best-effort — a hit may carry only its id;
|
|
34
|
-
* `priceRange` only comes from the products index.
|
|
35
|
-
*/
|
|
35
|
+
/** Indexed record. Display fields are optional; only products have `priceRange`. */
|
|
36
36
|
export interface SearchHit {
|
|
37
37
|
objectID: string;
|
|
38
38
|
title?: string;
|
|
@@ -52,7 +52,7 @@ export interface SearchResult {
|
|
|
52
52
|
query: string;
|
|
53
53
|
queryID: string;
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
55
|
+
/** Results in request order. */
|
|
56
56
|
export interface SearchResponse {
|
|
57
57
|
results: SearchResult[];
|
|
58
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/types/search.ts"],"names":[],"mappings":"AAAA
|
|
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"}
|
package/dist/types/search.js
CHANGED
|
@@ -4,26 +4,32 @@ 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
|
-
/**
|
|
7
|
+
/** Index returned by the API, e.g. `products_fr_eur`. */
|
|
8
8
|
index: string;
|
|
9
|
-
/**
|
|
9
|
+
/** UI surface displaying the results. */
|
|
10
10
|
surface: SearchSurface;
|
|
11
|
-
/**
|
|
11
|
+
/** Search mode that produced the results. */
|
|
12
12
|
search_type: SearchType;
|
|
13
|
-
/**
|
|
13
|
+
/** One-based results page. */
|
|
14
14
|
page: number;
|
|
15
|
-
/** Total
|
|
15
|
+
/** Total hits across all pages. */
|
|
16
16
|
total_hits: number;
|
|
17
|
-
/**
|
|
17
|
+
/** Number of code points in the trimmed query. */
|
|
18
18
|
query_length: number;
|
|
19
19
|
}
|
|
20
|
+
/** Position is one-based across pages: page 2 at 20 hits/page starts at 21. */
|
|
20
21
|
export interface SearchResultItem {
|
|
21
22
|
product_id: string;
|
|
22
23
|
position: number;
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Impressions batched and deduplicated by (query_id, product_id).
|
|
27
|
+
* Empty results use `items: []` and `total_hits: 0`.
|
|
28
|
+
*/
|
|
24
29
|
export interface ViewSearchResultsParams extends SearchAnalyticsEnvelope {
|
|
25
30
|
items: SearchResultItem[];
|
|
26
31
|
}
|
|
32
|
+
/** One selected result per event. */
|
|
27
33
|
export interface SelectSearchResultParams extends SearchAnalyticsEnvelope {
|
|
28
34
|
items: [SearchResultItem];
|
|
29
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchAnalytics.d.ts","sourceRoot":"","sources":["../../src/types/searchAnalytics.ts"],"names":[],"mappings":"
|
|
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,7 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// these property names ARE the canonical PostHog properties — the host
|
|
3
|
-
// bridge forwards them verbatim (no camelCase mapping), so any rename here
|
|
4
|
-
// is a breaking change of the cross-repo contract.
|
|
1
|
+
// Property names are forwarded unchanged to analytics; renaming them breaks consumers.
|
|
5
2
|
export const SEARCH_SURFACES = [
|
|
6
3
|
"autocomplete",
|
|
7
4
|
"search_page",
|