Skip to content

Test and cleanup of reading steps - #7750

Open
jasnell wants to merge 3 commits into
mainfrom
jasnell/ts-streams-duplicate-consolidaton-6
Open

jasnell wants to merge 3 commits into
mainfrom
jasnell/ts-streams-duplicate-consolidaton-6

Conversation

@jasnell

@jasnell jasnell commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

A ReadableStreamDrainingReader read that takes the last queued
chunk but stops before the close sentinel reports done: false and
leaves the stream readable, its desiredSize at the high-water mark
and its closed promise pending, until the next read reaches the
close. A default read closes the stream as it takes the last chunk.
Pin it ahead of reporting the close with the chunks.

The drain stops there for its maxSize, which only the
internal-testing global can pass, or where the chunk sizes add up
to Infinity, the one stop a read without a maxSize can meet: the
C++ bridge's pump and body consumption read that way. Pin both on a
value stream, and the maxSize case on a byte stream, whose
controller has its own close check. The reader exists only under
the TypeScript implementation; the C++ cells assert its absence, as
the rest of these files do.

A ReadableStreamDrainingReader read that takes the last queued
chunk but stops before the close sentinel reports done: false and
leaves the stream readable, its desiredSize at the high-water mark
and its closed promise pending, until the next read reaches the
close. A default read closes the stream as it takes the last chunk.
Pin it ahead of reporting the close with the chunks.

The drain stops there for its maxSize, which only the
internal-testing global can pass, or where the chunk sizes add up
to Infinity, the one stop a read without a maxSize can meet: the
C++ bridge's pump and body consumption read that way. Pin both on a
value stream, and the maxSize case on a byte stream, whose
controller has its own close check. The reader exists only under
the TypeScript implementation; the C++ cells assert its absence, as
the rest of these files do.
A ReadableStreamDrainingReader read whose size limit ran out at the
last chunk before the close sentinel stopped short of the sentinel
and reported done: false. The stream stayed readable, its closed
promise pending, until a second read found the close. A
controller.error() in between errored it, and the second read
rejected, although every chunk had been read. A default read closes
the stream as it takes the last chunk (spec PullSteps), and the
error is then a no-op.

The sentinel has no size, so the limit has no bearing on it.
QueueCursor.drain now reports done whenever it ends at the sentinel,
as the byte cursor's drain does through it, and the read closes the
stream in the same call, as a default read does.

The limit runs out at the read's maxSize, which only the
internal-testing global passes, or, for a read without one, where
the chunk sizes add up to Infinity. The C++ bridge's pump and body
consumption read without one, so in production only streams whose
chunk sizes add up to Infinity change: they finish in one read
instead of two.

Flip drainingReaderMaxSizeEndsAtClose,
drainingReaderSizeOverflowEndsAtClose and
drainingReaderByteMaxSizeEndsAtClose. typescript_implemented_streams
is experimental, so this needs no compatibility flag.
A default read that waits takes its completion steps, the
controller's drain-then-close check and then closing the stream if
the read is done, through readCompletionSteps. Answered from the
queue (readBufferedSync, which the pipe's synchronous read shares),
it repeated them inline, and the draining read repeated them too,
making the check only when done. Both now pull as before and then
call readCompletionSteps, which takes the done flag in place of the
result.

The default read and the draining read's wait-read each looked up
autoAllocateChunkSize behind their own queued-byte brand check and
cast. getByteControllerAutoAllocateChunkSize now takes any
controller and makes the check itself, answering undefined for all
but the queued byte controller.

No behavior change. The draining read now makes the check on every
completion. Since the previous commit it reports done whenever its
cursor ends at the close sentinel, so after any other completion
the check finds nothing to close: a stream whose cursor is at the
sentinel closed when the cursor got there, or at close(). The steps
keep their order, and the brand check is the same private-field
test.
@jasnell
jasnell requested a review from a team October 14, 2026 23:42
@jasnell
jasnell requested review from a team as code owners October 14, 2026 23:42
@ask-bonk

ask-bonk Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

LGTM!


Reviewed commit: bc0ab46f · github run

@jasnell
jasnell added this pull request to stack #7753 October 15, 2026 03:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant