Node forms
- STRING — a text node: “Hello”
- BIND — { “bind”: “path.to.value” } renders the value as text; ’.’ binds the scope itself
- PATHS — dot paths against the current scope (each item inside EACH). Escapes: ‘root.x’ reads the top-level data from any depth; ‘parent.index’ is the zero-based position in the innermost EACH
- EACH — { “each”: “path.to.array”, “template”: <node>, “empty”?: <node> } repeats template with each item as scope
- WHEN — { “when”: “path”, “template”: <node>, “else”?: <node> } renders template when the value is truthy
- ELEMENT — { “tag”: “div”, “attrs”?: { “class”: “x”, “src”: { “bind”: “path” } }, “children”?: [<node>…] }
- ATTR MAP — { “bind”: “status”, “map”: { “do-not-contact”: “wg-status wg-status-danger”, “active”: “wg-status wg-status-success” }, “default”: “wg-status” } — the bound value SELECTS one of your literals (semantic classes from data values); a miss emits default, or empty without one
- ATTR PREFIX — { “bind”: “email”, “prefix”: “mailto:” } — emits prefix+value only when the value is non-empty (mailto:/tel: links; both schemes are allowed on href). One transform per attr value: map OR prefix, never both
- ACTION — an element may carry “action”: { “ref”: “<shared action name>” } or an inline { “definition”: { “kind”: “prompt”, “text”: [“Show the forecast for ”, { “bind”: “city” }] } } / { “definition”: { “kind”: “http”, “method”: “GET”, “url”: “https://…”, “input”: <schema>, “output”: <schema> } }, plus “input”: { “<field>”: “<path>” | { “const”: <value> } } and “output”: { “mode”?: “replace”|“merge”|“patch”, “path”?, “map”? }. Bindings resolve at render time; buttons and links (never both href and action) become activatable in Apps hosts. A widget-level “load” (http GET only) runs once when the widget first renders
Safety rules
- Attribute names matching on* and srcdoc are forbidden (FORBIDDEN_ATTRIBUTE).
- script, iframe, frame, frameset, object, embed, style, link, meta, base, template and noscript are forbidden (FORBIDDEN_TAG) — a template is data, never active content.
- On href, src, action, formaction, xlink:href, data, poster, ping: only http, https, mailto, tel schemes or relative references survive rendering.
- Exception: an img element’s src additionally accepts base64 data:image/*;base64, URIs.
- Bindings only ever produce text and attribute strings — bound values can never inject markup.
Bounds
Validation rejects deeper nesting; interpretation stops at the node budget and marks the render truncated.
Actions bound from templates are specified on Action definition; entry shapes on Authoring contract.