// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
syntax = "proto3";
package webui;
// Per-component metadata keyed by tag name.
message ComponentData {
// Non-WebUI client-side template payload.
// FAST plugins store complete <f-template> HTML here.
string template = 1;
// Compiled component CSS content for the Style and Module strategies.
// Empty for Link and for components without CSS.
string css = 2;
// External stylesheet href for the Link CSS strategy.
// Set to e.g. "/my-card.css" when the component has a CSS file and the
// build used CssStrategy::Link. Empty for Style/Module strategies and
// for components without CSS.
// Ordered style closures decide which Document or ShadowRoot owns the link.
string css_href = 3;
// WebUI plugin JSON-safe component metadata.
// Stored separately from executable condition closures so handlers can emit
// the static metadata in application/json data blocks.
string template_json = 4;
// WebUI plugin component-local JavaScript condition closure array.
// Empty when the component has no conditions or boolean attributes.
string template_functions = 5;
// Exact @observable + @attr property keys when hydration_mode is KEYS.
// Existing SSR host attributes take precedence; bootstrap state fills
// attribute-backed properties that were not materialized on the host.
// Template roots already exist in the trusted SSR DOM and do not enter
// initial state solely because a component has authored browser code.
// Sorted and deduplicated; empty for NONE and ALL.
repeated string hydration_keys = 6;
// Exact partial-navigation keys when navigation_mode is KEYS. Authored
// components use @observable + @attr property keys plus template roots.
// Scriptless compiled templates include only their template roots.
// Sorted and deduplicated; empty for NONE and ALL.
repeated string navigation_keys = 7;
// How initial hydration state is selected for this component.
StateProjectionMode hydration_mode = 8;
// How partial-navigation state is selected for this component. Presence is
// significant: protocols produced before projection metadata leave this
// absent, so handlers preserve full state instead of treating it as NONE.
optional StateProjectionMode navigation_mode = 9;
// Whether this component effectively uses Shadow DOM after build-time
// fallback and authored declarative Shadow DOM resolution.
bool uses_shadow_dom = 10;
// Compiler-owned client work policy. Zero/eager is absent on the wire.
ComponentWorkPolicy work_policy = 11;
}
// Per-component client work scheduling policy.
enum ComponentWorkPolicy {
COMPONENT_WORK_POLICY_EAGER = 0;
COMPONENT_WORK_POLICY_LAZY_HYDRATION = 1;
COMPONENT_WORK_POLICY_LAZY_RENDER = 2;
COMPONENT_WORK_POLICY_INTERACTION = 3;
COMPONENT_WORK_POLICY_LAZY_RENDER_INTERACTION = 4;
}
// Link stylesheet metadata for one static component asset root.
//
// Shadow builds publish this finite manifest so the framework can begin CSS
// beside the root module on interaction. Light builds emit the hrefs as
// deduplicated document stylesheets because their CSS is globally scoped.
message ComponentAssetStylePreload {
// Root component tag used by defineComponentAssets().
string root = 1;
// Final Link-mode stylesheet hrefs required by this asset graph.
repeated string style_hrefs = 2;
}
// Build-wide initial-state behavior.
enum InitialStateStrategy {
// Emit the complete state object. Used by default and non-WebUI plugins.
INITIAL_STATE_STRATEGY_FULL = 0;
// Select state from reachable WebUI component surfaces.
INITIAL_STATE_STRATEGY_COMPONENTS = 1;
}
// Per-component state selection.
enum StateProjectionMode {
// The component is known not to consume state through this channel.
STATE_PROJECTION_MODE_NONE = 0;
// Select the component's explicit sorted key list.
STATE_PROJECTION_MODE_KEYS = 1;
// The complete surface is unknown, so preserve correctness with full state.
STATE_PROJECTION_MODE_ALL = 2;
}
// CSS delivery strategy chosen at build time.
enum CssStrategy {
CSS_STRATEGY_LINK = 0;
CSS_STRATEGY_STYLE = 1;
CSS_STRATEGY_MODULE = 2;
}
// Root protocol containing all fragment records and build metadata.
message WebUIProtocol {
map<string, FragmentList> fragments = 1;
// Sorted, deduplicated CSS custom property names used across all components
// and entry page styles (e.g., "colorBrandBackground", "spacingMedium").
// Only usage via var(--name) is included; local definitions are excluded.
repeated string tokens = 2;
// Per-component data keyed by tag name (f-template + CSS).
map<string, ComponentData> components = 3;
// Build-wide CSS delivery strategy. Determines how compiled component
// resources are installed in each CSS tree.
CssStrategy css_strategy = 4;
reserved 5;
reserved "dom_strategy";
// Whether initial SSR metadata contains full state or WebUI component
// projection. Partial-navigation state remains component-driven.
InitialStateStrategy initial_state_strategy = 6;
// Ready-to-write `<link rel="modulepreload">` hrefs for the shared chunks
// that the page's critical module entries statically import, in the exact
// order they must be emitted (largest chunk first).
//
// These chunks are named only inside the entry's own bytes, so the browser's
// preload scanner cannot discover them until it has downloaded and parsed
// the entry. Emitting them as hints removes that serialization.
//
// Order is load-bearing: preloads are issued in document order over one
// connection, so a small chunk ahead of a large one delays the long pole.
// The compiler resolves and sorts these at build time so the handler writes
// them verbatim with no per-request work.
repeated string module_preloads = 7;
// Build-generated document-level rules for component rendering policies.
// Empty when no component opts into lazy rendering.
string component_render_css = 9;
// Ordered, deduplicated component style resource tags for each Document or
// component-root CSS tree entry point.
map<string, ComponentStyleClosure> style_closures = 10;
// Bundled stylesheets in global cascade order. Empty unless the build
// enabled CSS bundling, in which case every component that owns a style
// resource belongs to exactly one chunk and handlers deliver chunks instead
// of individual component resources.
repeated StyleChunk style_chunks = 11;
// Build-generated eager metadata for static component asset roots.
//
// Entries are sorted by root tag. Builds without component assets omit the
// field from the wire.
repeated ComponentAssetStylePreload component_asset_style_preloads = 12;
}
// Compile-time component style resources for one CSS tree.
message ComponentStyleClosure {
// Component tags in cascade-sensitive first-discovery order.
repeated string component_tags = 1;
// Indices into WebUIProtocol.style_chunks covering exactly the components in
// component_tags, in the same cascade order. Empty unless the build enabled
// CSS bundling. A chunk is listed only when the closure consumes every
// component the chunk carries, so delivering a chunk never over-delivers.
repeated uint32 style_chunks = 2;
}
// One bundled stylesheet carrying a contiguous run of component styles that
// share the same set of consuming CSS trees.
message StyleChunk {
// Stable build-time chunk name. Surfaces as the data-webui-resource value so
// delivered stylesheets stay attributable without a source map.
string name = 1;
// Concatenated compiled CSS for the Style and Module strategies.
// Empty for Link.
string css = 2;
// External stylesheet href for the Link strategy.
// Empty for Style and Module.
string css_href = 3;
// Component tags whose CSS this chunk concatenates, in cascade order.
repeated string component_tags = 4;
}
// A list of fragments (needed because protobuf maps cannot have repeated values directly).
message FragmentList {
repeated WebUIFragment fragments = 1;
// True when this record directly or transitively reaches a boundary
// declaration. Computed once at build time so handlers never graph-walk a
// request merely to decide whether rendering can suspend.
bool contains_boundary = 2;
}
// A single fragment — one of several types.
message WebUIFragment {
oneof fragment {
WebUIFragmentRaw raw = 1;
WebUIFragmentComponent component = 2;
WebUIFragmentFor for_loop = 3;
WebUIFragmentSignal signal = 4;
WebUIFragmentIf if_cond = 5;
WebUIFragmentAttribute attribute = 6;
WebUIFragmentPlugin plugin = 7;
WebUIFragmentRoute route = 8;
WebUIFragmentOutlet outlet = 9;
WebUIFragmentBoundary boundary = 10;
}
}
// Which end of an inline boundary tape a fragment marks.
enum BoundaryPhase {
// Opens a boundary body. Carries the full declaration metadata.
BOUNDARY_PHASE_START = 0;
// Closes the body opened by the matching start in the same record. Only
// declaration_id is meaningful.
BOUNDARY_PHASE_END = 1;
}
// Compile-time streaming boundary declaration, written as an inline tape.
//
// A declaration emits a start marker and an end marker into its owner's
// fragment record, with the body fragments in between. Ordinary rendering
// therefore walks the body without any extra record lookup, and streaming
// suspends and resumes inside the record it is already traversing.
//
// A declaration can produce multiple response-local occurrences through
// components, conditions, loops, and routes. Runtime occurrence IDs are
// therefore assigned later by the handler and are not stored here.
message WebUIFragmentBoundary {
// Stable build-local declaration identity. Shared by the start/end pair.
uint32 declaration_id = 1;
// Entry or reusable component template that authored this declaration.
string owner_fragment_id = 2;
// Free-form authored name, unique only within owner_fragment_id.
string name = 3;
// Optional authored key expression preserved verbatim for handler evaluation.
optional string key = 4;
// Field 5 previously held the separately parsed boundary body record. Bodies
// are now inline between the start and end markers.
reserved 5;
reserved "fragment_id";
// Conservative graph result: this declaration may produce multiple runtime
// occurrences through repeated component callsites. A `<for>` repeat can no
// longer contribute: the build rejects every boundary a repeat body reaches
// (`boundary-in-repeat`), because a repeat iteration cannot suspend.
bool may_repeat = 6;
// Whether this fragment opens or closes the declaration's body.
BoundaryPhase phase = 7;
}
// Declarative route definition linking a URL path template to a component.
// Nested routes are expressed via repeated children.
message WebUIFragmentRoute {
string path = 1;
string fragment_id = 2;
bool exact = 3;
// Nested child routes rendered at the component's <outlet />.
repeated WebUIFragmentRoute children = 4;
// Comma-separated allowlist of query parameters forwarded as attributes.
// Empty string means no query params are forwarded (deny-by-default).
string allowed_query = 5;
// When true, the router preserves the mounted component instance across
// navigations instead of destroying and recreating it. Opt-in per route.
bool keep_alive = 6;
// Cache tag templates for this route (e.g. "thread:{threadId}", "inbox").
// Placeholders like {param} are resolved at render time with actual param values.
repeated string cache_tags = 7;
// Invalidation tag templates (e.g. "inbox", "thread:{threadId}").
// After a mutation action on this route, these tags are auto-invalidated.
// Supports {param} placeholders resolved at render time.
repeated string invalidates = 8;
// Component tag name for the pending/loading UI (e.g. "mail-skeleton").
// Validated at build time — build fails if the component does not exist.
string pending_component = 9;
// Component tag name for the error boundary UI (e.g. "error-page").
// Validated at build time — build fails if the component does not exist.
string error_component = 10;
// Optional runtime content authored directly inside this route. The record
// contains typed boundary references and is rendered only for this route
// path; nested route declarations remain in children.
string content_fragment_id = 11;
}
// Outlet placeholder — marks where matched child route content renders.
// Components use this to indicate where nested route children are rendered.
message WebUIFragmentOutlet {}
// Opaque plugin-specific data passed from parser plugin to handler plugin.
// WebUI framework does not interpret this data — it is a pass-through conduit.
// Each parser/handler plugin pair defines its own binary contract for the data field.
message WebUIFragmentPlugin {
bytes data = 1;
}
// Static text or HTML content.
message WebUIFragmentRaw {
string value = 1;
}
// Reference to a reusable component fragment.
message WebUIFragmentComponent {
string fragment_id = 1;
}
// Loop directive iterating over a collection.
message WebUIFragmentFor {
string item = 1;
string collection = 2;
string fragment_id = 3;
}
// Dynamic data binding via signal.
message WebUIFragmentSignal {
string value = 1;
bool raw = 2;
// True inside an HTML raw-text context such as <style>. Raw-text signals do
// not own replaceable HTML sibling ranges because comments parse as text.
bool raw_text_context = 3;
}
// Conditional rendering fragment.
message WebUIFragmentIf {
ConditionExpr condition = 1;
string fragment_id = 2;
}
// Dynamic attribute binding.
message WebUIFragmentAttribute {
string name = 1;
// For simple dynamic attrs, the signal name.
string value = 2;
// For mixed (template) attrs, the sub-stream ID.
string template = 3;
// True for :-prefixed complex attributes.
bool complex = 4;
// True for the first dynamic attribute on a component element.
bool attr_start = 5;
// True for skipped attributes (class, style, role, data-*, aria-*).
bool attr_skip = 6;
// True for static attribute values on components.
bool raw_value = 7;
// For ?-prefixed boolean attributes, the condition tree.
ConditionExpr condition_tree = 8;
}
// A condition expression tree.
message ConditionExpr {
oneof expr {
Predicate predicate = 1;
NotCondition not = 2;
CompoundCondition compound = 3;
IdentifierCondition identifier = 4;
}
}
// Simple comparison predicate.
message Predicate {
string left = 1;
ComparisonOperator operator = 2;
string right = 3;
}
// Negation wrapper.
message NotCondition {
ConditionExpr condition = 1;
}
// Two conditions joined by a logical operator.
message CompoundCondition {
ConditionExpr left = 1;
LogicalOperator op = 2;
ConditionExpr right = 3;
}
// Single identifier (variable name).
message IdentifierCondition {
string value = 1;
}
enum LogicalOperator {
LOGICAL_OPERATOR_UNSPECIFIED = 0;
AND = 1;
OR = 2;
}
enum ComparisonOperator {
COMPARISON_OPERATOR_UNSPECIFIED = 0;
GREATER_THAN = 1;
LESS_THAN = 2;
EQUAL = 3;
NOT_EQUAL = 4;
GREATER_THAN_OR_EQUAL = 5;
LESS_THAN_OR_EQUAL = 6;
}