Commit Graph
311 Commits
Author SHA1 Message Date
Chris Tate 714dbfe2bb Merge main 2026-10-22 06:59:47 -05:00
Chris Tate df6de14c94 Support new tls.TLSSocket and tls.connect({ socket }) (#1791) 2026-10-22 06:01:47 -05:00
Chris Tate 01a467060b Support WebCrypto wrapKey and unwrapKey
crypto.subtle.wrapKey exports a secret key (raw, raw-secret or jwk) and
encrypts it, and unwrapKey decrypts and imports one, as Node's
webcrypto.js does: the wrap algorithm normalizes through the wrapKey
registry (AES-KW) and then the encrypt registry (AES-GCM, AES-CBC and
AES-CTR with their dictionaries), the wrapping key's algorithm and
usages are checked, and a JWK payload is its JSON text, padded with
spaces to a multiple of 8 bytes for AES-KW. A wrapped JWK that is not
UTF-8 JSON with a kty member rejects with Node's DataError.

AES-KW is RFC 3394 key wrap over the vendored mbedTLS block cipher, in
scr_webcrypto_aes.c beside the other AES modes: wrapping takes a
multiple of 8 bytes and at least 16, unwrapping a multiple of 8 and at
least 24, and the integrity check value is compared in constant time.
The cipher's failures, and unwrapKey's import errors, reject on a later
loop turn as Node's job promises do.

This completes jose's symmetric key management: 7132 runs the real
jose 6.2.12 PBES2-HS256+A128KW, PBES2-HS384+A192KW, PBES2-HS512+A256KW,
A128KW and A256KW paths (EncryptJWT and jwtDecrypt) against Node.
getPublicKey replaces wrapKey as the refused method in the fenced
fixture and the JavaScript refusal test.
2026-10-22 05:48:30 -05:00
Chris Tate 1360830149 Support new tls.TLSSocket and tls.connect({ socket }) 2026-10-22 04:52:44 -05:00
Chris Tate b5fb4115e1 Merge main 2026-10-22 03:33:29 -05:00
Chris Tate 7d20212dd4 Merge main 2026-10-22 03:24:23 -05:00
Chris Tate 1773ffbef3 WebCrypto: the crypto global, node:crypto's webcrypto, and secret-key SubtleCrypto 2026-10-22 02:52:01 -05:00
Chris Tateandsaboursepideh 1a240fbf10 Support asymmetric keys, signatures and Ed25519 in node:crypto and WebCrypto
Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-22 02:49:33 -05:00
Chris Tate d426960f45 Make Cipheriv and Decipheriv Transforms and accept function stages anywhere in pipeline()
Cipheriv and Decipheriv values are Transforms, as in Node: they can be
written and ended, piped into, used as pipeline() and stream/promises
pipeline stages, composed, read, iterated with for await, read by
stream/consumers, passed to finished(), and listened to, and their stream
properties read as in Node. Each chunk goes through the cipher's
update(), and the end pushes final() or reports its error through
'error'; writing after final() throws out of write(). The stream unit
builds a cipher's Transform on its first stream use from operations the
cipher's handle provides (ScrDynHandleOps.transform), so programs that
use ciphers without streams link as before. The cipher is a
collector-headered object behind a traced handle: its Transform's steps
hold it, and the pair is reclaimed as a cycle. Through untyped values, in
a program that uses streams, a cipher's stream properties and its on,
once, off, pipe, unpipe, read, pause, resume, setEncoding, write, end,
destroy, cork and uncork members run on its Transform; other stream
members throw a coded SC2020 error.

A Transform holds a write's callback while its readable side is at the
high-water mark and runs it at the next read, and runs it on a tick once
the readable side has ended, as Transform._write does. write() on a
PassThrough, a Transform, a Hash or a cipher used to return true however
much the readable side buffered. zlib streams applied the rule already
and keep their timing.

pipeline() accepts function stages anywhere among its streams, so runs of
async generator stages can sit on both sides of a stream. A stage can be
named by a function declaration, or a const bound to a function
expression, that nothing reassigns; its body lowers again at the
pipeline with its source typed by the pipeline. The source can also be
an array, a string, a synchronous generator object, or a synchronous
generator function without parameters: an async generator steps the
source's iterator and yields each element, and the next stage receives
each element on the same tick as with Node's for await over the source.
The runtime's function-stage entry points take a bitmask of the function
positions and one (next, read, return) helper triple for each generator
pumped into a stream, so RUNTIME_ABI_VERSION is now 25. In JavaScript,
the chunks a stage receives from the previous stage's generator read as
the strings or Buffers it yields, so their string and Buffer methods
compile.

Corpus 7055-7059 cover cipher streams in TypeScript and JavaScript, their
errors, close order and backpressure, the pipeline stages and sources
with their tick order, and Transform backpressure.
2026-10-22 01:55:38 -05:00
Chris Tateandsaboursepideh 9b70fb5e7b Negotiate ALPN and bound the TLS version in tls.connect and tls.createServer
tls.connect and tls.createServer accept ALPNProtocols as an array of
strings, a Buffer or a Uint8Array in the wire format. The server answers
with the first of its own protocols that the client offers, and both
sockets report it as alpnProtocol. A client that offers none of the
server's protocols fails with ERR_SSL_TLSV1_ALERT_NO_APPLICATION_PROTOCOL.

minVersion and maxVersion bound the handshake, and getProtocol() reports
the version it picked. Ranges that do not overlap fail with Node's
ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION or ERR_SSL_NO_PROTOCOLS_AVAILABLE,
and invalid versions throw Node's TypeError. tls.connect's timeout option
arms the socket's inactivity timeout before it connects.

Refs #1438

Co-authored-by: saboursepideh <25549001+saboursepideh@users.noreply.github.com>
2026-10-21 08:58:29 -05:00
Chris Tate 06e202a126 Leave the per-chapter status counts out of the compatibility files
The ledger and the public artifact held a block of status counts for
every chapter. Nothing reads them (the docs page uses a chapter's slug
and title), and every classification change rewrote its chapter's block,
so concurrent compatibility changes conflicted there. The rows they were
counted from are unchanged.
2026-10-20 20:46:25 -05:00
Chris Tate 3ef1b9f7be Merge main 2026-10-20 20:40:58 -05:00
Chris Tate 2582e46e76 Support crypto.createECDH and ECDH.convertKey
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.
2026-10-20 19:17:05 -05:00
Chris Tate 8ba4208ca5 Build the Node compatibility backlog from the ledger instead of committing it
node-v24-backlog.json is a pure function of the committed ledger, and only
the backlog command read it. Its summary counts changed with every
classification change, so concurrent compatibility changes conflicted on
them. pnpm node-compat:backlog now builds the backlog in memory from
node-v24-internal.json with the same output, the generator no longer
writes the file, and the local check fails if a merge brings it back.
2026-10-20 18:17:57 -05:00
Chris Tate ae3642d1bb Merge main 2026-10-20 12:08:37 -05:00
Chris Tate a43f0a71a6 Merge main 2026-10-20 11:36:33 -05:00
Chris Tate 324f173f65 Merge main 2026-10-20 11:23:45 -05:00
Chris Tate 8eb5ad28e5 Keep WebAssembly references and memory storage alive while they are in use; merge main
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.
2026-10-20 10:58:34 -05:00
Chris Tate 21c6505709 Match tuple rests and positional any[] parameters in function patterns, and merge main
- 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.
2026-10-20 10:37:43 -05:00
Chris Tate c4ce357a9e Merge main and answer false for fixed-parameter function patterns that need more arguments
- 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.
2026-10-20 09:24:29 -05:00
Chris Tate 7d555c4cf3 Merge main 2026-10-20 09:04:02 -05:00
Chris Tate 7cd7b483bf Merge main 2026-10-20 09:02:06 -05:00
Chris Tate acf8b28f40 Merge main 2026-10-20 08:57:09 -05:00
Chris Tate c8b22e3435 Merge main 2026-10-20 02:40:17 -05:00
Chris Tate 391b9ac00e Merge main 2026-10-20 02:15:13 -05:00
Malte UblandClaude Opus 5.5 f4c6441cee Merge main (through opt/variadic-generic-wrappers) into opt/trace-reachable-blockers
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>
2026-10-20 00:10:52 -07:00
Malte Ubl 25e40afa83 Merge remote-tracking branch 'origin/main' into opt/bc-node-surface 2026-10-19 23:16:41 -07:00
Chris Tate 98a7d3b33d Merge main 2026-10-20 00:08:29 -05:00
Malte UblandClaude Opus 5.5 c17eec0261 Compile event-loop metrics, predicate-narrowed catch bindings and other wrapper-reachable gaps
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>
2026-10-19 19:04:07 -07:00
Malte UblandClaude Opus 5.5 20ab1c0531 Merge main into opt/bc-node-surface
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>
2026-10-19 19:02:41 -07:00
Chris Tate 2119198c55 Merge main 2026-10-19 20:53:44 -05:00
Malte UblandClaude Opus 5.5 503154e387 Compile the boot-path Node surface: process.send presence, process.exitCode reads, wider stream hooks, void race entries, RequestOptions and promisify of callbacks
- `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>
2026-10-19 16:46:46 -07:00
Chris Tate 305832a12d Merge main 2026-10-19 17:32:14 -05:00
Chris Tate a31b1fe23e Merge main 2026-10-19 17:17:48 -05:00
Chris Tate 37f54ff157 Merge main 2026-10-19 17:14:32 -05:00
Chris Tate c82b6d8188 Merge main 2026-10-19 17:06:58 -05:00
Chris Tate 0e02fc28ca Accept mock.fn() without an original
node:test changes, matching Node 24.22:

- mock.fn() and t.mock.fn() without an original return undefined and
  record the arguments of each call, whatever the callers pass. The
  original and the initial implementation are a function that returns
  undefined, so restore() brings that function back.
- mock.fn<F>() with an explicit function type records F's parameters
  when F's return type is void or includes undefined. A return type
  without undefined, such as mock.fn<() => number>(), throws SC2020.

mock.fn()'s default type, (...args: any[]) => undefined, maps to the
checked-dynamic rest ABI used for program-declared (...args: unknown[])
functions: the calls log records the argument array, and the mock's
wrapper spreads it into the implementation.

A function whose fixed parameters are all unknown or any, or that has
none, now converts to a checked-dynamic rest signature without fixed
parameters, such as (...args: unknown[]) => void: each call passes the
arguments in order, undefined for the ones the caller left out. This is
how () => {} reaches mockImplementation and mockImplementationOnce, and
it applies to any such slot in TypeScript programs, which threw SC1090
before.

The mock-fn-default fixture and corpus program 5816 compare with Node,
plain and with SCRIPTC_SAN=1.
2026-10-19 16:59:05 -05:00
Malte UblandClaude Opus 5.5 6e68a811f1 WebAssembly W5: the Memory, Table and Global constructors, Module.imports/exports/customSections, and function references from tables
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>
2026-10-19 12:27:45 -07:00
Malte UblandClaude Opus 5.5 cf198952e2 Compile the real jose root entry: string durations, chained builders, navigator
jose 6.2.12 now compiles natively through its root entry (`import { jwtVerify,
errors } from "jose"`), from TypeScript and JavaScript, with chained SignJWT
builders and string durations:

- A discarded `a && b;` / `a || b;` statement whose value form fails on its own
  result type (jose's secs(): `(!m || m[4] && m[1]) && invalid()`) lowers as
  `if (a) b;` / `if (!a) b;`. The value form still lowers first, so statements
  that compiled before are unchanged.
- In JavaScript, a number-keyed read on a `T[] | null | undefined` value outside
  an optional chain (`m[2]` after a statement ruled `m` non-null) reads the
  array arm like any element read: a group that did not participate is
  undefined instead of a trap.
- A JavaScript fluent method whose returns all end in `this` (including comma
  sequences) narrows a used call result back to the receiver's subclass. A
  receiver lowered at an ancestor of the class a call needs is bridged with a
  verified downcast instead of an upcast the validator rejected (the
  "upcast JWTClaimsBuilder does not extend SignJWT" internal error).
- `typeof navigator` is "object" and `navigator.userAgent` is "Node.js/24", as
  in Node 24, for the free name in JavaScript and under @types/node. Both fold
  to literals; other members refuse by name.
- TypeScript object destructuring of an `any` result of a JavaScript function
  (jose's jwtVerify) reads its members like the member reads that already
  compiled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:30:48 -07:00
Chris Tate 1f07166fbf Merge main 2026-10-19 08:56:48 -05:00
Chris Tate 178646baf0 Merge main 2026-10-19 08:31:43 -05:00
Chris Tate a57089aebf Accept readline's dispose, instanceof, dynamic streams and non-string prompts
rl[Symbol.dispose]() closes the interface, as Node's dispose method
calls close(). rl instanceof readline.Interface and
readlinePromises.Interface answer which module created the interface:
rl.create's mode marks readline/promises interfaces, and the fallback
declarations declare both Interface classes as values.

In JavaScript, createInterface's input and output can be checked-dynamic
values (a function's result, an object's member, an element that may be
undefined): rl.inputFromDyn and rl.outputFromDyn unbox the stream they
hold, an output of null or undefined means no output, and other values
are refused.

In JavaScript, question() and setPrompt() accept a value that is not a
string. When such a prompt is written, an interface that is not a
terminal throws ERR_INVALID_ARG_TYPE as Node's kWriteToOutput does, and
the readline/promises question rejects with it; the question stays
pending and the previous prompt returns when it is answered. A terminal
interface writes String(value) of a primitive.

Corpus 5876-5878 match Node v24.22.0.
2026-10-19 07:51:54 -05:00
Malte UblandClaude Opus 5.5 2b8cd22423 Add WebCrypto HMAC: crypto.subtle importKey, sign, verify and digest
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>
2026-10-19 05:34:26 -07:00
Malte UblandClaude Opus 5.5 78184a9869 WebAssembly W3–W4: translate modules to native code at build time, the JavaScript API over them, and Module transfer to workers
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>
2026-10-19 05:33:11 -07:00
Chris Tate e82686eab7 Add getTestContext to node:test
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.
2026-10-19 06:29:28 -05:00
Chris Tate 1f7f029a05 Carry AsyncLocalStorage stores into timer, immediate, tick and microtask callbacks
AsyncLocalStorage changes, matching Node 24.22:

- setTimeout, setInterval, setImmediate, process.nextTick and
  queueMicrotask callbacks run with the stores that were active when they
  were scheduled. Before, getStore() returned undefined in them.
- An interval keeps that context on every tick, and a refreshed timer
  keeps it too.
- enterWith inside one of these callbacks lasts until the callback
  returns. An enterWith in top-level code ends when the main module's body
  returns, and one inside a callback scheduled with no store active ends
  when that callback returns, so the store does not reach callbacks that
  were scheduled without it.

Programs that enter stores (store.run, exit, enterWith, or a
diagnostics_channel store binding) schedule through new *_als runtime
entry points, which wrap a callback scheduled with a store active in a
closure that installs the stores around the call. The emitter picks them
from the new asyncContext runtime feature; other programs keep the plain
entry points, so their scheduling is unchanged.

The limitations page notes that completion callbacks of callback-style
I/O APIs still run with no store.

5815-als-callback-context compares the stores each kind of callback sees
with Node, plain and with SCRIPTC_SAN=1.
2026-10-18 22:26:09 -05:00
Chris Tate 008eb0712c Merge main 2026-10-18 19:13:48 -05:00
Chris Tate 707ac5cf77 Carry Hash and Hmac values through unknown and any slots (#1257) 2026-10-18 18:38:52 -05:00
Malte UblandClaude Opus 5.5 b8ac5e3f6b Merge origin/main into opt/builtins-npm2
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>
2026-10-18 16:03:19 -07:00
Malte UblandClaude Opus 5.5 ec4a693091 Map crypto.getRandomValues and fs.constants in the compatibility ledger
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>
2026-10-18 15:55:27 -07:00