diff --git a/Cargo.lock b/Cargo.lock index 4d3e79d6..af7fa877 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1574,6 +1574,19 @@ dependencies = [ "libc", ] +[[package]] +name = "insta" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0f8fee8c926415c58d6ae43a08523a26faccb2323f5e6b644fe7dd4ef6b82" +dependencies = [ + "console", + "once_cell", + "serde", + "similar", + "tempfile", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1817,6 +1830,7 @@ dependencies = [ "clap", "console", "expand-tilde", + "insta", "log", "microsoft-webui", "microsoft-webui-dev-server", @@ -1825,6 +1839,7 @@ dependencies = [ "microsoft-webui-protocol", "microsoft-webui-tokens", "mime_guess", + "serde", "serde_json", "tempfile", "tokio", @@ -2933,6 +2948,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "siphasher" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index a93a4874..2703e7ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ windows-sys = { version = "0.61.2", features = [ # Test dependencies criterion = "0.8.2" +insta = { version = "1.48.0", features = ["json"] } tempfile = "3.27.0" # Build dependencies diff --git a/DESIGN.md b/DESIGN.md index 1b7458ff..307307c4 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -2989,6 +2989,74 @@ function returns `NULL`, call `webui_last_error()` for a human-readable diagnost The CLI specification and usage details are maintained in [crates/webui-cli/README.md](crates/webui-cli/README.md). +### Render State JSON Schema + +`webui schema ` analyzes the compiled entry and writes a +JSON Schema draft 2020-12 document to stdout. `--entry` selects the entry +fragment and `--title` sets the schema title. + +`webui build --emit-schema` runs the same analysis directly against the +in-memory protocol and writes `.state.schema.json` beside the +protocol. Schema generation and serialization complete before build outputs are +written. Emission is opt-in because schemas are build-time tooling artifacts, +not render-time requirements. + +For entries without routes, the output is a normal object schema. Inference +uses the following rules: + +- Plain text and normal attribute values accept JSON scalars: `string`, + `number`, or `boolean`. Complex `:property` bindings remain unconstrained + until child-template usage provides stronger structural evidence. +- Broad scalar and truthiness schemas can carry non-validating generation + metadata under `x-webui.preferredType`. Plain rendered values prefer + `string`; condition identifiers prefer `boolean`. Validators continue to use + the standard `type`/`anyOf` contract. +- Raw signals are strings. +- Dotted paths create nested objects. +- A terminal `.length` accepts the runtime's three valid forms: a string, an + array, or an object with a `length` property. +- `` collections create arrays; loop-item paths define the item schema. +- Ordered predicates infer numbers. Equality predicates infer the type of a + literal operand; integer literals produce `integer`, and known types propagate + across path-to-path equality. +- Condition-only paths are optional because a missing condition value evaluates + false. A path referenced by rendered output or a collection remains required. +- Normal component-attribute source paths are recorded as scalars, but + child-local comparisons do not re-type the parent path. Complex bindings + preserve structural origin through the child template. Static and computed + component-local values do not become root state properties. +- Parser-synthesized `head_end`, `body_start`, and `body_end` signals are not + state. + +For routed entries, the output is a schema bundle. Each complete matched route +chain receives a distinct schema under `$defs`. Top-level `anyOf` references +those definitions, while `x-webui-routes` maps each route pattern to its +definition: + +```json +{ + "$defs": { + "route": {}, + "route.contacts.:id": {} + }, + "anyOf": [ + { "$ref": "#/$defs/route" }, + { "$ref": "#/$defs/route.contacts.:id" } + ], + "x-webui-routes": { + "/": "#/$defs/route", + "/contacts/:id": "#/$defs/route.contacts.:id" + } +} +``` + +Route definitions are not structurally deduplicated. Even routes with identical +current shapes retain independent contracts so future changes do not +unexpectedly couple them. Definition keys are readable route encodings, but +consumers should still use `x-webui-routes` rather than constructing `$defs` +keys. Host-language DTO generation and runtime schema validation are separate +concerns and are not part of `schema`. + ## Example Workflow Examples and end-to-end walkthroughs are maintained in [examples/README.md](examples/README.md) diff --git a/crates/webui-cli/Cargo.toml b/crates/webui-cli/Cargo.toml index 551286c9..bf226ac1 100644 --- a/crates/webui-cli/Cargo.toml +++ b/crates/webui-cli/Cargo.toml @@ -25,6 +25,7 @@ microsoft-webui-dev-server = { path = "../webui-dev-server", version = "0.0.18" clap = { workspace = true } anyhow = { workspace = true } console = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } actix-web = { workspace = true } awc = { workspace = true } @@ -36,6 +37,7 @@ tokio-stream = { workspace = true } log = { workspace = true } [dev-dependencies] +insta = { workspace = true } tempfile = { workspace = true } microsoft-webui-protocol = { path = "../webui-protocol", version = "0.0.18" } diff --git a/crates/webui-cli/README.md b/crates/webui-cli/README.md index c9175637..969bb0f2 100644 --- a/crates/webui-cli/README.md +++ b/crates/webui-cli/README.md @@ -1,6 +1,6 @@ # microsoft-webui-cli -Command-line tool for the [WebUI](https://github.com/microsoft/webui) framework — build, serve, and inspect WebUI applications. +Command-line tool for the [WebUI](https://github.com/microsoft/webui) framework - build, serve, inspect, and generate render-state schemas for WebUI applications. ## Install @@ -17,7 +17,7 @@ This installs the `webui` binary. Build a WebUI application into a compiled protocol and CSS files. ```bash -webui build [APP] --out [--entry ] [--css ] [--plugin ] [--asset-file-name-template