microsoft-webui-desktop-build 3.0.0

Build-time typed IPC bindings for the WebUI desktop SDK
Documentation

WebUI Desktop Build

Build-time typed IPC generation for microsoft-webui-desktop. This crate is a build dependency, not part of the desktop runtime graph. Hosts using its generated Rust bindings must enable application-ipc on microsoft-webui-desktop. The SDK's native, source, and cli features do not enable application IPC.

Generate

Install protoc explicitly. The generator never downloads tools. The SDK options schema is embedded in this crate and automatically included; installed CLI consumers do not need the crate's source directory.

use webui_desktop_build::{generate, GenerateConfig};

let generated = generate(&GenerateConfig {
    roots: vec!["schema/application.proto".into()],
    includes: vec!["schema".into()],
    rust_out: "src/generated".into(),
    ts_out: "client/generated".into(),
    lock_file: "schema/ipc-schema.lock.json".into(),
    check: false,
    protoc: None, // PROTOC, then PATH, or an explicit executable
})?;
# Ok::<(), webui_desktop_build::GenerateError>(())

Create the lock file's parent directory first. Relative paths are relative to the invoking process's working directory. Inputs and import directories are canonicalized, and command arguments preserve paths containing spaces. An explicit protoc path takes precedence over PROTOC, then PATH. Windows compiler inputs and outputs must use local drive paths; unsupported UNC/device namespaces fail with ipc-tool-path.

Application schemas import webui/ipc/options.proto, declare a positive contract_major and nonempty contract_name, and set each service's receiver to HOST or RENDERER. Every method needs a globally unique explicit ID above 1023. Notifications require both notification = true and the response marker webui.ipc.Notification. A response of google.protobuf.Empty instead generates an acknowledged RPC returning Rust () / TypeScript void.

Outputs

  • Rust: ipc.rs, ipc_messages.rs, and generated payload codec modules. Include ipc.rs in an application module. messages contains the package tree; service-named modules contain Rpc / Event markers. HostHandler / register_host and RendererClient are generated from correspondingly named services. The host trait includes both RPC methods (RequestContext) and notification methods (NotificationContext). register_host preflights and installs both kinds before frame startup. Notification definitions are installed for each admitted document and count toward callback limits. Optional dynamic host event subscriptions remain in the service module, such as host::subscribe_selected.
  • TypeScript: ipc.ts, its private ipc-runtime.ts support module, and application codec modules. Application code uses connectDesktop, HostClient, RendererHandlers, RendererEvents, AppConnection, and schemaHash from ipc.ts. Message types are exported by their codec modules. The first explicit connectDesktop() loads the browser-only @microsoft/webui-desktop runtime and WebUI payload codecs; importing the bindings alone does not. Concurrent calls share loading, not connections. Bundle with ESM code splitting and deploy all emitted chunks for deferred transfer/parsing. Type-only imports require no runtime load. Load failures reject the connection promise without transport activation or retries.
  • Beside the lock: ipc-schema.json (normalized semantic schema) and ipc-generated-files.json (owned artifact inventory).
  • GeneratedFiles returns the paths and common SHA-256 schema hash.

Commit the lock and generated outputs together. check: true recompiles and compares all artifacts without rewriting them. Missing, changed, or obsolete artifacts return GenerateError::Drift. Normal generation removes obsolete modules listed in the prior inventory, without deleting unrelated files. Generated TypeScript omits host compiler-version comments, so differing supported protoc versions do not create metadata-only drift.

Every current and obsolete artifact, including the lock, manifest, and inventory, is validated before publication. Output roots, artifact files, and parents beneath those roots must be real directories/regular files, not symlinks (including dangling or internal symlinks). Ancestors above the configured roots are canonicalized. Invalid destinations return ipc-output-path; distinct artifacts cannot resolve to one destination.

Generation stages every changed file beside its destination before replacing anything. It retains originals through payload updates and obsolete-file removals, then advances the lock and inventory last. A publication failure rolls back completed operations, including newly created files/directories. Changed artifacts are rechecked before replacement/removal; concurrent changes return ipc-output-changed rather than being overwritten.

This is rollback on reported filesystem failures, not atomic multi-file visibility or power-loss/crash atomicity. Do not run concurrent writers or read a deployment while generating across multiple roots. Path checks are not an OS-level defense against adversarial filesystem races. If rollback itself fails, ipc-publication-rollback preserves staging backups for recovery. Post-publication cleanup failures explicitly report ipc-publication-cleanup with the committed state; they do not claim that publication was rolled back.

Schema names are also checked before publication. A renderer method that normalizes to new conflicts with the generated client constructor, and service/marker names that shadow generated modules or runtime types return ipc-name-collision. Normal Rust keywords such as a method named Type are escaped; colliding normalized names are rejected rather than emitting invalid bindings.

Types and bounds

WebUI codegen produces the Rust and TypeScript binary payload codecs. Integers retain their full protobuf-compatible ranges; JavaScript 64-bit fields are bigint, bytes are Uint8Array, unknown enum numbers are retained, optional presence is preserved, and oneof values use discriminated unions. Repeated fields and acyclic nested messages are supported. Generated metadata drives the runtime's iterative object and wire validators before codec use.

All protobuf maps use JavaScript Map<K, V> (useMapType=true), never object dictionaries. String keys stay strings, bool keys stay booleans, 32-bit integer keys are numbers, and all 64-bit integer keys are lossless bigints. Validators check native key types and integer ranges without string coercion. Strings such as __proto__, constructor, and prototype are ordinary safe Map keys. Rust uses BTreeMap<K, V> with the corresponding native key types.

Generation rejects proto2, application extensions, unrecognized custom options, streaming, recursive message graphs, groups, and well-known types other than Empty. The Notification marker cannot be used as application data. Graphs are limited to depth 16, 4096 messages, and 65536 fields; root source and descriptor files are capped at 16 MiB. Compiler diagnostics are capped at 16 KiB.

Compatibility and failures

The schema hash excludes source locations/comments and sorts messages, fields, enums, and method identities deterministically. Both endpoints require the same hash and major. Method IDs are permanent: removal retires an ID, and a retired ID cannot be reused. Intentional method renames require an explicit lock-name update retaining the ID. Changing a method signature or field presence requires a major increment. Existing protobuf field types cannot change; removed fields and enum values must be reserved by number and name. Reservation history is preserved, including temporarily disconnected types.

GenerateError::code() exposes stable diagnostic codes. Schema diagnostics carry qualified descriptor context and actionable help; protoc failures retain its file/line diagnostic. Tool and filesystem failures are explicit and do not silently fall back to another compiler or application DTO format.

Targeted verification

cargo test -p microsoft-webui-desktop-build
cargo test -p microsoft-webui-desktop --no-default-features --features application-ipc --test codecs
node crates/webui-desktop-build/tests/run-typescript.mjs
node crates/webui-desktop-build/tests/run-rust.mjs

To deliberately regenerate the shared checked-in test fixture:

WEBUI_UPDATE_IPC_FIXTURE=1 cargo test -p microsoft-webui-desktop-build --test generate shared_fixture_matches_generator
WEBUI_UPDATE_IPC_FIXTURE=1 cargo test -p microsoft-webui-desktop --no-default-features --features application-ipc --test codecs
WEBUI_UPDATE_IPC_FIXTURE=1 node crates/webui-desktop-build/tests/run-typescript.mjs