Commit Graph
13232 Commits
Author SHA1 Message Date
Philipp Dunkel df2ea49e00 zlib: add crc32c
Add zlib.crc32c(data[, value]), which computes the CRC-32C checksum
(Castagnoli polynomial, RFC 3720 section 12.1). CRC-32C has better
error detection properties than the CRC-32 of zlib.crc32() and is the
checksum required by many current protocols, formats and services,
among them:

- iSCSI header and data digests (RFC 7143)
- SCTP packet checksums (RFC 9260)
- iWARP MPA framing (RFC 5044)
- NVMe/TCP header and data digests
- ext4, Btrfs and XFS metadata checksums
- the Snappy framing format
- Apache Kafka record batches
- LevelDB and RocksDB block checksums
- TensorFlow TFRecord files
- Google Cloud Storage and Amazon S3 object checksums

Implementing any of these in JavaScript currently needs a userland
CRC-32C, which is far slower than native code.

The function mirrors zlib.crc32(): it accepts a string (encoded as
UTF-8) or an ArrayBufferView, takes an optional 32-bit unsigned
starting value so a checksum can be continued across chunks, and has a
V8 fast API path.

The checksum is computed by absl::ExtendCrc32c() from the bundled
Abseil, which is already built and linked into node, so this adds no
new dependency and no new checksum implementation. absl_crc32c is
added to the libraries linked with --shared-abseil.

Abseil selects its hardware-accelerated CRC-32C path (SSE4.2 and
PCLMULQDQ on x64, the CRC32 and PMULL extensions on arm64) at compile
time, so with node's default compiler flags the portable table-driven
implementation is used. It runs at about the same speed as zlib.crc32()
(about 6.5 GB/s on a recent x64 machine), while the hardware path
reaches about 38 GB/s. Builds whose target CPU baseline already
includes those instructions get the hardware path without changes.
Enabling it for generic builds is left out on purpose: on x64, Abseil
uses the accelerated code for unrecognized CPUs without a runtime
feature check, so compiling it in unconditionally would fault on CPUs
without SSE4.2.

Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/66828
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-20 12:33:08 +00:00
Donghoon Kang 531d28fc36 util: fix inspecting URL subclass prototypes
Check the URL private field brand before accessing href to avoid
throwing when inspecting URL subclass prototypes.

Assisted-by: Codex:6.1SolHigh
Signed-off-by: HoonDongKang <d159123@naver.com>
PR-URL: https://github.com/nodejs/node/pull/66822
Fixes: https://github.com/nodejs/node/issues/66821
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Matthew Aitken <maitken033380023@gmail.com>
2026-10-20 04:54:54 +00:00
Efe Karasakal 107133ceb8 quic: allow null body when opening streams
Signed-off-by: Efe Karasakal <hi@efe.dev>
PR-URL: https://github.com/nodejs/node/pull/66690
Reviewed-By: Tim Perry <pimterry@gmail.com>
2026-10-19 21:16:23 +00:00
Guy Bedford 8ae0ad0683 module: add "worker" condition for Web Worker threads
Adds a "worker" package condition that is applied within Web Worker
threads created via the `Worker` global, for the worker entry point,
its dependencies, `import()`, `require()` and `import.meta.resolve()`.
Threads created via `worker_threads`, including from within a Web
Worker, do not apply the condition. Other conditions are unaffected.

Assisted-by: OpenCode
Signed-off-by: Guy Bedford <guybedford@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66635
Reviewed-By: James M Snell <jasnell@gmail.com>
2026-10-19 17:09:42 +00:00
Marco Ippolito 2052d0dcdf module: add --experimental-strip-private-modules
Signed-off-by: Marco Ippolito <marcoippolito54@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/63869
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com>
2026-10-19 13:03:51 +00:00
Tim Perry 135b70e670 tls: add tlsSocket.socket, to access the raw stream beneath TLS
Signed-off-by: Tim Perry <pimterry@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66598
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
2026-10-19 12:35:59 +00:00
Efe Karasakal 6678d6f71d quic: reuse header support assertion
Signed-off-by: Efe Karasakal <hi@efe.dev>
PR-URL: https://github.com/nodejs/node/pull/66777
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Tim Perry <pimterry@gmail.com>
2026-10-19 12:35:33 +00:00
Marco 1a885caa6e fs: add mkstemp()
Add fs.mkstemp(), fs.mkstempSync() and fsPromises.mkstemp(), which
create and open a unique temporary file in one operation. They expose
uv_fs_mkstemp(), the file counterpart of the function behind
fs.mkdtemp().

