wow-view-host · diff
git:20261004.2702e5d to git:20261021.c4d0125
22 added, 13 removed. Audit A to A.
---
name: "wow-view-host"
- description: "Wire the Wow View Engine into a host app: one engine with resources and saved-view store (MemoryViewStore, WowViewStore behind CoSec, stored system views), ViewHost, bind and routes, Wow commands on records as declared actions, and moving off fetcher-viewer. Use for @ahoo-wang/wow-view-engine hosts, including the Wow repository's compensation console and Storybook. Exclude definitions (wow-view-definition), other client code, and changing the engine."
+ description: "Wire the Wow View Engine into a host app over Wow V10 services: one engine with query-route sources, datasets and a view store (MemoryViewStore, WowViewStore), ViewHost, bind and routes, commands on records as declared actions, moving off fetcher-viewer. Use for @ahoo-wang/wow-view-engine hosts, including the Wow repository's ops console and Storybook. Exclude definitions (wow-view-definition), other client code, changing the engine."
---
# wow-view-host
- The deliverable is a host a reviewer can merge: one engine for the application, registered once with every resource and a store; a `ViewHost` around the pages with the router, the language and the theme; a `bind` per resource with its route, its record reading and the commands on its records declared as actions; and tests that read those declarations, routes and definitions the way the engine does. What a definition declares is `wow-view-definition`; this Skill takes the definitions as given.
+ The job: definitions exist, and an application has to show them, keep the views people save, and let people act on the records. The deliverable is a host a reviewer can merge: one engine for the application, registered once with every resource and a store; a `ViewHost` around the pages with the router, the language and the theme; a `bind` per resource with its route, its record reading and the commands on its records declared as actions; and tests that read those declarations, routes and definitions the way the engine does. What a definition declares is `wow-view-definition`; this Skill takes the definitions as given.
- The engine owns the mechanism — where a command is placed, the confirmation, a selection partly able to take it, running a few at a time, progress, the refresh after, the address's `?view=` and `?id=`, light and dark. The host owns the choices: which resources, where each lives, which commands reach the screen, when each is available and what a refusal says. Write only the choices.
+ The engine owns the mechanism — where a command is placed, the confirmation, a selection partly able to take it, running a few at a time, progress, the refresh after, the address's `?view=` and `?id=`, light and dark. The host owns the choices: which resources, where each one's rows and saved views come from, which commands reach the screen, when each is available and what a refusal says. Write only the choices.
+ ## What a Wow V10 service gives a host
+
+ - **Rows and descriptors:** each query model has one HTTP route, `/{context}/{aggregate}/{model}/query` with the model `snapshot` or `events`: `POST` queries, `GET` returns the capability descriptor with its version as the `ETag`.
+ The clients `wow-generator` generates from the service's Manifest (`…SnapshotQueryClient`, `…EventQueryClient`), or wow-client's `SnapshotQueryClient` and `EventQueryClient`, are those routes; `viewSourceOf` from `@ahoo-wang/wow-view-store` makes one a source.
+ - **Datasets:** `GET /wow/dataset` lists the wide and warehouse tables the service publishes at run time; `DatasetResources` with `datasetCatalogOf` registers them.
+ - **Commands:** the generated command clients, which send `Wow-Wait` and read problem+json refusals.
+ - **Saved views:** the V10 view store service (`platform/view-store`), two Wow aggregates reached through `WowViewStore`. It takes who is asking from the caller's principal and authorizes every write itself.
+
## Before writing
- 1. Find the host's router, i18n, theme (shadcn or none, who paints dark mode), its Wow service clients (generated by `wow-generator`, or wow-client's), how it authenticates (CoSec token, or nobody signs in), and whether it renders on the server (Next.js). A page still on the deprecated `@ahoo-wang/fetcher-viewer` is rebuilt, not renamed: `references/wiring.md` has the path.
+ 1. Find the host's router, i18n, theme (shadcn or none, who paints dark mode), its Wow clients (generated by `wow-generator`, committed under `src/generated/`), how it authenticates (a CoSec token, or nobody signs in), and whether it renders on the server (Next.js). A page still on the deprecated `@ahoo-wang/fetcher-viewer` is rebuilt, not renamed: `references/wiring.md` has the path.
2. Collect the definitions to register and the words of their keys, per language.
- 3. For each resource, read the aggregate's commands and state: which commands a person issues on a record, and which state fields decide whether one is allowed.
+ 3. For each resource, read the aggregate's commands and state in its Manifest or generated client: which commands a person issues on a record, and which state fields decide whether one is allowed.
4. Load `references/wiring.md` before writing the engine and the host.
## Workflow
- 1. **One engine, registered once.** `new ViewEngine({ resources, store, environment })` at the application's start, shared by every page. Each resource pairs a definition with its source: the Wow snapshot (or event stream) query client's `paged`, `cursor`, `aggregate`, and the descriptor client's `describe`, so the engine narrows to the store it runs on. A board's definition has no source. Give the fetcher the sources and the store use a timeout (`fetcher.timeout = 60_000`): neither fetcher nor the engine sets one, and a server that never answers holds a view's opening, and the engine's query slots, for ever.
- 2. **Choose the store** by who must see saved views: `MemoryViewStore` for tests and demos, `localStorageSnapshot` for one browser in development, `WowViewStore` on the Wow view store for everyone else. Load `references/storage.md`; the tenant and owner come from the fetcher's interceptors, never from options, and the CoSec gateway decides who may use which path. Grant `editSystem` (publish and edit stored system views; absent means refused) only to the role the gateway admits to the system views' path (tenant `(platform)`, owner `(system)`).
+ 1. **One engine, registered once.** `new ViewEngine({ resources, store, environment })` at the application's start, shared by every page. Each resource pairs a definition with its source, `viewSourceOf(queryClient)`: the client's `paged`, `cursor`, `aggregate` and `describe`, so the engine narrows each definition to the backend it runs on. A board's definition has no source. Give the fetcher a timeout (`fetcher.timeout = 60_000`): neither fetcher nor the engine sets one, and a server that never answers holds a view's opening, and the engine's query slots, for ever.
+ 2. **Choose the store** by who must see saved views: `MemoryViewStore` for tests and demos, `localStorageSnapshot` for one browser (the ops console keeps an operator's views this way), `WowViewStore` on the V10 view store service for everyone else. Load `references/storage.md`. The store takes no tenant, owner or user: the fetcher's interceptors carry the caller, and the service decides from its roles who writes the shared views (`view-store.shared-writer`) and the stored system views (`view-store.system-writer`). `permissions` only enables buttons, by the same roles; grant `editSystem` only to the system writers.
3. **One `ViewHost` around the pages:** `engine`, `router` (`useReactRouter()` from `@ahoo-wang/wow-view-engine/react-router`, or the two-member `ViewRouter`), `locale` and `messages` (the engine's catalogue plus the definitions' words), `bindings`, and one theme path. Surfaces under it take only an id: `<DataWorkbench definitionId="orders" />`.
4. **Bind each resource.** `route(instanceId)` is the path of the page that opens a view of it (`null`: the page's default) — the engine keeps `?view=` and `?id=` in sync itself, so do not write that glue. `reading` is how one record reads in its detail. `actions` are its commands, declared. Draw your shell's navigation from `useViewNavigation()`; there is no page shell to configure.
- 5. **From commands to actions.** Load `references/actions.md` and decide, per command: whether it reaches the screen; the rule that makes it available, read from the record's state (declare the fields it reads in the definition's `record.rowFields`); the refusal in the audience's words; the aggregate id it goes to (read off the row when the row key is a business key); a confirmation asked for one record too (`ask` left at `'always'`) wherever it cannot be undone, `ask: 'bulk'` only where it is undone as easily, and `tone: 'danger'` only where something is lost (cancel, refund, delete), not for a routine step such as shipping; whether a selection may take it (`on`); the form or choice it needs; when its availability flips on its own (`changesAt`); and `run` resolving only after the read model shows the command (`CommandStage.SNAPSHOT`). Slots are the escape hatch, not the path.
- 6. **Theme and CSP are pointers, not work:** pick `theme="host"` (with `shadcn-bridge.css`) or a `preset` (with its stylesheet), `colorMode="host"` where the app already paints dark mode, and follow the engine's Content Security Policy section where the host has one. Do not hand-write token overrides or a dark-mode switch.
+ 5. **From commands to actions.** Load `references/actions.md` and decide, per command: whether it reaches the screen; the rule that makes it available, read from the record's state (declare the fields it reads in the definition's `record.rowFields`); the refusal in the audience's words; the aggregate id it goes to (read off the row when the row key is a business key); a confirmation asked for one record too (`ask` left at `'always'`) wherever it cannot be undone, `ask: 'bulk'` only where it is undone as easily, and `tone: 'danger'` only where something is lost (cancel, refund, delete), not for a routine step such as shipping; whether a selection may take it (`on`); the form or choice it needs; when its availability flips on its own (`changesAt`); and `run` resolving only after the read model shows the command (wait for the `SNAPSHOT` stage). Slots are the escape hatch, not the path.
+ 6. **Theme and CSP are pointers, not work:** pick `theme="host"` (with `shadcn-bridge.css`) or a `preset` (with its stylesheet), `colorMode="host"` where the app already paints dark mode, and follow the engine's Content Security Policy page where the host has one. Do not hand-write token overrides or a dark-mode switch.
## Self-check
A host is done when its tests read the declarations as the engine does and they pass:
- **Actions:** `actionHarness(actions, rows, { now })` from `@ahoo-wang/wow-view-engine/testing` — each action offered where intended (`at`), available or refused with the right reason on representative rows (`state`, `bulk`), asked where intended (`asks`), and `run` sending the right command (a fake commands object).
- **Routes:** `resolveNavigation(to, bindingOf)` over the host's own bindings sends each resource's ways off to its page.
- **Definitions:** `admit` over the registered resources and committed descriptors in every language returns `[]` or only findings asserted with their reason (`wow-view-definition` owns fixing them).
- These tests and `admit` run in Node; give them a Node Vitest config where the package's own is a browser project.
- The host's type check, lint and existing tests; `references/actions.md` lists the report.
Do not re-verify by hand what these read; they run the engine's own rules.
## References
- The references hold names, shapes, rules and gotchas; each links the page of https://wow.ahoo.me/guide/typescript/view-engine.html that covers its topic in depth. Repository paths (`compensation/…`, `typescript/…`) are in the Wow repository.
+ The references hold names, shapes, rules and gotchas; each links the page of https://wow.ahoo.me/typescript/view-engine/ that covers its topic in depth. Repository paths (`platform/…`, `typescript/…`) are in the Wow repository.
- - `references/wiring.md`: the engine, resources and sources, `ViewHost`, `bind`, routes, navigation, the record reading, theme and CSP pointers, the route test, server rendering, and moving off fetcher-viewer. Load it first.
+ - `references/wiring.md`: sources over V10 query routes, datasets, the engine, `ViewHost`, `bind`, routes, navigation, the record reading, theme and CSP pointers, the route test, server rendering, and moving off fetcher-viewer. Load it first.
- `references/actions.md`: from Wow commands to declared actions, the decisions per command, an example over a command client, the harness test and the report. Load it for any command on a record.
- - `references/storage.md`: choosing a store, `WowViewStore` with fetcher-cosec, permissions and `editSystem`, stored system views, a host nobody signs in to, and the CoSec gateway rules for the view store's paths. Load it before choosing the store.
+ - `references/storage.md`: choosing a store, `WowViewStore` on the V10 view store service, its roles, permissions and `editSystem`, stored and configured system views, dataset wordings, and a host nobody signs in to. Load it before choosing the store.
## Related Skills
- $wow-view-definition: what each definition declares, its words, system views and boards.
- - $wow-client: the command and query clients themselves, errors and wait stages, and generating the typed command clients the actions call.
+ - $wow-client: the command and query clients themselves, errors and wait stages, and generating the typed clients the sources and actions use.
+ - $wow-migrate: a host still on 9.x clients (`@ahoo-wang/fetcher-wow`, the 9.x wow-client) or the 9.x view store moves to V10 first.