@stapel/search-react 0.1.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 +1 -0
- package/MODULE.md +203 -0
- package/README.md +175 -0
- package/dist/api/generated/schema.d.ts +375 -0
- package/dist/api/generated/schema.d.ts.map +1 -0
- package/dist/api/generated/schema.js +2 -0
- package/dist/api/generated/schema.js.map +1 -0
- package/dist/api/searchApi.d.ts +77 -0
- package/dist/api/searchApi.d.ts.map +1 -0
- package/dist/api/searchApi.js +79 -0
- package/dist/api/searchApi.js.map +1 -0
- package/dist/api/types.d.ts +131 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +20 -0
- package/dist/api/types.js.map +1 -0
- package/dist/default/DegradationNotice.d.ts +19 -0
- package/dist/default/DegradationNotice.d.ts.map +1 -0
- package/dist/default/DegradationNotice.js +14 -0
- package/dist/default/DegradationNotice.js.map +1 -0
- package/dist/default/ErrorAlert.d.ts +9 -0
- package/dist/default/ErrorAlert.d.ts.map +1 -0
- package/dist/default/ErrorAlert.js +27 -0
- package/dist/default/ErrorAlert.js.map +1 -0
- package/dist/default/FacetPanelPane.d.ts +26 -0
- package/dist/default/FacetPanelPane.d.ts.map +1 -0
- package/dist/default/FacetPanelPane.js +32 -0
- package/dist/default/FacetPanelPane.js.map +1 -0
- package/dist/default/RankingDisclosurePane.d.ts +17 -0
- package/dist/default/RankingDisclosurePane.d.ts.map +1 -0
- package/dist/default/RankingDisclosurePane.js +23 -0
- package/dist/default/RankingDisclosurePane.js.map +1 -0
- package/dist/default/SearchPage.d.ts +34 -0
- package/dist/default/SearchPage.d.ts.map +1 -0
- package/dist/default/SearchPage.js +16 -0
- package/dist/default/SearchPage.js.map +1 -0
- package/dist/default/SearchResultCard.d.ts +34 -0
- package/dist/default/SearchResultCard.d.ts.map +1 -0
- package/dist/default/SearchResultCard.js +38 -0
- package/dist/default/SearchResultCard.js.map +1 -0
- package/dist/default/SearchResultsPane.d.ts +32 -0
- package/dist/default/SearchResultsPane.d.ts.map +1 -0
- package/dist/default/SearchResultsPane.js +46 -0
- package/dist/default/SearchResultsPane.js.map +1 -0
- package/dist/default/SortSelect.d.ts +22 -0
- package/dist/default/SortSelect.d.ts.map +1 -0
- package/dist/default/SortSelect.js +39 -0
- package/dist/default/SortSelect.js.map +1 -0
- package/dist/default/UrlIssueNotice.d.ts +12 -0
- package/dist/default/UrlIssueNotice.d.ts.map +1 -0
- package/dist/default/UrlIssueNotice.js +13 -0
- package/dist/default/UrlIssueNotice.js.map +1 -0
- package/dist/default/index.d.ts +40 -0
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +34 -0
- package/dist/default/index.js.map +1 -0
- package/dist/default/theme.d.ts +24 -0
- package/dist/default/theme.d.ts.map +1 -0
- package/dist/default/theme.js +39 -0
- package/dist/default/theme.js.map +1 -0
- package/dist/default/types.d.ts +12 -0
- package/dist/default/types.d.ts.map +1 -0
- package/dist/default/types.js +2 -0
- package/dist/default/types.js.map +1 -0
- package/dist/headless/FacetPanel.d.ts +61 -0
- package/dist/headless/FacetPanel.d.ts.map +1 -0
- package/dist/headless/FacetPanel.js +57 -0
- package/dist/headless/FacetPanel.js.map +1 -0
- package/dist/headless/RankingDisclosure.d.ts +39 -0
- package/dist/headless/RankingDisclosure.d.ts.map +1 -0
- package/dist/headless/RankingDisclosure.js +28 -0
- package/dist/headless/RankingDisclosure.js.map +1 -0
- package/dist/headless/SearchProvider.d.ts +19 -0
- package/dist/headless/SearchProvider.d.ts.map +1 -0
- package/dist/headless/SearchProvider.js +15 -0
- package/dist/headless/SearchProvider.js.map +1 -0
- package/dist/headless/SearchResults.d.ts +68 -0
- package/dist/headless/SearchResults.d.ts.map +1 -0
- package/dist/headless/SearchResults.js +70 -0
- package/dist/headless/SearchResults.js.map +1 -0
- package/dist/headless/SearchStateProvider.d.ts +69 -0
- package/dist/headless/SearchStateProvider.d.ts.map +1 -0
- package/dist/headless/SearchStateProvider.js +71 -0
- package/dist/headless/SearchStateProvider.js.map +1 -0
- package/dist/i18n/errorsMap.d.ts +27 -0
- package/dist/i18n/errorsMap.d.ts.map +1 -0
- package/dist/i18n/errorsMap.js +37 -0
- package/dist/i18n/errorsMap.js.map +1 -0
- package/dist/i18n/es.d.ts +13 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +85 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/generated/errors.es.gen.d.ts +9 -0
- package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.es.gen.js +63 -0
- package/dist/i18n/generated/errors.es.gen.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +359 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.gen.js +183 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts +9 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.js +63 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
- package/dist/i18n/keys.d.ts +107 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +185 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/i18n/ru.d.ts +29 -0
- package/dist/i18n/ru.d.ts.map +1 -0
- package/dist/i18n/ru.js +101 -0
- package/dist/i18n/ru.js.map +1 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/model/context.d.ts +11 -0
- package/dist/model/context.d.ts.map +1 -0
- package/dist/model/context.js +16 -0
- package/dist/model/context.js.map +1 -0
- package/dist/model/queries.d.ts +48 -0
- package/dist/model/queries.d.ts.map +1 -0
- package/dist/model/queries.js +65 -0
- package/dist/model/queries.js.map +1 -0
- package/dist/model/queryKeys.d.ts +25 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +22 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/runtime.d.ts +25 -0
- package/dist/model/runtime.d.ts.map +1 -0
- package/dist/model/runtime.js +6 -0
- package/dist/model/runtime.js.map +1 -0
- package/dist/nav/manifest.d.ts +21 -0
- package/dist/nav/manifest.d.ts.map +1 -0
- package/dist/nav/manifest.js +34 -0
- package/dist/nav/manifest.js.map +1 -0
- package/dist/router/index.d.ts +10 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +34 -0
- package/dist/router/index.js.map +1 -0
- package/dist/state/degradations.d.ts +30 -0
- package/dist/state/degradations.d.ts.map +1 -0
- package/dist/state/degradations.js +52 -0
- package/dist/state/degradations.js.map +1 -0
- package/dist/state/facets.d.ts +68 -0
- package/dist/state/facets.d.ts.map +1 -0
- package/dist/state/facets.js +171 -0
- package/dist/state/facets.js.map +1 -0
- package/dist/state/urlState.d.ts +143 -0
- package/dist/state/urlState.d.ts.map +1 -0
- package/dist/state/urlState.js +319 -0
- package/dist/state/urlState.js.map +1 -0
- package/llms.txt +87 -0
- package/manifest.json +652 -0
- package/nav-manifest.json +44 -0
- package/package.json +133 -0
- package/src/analytics/generated/events.json +7 -0
- package/src/api/generated/schema.ts +377 -0
- package/src/api/searchApi.ts +169 -0
- package/src/api/types.ts +160 -0
- package/src/default/DegradationNotice.tsx +50 -0
- package/src/default/ErrorAlert.tsx +46 -0
- package/src/default/FacetPanelPane.tsx +174 -0
- package/src/default/RankingDisclosurePane.tsx +116 -0
- package/src/default/SearchPage.tsx +87 -0
- package/src/default/SearchResultCard.tsx +96 -0
- package/src/default/SearchResultsPane.tsx +173 -0
- package/src/default/SortSelect.tsx +77 -0
- package/src/default/UrlIssueNotice.tsx +37 -0
- package/src/default/index.ts +48 -0
- package/src/default/theme.tsx +66 -0
- package/src/default/types.ts +12 -0
- package/src/headless/FacetPanel.tsx +113 -0
- package/src/headless/RankingDisclosure.tsx +58 -0
- package/src/headless/SearchProvider.tsx +20 -0
- package/src/headless/SearchResults.tsx +149 -0
- package/src/headless/SearchStateProvider.tsx +175 -0
- package/src/i18n/errorsMap.ts +50 -0
- package/src/i18n/es.ts +110 -0
- package/src/i18n/generated/errors.es.gen.ts +69 -0
- package/src/i18n/generated/errors.gen.ts +214 -0
- package/src/i18n/generated/errors.json +423 -0
- package/src/i18n/generated/errors.ru.gen.ts +69 -0
- package/src/i18n/keys.ts +220 -0
- package/src/i18n/ru.ts +126 -0
- package/src/index.ts +154 -0
- package/src/model/context.tsx +28 -0
- package/src/model/queries.ts +79 -0
- package/src/model/queryKeys.ts +48 -0
- package/src/model/runtime.ts +33 -0
- package/src/nav/manifest.ts +53 -0
- package/src/router/index.ts +38 -0
- package/src/state/degradations.ts +75 -0
- package/src/state/facets.ts +219 -0
- package/src/state/urlState.ts +458 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/model/context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAErC,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAYlD,eAAO,MAAM,oBAAoB,EAAE,OAAO,CAAC,aAAa,GAAG,IAAI,CAC3C,CAAC;AAErB,eAAO,MAAM,gBAAgB,EAAE,MAAM,aAA8B,CAAC;AAEpE,eAAO,MAAM,YAAY,EAAE,MAAM,SAAsB,CAAC;AAExD,eAAO,MAAM,kBAAkB,EAAE,MAAM,SAAS,GAAG,IAAuB,CAAC;AAE3E,oEAAoE;AACpE,eAAO,MAAM,cAAc,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC,UAAU,CACzD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createModuleContext } from "@stapel/core";
|
|
2
|
+
/**
|
|
3
|
+
* The wired SearchRuntime shared through React context by
|
|
4
|
+
* `<SearchProvider>`. Hooks in `model/` and `headless/` read the singletons
|
|
5
|
+
* from here. One reviewed copy of this plumbing lives in `@stapel/core`
|
|
6
|
+
* (`createModuleContext`, slim wave §21/S2); this module binds it under the
|
|
7
|
+
* pair's public names.
|
|
8
|
+
*/
|
|
9
|
+
const kit = createModuleContext("Search");
|
|
10
|
+
export const SearchRuntimeContext = kit.RuntimeContext;
|
|
11
|
+
export const useSearchRuntime = kit.useRuntime;
|
|
12
|
+
export const useSearchApi = kit.useApi;
|
|
13
|
+
export const useSearchAnalytics = kit.useAnalytics;
|
|
14
|
+
/** @internal Re-exported as `<SearchProvider>` from `headless/`. */
|
|
15
|
+
export const ModuleProvider = kit.Provider;
|
|
16
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/model/context.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAKnD;;;;;;GAMG;AACH,MAAM,GAAG,GACP,mBAAmB,CAAgB,QAAQ,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,oBAAoB,GAC/B,GAAG,CAAC,cAAc,CAAC;AAErB,MAAM,CAAC,MAAM,gBAAgB,GAAwB,GAAG,CAAC,UAAU,CAAC;AAEpE,MAAM,CAAC,MAAM,YAAY,GAAoB,GAAG,CAAC,MAAM,CAAC;AAExD,MAAM,CAAC,MAAM,kBAAkB,GAA2B,GAAG,CAAC,YAAY,CAAC;AAE3E,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GACzB,GAAG,CAAC,QAAQ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { UseQueryResult } from "@tanstack/react-query";
|
|
2
|
+
import type { StapelApiError } from "@stapel/core";
|
|
3
|
+
import type { RankingResponse, SearchQueryState, SearchResponse } from "../api/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Read hooks over the search API (frontend-standard §2). Keys are namespaced
|
|
6
|
+
* (see `searchQueryKeys`).
|
|
7
|
+
*
|
|
8
|
+
* NOT SESSION-GATED, and that is the documented exception rather than an
|
|
9
|
+
* omission. Sibling pairs gate every read on `useActiveSessionReady` because
|
|
10
|
+
* their endpoints need a principal; these three are `AllowAny`, and core's own
|
|
11
|
+
* doc comment carves out exactly this case ("or be unconditionally safe
|
|
12
|
+
* pre-session, e.g. a public GET"). Gating them would make a storefront's
|
|
13
|
+
* catalogue wait for a login bootstrap that a visitor who will never sign in
|
|
14
|
+
* has no stake in — a blank shop front for the length of a token refresh.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* One keyset page of results for a search state.
|
|
18
|
+
*
|
|
19
|
+
* `placeholderData: keepPreviousData`, and it is not a nicety. Facets are
|
|
20
|
+
* DRILL-DOWN: the whole point of the panel is that choosing a value leaves its
|
|
21
|
+
* siblings with the counts you would get by switching to them. A panel that
|
|
22
|
+
* blanks to a spinner between every click cannot show that — the person sees
|
|
23
|
+
* the numbers vanish and reappear, which reads exactly like the naive facets
|
|
24
|
+
* this contract avoids. The previous answer therefore stays on screen while
|
|
25
|
+
* the next one is in flight, with `isFetching` telling a skin to dim it.
|
|
26
|
+
*
|
|
27
|
+
* The load discipline is intact: the FIRST load has nothing to keep and is
|
|
28
|
+
* `loading`, and a failure still lands as `failed` rather than leaving stale
|
|
29
|
+
* rows pretending to be current.
|
|
30
|
+
*
|
|
31
|
+
* `retry: false`: the refusals here are VERDICTS about the request, not blips
|
|
32
|
+
* — an unknown sort, a malformed range, a cursor past `MAX_RESULT_WINDOW`.
|
|
33
|
+
* Retrying a 400 three times only delays the moment the page can say which
|
|
34
|
+
* one it was. A real outage answers 503 and is retried by the person, through
|
|
35
|
+
* the bag's `refetch` (the spec's §7.4 negative leg: "we could not ask" plus a
|
|
36
|
+
* retry, never "nothing found").
|
|
37
|
+
*/
|
|
38
|
+
export declare function useSearchQuery(state: SearchQueryState, options?: {
|
|
39
|
+
readonly enabled?: boolean;
|
|
40
|
+
}): UseQueryResult<SearchResponse, StapelApiError>;
|
|
41
|
+
/**
|
|
42
|
+
* The P2B Art. 5 ranking disclosure.
|
|
43
|
+
*
|
|
44
|
+
* `staleTime: Infinity`: the scorer registry changes when somebody deploys,
|
|
45
|
+
* not while a person reads the page it explains.
|
|
46
|
+
*/
|
|
47
|
+
export declare function useRankingDisclosure(type?: string): UseQueryResult<RankingResponse, StapelApiError>;
|
|
48
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../src/model/queries.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACf,MAAM,iBAAiB,CAAC;AAIzB;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,gBAAgB,EACvB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACvC,cAAc,CAAC,cAAc,EAAE,cAAc,CAAC,CAUhD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,CAAC,EAAE,MAAM,GACZ,cAAc,CAAC,eAAe,EAAE,cAAc,CAAC,CAQjD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { searchQueryParams } from "../api/searchApi.js";
|
|
3
|
+
import { useSearchApi } from "./context.js";
|
|
4
|
+
import { searchQueryKeys } from "./queryKeys.js";
|
|
5
|
+
/**
|
|
6
|
+
* Read hooks over the search API (frontend-standard §2). Keys are namespaced
|
|
7
|
+
* (see `searchQueryKeys`).
|
|
8
|
+
*
|
|
9
|
+
* NOT SESSION-GATED, and that is the documented exception rather than an
|
|
10
|
+
* omission. Sibling pairs gate every read on `useActiveSessionReady` because
|
|
11
|
+
* their endpoints need a principal; these three are `AllowAny`, and core's own
|
|
12
|
+
* doc comment carves out exactly this case ("or be unconditionally safe
|
|
13
|
+
* pre-session, e.g. a public GET"). Gating them would make a storefront's
|
|
14
|
+
* catalogue wait for a login bootstrap that a visitor who will never sign in
|
|
15
|
+
* has no stake in — a blank shop front for the length of a token refresh.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* One keyset page of results for a search state.
|
|
19
|
+
*
|
|
20
|
+
* `placeholderData: keepPreviousData`, and it is not a nicety. Facets are
|
|
21
|
+
* DRILL-DOWN: the whole point of the panel is that choosing a value leaves its
|
|
22
|
+
* siblings with the counts you would get by switching to them. A panel that
|
|
23
|
+
* blanks to a spinner between every click cannot show that — the person sees
|
|
24
|
+
* the numbers vanish and reappear, which reads exactly like the naive facets
|
|
25
|
+
* this contract avoids. The previous answer therefore stays on screen while
|
|
26
|
+
* the next one is in flight, with `isFetching` telling a skin to dim it.
|
|
27
|
+
*
|
|
28
|
+
* The load discipline is intact: the FIRST load has nothing to keep and is
|
|
29
|
+
* `loading`, and a failure still lands as `failed` rather than leaving stale
|
|
30
|
+
* rows pretending to be current.
|
|
31
|
+
*
|
|
32
|
+
* `retry: false`: the refusals here are VERDICTS about the request, not blips
|
|
33
|
+
* — an unknown sort, a malformed range, a cursor past `MAX_RESULT_WINDOW`.
|
|
34
|
+
* Retrying a 400 three times only delays the moment the page can say which
|
|
35
|
+
* one it was. A real outage answers 503 and is retried by the person, through
|
|
36
|
+
* the bag's `refetch` (the spec's §7.4 negative leg: "we could not ask" plus a
|
|
37
|
+
* retry, never "nothing found").
|
|
38
|
+
*/
|
|
39
|
+
export function useSearchQuery(state, options) {
|
|
40
|
+
const api = useSearchApi();
|
|
41
|
+
const params = searchQueryParams(state);
|
|
42
|
+
return useQuery({
|
|
43
|
+
queryKey: searchQueryKeys.query(params),
|
|
44
|
+
queryFn: ({ signal }) => api.query(state, { signal }),
|
|
45
|
+
enabled: (options?.enabled ?? true) && state.type.length > 0,
|
|
46
|
+
placeholderData: keepPreviousData,
|
|
47
|
+
retry: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The P2B Art. 5 ranking disclosure.
|
|
52
|
+
*
|
|
53
|
+
* `staleTime: Infinity`: the scorer registry changes when somebody deploys,
|
|
54
|
+
* not while a person reads the page it explains.
|
|
55
|
+
*/
|
|
56
|
+
export function useRankingDisclosure(type) {
|
|
57
|
+
const api = useSearchApi();
|
|
58
|
+
return useQuery({
|
|
59
|
+
queryKey: searchQueryKeys.ranking(type),
|
|
60
|
+
queryFn: ({ signal }) => api.ranking(type, { signal }),
|
|
61
|
+
staleTime: Number.POSITIVE_INFINITY,
|
|
62
|
+
retry: false,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../src/model/queries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAMxD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAuB,EACvB,OAAwC;IAExC,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACxC,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,eAAe,CAAC,KAAK,CAAC,MAAM,CAAC;QACvC,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC;QACrD,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;QAC5D,eAAe,EAAE,gBAAgB;QACjC,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAAa;IAEb,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;IAC3B,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC;QACvC,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;QACtD,SAAS,EAAE,MAAM,CAAC,iBAAiB;QACnC,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespaced TanStack Query keys (frontend-standard §2 — namespaced keys).
|
|
3
|
+
* Everything under the `"search"` root so a host can invalidate the whole
|
|
4
|
+
* module or match a single read. Explicit tuple return types satisfy
|
|
5
|
+
* `--isolatedDeclarations`.
|
|
6
|
+
*
|
|
7
|
+
* THE KEY IS THE REQUEST. `query()` is keyed on the very object
|
|
8
|
+
* `searchQueryParams()` hands the client, not on a hand-picked subset — so a
|
|
9
|
+
* parameter that changes the URL and the request but not the key (the stale
|
|
10
|
+
* page bug: new filter, cached rows) cannot be written. TanStack hashes keys
|
|
11
|
+
* with sorted-key JSON, so two equal states hash equal regardless of the order
|
|
12
|
+
* the members were built in, and one cursor's page stays cached under its own
|
|
13
|
+
* key — which is what makes "back" instant.
|
|
14
|
+
*/
|
|
15
|
+
/** The wire query object a search read is keyed on. */
|
|
16
|
+
export type SearchQueryKeyParams = Readonly<Record<string, string | number | boolean | undefined | readonly (string | number | boolean)[]>>;
|
|
17
|
+
export declare const searchQueryKeys: {
|
|
18
|
+
readonly all: readonly ["search"];
|
|
19
|
+
query(params: SearchQueryKeyParams): readonly ["search", "query", SearchQueryKeyParams];
|
|
20
|
+
/** The P2B disclosure. `type` is optional on the endpoint; normalized to
|
|
21
|
+
* `null` so an absent type and an explicit `undefined` cannot cache twice. */
|
|
22
|
+
ranking(type?: string): readonly ["search", "ranking", string | null];
|
|
23
|
+
suggest(type: string, q: string, limit?: number): readonly ["search", "suggest", string, string, number | null];
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=queryKeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryKeys.d.ts","sourceRoot":"","sources":["../../src/model/queryKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CACzC,MAAM,CACJ,MAAM,EACJ,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,GACT,SAAS,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CACzC,CACF,CAAC;AAIF,eAAO,MAAM,eAAe,EAAE;IAC5B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,CACH,MAAM,EAAE,oBAAoB,GAC3B,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC;IACtD;kFAC8E;IAC9E,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACtE,OAAO,CACL,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CAMlE,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespaced TanStack Query keys (frontend-standard §2 — namespaced keys).
|
|
3
|
+
* Everything under the `"search"` root so a host can invalidate the whole
|
|
4
|
+
* module or match a single read. Explicit tuple return types satisfy
|
|
5
|
+
* `--isolatedDeclarations`.
|
|
6
|
+
*
|
|
7
|
+
* THE KEY IS THE REQUEST. `query()` is keyed on the very object
|
|
8
|
+
* `searchQueryParams()` hands the client, not on a hand-picked subset — so a
|
|
9
|
+
* parameter that changes the URL and the request but not the key (the stale
|
|
10
|
+
* page bug: new filter, cached rows) cannot be written. TanStack hashes keys
|
|
11
|
+
* with sorted-key JSON, so two equal states hash equal regardless of the order
|
|
12
|
+
* the members were built in, and one cursor's page stays cached under its own
|
|
13
|
+
* key — which is what makes "back" instant.
|
|
14
|
+
*/
|
|
15
|
+
const ROOT = "search";
|
|
16
|
+
export const searchQueryKeys = {
|
|
17
|
+
all: [ROOT],
|
|
18
|
+
query: (params) => [ROOT, "query", params],
|
|
19
|
+
ranking: (type) => [ROOT, "ranking", type ?? null],
|
|
20
|
+
suggest: (type, q, limit) => [ROOT, "suggest", type, q, limit ?? null],
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=queryKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryKeys.js","sourceRoot":"","sources":["../../src/model/queryKeys.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,MAAM,IAAI,GAAG,QAAiB,CAAC;AAE/B,MAAM,CAAC,MAAM,eAAe,GAaxB;IACF,GAAG,EAAE,CAAC,IAAI,CAAC;IACX,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC;IAC1C,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,IAAI,CAAC;IAClD,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;CACvE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CreateModuleRuntimeOptions, ModuleRuntime } from "@stapel/core";
|
|
2
|
+
import type { SearchApi } from "../api/searchApi.js";
|
|
3
|
+
/**
|
|
4
|
+
* The wired search runtime — core's `ModuleRuntime` bound to this pair's API
|
|
5
|
+
* (slim wave §21/S2). The returned `client` is what the host injects into
|
|
6
|
+
* core's `StapelConfigProvider` (as the default or the `"search"` module
|
|
7
|
+
* client), preserving the client-injection fork seam (frontend-standard §7.2).
|
|
8
|
+
*
|
|
9
|
+
* ANONYMOUS BY DESIGN. Every endpoint this pair calls is `AllowAny`, so a
|
|
10
|
+
* storefront's catalogue, category and search pages need nothing but:
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const runtime = createSearchRuntime({ baseUrl: "/search/api/v1/" });
|
|
14
|
+
* <SearchProvider runtime={runtime}>…</SearchProvider>
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* No session, no workspace id, no auth client. When a session DOES exist the
|
|
18
|
+
* host's auth runtime supplies the token on the shared client and the same
|
|
19
|
+
* calls carry it — this pair neither requires nor waits for one, which is why
|
|
20
|
+
* its read hooks are deliberately not gated on `useActiveSessionReady`.
|
|
21
|
+
*/
|
|
22
|
+
export type SearchRuntime = ModuleRuntime<SearchApi>;
|
|
23
|
+
export type CreateSearchRuntimeOptions = CreateModuleRuntimeOptions;
|
|
24
|
+
export declare function createSearchRuntime(options: CreateSearchRuntimeOptions): SearchRuntime;
|
|
25
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/model/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;AAErD,MAAM,MAAM,0BAA0B,GAAG,0BAA0B,CAAC;AAEpE,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,0BAA0B,GAClC,aAAa,CAEf"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createModuleRuntime } from "@stapel/core";
|
|
2
|
+
import { createSearchApi } from "../api/searchApi.js";
|
|
3
|
+
export function createSearchRuntime(options) {
|
|
4
|
+
return createModuleRuntime((client) => createSearchApi(client), options);
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/model/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AA0BtD,MAAM,UAAU,mBAAmB,CACjC,OAAmC;IAEnC,OAAO,mBAAmB,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This pair's contribution to the scripted-fullstack nav contract
|
|
3
|
+
* (`@stapel/core`'s `NavEntry`/`PackageNavManifest`). `scripts/gen-nav-
|
|
4
|
+
* manifest.mjs` reads `navEntries` below, stamps `package`/`version` from THIS
|
|
5
|
+
* package's own `package.json`, and emits
|
|
6
|
+
* `packages/search-react/nav-manifest.json` plus this package's slice of the
|
|
7
|
+
* root aggregate.
|
|
8
|
+
*
|
|
9
|
+
* Both entries declare `surface: "public"` EXPLICITLY. The derivation
|
|
10
|
+
* `requiresAuth ? "member" : "public"` would land on the same answer here, but
|
|
11
|
+
* the explicit declaration is what a public container can rely on: an entry
|
|
12
|
+
* that later gains `requiresAuth` for an unrelated reason must not silently
|
|
13
|
+
* fall out of the anonymous tree (`core/src/nav.ts`, `navEntrySurface`).
|
|
14
|
+
*
|
|
15
|
+
* `menuVisibleDefault: false` on both: the results page is a navigation TARGET
|
|
16
|
+
* reached from the header's search box, and the ranking disclosure is a
|
|
17
|
+
* footer link. Neither is a menu item — the same treatment `auth.login` gets.
|
|
18
|
+
*/
|
|
19
|
+
import type { NavEntry } from "@stapel/core";
|
|
20
|
+
export declare const navEntries: readonly NavEntry[];
|
|
21
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/nav/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7C,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAgCzC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const navEntries = [
|
|
2
|
+
{
|
|
3
|
+
id: "search.results",
|
|
4
|
+
labelKey: "search.results.title",
|
|
5
|
+
icon: "SearchOutlined",
|
|
6
|
+
route: { path: "/s" },
|
|
7
|
+
component: { export: "SearchPage", subpath: "default" },
|
|
8
|
+
placement: { level: "top" },
|
|
9
|
+
menuVisibleDefault: false,
|
|
10
|
+
requiresAuth: false,
|
|
11
|
+
surface: "public",
|
|
12
|
+
order: 10,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
// The P2B Art. 5 disclosure as a PAGE, not a link to raw JSON. The
|
|
16
|
+
// regulation asks for a plain-language description of the main ranking
|
|
17
|
+
// parameters; `GET /search/api/v1/ranking` is the data behind it, and
|
|
18
|
+
// handing a visitor that JSON would satisfy nobody's reading of it. The
|
|
19
|
+
// route is `/ranking-disclosure`, not `/ranking`, so the SPA path cannot
|
|
20
|
+
// be mistaken — by a person or by `stapel/no-string-paths` — for the API
|
|
21
|
+
// operation of the same name.
|
|
22
|
+
id: "search.ranking",
|
|
23
|
+
labelKey: "search.ranking.title",
|
|
24
|
+
icon: "OrderedListOutlined",
|
|
25
|
+
route: { path: "/ranking-disclosure" },
|
|
26
|
+
component: { export: "RankingDisclosurePane", subpath: "default" },
|
|
27
|
+
placement: { level: "top" },
|
|
28
|
+
menuVisibleDefault: false,
|
|
29
|
+
requiresAuth: false,
|
|
30
|
+
surface: "public",
|
|
31
|
+
order: 11,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/nav/manifest.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,UAAU,GAAwB;IAC7C;QACE,EAAE,EAAE,gBAAgB;QACpB,QAAQ,EAAE,sBAAsB;QAChC,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;QACrB,SAAS,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE;QACvD,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;QAC3B,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,KAAK;QACnB,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,EAAE;KACV;IACD;QACE,mEAAmE;QACnE,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,8BAA8B;QAC9B,EAAE,EAAE,gBAAgB;QACpB,QAAQ,EAAE,sBAAsB;QAChC,IAAI,EAAE,qBAAqB;QAC3B,KAAK,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE;QACtC,SAAS,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE;QAClE,SAAS,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE;QAC3B,kBAAkB,EAAE,KAAK;QACzB,YAAY,EAAE,KAAK;QACnB,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,EAAE;KACV;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SearchParamsAdapter } from "../headless/SearchStateProvider.js";
|
|
2
|
+
/**
|
|
3
|
+
* react-router's `useSearchParams()` as a {@link SearchParamsAdapter}.
|
|
4
|
+
*
|
|
5
|
+
* `replace` is forwarded rather than dropped: a facet click PUSHES, so Back
|
|
6
|
+
* removes exactly that filter (the spec's §4.2 acceptance), while typing in
|
|
7
|
+
* the search box replaces so one history entry per keystroke never happens.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useRouterSearchParams(): SearchParamsAdapter;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAE9E;;;;;;GAMG;AACH,wBAAgB,qBAAqB,IAAI,mBAAmB,CAW3D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@stapel/search-react/router` — the react-router v7 binding for the URL
|
|
3
|
+
* state, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* A SEPARATE entry point on purpose. URL state is a router concern, and the
|
|
6
|
+
* pair does not own the host's router: a Next.js app, a hash router, or a
|
|
7
|
+
* server render that reads `new URL(request.url).searchParams` all satisfy
|
|
8
|
+
* `SearchParamsAdapter` without this file. Keeping the binding here means the
|
|
9
|
+
* main entry pulls no router at all (size-limit proves it), and a host that
|
|
10
|
+
* uses react-router gets the call it cannot make wrong:
|
|
11
|
+
*
|
|
12
|
+
* ```tsx
|
|
13
|
+
* <SearchStateProvider adapter={useRouterSearchParams()} defaultType="listing">
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import { useMemo } from "react";
|
|
17
|
+
import { useSearchParams } from "react-router";
|
|
18
|
+
/**
|
|
19
|
+
* react-router's `useSearchParams()` as a {@link SearchParamsAdapter}.
|
|
20
|
+
*
|
|
21
|
+
* `replace` is forwarded rather than dropped: a facet click PUSHES, so Back
|
|
22
|
+
* removes exactly that filter (the spec's §4.2 acceptance), while typing in
|
|
23
|
+
* the search box replaces so one history entry per keystroke never happens.
|
|
24
|
+
*/
|
|
25
|
+
export function useRouterSearchParams() {
|
|
26
|
+
const [params, setParams] = useSearchParams();
|
|
27
|
+
return useMemo(() => ({
|
|
28
|
+
params,
|
|
29
|
+
setParams: (next, options) => {
|
|
30
|
+
setParams(next, { replace: options?.replace ?? false });
|
|
31
|
+
},
|
|
32
|
+
}), [params, setParams]);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/router/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,eAAe,EAAE,CAAC;IAC9C,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC;QACL,MAAM;QACN,SAAS,EAAE,CAAC,IAAqB,EAAE,OAAwC,EAAE,EAAE;YAC7E,SAAS,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,CAAC,CAAC;QAC1D,CAAC;KACF,CAAC,EACF,CAAC,MAAM,EAAE,SAAS,CAAC,CACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `degraded[]` — the backend declaring, per query, what the configured engine
|
|
3
|
+
* could not do. The UI's job is to say it, not to smooth it over: a result
|
|
4
|
+
* page that quietly drops "typo tolerance is off" is a page that looks like a
|
|
5
|
+
* complete answer and is not one.
|
|
6
|
+
*
|
|
7
|
+
* The literals are produced in `stapel-search/services.py::_degradations` plus
|
|
8
|
+
* whatever the backend and the facet counter contribute; the concatenation is
|
|
9
|
+
* NOT de-duplicated upstream, so `"typo_tolerance"` can genuinely arrive
|
|
10
|
+
* twice. {@link parseDegradations} de-duplicates by the raw literal.
|
|
11
|
+
*
|
|
12
|
+
* An unrecognised literal is kept, not dropped — the honest failure mode for
|
|
13
|
+
* "the backend degraded in a way this build predates" is a generic sentence
|
|
14
|
+
* with the literal beside it, not silence.
|
|
15
|
+
*/
|
|
16
|
+
import type { SearchDegradation, SearchDegradationKind } from "../api/types.js";
|
|
17
|
+
/** i18n key per kind (`search.degraded.*`). */
|
|
18
|
+
export declare function degradationMessageKey(kind: SearchDegradationKind): string;
|
|
19
|
+
/** Parse and de-duplicate the envelope's `degraded[]`. */
|
|
20
|
+
export declare function parseDegradations(degraded: readonly string[] | undefined): readonly SearchDegradation[];
|
|
21
|
+
/**
|
|
22
|
+
* Does this set of degradations mean the RESULT COUNT is an estimate?
|
|
23
|
+
*
|
|
24
|
+
* Two independent signals say so and a skin must not have to know which:
|
|
25
|
+
* the envelope's own `exact_total: false`, and the `"exact_total"`
|
|
26
|
+
* degradation. They agree in practice; treating either as decisive is what
|
|
27
|
+
* keeps "≈1 200" from ever being rendered as "1 200".
|
|
28
|
+
*/
|
|
29
|
+
export declare function countIsEstimate(exactTotal: boolean, degradations: readonly SearchDegradation[]): boolean;
|
|
30
|
+
//# sourceMappingURL=degradations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degradations.d.ts","sourceRoot":"","sources":["../../src/state/degradations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAYhF,+CAA+C;AAC/C,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAEzE;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACtC,SAAS,iBAAiB,EAAE,CAwB9B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,OAAO,EACnB,YAAY,EAAE,SAAS,iBAAiB,EAAE,GACzC,OAAO,CAET"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const SCORER_PREFIX = "scorer:";
|
|
2
|
+
const KNOWN = {
|
|
3
|
+
typo_tolerance: "typo_tolerance",
|
|
4
|
+
phrase_synonyms: "phrase_synonyms",
|
|
5
|
+
exact_total: "exact_total",
|
|
6
|
+
exact_facet_counts: "exact_facet_counts",
|
|
7
|
+
category_rollup: "category_rollup",
|
|
8
|
+
};
|
|
9
|
+
/** i18n key per kind (`search.degraded.*`). */
|
|
10
|
+
export function degradationMessageKey(kind) {
|
|
11
|
+
return `search.degraded.${kind}`;
|
|
12
|
+
}
|
|
13
|
+
/** Parse and de-duplicate the envelope's `degraded[]`. */
|
|
14
|
+
export function parseDegradations(degraded) {
|
|
15
|
+
if (degraded === undefined)
|
|
16
|
+
return [];
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const raw of degraded) {
|
|
20
|
+
if (seen.has(raw))
|
|
21
|
+
continue;
|
|
22
|
+
seen.add(raw);
|
|
23
|
+
if (raw.startsWith(SCORER_PREFIX)) {
|
|
24
|
+
out.push({
|
|
25
|
+
kind: "scorer",
|
|
26
|
+
raw,
|
|
27
|
+
scorer: raw.slice(SCORER_PREFIX.length),
|
|
28
|
+
messageKey: degradationMessageKey("scorer"),
|
|
29
|
+
});
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const kind = KNOWN[raw];
|
|
33
|
+
out.push({
|
|
34
|
+
kind: kind ?? "unknown",
|
|
35
|
+
raw,
|
|
36
|
+
messageKey: degradationMessageKey(kind ?? "unknown"),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Does this set of degradations mean the RESULT COUNT is an estimate?
|
|
43
|
+
*
|
|
44
|
+
* Two independent signals say so and a skin must not have to know which:
|
|
45
|
+
* the envelope's own `exact_total: false`, and the `"exact_total"`
|
|
46
|
+
* degradation. They agree in practice; treating either as decisive is what
|
|
47
|
+
* keeps "≈1 200" from ever being rendered as "1 200".
|
|
48
|
+
*/
|
|
49
|
+
export function countIsEstimate(exactTotal, degradations) {
|
|
50
|
+
return !exactTotal || degradations.some((d) => d.kind === "exact_total");
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=degradations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"degradations.js","sourceRoot":"","sources":["../../src/state/degradations.ts"],"names":[],"mappings":"AAiBA,MAAM,aAAa,GAAG,SAAS,CAAC;AAEhC,MAAM,KAAK,GAAoD;IAC7D,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,WAAW,EAAE,aAAa;IAC1B,kBAAkB,EAAE,oBAAoB;IACxC,eAAe,EAAE,iBAAiB;CACnC,CAAC;AAEF,+CAA+C;AAC/C,MAAM,UAAU,qBAAqB,CAAC,IAA2B;IAC/D,OAAO,mBAAmB,IAAI,EAAE,CAAC;AACnC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,iBAAiB,CAC/B,QAAuC;IAEvC,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,GAAG,CAAC,IAAI,CAAC;gBACP,IAAI,EAAE,QAAQ;gBACd,GAAG;gBACH,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC;gBACvC,UAAU,EAAE,qBAAqB,CAAC,QAAQ,CAAC;aAC5C,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,IAAI,IAAI,SAAS;YACvB,GAAG;YACH,UAAU,EAAE,qBAAqB,CAAC,IAAI,IAAI,SAAS,CAAC;SACrD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,UAAmB,EACnB,YAA0C;IAE1C,OAAO,CAAC,UAAU,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { FeatureDef } from "@stapel/attributes-react";
|
|
2
|
+
import type { FacetMeta, SearchQueryState } from "../api/types.js";
|
|
3
|
+
/** One value of one facet. */
|
|
4
|
+
export interface FacetOption {
|
|
5
|
+
readonly value: string;
|
|
6
|
+
/**
|
|
7
|
+
* How many candidates carry this value with the slug's own filter removed —
|
|
8
|
+
* or `null` when the slug was not counted (skipped, or facets off).
|
|
9
|
+
*/
|
|
10
|
+
readonly count: number | null;
|
|
11
|
+
/** Resolved through the category schema; the raw value when there is none. */
|
|
12
|
+
readonly label: string;
|
|
13
|
+
readonly selected: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** One facet slug, with its options. */
|
|
16
|
+
export interface FacetGroup {
|
|
17
|
+
readonly slug: string;
|
|
18
|
+
/** The feature's display name (translated when it is a key), else the slug. */
|
|
19
|
+
readonly label: string;
|
|
20
|
+
/** The category-schema entry behind the slug, when the host supplied one. */
|
|
21
|
+
readonly feature: FeatureDef | undefined;
|
|
22
|
+
/** `false` when the server skipped this slug — counts are `null`. */
|
|
23
|
+
readonly counted: boolean;
|
|
24
|
+
readonly options: readonly FacetOption[];
|
|
25
|
+
/** The values currently chosen for this slug (URL state, not the response). */
|
|
26
|
+
readonly selected: readonly string[];
|
|
27
|
+
}
|
|
28
|
+
export interface BuildFacetGroupsInput {
|
|
29
|
+
/** The envelope's `facets`: `{slug: {value: count}}`. */
|
|
30
|
+
readonly facets: Readonly<Record<string, Readonly<Record<string, number>>>>;
|
|
31
|
+
readonly meta: FacetMeta;
|
|
32
|
+
/** Current URL state — the source of what is selected. */
|
|
33
|
+
readonly state: SearchQueryState;
|
|
34
|
+
/** The category's feature schema, for labels and option order. */
|
|
35
|
+
readonly categoryFeatures?: readonly FeatureDef[];
|
|
36
|
+
/** Translator for label keys. */
|
|
37
|
+
readonly t?: (key: string) => string;
|
|
38
|
+
/** BCP-47 tag, forwarded to `formatFeatureValue` for `date` options. */
|
|
39
|
+
readonly locale?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A facet value as a person reads it, through
|
|
43
|
+
* `@stapel/attributes-react`'s `formatFeatureValue` — the same formatter the
|
|
44
|
+
* card and the spec table use, so a value cannot read one way in the filter
|
|
45
|
+
* and another way in the result.
|
|
46
|
+
*
|
|
47
|
+
* The wire carries facet values as STRINGS (they are index terms), so the raw
|
|
48
|
+
* value is coerced back into the shape the type's formatter expects before
|
|
49
|
+
* being handed over. `select` is a list even for one value
|
|
50
|
+
* (`types/select/dto.py`), `date` is a Unix timestamp integer, numbers are
|
|
51
|
+
* numbers. A type the formatter cannot read gives back the raw value — never
|
|
52
|
+
* an empty label, which would leave an unnameable checkbox on screen.
|
|
53
|
+
*/
|
|
54
|
+
export declare function facetOptionLabel(feature: FeatureDef | undefined, value: string, options?: {
|
|
55
|
+
readonly t?: (key: string) => string;
|
|
56
|
+
readonly locale?: string;
|
|
57
|
+
}): string;
|
|
58
|
+
/**
|
|
59
|
+
* Fold the response's counts, the URL's selections and the category schema
|
|
60
|
+
* into the panel's groups.
|
|
61
|
+
*
|
|
62
|
+
* Groups appear for every counted slug, every skipped slug, and every slug the
|
|
63
|
+
* person has filtered on — the last one matters: a filter whose slug fell out
|
|
64
|
+
* of the plan must stay visible, or it becomes a constraint with no control to
|
|
65
|
+
* remove it.
|
|
66
|
+
*/
|
|
67
|
+
export declare function buildFacetGroups(input: BuildFacetGroupsInput): readonly FacetGroup[];
|
|
68
|
+
//# sourceMappingURL=facets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../src/state/facets.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnE,8BAA8B;AAC9B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAAC;IACzC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,qBAAqB;IACpC,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAClD,iCAAiC;IACjC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAgCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3E,MAAM,CAkCR;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,SAAS,UAAU,EAAE,CA2DpF"}
|