The callback and sync versions return the path and a file descriptor,
the promise version returns the path and a FileHandle.

libuv clears the path of the request when mkstemp() fails, so the
template is saved in the request and used for the path of the error.

The functions are also supported on mounted virtual file systems.

Refs: https://github.com/nodejs/node/pull/33549
Refs: https://github.com/nodejs/node/issues/33890
Refs: https://github.com/nodejs/node/issues/5332
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66484
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-19 02:34:09 +00:00
Harjoth Khara 06550c777f vfs: enforce readonly state on open handles
Signed-off-by: harjoth <harjoth.khara@gmail.com>
Fixes: https://github.com/nodejs/node/issues/64401
Assisted-by: Codex
PR-URL: https://github.com/nodejs/node/pull/64403
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2026-10-19 01:41:35 +00:00
Matteo Collina 9a9f7e87b8 process: return unregister fn from finalization
process.finalization.register() and registerBeforeExit() now return
a function that removes only that registration, leaving other
registrations for the same object in place. The function does not
hold a strong reference to the object.

Each registration now uses its WeakRef as the unregister token, which
also makes process.finalization.unregister() remove the entries from
the FinalizationRegistry; previously it passed the object as the
token, which was never registered, so the call had no effect.

Refs: https://github.com/nodejs/node/pull/66482

Signed-off-by: Matteo Collina <matteo.collina@gmail.com>
Assisted-by: pi
PR-URL: https://github.com/nodejs/node/pull/66482
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
2026-10-18 06:36:43 +00:00
Efe Karasakal f88a1d25e9 quic: validate stream error codes
Assisted-by: Codex
Signed-off-by: Efe Karasakal <hi@efe.dev>
PR-URL: https://github.com/nodejs/node/pull/66650
Reviewed-By: Tim Perry <pimterry@gmail.com>
2026-10-17 21:42:53 +00:00
Matteo Collina c526694ae9 module: fix require(esm) on the module.register() hooks thread
On the async loader hooks thread, resolve kImportInRequiredESM and
kRequireInImportedCJS requests synchronously again, and always use
ModuleJobSync for kImportInRequiredESM. Otherwise require() of an ES
module with imports from a hooks module hit an internal assertion in
ModuleJobSync.syncLink().

Fixes: https://github.com/nodejs/node/issues/66767
Refs: https://github.com/nodejs/node/pull/62530
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66770
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
2026-10-17 16:26:39 +00:00
Matteo Collina 269cab6320 net: fail closed when BlockList peer address is unavailable
When a net.Server is configured with a `blockList`, the peer address is
looked up with getpeername() and checked before the connection is
accepted. If the peer address cannot be obtained (for example, the
connection was reset before getpeername(), causing
uv_tcp_getpeername() to return ENOTCONN), the access-control check was
silently skipped and the connection proceeded, allowing a blocked peer
to reach the application's 'connection' handler (fail open).

Fail closed: when a BlockList is configured but the peer address cannot
be determined, close the connection instead of bypassing the check. A
connection whose peer address cannot be obtained cannot be proven to be
allowed, so it must be refused.

Ref: https://github.com/nodejs/node/issues/66422
Ref: https://github.com/libuv/libuv/pull/5337

Signed-off-by: Matteo Collina <hello@matteocollina.com>
Assisted-by: pi
PR-URL: https://github.com/nodejs/node/pull/66640
Refs: https://github.com/nodejs/node/issues/66422
Refs: https://github.com/libuv/libuv/pull/5337
Reviewed-By: Tim Perry <pimterry@gmail.com>
2026-10-17 09:45:08 +00:00
Matteo Collina 4e75b714df stream: trim TransformStream construction costs
Constructing a TransformStream allocated five closures for the sink and
source algorithms of its two sides, and each side adopted the start
promise through a wrapper promise plus a thenable job, so a stream
without a start() left about three kilobytes pending in the microtask
queue until the started steps ran. Per-stream creation bursts spend
most of their time copying that graph through the scavenger.

