Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/skills/docs-sync/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Use this skill whenever a change touches user-visible behavior, APIs, or contrac
| Public API (Rust crate, npm package) | Relevant handler/integration docs; `DESIGN.md` only if an architectural boundary changes |
| Error variants or error messages | Defining source/tests and relevant public troubleshooting docs; `DESIGN.md` only if error-handling architecture changes |
| `@microsoft/webui-framework` decorators or API | `docs/guide/concepts/interactivity.md` + `docs/ai.md` + `packages/webui-framework/README.md` |
| `@microsoft/webui-router` behavior | `docs/guide/concepts/routing.md` + `packages/webui-router/README.md` |
| `@microsoft/webui-delivery` behavior | `docs/guide/concepts/routing.md` + `packages/webui-delivery/README.md` |

## DESIGN.md rules

Expand Down
8 changes: 4 additions & 4 deletions .github/skills/perf/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: perf
description: Speed and memory performance rules for Rust crates, the Node addon, webui-framework, and webui-router.
description: Speed and memory performance rules for Rust crates, the Node addon, webui-framework, and webui-delivery.
---

# Performance

WebUI's value proposition is speed and low memory usage. Every change to core Rust crates, `@microsoft/webui-framework`, or `@microsoft/webui-router` must be evaluated through two lenses: **throughput** (how fast) and **memory** (how little).
WebUI's value proposition is speed and low memory usage. Every change to core Rust crates, `@microsoft/webui-framework`, or `@microsoft/webui-delivery` must be evaluated through two lenses: **throughput** (how fast) and **memory** (how little).

Server memory is not cheap. Client memory is not unlimited. Every allocation that can be avoided is a win on both sides.

