One tested async toolkit instead of per-framework glue
A framework-neutral async toolkit now sits on the Runtime, with an async resource, matchAsyncState and a conformance suite. Production and testing entry points are separate, and an import-boundary test checks the split. One implementation means an async-state bug is fixed once instead of once per framework.
Technical detail
- Package
@ultrabricks/state-toolkit-async@0.0.0-pilot.0 under packages/state-lib/toolkit, with a peer on @ultrabricks/state-lib@0.1.0-pilot.1. The . and ./testing exports are checked by import-boundary.test.ts and package-contract.test.ts. - Tests:
async-conformance.test.ts, async-ownership.test.ts, async-resource.test.ts, async-thenable-once.test.ts and match-async-state.test.ts.
Key pull requests: #810
Validated immutable records on the existing Runtime
createRecord adds validated, deeply frozen records on the Runtime for small plain objects such as editor workspace preferences. Callers cannot change a committed snapshot, and a nested update that re-enters the same record now throws instead of letting a stale draft overwrite it. It reuses the Runtime’s transactions, so there is no second state engine to maintain.
Technical detail
- A regression test written first reproduced the re-entrancy bug. The error is
STATE_RECORD_REENTRANT, thrown before the inner mutation, and async recipes are rejected. record.test.ts runs under a strict tsconfig.record.json; the pull request records 75 tests passing across core, regression, follow-up and adapter cases.
Key pull requests: #803
Stale async work cannot publish
createOperationScope gives each lane of async work a context generation. Once a lane is replaced, its context changes or it is disposed, the old write or feedback cannot publish, and an old finish or cancel cannot release a newer lease. It is library code; the pull request does not turn it on in the inspector.
Technical detail
- Independent lanes, synchronous commit and notify gates, callback binding checked at invocation, and bounded metadata-only diagnostics.
- Tests:
operation-scope.test.ts, operation-scope-async.test.ts and a type fixture. A real-Chromium check runs the emitted package pair through nine scenarios, and with the stale-publication guard removed, five of seven checks fail.
Key pull requests: #826
Package export fails closed and records provenance
The package exporter assembles in an owner-only staging directory on the destination filesystem and publishes by rename. It refuses to overwrite existing files, directories or dangling symlinks and refuses an output directory inside the source trees. Each export records source and output hashes and tool versions, so a package can be traced to its source.
Technical detail
export-package.mjs keeps the original cause on errors and reports both failures if export and cleanup fail. export-filesystem.test.ts covers it, and #826 added cases. The pull request states the limits: the publish is not atomic no-replace and not crash-durable.
Key pull requests: #810
Generated commands and complete entries with rollback
The compiler generates immutable commands for declared edits such as assign, toggle, row assign and move, so on the benchmark fixture those edits no longer copy the whole document. state build compiles complete fixed application entries and publishes each output with rollback, and size budgets reject an oversized entry and keep the previous output. The figures below carry their fixtures and comparators; they are not a general speed or size claim.
Technical detail
- Native Rust compiler:
packages/state-lib/compiler/src/command.rs and lib.rs. Median improved in all 42 Node measurements; 26 of 42 also pass the stricter max-versus-min cell test (#802). - One ownership-aware publication and history module runs on a single dedicated Runtime. Entry budgets are
maxRawBytes, maxGzipBytes and maxBrotliBytes plus a relative-growth limit (#802). - Fingerprint walks went from three to two and path resolution is cached within a build. A lock-cleanup failure after a successful install is only a warning (#832). A hole that let an alias into authored
src/node_modules bypass the browser-root check was closed, with nine regression tests; the pull request says this is not a sandbox for third-party code. - An optional immutable-row adapter updates completion on existing rows in the board view and falls back to the full renderer for unsupported changes (#832). On the small fixtures the same pull request records about 32 to 38 KB more retained heap, and an entry of 42,166 gzip bytes against 37,132 for the equally optimized original.
Key pull requests: #802 · #832
Cold import skips a full-document copy
importJsonField certifies a JSON field while importing it, and replaceImported adopts that exact root. A cold load skips one whole-document structuredClone, while the normal replace() still copies and behaves as before.
Technical detail
replaceImported throws TypeError for anything not produced by importJsonField, so the shortcut cannot be used with an uncertified value. Certification, freezing, history reset and the save payload contract are unchanged.owned-import.test.ts was written red first and has 11 passing tests. The pull request records whole-call structuredClone uses during load going from 3 to 2 and makes no timing claim.
Key pull requests: #834
Live examples, a keyed-row patch boundary and correct docs
The documentation site has two live examples, /repl/board-commands and /repl/issue-triage. Visitors edit multi-file source, compile it with the real Rust compiler in WebAssembly, and see generated commands, readable and minified output, sizes, live state, Undo and Redo, and browser benchmarks. A checked keyed-row patch boundary lets an app update real text edits without rebuilding the list, and the generated API docs keep ordinary member names.
Diagram caption in the docs site
Merged, pull request #810 Before and after: same window, same region, same scale.
StateLib documentation site, captured for #810 on 14 October 2026 from the same fixture before and after the fix. Both crops cut the same region.
Technical detail
- Edited code runs in a disposable worker inside an opaque-origin, network-denied frame. Cancellation, latest-request authority, timeout and keeping the last good build after a failed build are tested (#809).
patchKeyedRows locates every target before the first row write and rejects a null or invalid plan without applying rows; the pull request documents that a throwing callback leaves a view to rebuild (#833). 14 planner and boundary tests pass.apiSymbolName() used to throw on a real member named like __@iterator@999 and abort API-doc generation. It now accepts such a name only when a declaration’s string literal is exactly that name, and the regenerated docs are unchanged at 119 entries (#807).- Diagram captions no longer show quoted attribute text, and docs-site readiness waits for exactly three lazy editors. SVG comparison in the gallery tolerates only narrowly parsed coordinate roundoff (#810).
Key pull requests: #809 · #833 · #807