Commit Graph
204 Commits
Author SHA1 Message Date
Chris Tate 5c39821726 Merge main 2026-10-17 02:30:43 -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 d7a5568726 Revert "Lower child_process.exec's callback forms" (#1161) 2026-10-16 22:05:36 -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 d53c1b8ba0 Merge main 2026-10-16 20:30:22 -05:00
Chris Tate 9681454556 Lower util.promisify(setTimeout) and util.promisify(setImmediate)
Node's util.promisify returns the custom promisified form of setTimeout
and setImmediate, which is timers/promises' function of the same name.
A const binding of promisify over the global functions, the node:timers
named imports or the node:timers namespace members now registers that
timers/promises member as the projection, so calls through the binding
lower as the timers/promises call: they fulfill with the given value
(undefined without one) after the delay or in the check phase.

The fallback declarations gain the matching util.promisify overloads.
2026-10-16 20:22:19 -05:00
Chris Tate cef618b553 Lower child_process.exec's callback forms
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.
2026-10-16 20:14:33 -05:00
Chris Tate 85439ae670 Merge main 2026-10-16 19:54:32 -05:00
Chris Tate 2e52dcd35c Add Readable.from over Sets, iterators and class iterables (#1074) 2026-10-16 19:30:21 -05:00
Chris Tate 5a091f050f Run the zlib one-shot forms on the stream engine (#1139) 2026-10-16 19:11:18 -05:00
Chris Tate d533fd7ce9 Merge main 2026-10-16 09:33:08 -05:00
Chris Tate ed5ab6796b Run the zlib one-shot forms on the stream engine
deflateSync, inflateSync, gzipSync, gunzipSync, deflateRawSync,
inflateRawSync and unzipSync, and their callback forms, accept Node's
options and run Node's processChunkSync over the zlib stream engine.

- Options (windowBits, level, memLevel, strategy, dictionary, chunkSize,
  finishFlush, maxOutputLength, rejectGarbageAfterEnd) are validated at
  run time with Node's ladder, shared with the stream classes; info is
  refused at run time.
- Errors carry Node's errno and code; maxOutputLength reports
  ERR_BUFFER_TOO_LARGE and rejectGarbageAfterEnd
  ERR_TRAILING_JUNK_AFTER_STREAM_END. The default-options decoders also
  read every gzip member.
- Untyped JavaScript inputs may be strings, TypedArrays, DataViews or
  ArrayBuffers; other values throw Node's ERR_INVALID_ARG_TYPE after the
  options are checked.
- Results are Buffers, as in Node: they convert to their text.

Corpus programs 5378, 5379 and 5415 cover the sync, callback and untyped
input forms.
2026-10-16 09:18:32 -05:00
Chris Tate daa5cc256f Lower util.promisify over the fs callback functions
promisify(fs.X) over a named or namespace import of node:fs lowers as
the fs/promises member of the same name for access, appendFile, chmod,
chown, copyFile, lchown, link, lstat, lutimes, mkdir, mkdtemp, readFile,
readdir, readlink, realpath, rename, rm, rmdir, stat, statfs, symlink,
truncate, unlink, utimes and writeFile. For these, Node's promisified
callback form settles with the callback's single result and rejects
with the same errors as the fs/promises call. Calls through the bound
function accept the forms the fs/promises lowering accepts and refuse
the others by name.

fs.open (an fd number where fs/promises.open gives a FileHandle),
fs.exists (a custom promisify) and fs.read/fs.write (multi-value
callbacks) remain refused.
2026-10-16 09:14:35 -05:00
Chris Tate 451e925b12 Merge main 2026-10-16 08:32:21 -05:00
Chris Tate a5da46267b Merge main 2026-10-16 08:31:20 -05:00
Chris Tate a09990192e Add tlsSocket.getProtocol, getCipher, alpnProtocol and tls.DEFAULT_MIN_VERSION/MAX_VERSION (#1054) 2026-10-16 08:23:42 -05:00
Chris Tate 60623086a3 Lower util.promisify(child_process.exec)
A const binding of promisify(exec) now lowers like promisify(execFile):
each call runs the command line with /bin/sh -c, as execSync does, and
settles with { stdout, stderr }. A non-zero exit, a missing command (the
shell's 127) or a timeout rejects with Node's "Command failed: <command>\n
<stderr>" message. The options are the execFile slice: encoding utf8,
cwd, env (inline spread included), timeout, and maxBuffer accepted.

Three gaps closed for both targets along the way:

- the { stdout, stderr } result maps as a record when it is held in a
  variable, passed or returned, instead of only when destructured at the
  await (it is an anonymous literal in @types/node and in the fallback);
- a namespace member is a recognized target (promisify(cp.exec));
- util.promisify(...) through a namespace or default import is
  recognized like the named import.
2026-10-16 07:32:06 -05:00
Chris Tate d28ec3e31e Merge main 2026-10-16 04:34:48 -05:00
Chris Tate 2e8b836a79 Merge main 2026-10-16 04:33:13 -05:00
Chris Tate 18e69b81c5 Merge main 2026-10-16 04:30:20 -05:00
Chris Tate b487571539 Merge main 2026-10-16 03:33:30 -05:00
Chris Tate 4858a24420 Merge main 2026-10-16 03:30:56 -05:00
Chris Tate 0d59abe143 Pool small Buffers as Node does
Buffer.from of strings, arrays and Uint8Arrays, Buffer.allocUnsafe and
Buffer.concat now return views into a per-thread pool slab of
Buffer.poolSize bytes (64 KiB by default) when the size is below
Buffer.poolSize >>> 1, following Node's allocate, fromStringFast and
fromArrayLike: 8-byte aligned offsets, a new slab when a request does not
fit, and the four-bytes-per-unit reservation for strings. Buffer.poolSize
can be read and set, and Buffer.allocUnsafeSlow returns an unpooled Buffer.
A slab lives while any of its views does. Its ArrayBuffer cannot be
transferred, and structuredClone of a typed array view copies the whole
ArrayBuffer and keeps the view's offset. Buffer.alloc, allocUnsafe and
allocUnsafeSlow validate sizes with Node's ERR_OUT_OF_RANGE error.

The first pooled allocation finds the startup slab, which has the
default 64 KiB size. A request it cannot hold gets a slab of the current
Buffer.poolSize, as in Node, and the room is checked after every new
slab, so a Buffer.poolSize raised before the first pooled allocation
cannot place a reservation past the end of a 64 KiB slab. A reservation
larger than a fresh slab traps instead of writing past it.

Corpus 5531 (typed) and 5532 (untyped) raise Buffer.poolSize before the
first pooled allocation and allocate just under the new threshold
through each pooled path, each case in a fresh child process, and also
shrink Buffer.poolSize and change it between allocations. The
node-types fenced fixture no longer lists Buffer.poolSize, which now
compiles.
2026-10-16 01:39:10 -05:00
Chris Tate 2c04163350 Merge main 2026-10-16 01:32:17 -05:00
Chris Tate ba72bb6420 Merge main 2026-10-16 00:50:23 -05:00
Chris Tate 00bb57ac0a Merge main 2026-10-16 00:37:00 -05:00
Chris Tate 42954a3266 Add mock.fn and t.mock.fn with Node's calls log and implementation controls
node:test's mock.fn(original[, implementation]) and t.mock.fn compile.
A Mock<F> is the function-with-properties record { %call, mock }: the
callable is a wrapper closure that shares the mock context with the mock
field. The context records each call's arguments, result, error and this,
and keeps the current implementation, the original and the one-time
implementations by call index. fn.mock supports, as in Node 24.22:

- calls, read as a copy of the log;
- callCount();
- mockImplementation(fn);
- mockImplementationOnce(fn[, onCall]), for the next call by default, a
  later registration for the same index replacing the earlier one;
- resetCalls();
- restore(), which brings back the original.

A throwing implementation records the error and rethrows it. Functions
with fixed parameters or a single rest array parameter are supported; a
union return type needs an undefined arm. mock.fn() without an original,
mock.method, the tracker's reset and restoreAll, and t.mock.method are
refused with SC2020.

The node:test fixture mock-fn compares the behavior with Node, and
tests/diagnostics/mock-fences.ts covers the refusals.
2026-10-16 00:22:22 -05:00
Chris Tate de0917284d Merge main 2026-10-16 00:05:07 -05:00
Chris Tate 93dcd9865e Merge main 2026-10-16 00:01:13 -05:00
Chris Tate 524af28870 Add fs read and write streams
fs.createReadStream, fs.createWriteStream and FileHandle
createReadStream, createWriteStream and readLines return runtime
Readable and Writable streams (scr_fs_stream.c), as do new fs.ReadStream
and new fs.WriteStream in JavaScript sources. They follow
lib/internal/fs/streams.js on the stream engine:

- _construct opens the file, then emits 'open' (with the fd) and
  'ready'; _read reads up to end; _write retries short writes as
  writeAll does; _destroy waits for I/O in flight, then closes (with
  fsync first for the flush option). Every open, read, write, fsync and
  close runs on the shared worker pool.
- Options are checked in Node's order: getOptions, options.fd or the
  path, start, end, then highWaterMark, encoding, emitClose and
  autoClose. flags and mode are checked when the file opens, so their
  errors arrive as 'error' events.
- bytesRead, bytesWritten, pending, path and close(cb) compile, also
  through untyped values (close() there).

A FileHandle stream holds a reference on the handle; closing the stream
releases it and closes the handle unless autoClose is false.
FileHandle.close() follows Node's reference count: it waits for the
streams over the handle and runs their close(). readLines is a readline
Interface over createReadStream with crlfDelay: Infinity.

The stream engine gains Node's _construct (reads, buffered writes,
finish and _destroy wait for construction; destroy errors combine with
aggregateTwoErrors) and _writev, both internal. Writable.destroy() now
runs the buffered write callbacks and end() callbacks (errorBuffer),
which it dropped before. highWaterMark in a dynamic options record is
checked as getHighWaterMark does, and for await over a constructing
stream waits for construction.

Numeric flags, the fs, signal and windowsHandle options, non-UTF-8
write encodings, option functions that replace the file implementation,
and instanceof fs.ReadStream/WriteStream are refused.

Corpus 5546, 5547 and 5554 cover both lanes against Node 24.22.
2026-10-15 23:58:50 -05:00
Chris Tate 9b452b88de Merge main 2026-10-15 23:33:48 -05:00
Chris Tate 681f5ceee7 Add TestContext hooks, plan and fullName, and Node's todo and only rules
node:test changes, matching Node 24.22:

- t.before, t.after, t.beforeEach and t.afterEach lower. t.before runs
  when it is called; a throw fails the test and the subtests that follow,
  which skip their body and beforeEach hooks and run their afterEach hooks.
  t.beforeEach and t.afterEach wrap every descendant subtest. t.after runs
  after the test's afterEach chain, also when the body throws, and a throw
  fails the test.
- t.plan(count) counts the test's t.assert calls and subtests. A mismatch
  fails the test with Node's "plan expected N assertions but received M"
  string, unless an assertion failed first.
- t.fullName joins the names from the outermost suite or test with " > ".
- Tests and suites under a todo suite or test are todo, with a bare TODO
  directive, unless they are skipped or carry their own todo message. A
  todo suite reports its directive.
- describe.only, suite.only and { only: true } on a suite mark it. At each
  level, when a child is marked or contains a marked test or suite, only
  those children run.
- The failing-tests section lists failures in completion order, so
  subtests come before the test that ran them, and prints thrown strings
  quoted, as Node inspects them.

The fixtures ctx-hooks, ctx-plan, todo-suites, suite-variants,
only-nested and thrown-values compare the reporter output and exit code
with Node.
2026-10-15 23:16:04 -05:00
Chris Tate 386cfd0c65 Accept readline's history and completer options and add the history members
createInterface accepts history, historySize, removeHistoryDuplicates,
completer, tabSize and escapeCodeTimeout, as Node does for a non-terminal
interface: tabSize, escapeCodeTimeout and historySize are checked in the
constructor's order before the interface opens (ERR_OUT_OF_RANGE and
ERR_INVALID_ARG_VALUE with Node's messages), a completer that is not a
function throws ERR_INVALID_ARG_VALUE, and the values are kept on the
interface. Options may be written as shorthand properties.

rl.history answers the history option itself, or the interface's own
empty array, and historySize, historyIndex, crlfDelay, tabSize,
escapeCodeTimeout, line and terminal read as in Node. 'history'
listeners can be registered; without terminal mode Node records no
history, so they never fire.
2026-10-15 22:53:40 -05:00
Chris Tate 54727fc983 Merge main 2026-10-15 22:40:47 -05:00
Chris Tate 1b4103889b Add assert.deepEqual, partialDeepStrictEqual and the Assert class (#1079) 2026-10-15 22:22:57 -05:00
Chris Tate 7c09e1c020 Add assert.deepEqual, partialDeepStrictEqual and the Assert class 2026-10-15 22:01:38 -05:00
Chris Tate 647bc2a492 Add source functions, promise destinations and { signal } to pipeline stages
pipeline() accepts a source function (an async generator function called
with { signal }) and a destination async function, following
pipeline.js: the destination's promise value resolves the
stream/promises form and reaches the callback's second argument, and
pipeline() answers a PassThrough for it. A rejected destination destroys
that PassThrough with the reason and fails the pipeline.

Stages may take the { signal } argument: the pipeline creates its own
AbortController when one does, and aborts it when the pipeline finishes
or fails, as finishImpl does. Stage functions take the source and
{ signal } through the closure ABI, and the PassThrough and the value
are released with the pipeline.

The stream unit now links the emitter unit whenever it links, so a
pipeline of functions builds without constructing a stream. An async
generator destination and reading the PassThrough a function destination
returns are refused at compile time.
2026-10-15 21:50:01 -05:00
Chris Tate 77c9a2ff23 Merge main 2026-10-15 21:48:48 -05:00
Chris Tate 9e81333bbc Add Readable.from over Sets, iterators and class iterables
Readable.from accepts any iterable or async iterable of strings or
Buffers, following from.js: the iterator is taken when the stream is
built, before the options are read, next() is looked up and called on
each pull, a sync iterator's promise values are awaited, and destroy
calls the iterator's throw() and return() only when it has them, reading
each method when close() runs. A close() that fails with a second error
passes an AggregateError of both to the destroy callback, as
aggregateTwoErrors does.

In typed code this covers Sets of strings, Map and Set iterators,
classes whose [Symbol.iterator]() or [Symbol.asyncIterator]() is a
generator method, iterator classes, and async iterator classes, whose
return() runs at the end of the stream and on destroy. In JavaScript it
covers every iterable; an object whose iterator method is a generator
method throws a not-supported error when the stream is built.
2026-10-15 21:10:22 -05:00
Chris Tate 0ebc146405 Merge main 2026-10-15 21:07:04 -05:00
Chris Tate 293370e2e7 Merge main 2026-10-15 21:06:45 -05:00
Chris Tate a3b6b05a4c Merge main 2026-10-15 20:42:11 -05:00
Chris Tate feeba0a166 Add assert.AssertionError with Node's properties and constructor (#1022) 2026-10-15 20:18:29 -05:00
Chris Tate b00d65b8be Merge main 2026-10-15 19:57:06 -05:00
Chris Tate eb90bf7eee Merge main 2026-10-15 19:50:35 -05:00
Chris Tate f6dc19ad98 Merge main 2026-10-15 19:13:52 -05:00
Chris Tate f0151d8c17 Merge main 2026-10-15 19:06:52 -05:00
Chris Tate ba3fcfb7f9 Add async generator stages to pipeline
pipeline() accepts a run of async generator function stages between
streams, following pipeline.js: each stage is called during setup with the
previous stream (a Readable is async iterable) or the previous stage's
generator, and the last generator is pumped into the next stream with
pumpToNode's write, drain and end waits. An error in the pump loop returns
the generator before the pipeline finishes, so the stage's finally runs.

A stage's source parameter is typed as what the pipeline passes it and may
only head for await loops. Function sources and destinations, stages that
take the signal argument, and stages separated by streams are refused.
2026-10-15 18:46:41 -05:00
Chris Tate 05dabea9a6 Merge main 2026-10-15 18:31:17 -05:00