@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
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",
|
|
@@ -4,7 +4,7 @@ export declare const SearchStatus: {
|
|
|
4
4
|
readonly IDLE: "idle";
|
|
5
5
|
readonly LOADING: "loading";
|
|
6
6
|
readonly SUCCESS: "success";
|
|
7
|
-
/**
|
|
7
|
+
/** Successful request with zero hits. */
|
|
8
8
|
readonly EMPTY: "empty";
|
|
9
9
|
readonly ERROR: "error";
|
|
10
10
|
};
|
|
@@ -15,89 +15,60 @@ export interface SearchSection {
|
|
|
15
15
|
}
|
|
16
16
|
export interface SearchControllerState {
|
|
17
17
|
status: SearchStatus;
|
|
18
|
-
/** Last
|
|
18
|
+
/** Last submitted query, excluding pending input. */
|
|
19
19
|
query: string;
|
|
20
|
-
/** Zero-
|
|
20
|
+
/** Zero-based page of the last request. */
|
|
21
21
|
page: number;
|
|
22
|
-
/**
|
|
23
|
-
* Products entry of the last landed response; kept while the next page
|
|
24
|
-
* loads, cleared on error. Hits are flat records (`objectID`, `title`, …).
|
|
25
|
-
*/
|
|
22
|
+
/** Last products result; retained while loading and cleared on error or reset. */
|
|
26
23
|
response?: SearchResult;
|
|
27
24
|
sections?: Partial<Record<SearchIndex, SearchResult>>;
|
|
28
25
|
error?: unknown;
|
|
29
26
|
}
|
|
30
27
|
export type SearchFunction = (request: SearchRequest, options?: SearchOptions) => Promise<SearchResponse>;
|
|
31
|
-
/**
|
|
32
|
-
* Analytics sinks for the DEC-2448 storefront-search contract — wire the two
|
|
33
|
-
* track methods to the `Dialog` instance. Impressions (viewport batching,
|
|
34
|
-
* dedup, click-forced) and the zero-items no-results event are owned by the
|
|
35
|
-
* controller; integrations only declare where results are displayed.
|
|
36
|
-
*/
|
|
28
|
+
/** Event callbacks; the controller handles impression batching and deduplication. */
|
|
37
29
|
export interface SearchControllerAnalytics {
|
|
38
30
|
surface: SearchSurface;
|
|
39
31
|
trackViewSearchResults: (params: ViewSearchResultsParams) => void;
|
|
40
32
|
trackSelectSearchResult: (params: SelectSearchResultParams) => void;
|
|
41
33
|
}
|
|
42
34
|
export interface SearchControllerOptions {
|
|
43
|
-
/**
|
|
35
|
+
/** Search function bound to its client, or a custom transport. */
|
|
44
36
|
search: SearchFunction;
|
|
37
|
+
/** Lowercase ISO 639-1 language code, e.g. `fr`. */
|
|
38
|
+
language: string;
|
|
39
|
+
/** ISO 4217 currency, independent of language. */
|
|
40
|
+
currency: string;
|
|
45
41
|
analytics: SearchControllerAnalytics;
|
|
46
|
-
/**
|
|
47
|
-
* Platform navigation adapter, called by `selectResult` AFTER attribution
|
|
48
|
-
* is recorded, with the product URL of the selected hit. Omit it when the
|
|
49
|
-
* rendering layer navigates natively (plain `<a href>`): attribution still
|
|
50
|
-
* fires, the events are designed to survive navigation.
|
|
51
|
-
*/
|
|
42
|
+
/** Navigate after recording selection. Omit to use native link navigation. */
|
|
52
43
|
navigate?: (url: string, hit: SearchHit) => void;
|
|
53
44
|
debounceMs?: number;
|
|
54
45
|
hitsPerPage?: number;
|
|
55
|
-
locale: string;
|
|
56
46
|
sections?: readonly SearchSection[] | (() => readonly SearchSection[]);
|
|
57
47
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Framework-agnostic stateful search behavior around the stateless
|
|
60
|
-
* `dialog.search()` transport: debounce, cancellation, stale-response
|
|
61
|
-
* protection, pagination, retry and DEC-2448 attribution. Rendering and
|
|
62
|
-
* routing stay in the integration (raw JavaScript, React, Vue, Shopify).
|
|
63
|
-
*/
|
|
48
|
+
/** Manage search requests, pagination and analytics independently of rendering. */
|
|
64
49
|
export interface SearchController {
|
|
65
|
-
/**
|
|
50
|
+
/** Submit after the debounce delay. */
|
|
66
51
|
setQuery(rawQuery: string): void;
|
|
67
|
-
/**
|
|
52
|
+
/** Submit immediately. */
|
|
68
53
|
submit(rawQuery: string): void;
|
|
69
|
-
/**
|
|
54
|
+
/** Request a zero-based page immediately. Pending input starts at page zero. */
|
|
70
55
|
setPage(page: number): void;
|
|
71
|
-
/**
|
|
56
|
+
/** Retry the last request when the state is `error`. */
|
|
72
57
|
retry(): void;
|
|
73
|
-
/**
|
|
74
|
-
* Watch the rendered element of `state.response.hits[index]` for a
|
|
75
|
-
* viewport impression. Call it for every result element after rendering.
|
|
76
|
-
*/
|
|
58
|
+
/** Track impressions for the element representing `response.hits[index]`. */
|
|
77
59
|
observeResult(element: Element, index: number): void;
|
|
78
60
|
/**
|
|
79
|
-
* Record selection
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* middle-click/cmd+click.
|
|
83
|
-
*
|
|
84
|
-
* Pass `{ navigate: false }` for gestures the browser should handle natively
|
|
85
|
-
* (middle-click, or cmd/ctrl/shift/alt+click → new tab/window): attribution
|
|
86
|
-
* is still recorded, but the in-app adapter — which cannot open a new tab —
|
|
87
|
-
* is skipped so the native `<a href>` navigation runs.
|
|
88
|
-
*
|
|
89
|
-
* Returns `true` when the adapter handled the transition, so a rendering
|
|
90
|
-
* layer wrapping results in `<a href>` can `preventDefault()` and avoid a
|
|
91
|
-
* second (native) navigation. Returns `false` otherwise (adapter skipped, no
|
|
92
|
-
* adapter, or the hit has no URL) — let the native navigation proceed.
|
|
61
|
+
* Record the impression and selection for `response.hits[index]`.
|
|
62
|
+
* Pass `{ navigate: false }` for middle-clicks or modified clicks.
|
|
63
|
+
* Returns true if the navigation adapter ran; call `preventDefault()` then.
|
|
93
64
|
*/
|
|
94
65
|
selectResult(index: number, options?: {
|
|
95
66
|
navigate?: boolean;
|
|
96
67
|
}): boolean;
|
|
97
|
-
/**
|
|
68
|
+
/** Subscribe to state changes; returns an unsubscribe function. */
|
|
98
69
|
subscribe(listener: (state: SearchControllerState) => void): () => void;
|
|
99
70
|
getState(): SearchControllerState;
|
|
100
|
-
/** Cancel
|
|
71
|
+
/** Cancel pending requests and remove listeners and observers. */
|
|
101
72
|
dispose(): void;
|
|
102
73
|
}
|
|
103
74
|
//# sourceMappingURL=searchController.d.ts.map
|
|
@@ -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,
|
|
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"}
|
|
@@ -2,7 +2,7 @@ export const SearchStatus = {
|
|
|
2
2
|
IDLE: "idle",
|
|
3
3
|
LOADING: "loading",
|
|
4
4
|
SUCCESS: "success",
|
|
5
|
-
/**
|
|
5
|
+
/** Successful request with zero hits. */
|
|
6
6
|
EMPTY: "empty",
|
|
7
7
|
ERROR: "error",
|
|
8
8
|
};
|
|
@@ -23,5 +23,4 @@ export declare const resolveTextDirection: (code?: string) => TextDirection;
|
|
|
23
23
|
* therefore keeps a generic language name ("French", not "French (France)").
|
|
24
24
|
*/
|
|
25
25
|
export declare const getDetailedLocaleInfo: (locale: string, countryCodeOverride?: string) => DetailedLocaleInfo | null;
|
|
26
|
-
export declare const toIso639LanguageCode: (locale: string) => string;
|
|
27
26
|
//# sourceMappingURL=localization.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../../src/utils/localization.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,GAC7B,aAAa,MAAM,GAAG,SAAS,KAC9B,WAAW,IAAI,MAC8C,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,CAAC;AAQ1C,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,OAEa,CAAC;AAEhE,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,aACZ,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EACd,sBAAsB,MAAM,KAC3B,kBAAkB,GAAG,IAoCvB,CAAC
|
|
1
|
+
{"version":3,"file":"localization.d.ts","sourceRoot":"","sources":["../../src/utils/localization.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,kBAAkB,GAC7B,aAAa,MAAM,GAAG,SAAS,KAC9B,WAAW,IAAI,MAC8C,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,CAAC;AAQ1C,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,OAEa,CAAC;AAEhE,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,aACZ,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,EACd,sBAAsB,MAAM,KAC3B,kBAAkB,GAAG,IAoCvB,CAAC"}
|
|
@@ -52,14 +52,3 @@ export const getDetailedLocaleInfo = (locale, countryCodeOverride) => {
|
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
|
-
// The search backend keys its indexes on the bare ISO 639-1 language, while
|
|
56
|
-
// storefronts usually hold full BCP-47 tags ("fr-FR", "pt-BR"). An unparsable
|
|
57
|
-
// value is returned as-is so a search is never dropped over its locale.
|
|
58
|
-
export const toIso639LanguageCode = (locale) => {
|
|
59
|
-
try {
|
|
60
|
-
return new Intl.Locale(locale).language;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return locale;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { SearchResult } from "../types/search";
|
|
2
2
|
import { SearchControllerAnalytics } from "../types/searchController";
|
|
3
|
-
/**
|
|
4
|
-
* Internal DEC-2448 wiring of the search controller: envelope computation,
|
|
5
|
-
* viewport impressions and selection attribution. Not part of the public API.
|
|
6
|
-
*/
|
|
3
|
+
/** Internal binding for search impressions and selections. */
|
|
7
4
|
export interface ControllerAnalyticsBinding {
|
|
8
5
|
onResponse(result: SearchResult): void;
|
|
9
6
|
observeResult(element: Element, result: SearchResult, index: number): void;
|
|
@@ -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
|
|
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,14 +1,13 @@
|
|
|
1
1
|
import { createSearchImpressionTracker, } from "./searchImpressions";
|
|
2
2
|
const resultItem = (result, index) => ({
|
|
3
3
|
product_id: result.hits[index].objectID,
|
|
4
|
-
//
|
|
4
|
+
// One-based position across all pages.
|
|
5
5
|
position: result.page * result.hitsPerPage + index + 1,
|
|
6
6
|
});
|
|
7
7
|
export function createControllerAnalytics(analytics) {
|
|
8
8
|
let impressions;
|
|
9
9
|
let envelope;
|
|
10
|
-
//
|
|
11
|
-
// constructed in a non-browser context stays inert until a response lands.
|
|
10
|
+
// Defer browser-dependent tracking until a response arrives.
|
|
12
11
|
const tracker = () => {
|
|
13
12
|
impressions ??= createSearchImpressionTracker({
|
|
14
13
|
emit: analytics.trackViewSearchResults,
|
|
@@ -21,17 +20,15 @@ export function createControllerAnalytics(analytics) {
|
|
|
21
20
|
query_id: result.queryID,
|
|
22
21
|
index: result.index,
|
|
23
22
|
surface: analytics.surface,
|
|
24
|
-
// The storefront search API is lexical-only today.
|
|
25
23
|
search_type: "lexical",
|
|
26
|
-
//
|
|
24
|
+
// Convert the API page to one-based analytics numbering.
|
|
27
25
|
page: result.page + 1,
|
|
28
26
|
total_hits: result.nbHits,
|
|
29
27
|
query_length: [...result.query].length,
|
|
30
28
|
};
|
|
31
29
|
tracker().setContext(envelope);
|
|
32
30
|
if (result.nbHits === 0) {
|
|
33
|
-
//
|
|
34
|
-
// shows above the fold, no viewport gating needed.
|
|
31
|
+
// Empty results emit immediately without visibility tracking.
|
|
35
32
|
analytics.trackViewSearchResults({ ...envelope, items: [] });
|
|
36
33
|
}
|
|
37
34
|
},
|
|
@@ -43,7 +40,7 @@ export function createControllerAnalytics(analytics) {
|
|
|
43
40
|
return;
|
|
44
41
|
}
|
|
45
42
|
const item = resultItem(result, index);
|
|
46
|
-
//
|
|
43
|
+
// Count the impression before recording the selection.
|
|
47
44
|
tracker().forceImpression(item);
|
|
48
45
|
analytics.trackSelectSearchResult({ ...envelope, items: [item] });
|
|
49
46
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SearchAnalyticsEnvelope, SearchResultItem, ViewSearchResultsParams } from "../types/searchAnalytics";
|
|
2
2
|
export interface SearchImpressionTrackerOptions {
|
|
3
|
-
/**
|
|
3
|
+
/** Receive each batch, typically through `dialog.trackViewSearchResults`. */
|
|
4
4
|
emit: (params: ViewSearchResultsParams) => void;
|
|
5
5
|
visibilityThreshold?: number;
|
|
6
6
|
dwellMs?: number;
|
|
@@ -8,11 +8,11 @@ export interface SearchImpressionTrackerOptions {
|
|
|
8
8
|
maxItemsPerEvent?: number;
|
|
9
9
|
}
|
|
10
10
|
export interface SearchImpressionTracker {
|
|
11
|
-
/**
|
|
11
|
+
/** Flush the previous batch and start tracking the new response. */
|
|
12
12
|
setContext(envelope: SearchAnalyticsEnvelope): void;
|
|
13
|
-
/**
|
|
13
|
+
/** Track viewport impressions for a result element. */
|
|
14
14
|
observe(element: Element, item: SearchResultItem): void;
|
|
15
|
-
/**
|
|
15
|
+
/** Record and flush an impression without waiting for visibility. */
|
|
16
16
|
forceImpression(item: SearchResultItem): void;
|
|
17
17
|
flush(): void;
|
|
18
18
|
disconnect(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchImpressions.d.ts","sourceRoot":"","sources":["../../src/utils/searchImpressions.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"searchImpressions.d.ts","sourceRoot":"","sources":["../../src/utils/searchImpressions.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAQlC,MAAM,WAAW,8BAA8B;IAC7C,6EAA6E;IAC7E,IAAI,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAChD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,UAAU,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACpD,uDAAuD;IACvD,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACxD,qEAAqE;IACrE,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC9C,KAAK,IAAI,IAAI,CAAC;IACd,UAAU,IAAI,IAAI,CAAC;CACpB;AAED,wBAAgB,6BAA6B,CAAC,EAC5C,IAAI,EACJ,mBAAkD,EAClD,OAA0B,EAC1B,WAAmC,EACnC,gBAA8C,GAC/C,EAAE,8BAA8B,GAAG,uBAAuB,CA2H1D"}
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
// are batched and flushed on qualification inactivity, context change,
|
|
3
|
-
// pagehide or the batch cap — in practice 1-3 events per search, not one per
|
|
4
|
-
// item.
|
|
1
|
+
// Defaults: 50% visibility for 500ms. Flush on idle, context change, pagehide or batch limit.
|
|
5
2
|
const DEFAULT_VISIBILITY_THRESHOLD = 0.5;
|
|
6
3
|
const DEFAULT_DWELL_MS = 500;
|
|
7
4
|
const DEFAULT_IDLE_FLUSH_MS = 1500;
|
|
@@ -72,15 +69,11 @@ export function createSearchImpressionTracker({ emit, visibilityThreshold = DEFA
|
|
|
72
69
|
dwellTimers.clear();
|
|
73
70
|
observedItems.clear();
|
|
74
71
|
};
|
|
75
|
-
//
|
|
76
|
-
// captures this last event with a beacon-capable transport.
|
|
72
|
+
// Flush qualified impressions before leaving the page.
|
|
77
73
|
const handlePagehide = () => {
|
|
78
74
|
flush();
|
|
79
75
|
};
|
|
80
|
-
//
|
|
81
|
-
// screen instead of keeping it deduplicated forever. Dwell timers frozen
|
|
82
|
-
// mid-count resume with their remaining delay, so cancel them — the
|
|
83
|
-
// restored exposure must earn its full dwell.
|
|
76
|
+
// After a back/forward-cache restore, reset deduplication and restart dwell timers.
|
|
84
77
|
const handlePageshow = (event) => {
|
|
85
78
|
if (!event.persisted) {
|
|
86
79
|
return;
|
|
@@ -96,7 +89,7 @@ export function createSearchImpressionTracker({ emit, visibilityThreshold = DEFA
|
|
|
96
89
|
window.addEventListener("pageshow", handlePageshow);
|
|
97
90
|
return {
|
|
98
91
|
setContext(nextEnvelope) {
|
|
99
|
-
//
|
|
92
|
+
// Flush under the previous context before replacing it.
|
|
100
93
|
flush();
|
|
101
94
|
seen.clear();
|
|
102
95
|
envelope = nextEnvelope;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SearchIndex, SearchRequest, SearchResponse, SearchResult } from "../types/search";
|
|
2
|
+
import { SearchSection } from "../types/searchController";
|
|
3
|
+
export declare const normalizeQuery: (rawQuery: string) => string | undefined;
|
|
4
|
+
interface SearchRequestConfig {
|
|
5
|
+
indexName: string;
|
|
6
|
+
language: string;
|
|
7
|
+
currency: string;
|
|
8
|
+
hitsPerPage: number;
|
|
9
|
+
}
|
|
10
|
+
interface SearchResults {
|
|
11
|
+
response: SearchResult;
|
|
12
|
+
sections: Partial<Record<SearchIndex, SearchResult>> | undefined;
|
|
13
|
+
}
|
|
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;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=searchRequests.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { searchIndexName } from "../services/search";
|
|
2
|
+
// Match the API minimum query length.
|
|
3
|
+
const MIN_QUERY_CODE_POINTS = 2;
|
|
4
|
+
export const normalizeQuery = (rawQuery) => {
|
|
5
|
+
const query = rawQuery.trim();
|
|
6
|
+
return [...query].length >= MIN_QUERY_CODE_POINTS ? query : undefined;
|
|
7
|
+
};
|
|
8
|
+
export const buildSearchRequest = (query, page, sections, { indexName, language, currency, hitsPerPage }) => ({
|
|
9
|
+
requests: [
|
|
10
|
+
{ indexName, query, page, hitsPerPage },
|
|
11
|
+
...sections.map((section) => ({
|
|
12
|
+
indexName: searchIndexName(section.index, language, currency),
|
|
13
|
+
query,
|
|
14
|
+
page: 0,
|
|
15
|
+
hitsPerPage: section.hitsPerPage ?? hitsPerPage,
|
|
16
|
+
})),
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
export const readSearchResults = (result, requested, { indexName, language, currency }) => {
|
|
20
|
+
const response = result.results.find((entry) => entry.index === indexName);
|
|
21
|
+
if (response === undefined) {
|
|
22
|
+
throw new Error(`Dialog search returned no ${indexName} entry`);
|
|
23
|
+
}
|
|
24
|
+
const sections = {};
|
|
25
|
+
for (const section of requested) {
|
|
26
|
+
const requestedIndexName = searchIndexName(section.index, language, currency);
|
|
27
|
+
const entry = result.results.find((candidate) => candidate.index === requestedIndexName);
|
|
28
|
+
if (entry !== undefined) {
|
|
29
|
+
sections[section.index] = entry;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
response,
|
|
34
|
+
sections: requested.length === 0 ? undefined : sections,
|
|
35
|
+
};
|
|
36
|
+
};
|
package/package.json
CHANGED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { SearchIndex, SearchQuery, SearchResponse, SearchResult } from "../types/search";
|
|
2
|
-
import { SearchSection } from "../types/searchController";
|
|
3
|
-
export declare const buildSectionQueries: (sections: readonly SearchSection[], query: string, locale: string, hitsPerPage: number) => SearchQuery[];
|
|
4
|
-
export declare const pickSectionResults: (response: SearchResponse, sections: readonly SearchSection[], locale: string) => Partial<Record<SearchIndex, SearchResult>> | undefined;
|
|
5
|
-
//# sourceMappingURL=searchSections.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"searchSections.d.ts","sourceRoot":"","sources":["../../src/utils/searchSections.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,WAAW,EACX,cAAc,EACd,YAAY,EACb,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,eAAO,MAAM,mBAAmB,GAC9B,UAAU,SAAS,aAAa,EAAE,EAClC,OAAO,MAAM,EACb,QAAQ,MAAM,EACd,aAAa,MAAM,KAClB,WAAW,EAMT,CAAC;AAEN,eAAO,MAAM,kBAAkB,GAC7B,UAAU,cAAc,EACxB,UAAU,SAAS,aAAa,EAAE,EAClC,QAAQ,MAAM,KACb,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,GAAG,SAgB/C,CAAC"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { searchIndexName } from "../services/search";
|
|
2
|
-
export const buildSectionQueries = (sections, query, locale, hitsPerPage) => sections.map((section) => ({
|
|
3
|
-
indexName: searchIndexName(section.index, locale),
|
|
4
|
-
query,
|
|
5
|
-
page: 0,
|
|
6
|
-
hitsPerPage: section.hitsPerPage ?? hitsPerPage,
|
|
7
|
-
}));
|
|
8
|
-
export const pickSectionResults = (response, sections, locale) => {
|
|
9
|
-
if (sections.length === 0) {
|
|
10
|
-
return undefined;
|
|
11
|
-
}
|
|
12
|
-
const picked = {};
|
|
13
|
-
for (const section of sections) {
|
|
14
|
-
const sectionIndexName = searchIndexName(section.index, locale);
|
|
15
|
-
const entry = response.results.find((candidate) => candidate.index === sectionIndexName);
|
|
16
|
-
if (entry !== undefined) {
|
|
17
|
-
picked[section.index] = entry;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return picked;
|
|
21
|
-
};
|