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>
node:test changes, matching Node 24.22:
- getTestContext() returns the context of the test, suite or hook whose
code is running: undefined at module top level, the root's context in
module-level hooks, the suite's in a suite body and in the suite's
hooks, and the test's in its body. In the afterEach and t.after hooks
of a subtest cancelled by its parent's timeout, it returns each hook's
owner.
- The context rides the AsyncLocalStorage snapshot under a reserved id,
so it follows a test across its awaits, into the async functions,
promise callbacks, timers and other callbacks it schedules, and into
concurrent subtests, which each see their own.
- The root's fullName is "<root>", as its name is.
- Optional chains over a test context, such as getTestContext()?.name,
lower.
AsyncLocalStorage enter and restore move into the always-linked async
core so the test runner can use them. The snapshot holds the node's
index in a table of live nodes, so a snapshot that outlives its node
answers undefined.
The get-test-context and get-test-context-cancelled fixtures compare
the contexts seen at each point with Node, plain and with SCRIPTC_SAN=1.
node:test changes, matching Node 24.22:
- test, it, describe, suite, their skip/todo/only twins and t.test take
Node's (name?, options?, fn?) forms: test(fn), test(options, fn),
test(options) and test() compile, as does describe(fn) and
describe(options, fn). The fallback declarations add the overloads.
- A missing or empty name is the function's name, or "<anonymous>", as
Node's Test constructor completes it. The name is the function
expression's or declaration's own name, or the binding name of a
const arrow or anonymous function; a name that is empty at run time
takes the same default.
- With both skip and todo, the test is skipped, and the directive message
is the skip string, else the todo string. The skip and todo method
twins override the option of the same name, so test.skip("x",
{ skip: "msg" }) reports a bare SKIP and it.skip("x", { todo: "msg" })
reports "msg". Before, the last option set the mode.
The limitations page notes that a thrown object or array that is not an
Error reports [object Object] in the failing section.
The test-name-forms fixture compares the names, directives and counts
with Node, plain and with SCRIPTC_SAN=1.
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>
Only the access-mode and COPYFILE bits of fs.constants lowered; every other
member and the object itself refused. The runtime now builds
node_constants.cc's fs group in its order under the target libc's #ifdef
guards (O_NOATIME and O_DIRECT on Linux, O_SYMLINK on macOS) as a cached
null-prototype object with read-only members. `fs.constants` as a value
reads it, and members beyond the baked literals read from it (undefined
for a constant the target lacks, in JavaScript sources). The fallback
declarations list the full set. The table keeps each name inside its
entry, so the whole table is one data object section GC drops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nanoid fills its id bytes with `crypto.getRandomValues(buffer.subarray(...))`,
which had no lowering. The WebCrypto global, node:crypto's export and
`webcrypto.getRandomValues` now fill typed views in place and return them
at their own type. Float views, DataViews and other values throw Node's
TypeMismatchError DOMException, views over 65536 bytes QuotaExceededError,
with Node's messages and codes. The fallback declarations gain the
node:crypto export and webcrypto's member (the crypto global stays
unknown in builds without @types/node).
The runtime entry points are new functions reached only from these
calls; the size check is integer arithmetic so no constant joins the
pool every binary links.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
node:test changes, matching Node 24.22:
- A describe or suite body may take its SuiteContext, with name,
fullName, filePath, attempt, passed and diagnostic. Its passed is false
until the suite has finished, after its after hooks.
- before and after hooks may take the suite's context, and beforeEach
and afterEach hooks the test's. A test's t.before, t.after,
t.beforeEach and t.afterEach hooks may take the test's context.
- SuiteContext maps to the runner node that TestContext maps to, so the
TestContext members that SuiteContext shares lower the same way.
The suite-context fixture compares the reporter output and the order of
the bodies and hooks with Node, plain and with SCRIPTC_SAN=1.
node:test changes, matching Node 24.22:
- t.filePath is the entry file's path, as Node's entryFile, fixed when the
program is compiled.
- t.attempt is 0, since a test runs once.
- t.passed is false while the body runs and the test's outcome once the
body ends, so afterEach and t.after hooks see it, as Node's pass() and
fail() set it before them.
- t.workerId is Number(process.env.NODE_TEST_WORKER_ID) || undefined.
- t.tags is [], since the tags option has no lowering.
- A test's t.before, t.after, t.beforeEach and t.afterEach hooks are
released when the test ends. Hooks whose closures capture the test's
context no longer keep it alive in a cycle, which the SCRIPTC_SAN=1
audit reported as live closures at exit.
The ctx-members fixture compares the reporter output with Node, plain and
with SCRIPTC_SAN=1.
promisify(crypto.randomBytes), pbkdf2, scrypt, hkdf, randomFill, randomInt
and generateKey, and promisify(dns.lookup) and dns.lookupService, over a
named or namespace import, now lower. A call through the binding runs the
callback form with a runtime callback that settles the returned promise
where the callback would run: fulfilled with the callback's value, or
rejected with its error. An argument error the callback form throws rejects
the promise instead, as the throw inside Node's promisify wrapper does.
The crypto promise's value follows the declared result type: a Buffer,
the filled view (the same object), a number, or a checked-dynamic value
where @types/node declares unknown (scrypt, randomFill, randomInt) and for
ArrayBuffer and KeyObject results. The runtime adds a callback closure that
owns the promise and settle adapters for each representation, next to the
existing callback forms in scr_crypto_async.c.
dns.lookup's promise fulfills with { address, family }, or the address
array with options.all, and lookupService's with { hostname, service }, as
Node's custom promisified forms do. The runtime builds the checked-dynamic
result; a declared record type is reached through an async helper that
awaits it and checks the value. The type mapper maps the promisified
lookupService result literal like the promisified exec result.
The fallback declarations gain the matching util.promisify overloads.
exec(command, callback) and exec(command, { encoding, maxBuffer },
callback) lower like execFile's callback slice. The command line runs
with /bin/sh -c, as execSync runs it, the call returns the live
ChildProcess, and the error-first callback receives the captured utf8
stdout and stderr. A non-zero exit, including the shell's 127 for a
missing command, reports Node's "Command failed: <command>\n<stderr>":
scr_exec_file takes a shell flag so the message shows the command line.
Under @types/node, child_process.ExecException maps to Error, as
ExecFileException does. Its code member is the child's numeric exit
status, which the runtime error's string code slot does not hold, so
reading it, testing it with `in`, or testing its typeof is a named
refusal.
- node:punycode: encode, decode, toASCII, toUnicode, ucs2.decode,
ucs2.encode and version, ported from the punycode.js 2.1.0 Node bundles
(scr_url.c), with its RangeErrors. DEP0040 follows Node: a static ES
import reports before any module evaluates and before the program can
register a 'warning' listener; require() reports on the next tick, once
for both spellings, not when a dependency under node_modules loads the
module first, and not under process.noDeprecation.
- node:constants: the frozen object Node builds from os.constants'
dlopen, errno, priority and signals groups, fs.constants (node_constants.cc
order and guards) and crypto.constants (scr_os.c); its bindings read it as
a checked-dynamic value.
These unblock tr46 (whatwg-url, node-fetch@2) and graceful-fs (fs-extra).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
node:test changes, matching Node 24.22:
- t.assert.snapshot(value[, options]) serializes the value with Node's
default serializer, JSON.stringify(value, null, 2), or with an
options.serializers array literal applied in order, and compares it
with assert.strictEqual against the snapshot stored under
"<test full name> <n>" in "<entry file>.snapshot".
- t.assert.fileSnapshot(value, path[, options]) compares the serialized
value with the contents of path.
- Snapshot files are read in the form Node writes them, exports[`key`] =
`value`; statements, with template escapes and CRLF line endings
cooked as in a template literal.
- Missing snapshots, unreadable snapshot files and throwing serializers
throw Node's ERR_INVALID_STATE errors, with Node's messages.
- Running the binary with Node's --test-update-snapshots flag as its
first argument records the values and writes the snapshot files after
the last test, keys sorted as Node sorts them. The flag is kept out of
process.argv and appears in process.execArgv, which is otherwise [].
- Snapshot assertions count toward the test's plan.
The snapshot fixture compares the reporter output with Node, plain and
with SCRIPTC_SAN=1, against a snapshot file Node wrote. The update-mode
harness test runs Node and the binary with --test-update-snapshots and
compares the files they write. The node:test normalization also drops
the "... N lines matching cause stack trace ..." line Node prints for an
error cause, with the stack frames it summarizes.
markAsUncloneable(value) marks a plain object, a record, an error or a
class instance (scr_json.c); structuredClone and postMessage then throw
DataCloneError "Cannot clone object of unsupported type." for it, also
where it is nested. As in Node, the mark has no effect on primitives,
arrays, typed arrays and ArrayBuffers. Marked values are retained until
the thread exits; with nothing marked, the clone paths add one branch.
worker[Symbol.asyncDispose]() terminates the worker and resolves with
undefined after its 'exit' event, so `await using` disposes a Worker at
the end of its block. The Worker handle's symbol_get op answers the
well-known symbol: the symbol unit registers Symbol.asyncDispose with the
dyn core when the program names it, and scr_dyn_is_async_dispose_symbol
tells the worker unit, so worker programs that never name the symbol do
not link the symbol unit.
static-support.json adds rows for markAsUncloneable, isInternalThread
(already lowered as false and declared) and worker[Symbol.asyncDispose]().
Corpus 5990-5991 match Node v24.22.0; corpus-costs.json records their
costs.
The util.types brands answered isFloat16Array with false for every value.
Float16Array now answers it from the value's storage kind, ahead of that
probe, so this drops the unreachable entry and the declaration that
returns boolean.
Float16Array is a native typed-array kind with IEEE 754 binary16 storage.
Stores round the number to nearest-even in one step from the double, as
Node does, including overflow to Infinity, subnormals, -0 and NaN; reads
widen exactly. The runtime converts with integer operations, so no target
needs half-precision hardware or compiler-rt support.
Construction, element reads and writes, ArrayBuffer views, fill, set,
subarray, slice, both sort orders, constructor values and checked-dynamic
storage work as for the other typed arrays. Math.f16round,
DataView.prototype.getFloat16 and setFloat16, and util.types.isFloat16Array
compile.
- util.format %d and %i format bigint, symbol, null and undefined arguments
- util.format %j prints [Circular] for circular arguments
- util.types answers the remaining brand checks
- util.inspect prints WeakMap, WeakSet, Date and numeric typed array values as Node does
- util.inspect lowers the breakLength, compact and sorted options
- URLSearchParams iterators compile outside a for-of head
- URLSearchParams can be built from [string, string] tuple pairs
- querystring.stringify writes bigint values
- StringDecoder accepts an encoding known only at run time
- os.setPriority is lowered
- The EventEmitter leak warning reaches process 'warning' listeners on the next tick
- The events listener-count and max-listener helpers work over EventEmitter instances
- timers/promises setTimeout, setImmediate and scheduler.wait accept an AbortSignal
- timers/promises scheduler.wait and scheduler.yield are lowered
net.SocketAddress and net.BlockList were refused at compile time. They
are now native checked-dynamic handles in scr_net.c, so their getters
and methods also work on untyped values, and they match Node 24:
- new SocketAddress(options) checks the options in Node's order
(ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE for the family,
ERR_SOCKET_BAD_PORT, ERR_OUT_OF_RANGE for the flow label) and throws
ERR_INVALID_ADDRESS for an address that does not parse. parse() reads
URL.parse(`http://${input}`)'s hostname and port and answers undefined
on failure; isSocketAddress(), toJSON() and util.inspect match.
- BlockList takes address, range, subnet and CIDR rules (addAddress,
addAddresses, addRange, addSubnet, addCIDR, addCIDRs), removes them
(removeAddress, removeRange, removeSubnet, removeCIDR, clear) and lists
them in Node's order: IPv4 addresses, IPv6 addresses, subnets, then
ranges, each newest first, with a repeated address kept once.
- check() compares across families through the IPv4-mapped IPv6 form,
as Node's SocketAddress::compare and is_in_network do, and answers
false for a string that does not parse in the given family.
- rules, size, toJSON(), fromJSON() (Node's rule patterns), isBlockList(),
PRIVATE_RANGES, instanceof and util.inspect match, with the validation
errors of lib/internal/blocklist.js.
Corpus 5659 and the node-types fixture net-blocklist.ts cover both
classes.
The compatibility registry lists the BlockList methods and
SocketAddress.parse as supported, and both classes as partial: their
structured clone and worker transfer forms are not modeled.