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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Readable.from accepts sync and async generators of strings and Buffers,
following from.js: values are pushed while push() accepts, an async
generator's next() promise is awaited once per value, and destroy runs
the generator's throw() and return() before the destroy callback.
for await over a stream follows createAsyncIterator: the loop registers
its 'readable' listener, reads through read(), and a parked loop wakes
through the 'readable' tick. push(null) before the first read ends the
stream after the next read, finished() answers an already settled stream
with its result on a nextTick, read(0) on an object-mode stream reads
nothing, and write(string, 'buffer') throws ERR_UNKNOWN_ENCODING.
- getProtocol() answers the negotiated version ("TLSv1.2" or
"TLSv1.3"), the highest version on offer before the handshake as
OpenSSL does, and null after close.
- getCipher() answers { name, standardName, version }: OpenSSL's name
for the negotiated suite, its IANA name, and the suite's minimum
protocol version. A suite without a known OpenSSL name throws the
"not supported yet" error.
- alpnProtocol is null before the handshake, then the negotiated
protocol or false.
- tls.DEFAULT_MIN_VERSION and DEFAULT_MAX_VERSION are 'TLSv1.2' and
'TLSv1.3', and the bundled declarations type the server's
secureConnection socket as a TLSSocket.
- The limitations page notes that a typed getCipher() read before the
handshake or after close throws where Node answers undefined or null.
Corpus 5651 covers each case.
- socket.remotePort, remoteFamily, localAddress, localPort, localFamily,
bytesRead, readyState, connecting and pending. The peer's fields are
cached at first read as remoteAddress is (undefined while
connecting), and the local ones are undefined while connecting and
after close.
- write(data, cb) runs cb with null once the data is flushed, and
end(cb) / end(data, cb) once the socket finishes.
- 'close' listeners receive hadError, 'ready' fires after 'connect',
and 'data' listeners may take the string chunks setEncoding gives.
- setTimeout(ms, cb) adds cb as a once('timeout') listener (0 removes
it), and setTimeout throws ERR_OUT_OF_RANGE for a negative, NaN or
infinite msecs where it disabled the timer before. setKeepAlive
chains.
- server.maxConnections closes connections past the cap and emits
'drop' with their addresses; server.getConnections(cb) answers the
count on the next tick.
- net.isIP, isIPv4 and isIPv6 follow Node's address patterns.
- A callback adapted to a void return keeps the identity of the
function it wraps, so removing it by that function finds it.
Corpus 5644-5647 cover each case.