The sink and source algorithms are now shared functions that reach the
transform stream through a field on their controller state (the
controller is passed to the close, abort and cancel algorithms for
that), and the post-start steps of both sides are delivered by one
reaction chain on a shared promise, taking the same microtask hops as
the spec's start promise adoption: three after construction for a
non-thenable start result, two after the adopting promise settles for a
thenable one. The readable and writable transfer state records are
materialized on first transfer instead of per stream.

Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66643
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Mattias Buelens <mattias@buelens.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2026-10-17 06:20:05 +00:00
Rafael Gonzaga 62ca1bae5a vfs: allow mounting in permission audit mode
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com>
PR-URL: https://github.com/nodejs/node/pull/66745
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-17 00:53:49 +00:00
Guilherme Araújo f513ec7e7c test_runner: add mockReturnValue and promise helpers
Add mockReturnValue(), mockResolvedValue() and mockRejectedValue()
to MockFunctionContext as shorthands for common mockImplementation()
patterns.

Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66631
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
2026-10-16 18:06:44 +00:00
Roberto Bianchi f59539b514 url: skip plain prefixes in percent-escape checks
decodeFormComponent() already finds the first percent character, but
hasPercentHex() scans the plain prefix again. Start the check at the
known position to avoid repeating that work.

Replacing plus signs with spaces preserves character positions, so
escape validation and decoding behavior remain unchanged.

Signed-off-by: Roberto Bianchi <roberto.bianchi@spendesk.com>
PR-URL: https://github.com/nodejs/node/pull/66696
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-16 16:59:40 +00:00
Philipp Dunkel 9f2fb935fd sea: mount vfsArchive at layer 0 and in workers
A single executable application built with "vfsArchive" mounted its
archive at the next free layer, like any file system a program mounts
itself, and only in the main thread. A worker had no mount at all, so
a path into the archive, such as the main script's __filename, could
not be loaded there.

Mount the archive at the layer reserved for --vfs-load instead, so it
is at the same mount point in every thread, and mount it in each
worker as well, with the main script placed at its root just as in
the main thread. The worker reads the main script through a new
getMainCode() binding, since no main script is handed to it. Unlike
the --vfs-load source, the archive needs no provider that a preload
could register, so its mount in a worker is not deferred past
--import.

Since the archive takes that mount point, reject --vfs-load alongside
it: --build-sea refuses it in "execArgv", and the executable refuses it
at startup when it comes from --node-options or from the execArgv of a
worker.

Also fix the example in vfs.md, which showed a file system a program
mounts itself at layer 0, the layer reserved for these mounts.

Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/66709
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-16 12:44:35 +00:00
James M Snell 153ec89621 stream: make stream/iter from() cancellation waits cheaper
To stay cancellable while a source is pending, from() waits for every
value of an async source through waitForNormalization(). For each value
it created a PromiseWithResolvers(), raced it against the value with
SafePromiseRace(), which wraps both in new promises, and ran an async
function with try/finally. This was the largest per-batch cost of
normalizing an async source.

Wait with a single promise and a single reaction on the value instead,
and reject that promise directly on cancellation. The outcome is
unchanged: the value's result, its rejection, or the cancellation
reason, whichever comes first, and the cancellation reason if the
normalization was cancelled by the time the value fulfills.

With an async generator yielding 16-byte chunks, pipeTo() is about
1.7x faster and iterating from() about 1.75x faster.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:52 +00:00
James M Snell 818f34833a stream: share the once option for stream/iter abort listeners
stream/iter registered its one-time 'abort' listeners with a new
`{ __proto__: null, once: true }` options object every time, which can
be once per chunk (abortableNext()) or per waiting write. Use a single
shared kNullOnceOption instead. It is frozen, because signals can come
from user code and a patched addEventListener() must not be able to
change the options for every later registration.

