@stapel/vocabularies-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 +52 -0
- package/LICENSE +21 -0
- package/MODULE.md +92 -0
- package/README.md +173 -0
- package/dist/api/extensions.d.ts +8 -0
- package/dist/api/extensions.d.ts.map +1 -0
- package/dist/api/extensions.js +8 -0
- package/dist/api/extensions.js.map +1 -0
- package/dist/api/generated/schema.d.ts +234 -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/types.d.ts +23 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/types.js +2 -0
- package/dist/api/types.js.map +1 -0
- package/dist/api/vocabulariesApi.d.ts +14 -0
- package/dist/api/vocabulariesApi.d.ts.map +1 -0
- package/dist/api/vocabulariesApi.js +4 -0
- package/dist/api/vocabulariesApi.js.map +1 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +118 -0
- package/dist/client.js.map +1 -0
- package/dist/default/VocabularyTermSelect.d.ts +24 -0
- package/dist/default/VocabularyTermSelect.d.ts.map +1 -0
- package/dist/default/VocabularyTermSelect.js +75 -0
- package/dist/default/VocabularyTermSelect.js.map +1 -0
- package/dist/default/index.d.ts +16 -0
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +14 -0
- package/dist/default/index.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/flows/errors.d.ts +12 -0
- package/dist/flows/errors.d.ts.map +1 -0
- package/dist/flows/errors.js +13 -0
- package/dist/flows/errors.js.map +1 -0
- package/dist/flows/registry.d.ts +22 -0
- package/dist/flows/registry.d.ts.map +1 -0
- package/dist/flows/registry.js +20 -0
- package/dist/flows/registry.js.map +1 -0
- package/dist/headless/VocabulariesProvider.d.ts +19 -0
- package/dist/headless/VocabulariesProvider.d.ts.map +1 -0
- package/dist/headless/VocabulariesProvider.js +15 -0
- package/dist/headless/VocabulariesProvider.js.map +1 -0
- package/dist/i18n/errorsMap.d.ts +10 -0
- package/dist/i18n/errorsMap.d.ts.map +1 -0
- package/dist/i18n/errorsMap.js +20 -0
- package/dist/i18n/errorsMap.js.map +1 -0
- package/dist/i18n/es.d.ts +21 -0
- package/dist/i18n/es.d.ts.map +1 -0
- package/dist/i18n/es.js +38 -0
- package/dist/i18n/es.js.map +1 -0
- package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.es.gen.js +58 -0
- package/dist/i18n/generated/errors.es.gen.js.map +1 -0
- package/dist/i18n/generated/errors.gen.d.ts +305 -0
- package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.gen.js +156 -0
- package/dist/i18n/generated/errors.gen.js.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
- package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
- package/dist/i18n/generated/errors.ru.gen.js +58 -0
- package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
- package/dist/i18n/keys.d.ts +33 -0
- package/dist/i18n/keys.d.ts.map +1 -0
- package/dist/i18n/keys.js +44 -0
- package/dist/i18n/keys.js.map +1 -0
- package/dist/i18n/ru.d.ts +26 -0
- package/dist/i18n/ru.d.ts.map +1 -0
- package/dist/i18n/ru.js +43 -0
- package/dist/i18n/ru.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -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/queryKeys.d.ts +5 -0
- package/dist/model/queryKeys.d.ts.map +1 -0
- package/dist/model/queryKeys.js +28 -0
- package/dist/model/queryKeys.js.map +1 -0
- package/dist/model/runtime.d.ts +17 -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/model/useTermLabels.d.ts +26 -0
- package/dist/model/useTermLabels.d.ts.map +1 -0
- package/dist/model/useTermLabels.js +58 -0
- package/dist/model/useTermLabels.js.map +1 -0
- package/dist/model/useTermSearch.d.ts +24 -0
- package/dist/model/useTermSearch.d.ts.map +1 -0
- package/dist/model/useTermSearch.js +85 -0
- package/dist/model/useTermSearch.js.map +1 -0
- package/llms.txt +79 -0
- package/manifest.json +466 -0
- package/package.json +108 -0
- package/src/analytics/generated/events.json +7 -0
- package/src/api/extensions.ts +7 -0
- package/src/api/generated/schema.ts +236 -0
- package/src/api/types.ts +27 -0
- package/src/api/vocabulariesApi.ts +17 -0
- package/src/client.ts +176 -0
- package/src/default/VocabularyTermSelect.tsx +147 -0
- package/src/default/index.ts +15 -0
- package/src/default/types.ts +11 -0
- package/src/flows/errors.ts +16 -0
- package/src/flows/registry.ts +32 -0
- package/src/headless/VocabulariesProvider.tsx +20 -0
- package/src/i18n/errorsMap.ts +31 -0
- package/src/i18n/es.ts +49 -0
- package/src/i18n/generated/errors.es.gen.ts +64 -0
- package/src/i18n/generated/errors.gen.ts +187 -0
- package/src/i18n/generated/errors.json +351 -0
- package/src/i18n/generated/errors.ru.gen.ts +64 -0
- package/src/i18n/keys.ts +53 -0
- package/src/i18n/ru.ts +54 -0
- package/src/index.ts +92 -0
- package/src/model/context.tsx +28 -0
- package/src/model/queryKeys.ts +35 -0
- package/src/model/runtime.ts +25 -0
- package/src/model/useTermLabels.ts +72 -0
- package/src/model/useTermSearch.ts +118 -0
- package/tsconfig.json +26 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# @stapel/vocabularies-react
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d1125bc: The React pair for stapel-vocabularies 0.1.0 — first release.
|
|
8
|
+
|
|
9
|
+
A ref feature carries a POINTER to a vocabulary level instead of an options
|
|
10
|
+
list, because the lists are the wrong size to inline into a category schema
|
|
11
|
+
(529 phone vendors → 14 962 models; 107 049 car modifications). The terms
|
|
12
|
+
therefore arrive over a second wire, and this package is the client for it.
|
|
13
|
+
|
|
14
|
+
**The seam, satisfied structurally.** `createVocabularyClient({ baseUrl, fetch? })`
|
|
15
|
+
returns the two functions `@stapel/attributes-react` declares as
|
|
16
|
+
`VocabularyClient` and hands to `<VocabularyClientProvider>`. Neither package
|
|
17
|
+
imports the other — in source or in `package.json` — so the two L2 pairs stay
|
|
18
|
+
independently releasable, and a host with no vocabularies module at all can
|
|
19
|
+
supply its own two functions and keep every ref editor working.
|
|
20
|
+
`test/clientShape.test.ts` holds a hand-transcribed copy of the upstream
|
|
21
|
+
interface and ASSIGNS this client to it, so a drift on either side is a red
|
|
22
|
+
build here rather than a hole a storefront finds at the composer.
|
|
23
|
+
|
|
24
|
+
It is a bare `fetch` client on purpose: both endpoints are public reads
|
|
25
|
+
(`ReadOnlyOrStaff`, ETag'd on the vocabulary revision, no session), and
|
|
26
|
+
anything needing React context could not be passed to a `value` prop at a
|
|
27
|
+
container's composition root, which is the one call site the seam exists for.
|
|
28
|
+
`parent` is OMITTED rather than sent empty (`parent=` asks for the children of
|
|
29
|
+
a term whose code is the empty string); `resolve` splits at 200 codes, because
|
|
30
|
+
the server ignores the tail of a longer list instead of refusing it; a refusal
|
|
31
|
+
folds into core's one dialect and an abort is rethrown AS an abort, so a
|
|
32
|
+
superseded keystroke never looks like a dead backend.
|
|
33
|
+
|
|
34
|
+
**Hooks.** `useTermSearch` debounces 250 ms, aborts what it supersedes, fetches
|
|
35
|
+
the first page immediately on open (once per level and parent — antd reports a
|
|
36
|
+
dropdown as opening on every keystroke) and empties itself when the parent
|
|
37
|
+
moves. It is deliberately not a query. `useTermLabels` is: the same codes give
|
|
38
|
+
the same labels until the vocabulary's revision moves, so it is keyed through
|
|
39
|
+
`vocabulariesQueryKeys` and hands out a `LoadState` rather than a bare map —
|
|
40
|
+
`{}` would say "these codes have no labels" in the same words as "nobody has
|
|
41
|
+
asked yet".
|
|
42
|
+
|
|
43
|
+
**`/default` `<VocabularyTermSelect>`.** The same control the `ref_select`
|
|
44
|
+
editor draws, shipped on its own for the places a composer is not: a facet
|
|
45
|
+
filter, an admin form, a bulk-edit row. `filterOption={false}` — the options
|
|
46
|
+
ARE the answer to the current query. A held code is resolved to its label and
|
|
47
|
+
stays pickable even when the current page does not contain it. No client is a
|
|
48
|
+
LOUD state: the notice, not an empty dropdown.
|
|
49
|
+
|
|
50
|
+
Ships en/ru/es. No nav entry and no overview screen: this pair ships a control
|
|
51
|
+
other pairs' forms mount, not a destination (the `@stapel/attributes-react`
|
|
52
|
+
precedent).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stapel contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/MODULE.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @stapel/vocabularies-react — module guide
|
|
2
|
+
|
|
3
|
+
The React pair for **stapel-vocabularies**. Human companion to the generated
|
|
4
|
+
`llms.txt` (agent context) and `manifest.json` (machine catalog); `README.md` is
|
|
5
|
+
the usage entry point.
|
|
6
|
+
|
|
7
|
+
## What this pair is for
|
|
8
|
+
|
|
9
|
+
One sentence: **a ref feature points at a vocabulary level instead of carrying
|
|
10
|
+
options, and this is how the terms get to the browser.**
|
|
11
|
+
|
|
12
|
+
Everything below follows from the sizes. A category schema that inlined 14 962
|
|
13
|
+
phone models would be a response nobody can cache and a form nobody can open, so
|
|
14
|
+
`stapel-attributes` 0.5 made `ref_select` / `ref_hierarchical_select` carry a
|
|
15
|
+
pointer, and the terms travel on their own wire. That wire has exactly two reads
|
|
16
|
+
a form needs — search a level, resolve some codes — and they are the whole
|
|
17
|
+
public surface of this package.
|
|
18
|
+
|
|
19
|
+
## Layers
|
|
20
|
+
|
|
21
|
+
- **client.ts** — `createVocabularyClient({ baseUrl, fetch?, limit? })`. A bare
|
|
22
|
+
`fetch` client on purpose: the seam it satisfies is two async functions that a
|
|
23
|
+
container hands to a React *value* prop, so anything requiring context could
|
|
24
|
+
not be passed at all. Both endpoints are public reads (`ReadOnlyOrStaff`,
|
|
25
|
+
ETag'd on the vocabulary revision, no session), so there is no token to carry;
|
|
26
|
+
a host that needs headers passes its own `fetch`. Refusals are folded into
|
|
27
|
+
core's one dialect (`toStapelApiError`) at the single rethrow point — except
|
|
28
|
+
an abort, which is rethrown as itself, because a superseded keystroke folded
|
|
29
|
+
into an API error looks exactly like a dead backend.
|
|
30
|
+
- **api/** — `createVocabulariesApi(client)` plus type aliases over the
|
|
31
|
+
package-LOCAL generated `components["schemas"]` (`Vocabulary`, `Level`,
|
|
32
|
+
`Term`, `TermPage`), produced by `pnpm gen:api` from stapel-vocabularies's own
|
|
33
|
+
`docs/schema.json`. Never parallel hand-written bodies.
|
|
34
|
+
- **model/** — `useTermSearch` (debounced, superseding, parent-scoped),
|
|
35
|
+
`useTermLabels` (a TanStack query keyed through `vocabulariesQueryKeys`,
|
|
36
|
+
handing out a `LoadState`), `termLabel`, and the standard runtime wiring.
|
|
37
|
+
The split between the two is a decision, not an accident: see README.
|
|
38
|
+
- **flows/** — `toFlowError` + the zero-flow `VOCABULARIES_FLOWS` shim. The
|
|
39
|
+
module annotates no `@flow_step` and its `docs/flows.json` is literally `[]`
|
|
40
|
+
(the stapel-categories precedent), so there is no machine here and none is
|
|
41
|
+
pending.
|
|
42
|
+
- **headless/** — `<VocabulariesProvider>`, the standard runtime context. Not
|
|
43
|
+
needed by the seam client; needed to reach the catalogue endpoints through
|
|
44
|
+
this pair's `StapelClient`.
|
|
45
|
+
- **default/** — `<VocabularyTermSelect/>`, the antd control, on the opt-in
|
|
46
|
+
`./default` subpath.
|
|
47
|
+
- **i18n/** — `VOCABULARIES_I18N_KEYS` + en/ru/es. The generated backend error
|
|
48
|
+
bundles are merged in so every `error.*` code has a fallback; the three codes
|
|
49
|
+
stapel-vocabularies owns are authored by hand until upstream ships a
|
|
50
|
+
`translations/` directory.
|
|
51
|
+
|
|
52
|
+
## Extension seams (frontend-standard §7)
|
|
53
|
+
|
|
54
|
+
- **The vocabulary seam itself.** `VocabularyClient` is declared by
|
|
55
|
+
`@stapel/attributes-react` and satisfied here STRUCTURALLY — no import in
|
|
56
|
+
either direction, in source or in `package.json`, asserted by
|
|
57
|
+
`test/clientShape.test.ts`. A host may replace this implementation entirely
|
|
58
|
+
with its own two functions.
|
|
59
|
+
- **Transport.** `createVocabularyClient({ fetch })` takes a host's own
|
|
60
|
+
instrumented/authenticated `fetch`.
|
|
61
|
+
- **The control.** A host that draws its own can use `useTermSearch` /
|
|
62
|
+
`useTermLabels` and never import `./default`.
|
|
63
|
+
- Client injection for the standard layers is via `<VocabulariesProvider>` /
|
|
64
|
+
core's `StapelConfigProvider` per-module override, as in every pair.
|
|
65
|
+
|
|
66
|
+
## Deliberately absent
|
|
67
|
+
|
|
68
|
+
- **A nav entry and an overview screen.** This pair ships a control that other
|
|
69
|
+
pairs' forms mount, not a destination — the same shape as
|
|
70
|
+
`@stapel/attributes-react`, `@stapel/cdn-react` and `@stapel/geo-react`. The
|
|
71
|
+
scaffold's placeholder panel was removed rather than shipped: a menu item
|
|
72
|
+
leading to a card that says "nothing here yet" is a promise the package does
|
|
73
|
+
not keep.
|
|
74
|
+
- **A cascading control.** `ref_hierarchical_select` is drawn by
|
|
75
|
+
`@stapel/attributes-react`'s own `Cascader` editor, which reads its columns
|
|
76
|
+
from the feature's `levels`. Duplicating it here would be a second answer to
|
|
77
|
+
one question.
|
|
78
|
+
- **Vocabulary administration.** Loading and editing vocabularies is
|
|
79
|
+
`manage.py load_vocabulary` and Django admin — data plumbing over reviewed
|
|
80
|
+
fixtures, not a screen.
|
|
81
|
+
|
|
82
|
+
## Wire contract read here
|
|
83
|
+
|
|
84
|
+
| Verb | Path | Used by |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| GET | `vocabularies/{slug}/terms/?level=&parent=&q=&limit=&offset=` | `client.search`, `useTermSearch` |
|
|
87
|
+
| GET | `vocabularies/{slug}/terms/resolve/?level=&codes=` | `client.resolve`, `useTermLabels` |
|
|
88
|
+
| GET | `vocabularies/`, `vocabularies/{slug}/` | typed in `api/types.ts`; no hook yet |
|
|
89
|
+
|
|
90
|
+
Errors this pair can surface: `error.404.vocabularies_vocabulary_not_found`,
|
|
91
|
+
`error.404.vocabularies_level_not_found`, `error.400.vocabularies_bad_parent`,
|
|
92
|
+
plus the 42 cross-cutting `stapel_core` codes.
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# @stapel/vocabularies-react
|
|
2
|
+
|
|
3
|
+
Reference vocabularies too large to inline into a category schema: term
|
|
4
|
+
typeahead and code-to-label resolve behind the `VocabularyClient` seam, plus the
|
|
5
|
+
antd term select.
|
|
6
|
+
|
|
7
|
+
## Why this package exists
|
|
8
|
+
|
|
9
|
+
A category's feature schema carries its own options — except when it cannot. A
|
|
10
|
+
phone's `Vendor` is 529 rows and its `Model` is 14 962; a car's `Modification`
|
|
11
|
+
is 107 049. `stapel-attributes` 0.5 therefore added two value types,
|
|
12
|
+
`ref_select` and `ref_hierarchical_select`, whose config carries a **pointer**
|
|
13
|
+
(`optionsRef {vocabulary, level, parentFeature?}`) instead of a list, and
|
|
14
|
+
`GET /categories/{id}/features` sends the pointer as-is.
|
|
15
|
+
|
|
16
|
+
The terms then arrive over a second wire — `stapel-vocabularies` — and this
|
|
17
|
+
package is the client for it.
|
|
18
|
+
|
|
19
|
+
## The seam
|
|
20
|
+
|
|
21
|
+
`@stapel/attributes-react` **declares** the interface; this package
|
|
22
|
+
**implements** it, structurally, and neither imports the other. Two L2 pairs do
|
|
23
|
+
not depend on each other, so both stay independently releasable; the container
|
|
24
|
+
is what joins them:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { createVocabularyClient } from "@stapel/vocabularies-react";
|
|
28
|
+
import { VocabularyClientProvider } from "@stapel/attributes-react";
|
|
29
|
+
|
|
30
|
+
const vocabularies = createVocabularyClient({ baseUrl: "/vocabularies/api/v1/" });
|
|
31
|
+
|
|
32
|
+
<VocabularyClientProvider value={vocabularies}>
|
|
33
|
+
<ListingComposerPage … />
|
|
34
|
+
</VocabularyClientProvider>;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`test/clientShape.test.ts` holds a hand-transcribed copy of the upstream
|
|
38
|
+
interface and assigns this client to it, so a drift on either side is a red
|
|
39
|
+
build here rather than a hole a storefront finds.
|
|
40
|
+
|
|
41
|
+
The interface is two functions and nothing else — no query client, no auth
|
|
42
|
+
runtime, no provider — which is what lets a host with no vocabularies module at
|
|
43
|
+
all supply its own two functions (an in-memory table, an existing search
|
|
44
|
+
endpoint) and keep every ref editor working.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
search(vocabulary, level, query, parent?, signal?): Promise<readonly VocabularyTerm[]>
|
|
48
|
+
resolve(vocabulary, level, codes): Promise<Readonly<Record<string, string>>>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`search` is `GET vocabularies/{slug}/terms/?level=&parent=&q=&limit=` —
|
|
52
|
+
`parent` is OMITTED, never sent empty, because `parent=` asks for the children
|
|
53
|
+
of a term whose code is the empty string. `resolve` is
|
|
54
|
+
`GET vocabularies/{slug}/terms/resolve/?level=&codes=`, split into batches of
|
|
55
|
+
200 because the server ignores the tail of a longer list rather than refusing
|
|
56
|
+
it.
|
|
57
|
+
|
|
58
|
+
## Hooks
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
const { terms, loading, search, open } = useTermSearch(client, {
|
|
62
|
+
vocabulary: "avito-phones",
|
|
63
|
+
level: "Model",
|
|
64
|
+
parent: vendorCode, // undefined = the whole level
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const labels = useTermLabels(client, { vocabulary, level, codes });
|
|
68
|
+
termLabel(labels, "iphone-15-pro"); // "iPhone 15 Pro", or the code
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`useTermSearch` debounces 250 ms, aborts the request it supersedes, fetches the
|
|
72
|
+
first page immediately on open (once per level and parent), and empties itself
|
|
73
|
+
when the parent moves — what is listed belongs to the previous parent's
|
|
74
|
+
children. It is deliberately NOT a query: a search is superseded per keystroke
|
|
75
|
+
and scoped to a dropdown that is about to close.
|
|
76
|
+
|
|
77
|
+
`useTermLabels` is the opposite and IS a query, keyed through
|
|
78
|
+
`vocabulariesQueryKeys.termLabels` — the same codes give the same labels until
|
|
79
|
+
the vocabulary's revision moves, and several controls on one page routinely ask
|
|
80
|
+
for the same set. It hands out a `LoadState`, not a bare map: `{}` would say
|
|
81
|
+
"these codes have no labels" in the same words as "nobody has asked yet".
|
|
82
|
+
|
|
83
|
+
## The control
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { VocabularyTermSelect } from "@stapel/vocabularies-react/default";
|
|
87
|
+
import { registerVocabulariesI18nRu } from "@stapel/vocabularies-react/i18n/ru";
|
|
88
|
+
|
|
89
|
+
<VocabularyTermSelect
|
|
90
|
+
client={vocabularies}
|
|
91
|
+
vocabulary="avito-phones"
|
|
92
|
+
level="Vendor"
|
|
93
|
+
value={codes}
|
|
94
|
+
onChange={setCodes}
|
|
95
|
+
/>;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The same control `attributes-react`'s `ref_select` editor draws, shipped on its
|
|
99
|
+
own for the places a composer is not — a facet filter, an admin form, a
|
|
100
|
+
bulk-edit row. `filterOption={false}`: the options ARE the answer to the current
|
|
101
|
+
query, and letting antd filter them again would hide rows the server
|
|
102
|
+
deliberately ranked. A code the control already holds is resolved to its label
|
|
103
|
+
and stays pickable even when the current page does not contain it. **No client
|
|
104
|
+
is a loud state** — the notice, not an empty dropdown: a control that cannot
|
|
105
|
+
reach its terms and looks like one that found none is how a person is left
|
|
106
|
+
unable to answer a question nobody told them was broken.
|
|
107
|
+
|
|
108
|
+
`./default` is themed through `SkinTheme` from `@stapel/tokens-antd/skin` (one
|
|
109
|
+
bridge for the whole fleet — a pair never mounts its own `ConfigProvider` and
|
|
110
|
+
never defaults a theme mode). Importing the subpath is the opt-in that pulls
|
|
111
|
+
`antd`; a host with its own design system keeps importing the root entry and
|
|
112
|
+
draws its own control over the hooks.
|
|
113
|
+
|
|
114
|
+
Locales ship as subpaths (`./i18n/ru`, `./i18n/es`) so a host carries only the
|
|
115
|
+
ones it registers; `test/i18nParity.test.ts` fails the build if a key exists in
|
|
116
|
+
en and not in ru/es.
|
|
117
|
+
|
|
118
|
+
This pair contributes **no nav entry**: it ships a control other pairs' forms
|
|
119
|
+
mount, not a screen of its own (the `@stapel/attributes-react` precedent).
|
|
120
|
+
|
|
121
|
+
## Install
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
pnpm add @stapel/vocabularies-react @stapel/core @tanstack/react-query react
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The `VocabulariesProvider` / `createVocabulariesRuntime` pair is the standard
|
|
128
|
+
runtime wiring, needed only if you reach the module's other endpoints (the
|
|
129
|
+
vocabulary catalogue) through this pair's `StapelClient`. The seam client above
|
|
130
|
+
needs none of it.
|
|
131
|
+
|
|
132
|
+
## Layers
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
src/
|
|
136
|
+
client.ts createVocabularyClient — the seam, a bare fetch client
|
|
137
|
+
api/ typed aliases over this pair's own generated `components`
|
|
138
|
+
model/ query keys, useTermSearch, useTermLabels, runtime wiring
|
|
139
|
+
flows/ toFlowError + zero-flow registry shim (the module annotates none)
|
|
140
|
+
headless/ VocabulariesProvider
|
|
141
|
+
default/ VocabularyTermSelect (antd, opt-in subpath)
|
|
142
|
+
i18n/ translation keys + generated backend error map (en/ru/es)
|
|
143
|
+
demo/ first-class demos (compiled, product-linted, smoke-rendered)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Generated surfaces (drift-gated)
|
|
147
|
+
|
|
148
|
+
| Surface | Path | Gate |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| Typed API schema | `src/api/generated/schema.ts`, from stapel-vocabularies's own `docs/schema.json` | `pnpm gen:api:check` |
|
|
151
|
+
| Flow registry | none — zero-flow module (`src/flows/registry.ts` shim) | `pnpm gen:flows:check` |
|
|
152
|
+
| Backend error map + en/ru/es bundles | `src/i18n/generated/` | `pnpm gen:errors:check` |
|
|
153
|
+
| Typed-event registry | `src/analytics/generated/events.json` | `pnpm gen:events:check` |
|
|
154
|
+
| Demos → Ladle stories | `demo/generated/` | `pnpm gen:demos:check` |
|
|
155
|
+
| `manifest.json` + `llms.txt` | package root | `pnpm gen:manifest:check` |
|
|
156
|
+
|
|
157
|
+
The module ships no `translations/` directory, so `gen:errors` runs with
|
|
158
|
+
`ERRORS_CATALOG_DIR` pointed at stapel-core's catalogue and
|
|
159
|
+
`ERRORS_LOCALE_EXEMPT_OWNERS=stapel_vocabularies`: the cross-cutting keys come
|
|
160
|
+
from core in ru/es, and the module's own three are authored in `i18n/ru.ts` /
|
|
161
|
+
`i18n/es.ts` until upstream localizes them.
|
|
162
|
+
|
|
163
|
+
## Guardrails
|
|
164
|
+
|
|
165
|
+
Linted by the shared `@stapel/eslint-plugin` flat config and the shared
|
|
166
|
+
stylelint preset. Demos are first-class code: compiled by `tsconfig.demo.json`,
|
|
167
|
+
linted with the product ruleset, smoke-rendered by `test/demos.test.tsx` — and
|
|
168
|
+
never shipped (excluded from the `files` allowlist; proven by
|
|
169
|
+
`test/prodBundlePurity.test.ts`).
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-authored API surface the codegen does not (yet) cover — browser-redirect
|
|
3
|
+
* URL builders, open-redirect guards, narrow domain type-guards, header
|
|
4
|
+
* conventions. Everything that CAN be derived from schema.json belongs in the
|
|
5
|
+
* generated operations (`api/vocabulariesApi.ts`), not here.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=extensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../src/api/extensions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-authored API surface the codegen does not (yet) cover — browser-redirect
|
|
3
|
+
* URL builders, open-redirect guards, narrow domain type-guards, header
|
|
4
|
+
* conventions. Everything that CAN be derived from schema.json belongs in the
|
|
5
|
+
* generated operations (`api/vocabulariesApi.ts`), not here.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=extensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../src/api/extensions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
export interface paths {
|
|
2
|
+
"/vocabularies/api/v1/vocabularies/": {
|
|
3
|
+
parameters: {
|
|
4
|
+
query?: never;
|
|
5
|
+
header?: never;
|
|
6
|
+
path?: never;
|
|
7
|
+
cookie?: never;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* List vocabularies
|
|
11
|
+
* @description Every vocabulary this deployment holds, with its levels and revision. Cacheable: the ETag covers the highest revision and the number of vocabularies.
|
|
12
|
+
*
|
|
13
|
+
* **Permissions:** `ReadOnlyOrStaff`
|
|
14
|
+
*/
|
|
15
|
+
get: operations["vocabularies_api_v1_vocabularies_list"];
|
|
16
|
+
put?: never;
|
|
17
|
+
post?: never;
|
|
18
|
+
delete?: never;
|
|
19
|
+
options?: never;
|
|
20
|
+
head?: never;
|
|
21
|
+
patch?: never;
|
|
22
|
+
trace?: never;
|
|
23
|
+
};
|
|
24
|
+
"/vocabularies/api/v1/vocabularies/{slug}/": {
|
|
25
|
+
parameters: {
|
|
26
|
+
query?: never;
|
|
27
|
+
header?: never;
|
|
28
|
+
path?: never;
|
|
29
|
+
cookie?: never;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Retrieve a vocabulary
|
|
33
|
+
* @description ``GET vocabularies/{slug}/`` — one vocabulary.
|
|
34
|
+
*
|
|
35
|
+
* **Permissions:** `ReadOnlyOrStaff`
|
|
36
|
+
*/
|
|
37
|
+
get: operations["vocabularies_api_v1_vocabularies_retrieve"];
|
|
38
|
+
put?: never;
|
|
39
|
+
post?: never;
|
|
40
|
+
delete?: never;
|
|
41
|
+
options?: never;
|
|
42
|
+
head?: never;
|
|
43
|
+
patch?: never;
|
|
44
|
+
trace?: never;
|
|
45
|
+
};
|
|
46
|
+
"/vocabularies/api/v1/vocabularies/{slug}/terms/": {
|
|
47
|
+
parameters: {
|
|
48
|
+
query?: never;
|
|
49
|
+
header?: never;
|
|
50
|
+
path?: never;
|
|
51
|
+
cookie?: never;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Search the terms of one level
|
|
55
|
+
* @description A page of terms at `level`, optionally the children of a `parent` term at the level above, optionally matching `q`. Prefix matches rank before the rest, then the level's own sort order and label. `has_children` is what tells a cascading control whether to ask for the next level. `total` counts the whole filtered set, before limit and offset.
|
|
56
|
+
*
|
|
57
|
+
* **Permissions:** `ReadOnlyOrStaff`
|
|
58
|
+
*/
|
|
59
|
+
get: operations["vocabularies_api_v1_vocabularies_terms_retrieve"];
|
|
60
|
+
put?: never;
|
|
61
|
+
post?: never;
|
|
62
|
+
delete?: never;
|
|
63
|
+
options?: never;
|
|
64
|
+
head?: never;
|
|
65
|
+
patch?: never;
|
|
66
|
+
trace?: never;
|
|
67
|
+
};
|
|
68
|
+
"/vocabularies/api/v1/vocabularies/{slug}/terms/resolve/": {
|
|
69
|
+
parameters: {
|
|
70
|
+
query?: never;
|
|
71
|
+
header?: never;
|
|
72
|
+
path?: never;
|
|
73
|
+
cookie?: never;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Resolve term codes to labels
|
|
77
|
+
* @description `{code: label}` for the codes named in `codes` (comma separated, at most 200 — the rest are ignored). Unknown codes are omitted, so a caller falls back to the code, which is what a stored DAO value does when its labels are missing.
|
|
78
|
+
*
|
|
79
|
+
* **Permissions:** `ReadOnlyOrStaff`
|
|
80
|
+
*/
|
|
81
|
+
get: operations["vocabularies_api_v1_vocabularies_terms_resolve_retrieve"];
|
|
82
|
+
put?: never;
|
|
83
|
+
post?: never;
|
|
84
|
+
delete?: never;
|
|
85
|
+
options?: never;
|
|
86
|
+
head?: never;
|
|
87
|
+
patch?: never;
|
|
88
|
+
trace?: never;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export type webhooks = Record<string, never>;
|
|
92
|
+
export interface components {
|
|
93
|
+
schemas: {
|
|
94
|
+
/** @description One level of a vocabulary. */
|
|
95
|
+
Level: {
|
|
96
|
+
name: string;
|
|
97
|
+
/** @description Name of the level this one hangs off; null for a root level. */
|
|
98
|
+
parent?: string | null;
|
|
99
|
+
};
|
|
100
|
+
/** @description One term of one level. */
|
|
101
|
+
Term: {
|
|
102
|
+
code: string;
|
|
103
|
+
/** @description Resolved for the request's Accept-Language when the term carries a translation, otherwise the term's own label. */
|
|
104
|
+
label: string;
|
|
105
|
+
level: string;
|
|
106
|
+
/** @description Whether this term has any child term — what tells a cascading control whether to ask for the next level. */
|
|
107
|
+
has_children: boolean;
|
|
108
|
+
};
|
|
109
|
+
/** @description One page of terms plus the size of the whole filtered set. */
|
|
110
|
+
TermPage: {
|
|
111
|
+
results: components["schemas"]["Term"][];
|
|
112
|
+
/** @description Number of terms matching level/parent/q, before limit and offset. */
|
|
113
|
+
total: number;
|
|
114
|
+
};
|
|
115
|
+
/** @description A vocabulary as the catalogue endpoints render it. */
|
|
116
|
+
Vocabulary: {
|
|
117
|
+
slug: string;
|
|
118
|
+
name: string;
|
|
119
|
+
levels: components["schemas"]["Level"][];
|
|
120
|
+
term_count: number;
|
|
121
|
+
/** @description Cache key of the whole vocabulary; also the ETag and the vocabulary.changed payload. */
|
|
122
|
+
revision: number;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
responses: never;
|
|
126
|
+
parameters: never;
|
|
127
|
+
requestBodies: never;
|
|
128
|
+
headers: never;
|
|
129
|
+
pathItems: never;
|
|
130
|
+
}
|
|
131
|
+
export type $defs = Record<string, never>;
|
|
132
|
+
export interface operations {
|
|
133
|
+
vocabularies_api_v1_vocabularies_list: {
|
|
134
|
+
parameters: {
|
|
135
|
+
query?: never;
|
|
136
|
+
header?: never;
|
|
137
|
+
path?: never;
|
|
138
|
+
cookie?: never;
|
|
139
|
+
};
|
|
140
|
+
requestBody?: never;
|
|
141
|
+
responses: {
|
|
142
|
+
200: {
|
|
143
|
+
headers: {
|
|
144
|
+
[name: string]: unknown;
|
|
145
|
+
};
|
|
146
|
+
content: {
|
|
147
|
+
"application/json": components["schemas"]["Vocabulary"][];
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
vocabularies_api_v1_vocabularies_retrieve: {
|
|
153
|
+
parameters: {
|
|
154
|
+
query?: never;
|
|
155
|
+
header?: never;
|
|
156
|
+
path: {
|
|
157
|
+
slug: string;
|
|
158
|
+
};
|
|
159
|
+
cookie?: never;
|
|
160
|
+
};
|
|
161
|
+
requestBody?: never;
|
|
162
|
+
responses: {
|
|
163
|
+
200: {
|
|
164
|
+
headers: {
|
|
165
|
+
[name: string]: unknown;
|
|
166
|
+
};
|
|
167
|
+
content: {
|
|
168
|
+
"application/json": components["schemas"]["Vocabulary"];
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
vocabularies_api_v1_vocabularies_terms_retrieve: {
|
|
174
|
+
parameters: {
|
|
175
|
+
query: {
|
|
176
|
+
/** @description Level to list. Required. */
|
|
177
|
+
level: string;
|
|
178
|
+
/** @description Page size, 1..200 (default 50). */
|
|
179
|
+
limit?: number;
|
|
180
|
+
/** @description Rows to skip. */
|
|
181
|
+
offset?: number;
|
|
182
|
+
/** @description Code of a term at the parent level; restricts the page to its children. */
|
|
183
|
+
parent?: string;
|
|
184
|
+
/** @description Case-insensitive substring of the label. */
|
|
185
|
+
q?: string;
|
|
186
|
+
};
|
|
187
|
+
header?: never;
|
|
188
|
+
path: {
|
|
189
|
+
slug: string;
|
|
190
|
+
};
|
|
191
|
+
cookie?: never;
|
|
192
|
+
};
|
|
193
|
+
requestBody?: never;
|
|
194
|
+
responses: {
|
|
195
|
+
200: {
|
|
196
|
+
headers: {
|
|
197
|
+
[name: string]: unknown;
|
|
198
|
+
};
|
|
199
|
+
content: {
|
|
200
|
+
"application/json": components["schemas"]["TermPage"];
|
|
201
|
+
};
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
vocabularies_api_v1_vocabularies_terms_resolve_retrieve: {
|
|
206
|
+
parameters: {
|
|
207
|
+
query: {
|
|
208
|
+
/** @description Comma-separated term codes, at most 200. */
|
|
209
|
+
codes: string;
|
|
210
|
+
/** @description Level the codes belong to. Required. */
|
|
211
|
+
level: string;
|
|
212
|
+
};
|
|
213
|
+
header?: never;
|
|
214
|
+
path: {
|
|
215
|
+
slug: string;
|
|
216
|
+
};
|
|
217
|
+
cookie?: never;
|
|
218
|
+
};
|
|
219
|
+
requestBody?: never;
|
|
220
|
+
responses: {
|
|
221
|
+
200: {
|
|
222
|
+
headers: {
|
|
223
|
+
[name: string]: unknown;
|
|
224
|
+
};
|
|
225
|
+
content: {
|
|
226
|
+
"application/json": {
|
|
227
|
+
[key: string]: unknown;
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/api/generated/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/api/generated/schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types for the stapel-vocabularies HTTP contract — **derived from the generated
|
|
3
|
+
* OpenAPI surface** (frontend-standard §2/§3), never hand-maintained. The
|
|
4
|
+
* single source of truth is `components["schemas"]` from this pair's own
|
|
5
|
+
* package-LOCAL generated schema (`./generated/schema.js`, produced by
|
|
6
|
+
* `pnpm gen:api` from stapel-vocabularies's OWN `docs/schema.json` — the §17-native
|
|
7
|
+
* per-module contract). Alias the schemas this pair uses under local names
|
|
8
|
+
* here; do NOT write parallel response bodies.
|
|
9
|
+
*/
|
|
10
|
+
import type { components } from "./generated/schema.js";
|
|
11
|
+
/** The generated schema table — the one source of truth for wire shapes. */
|
|
12
|
+
export type Schemas = components["schemas"];
|
|
13
|
+
/** A vocabulary as the catalogue endpoints render it. */
|
|
14
|
+
export type Vocabulary = Schemas["Vocabulary"];
|
|
15
|
+
/** One level of a vocabulary — `parent` names the level above, null at the root. */
|
|
16
|
+
export type Level = Schemas["Level"];
|
|
17
|
+
/** One term of one level. `has_children` is what tells a cascading control
|
|
18
|
+
* whether there is another column to ask for. */
|
|
19
|
+
export type Term = Schemas["Term"];
|
|
20
|
+
/** One page of terms plus the size of the whole filtered set (`total` counts
|
|
21
|
+
* before `limit`/`offset`, so a control can say "50 of 14 962"). */
|
|
22
|
+
export type TermPage = Schemas["TermPage"];
|
|
23
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,4EAA4E;AAC5E,MAAM,MAAM,OAAO,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;AAE5C,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAE/C,oFAAoF;AACpF,MAAM,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAErC;iDACiD;AACjD,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnC;oEACoE;AACpE,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/api/types.ts"],"names":[],"mappings":""}
|