Skip to content

Releases

MESH v0.18.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 06:34
db5f62f

MESH v0.18.0 release notes

Prepared 2026-10-30 (not yet published: v0.17.0 is the latest release).

v0.18 gives the units API one word per concept, and adds the context channel (direction decision C-1, K27). MPRX already used "instance" for an element of a component and for a repeat's key; the runtime used it again for one occurrence of a component with a unit. The prose already said "occurrence". Units are Provisional, so the cheapest time to rename is before the first outside dependent (direction decision C-4).

Highlights

  • The rename. Render.instances is Render.occurrences; intent.instance is intent.occurrence; a listed unit's instance is occurrence; a units entry's "instance" key is "occurrence"; the diagnostic runtime-unknown-instance is runtime-unknown-occurrence. In Rust: Render::occurrences, occurrences_to_json, Occurrence, Intent::occurrence(), RuntimeCode::UNKNOWN_OCCURRENCE. The WebAssembly results, render-v1's intent, the specification, the manuals and the conformance vectors (units/step-N.expected.occurrences.json) follow.
  • "Instance" keeps its MPRX meanings (an element of a component; a repeat's key, §9.10). Nothing about the language changes.
  • The old names work for one release, deprecated, and are removed in the next minor. See below.
  • The diagnostic runtime-unknown-instance is retired, never reused. A host that matched it must match runtime-unknown-occurrence.
  • The screen (openScreen) and the argument names on the intent, both Unreleased on the branch, ship in this release; the screen already said occurrence.
  • The context channel. A value many distant components need (a theme, a locale, the current user) reaches them without being threaded through every layer, declared and type-checked. See below.

The context channel

"context": { "theme": { "kind": "enum", "values": ["light", "dark"] } },
"components": { "badge": { "props": {}, "events": {}, "commands": {}, "scope": {}, "uses": ["theme"] }, ... }
  • Declared once, used by name. The manifest's top-level context declares each name and its type. A component lists what it reads in uses; those names join its scope read-only, and mesh check type-checks every read before anything runs. A component that doesn't list a name can't read it: no inheritance, no lookup. manifest-unknown-context is a name the context doesn't declare; manifest-context-clash is one that is also a prop or scope name, or listed twice.
  • Given beside the snapshot. render({ program, model, snapshot, context }); update(render, snapshot, units, context) (without context, the render's own); in Rust render_context and update_with_context. A value that doesn't fit is reported at $context.<name>. Dispatch evaluates against the context the render had.
  • Changed by changes. An edit whose path starts { "context": name } edits that value: { "op": "set", "path": [{ "context": "theme" }], "value": "dark" }. Every node that reads it is evaluated again (in this release the whole tree is rendered again on a context change; the patches are still the difference).
  • Where it is read. A snippet, and the content a slot places, read the context of the template that declared them. Commands are not context. A subtree override ({#with}) and per-subtree providers are left for later.
  • Use props first. Context is for values many distant components need; a value one or two levels down is a prop.

It is Tier 2 and Provisional. The specification gains §9.11, and §9.1 reads "no implicit names: a context name is in scope only where the manifest says the component uses it". The screen takes it too: screen.show(snapshot, context?), which keeps the values last shown when given none.

Migrating

Was Is
render.instances / Render::instances() render.occurrences / Render::occurrences()
each.instance (a listed unit), intent.instance each.occurrence, intent.occurrence (Rust: intent.occurrence())
[ { "instance": key, "state": ... } ] (Rust Units::from_json) "occurrence" (the old key is still read; both in one entry is refused)
mesh_runtime::Instance, instances_to_json() Occurrence, occurrences_to_json()
runtime-unknown-instance runtime-unknown-occurrence

What the runtime writes (the JSON of an intent or of the occurrences, the module's result documents) has the new names only. The JavaScript aliases are not enumerable, so deep comparisons and JSON.stringify see the new shape. In Rust, Intent is Stable and its fields are public, so the instance field stays under its old name, #[deprecated], and Intent::occurrence() reads it: renaming it or adding a second field would break code that builds an Intent. A Rust host that builds Intent { .. } keeps compiling (with a deprecation warning for the field). The field is renamed in the release that removes the aliases.

Compatibility

A program, a manifest, a snapshot and every other document are unchanged; context and uses are new and optional. A manifest that has either is refused by an older MESH (manifest-unknown-property). render-v1, the patches and the intent are unchanged by the context channel. In Rust the new entry points sit beside the old ones, which are the same operations with no context, and Component and Declaration (both #[non_exhaustive]) gain uses and Context. A host that reads render-v1 intents or the module's result documents by the old property names must read the new ones. The module and the package still have to be the same version.

North-star scorecard

M2 (one term per concept): the "instance" collision is removed; M2 stays ◐ while the other collisions remain. Tier 2 (Provisional).

C1 (a value reaches a deep component without threading): ✗ → ✓, the context channel. C2 (the channel is explicit and checkable): not applicable → ✓, declared in the manifest and type-checked by mesh check ahead of run. Tier 2 (Provisional). No audit regresses.

Verification

cargo test --workspace; the runtime package's tests (a new test for each of the old names in both runtimes; the context channel through the package); the conformance vectors for units under their new names, and context/ in both runtimes. Not run in this environment: the runtime package's size test (needs binaryen) and the browser tests.

MESH v0.17.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 14:37
8a019ae

MESH v0.17.0 release notes

Prepared 2026-10-24 (not yet published: v0.16.0 is the latest release).

v0.17 lets a host check generated UI while it streams. A model writes MPRX a few tokens at a time; a batch check of half a file reports a syntax error and nothing else, so a wrong prop is invisible until the file ends. A stream session checks what has arrived.

Highlights

  • openSession (@valancex/mesh-compiler) and mesh_compiler::stream. push(chunk) answers with the diagnostics that are new, the ones standing, what is open, and, while no error stands, a checked preview (a template-v1) to draw as a fresh render. close() is exactly check of the whole text. See the streaming manual.
  • A repair that fails closed. The text is cut back to its last complete token and what is open is closed, by the module. Nothing is kept that was cut inside a start tag, an attribute string, an expression or a block header, an open {#each} is withheld until it closes, and a text run grows. A planted mistake is heard within 20 bytes of where it starts (8 of 8 cases), and every prefix of every valid page checks clean.
  • A budget. 50 ms of checker time per push, then the session ends with check-budget-exceeded. The input limits of 0.13 hold in a session.
  • Command annotations. A command in the manifest may say "effect": "read" | "write" | "destructive" and a "title". A host builds a consent prompt from them, never from text a model wrote. They are annotations: nothing about a check, a template or a fingerprint changes.
  • generated/ in @valancex/mesh-conformance: six pages, eight planted mistakes and the digests of every prefix's step; both runtimes give the same digests.

Compatibility

check, compile, checkProgram, compileProgram, render, update, dispatch and every document are unchanged. The manifest accepts two more optional command properties; a manifest without them is read exactly as before. The WebAssembly compiler module gains one export, mesh_stream; the module and the package still have to be the same version.

Release order

MESH 0.17.0 (its nine packages); then VALANCE, whose @valancex/valance/generate builds the profile, the prompt, the correction loop and the consent prompt on the session.

Verification

cargo test --workspace (the law over 100,000 random prefixes and mutations, the prefixes of every page, the planted mistakes, the digests), cargo clippy --all-targets -- -D warnings, cargo fmt --check, RUSTDOCFLAGS="-D warnings" cargo doc, the grammar's parser regenerated with no difference, number_to_text.py --check; the compiler package's tests (the same law and digests through WebAssembly, the budget, the surrogate pair, the limits), the runtime package's (every preview of every page rendered and compared with the final tree, in three states of the data) and the conformance package's. Shown failing: with the withholding of an open {#each} removed, the native digest test, the repair test and the runtime's preview test fail; with a required-prop report added to the early check of a start tag, the false-positive test fails.

MESH v0.16.0

Choose a tag to compare

@github-actions github-actions released this 24 Oct 03:53
08de6ce

MESH v0.16.0 release notes

Prepared 2026-10-24 (not yet published: v0.15.0 is the latest release).

v0.16 adds no runtime behavior. It publishes what a renderer, a host and a debugger need to check replay: that a recorded session gives the same trees, byte for byte, in every MESH runtime and by every route to them.

Highlights

  • replay/ in @valancex/mesh-conformance. The session-log format (replay/README.md), seven recorded logs and 1,000 random sessions. The format is line-JSON: a header (the program's and the manifest's hashes, the initial snapshot), changes, event (what a PORT reported, the render it was drawn against, and the intent or diagnostic code the runtime returned), tree (a SHA-256 of the compact JSON, and the whole tree at every 50th line and at the end) and an informational result. Render versions are never recorded: they are process-local.
  • Three routes, both runtimes. A conforming player replays a log by an update_changes chain, an update chain over whole snapshots and a render of each snapshot, and requires the same hashes from each. cargo test -p mesh-runtime --test replay (the native runtime, which also records: MESH_BLESS=1) and packages/mesh-runtime/test/replay.test.mjs (the WebAssembly package) both do; the 1,000 random sessions give the same digests in both.

Compatibility

Nothing changes in the compiler, the runtime, the manifest, render-v1, render-patch-v1 or template-v1/v2. @valancex/mesh-conformance gains files (conformance/replay/…); a consumer that reads the other directories is unaffected.

Release order

MESH 0.16.0 (its nine packages); then VALANCE, whose @valancex/conformance runs the logs (vlx replay) and records its own.

Verification

cargo test --workspace, cargo clippy --all-targets -- -D warnings, cargo fmt --check, RUSTDOCFLAGS="-D warnings" cargo doc, the grammar's parser regenerated with no difference, number_to_text.py --check; the runtime package's tests (including the replay test and the browser test with MESH_CHROMIUM), the compiler's and the conformance package's. The wrong-hash check of each player fails a log whose tree hash was changed.

MESH v0.15.0

Choose a tag to compare

@github-actions github-actions released this 23 Oct 10:41
abb664d

MESH v0.15.0 release notes

Prepared 2026-10-23 (not yet published: v0.14.0 is the latest release).

v0.15 gives a component state of its own for one occurrence of it: whether a disclosure is open, which tab is chosen. A manifest, a program and a snapshot that use none of it compile and render as they did in 0.14.0: the same templates, the same render trees byte for byte, the same patches.

Highlights

  • A component's unit. "unit": { "state": { "open": { "type": …, "default": false } }, "commands": { "toggle": { "parameters": [] } } } in a component's manifest entry. The state names join the component's scope (read-only) and the commands join its commands, so a template reads open and runs toggle() like anything else and checking needs nothing new. A name that is already taken is manifest-unit-clash. See the manifest reference.
  • The state is an input beside the snapshot. render, update, updateChanges and dispatch take the state the host holds for the occurrences it has changed (render_units, update_with_units, update_changes_units, dispatch_from_units in Rust; units in JavaScript). An occurrence with no entry has the defaults, which is also what the first render on a server and in a browser use, so they agree. The runtime holds none of it, so a render is still a function of its inputs and the equivalence law of update holds with units in it. See Units.
  • A render lists the occurrences it met (render.instances: the key, the component, the state the template saw, the fields the caller owned), so a host can keep the state of the occurrences that exist and let go of the rest. An occurrence's key is its place in the program, by the item's declared key inside a repeat: a re-sorted list keeps the state with the row; hiding and showing, or a changed key, is a new occurrence.
  • A unit's command names its occurrence. The intent gains instance and, when the caller owns a field, controlled. The host runs the unit's transition on the occurrence's state and renders again.
  • controllable fields. A prop of the same name, when the caller writes it, is the value, and the unit's change to it is not applied: one copy of the value, owned by whoever wants it.
  • raise(render, instance, event, payload) resolves an event a unit announces to the handler its caller bound to it.
  • An update does not keep a part that a unit under it has changed. The state of the units below a part is one of its inputs.
  • New diagnostics: manifest-unit-clash, runtime-unknown-instance, runtime-unknown-event.
  • Conformance vectors: units/, with a committed tree and list of occurrences for each step, run natively and through the package.

Compatibility

  • API: additive. The Rust Intent gains two fields, and Component gains unit (both were open to growth: Component is non_exhaustive).
  • Manifest: a manifest with a unit is refused by an older MESH (manifest-unknown-property); one without is read as before. manifest-v1 still means what it meant.
  • render-v1, render-patch-v1, template-v1/v2: unchanged.
  • WebAssembly: the module's exports are internal and changed (units in, occurrences out, mesh_raise); @valancex/mesh-runtime and its module are one version, as always.
  • Size: the module grows about 3.5 percent (gzipped 234,818 to about 243,000 bytes).

Release order

MESH 0.15.0 (its nine packages), NEXUS 0.14.0, PORT unchanged, then the VALANCE packages, which depend on MESH 0.15 and NEXUS 0.14 for units.

Verification

cargo test --workspace, cargo clippy --all-targets -- -D warnings, cargo fmt --check; the runtime package's tests (including the browser test with MESH_CHROMIUM), the compiler package's tests, and the conformance package's. Each new rule was shown failing against a deliberate wrong value: the update that keeps a part when a unit under it changed (the test fails when the check is removed), a reorder (the committed vectors), a clash of names (the fixture), and a state the manifest forbids (the diagnostic).

MESH v0.14.0

Choose a tag to compare

@github-actions github-actions released this 18 Oct 14:20
edc1cbd

MESH v0.14.0 release notes

Prepared 2026-10-18 (not yet published: v0.13.0 is the latest release).

v0.14 gives MPRX its first new types and its first new template construct since 0.10. A program, a manifest and a template that use none of it compile and render as they did in 0.13.0: the same template, fingerprint, render tree and keys (a test installs 0.13.0 from the registry and compares).

Highlights

  • Enum types. { "kind": "enum", "values": [...] }: a string that is one of a closed set. A literal that is not a member is unknown-variant with a did-you-mean; the editor completes the members inside the quotes of a prop value and beside == and !=.
  • has(x) and x ?? y, a presence test and a default; defaults for props (default in the manifest). They make a template template-v2; the compiler writes v2 only for a template that uses them, and a v1 document that contains them is malformed.
  • Snippets. {#snippet name(a, b)} … {/snippet} and {@render name(args)}: a piece of template with parameters, declared in a template and passed to a component (a prop of kind snippet, with children as the content between the tags), or kept local and rendered, recursively if it likes (64 levels). A snippet reads the scope it was declared in. A local snippet can be passed along as a prop value (row={fmt}). An optional snippet must be rendered inside {#if has(name)} (possibly-absent-snippet).
  • A composite that only forwards. A template that is only {@render children()} makes no node of its own.
  • Language server: completion of snippet names, snippet parameters and enum members; hover and go to definition for snippets; signature help in a render's arguments.
  • Smaller compiler. The WebAssembly is optimized after linking: 320,778 gzipped bytes become 301,420.

Compatibility

  • @valancex/mesh-compiler and @valancex/mesh-runtime 0.13.0 read a manifest that uses a new kind as manifest-unknown-kind, and a template-v2 document as assembly-unsupported-format-version: nothing older misreads them.
  • Identity: a render site adds the step 0x08, a forwarder the fragment step 0x07 before it. A program that has neither keeps its keys.
  • New diagnostics: unknown-variant (with enums), unknown-snippet, snippet-arity-mismatch, duplicate-snippet, children-conflict, possibly-absent-snippet, the manifest codes manifest-snippet-misplaced and manifest-children-not-snippet, and the runtime codes assembly-snippet-to-primitive, assembly-snippet-root-scope, runtime-snippet-depth. See the diagnostics manual.

Verification

cargo test --workspace, cargo clippy --workspace --all-targets -- -D warnings, cargo fmt --check; the package tests (compiler, runtime, conformance, language server, editor grammar) including those that run Chromium; conformance vectors enums/, snippets/, forwarder/ and passing/ in Rust and JavaScript; and the replay hashes of evidence/language/replay.mjs, which are the same on 0.13.0 and 0.14.0.

MESH v0.13.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 04:22
a9f6200

MESH v0.13.0 release notes

Prepared 2026-10-17 (not yet released: v0.12.0 is the latest release).

v0.13 fixes how MPRX reads a space between two interpolations. <text>{first} {last}</text> rendered AdaLovelace: the space is a whitespace-only text child, and whitespace-only text is formatting (spec §3) and is dropped. A Valance application found it writing #{r.id} {r.customer} and worked around it with a separator character. A change in rendered text, so a minor version; nothing else changes.

Highlights

  • A space on the line is content. A whitespace-only text child that has no line break and an interpolation immediately before and after it is kept verbatim, as part of the text run. {first} {last} reads "Ada Lovelace"; {a} {b} (two spaces) keeps both; #{id} {name} ! is one run.
  • Layout is still layout. A line break is formatting wherever it is: {a} and {b} on separate lines read ab, as before, so a person or a formatter that re-wraps a template between two interpolations changes nothing it renders. Whitespace next to an element, a block, a slot or the start or end of the content is dropped as before (<b>x</b> <i>y</i> still has no space between the two elements; nothing asks for that yet, and a stray text child is not harmless on every target).
  • {" "} still works where a space must sit at a line break or beside an element.

The rule is the one JSX and Vue (condensed) use: whitespace with a line break is layout, whitespace without one is content, narrowed here to the case the spec's own model of a text run already implied (§9.7: a run is literal text and interpolations concatenated).

Upgrading

Move @valancex/mesh-runtime, @valancex/mesh-compiler and @valancex/mesh-lsp to ^0.13.0 together (before 1.0 a minor is a new range). Search your templates for a space between two interpolations: {a} {b} on one line now renders its space. A template with none renders exactly what it did, with the same keys and handler identifiers; templates, manifests and programs compiled with 0.12 mean the same under 0.13 apart from this.

Compatibility

  • Rendered text. The one change, above. The semantic model keeps a whitespace-only text child it used to drop (spec §3), so a template compiled with 0.13 has a text child in that place and one compiled with 0.12 has none; both are valid mesh-template documents and run on either runtime.
  • Rust and npm. No API changes. All packages move to 0.13.0 together.
  • Tooling. mesh-format and the language server leave the space alone (it is content); the grammar does not change.
  • No diagnostics change.

Stability

The rule (a space with no line break between two interpolations is content; a line break is formatting; neighbours other than interpolations are unchanged) is stable on release. Whether whitespace between two inline elements should be content too is open, and would be a later minor with its own note.

Verification

cargo test --workspace, cargo clippy --workspace --all-targets -- -D warnings, cargo fmt --check, cargo doc with warnings as errors; five new runtime tests (mesh-runtime/tests/whitespace.rs: three fail without the change) and one formatter test; the design note is docs/superpowers/specs/2026-10-16-mprx-whitespace-between-interpolations.md.

MESH v0.12.0

Choose a tag to compare

@github-actions github-actions released this 14 Oct 01:11
9956198

MESH v0.12.0 release notes

Prepared 2026-10-13 (not yet released: v0.11.0 is the latest release).

v0.12 teaches the editor to format and indent MPRX. mesh-lsp answers Format Document, Format Selection and the indentation of the new line after Enter, so an .mprx file behaves in an editor the way HTML, XML or YAML does: the editor understands its scope. Nothing in the language, the compiler, the runtime or any document format changes; an application that changes nothing behaves as before.

Highlights

  • A formatter: mesh-format. A new crate that changes only whitespace between tokens. Whitespace-only text between tags, {expressions} and block markers is formatting (spec §3), and is re-indented: one level per element and per {#if} / {#each}, with {:else}, {:else if} and the closers one level out. A tag already written over several lines gets one attribute per line, with its > or /> on a line of its own; a tag on one line stays on one line. A run of blank lines becomes one, and the text ends with one newline. Text with a character other than whitespace in it, and everything inside {…}, is kept exactly as written.

    <list>                                  <list>
    {#if ready}                               {#if ready}
    <a />                  becomes              <a />
    {:else}                                   {:else}
        <b/>                                    <b />
    {/if}                                     {/if}
    </list>                                 </list>
  • It checks its own work. The tokens of the result must be exactly those of the source, or the formatter returns an error and the source stands. A source with syntax errors is not formatted (the structure its indentation follows is not known).

  • Three LSP requests. textDocument/formatting (Format Document), rangeFormatting (Format Selection: the edits of the whole formatting that touch the selection) and onTypeFormatting (trigger character: a newline; the new line is indented by the scope it is in, and this works while the file does not parse yet). They need no model and no check, and are never held. Indentation follows the editor's tabSize and insertSpaces.

  • Indentation rules for editors that read a language configuration. editors/vscode/language-configuration.json carries indent and outdent patterns and Enter rules (between a tag and its closer, between a block opener and its closer); the VS Code language configuration turns on editor.autoIndent and editor.formatOnType for MPRX. Other editors use the server.

Upgrading

Nothing has to change. Move @valancex/mesh-runtime, @valancex/mesh-compiler and @valancex/mesh-lsp to ^0.12.0 together (before 1.0 a minor is a new range) to get formatting; templates, manifests and programs compiled with 0.11 mean the same under 0.12.

To format from an editor that is not VS Code, ask the server: textDocument/formatting with tabSize and insertSpaces in options (manual).

Compatibility

  • Additive. Three new LSP capabilities (documentFormattingProvider, documentRangeFormattingProvider, documentOnTypeFormattingProvider) in initialize's result. A client that does not use them is unaffected.
  • Rust. A new workspace crate, mesh-format (format, format_edits, format_range_edits, indent_for_line, Options, Edit, Error); mesh-lsp depends on it. No existing crate's API changes.
  • npm. All packages move to 0.12.0 together; compileProgram, render and the other entries are unchanged.
  • No diagnostics change.

Stability

mesh-format's guarantee (only whitespace between tokens changes) and the three requests are stable on release. What the layout looks like (one attribute per line in a multi-line tag; blank lines collapsed; text with content never re-indented) can be refined in a later minor, always within that guarantee, and the changelog will say so.

Verification

cargo test --workspace (934 tests), cargo clippy --workspace --all-targets, cargo fmt --check, cargo doc with warnings as errors; mesh-format's 18 tests (layout, tabs, idempotence, selection, every valid example and fixture formats and stays formatted, indentation on Enter); the protocol tests for the three requests; the editor's indentation-rule tests.

MESH v0.11.0

Choose a tag to compare

@github-actions github-actions released this 13 Oct 14:42
5b48220

MESH v0.11.0 release notes

Prepared 2026-10-13 (not yet released: v0.10.0 is the latest release).

v0.11 makes control flow part of the language. Conditional and repeated structure are written as blocks in an element's content, not as reserved elements, so a tool that lists a model's components has nothing to offer for control flow and a model declares nothing for it.

Highlights

  • Blocks. {#if condition} … {:else if condition} … {:else} … {/if}, and {#each items as item (key)} … {/each}. A chain of {:else if} branches replaces the multi-way choice. The key of a repeat is mandatory.

    <list>
      {#if loading}
        <spinner />
      {:else if failed}
        <note>Could not load</note>
      {:else}
        {#each users as user (user.id)}
          <user-card user={user} />
        {/each}
      {/if}
    </list>
  • A branch or body is one element. If it is exactly one element it is that element; anything else (text, several nodes, none, a lone block or slot) is wrapped in a mesh-fragment for you.

  • Nothing to declare. The constructs a block lowers to (mesh-if, mesh-each, mesh-fragment) are built in. A manifest need not declare them, one that does is used as declared, and the model fingerprint covers only what is declared.

  • Same templates. The runtime, template-v1, render-v1, node identities and handler identifiers do not change. A program written with the old elements compiles to the same templates, apart from source spans.

  • Better messages. A mistake in a block names the block ("the condition of {#if}"). A repeat without a key is missing-repeat-key. Syntax errors in blocks say what is wrong and where: never closed, no open block, malformed opener.

  • Editors. The Tree-sitter grammar and highlight queries, the TextMate grammar, completion after the openers, and hover and go-to-definition on a repeat's binding.

Upgrading

  1. Move @valancex/mesh-runtime and @valancex/mesh-compiler (and mesh-lsp) to ^0.11.0 together. Before 1.0 a minor is a new range.

  2. Rewrite control flow as blocks. retired-element tells you which block to write.

    Before After
    <mesh-if when={a}>X</mesh-if> {#if a}X{/if}
    <mesh-if when={a}>X Y</mesh-if> (two alternatives) {#if a}X{:else}Y{/if}
    <mesh-each items={xs} as="x" key={x.id}>X</mesh-each> {#each xs as x (x.id)}X{/each}
    <mesh-switch><mesh-case when={a}>X</mesh-case><mesh-default>Y</mesh-default></mesh-switch> {#if a}X{:else}Y{/if}

    A two-alternative mesh-if and an {#if}/{:else} are both conditional sites, but an {:else} is written as a second conditional (as a mesh-switch was), so node identities under the else branch differ from the second alternative of the old element.

  3. A model that declared mesh-if, mesh-each, mesh-switch, mesh-case or mesh-default can drop them; one that keeps them is used as declared.

Compatibility

  • Breaking, before 1.0. The reserved elements are errors. Templates already compiled keep their meaning: stored renders and handler identifiers are unchanged.
  • Tooling. Tag completion and component listing offer nothing for a block. Target::Binding is a new resolution target (a Rust match on Target needs an arm).
  • Diagnostics. New codes retired-element and missing-repeat-key. invalid-switch is no longer reported and stays in the catalogue.
  • npm. All packages move to 0.11.0 together.

Stability

Blocks, retired-element and missing-repeat-key are stable on release. The runtime's behavior for the constructs a template names (mesh-if, mesh-each, mesh-fragment and their identity steps) stays provisional.

MESH v0.10.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 04:14
da4d9b9

MESH v0.10.0 release notes

Prepared 2026-10-10 (not yet released: v0.9.0 is the latest release).

v0.10 makes an application cheap to update and cheap to compose. The runtime turns a new snapshot, or just the edits to the last one, into patches that touch only what changed; composites take children, named slots and events; and a program's composites no longer need a hand-written contract. The public APIs were reviewed against the API design principles before this release; the review record is docs/superpowers/specs/2026-10-09-api-design-review.md.

Highlights

  • Updates. update(render, snapshot) and updateChanges(render, { base, changes }) return the new render and render-patch-v1 patches (setProp, removeProp, setText, insert, remove, move, replace). A node whose inputs are unchanged is the same node, and the work is proportional to the change: on a keyed list of 10,000 items with one changed, about 22 ms from JavaScript through changes, against 460 ms for a full render. diff(previous, next) computes the edits between two snapshots. render.version, render.release() and [Symbol.dispose] manage what the module keeps; render() keeps nothing.
  • Composition. A composite's template places an occurrence's children with mesh-slot, and, by name, the mesh-fills of an occurrence with <mesh-slot name>. A composite declares events and forwards them by naming one as a handler. Identity extends without changing any existing key.
  • No contract to write. compileProgram infers the manifest entry of every listed component the manifest does not declare: props from the arguments its occurrences pass, events from the handlers it forwards. The reserved tags a program uses are declared for it. A declared entry is used as written.
  • mesh-fragment. Content without a node: a composite, a conditional alternative, a switch case or a repeat item can be text or several nodes by writing a fragment, and MESH wraps nothing on the author's behalf. Provisional. A fragment cannot start the program's root (assembly-root-fragment).
  • Maximal text runs. Text on either side of a conditional or a repeat that chose nothing is now one run, as a render tree's runs are meant to be, so a server's single text node can be matched by the client.
  • mesh-switch. Cases with a boolean when, and a last default; the first that holds is rendered. The compiler writes it as the mesh-ifs it stands for, so template-v1 and the runtime are unchanged. Provisional, like mesh-if.
  • Errors you can match on. MeshInternalError, MeshVersionError and the new MeshUsageError carry a stable code; runtime diagnostics gain an optional hint. New diagnostic codes: invalid-switch, runtime-changes-base-mismatch, runtime-invalid-change, runtime-changes-disagree.
  • Stability tiers. API stability says what each surface promises: Stable, Provisional (mesh-if, mesh-each, mesh-switch) and Internal.

Compatibility

  • Additive for hosts. render, dispatch, declaredEvents, compile, check, checkProgram, template-v1, render-v1 and the manifest format are unchanged, and a program that does not use the new constructs renders exactly as in 0.9. mesh-slot gains an optional name prop; a manifest that declares it without one still works for the default slot.
  • Rust. Tree::root and TreeChild::Node hold an Rc<Node> (code that reads a tree is unchanged by auto-deref; code that builds one, which only a render does, is not). update_with takes the snapshot instead of copying it.
  • Messages. assembly-composite-children now covers fills as well as children, and its message changed; match on the code.
  • npm. All packages move to 0.10.0 together. Before 1.0 a minor is a new range: a package that depends on @valancex/mesh-compiler or @valancex/mesh-runtime ^0.9.0 does not receive 0.10.
  • WebAssembly. The modules' exports are internal and changed (mesh_update, mesh_update_changes, mesh_release, mesh_infer and others); a package checks for the exports it needs, so a package is always built with its own module.

Not decided

Recursion in composites, and whether mesh-switch's lowering should become a runtime construct.

MESH v0.9.0

Choose a tag to compare

@github-actions github-actions released this 04 Oct 09:51
ef608d0

MESH v0.9.0 release notes

Released 2026-10-04 (v0.9.0).

v0.9 gives build tools one operation that turns a program's components into the program's parts. It adds no MPRX syntax, changes no format and changes no runtime behavior: the compiler package gains compileProgram.

Highlights

  • compileProgram({ model, root, components }) in @valancex/mesh-compiler is the build-time program assembly boundary. It compiles each listed component ({ component, source, path }) against one manifest (model: { manifest, path }), checks the program they make, and returns a promise of { components, assembly?, program? }.
  • The program check is checkProgram. The assembled parts are given to the existing checkProgram, so a program is valid for compileProgram exactly when it is valid for mesh check-program and for the runtime's render. There is no second set of checks. A refusal is checkProgram's own document, in assembly; a component that does not compile is compile's own diagnostics document, in components.
  • The program is the existing representation. program is { model, root, templates }: the manifest's text, the root component, and the templates as text, in the order of components. It is the ProgramInput that checkProgram takes and that the runtime's render takes, and the value a host assembled by hand from compile before. compileProgram writes each template out as text itself, so a caller never converts between the object compile returns and the text the program takes.
  • Present only when nothing failed. program exists when no component had an error and the program check found no diagnostics. Warnings do not stop it. Every component is compiled even when an earlier one fails, so one run reports every component's errors.
  • No dependencies, no initialization. The compiler package still depends on nothing: not the runtime, and not VALANCE, NEXUS or PORT. compileProgram needs no runtime initialization, as compile and checkProgram do not.

Compatibility

  • Additive. compile, check, checkProgram, the runtime, template-v1, render-v1, the manifest format and model fingerprints are unchanged. The Rust crates and the CLI gain nothing.
  • A component that is not listed has no template, so it is a primitive, as it always was: nothing in compileProgram finds components, so the caller lists those that have a template.
  • npm: @valancex/mesh-compiler has compileProgram() and the types CompileProgramInput, CompileProgramResult and ProgramComponent. All packages move to 0.9.0 together, as before.
  • Ranges. Before 1.0 a minor version is a new range: a package that depends on @valancex/mesh-compiler or @valancex/mesh-runtime ^0.8.0 does not receive 0.9. A host that wants one runtime copy needs every package that depends on the runtime to accept 0.9.

Not decided

Whether the CLI should print a program's parts, and whether the runtime package should accept the parts without a caller passing them on.