Skip to content

Conversation

@Renegade334
Copy link
Member

@Renegade334 Renegade334 commented Nov 30, 2025

readable.compose() was intended to return the Duplex constructed by stream.compose(), and is documented as such.

However, because it was added as a "stream-returning operator", its output is being passed via Readable.from(), which constructs a new object-mode Readable by consuming the async iterator of the composed Duplex. This is inefficient in the best case, but causes breakage in a load of others:

  • if the destination stream is writable-only, then the composed stream would be a non-readable Duplex, but would be returned as a readable Readable.
  • if the source stream is a readable/writable Duplex, then the composed stream would be returned as a non-writable Readable, which misses the point of stream composition – the composed stream should be able to write to the head of the pipeline if it's writable, and read from the end if it's readable.
  • the returned stream is always in object mode, clobbering the object mode detection of stream.compose().

This change gets rid of the "operator" semantics for readable.compose(), and makes it a standalone method which returns the unaltered composed Duplex stream from stream.compose().

Fixes: #55203

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/streams

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. stream Issues and PRs related to the stream subsystem. labels Nov 30, 2025
-->

* `stream` {Stream|Iterable|AsyncIterable|Function}
* `stream` {Writable|Duplex|WritableStream|TransformStream|Function}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no behaviour change here. Technically iterables are valid as "transformers" in stream.compose(), but since they ignore the source stream output entirely, it seems pointless to be documenting them here.

@codecov
Copy link

codecov bot commented Nov 30, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.54%. Comparing base (768f3ba) to head (47933e0).
⚠️ Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #60907      +/-   ##
==========================================
- Coverage   88.56%   88.54%   -0.02%     
==========================================
  Files         703      703              
  Lines      208291   208287       -4     
  Branches    40169    40165       -4     
==========================================
- Hits       184472   184430      -42     
- Misses      15831    15869      +38     
  Partials     7988     7988              
Files with missing lines Coverage Δ
lib/internal/streams/operators.js 96.14% <ø> (-0.30%) ⬇️
lib/internal/streams/readable.js 96.26% <100.00%> (+0.05%) ⬆️

... and 45 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. stream Issues and PRs related to the stream subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

stream: readable.compose fails to return a Duplex

2 participants