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.
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.
- 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.
- Merge origin/main (e1c27176a).
- matchInferSignature answers false when a function needs more arguments
than a fixed-parameter pattern passes, as TypeScript's assignability
does (corpus 6398).
- Record corpus costs for the programs this change adds.
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>
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>
Resolve the conflicts with main's process.features value, the WebCrypto dynamic types, the NODE_COMPAT_VERSION import and the symbol.declarations API. Renumber this branch's corpus programs to 6420-6429, write the new limitations partial one sentence per line, and regenerate the compatibility outputs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `if (process.send)`, `Boolean(process.send)`, `!process.send` and `typeof process.send` answer whether the process has an IPC channel (a fork channel this binary opened, or NODE_CHANNEL_FD from a Node.js parent). The same holds for process.disconnect.
- In a program without fork targets, `process.send?.(...)` answers undefined, and a direct call throws Node's TypeError when there is no channel. A Node.js parent's channel has no lowering, so a call fences when it runs. `process.send!(...)` lowers like the direct call.
- process.exitCode reads answer the assigned code, or undefined. Only programs that read it record assignments, including process.exit(code) before the 'exit' listeners run. Other programs keep the untracked write.
- A stream subclass hook may declare a supertype of Node's argument (`_write(chunk: Buffer | string, ...)`): the wrapper takes Node's Buffer and widens it.
- Promise.race accepts a Promise<void> entry when the result holds undefined.
- http.RequestOptions and https.RequestOptions as declared types ride checked-dynamic storage, like SpawnOptions.
- util.promisify over a typed callback-last function value (a bound method, a declaration, an arrow) builds a native promisified function. A truthy error rejects, the value fulfills, and a synchronous throw rejects.
- The bundled Node declarations gain the callback-only generic promisify overloads. Before this, `promisify(obj.close.bind(obj))` matched crypto.randomFill's overload and reported "Expected 1-3 arguments".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
new WebAssembly.Memory, Table and Global construct the runtime objects an
instance exports, with Node 24's descriptor order, defaults, conversions
and messages (V8's EnforceUint32 errors, the v128 and shared-memory
errors; a valid shared memory is refused with a TypeError until threads),
so either kind can be imported. Imports of memories, tables and globals
now check limits, types and mutability with V8's LinkError messages, and
exports keep V8's identity (imported objects re-export as themselves, one
object per memory and table). The classes called without new throw Node's
TypeError.
WebAssembly.Module.imports, exports and customSections read the
descriptor (ABI v2). Custom sections are carried only by programs that
call customSections. Every function a module references reaches
JavaScript as a callable function: the translator emits one call thunk
per signature, so table.get and funcref globals return the module's
unexported functions too, and exported functions can be stored in tables
and globals from any instance. Discovery also finds .so WebAssembly
shared libraries at resolved paths.
Fixes to W3/W4 found on the way: externref Table.grow fills with
undefined; Memory.grow/Table index errors and funcref messages match V8;
an invalid funcref global import is a LinkError; global.valueOf() on a
WebAssembly.Global-typed receiver answers the value; explicit-bounds
memories keep the blocks grow replaced, so old ArrayBuffers never
dangle.
This unblocks quickjs-wasi's extension loader: quickjs-wasi with its url,
encoding, headers and structured-clone extensions runs through its own
glue identically to Node, and pi codemode's static coverage goes from 83%
to 84% with no WebAssembly blocker left.
Evidence: differential corpus programs for the API (webassembly-jsapi),
a 261-test port of WPT wasm/jsapi (webassembly-wpt-jsapi), quickjs-wasi's
dist/extensions.js run unmodified against dylink-shaped modules
(webassembly-dylink), and the 2.0 spec suite's modules that import the
spectest memory and table (new webassembly-spec-imports group plus the
memory and table groups); all match Node in the plain and sanitized
lanes. The zero-cost gate leaves every non-WebAssembly program
byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crypto.subtle.importKey('raw', ...) for HMAC keys, HMAC sign and verify,
and the SHA-1/SHA-2 digests compile through the crypto global, with
CryptoKey values as native handles. scr_webcrypto.c is only the WebCrypto
layer (Web IDL conversion, algorithm normalization, the CryptoKey handle,
promise wrapping and Node's TypeErrors and DOMExceptions) over node:crypto's
digest core and deferred promise queue. It links only into programs that
call these methods (the new webcrypto link feature). Other algorithms, the
jwk format and the other SubtleCrypto methods are SC2020 refusals.
The real jose@6.2.12 HS256 path (SignJWT, jwtVerify) now runs natively. It
also needed four compiler fixes: object patterns of positions and length
over arrays, the Uint8Array base64/hex feature probes, a compile-time
String.fromCharCode.apply, and structuredClone of a parameter whose default
is {}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch lowers toPrecision statically, but STATIC_NUMBER_METHODS still
lacked it, so the regenerated manifest kept stdlib.number.toPrecision as
unsupported/SC2012 and a determinism fence on it had no reach detector.
List it with its num.toPrecision witness and 0 to 1 arguments, like
toFixed, and regenerate the manifest; the compatibility outputs are
unchanged.
library-profile's remediation test used that id as its SC2012 fence;
it now names the SC2012 code family (diagnostic.sc2012), so the explicit
remediations key still competes with fence-supplied SC2012 text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A build now translates every WebAssembly module whose bytes it can see
(W2's discovery) into LLVM IR with a TypeScript translator
(packages/compiler/src/wasm/module.ts, translate.ts) and appends it to the
program module, so the existing LLVM helper optimizes, partitions and
caches it with the program. The program also emits scr_wasm_registry, a
portable table of module descriptors (scr_wasm_abi.h). At run time,
WebAssembly.compile, instantiate, validate, new Module and new Instance
find the translated module by the SHA-256 of its bytes; bytes the build did
not see reject with a CompileError. There is no interpreter and no JIT.
The runtime (scr_wasm.c, link-gated by the new `wasm` feature, handle tags
inside W0's headroom) implements the JavaScript API once, over
descriptors, with Node's semantics and messages: exports (functions,
memories, tables, globals), imports (functions, immutable globals, and
memories, tables and globals other instances export), every trap as
WebAssembly.RuntimeError, the CompileError, LinkError and RuntimeError
classes, compile and instantiate settling on a later turn, and
globalThis.WebAssembly as a value. On 64-bit Linux and macOS, memories sit
in 8 GiB guard reservations and a SIGSEGV/SIGBUS handler turns faults into
traps or stack exhaustion; async fibers get a guard page on first use.
Sanitizer builds and Windows check bounds and the stack explicitly. A
WebAssembly.Module crosses to workers through W0's transfer hook (W4).
W1's refusals become lowerings; the Memory, Table and Global constructors,
Module.imports/exports/customSections and the streaming functions still
refuse with SC2020. The globals: WebAssembly compatibility row moves to
partial.
Evidence: differential corpus programs for the API, TypeScript use and the
codemode worker shape, plus five programs executing about 9,300 commands
of the WebAssembly 2.0 spec suite; all match Node in the plain and
sanitized lanes. quickjs-wasi runs through its own glue, matching Node
(apart from a pre-existing non-wasm fence) and on par with or faster than
V8. The zero-cost gate leaves every non-wasm program byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Date.prototype.toString, toDateString and toTimeString, String(date),
template literals and string concatenation print local time with the GMT
offset and the zone's long name, as Node does. The runtime identifies the
host zone from TZ or /etc/localtime, again after each write to
process.env.TZ, and looks its name up in a table generated from Node's
ICU data. Dates held in untyped values convert to strings with toString()
and to numbers with their time value.
fs.constants as a value now builds its object from the fs group fill that
node:constants added (scr_fs_constants_object, defined after
scr_node_constants so the shared static keeps its place); the fallback
declarations gain the UV_FS_O_* Windows-only flags. Compatibility files
regenerated with pnpm manifest and pnpm node-compat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crypto.getRandomValues (crypto and webcrypto chapters) is supported with
corpus 6204/6205; fs.constants is partial (the fs group, not the other
platforms' sets) with corpus 6207/6208. Regenerated with pnpm manifest and
pnpm node-compat.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
map, filter, take, drop, flatMap, toArray, forEach, reduce, some, every
and find compiled only as one chain from arr.values() or arr.entries()
that ended in a consuming method. Every other form was refused, and in
JavaScript the refusal was thrown at run time.
IteratorObject now maps to the generator type. A consuming method over
a chain of stages fuses into one loop over the source: a generator
driven like for-of, a Map or Set walked live, or an array by index. A
stage on its own builds a helper object: a generator over its source
that supports next(), for...of, spreads, Array.from and yield*. A
for...of whose head is a chain of stages without flatMap runs the
stages inside the loop.
The source is closed where Node closes it: when take reaches its count,
when some, every or find has its answer, when a callback or the loop
body throws, when the loop exits early, and before the RangeError of an
invalid take or drop budget. It is not closed when it is exhausted or
its own next() throws. return() on a helper object before its first
next() closes the source through a new runtime hook,
scr_gen_set_unstarted_return.
When closing an iterator throws while another error propagates, the
first error reaches the caller, as in Node. This applies to for...of
over a generator too, where the error from the close used to replace
the body's error. The try statement takes a dropFinallyErrors flag,
whose cleanup handler calls the new scr_exc_keep_first.
throw() lowers only on values typed as generators. Iterator helpers and
the built-in iterators have no throw method, and a value typed as an
iterator may hold one.
Callbacks in JavaScript bind their unannotated parameters to the
element type, and flatMap callbacks may return generators. A generator
whose record yields share checked storage with its return channel has
each value converted to the declared element type as it is pulled.
Packages whose declarations name WebAssembly types (quickjs-wasi,
photon-node, and therefore pi) failed scriptc's type check with SC0001
"Cannot find namespace 'WebAssembly'", because scriptc's ES-only type
world never loads lib.dom, so `scriptc coverage` could not analyze them.
- scriptc.d.ts declares lib.dom's WebAssembly namespace and BufferSource
(admitting every ArrayBufferView, so Node's Buffer types fit).
- The type mapper gives every type declared in the namespace, and unions
over one, the checked-dynamic representation, so declarations that only
mention them compile.
- Every reached use of the API value refuses at its site with a named
SC2020 diagnostic: `WebAssembly.<member>` reads and calls,
`new WebAssembly.<Class>`, `instanceof WebAssembly.<Class>`, the bare
namespace, `globalThis.WebAssembly`, and the global object viewed
through a cast to a type with a WebAssembly member (pi codemode's
spelling). TypeScript sources fail the build; JavaScript sources get a
located runtime fence.
- `typeof WebAssembly` and `typeof WebAssembly.<member>` need no value and
fold to Node's answers ("object", "function"), including inside
`typeof x === "..."` tests, so feature probes in JavaScript glue behave
as in Node. Before, a JavaScript probe answered "string" (the identity
token) and silently took the non-wasm branch.
- The surface manifest projects the refusal (stdlib.webassembly), and the
compatibility ledger's `WebAssembly` global row moves from
not-implemented to refused; no other row changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Date.prototype.toUTCString and the static Response.json operation are
static surfaces now (pnpm manifest). pnpm node-compat leaves the Node
ledger and the public artifact unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The multi-argument Date constructor was refused (SC2020), which blocked
date-fns and consumer code that builds dates from calendar fields. Typed
callers now lower to a seven-f64 runtime call (omitted trailing fields
complete with the spec's defaults) and JavaScript callers pass their
arguments through ToNumber in order. The runtime mirrors V8: MakeDay
bounds the untruncated year and month, years 0-99 map to 1900-1999,
fields roll over, and the local time value converts to UTC in the host
zone with the same UTC(t) the date parser uses, then TimeClip. Spread
arguments stay fenced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Date.parse and new Date(string) answered NaN natively for every form
outside ISO 8601 with an explicit offset and the X509 certificate shape,
so RFC 1123 HTTP dates ("Mon, 01 Jan 2024 00:00:00 GMT") never parsed and
the fresh/express/send conditional GET never matched. The runtime now
falls back to a port of V8's DateParser (the ES5 date-time format, then
the Safari-compatible legacy grammar: weekday and month names, RFC 2822
offsets, zone abbreviations, AM/PM, parenthesized comments, slashes) for
exactly the strings the bounded grammars reject, so the timestamps they
accept cost nothing more. Zone-less dates are local time, converted with
the spec's UTC(t) over the host zone (DST gaps read with the offset before
the transition, overlaps answer the earlier instant).
With native parsing matching Node for every input, the SC6005 literal
warning has nothing left to report and is retired rather than extended to
non-literal inputs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the dns/promises libCalls against main's util.promisify(dns.lookup)
forms: main's dns.lookupPromise/scr_dns_lookup_promise keep their
promisify semantics, and node:dns/promises moves to dns.promisesLookup /
scr_dns_promises_lookup in scr_dns_promises.c. Generated compatibility
files are main's, regenerated with pnpm manifest and pnpm node-compat.
The npm harness's web-stream fixtures now compile and defer their
refusals, and node24-util-types defers at its Promise subclass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>