The difference is small, since the listener registration itself costs
much more: with 300,000 16-byte chunks, pull() with a signal is about
1.7% faster, push() writes that wait with a signal about 3% faster, and
pipeTo() and bytes() with a signal are unchanged.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:51 +00:00
James M Snell 17e7b68996 stream: do not miss 'drain' in fromWritable()
When a write filled the classic Writable, fromWritable() recorded that
it needed to drain, but only listened for 'drain' once a later write
was queued or something waited for drain. If the Writable emitted
'drain' before that, for example because its write callback ran on a
microtask or with process.nextTick(), the event was missed and the flag
was never cleared: the next write() or writev() never settled, canWrite
stayed false and ondrain() never resolved.

Listen for 'drain' as soon as a write returns false, and keep listening
until it is emitted.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:50 +00:00
James M Snell a7c156fd37 stream: construct the options passed to stream/iter transforms
pull() passes every stateless transform call a new
`{ __proto__: null, signal }` options object, which V8 creates in
dictionary mode, once per batch and transform.

Create the options with a TransformOptions constructor instead, for
stateful transforms as well so that both forms receive the same kind of
object. Each call still gets its own object, as the pipeline requires.
Its prototype is a single frozen object with no %Object.prototype% in
its chain, so a transform cannot pass state to other transforms through
it, and with a non-enumerable `constructor` so that util.inspect()
prints `TransformOptions { signal }`.

With 300,000 single-chunk batches, pull() is about 2% faster with one
stateless transform and about 8% faster with four.

This is observable: the options object's prototype is no longer null.
A new test covers the options contract, and the documentation now
describes it, including that pullSync() passes transforms no options.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:49 +00:00
James M Snell 13d0167013 stream: construct stream/iter wait and merge records
The records queued when a stream/iter read, write or drain has to wait,
and merge()'s ready-queue entries, were `{ __proto__: null, ... }`
literals, which V8 creates in dictionary mode. They can be created once
per chunk: whenever the consumer is ahead of the producer, every read
waits, and with a full budget every write does.

Create them with constructors whose prototype is an empty, frozen,
null-prototype object: PendingRequest and PendingWrite (push(),
broadcast() and fromWritable()), QueuedWrite (fromWritable()) and
MergeEntry (merge()). fromWritable() drain waiters now settle through
resolve(false) instead of a per-waiter close() closure. merge() tells
error entries apart by their missing iterator rather than by a kind
string.

When every push() read waits for data, or every write waits behind a
full budget, reading or writing 16-byte chunks is about 30% faster.
fromWritable() with queued writes is about 18% faster, and merge() of
two sources about 9% faster.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:48 +00:00
James M Snell e6c9c8eecd stream: construct stream/iter byte view snapshots and batch entries
createBatchEntry() and recordChunk() snapshot every chunk in a
seven-property `{ __proto__: null, ... }` literal, and every batch gets
a `{ __proto__: null, views, byteLength }` record. V8 creates such
literals in dictionary mode, which is expensive for objects created
for every chunk.

Create them with constructors whose prototype is an empty, frozen,
null-prototype object instead, as for IterResult. They have fast
properties and a single shape, and are only used internally.

Writing 1e6 16-byte chunks into a push() stream and reading them back
is about 6x faster with writeSync(), 3.7x faster with writevSync() (4
chunks per call) and 1.7x faster with string chunks.
benchmark/streams/iter-throughput-share-sync.js improves by 67-93%,
iter-throughput-share.js by 4-8%, and iter-throughput-broadcast.js
with 4 consumers by 8%. pipeTo() with small chunks is about 1.5x
faster.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:47 +00:00
James M Snell 724190cfdc stream: avoid per-write allocations in stream/iter writers
Every write() and writeSync() allocated a `{ __proto__: null, context }`
options object for the WebIDL chunk conversion, and for a Uint8Array
chunk a second, six-property one with [AllowShared] and
[AllowResizable]. Both are created in V8 dictionary mode.

Return Uint8Array chunks directly from the WriterChunk converter: with
[AllowShared] and [AllowResizable], the Uint8Array conversion cannot
reject a value isUint8Array() accepts and returns the same object. Use
shared, frozen conversion contexts for chunks, chunk sequences and
write options; the converters only read them to build error messages.

