Commit Graph
6750 Commits
Author SHA1 Message Date
Chris Tate b429d4ca6f Merge pull request #1108 from vercel-labs/ctate/add-messagechannel-receivemessageonport-5d9f36
Add MessageChannel, receiveMessageOnPort and MessagePort transfer
2026-10-17 09:35:15 -05:00
Chris Tate 4b7ff49da8 Support createHash and createHmac values as streams (#1120) 2026-10-17 08:47:55 -05:00
Chris Tate defca89c93 Revert "Do not merge: CI check of the exec source changes on current main" (#1186) 2026-10-17 08:46:29 -05:00
Chris Tate 591994217c Run every subtest at once with concurrency: true
Node sets a test's concurrency to Infinity for `true` whenever the test
has a parent, and to os.availableParallelism() - 1 only for the root
test. Every test and suite a program declares has a parent, so `true`
now runs all of its subtests at once instead of capping them at the CPU
count minus one.

The concurrency fixture adds a suite of 64 concurrent tests that checks
all of them run at once.
2026-10-17 08:39:10 -05:00
Chris Tate 3fadf25b02 Merge main 2026-10-17 08:38:10 -05:00
Chris Tate 89e920ac99 Give a copy of a Hash whose stream closed a stream of its own
copy() of a Hash whose stream was destroyed before a digest, by
destroy() or an error, carried over the closed-stream flag, so the
copy's first stream use threw the SC2020 error. The copy now starts
without a stream, as in Node, and gets one on first stream use.
Corpus 5761 covers a copy written and ended after the original's
'close' event.
2026-10-17 08:24:36 -05:00
Chris Tate 4a53870702 Revert "Do not merge: CI check of the exec source changes on current main" (#1184) 2026-10-17 08:20:45 -05:00
Chris Tate 66125d783b Do not merge: CI check of the exec source changes on current main (#1184) 2026-10-17 05:08:21 -05:00
Chris Tate 4060f9135b Do not merge: CI check of the exec source changes on current main 2026-10-17 04:30:49 -05:00
Chris Tate 31ab50381f Give Object.groupBy results a null prototype (#1182) 2026-10-17 03:57:52 -05:00
Chris Tate 78ec9c1752 Fix or refuse Node 24 builtin forms that ran with wrong output (#1181) 2026-10-17 03:53:23 -05:00
Chris Tate 844aa87ae0 Give Object.groupBy results a null prototype
Object.groupBy now builds its result record with a null prototype,
stored as a flag on the record's overflow map. Inspection prints
"[Object: null prototype]", Object.getPrototypeOf answers null, `in`
finds no inherited names, and String(), template literals, toString(),
hasOwnProperty() and propertyIsEnumerable() throw Node's TypeErrors.
Spread copies and structuredClone keep Object.prototype, as in Node.
Index-signature records converted from an Object.create(null) value
keep the null prototype too.
2026-10-17 03:35:02 -05:00
Chris Tate d5034758fb Merge main 2026-10-17 03:33:06 -05:00
Chris Tate 53023c615d Merge main 2026-10-17 03:30:55 -05:00
Chris Tate b37c9491fe Fix or refuse Node 24 builtin forms that ran with wrong output
- JavaScript calls of Promise.withResolvers' resolve/reject through the
  record no longer fail with an internal compiler error.
- new RegExp with the d or v flag is refused (SC1120) for literal flags
  and throws a named error for computed flags, instead of SyntaxError.
- JavaScript .groups reads on matches whose regex is not traceable are
  refused like TypeScript's, instead of reading undefined.
- Iterator helpers on the string iterator are refused instead of
  failing at run time.
2026-10-17 03:29:53 -05:00
Chris Tate 05cb9bd609 Add the concurrency test option
node:test changes, matching Node 24.22:

- { concurrency } on describe, suite, test, it and t.test sets how many
  subtests run at once: a number, true for os.availableParallelism() - 1,
  or false for 1. Subtests inherit the parent's value.
- Above 1, each subtest runs on its own fiber. Subtests start in
  declaration order as slots free up, and t.test and test() calls inside a
  concurrent test start the subtest at once; t.test's promise settles when
  it finishes. The parent finishes after all of them.
- Results, diagnostics and failures are reported in declaration order.
- A concurrent subtest waits after its beforeEach hooks, and a concurrent
  suite before its children, for as many turns as Node's runner does, so
  sibling bodies interleave as they do there.
- A concurrency that is not an integer from 1 to 4294967295 throws Node's
  ERR_OUT_OF_RANGE RangeError when the test or suite is created.
- Nested test() calls and hooks inside a concurrent subtest attach to the
  subtest that runs on the calling fiber.

The concurrency fixture compares the reporter output, the order of the
bodies' side effects, and the range errors with Node, plain and with
SCRIPTC_SAN=1.
2026-10-17 03:28:15 -05:00
Chris Tate 0c4056fb0e Add the plan and timeout test options (#1165) 2026-10-17 03:22:28 -05:00
Chris Tate 26aeeb4c6f Lower util.promisify over the zlib callback one-shots (#1179) 2026-10-17 03:10:14 -05:00
Chris Tate 2896e94b20 Merge main 2026-10-17 02:59:21 -05:00
Chris Tate bf63243e64 Add fs.cpSync, fs.cp and fs/promises cp (#1166) 2026-10-17 02:55:40 -05:00
Chris Tate 7dcb1e6c4e Lower util.promisify over the zlib callback one-shots
promisify(zlib.deflate) and the other callback one-shots (inflate,
deflateRaw, inflateRaw, gzip, gunzip, unzip) over a named or namespace
import of node:zlib now lower. A call through the binding,
gzipAsync(data, options?), runs the callback form's work on the worker
pool and returns a promise that settles where the callback would run:
fulfilled with the Buffer, or rejected with the callback form's Error
(errno, code and message). An invalid option or input rejects the promise
instead of throwing, as the throw inside Node's promisify wrapper does.

The runtime adds scr_zlib_codec_promise and its checked-dynamic twin,
which share the callback forms' options ladder and worker-pool operation
and settle a pending promise in place of the callback. The fallback
declarations gain the matching util.promisify overload.
2026-10-17 02:46:20 -05:00
Chris Tate bc0636de7b Merge main 2026-10-17 02:35:31 -05:00
Chris Tate 0777ad7223 Merge main 2026-10-17 02:33:12 -05:00
Chris Tate 5c39821726 Merge main 2026-10-17 02:30:43 -05:00
Chris Tate 6fa1f8ce01 Lower util.promisify(stream.finished) and util.promisify(stream.pipeline) (#1177) 2026-10-17 02:19:12 -05:00
Chris Tate f61e5dc938 Add readline's terminal mode, cursor helpers and emitKeypressEvents 2026-10-17 02:10:27 -05:00
Chris Tate eca55f55ed Lower Array.fromAsync over arrays (#1176) 2026-10-17 02:09:54 -05:00
Chris Tate 3e83b4a5c7 Lower util.promisify(stream.finished) and util.promisify(stream.pipeline)
Node's util.promisify returns the custom promisified forms of
stream.finished and stream.pipeline, which are stream/promises' functions
of the same name. A const binding of promisify over the node:stream named
imports or namespace members now registers that stream/promises member
as the projection, so calls through the binding lower as the
stream/promises call and accept the stream arguments and options it
accepts.

The fallback declarations gain the matching util.promisify overloads.
pipeline's overload comes first: its (...streams: unknown[]) => unknown
type is assignable to every other target's parameter type.
2026-10-17 01:51:57 -05:00
Chris Tate f7ad1f96a4 Lower Array.fromAsync over arrays
The ESNext array lib is now part of the type world, and Array.fromAsync(source,
mapFn?) compiles when the source is an array of values or of promises. Each
call lowers to an async helper with the spec's loop: elements are awaited
through the async-from-sync iterator, with the same microtask turns as a
`for await` loop over an array, and a mapping function's result is awaited
before it is stored. A throwing or rejecting mapping function and a rejected
element close the iterator before the returned promise rejects, on the turn
Node rejects it. Async generator sources, a thisArg, and arrays that mix
values and promises are refused.
2026-10-17 01:47:40 -05:00
Chris Tate 4700ce5c21 Dispose Disposable-typed values and expose SuppressedError's error and suppressed (#1169) 2026-10-17 01:43:10 -05:00
Chris Tate c34dd7898b Keep disposal snapshots out of programs that never read them
The SuppressedError hook was installed by a load-time constructor in
scr_json.c, which kept the checked-dynamic conversion of caught values in
every program that linked that unit. scr_exc_suppress now records the
disposal and body snapshots in a side table in scr_exception.c, the error's
dyn view reads them when scr_json.c builds it, and the SuppressedError's
teardown releases them.
2026-10-16 23:09:58 -05:00
Chris Tate 6d4843ea99 Dispose Disposable-typed values and expose SuppressedError's error and suppressed
`using` and `await using` now accept object literals and other values typed
Disposable, AsyncDisposable or unknown. The declaration reads the value's
[Symbol.dispose] method ([Symbol.asyncDispose] first for `await using`) and
throws Node's TypeErrors for values that are not objects and for missing or
non-callable methods; null and undefined dispose nothing. Scope exit calls the
method with the value as `this`, and `await using` awaits the result of an
async method but not of the sync fallback. A class instance reached through
such a value throws an SC2020 error, because its dynamic view has no methods.

SuppressedError is its own error class: a failing disposal over a pending
error throws one whose `error` is the disposal error and whose `suppressed` is
the pending error, both non-enumerable as in Node. `new SuppressedError(error,
suppressed, message?)` and `instanceof SuppressedError` compile, and
`extends SuppressedError` is refused.
2026-10-16 22:36:18 -05:00
Chris Tate 1b34b4069a Revert "Lower child_process.exec's callback forms" (#1168) 2026-10-16 22:30:09 -05:00
Chris Tate d7a5568726 Revert "Lower child_process.exec's callback forms" (#1161) 2026-10-16 22:05:36 -05:00
Chris Tate 2e96c71a67 Order only timers in the promisified timers test (#1167) 2026-10-16 22:02:47 -05:00
Chris Tate 23e92e4cff Match libc++'s copy errors and macOS's PATH_MAX in the fs.cp tests
std::filesystem::copy_file reports a destination that exists but is not
a regular file as invalid_argument in libstdc++ and as not_supported in
libc++, which Node's macOS builds use. cpSync on Apple platforms now
throws ENOTSUP there, as Node does, and the fs errno names include
ENOTSUP.

Corpus 5571 copies a 400-level directory chain instead of 1500 levels,
so its deepest path stays under macOS's 1024-byte PATH_MAX.
2026-10-16 21:53:32 -05:00
Chris Tate 85690cf2f4 Order only timers in the promisified timers test
5628-promisify-timers.ts compared a 1 ms timer with an immediate armed
at the same time. Their order depends on how much time has passed when
the loop reaches its timers phase: once the delay has elapsed, the timer
fires before the check phase. Node prints either order under load. The
test now orders three timers armed together (1, 10 and 30 ms), which
settle in deadline order. The node-types fixture does the same with two
timers.
2026-10-16 21:36:39 -05:00
Chris Tate dfac9a950c Merge main 2026-10-16 21:33:56 -05:00
Chris Tate 53f22f7054 Merge main 2026-10-16 21:31:59 -05:00
Chris Tate db94b2d257 Add fs.cpSync, fs.cp and fs/promises cp
fs.cpSync, fs.cp and fs/promises cp compile with every option:
dereference, errorOnExist, filter, force, mode, preserveTimestamps,
recursive and verbatimSymlinks. They follow Node's two implementations.
cpSync follows cp-sync.js over the fs binding: copy errors are plain
Errors with a code, std::error_code failures throw an ErrnoException,
and links inside a copied directory point at their canonical targets.
fs.cp and fs/promises cp follow cp.js, whose copy errors are
SystemErrors with code, info, errno, syscall and path.

Options and paths validate in Node's order with Node's errors. filter
runs for the top-level paths and each entry in Node's order; an async
filter must return a promise that is already settled, and a pending one
throws SC2020. The callback and promise forms complete the copy before
the callback runs or the promise settles. Windows builds throw SC2020.

The copy lives in the new scr_fs_cp.c. Directory walks keep their frames
on the heap, so deep trees also copy on a fiber's stack. File contents
copy with sendfile on Linux, and COPYFILE_FICLONE uses FICLONE
where it exists. ScrError gains the SystemError context and the
ErrnoException key order, with matching fields in the compiler's Error
layout. The stored fs.cp callback uses the same implementation.

Corpus 5567-5571 cover the sync, callback and promise forms, errors,
symlinks and 1500-level trees.
2026-10-16 21:06:22 -05:00
Chris Tate d8c2b0f96d Lower child_process.exec's callback forms (#1161) 2026-10-16 20:57:36 -05:00
Chris Tate e2c34f381b Add the plan and timeout test options 2026-10-16 20:55:26 -05:00
Chris Tate 6f4334be00 Accept Map arguments in the Set composition methods (#1164) 2026-10-16 20:51:52 -05:00
Chris Tate a5bfafe590 Parse IPv4 hosts in special-scheme URLs like WHATWG URL (#1163) 2026-10-16 20:49:31 -05:00
Chris Tate 5e016cb5c4 Lower util.promisify(setTimeout) and util.promisify(setImmediate) (#1162) 2026-10-16 20:46:57 -05:00
Chris Tate 5da70549a0 Keep int32 class fields when a program registers process event listeners (#1160) 2026-10-16 20:36:11 -05:00
Chris Tate 2ed22d38a4 Merge main 2026-10-16 20:32:43 -05:00
Chris Tate d53c1b8ba0 Merge main 2026-10-16 20:30:22 -05:00
Chris Tate 58842aa39b Accept Map arguments in the Set composition methods
union, intersection, difference, symmetricDifference, isSubsetOf,
isSupersetOf and isDisjointFrom now compile with a Map argument whose key type
is the Set's element type, as well as with a Set. A Map's set record reads its
keys, and its size, has and keys are the builtins, so the helper loops walk
and test the Map's keys in the order the spec gives. Other set-like arguments
are still refused.
2026-10-16 20:28:04 -05:00
Chris Tate b18a28bdef Parse IPv4 hosts in special-scheme URLs like WHATWG URL
The URL host parser kept a host whose last label is a number as a
domain, so new URL("http://127.1").hostname was "127.1" where Node
answers 127.0.0.1, and hosts such as 1.2.3.256, 0x100000000 and
1.2.3.4.5 parsed where Node throws Invalid URL.

A special-scheme host (file: included) that ends in a number now goes
through the WHATWG IPv4 parser: up to four labels after one trailing
empty label, each a decimal, 0x hexadecimal or leading-zero octal
number, every label but the last at most 255 and the last below
256^(5 - labels). The address serializes as dotted decimal, and any
failure makes the URL invalid. The constructor, URL.parse, URL.canParse,
the host and hostname setters and domainToASCII/domainToUnicode share
the parser. Non-special schemes keep their opaque host as written.
Corpus 5860 covers each form.
2026-10-16 20:27:21 -05:00