@stapel/search-react 0.9.1 → 0.11.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/CHANGELOG.md +74 -0
- package/dist/api/generated/schema.d.ts +16 -0
- package/dist/api/generated/schema.d.ts.map +1 -1
- package/dist/default/DegradationNotice.d.ts +22 -13
- package/dist/default/DegradationNotice.d.ts.map +1 -1
- package/dist/default/DegradationNotice.js +7 -8
- package/dist/default/DegradationNotice.js.map +1 -1
- package/dist/default/FacetPanelPane.d.ts.map +1 -1
- package/dist/default/FacetPanelPane.js +27 -16
- package/dist/default/FacetPanelPane.js.map +1 -1
- package/dist/default/FilterChips.d.ts.map +1 -1
- package/dist/default/FilterChips.js +13 -23
- package/dist/default/FilterChips.js.map +1 -1
- package/dist/default/LocationSummaryLine.d.ts +24 -0
- package/dist/default/LocationSummaryLine.d.ts.map +1 -0
- package/dist/default/LocationSummaryLine.js +83 -0
- package/dist/default/LocationSummaryLine.js.map +1 -0
- package/dist/default/RangeFilterRow.d.ts.map +1 -1
- package/dist/default/RangeFilterRow.js +49 -5
- package/dist/default/RangeFilterRow.js.map +1 -1
- package/dist/default/SearchPage.d.ts +19 -0
- package/dist/default/SearchPage.d.ts.map +1 -1
- package/dist/default/SearchPage.js +6 -6
- package/dist/default/SearchPage.js.map +1 -1
- package/dist/default/geoSheet.d.ts +61 -0
- package/dist/default/geoSheet.d.ts.map +1 -0
- package/dist/default/geoSheet.js +63 -0
- package/dist/default/geoSheet.js.map +1 -0
- package/dist/default/index.d.ts +2 -0
- package/dist/default/index.d.ts.map +1 -1
- package/dist/default/index.js +1 -0
- package/dist/default/index.js.map +1 -1
- package/dist/headless/FacetPanel.d.ts +16 -0
- package/dist/headless/FacetPanel.d.ts.map +1 -1
- package/dist/headless/FacetPanel.js +7 -0
- package/dist/headless/FacetPanel.js.map +1 -1
- package/dist/headless/useSearchCount.d.ts +44 -0
- package/dist/headless/useSearchCount.d.ts.map +1 -0
- package/dist/headless/useSearchCount.js +121 -0
- package/dist/headless/useSearchCount.js.map +1 -0
- package/dist/i18n/es.d.ts.map +1 -1
- package/dist/i18n/es.js +2 -0
- package/dist/i18n/es.js.map +1 -1
- package/dist/i18n/keys.d.ts +13 -0
- package/dist/i18n/keys.d.ts.map +1 -1
- package/dist/i18n/keys.js +15 -0
- package/dist/i18n/keys.js.map +1 -1
- package/dist/i18n/ru.d.ts.map +1 -1
- package/dist/i18n/ru.js +2 -0
- package/dist/i18n/ru.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/state/degradations.d.ts +32 -0
- package/dist/state/degradations.d.ts.map +1 -1
- package/dist/state/degradations.js +33 -0
- package/dist/state/degradations.js.map +1 -1
- package/dist/state/facets.d.ts +23 -0
- package/dist/state/facets.d.ts.map +1 -1
- package/dist/state/facets.js +59 -8
- package/dist/state/facets.js.map +1 -1
- package/dist/state/limits.d.ts +21 -0
- package/dist/state/limits.d.ts.map +1 -1
- package/dist/state/limits.js +21 -0
- package/dist/state/limits.js.map +1 -1
- package/dist/state/ranges.d.ts +45 -3
- package/dist/state/ranges.d.ts.map +1 -1
- package/dist/state/ranges.js +55 -14
- package/dist/state/ranges.js.map +1 -1
- package/llms.txt +3 -2
- package/manifest.json +36 -3
- package/nav-manifest.json +1 -1
- package/package.json +6 -6
- package/src/analytics/generated/events.json +1 -1
- package/src/api/generated/schema.ts +16 -0
- package/src/default/DegradationNotice.tsx +30 -20
- package/src/default/FacetPanelPane.tsx +39 -23
- package/src/default/FilterChips.tsx +22 -56
- package/src/default/LocationSummaryLine.tsx +187 -0
- package/src/default/RangeFilterRow.tsx +61 -3
- package/src/default/SearchPage.tsx +33 -3
- package/src/default/geoSheet.tsx +161 -0
- package/src/default/index.ts +3 -0
- package/src/headless/FacetPanel.tsx +24 -0
- package/src/headless/useSearchCount.ts +189 -0
- package/src/i18n/es.ts +2 -0
- package/src/i18n/keys.ts +15 -0
- package/src/i18n/ru.ts +2 -0
- package/src/index.ts +15 -1
- package/src/state/degradations.ts +56 -0
- package/src/state/facets.ts +96 -8
- package/src/state/limits.ts +23 -0
- package/src/state/ranges.ts +85 -15
package/dist/state/facets.js
CHANGED
|
@@ -8,12 +8,20 @@
|
|
|
8
8
|
* they would have if you swapped to them. A panel that greys the siblings
|
|
9
9
|
* out has silently converted a drill-down facet into a naive one, and the
|
|
10
10
|
* e2e leg in the spec (§7.2) exists to catch exactly that.
|
|
11
|
-
* 2. **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
11
|
+
* 2. **Option LABELS come from two places, in that order.** The category's
|
|
12
|
+
* feature schema — `categoryFeatures`, the second slot-seam of the pair,
|
|
13
|
+
* filled by the container from `categories-react` (spec §6.2 item 2) —
|
|
14
|
+
* and, under it, the answer's own `facet_labels` (stapel-search 0.4.0+).
|
|
15
|
+
*
|
|
16
|
+
* Until 0.4.0 the server sent `{value: count}` and nothing else, on the
|
|
17
|
+
* reasoning that the container has the schema anyway. That reasoning had
|
|
18
|
+
* a hole the size of a marketplace: the slot is OPTIONAL, a live
|
|
19
|
+
* classified board never filled it, and its buyers read "Condition:
|
|
20
|
+
* **b-u**", "Listing kind: **prodayu-svoe**", "Screen condition:
|
|
21
|
+
* **bez-defektov**" on the SERP and in the filter chips. The schema still
|
|
22
|
+
* wins where it resolves — the client fetched it with its own
|
|
23
|
+
* `Accept-Language` — and the answer's captions are the floor beneath it.
|
|
24
|
+
* Neither invents a label: a value nobody names renders as itself.
|
|
17
25
|
*
|
|
18
26
|
* A slug the server SKIPPED (`facet_meta.skipped`, dropped at
|
|
19
27
|
* `MAX_FACET_FIELDS`) is not counted at all. Its options carry `count: null`,
|
|
@@ -27,6 +35,21 @@ function translate(t, key) {
|
|
|
27
35
|
const resolved = t(key);
|
|
28
36
|
return resolved.length > 0 ? resolved : key;
|
|
29
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The caption the ANSWER carries for one option, or `undefined`.
|
|
40
|
+
*
|
|
41
|
+
* `translatable` is the server saying whether `values` holds translation
|
|
42
|
+
* KEYS or literal captions, and it has to be said rather than sniffed:
|
|
43
|
+
* `b.apple` and a rendered caption are both strings, and guessing wrong prints either a
|
|
44
|
+
* dotted key or an untranslated word at a buyer.
|
|
45
|
+
*/
|
|
46
|
+
function serverLabel(labels, slug, value, t) {
|
|
47
|
+
const entry = labels?.[slug];
|
|
48
|
+
const caption = entry?.values[value];
|
|
49
|
+
if (caption === undefined || caption.length === 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
return entry?.translatable === true ? translate(t, caption) : caption;
|
|
52
|
+
}
|
|
30
53
|
/**
|
|
31
54
|
* The declared option ORDER for a closed set, if the schema declares one.
|
|
32
55
|
*
|
|
@@ -54,6 +77,22 @@ function declaredOptionValues(feature) {
|
|
|
54
77
|
}
|
|
55
78
|
return out;
|
|
56
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* One option's caption: schema, then answer, then the raw value.
|
|
82
|
+
*
|
|
83
|
+
* Order matters and is deliberate. Both sources are the same category
|
|
84
|
+
* config, but the client fetched its copy with its own `Accept-Language`, so
|
|
85
|
+
* where the host threaded a schema through and it names the value, that is
|
|
86
|
+
* the better-localized answer. Where it does not — no schema, a slug the
|
|
87
|
+
* schema omits, a value added since — the server's caption is what stops
|
|
88
|
+
* `b-u` reaching a buyer.
|
|
89
|
+
*/
|
|
90
|
+
function resolveLabel(input, feature, slug, value, labelOptions) {
|
|
91
|
+
const viaSchema = facetOptionLabel(feature, value, labelOptions);
|
|
92
|
+
if (feature !== undefined && viaSchema !== value)
|
|
93
|
+
return viaSchema;
|
|
94
|
+
return serverLabel(input.facetLabels, slug, value, input.t) ?? viaSchema;
|
|
95
|
+
}
|
|
57
96
|
/**
|
|
58
97
|
* A facet value as a person reads it, through
|
|
59
98
|
* `@stapel/attributes-react`'s `formatFeatureValue` — the same formatter the
|
|
@@ -129,7 +168,14 @@ export function buildFacetGroups(input) {
|
|
|
129
168
|
const counts = input.facets[slug] ?? {};
|
|
130
169
|
const counted = !skipped.has(slug) && slug in input.facets;
|
|
131
170
|
const selected = input.state.filters[slug] ?? [];
|
|
132
|
-
|
|
171
|
+
// The authored option order, from whichever copy of the category config
|
|
172
|
+
// this page has. An authored list reshuffled by count is a size chart
|
|
173
|
+
// that moves on every click, and until 0.11 a host that passed no schema
|
|
174
|
+
// got exactly that.
|
|
175
|
+
const fromSchema = declaredOptionValues(feature);
|
|
176
|
+
const declared = fromSchema.length > 0
|
|
177
|
+
? fromSchema
|
|
178
|
+
: Object.keys(input.facetLabels?.[slug]?.values ?? {});
|
|
133
179
|
const values = [];
|
|
134
180
|
const push = (value) => {
|
|
135
181
|
if (!values.includes(value))
|
|
@@ -162,7 +208,12 @@ export function buildFacetGroups(input) {
|
|
|
162
208
|
options: values.map((value) => ({
|
|
163
209
|
value,
|
|
164
210
|
count: counted ? (counts[value] ?? 0) : null,
|
|
165
|
-
|
|
211
|
+
// The host's schema first when it actually resolves the value, the
|
|
212
|
+
// answer's caption when it does not (or when there is no schema at
|
|
213
|
+
// all), the raw value when neither knows. `facetOptionLabel` returns
|
|
214
|
+
// the value unchanged for an option it cannot name, which is what
|
|
215
|
+
// makes "did it resolve?" answerable without a second lookup.
|
|
216
|
+
label: resolveLabel(input, feature, slug, value, labelOptions),
|
|
166
217
|
selected: selected.includes(value),
|
|
167
218
|
})),
|
|
168
219
|
};
|
package/dist/state/facets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facets.js","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"facets.js","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"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"}
|
package/dist/state/limits.d.ts
CHANGED
|
@@ -22,6 +22,27 @@ export declare const SEARCH_DEFAULT_PAGE_SIZE = 24;
|
|
|
22
22
|
export declare const SEARCH_MAX_PAGE_SIZE = 100;
|
|
23
23
|
/** `services.suggest` clamps `limit` into `1..25`. */
|
|
24
24
|
export declare const SUGGEST_MAX_LIMIT = 25;
|
|
25
|
+
/**
|
|
26
|
+
* The page a COUNT read asks for.
|
|
27
|
+
*
|
|
28
|
+
* There is no count-only endpoint (`SearchApi` is query/suggest/ranking), so
|
|
29
|
+
* `useSearchCount` rides the ordinary query and takes the total out of the
|
|
30
|
+
* envelope. One row is the smallest page the endpoint documents — `limit=0` is
|
|
31
|
+
* not a page size — and it is the difference between serializing one card
|
|
32
|
+
* nobody renders and serializing the default twenty-four.
|
|
33
|
+
*/
|
|
34
|
+
export declare const SEARCH_COUNT_PAGE_SIZE = 1;
|
|
35
|
+
/**
|
|
36
|
+
* How long a composing panel has to stop changing before its count is asked
|
|
37
|
+
* for.
|
|
38
|
+
*
|
|
39
|
+
* Shorter than the search commit (350ms): a count follows discrete choices — a
|
|
40
|
+
* select, a chip, a range end — rather than keystrokes, and a button whose
|
|
41
|
+
* number arrives a third of a second after the choice reads as broken. Longer
|
|
42
|
+
* than zero: the read is a full query (see `SEARCH_COUNT_PAGE_SIZE`), so a
|
|
43
|
+
* person walking a select's options must not spend one search per option.
|
|
44
|
+
*/
|
|
45
|
+
export declare const SEARCH_COUNT_DEBOUNCE_MS = 250;
|
|
25
46
|
/**
|
|
26
47
|
* The shortest prefix worth asking the index about.
|
|
27
48
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/state/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,+DAA+D;AAC/D,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,8DAA8D;AAC9D,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/state/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,iFAAiF;AACjF,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,iEAAiE;AACjE,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,+DAA+D;AAC/D,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,sDAAsD;AACtD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAEnC,8DAA8D;AAC9D,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,MAAM,CAAC"}
|
package/dist/state/limits.js
CHANGED
|
@@ -22,6 +22,27 @@ export const SEARCH_DEFAULT_PAGE_SIZE = 24;
|
|
|
22
22
|
export const SEARCH_MAX_PAGE_SIZE = 100;
|
|
23
23
|
/** `services.suggest` clamps `limit` into `1..25`. */
|
|
24
24
|
export const SUGGEST_MAX_LIMIT = 25;
|
|
25
|
+
/**
|
|
26
|
+
* The page a COUNT read asks for.
|
|
27
|
+
*
|
|
28
|
+
* There is no count-only endpoint (`SearchApi` is query/suggest/ranking), so
|
|
29
|
+
* `useSearchCount` rides the ordinary query and takes the total out of the
|
|
30
|
+
* envelope. One row is the smallest page the endpoint documents — `limit=0` is
|
|
31
|
+
* not a page size — and it is the difference between serializing one card
|
|
32
|
+
* nobody renders and serializing the default twenty-four.
|
|
33
|
+
*/
|
|
34
|
+
export const SEARCH_COUNT_PAGE_SIZE = 1;
|
|
35
|
+
/**
|
|
36
|
+
* How long a composing panel has to stop changing before its count is asked
|
|
37
|
+
* for.
|
|
38
|
+
*
|
|
39
|
+
* Shorter than the search commit (350ms): a count follows discrete choices — a
|
|
40
|
+
* select, a chip, a range end — rather than keystrokes, and a button whose
|
|
41
|
+
* number arrives a third of a second after the choice reads as broken. Longer
|
|
42
|
+
* than zero: the read is a full query (see `SEARCH_COUNT_PAGE_SIZE`), so a
|
|
43
|
+
* person walking a select's options must not spend one search per option.
|
|
44
|
+
*/
|
|
45
|
+
export const SEARCH_COUNT_DEBOUNCE_MS = 250;
|
|
25
46
|
/**
|
|
26
47
|
* The shortest prefix worth asking the index about.
|
|
27
48
|
*
|
package/dist/state/limits.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/state/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C,iEAAiE;AACjE,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C,+DAA+D;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,sDAAsD;AACtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAEnC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC"}
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/state/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C,iEAAiE;AACjE,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C,+DAA+D;AAC/D,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC,sDAAsD;AACtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAEnC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,GAAG,CAAC"}
|
package/dist/state/ranges.d.ts
CHANGED
|
@@ -25,19 +25,61 @@ export interface RangeGroup {
|
|
|
25
25
|
readonly step: number | undefined;
|
|
26
26
|
/** Whether the URL currently constrains this slug. */
|
|
27
27
|
readonly active: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* `true` when the axis is a CORE document column the server declared in
|
|
30
|
+
* `facet_meta.core_ranges` rather than a category feature. Price is the
|
|
31
|
+
* shipped one. A core axis is drawn first and is never absent because a
|
|
32
|
+
* category forgot to declare an attribute for it.
|
|
33
|
+
*/
|
|
34
|
+
readonly core: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* ISO 4217 code when the row is money, so the control can read as money
|
|
37
|
+
* instead of as a bare integer. Only ever set on a core axis: an attribute
|
|
38
|
+
* carries a `postfix`, not a currency.
|
|
39
|
+
*/
|
|
40
|
+
readonly currency: string | undefined;
|
|
28
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* i18n key for a core axis's own name.
|
|
44
|
+
*
|
|
45
|
+
* A core axis has no FeatureDef and therefore no `name` to translate. The
|
|
46
|
+
* label is this package's, because the axis is this package's.
|
|
47
|
+
*/
|
|
48
|
+
export declare function coreRangeLabelKey(slug: string): string;
|
|
29
49
|
export interface BuildRangeGroupsInput {
|
|
30
50
|
readonly state: SearchQueryState;
|
|
31
51
|
readonly categoryFeatures?: readonly FeatureDef[];
|
|
52
|
+
/**
|
|
53
|
+
* `facet_meta.core_ranges` — range slugs that address a column of the
|
|
54
|
+
* document rather than an attribute (stapel-search 0.4.0+).
|
|
55
|
+
*
|
|
56
|
+
* It comes from the ANSWER on purpose. A live classified board offered
|
|
57
|
+
* seven numeric ranges — parcel weight, length, height, width, packing
|
|
58
|
+
* quantity, minimum-order quantity, battery condition — and no price,
|
|
59
|
+
* because a row was only ever drawn for a category feature and price is a
|
|
60
|
+
* column of the listing. Hardcoding `"price"` here would have fixed that
|
|
61
|
+
* board and broken the next one, where `r.price` still answers zero
|
|
62
|
+
* because the server predates the axis. So the server says which axes it
|
|
63
|
+
* can actually serve, and this list is empty against an older one.
|
|
64
|
+
*/
|
|
65
|
+
readonly coreRanges?: readonly string[];
|
|
66
|
+
/** ISO 4217 code for the money axes, when the surface knows one. */
|
|
67
|
+
readonly currency?: string;
|
|
32
68
|
/** Translator for label keys (the schema's `name` is often one). */
|
|
33
69
|
readonly t?: (key: string) => string;
|
|
34
70
|
}
|
|
35
71
|
/** Is this feature one a numeric range row is drawn for? */
|
|
36
72
|
export declare function isRangeFeature(feature: FeatureDef): boolean;
|
|
37
73
|
/**
|
|
38
|
-
* The range rows for the current search
|
|
39
|
-
*
|
|
40
|
-
* the schema
|
|
74
|
+
* The range rows for the current search, in the order they should be read:
|
|
75
|
+
* the CORE axes the answer declares, then every numeric feature of the
|
|
76
|
+
* category in the schema's own order, then any slug the URL constrains that
|
|
77
|
+
* neither explains.
|
|
78
|
+
*
|
|
79
|
+
* Core first is not cosmetic. On the board this was measured against, the
|
|
80
|
+
* seven numeric attributes a phone category happens to declare are all
|
|
81
|
+
* shipping and wholesale inputs; the one number a phone buyer narrows by is
|
|
82
|
+
* the price, and it belongs above them.
|
|
41
83
|
*/
|
|
42
84
|
export declare function buildRangeGroups(input: BuildRangeGroupsInput): readonly RangeGroup[];
|
|
43
85
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ranges.d.ts","sourceRoot":"","sources":["../../src/state/ranges.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ranges.d.ts","sourceRoot":"","sources":["../../src/state/ranges.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAIhD,CAAC;AAEF,8CAA8C;AAC9C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAAC;IACzC;iFAC6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;AAgBD,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAG3D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,GAC3B,SAAS,UAAU,EAAE,CA6CvB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAMzD"}
|
package/dist/state/ranges.js
CHANGED
|
@@ -6,13 +6,23 @@
|
|
|
6
6
|
* the panel only knew how to draw checkboxes. This module is the missing half:
|
|
7
7
|
* which slugs a range row exists for, and what the row is called.
|
|
8
8
|
*
|
|
9
|
-
* ── Where a range row comes from
|
|
9
|
+
* ── Where a range row comes from ──────────────────────────────────────────
|
|
10
10
|
*
|
|
11
|
-
* A facet answer (`facets: {slug: {value: count}}`) enumerates DISCRETE
|
|
12
|
-
* a range is not enumerable and
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* A facet answer (`facets: {slug: {value: count}}`) enumerates DISCRETE
|
|
12
|
+
* values; a range is not enumerable and no bucket is ever sent for one. So
|
|
13
|
+
* the rows come from two places:
|
|
14
|
+
*
|
|
15
|
+
* - the CATEGORY SCHEMA — the same `categoryFeatures` slot that gives the
|
|
16
|
+
* checkboxes their labels — filtered to the numeric value types;
|
|
17
|
+
* - the ANSWER's `facet_meta.core_ranges` (stapel-search 0.4.0+), which
|
|
18
|
+
* names the range slugs addressing a COLUMN of the document rather than
|
|
19
|
+
* an attribute. `price` is the shipped one, and it is why this module
|
|
20
|
+
* exists at all: a live classified board offered seven numeric ranges,
|
|
21
|
+
* every one of them a shipping or wholesale input, and no price — because
|
|
22
|
+
* price is not a category feature anywhere, and a row was only ever drawn
|
|
23
|
+
* for a feature.
|
|
24
|
+
*
|
|
25
|
+
* Plus every slug the URL already carries a range for.
|
|
16
26
|
*
|
|
17
27
|
* That last clause is the same rule `buildFacetGroups` follows for a filtered
|
|
18
28
|
* slug that fell out of the plan: a constraint that is ACTIVE must always have
|
|
@@ -35,6 +45,15 @@ export const RANGE_FEATURE_TYPES = [
|
|
|
35
45
|
"float",
|
|
36
46
|
"convertible_unit",
|
|
37
47
|
];
|
|
48
|
+
/**
|
|
49
|
+
* i18n key for a core axis's own name.
|
|
50
|
+
*
|
|
51
|
+
* A core axis has no FeatureDef and therefore no `name` to translate. The
|
|
52
|
+
* label is this package's, because the axis is this package's.
|
|
53
|
+
*/
|
|
54
|
+
export function coreRangeLabelKey(slug) {
|
|
55
|
+
return `search.range.${slug}`;
|
|
56
|
+
}
|
|
38
57
|
function num(value) {
|
|
39
58
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
40
59
|
}
|
|
@@ -53,17 +72,27 @@ export function isRangeFeature(feature) {
|
|
|
53
72
|
return type !== undefined && RANGE_FEATURE_TYPES.includes(type);
|
|
54
73
|
}
|
|
55
74
|
/**
|
|
56
|
-
* The range rows for the current search
|
|
57
|
-
*
|
|
58
|
-
* the schema
|
|
75
|
+
* The range rows for the current search, in the order they should be read:
|
|
76
|
+
* the CORE axes the answer declares, then every numeric feature of the
|
|
77
|
+
* category in the schema's own order, then any slug the URL constrains that
|
|
78
|
+
* neither explains.
|
|
79
|
+
*
|
|
80
|
+
* Core first is not cosmetic. On the board this was measured against, the
|
|
81
|
+
* seven numeric attributes a phone category happens to declare are all
|
|
82
|
+
* shipping and wholesale inputs; the one number a phone buyer narrows by is
|
|
83
|
+
* the price, and it belongs above them.
|
|
59
84
|
*/
|
|
60
85
|
export function buildRangeGroups(input) {
|
|
61
86
|
const bySlug = new Map();
|
|
62
87
|
for (const feature of input.categoryFeatures ?? [])
|
|
63
88
|
bySlug.set(feature.slug, feature);
|
|
64
|
-
const
|
|
89
|
+
const core = new Set(input.coreRanges ?? []);
|
|
90
|
+
const slugs = [...core];
|
|
65
91
|
for (const feature of input.categoryFeatures ?? []) {
|
|
66
|
-
|
|
92
|
+
// A core slug shadows a same-named attribute — which is exactly what the
|
|
93
|
+
// server does with it (`index_schema.CORE_RANGE_FIELDS` reserves the
|
|
94
|
+
// slug), so drawing both would put two controls over one filter.
|
|
95
|
+
if (isRangeFeature(feature) && !core.has(feature.slug))
|
|
67
96
|
slugs.push(feature.slug);
|
|
68
97
|
}
|
|
69
98
|
for (const slug of Object.keys(input.state.ranges)) {
|
|
@@ -71,20 +100,32 @@ export function buildRangeGroups(input) {
|
|
|
71
100
|
slugs.push(slug);
|
|
72
101
|
}
|
|
73
102
|
return slugs.map((slug) => {
|
|
74
|
-
const
|
|
103
|
+
const isCore = core.has(slug);
|
|
104
|
+
const feature = isCore ? undefined : bySlug.get(slug);
|
|
75
105
|
const config = feature === undefined ? {} : featureConfig(feature);
|
|
76
106
|
const applied = input.state.ranges[slug];
|
|
77
107
|
return {
|
|
78
108
|
slug,
|
|
79
|
-
label:
|
|
109
|
+
label: isCore
|
|
110
|
+
? translate(input.t, coreRangeLabelKey(slug))
|
|
111
|
+
: feature === undefined
|
|
112
|
+
? slug
|
|
113
|
+
: translate(input.t, featureName(feature)),
|
|
80
114
|
feature,
|
|
81
115
|
from: applied?.from,
|
|
82
116
|
to: applied?.to,
|
|
83
117
|
min: num(config["min"]),
|
|
84
118
|
max: num(config["max"]),
|
|
85
|
-
|
|
119
|
+
// A core money axis carries a CURRENCY, not a unit suffix: "₽" is
|
|
120
|
+
// formatted from the code for the reader's locale, a unit suffix is a literal
|
|
121
|
+
// the category author typed.
|
|
122
|
+
unit: isCore
|
|
123
|
+
? undefined
|
|
124
|
+
: (str(config["postfix"]) ?? str(config["unit_m"]) ?? str(config["unit_i"])),
|
|
86
125
|
step: feature !== undefined && featureType(feature) === "int" ? 1 : undefined,
|
|
87
126
|
active: applied !== undefined,
|
|
127
|
+
core: isCore,
|
|
128
|
+
currency: isCore ? str(input.currency) : undefined,
|
|
88
129
|
};
|
|
89
130
|
});
|
|
90
131
|
}
|
package/dist/state/ranges.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ranges.js","sourceRoot":"","sources":["../../src/state/ranges.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ranges.js","sourceRoot":"","sources":["../../src/state/ranges.ts"],"names":[],"mappings":"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"}
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# @stapel/search-react 0.
|
|
1
|
+
# @stapel/search-react 0.11.0
|
|
2
2
|
|
|
3
|
-
Headless React flow pair for stapel-search (contract >=0.
|
|
3
|
+
Headless React flow pair for stapel-search (contract >=0.4 <0.5) — business + state, zero visual opinion.
|
|
4
4
|
Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
|
|
5
5
|
verification-403 interception, i18n engine, analytics facade, TanStack Query layer.
|
|
6
6
|
|
|
@@ -81,6 +81,7 @@ const { tracked } = useTracked();
|
|
|
81
81
|
- search.facet-panel-pane → <FacetPanelPane> [open-search|narrowed] demo/FacetPanelPane.demo.tsx
|
|
82
82
|
- search.filter-sheet → <SearchPage> [open] demo/FilterSheet.demo.tsx
|
|
83
83
|
- search.language-select → <LanguageSelect> [offered|from-a-link] demo/LanguageSelect.demo.tsx
|
|
84
|
+
- search.location-summary → <LocationSummaryLine> [everywhere|placed|narrowed|wide] demo/LocationSummaryLine.demo.tsx
|
|
84
85
|
- search.page → <SearchPage> [desktop|phone|unreadable-link] demo/SearchPage.demo.tsx
|
|
85
86
|
- search.page-size-select → <PageSizeSelect> [ladder|off-ladder] demo/PageSizeSelect.demo.tsx
|
|
86
87
|
- search.range-filter-row → <RangeFilterRow> [untouched|applied] demo/RangeFilterRow.demo.tsx
|
package/manifest.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
|
|
3
3
|
"package": "@stapel/search-react",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
5
5
|
"backend": {
|
|
6
6
|
"module": "stapel-search",
|
|
7
|
-
"contract": ">=0.
|
|
7
|
+
"contract": ">=0.4 <0.5"
|
|
8
8
|
},
|
|
9
9
|
"layers": [
|
|
10
10
|
"api",
|
|
@@ -496,6 +496,26 @@
|
|
|
496
496
|
],
|
|
497
497
|
"source": "demo/LanguageSelect.demo.tsx"
|
|
498
498
|
},
|
|
499
|
+
{
|
|
500
|
+
"id": "search.location-summary",
|
|
501
|
+
"title": "Location summary line",
|
|
502
|
+
"description": "Where this search is looking, on a row of its own above the chips — because the chip row scrolls and location is the one constraint that changes what a result MEANS rather than narrowing a set. It prints the name the container handed it plus the radius, never a coordinate, and it opens the same location sheet the geo chip opens. On the right, the filter affordance carries a COUNT rather than the chip row's dot: this row has the width to say how many.",
|
|
503
|
+
"component": "LocationSummaryLine",
|
|
504
|
+
"covers": [
|
|
505
|
+
"SearchStateProvider"
|
|
506
|
+
],
|
|
507
|
+
"tokens": [
|
|
508
|
+
"brand",
|
|
509
|
+
"text-muted"
|
|
510
|
+
],
|
|
511
|
+
"variants": [
|
|
512
|
+
"everywhere",
|
|
513
|
+
"placed",
|
|
514
|
+
"narrowed",
|
|
515
|
+
"wide"
|
|
516
|
+
],
|
|
517
|
+
"source": "demo/LocationSummaryLine.demo.tsx"
|
|
518
|
+
},
|
|
499
519
|
{
|
|
500
520
|
"id": "search.page",
|
|
501
521
|
"title": "Search page",
|
|
@@ -734,6 +754,7 @@
|
|
|
734
754
|
"search.geo.box",
|
|
735
755
|
"search.geo.chosen_place",
|
|
736
756
|
"search.geo.clear",
|
|
757
|
+
"search.geo.everywhere",
|
|
737
758
|
"search.geo.radius_km",
|
|
738
759
|
"search.geo.radius_label",
|
|
739
760
|
"search.geo.title",
|
|
@@ -742,6 +763,7 @@
|
|
|
742
763
|
"search.limit.from_link",
|
|
743
764
|
"search.limit.label",
|
|
744
765
|
"search.limit.option",
|
|
766
|
+
"search.range.price",
|
|
745
767
|
"search.ranking.applies_to",
|
|
746
768
|
"search.ranking.empty",
|
|
747
769
|
"search.ranking.inactive",
|
|
@@ -805,6 +827,8 @@
|
|
|
805
827
|
"SEARCH_BACKEND_UNAVAILABLE",
|
|
806
828
|
"SEARCH_BOX_DEBOUNCE_MS",
|
|
807
829
|
"SEARCH_BOX_SUGGEST_DEBOUNCE_MS",
|
|
830
|
+
"SEARCH_COUNT_DEBOUNCE_MS",
|
|
831
|
+
"SEARCH_COUNT_PAGE_SIZE",
|
|
808
832
|
"SEARCH_DEFAULT_PAGE_SIZE",
|
|
809
833
|
"SEARCH_ERRORS",
|
|
810
834
|
"SEARCH_ERROR_CODES",
|
|
@@ -825,10 +849,13 @@
|
|
|
825
849
|
"buildFacetGroups",
|
|
826
850
|
"buildRangeGroups",
|
|
827
851
|
"clearFilters",
|
|
852
|
+
"coreRangeLabelKey",
|
|
828
853
|
"countIsEstimate",
|
|
829
854
|
"countKind",
|
|
855
|
+
"countQueryState",
|
|
830
856
|
"createSearchApi",
|
|
831
857
|
"createSearchRuntime",
|
|
858
|
+
"degradationAudience",
|
|
832
859
|
"degradationMessageKey",
|
|
833
860
|
"explainSearchError",
|
|
834
861
|
"facetOptionLabel",
|
|
@@ -840,6 +867,7 @@
|
|
|
840
867
|
"parseDegradations",
|
|
841
868
|
"parseSearchState",
|
|
842
869
|
"patchSearchState",
|
|
870
|
+
"readerFacing",
|
|
843
871
|
"registerSearchI18n",
|
|
844
872
|
"searchErrorBundleEn",
|
|
845
873
|
"searchI18nBundleEn",
|
|
@@ -854,6 +882,7 @@
|
|
|
854
882
|
"useSearchAnalytics",
|
|
855
883
|
"useSearchApi",
|
|
856
884
|
"useSearchBox",
|
|
885
|
+
"useSearchCount",
|
|
857
886
|
"useSearchQuery",
|
|
858
887
|
"useSearchRuntime",
|
|
859
888
|
"useSearchState",
|
|
@@ -879,8 +908,11 @@
|
|
|
879
908
|
"Scorer",
|
|
880
909
|
"SearchApi",
|
|
881
910
|
"SearchBoxBag",
|
|
911
|
+
"SearchCount",
|
|
882
912
|
"SearchCountKind",
|
|
913
|
+
"SearchCountState",
|
|
883
914
|
"SearchDegradation",
|
|
915
|
+
"SearchDegradationAudience",
|
|
884
916
|
"SearchDegradationKind",
|
|
885
917
|
"SearchErrorCode",
|
|
886
918
|
"SearchErrorSpec",
|
|
@@ -904,7 +936,8 @@
|
|
|
904
936
|
"SearchStateProviderProps",
|
|
905
937
|
"SuggestParams",
|
|
906
938
|
"SuggestResponse",
|
|
907
|
-
"UseSearchBoxOptions"
|
|
939
|
+
"UseSearchBoxOptions",
|
|
940
|
+
"UseSearchCountOptions"
|
|
908
941
|
]
|
|
909
942
|
}
|
|
910
943
|
}
|
package/nav-manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stapel/search-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Headless React pair for stapel-search: a typed query client, TanStack Query hooks, and a URL-first state codec that makes a search shareable by construction (filters, ranges, geo, sort and the keyset cursor all live in the query string). Drill-down facets rendered with their remaining counts and with the server's own honesty flags — approximate, skipped, degraded — never swallowed; keyset pagination with the window refusal named; DSA Art. 26 `promoted` marking carried into every card slot and the P2B Art. 5 ranking disclosure exposed as data. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin, and /router binds the codec to react-router's useSearchParams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"limit": "10 KB"
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
|
-
"name": "default — the antd skin (query box + typeahead, filters incl. ranges/geo/category slots + the phone chip row, results incl. the view switch, ranking) must stay out of the main bundle",
|
|
60
|
+
"name": "default — the antd skin (query box + typeahead, filters incl. ranges/geo/category slots + the phone chip row and the location summary row, results incl. the view switch, ranking) must stay out of the main bundle",
|
|
61
61
|
"path": "dist/default/index.js",
|
|
62
62
|
"limit": "18 KB"
|
|
63
63
|
},
|
|
@@ -115,12 +115,12 @@
|
|
|
115
115
|
"size-limit": "^11.2.0",
|
|
116
116
|
"typescript": "^5.8.3",
|
|
117
117
|
"vitest": "^3.2.4",
|
|
118
|
-
"@stapel/attributes-react": "^0.
|
|
119
|
-
"@stapel/core": "^0.
|
|
118
|
+
"@stapel/attributes-react": "^0.5.0",
|
|
119
|
+
"@stapel/core": "^0.21.0",
|
|
120
120
|
"@stapel/image": "^0.4.2",
|
|
121
|
-
"@stapel/showcase": "^0.3.0",
|
|
122
121
|
"@stapel/tokens": "^0.6.0",
|
|
123
|
-
"@stapel/tokens-antd": "^0.
|
|
122
|
+
"@stapel/tokens-antd": "^0.10.0",
|
|
123
|
+
"@stapel/showcase": "^0.3.0"
|
|
124
124
|
},
|
|
125
125
|
"engines": {
|
|
126
126
|
"node": ">=22"
|
|
@@ -124,6 +124,14 @@ export interface paths {
|
|
|
124
124
|
export type webhooks = Record<string, never>;
|
|
125
125
|
export interface components {
|
|
126
126
|
schemas: {
|
|
127
|
+
/** @description Captions for one slug's option codes. */
|
|
128
|
+
FacetLabels: {
|
|
129
|
+
/** @description True when `values` holds translation KEYS to run through the catalogue; false when it holds literal captions. The reader cannot tell by looking — `b.apple` and `Б/у` are both strings. */
|
|
130
|
+
translatable: boolean;
|
|
131
|
+
values: {
|
|
132
|
+
[key: string]: string;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
127
135
|
FacetMeta: {
|
|
128
136
|
/** @description True when counts came from a sample because the candidate set exceeded FACET_CANDIDATE_CAP. */
|
|
129
137
|
approximate: boolean;
|
|
@@ -132,6 +140,8 @@ export interface components {
|
|
|
132
140
|
counted: string[];
|
|
133
141
|
/** @description Plan slugs dropped at MAX_FACET_FIELDS — reported, not vanished. */
|
|
134
142
|
skipped: string[];
|
|
143
|
+
/** @description Range slugs that address a core document column rather than an attribute (`r.price`). Offer them as filters unconditionally: they exist for every document in every category, which is why they are not in the category's own plan. */
|
|
144
|
+
core_ranges: string[];
|
|
135
145
|
};
|
|
136
146
|
HealthResponse: {
|
|
137
147
|
backend: string;
|
|
@@ -208,6 +218,10 @@ export interface components {
|
|
|
208
218
|
[key: string]: number;
|
|
209
219
|
};
|
|
210
220
|
};
|
|
221
|
+
/** @description {slug: {translatable, values: {value: caption}}} for slugs whose options are inline in the category schema. Absent for a vocabulary-backed slug: its level lives outside the schema and the plan will not invent a caption it has not read. */
|
|
222
|
+
facet_labels: {
|
|
223
|
+
[key: string]: components["schemas"]["FacetLabels"];
|
|
224
|
+
};
|
|
211
225
|
facet_meta: components["schemas"]["FacetMeta"];
|
|
212
226
|
next_anchor: string | null;
|
|
213
227
|
prev_anchor: string | null;
|
|
@@ -222,6 +236,8 @@ export interface components {
|
|
|
222
236
|
/** @description What the configured engine could not do for this query. */
|
|
223
237
|
degraded: string[];
|
|
224
238
|
backend: string;
|
|
239
|
+
/** @description The language whose dictionary and analyzer configuration answered — `lang`, else Accept-Language, else DEFAULT_LANGUAGE. When the fallback is wrong the synonym layer silently does not apply, and this field is the only place the answer says so. */
|
|
240
|
+
language: string;
|
|
225
241
|
sort: string;
|
|
226
242
|
took_ms: number;
|
|
227
243
|
};
|