Writing 1e6 16-byte Uint8Array chunks into a push() stream and reading
them back is about 27% faster with writeSync() and with writevSync()
(4 chunks per call). String chunks are unaffected. Behavior and error
messages are unchanged.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:46 +00:00
James M Snell 778306c001 stream: create stream/iter iterator results with a constructor
The iterators of push(), share(), shareSync(), broadcast() consumers
and from() normalization created a `{ __proto__: null, done, value }`
literal for every result. V8 creates such literals in dictionary mode,
which makes them several times more expensive to create and read than
ordinary objects.

Create them with an IterResult constructor whose prototype is a single
frozen, null-prototype object instead. Results have fast properties
and a single shape, and still have no %Object.prototype% in their
prototype chain, so a polluted Object.prototype.then still cannot turn
a result into a thenable. Creating and reading a result is about 5x
faster in a microbenchmark.

benchmark/streams/iter-throughput-share-sync.js improves by 7% to 42%
(more with more consumers) and iter-throughput-share.js by 4-6%;
pipeTo() with small chunks is about 2.5% faster.

This is observable: results are no longer null-prototype objects, so
deepStrictEqual() comparisons against `{ __proto__: null, ... }` no
longer match, and util.inspect() prints them as
`IterResult { done, value }` (the prototype has a non-enumerable
`constructor` for that purpose). The five tests that compared results
that way now compare their own properties, and a new test covers the
result contract and the prototype pollution case.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:46 +00:00
James M Snell 5bcade91de stream: make endSync() optional in pipeToSync()
pipeToSync() threw ERR_INVALID_ARG_TYPE before writing anything when
the writer had no endSync() method and preventClose was not set.
endSync() is optional: the spec (pipeToSync() step 7) only calls it if
the writer has it, and pipeTo() already treats it that way. A writer
without endSync() now receives the data and is not closed. pipeToSync()
still never falls back to the async end().

This also fixes the from-sync-writev case of
benchmark/streams/iter-from-batching.js, whose writer has no endSync().

testPipeToSyncNoEndSync asserted the previous rejection and now checks
that the data is written and end() is not called. The documentation of
the writer requirements is corrected as well: only writeSync() is
required.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:45 +00:00
James M Snell 82563d8c39 stream: keep long-lived stream/iter objects in fast mode
Object literals with `__proto__: null` are created in V8 dictionary
mode. Create the objects that live as long as a stream and are used for
every chunk with ObjectSetPrototypeOf() instead, as was done for the
share and broadcast consumer state, so that they keep fast properties:

- the iterators returned by push(), pull(), share(), shareSync() and
  broadcast() consumers, and the pull() consumer-cleanup wrapper,
- the iterators and the cancellation context used by from()
  normalization,
- the async wrapper share() uses for sync sources.

Objects created per call or per chunk (iterator results, options bags,
promise resolver records, single-use iterables) keep the literal form:
for those, setting the prototype after creation costs more than it
saves, about 2x slower in a create-and-read microbenchmark. The
fromWritable() writer is also unchanged, since V8 keeps object literals
with accessors in dictionary mode regardless.

With 200,000 16-byte chunks, pipeTo() is about 3.5% faster and pull()
with a transform or a signal about 1-1.5% faster. In
benchmark/streams/iter-throughput-share*.js, share() and shareSync()
improve by 1-4.5%; no benchmark regressed significantly.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66648
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-15 13:26:44 +00:00
Jiawen Geng 38aa1f3881 crypto: avoid reusing randomInt() cache bytes
When an asynchronous randomInt() call finds the cache empty, it queues
itself and starts randomFill() on the shared cache buffer. A synchronous
call made before that job completes refills the same buffer with
randomFillSync() and returns values from offset 0. The completion
callback then reset the offset to 0, so the queued call and later calls
returned the bytes that synchronous calls had already used. The
threadpool job and randomFillSync() could also write the buffer at the
same time.

Fill a separate buffer asynchronously and copy it into the cache once
the job is done.

Fixes: https://github.com/nodejs/node/issues/66595
Refs: https://github.com/nodejs/node/pull/35110
Signed-off-by: Jiawen Geng <technicalcute@gmail.com>
Assisted-by: claude:opus-5.5
PR-URL: https://github.com/nodejs/node/pull/66597
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
2026-10-15 09:46:38 +00:00
Matteo Collina 49ad95045a vfs: unflag
Enable node:vfs by default. --no-experimental-vfs disables node:vfs
exposure and --vfs-load.

