- Synchronize published packages and the compiler surface manifest at v0.2.8, and bump the runtime ABI marker to v9.
- Add release notes for worker graph sharing, catchable deep recursion, parallel code generation, runtime performance, and compatibility fixes.
The new docs page (/docs/threads) explains @scriptc/threads: a runnable
example that publishes a million-order ledger and sends it to four
workers, verified under Node.js 24 and as a scriptc executable; what
publish accepts and refuses, and that a refused publish leaves the graph
unchanged; the frozen-object TypeErrors of the write guard; a comparison
with Node's structured clone (constant-time sends, preserved classes and
identity, immortal objects) and the three rules that make a program
behave the same under both; the automatic sharing of deeply frozen
untyped data; and the cost model. The limitations page gains a Published
graphs section (immortality, class and identity differences, write-check
evaluation order, private fields) and links the new page from the
worker-threads section.
This is the first page added since the migration, so the route-test
fixture now accepts pages without a legacy URL, and the agent guide's
checklist for new pages names the fixture.
Large executables were simplified and inlined as one module before the
optimizer split them, so most of a release build ran on a single core,
and any edit recompiled the whole program.
The LLVM helper now divides the program before optimization. It splits
the emitter's assembly text directly, so partitions also parse
concurrently, and falls back to LLVM's module splitter for any input
outside the emitter's grammar. Internal functions referenced by a single
function stay with that function, and globals stay with their first user.
Optimized partitions follow the ThinLTO model: each is simplified and
summarized on its own thread, a thin link imports small functions across
partitions for inlining and returns unexported hidden definitions to
internal linkage, then each partition is optimized and compiled on its
own thread. Unoptimized partitions compile independently, so dev builds
of large programs are parallel too.
A private per-helper cache directory stores simplified partitions under
the hash of their source and objects under the hash of the module after
importing, so a rebuild recompiles only the partitions an edit can
affect. To keep unchanged partitions identical across edits, each
partition numbers its metadata in first-use order, lists only the debug
compile units it uses, and orders declarations by name, and interned
string constants are named by their content rather than first-use order.
Partition counts are powers of two that depend only on the module size,
up to 64, so output stays the same on every host and functions keep
their partition across ordinary edits. LLVM text that arrives in parts is
written in large batches and handed to the helper in place instead of
being written a second time.
Async functions and generators run on their own fiber stacks. The new
stack-overflow guard keeps a quarter of each stack as a reserve for
unwinding and error reporting, which cut the depth they could recurse to
from 256 KiB to 192 KiB of frames. Moderately deep recursion inside an
async body or generator then threw RangeError where Node runs it, for
example 3000 frames of an unoptimized build on arm64 macOS.
Fibers now get 512 KiB, which leaves 384 KiB after the reserve. Pages are
still committed lazily, so idle and shallow fibers cost address space, not
memory. The corpus pins recursion inside async functions and generators
next to deeper recursion on the main stack.
Native executables used to terminate with a signal when recursion
exhausted the stack. Functions that can take part in unbounded recursion
now compare the stack pointer against a per-stack guard on entry and
throw Node's `RangeError: Maximum call stack size exceeded` through the
ordinary exception path, so catch handlers, finally blocks, worker error
events and the uncaught-error exit code behave as they do under Node.
- The checked set is every function on a cycle of the call graph, where
operations that can reenter compiled code indirectly (indirect calls,
callbacks, dynamic and library operations) reach closure targets and
class members. Non-recursive functions, including leaf callbacks, stay
check-free, and the may-throw analysis accounts for the new throw.
- The runtime installs the guard per context (main thread from the stack
rlimit, workers from their thread attributes) and per async fiber, with
a reserve for the throw, unwinding and error reporting. Worker threads
now get an explicit 8 MiB stack.
- Scalar call results pass through the unwind path as its dummy value, so
calls in tail position keep tail-call and recursion elimination.
The default build's largest size contributor on the stack was H23's
-falign-functions=64 on every x86-64 runtime unit (2-11 KB per program;
results/h45-size-budget). Release and library flavors now keep the
compiler's default function alignment; the opt-in speed flavor keeps the
blanket 64-byte alignment. Vendored archives stay 64-byte aligned
(lre_exec keeps its pinned placement) except libunicode.c, whose small
table helpers were most of the regex programs' padding. The runtime-first
link order is unchanged.
Docs and usage text describe speed without the reverted runtime profile.
- Preserve callback identity, receiver binding, and class references across unions and collections.
- Track record field presence independently of undefined values and improve native runtime behavior.
- Expand differential coverage and streamline CI bootstrap and compiler rebuild checks.
- Add native worker execution with isolated module state, structured messages, and lifecycle controls.
- Support shared buffers and integer atomics with coordinated runtime ownership and cancellation.
- Extend compiler, runtime-pack, differential, and compatibility coverage for native concurrency.
- Preserve live class identity through interface receivers and object methods.
- Extend synchronous text reads and regular expression replacement callbacks.
- Keep unsupported generic dispatch boundaries explicit.
- Support concrete generic inheritance, forward references, and nullable substitutions.
- Bound recursive specialization demands while preserving independent concrete uses.
- Preserve class identity through checked assertions and contextual lowering.
- Sort primitive arrays with stable native ordering and pure conversion keys.
- Preserve sparse semantics and argument effects while avoiding callback overhead.
- Verify Unicode ordering and ownership across native and WASI paths.
- Support boolean, bigint, and mixed value keys in native Maps and Sets.
- Reuse key hashes and copy collections directly into compact storage.
- Preserve collection identity, ordering, iteration, and ownership behavior.
* Add Node 24 filesystem link support
* Use executable extensions in Windows link fixtures
* Remove Windows links without walking their targets
* Match Windows symlink inference and Buffer validation
* Match Node Windows symlink paths and fallback state
* Preserve Windows errors when following filesystem links
* feat: support Node 24 filesystem timestamp operations
* test: record frontend baselines for timestamp fixtures
* test: link timestamp validation into the runtime smoke test
* Add Node 24 URL factories and shared parsing
* Clarify URL fixture name and remaining refusal coverage
* Update island URL test for protocol-relative resolution
* Expand Node 24 filesystem descriptor and FileHandle support
* Preserve filesystem options getter evaluation and record fixture baselines
* Normalize the known Linux fiber warning in npm stderr comparisons