Expand Down Expand Up @@ -58,9 +58,9 @@ These apply to `packages/webui-framework` (the client-side Web Component runtime
4. **Strip SSR markers after hydration.** Comment nodes used as markers are removed from the DOM once wiring is complete - they don't persist as memory overhead.
5. **Scope frames are stack-allocated.** `<for>` loop item variables use a linked-list scope chain, not cloned Maps or Objects.

## TypeScript - `@microsoft/webui-router` rules
## TypeScript - `@microsoft/webui-delivery` rules

These apply to `packages/webui-router` (the client-side SPA router).
These apply to `packages/webui-delivery` (the client-side SPA router).

### Speed

Expand Down
3 changes: 3 additions & 0 deletions .github/skills/testing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ The test server (`fixture-render.ts`) auto-discovers fixtures with `src/index.ht
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { startRuntime } from '../../../src/runtime.js';
import { WebUIElement, attr, observable } from '../../../src/index.js';

startRuntime();

export class TestWidget extends WebUIElement {
@attr label = 'Hello';
@observable count = 0;
Expand Down
53 changes: 18 additions & 35 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,9 @@ jobs:
python-test:
name: Python test (${{ matrix.platform.label }} / CPython ${{ matrix.python }})
runs-on: ${{ matrix.platform.os }}
needs: python-wheel
needs: [python-wheel, python-fixture]
env:
WEBUI_TEST_FIXTURES: ${{ github.workspace }}/target/python-fixtures
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -635,6 +637,11 @@ jobs:
name: ${{ matrix.platform.artifact }}
path: publish/python

- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: python-fixtures
path: target/python-fixtures

- name: Install the built wheel and test it
shell: bash
run: |
Expand All @@ -652,9 +659,9 @@ jobs:
python -m mypy
python -m mypy.stubtest microsoft_webui

# ── Phase 3: Python fixture drift (after Linux build) ──────────────
# The committed protocol fixtures are build outputs, so rebuild them and fail
# if they drift, keeping the Python tests from asserting against stale binaries.
# ── Phase 3: Current Python fixtures (after Linux build) ──────────
# Build fixtures from source, verify exact semantic repeatability and v3
# metadata, and share these bytes with every interpreter/platform test.
python-fixture:
name: Python fixture
runs-on: ubuntu-latest
Expand All @@ -673,37 +680,13 @@ jobs:
- name: Verify the release-target contract is restated consistently
run: python crates/webui-python/tests/validate_release_targets.py

- name: Rebuild and compare the protocol fixtures
run: |
set -euo pipefail
output="target/python-fixture-check"
streaming_output="target/python-streaming-fixture-check"
rm -rf "$output" "$streaming_output"
cargo run --quiet -p microsoft-webui-cli -- build \
"crates/webui-python/tests/fixtures/app" --plugin webui --out "$output"
cargo run --quiet -p microsoft-webui-cli -- build \
"crates/webui-python/tests/fixtures/streaming-app" \
--plugin webui --out "$streaming_output"
cargo run --quiet -p microsoft-webui-cli -- inspect \
"crates/webui-python/tests/fixtures/protocol.bin" \
> "$output/committed.json"
cargo run --quiet -p microsoft-webui-cli -- inspect \
"$output/protocol.bin" > "$output/generated.json"
python "crates/webui-python/tests/compare_fixture.py" \
"$output/committed.json" "$output/generated.json" \
"crates/webui-python/tests/fixtures/protocol.bin"
cargo run --quiet -p microsoft-webui-cli -- inspect \
"crates/webui-python/tests/fixtures/streaming_protocol.bin" \
> "$streaming_output/committed.json"
cargo run --quiet -p microsoft-webui-cli -- inspect \
"$streaming_output/protocol.bin" \
> "$streaming_output/generated.json"
python "crates/webui-python/tests/compare_fixture.py" \
"$streaming_output/committed.json" \
"$streaming_output/generated.json" \
"crates/webui-python/tests/fixtures/streaming_protocol.bin"
cmp "crates/webui-python/tests/fixtures/greeting-card.css" \
"$output/greeting-card.css"
- name: Build and verify current protocol fixtures
run: python crates/webui-python/tests/build_fixtures.py

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: python-fixtures
path: target/python-fixtures

# ── Gate: one stable required check ────────────────────────────────
# Branch protection can only require per-job contexts, so requiring the jobs
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ thiserror = "2.0.18"
anyhow = "1.0.104"
log = "0.4.34"
env_logger = "0.11.11"
tokio = { version = "1.53.1", features = ["full"] }
tokio = { version = "1.53.3", features = ["full"] }
clap = { version = "4.6.3", features = ["derive", "color"] }
console = "0.16.6"
rayon = "1.12.0"
Expand Down
69 changes: 64 additions & 5 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ The **compiler** (`webui`, `webui-discovery`, `webui-parser`,
artifact. The **handler** (`webui-handler`, `webui-state`,
`webui-expressions`) owns request-scoped evaluation and output; it does not
know how to find source files. The **browser** (`@microsoft/webui-framework`
and, independently, `@microsoft/webui-router`) consumes compiler output and
and, independently, `@microsoft/webui-delivery`) consumes compiler output and
server HTML rather than compiling templates again. CLI, desktop, and language
bindings are hosts around the same protocol and handler.

Expand All @@ -63,6 +63,27 @@ browser runtime, and a host can load `protocol.bin` without shipping the
compiler. Only the selected optional browser entry points should reach an
application's JavaScript bundle.

Browser resource data, rendering, and delivery are separate layers. Template
registration and style catalogs do not require a renderer or navigation
orchestrator. Buffered rendering selects its style adapter explicitly; routing
selects transport and optional navigation policies. Public API imports do not
install runtimes or construct application singletons. Applications select
renderer/style installers explicitly, start rendering after their selected
modules have loaded, and configure routing once at startup;
selected request-critical policies are ready before interaction begins.
Unselected implementations must not reach production output through either
static or dynamic imports. Application feature code can load on demand without
changing global routing policy.

Shared browser delivery remains hydration-agnostic. Navigation and lazy
template loading consume one request-lifetime, resource availability, build
identity, and transport contract. Route reconciliation owns early commits and
per-level state; lazy loading returns scoped state to its existing owner only
after resources are ready. Resource-only preloading does not cache application
state. A renderer adapter registers opaque compiled
payloads, reports readiness and release, and owns its live views. Delivery does
not import the WebUI element renderer or turn a template section into a component.

## Compilation and artifact ownership

### Source discovery and parsing
Expand Down Expand Up @@ -96,6 +117,13 @@ become typed instructions or references. `<if>`, `<for>`, `<route>`,
parser rejects invalid structure and unsupported syntax with source-located
diagnostics, rather than trying to repair arbitrary browser HTML.

A lazy `<if>` is an ordinary conditional with a delivery annotation. Its
body retains the enclosing template's state, lexical scope, refs, and event
ownership. Compilation separates that body's client payload from eager owner
metadata without introducing a custom-element host. The protocol retains both
server instructions and independently deliverable client sections; private
sub-blocks travel together, while genuinely shared blocks remain reusable.

Component templates may share named loop bodies without expanding them at each
callsite. Structural recursion through component templates is invalid; finite
data-driven loop reuse is supported. Lexical scopes are compiled so the
Expand Down Expand Up @@ -137,6 +165,11 @@ requests, and creates separate render contexts for state, route, nonce,
inventory, writer, and plugin instance. No render should decode or re-index
the protocol per request.

The compiler assigns an artifact identity over the complete protocol content.
Availability bits are meaningful only for that identity. A response from another
build is rejected rather than applied to a document with different resource
positions or template definitions.

### CSS and themes

The build determines each component's **effective DOM mode**. Unwrapped
Expand Down Expand Up @@ -191,7 +224,9 @@ Initial hydration and partial navigation have separate state surfaces.
state is required. Initial state includes proven authored observable/attribute
properties; navigation additionally includes compiled template roots.
Reachability follows the active route but conservatively includes conditional
and loop branches it might render. Projection is a payload/performance
and loop branches it might render. Explicitly deferred edges are separate
delivery roots: initial delivery follows the ones active in the request's actual
scope. They do not create independent component state. Projection is a payload/performance
optimization, **not a secrecy boundary**: any browser render state may be
sent to the client. Never put secrets in it.

Expand Down Expand Up @@ -229,6 +264,14 @@ the handler does not finish rendering into an abandoned channel. Host entry
points may render a whole document, a component fragment, a route partial,
or a host-paced streaming session from the same loaded protocol.

Lazy-body requests name an existing owner and region, not a route. A preload
delivers the body's ordinary resource closure without application state or DOM
creation. A stateful response can send resources first and fresh scoped state
later; the final record adds any nested deferred resources selected by that
state. Hosts own authorization, data providers, cancellation, and backpressure.
The browser applies state to its existing owner/scope and uses normal conditional
mounting. Initially active regions instead use ordinary SSR and hydrate that DOM.

### Server-authoritative routing

Nested `<route>` declarations and a component's `<outlet>` define the route
Expand All @@ -243,12 +286,18 @@ location.
For soft navigation, the server returns the authoritative route chain and
projected state, filters templates against the client's component inventory,
and supplies separately tracked style resources required by the destination.
The router registers those resources, waits for their readiness, and reconciles
The resource delivery layer registers those resources and acknowledges inventory
only after registration/readiness succeeds. Concurrent responses merge availability;
local cache collection invalidates older inventory acknowledgements without
turning template retention into a cache of application state.
The router waits for resource readiness and reconciles
changed route levels while preserving
unchanged parents. It may fall back to document navigation when client-side
mounting cannot complete. Partial responses can be complete JSON or an NDJSON
pair that sends chain/templates first and state later; neither is the
progressive HTML streaming protocol. Cache tags, mutation invalidation,
progressive HTML streaming protocol. A bounded record reader owns framing and
body cancellation; the route adapter owns positional state and commit ordering.
JSON-only navigation does not import that record reader. Cache tags, mutation invalidation,
pending/error components, and speculative preloading are optional router
features, not dependencies of the SSR handler.

Expand Down Expand Up @@ -305,6 +354,15 @@ re-render the first paint, or do a document-wide search per binding.
Client-created components clone compiled static HTML and fill the recorded
slots. Raw HTML ranges retain explicit ownership for later replacement.

Native custom-element definitions retain only the identity and observer surface
needed for future instances, not complete template bodies. Optional template
collection uses compiler-derived direct dependencies: a live component's
metadata retains the ordinary descendants that its conditions and repeats can
instantiate synchronously. Cache ownership may be released without breaking
those consumers. Constructor registration, behavior modules, and CSS still
required by a live tree have distinct lifetimes and are not promised to unload
when a template cache is cleared.

`WebUIElement` is the authored behavior layer. Compiler-owned scriptless hosts
need no application module and can stay dormant until state or client creation
requires activation. Component work policies may defer SSR hydration to
Expand Down Expand Up @@ -504,7 +562,8 @@ engineering another; they are not public application-authoring guides.
| Rust render and streaming APIs | `crates/webui-handler/src/`, `crates/webui/src/streaming.rs` |
| Progressive response wire and host-step invariants | [`specs/streaming.md`](specs/streaming.md); `crates/webui-handler/src/streaming/`, `packages/webui-framework/src/streaming-protocol.ts` |
| Client template metadata, SSR markers, and hydration | [`specs/hydration.md`](specs/hydration.md); `crates/webui-handler/src/plugin/webui.rs`, `packages/webui-framework/src/element/markers.ts`, `docs/guide/concepts/hydration.md` |
| Browser routing | `packages/webui-router/`, `docs/guide/concepts/routing.md` |
| Browser routing | `packages/webui-delivery/`, `docs/guide/concepts/routing.md` |
| Optional client delivery and inventory ownership | [`specs/client-delivery.md`](specs/client-delivery.md); `packages/webui-delivery/src/`, `packages/webui-framework/src/template-cache.ts` |
| Desktop IPC trust, admission, and envelope contract | [`specs/desktop-ipc.md`](specs/desktop-ipc.md); `crates/webui-desktop/src/ipc/`, `packages/webui-desktop/src/envelope.ts` |
| Desktop website data ownership | [`specs/desktop-website-data.md`](specs/desktop-website-data.md); `crates/webui-desktop/src/local_server.rs`, `crates/webui-desktop/src/macos/website_data.rs` |
| Desktop app and window APIs | `docs/guide/concepts/desktop.md`, `crates/webui-desktop/src/` |
Expand Down
Loading
Loading