Assisted-by: pi
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66318
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
2026-10-15 07:53:03 +00:00
Sebastian Beltran 12ce81920a lib: deprecate _http_*
Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/58535
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Aviv Keller <me@aviv.sh>
2026-10-15 00:52:34 +00:00
Roberto Bianchi dd7235cb6a url: reduce search params sort buffering
URLSearchParams.sort() copies both merge runs into temporary arrays.
Buffer only the left run and read the right run from the output array,
removing one scratch-array allocation and the right-run copying.

While left-side elements remain, the output cursor stays behind unread
right-side elements. Once the left run is exhausted, the right remainder
is already in place. Taking the left key on equality preserves
stability. The insertion-sort path below 50 pairs is unchanged.

Signed-off-by: Roberto Bianchi <roberto.bianchi@spendesk.com>
PR-URL: https://github.com/nodejs/node/pull/66665
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Ilyas Shabi <ilyasshabi94@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-14 08:51:58 +00:00
Antoine du Hamel 3f0c2e4c8b Revert "sea: mount vfsArchive at layer 0 and in workers"
This reverts commit ac9d85c418.

PR-URL: https://github.com/nodejs/node/pull/66706
Fixes: https://github.com/nodejs/node/issues/66703
Reviewed-By: Richard Lau <richard.lau@ibm.com>
Reviewed-By: Stewart X Addison <sxa@redhat.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
2026-10-13 15:48:49 +02:00
Matteo Collina 29bd7402f8 stream: skip idle webstreams start and watchers
A source without pull() has nothing observable left to do in its
post-start step, since the started flag only gates calls into the pull
algorithm. Set the flag right away instead of from a microtask, so a
push-style ReadableStream allocates neither the closure nor the task
and is not kept alive until the next microtask checkpoint.

pipeTo and tee hold the only references to their reader and writer, so
their [[closedPromise]] records are never observed as promises. Install
the watchers as the records themselves, as pipeTo's ready hook already
does, instead of materializing a promise plus reaction per side, and
hand the erroring/release probes one shared pending promise. The tee's
cancel promise is likewise materialized by the first branch cancel.

Microtask ordering is unchanged: each hook enqueues its watcher at the
position the promise reaction would have had.

node benchmark/compare.js --runs 20 over benchmark/webstreams (46 rows,
all others within the confidence interval):

webstreams/creation.js kind='ReadableStream'               ***  +172.40%
webstreams/creation.js kind='ReadableStream.tee'           ***   +20.47%
webstreams/creation.js kind='ReadableStreamBYOBReader'     ***   +15.66%
webstreams/creation.js kind='ReadableStreamDefaultReader'  ***   +14.53%
webstreams/lifecycle.js kind='pipe-to' (40 runs)           **    +10.98%

Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66424
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: James M Snell <jasnell@gmail.com>
2026-10-13 10:10:59 +02:00
Philipp Dunkel ac9d85c418 sea: mount vfsArchive at layer 0 and in workers
A single executable application built with "vfsArchive" mounted its
archive at the next free layer, like any file system a program mounts
itself, and only in the main thread. A worker had no mount at all, so
a path into the archive, such as the main script's __filename, could
not be loaded there.

Mount the archive at the layer reserved for --vfs-load instead, so it
is at the same mount point in every thread, and mount it in each
worker as well, with the main script placed at its root just as in
the main thread. The worker reads the main script through a new
getMainCode() binding, since no main script is handed to it. Unlike
the --vfs-load source, the archive needs no provider that a preload
could register, so its mount in a worker is not deferred past
--import.

Since the archive takes that mount point, reject --vfs-load alongside
it: --build-sea refuses it in "execArgv", and the executable refuses it
at startup when it comes from --node-options or from the execArgv of a
worker.

Also fix the example in vfs.md, which showed a file system a program
mounts itself at layer 0, the layer reserved for these mounts.

Signed-off-by: Philipp Dunkel <pip@pipobscure.com>
PR-URL: https://github.com/nodejs/node/pull/66614
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
2026-10-13 10:09:55 +02:00
Ved Chaudhari 8cf60c5db1 querystring: fix value decoding after encoded keys
In the non-default parse() path, a key with more than one percent
escape could corrupt non-ASCII characters in its value.

