Commit 818f34833af for nodejs

commit 818f34833afc32da4768374ab035c0d053ad1cd9
Author: James M Snell <jasnell@gmail.com>
Date:   Sun Oct 4 02:47:38 2026 +0000

    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>

diff --git a/lib/internal/streams/iter/broadcast.js b/lib/internal/streams/iter/broadcast.js
index e395b5e84f2..00ed899d653 100644
--- a/lib/internal/streams/iter/broadcast.js
+++ b/lib/internal/streams/iter/broadcast.js
@@ -61,6 +61,7 @@ const {
   PendingRequest,
   PendingWrite,
   kMultiConsumerDefaultBudget,
+  kNullOnceOption,
   kResolvedPromise,
   convertChunks,
   createBatchEntry,
@@ -99,7 +100,7 @@ function raceEndWithSignal(promise, signal) {

   const { promise: aborted, reject } = PromiseWithResolvers();
   const onAbort = () => reject(signal.reason);
-  signal.addEventListener('abort', onAbort, { __proto__: null, once: true });
+  signal.addEventListener('abort', onAbort, kNullOnceOption);
   if (signal.aborted) onAbort();

   return SafePromisePrototypeFinally(
@@ -873,7 +874,7 @@ function wireBroadcastWriteSignal(entry, signal, resolve, reject, self) {
     entry.batch = null;
     reject(reason);
   };
-  signal.addEventListener('abort', onAbort, { __proto__: null, once: true });
+  signal.addEventListener('abort', onAbort, kNullOnceOption);
 }

 // =============================================================================
diff --git a/lib/internal/streams/iter/classic.js b/lib/internal/streams/iter/classic.js
index 0eafba0182b..c1ed8ad51f5 100644
--- a/lib/internal/streams/iter/classic.js
+++ b/lib/internal/streams/iter/classic.js
@@ -63,6 +63,7 @@ const {

 const {
   PendingRequest,
+  kNullOnceOption,
   convertChunks,
   getWriterSignal,
   onSignalAbort,
@@ -103,10 +104,7 @@ function raceWithSignal(promise, signal) {
     reject,
   } = PromiseWithResolvers();
   const onAbort = () => reject(signal.reason);
-  signal.addEventListener('abort', onAbort, {
-    __proto__: null,
-    once: true,
-  });
+  signal.addEventListener('abort', onAbort, kNullOnceOption);
   PromisePrototypeThen(
     promise,
     (value) => {
diff --git a/lib/internal/streams/iter/consumers.js b/lib/internal/streams/iter/consumers.js
index 8678e8235ac..0751d2cc95f 100644
--- a/lib/internal/streams/iter/consumers.js
+++ b/lib/internal/streams/iter/consumers.js
@@ -54,6 +54,7 @@ const {
 } = require('internal/streams/iter/from');

 const {
+  kNullOnceOption,
   concatBytes,
   getProtocolMethod,
   recordChunk,
@@ -528,10 +529,7 @@ function merge(...args) {
             waitResolve = null;
           }
         };
-        signal.addEventListener('abort', onAbort, {
-          __proto__: null,
-          once: true,
-        });
+        signal.addEventListener('abort', onAbort, kNullOnceOption);
       }

       // Called when a source's .next() settles. Pushes the result into
diff --git a/lib/internal/streams/iter/duplex.js b/lib/internal/streams/iter/duplex.js
index 95a117d8141..ce5ccadd060 100644
--- a/lib/internal/streams/iter/duplex.js
+++ b/lib/internal/streams/iter/duplex.js
@@ -18,6 +18,7 @@ const {
 const {
   converters,
 } = require('internal/streams/iter/webidl');
+const { kNullOnceOption } = require('internal/streams/iter/utils');

 /**
  * Create a pair of connected duplex channels for bidirectional communication.
@@ -66,8 +67,7 @@ function duplex(options = { __proto__: null }) {
     if (signal.aborted) {
       abortBoth();
     } else {
-      signal.addEventListener('abort', abortBoth,
-                              { __proto__: null, once: true });
+      signal.addEventListener('abort', abortBoth, kNullOnceOption);
     }
   }

diff --git a/lib/internal/streams/iter/pull.js b/lib/internal/streams/iter/pull.js
index 35836dd27d6..fc8fa9902c1 100644
--- a/lib/internal/streams/iter/pull.js
+++ b/lib/internal/streams/iter/pull.js
@@ -53,6 +53,7 @@ const {

 const {
   IterResult,
+  kNullOnceOption,
   createBatchEntry,
   isTransformObject,
   parsePullArgs,
@@ -748,7 +749,7 @@ async function* createAsyncPipeline(source, transforms, signal) {
     abortHandler = () => {
       abortSignal(controller.signal, signal.reason);
     };
-    signal.addEventListener('abort', abortHandler, { __proto__: null, once: true });
+    signal.addEventListener('abort', abortHandler, kNullOnceOption);
   }

   let completed = false;
@@ -992,8 +993,7 @@ function pullWithConsumerCleanup(source, transforms, signal) {

   if (signal !== undefined) {
     abortHandler = () => closeSource('throw', signal.reason);
-    signal.addEventListener('abort', abortHandler,
-                            { __proto__: null, once: true });
+    signal.addEventListener('abort', abortHandler, kNullOnceOption);
     if (signal.aborted) abortHandler();
   }

diff --git a/lib/internal/streams/iter/push.js b/lib/internal/streams/iter/push.js
index b5db45a1a32..c99253af43e 100644
--- a/lib/internal/streams/iter/push.js
+++ b/lib/internal/streams/iter/push.js
@@ -32,6 +32,7 @@ const {
   IterResult,
   PendingRequest,
   PendingWrite,
+  kNullOnceOption,
   kPushDefaultBudget,
   kResolvedPromise,
   createBatchEntry,
@@ -72,10 +73,7 @@ function raceEndWithSignal(promise, signal) {
   } = PromiseWithResolvers();
   const onAbort = () => reject(signal.reason);

-  signal.addEventListener('abort', onAbort, {
-    __proto__: null,
-    once: true,
-  });
+  signal.addEventListener('abort', onAbort, kNullOnceOption);
   PromisePrototypeThen(
     promise,
     (value) => {
@@ -320,7 +318,7 @@ class PushQueue {
         reject(reason);
       };

-      signal.addEventListener('abort', onAbort, { __proto__: null, once: true });
+      signal.addEventListener('abort', onAbort, kNullOnceOption);
     }

     return promise;
diff --git a/lib/internal/streams/iter/transform.js b/lib/internal/streams/iter/transform.js
index 2cd06957b80..c6c315cf6bd 100644
--- a/lib/internal/streams/iter/transform.js
+++ b/lib/internal/streams/iter/transform.js
@@ -40,6 +40,7 @@ const {
 } = require('internal/errors');
 const { isArrayBufferView, isAnyArrayBuffer } = require('internal/util/types');
 const { kValidatedTransform } = require('internal/streams/iter/types');
+const { kNullOnceOption } = require('internal/streams/iter/utils');
 const {
   checkRangesOrGetDefault,
   kValidateObjectAllowArray,
@@ -413,7 +414,7 @@ function makeZlibTransform(createHandleFn, processFlag, finishFlag) {
           reject(signal.reason);
         }
       };
-      signal.addEventListener('abort', onAbort, { __proto__: null, once: true });
+      signal.addEventListener('abort', onAbort, kNullOnceOption);

       function continueInputAsync() {
         const { promise, resolve, reject } = PromiseWithResolvers();
diff --git a/lib/internal/streams/iter/utils.js b/lib/internal/streams/iter/utils.js
index 8b0c074b4e1..8c2835acd8b 100644
--- a/lib/internal/streams/iter/utils.js
+++ b/lib/internal/streams/iter/utils.js
@@ -47,6 +47,11 @@ const {
   kValidatedTransform,
 } = require('internal/streams/iter/types');

+// Shared `addEventListener()` options for one-time 'abort' listeners. Frozen
+// because signals can come from user code, and a patched addEventListener()
+// must not be able to change the options for every later registration.
+const kNullOnceOption = ObjectFreeze({ __proto__: null, once: true });
+
 // Cached resolved promise to avoid allocating a new one on every sync fast-path.
 const kResolvedPromise = PromiseResolve();

@@ -98,7 +103,7 @@ function onSignalAbort(signal, handler) {
   if (signal.aborted) {
     handler();
   } else {
-    signal.addEventListener('abort', handler, { __proto__: null, once: true });
+    signal.addEventListener('abort', handler, kNullOnceOption);
   }
 }

@@ -122,7 +127,7 @@ function abortableNext(iterator, signal) {
   const next = iterator.next();
   const { promise, reject } = PromiseWithResolvers();
   const onAbort = getOnAbort(reject, signal);
-  signal.addEventListener('abort', onAbort, { __proto__: null, once: true });
+  signal.addEventListener('abort', onAbort, kNullOnceOption);
   if (signal.aborted) {
     onAbort();
   }
@@ -594,6 +599,7 @@ module.exports = {
   PendingRequest,
   PendingWrite,
   kMultiConsumerDefaultBudget,
+  kNullOnceOption,
   kPushDefaultBudget,
   kResolvedPromise,
   concatBytes,