A value that contradicts a type an npm package declares used to throw
"expected number at $, got undefined" and nothing else. Such a check now
names the declaration and the type patch that admits the value:
profilekit@3.1.0 declares Profile.email as string (index.d.ts:7)
if that declaration is wrong, patch it in package.json: ...
Origins are found for values a package function returns under a written
return type, for properties of records typed by a package-declared type
(record projections into such a type are keyed by it), and for
declaration-boundary casts, whose origin the projection records from the
declaration-world checker. Generic declarations get none: their types
come from the program's own instantiation.
Checked conversions carry dynCheck.origin, an index into
IrModule.declOrigins; the site's existing cold exception block calls
scr_type_origin_note (new runtime unit behind the typeOrigins feature),
which appends the note to the pending TypeError and derives the selector
suffix and the admitted kind from the failure. Union extractions get the
note per arm at compile time. The hot path is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In JavaScript dependencies, a createRequire require call with a computed
specifier, and require.resolve called with a computed request or read as
a value, now compile. The new runtime unit scr_require.c resolves the
specifier with Node 24's CommonJS algorithm (builtins, relative and
absolute paths with Node's extension search, package "main", "exports"
and self-reference, node_modules and NODE_PATH lookup) when the call runs:
- require.resolve returns the id or real path Node returns, and throws
Node's MODULE_NOT_FOUND error when nothing resolves.
- require throws the same MODULE_NOT_FOUND error when nothing resolves,
so packages that probe for optional addons, such as pi-tui, take their
absent branch as in Node. When the specifier resolves to a native addon,
a builtin or another file, Node would load that module: the program
prints an SC2020 error naming it and exits with status 1, without
running the catch clause around the call.
Coverage lists these calls as deferred to runtime. TypeScript sources keep
the compile-time refusal.
Refs #1777.
A codec stored in untyped storage (hono's StreamingApi keeps
`this.encoder = new TextEncoder()` in a JavaScript class field) had no
dynamic representation and threw SC1101 at run time. The codec records
now cross 'unknown' slots as handles of two new tags that hold the record
itself, so identity and a decoder's streaming state survive the round
trip, and checked casts unbox the same record.
The handles answer encode, encodeInto and decode (with options objects),
the encoding, fatal and ignoreBOM properties, method values with Node's
ERR_INVALID_THIS, instanceof, String() tags and JSON's {}. The unit
scr_text_codec_dyn.c links only into programs that convert a codec to or
from an untyped value (the textCodecDyn runtime feature, found with the
dgram carrier walk, now shared).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crypto.createDiffieHellman, crypto.getDiffieHellman and
crypto.createDiffieHellmanGroup return native DiffieHellman and
DiffieHellmanGroup handles backed by mbedTLS bignums. generateKeys,
computeSecret, getPrime, getGenerator, getPublicKey, getPrivateKey,
setPublicKey, setPrivateKey and verifyError follow Node 24.22:
- the modp1, modp2, modp5 and modp14 through modp18 groups, with the
private key widths of Node's OpenSSL 3 path (200 to 400 bits for
modp5 and up, [2, p - 2] for modp1 and modp2);
- explicit primes and generators in every argument form and encoding,
with keys of bits(p) - 2 bits, or below min(2^N, q) when the
parameters equal an OpenSSL named group (RFC 3526, RFC 7919,
RFC 5114);
- generated safe primes with OpenSSL's congruences for the generator;
- verifyError's DH_check bits, computed when it is first read;
- Node's argument, generator, modulus, key range, state and encoding
errors, including the OpenSSL errors' library and reason properties.
Safe-prime generation sieves candidates with the odd primes below a
bound that grows with the prime's length and tests them with
Miller-Rabin rounds over a variable-time Montgomery exponentiation that
only reads public values; key and secret exponentiations stay on
mbedTLS's constant-time path. Primes longer than 8192 bits, property
reads other than verifyError, property writes, Object.prototype methods,
and calls whose output encoding is typed any or matches no @types/node
overload are refused.
The runtime unit scr_dh.c links, with the mbedTLS archive, only into
programs that use DiffieHellman. wasm32-wasi builds refuse it. The ECDH
unit's argument and encoding helpers move to scr_crypto_args.h, which
both units include.
execa 5 mixes then/catch/finally into a real ChildProcess with
Reflect.defineProperty, replaces kill and adds cancel by assignment, and
awaits the child.
- A new optional runtime unit, scr_dyn_handle_props.c, keeps those
properties in a side table keyed by the native child. Wrapper ops for
the child tag look there first for reads, member calls and writes.
defineProperty and getOwnPropertyDescriptor use it, and await adopts a
child with a callable then. Class methods read as values call the
class's own method.
- In JavaScript files, writes onto typed children, reads and calls of
added names, method values for bind/call/apply, and members no typed
lowering claims go through the child's checked-dynamic box.
- Only programs whose JavaScript adds properties to children link the
unit and install the table; everything else compiles as before.
- A setTimeout delay that may be a boolean converts with ToNumber, and
`if (t.unref)` over a timer handle folds to true (execa's lib/kill.js).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crypto.createECDH returns a native ECDH handle backed by mbedTLS for
prime192v1, secp224r1, prime256v1, secp384r1, secp521r1, secp192k1,
secp224k1, secp256k1, brainpoolP256r1, brainpoolP384r1 and
brainpoolP512r1. generateKeys, computeSecret, getPublicKey,
getPrivateKey, setPrivateKey, setPublicKey and ECDH.convertKey follow
Node 24: compressed, uncompressed and hybrid points, input and output
encodings, the private key range, key pair validation, and Node's error
codes and messages.
Curve names follow OpenSSL's short-name lookup: names of other curves
from crypto.getCurves() are refused at compile time when literal and
throw a coded SC2020 error when computed, other OpenSSL short names
throw ERR_CRYPTO_OPERATION_FAILED, and unknown names throw
ERR_CRYPTO_INVALID_CURVE. Calls whose arguments no @types/node overload
accepts, and computeSecret(view, encoding), are refused at compile time.
The runtime unit scr_ecdh.c links, with the mbedTLS archive, only into
programs that use ECDH. wasm32-wasi builds refuse it.
hot-shots, a StatsD client compiled from its JavaScript, failed when a
program constructed a client. Each gap on its UDP path is closed
generically:
- Sloppy-mode functions with named parameters may read `arguments`
where the aliasing of their parameters cannot show: only
`arguments.length`, or element reads without parameter or element
writes.
- An `any` slot admits any boxable function value, including functions
with optional parameters (`C.prototype.m = function (...)` through an
untyped receiver).
- In a JavaScript file TypeScript does not check, a prototype method
takes its documented parameters as checked-dynamic values, as
constructors already did.
- JavaScript dgram sends run completion callbacks, buffer lists,
connected sends and the six-argument offset/length form.
- A dgram socket crosses into the checked-dynamic tree as a handle
(SCR_DYNH_DGRAM_SOCKET) whose methods are function values, with
untyped listeners that survive 'close'. The handle lives in its own
runtime unit (scr_dgram_dyn.c, the dgramDyn feature), linked only
when a program converts a socket to or from an untyped value.
- `String.prototype.concat` on strings, and `parseInt` over a possibly
missing or untyped value in JavaScript.
Corpus programs 6590-6596 and the hot-shots-udp npm
case (hot-shots 8.5.0 vendored) match Node in both lanes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A JavaScript generator whose inferred return is `any` (it delegates to
itself) is a generator with checked-dynamic channels.
- for await over a checked-dynamic value: a compiled async generator, the
value's Symbol.asyncIterator result, or its synchronous iterator with
awaited values; break and throws close it through an awaited return().
A value that is not iterable throws V8's TypeError.
- yield* in async generators delegates through the same protocol and
completes with the delegate's return value.
The runtime part is a new unit, scr_dyn_for_await.c, linked only by
programs that use it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- bind/call/apply on builtin function values held as checked-dynamic
functions (String.fromCharCode, JSON.stringify and the forms below).
- Function values for Buffer.byteLength (Node's runtime encoding ladder),
Buffer.compare, Buffer.alloc (size check, then fill), JSON.parse,
fs.realpathSync and fs.realpathSync.native (Node's options handling).
- A JavaScript string method read as a value answers String.prototype's
function (smol-toml's `!!(''.isWellFormed)` probe).
The runtime entries live in a new unit, scr_builtin_values.c, linked only
by programs that read these values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A JavaScript binding written with a value whose representation differs
from its initializer's (buffer-crc32's Int32Array table) stores a
checked-dynamic value.
- A top-level JavaScript function declaration its module reassigns
(yazl's self-redefining bufferFrom) lives in a mutable module global,
assigned when the module starts; reads, calls and writes use it.
- Buffer.from with a non-literal encoding follows Node's fromString rules
at run time, and copies arrays crossing by reference; array and byte
sources ignore the extra argument.
- `new Buffer(...)` in dependency sources, where Node never warns, is
Node's construction.
- Fixed-width read/write methods on Buffers held as checked-dynamic
values dispatch through a separate runtime unit (scr_bytes_dyn.c),
linked only by programs that call them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pipe() into an untyped destination now comes from main's pipeIntoDyn
(stream.unboxDyn); programs with constructor-function streams unbox
through scr_dyn_builtin_stream.c, which also takes those streams, so the
separate dyn.builtinStreamPipe entry point is gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bundled npm code (from2, bl, tar-stream, yazl, multistream) builds its
streams as ES5 constructor functions: util.inherits(Ctor, Readable) and
Readable.call(this, opts), with _read/_write/_transform on the
prototype. Readable.call(this) threw SC2020.
A new runtime unit, scr_dyn_builtin_stream.c (feature
dynBuiltinStream), gives the untyped stream constructors their stream
half. Base.call(this, opts) creates a runtime stream over the receiver
and keeps it in a hidden slot; the runtime calls the receiver's
underscore methods through its property lookup; the stream prototypes
carry Node's methods and accessors over the hidden stream; and
EventEmitter's methods register listeners with the runtime stream's
emitter, so runtime events and pipes reach JavaScript listeners with the
receiver as this. _readableState/_writableState are live views.
Stream class values now come from dyn.builtinStreamClass, so only
programs that use them link the stream half. Typed code sees these
objects as their runtime streams: checked casts to the stream classes
take them, readable.pipe() into an untyped destination pipes into them,
and runtime streams crossing into JavaScript answer the constructor
object.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Externrefs a guest holds are borrowed pointers. An import's result was
kept only until the outermost call returned, which was before the export
call converted its results to JavaScript, so a result created by an
import was freed before conversion. A table or global slot released the
reference it replaced at once, although the guest may still hold it in a
local after reading the slot. The kept references are now released after
the outermost caller has converted its results (scr_wasm_leave), and a
table or global store during a call keeps the reference it replaces until
then.
Checked memories (explicit bounds checks: sanitized builds, Windows)
grew with realloc, which can move the storage that memory.buffer and its
views alias. They now reserve the address space for their largest size
(mmap on POSIX hosts, VirtualAlloc on Windows) and commit pages as they
grow, so the base never moves. Hosts without address-space reservation
grow in place only until a buffer has been handed out; after that,
growth that would move the storage fails.
Tests: webassembly-externref-lifetimes.mjs (import results through an
export, table and global references replaced by the guest and by
JavaScript), webassembly-memory-growth.test.ts (views written after
growth from JavaScript and from the guest), and translate.test.ts cases
for memargs naming memory 0 explicitly and for local.tee operand types,
which b3bcbaaf7 made consistent with validation. Under SCRIPTC_SAN=1 the
two runtime tests report heap-use-after-free without these changes.
Merges main; internal/compatibility/generated/node-v24-backlog.json is
regenerated with pnpm node-compat.
events.once(emitter, name, { signal }) lowers to events.onceSignal, served
by a new runtime unit (scr_events_once_signal.c, linked only through the
eventsOnceSignal feature). It follows Node 24's lib/events.js: an
undefined signal is the plain form, a value that is not an AbortSignal
rejects with ERR_INVALID_ARG_TYPE, an aborted signal rejects with an
AbortError whose cause is signal.reason, and otherwise a once,
resist-stop-propagation 'abort' listener removes the event and 'error'
listeners and rejects. The event or the error removes the abort listener.
Options other than an object literal with one signal stay refused.
events.once now accepts stream receivers: a stream's runtime-emitted
events use their fixed tuples, and its 'data' and 'keypress' events stay
refused. A NodeJS stream interface slot that holds a stdio handle requests
the general stream representation.
Node's once is an async function that returns the executor's promise, so
its result adopts that promise through a thenable job. The plain form
settled its result two turns early; both forms now settle on Node's turn.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- matchInferSignature reads a fixed-tuple rest as its positions, so
(...args: [string, number]) needs two arguments, and a positional
any[] pattern parameter matches only when the function's parameter
there accepts an array, as TypeScript's assignability does
(corpus 6398).
- Merge origin/main (200fe8d22); node-v24-backlog.json regenerated.
Under the general stream representation, NodeJS.ReadableStream and
NodeJS.WritableStream compile as the runtime Readable and Writable
classes, so a JavaScript stream object (a constructor-function stream, a
plain object with on/pipe, a stream from bundled npm code) passed through
a TypeScript function typed with them took the checked unwrap and threw.
A program that moves an untyped value into a runtime stream class slot,
or passes an untyped pipe() destination or pipeline stage, now lowers
once more with checked-dynamic stream slots: the two interfaces (and
intersections over them) map to dyn. A native stream rides its typed
capsule and keeps its identity; any other value is the JavaScript object
itself. Member calls dispatch per value: stream.dynNative tests for a
boxed native stream, which takes the ordinary class-method lowering;
other values call their own member through dynInvoke. pipe() into a
JavaScript destination follows Node's Readable.prototype.pipe listener
protocol (stream.pipeDyn). for await and pipeline stages keep the
not-supported error for values that are not native streams.
The runtime code lives in scr_stream_dyn.c behind a new streamDyn
feature, so programs without these libCalls link the same objects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bundled npm code (glob, yazl, from2, multistream) subclasses Node's
classes with constructor functions: util.inherits(Ctor, EventEmitter) and
EventEmitter.call(this). util.inherits received an identity token and
threw ERR_INVALID_ARG_TYPE, EventEmitter.call(this) and
EventEmitter.prototype were refused, and `this instanceof Ctor` against
a function was refused.
A new runtime unit (scr_dyn_builtin_class.c, linked by the new
dynBuiltinClass feature) provides EventEmitter, the legacy Stream and
the stream classes as untyped constructor functions with Node's
prototype chain. EventEmitter's prototype ports lib/events.js over the
object's own _events, _eventsCount and _maxListeners properties; Stream
adds the legacy pipe. util.inherits, Base.call/apply(this, ...),
Base.prototype, instanceof on untyped values and JavaScript value reads
of these classes use them. Constructing over a stream class throws
SC2020 where it runs. instanceof against a compiled constructor function
compares its prototype with the value's prototype chain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main replaced the runtime-feature scan with a rule table, the executable
unit list with a unit table and the module emission with emit-module.ts:
perfLoop becomes a rule (perf.idleTime, perf.loop*, perf.eld*), a unit
(scr_perf_loop.c) and emit-module's loop-start mark (readsLoopMetrics is
now public). The feature lists take both perfLoop and main's webcrypto.
This branch's lower-exprs.ts hunks move to expressions/property-access.ts
(histogram readings), expressions/binary.ts (`??` with a function
default) and expressions/relational.ts (`in` over class members); the
caughtRead fallback stays in lower-exprs.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
util.promisify(fn) over a function typed any (an npm package method typed
by its JavaScript, a bound method of an untyped value) was refused,
because the promisified signature is only known at run time. A native
wrapper now calls the function with the call's receiver, its arguments
and a callback, and answers a promise: a truthy error rejects, otherwise
the first value fulfills, the first settlement wins, and a synchronous
throw rejects. util.promisify.custom functions are not consulted.
The wrapper lives in its own runtime unit (scr_util_promisify.c), linked
by the new utilPromisify feature, so other programs are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once generic wrappers compile (#1413), the code behind them is reachable.
This closes the general gaps found there:
- performance.eventLoopUtilization() and monitorEventLoopDelay()'s
IntervalHistogram (enable, disable, reset, percentile and the min, max,
mean, stddev, count and exceeds readings), under the fallback
declarations and @types/node. A new runtime unit, scr_perf_loop.c, links
only for programs that read them (the perfLoop feature): an unref'd
sampling interval records into HdrHistogram-shaped buckets, and
utilization measures active time as the main thread's CPU time since
the loop started.
- Catch bindings narrowed by a user type predicate or an assertion read
their members through the checked-dynamic value.
- `??` with a function default of a different signature takes the
optional callback's own function type and adapts the default.
- Optional calls on interface-typed receivers dispatch like the plain
protocol call.
- `any` parameters of function declarations and spelled function types
are checked-dynamic slots, and a generic instantiated at `any` takes
`unknown` there.
- `'key' in err` on Error receivers answers from the declaring subclasses.
- Object.entries over a generic record resolves
`keyof T extends never ? unknown : T[keyof T]` through the binding.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>