Keep key characters out of the value's encoding check.

Assisted-by: claude:opus-5.5
Signed-off-by: vedchaudhari <vedc2853@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66553
Refs: https://github.com/nodejs/node/pull/66175
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2026-10-13 07:48:55 +00:00
Roberto Bianchi e2bc15692e http: reduce raw header name allocations
Reuse the values array instead of allocating a second result array.

Signed-off-by: Roberto Bianchi <roberto.bianchi@spendesk.com>
PR-URL: https://github.com/nodejs/node/pull/66488
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Tim Perry <pimterry@gmail.com>
2026-10-12 09:13:52 +00:00
Darius 1d3b65d101 lib: fix AbortSignal.any() nested composite leak
AbortSignal.any() copies the source WeakRefs of a composite input into
the new composite, so nested composites share them. Those WeakRefs were
also used as the unregister tokens for the registrations that prune a
composite's sources once they are collected. When an inner composite
was collected first, its cleanup therefore also unregistered the outer
composite from the same sources. An observed outer composite then
stayed in gcPersistentSignals, together with its listeners, even after
all of its sources were gone and nothing could abort it anymore.

Use each composite's own WeakRef as the unregister token instead, so
collecting one composite only removes its own registrations.

Fixes: https://github.com/nodejs/node/issues/66612
Refs: https://github.com/nodejs/node/pull/62367
Assisted-by: Claude Code
Signed-off-by: journaltraces <kxenk@protonmail.com>
PR-URL: https://github.com/nodejs/node/pull/66617
Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
2026-10-12 06:41:16 +00:00
Lazizbek Ergashev 6b07f2fb5d module: fix require of imports with package map
Signed-off-by: lazerg <lazerg2@gmail.com>
Assisted-by: Claude Code
PR-URL: https://github.com/nodejs/node/pull/66565
Fixes: https://github.com/nodejs/node/issues/66562
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Jacob Smith <jacob@frende.me>
2026-10-11 16:10:53 +00:00
Matteo Collina cbac135e5a diagnostics_channel: fix default callback context
traceCallback() defaults to a frozen context but writes the callback's
result or error to it. Allocate a fresh context when undefined is passed
and subscribers are present, matching traceSync() and tracePromise().
Set the prototype after allocation to avoid dictionary-mode properties.

Assisted-by: pi
Signed-off-by: Matteo Collina <hello@matteocollina.com>
PR-URL: https://github.com/nodejs/node/pull/66481
Reviewed-By: Bryan English <bryan@bryanenglish.com>
2026-10-11 07:07:49 +00:00
James M Snell 04bd7378a2 stream: add pipeToSync() failOnIncompleteClose option
pipeToSync() does not fail the writer when endSync() returns -1, so
that a caller can still close it asynchronously. A caller that cannot,
e.g. because the writer is sync-only and has no end(), would be left
with a writer that is neither closed nor failed.

Add a failOnIncompleteClose option (a Node.js extension) that fails
the writer with the thrown ERR_INVALID_STATE error in that case.
preventFail takes precedence over it.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:39 +00:00
James M Snell 2a4624cd93 stream: do not fail the writer when pipeToSync() cannot close
After writing every chunk, pipeToSync() treated endSync() returning -1
like any other error: it threw ERR_INVALID_STATE and, unless
preventFail was set, called writer.fail() with it. -1 only means that
the writer cannot close synchronously, e.g. a push() writer whose
consumer has not drained it yet. All of the data had been accepted,
but failing the writer discarded it, so the consumer saw an error
instead of the end of the stream, and the caller could not recover.

