Commit 3407386503e for nodejs

commit 3407386503e54ed20287b995daa6dd84a626e8d1
Author: James M Snell <jasnell@gmail.com>
Date:   Thu Sep 17 06:18:26 2026 +0000

    stream: update ShareImpl to retain factory abort listener

    Signed-off-by: James M Snell <jasnell@gmail.com>
    Assisted-by: Opencode
    PR-URL: https://github.com/nodejs/node/pull/66030
    Reviewed-By: Filip Skokan <panva.ip@gmail.com>
    Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>

diff --git a/lib/internal/streams/iter/share.js b/lib/internal/streams/iter/share.js
index 943d83ca755..89170bdabd2 100644
--- a/lib/internal/streams/iter/share.js
+++ b/lib/internal/streams/iter/share.js
@@ -432,7 +432,9 @@ class ShareImpl {
         this.#sourceError = error;
         this.#sourceExhausted = true;
       } finally {
-        if (this.#sourceExhausted) this.#cleanupFactorySignal();
+        if (this.#sourceExhausted && this.#consumers.size === 0) {
+          this.#cleanupFactorySignal();
+        }
         this.#pulling = false;
         for (let i = 0; i < this.#pullWaiters.length; i++) {
           this.#pullWaiters[i]();
@@ -496,7 +498,11 @@ class ShareImpl {

   #deleteConsumer(consumer) {
     if (this.#consumers.delete(consumer)) {
-      return this.#deleteConsumerFromMin(consumer);
+      const shouldTrimBuffer = this.#deleteConsumerFromMin(consumer);
+      if (this.#sourceExhausted && this.#consumers.size === 0) {
+        this.#cleanupFactorySignal();
+      }
+      return shouldTrimBuffer;
     }
     return false;
   }
diff --git a/test/parallel/test-stream-iter-factory-signal.js b/test/parallel/test-stream-iter-factory-signal.js
index 7d0ac92b125..4123115b2fd 100644
--- a/test/parallel/test-stream-iter-factory-signal.js
+++ b/test/parallel/test-stream-iter-factory-signal.js
@@ -62,6 +62,22 @@ async function testShareSignalLifetime() {
   assert.strictEqual(abortListenerCount(controller.signal), 0);
 }

+async function testShareSignalLifetimeWithSlowConsumer() {
+  const controller = new AbortController();
+  const reason = new Error('aborted');
+  const shared = share('x', { signal: controller.signal });
+  const fast = shared.pull()[Symbol.asyncIterator]();
+  const slow = shared.pull()[Symbol.asyncIterator]();
+
+  assert.strictEqual((await fast.next()).done, false);
+  assert.strictEqual((await fast.next()).done, true);
+  assert.strictEqual(abortListenerCount(controller.signal), 1);
+
+  controller.abort(reason);
+  await assert.rejects(slow.next(), (error) => error === reason);
+  assert.strictEqual(abortListenerCount(controller.signal), 0);
+}
+
 async function testDuplexSignalLifetime() {
   const controller = new AbortController();
   const [channelA, channelB] = duplex({ signal: controller.signal });
@@ -80,5 +96,6 @@ Promise.all([
   testPushSignalLifetime(),
   testBroadcastSignalLifetime(),
   testShareSignalLifetime(),
+  testShareSignalLifetimeWithSlowConsumer(),
   testDuplexSignalLifetime(),
 ]).then(common.mustCall());