pipeToSync() still throws ERR_INVALID_STATE in that case, since it
never falls back to the async end(), but it no longer fails the
writer. The caller can still close it, e.g. with `await writer.end()`.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:38 +00:00
James M Snell b5a14634e5 stream: fix fromSync() async input error messages
The ERR_INVALID_ARG_TYPE messages for async iterable and promise inputs
read "must be an a synchronous input (not AsyncIterable)", because the
error formatter adds "an" to expected-type strings that contain
uppercase letters. Rephrase them in lowercase.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:23 +00:00
James M Snell 1caad8efb4 quic: reject closed stream writer writes with a TypeError
The spec for stream/iter Writers (write(), step 2) requires writes to
a closed writer to reject with a TypeError, as the push(), broadcast()
and FileHandle writers do. The QUIC stream writer rejected write() and
writev() with a plain ERR_INVALID_STATE Error. Use its TypeError
variant; the error code is unchanged.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:22 +00:00
James M Snell b8090ca4f6 stream: reject closed fromWritable() writes with a TypeError
The spec (Writer write(), step 2) requires writes to a closed writer to
reject with a TypeError. The fromWritable() adapter rejected write()
and writev() with ERR_STREAM_WRITE_AFTER_END, which is a plain Error,
unlike the other stream/iter writers.

Add a TypeError variant of ERR_STREAM_WRITE_AFTER_END and use it, so
the error code is unchanged.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:21 +00:00
James M Snell 12e34ca588 stream: accept explicit stream/iter budgets below 16384
push(), duplex(), broadcast(), share() and shareSync() rejected an
explicit budget below 16384 bytes with ERR_OUT_OF_RANGE. The spec
(push() step 3, broadcast() step 1 and share() step 2) only requires
the implementation-defined default to be at least 16384 bytes; an
explicit budget is used as given. Small budgets are also useful in
tests and in memory-constrained code.

Accept any explicit budget of at least 1 byte, and validate it in one
place. Defaults are unchanged. The validation tests are updated to the
new lower bound; note that WebIDL conversion truncates fractions, so
1.5 is now a valid budget of 1 and 0.5 is used to exercise the
rejection instead.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:19 +00:00
James M Snell e8cdfd3c2b stream: reduce per-chunk overhead in stream/iter consumers
bytes(), text(), arrayBuffer(), array() and their sync variants kept a
snapshot object for every collected chunk, plus a batch entry and its
views array, so they can detect chunks that were resized or detached
before the result is assembled. For streams of many small chunks this
dominated memory use: collecting 1,000,000 one-byte chunks peaked at
about 490 MB of heap for 1 MB of data.

A non-empty view of a fixed-length, non-shared ArrayBuffer can only
change by its buffer being detached, which makes its byteLength 0, so
recording its byteLength is enough. Keep a full snapshot only for
empty views and views of resizable or shared buffers. The same input
now peaks at about 60 MB and is collected about five times faster.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:18 +00:00
James M Snell 0e77f91f26 stream: keep the original error when writer.fail() throws
When pipeTo() or pipeToSync() failed, they called writer.fail(error)
and then rethrew the error. If fail() itself threw, its exception
replaced the error that made the pipe fail, which was then lost.

Call fail() on a best-effort basis and always surface the original
error.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:17 +00:00
James M Snell ee7462aa37 stream: align pull() abort handling with the spec
pull() threw synchronously when given an already-aborted signal. The
spec (Iterable Streams, Stream.pull() step 5) requires it to return an
iterable that throws the abort reason when read. This also matches how
broadcast.push() and share.pull() already handle a pre-aborted signal.

Once the signal aborted, the pull that observed the abort rejected,
but later pulls resolved { done: true } because the pipeline is an
async generator, which completes after throwing. The spec (step 7)
requires future pulls to reject with the abort reason as well, so
that a stream that was cancelled is never reported as having ended
cleanly.

Return an iterator that rejects every read with the abort reason once
the signal has aborted the pipeline, without starting the pipeline if
the signal was already aborted. Pipelines without a signal are not
affected. The two tests that asserted the synchronous throw now check
the rejection instead, and the documentation is updated.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:15 +00:00
James M Snell 7c0a5179c4 stream: honor stream/iter protocols on function objects
The protocol lookup used by from(), fromSync(), and the
Broadcast/Share helpers only considered values with typeof 'object',
so a function implementing, e.g., Symbol.for('Stream.toStreamable') was
rejected with ERR_INVALID_ARG_TYPE. Functions are objects and the spec
does not exclude them; the iteration protocol checks already accept
them.

Assisted-by: OpenCode
Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66483
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-10-10 17:56:14 +00:00