Skip to content

Pin a byte source's expectedLength as an exact total - #7786

Merged
jasnell merged 5 commits into
mainfrom
jasnell/ts-streams-duplicate-consolidaton-14
Oct 16, 2026
Merged

jasnell merged 5 commits into
mainfrom
jasnell/ts-streams-duplicate-consolidaton-14

Conversation

@jasnell

@jasnell jasnell commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

Expand test for expectedLength, deduplicate some more

@jasnell
jasnell added this pull request to stack #7753 October 16, 2026 14:09
@jasnell
jasnell requested review from a team as code owners October 16, 2026 14:09
@clanky-the-clanker

clanky-the-clanker Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

✅ Approved — The readable.ts change is a refactor that keeps the behaviour of respond() and respondWithNewView() the same, and the new tests cover the expectedLength paths well. No issues found.

The decision is advisory: this comment never approves, blocks or requests changes on the pull request.


✅ All checks passed

🔒 Security: ✅ LGTM

No security concerns.


📖 AGENTS.md: ✅ LGTM

The AGENTS.md update in the test directory matches the expanded test.


📋 Code Quality: ✅ LGTM

#respondInternal keeps the same branch logic and call order as the two blocks it replaces in respond() and respondWithNewView(), so behaviour is unchanged. The new tests cover overflow through enqueue(), respond() and respondWithNewView(), closing at the exact total, closing short of it, and the same cases with a byobRequest held across tee().


⚡ Performance: ✅ LGTM

Moving shared code into a private helper has no performance impact.


📝 Documentation: ✅ LGTM

The comments describe the code as it is now.


Reviewed commit: a5970ca5

@ask-bonk

ask-bonk Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Since last review: 0 resolved, 1 still open, 0 new.

The BYOB response refactor and expected-length coverage remain, but the prior in-range respondWithNewView() coverage gap is still open.

Not re-run: tests, api-compat, docs, compat-flags, design-simplicity (no author changes in their files since the last review)


Reviewed commit: a5970ca5 · github run

Comment thread src/tests/streams/readable-byte/expected-length.js Outdated
@jasnell
jasnell force-pushed the jasnell/ts-streams-duplicate-consolidaton-14 branch from eae1984 to b9295c2 Compare October 16, 2026 15:39

@guybedford guybedford left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. The #respondInternal extraction keeps the steps in the same order, and it splits where the spec splits Respond/RespondWithNewView from RespondInternal. Verified locally: the readable-byte suite passes (22 targets), and dropping #accountDelivery from #respondInternal / #respondToReleasedHead fails only byteSourceExpectedLengthContract / byteSourceExpectedLengthHeldRequest respectively.

Comment thread src/tests/streams/readable-byte/expected-length.js Outdated
Comment thread src/tests/streams/readable-byte/expected-length.js
@jasnell
jasnell force-pushed the jasnell/ts-streams-duplicate-consolidaton-14 branch from b9295c2 to cb6b949 Compare October 16, 2026 19:43
Base automatically changed from jasnell/ts-streams-duplicate-consolidaton-13 to main October 16, 2026 20:15
DUP-23 step 1, test only. Under TypeScript a byte source that
declares expectedLength must deliver exactly that many bytes through
enqueue(), respond() and respondWithNewView() combined: the delivery
that goes past the total throws RangeError and errors the stream, and
so does a close() short of it. No test reached either error, so the
accounting on any of those paths could go without a test failing.

byteSourceExpectedLengthContract pins an overflow through each of the
three, the exact total followed by close(), and a close() short of
the total. C++ enforces neither error: every delivery reaches the
reader, and close() closes the stream. Readable-byte ledger #42
records the divergence.
DUP-23 step 2. respond() and respondWithNewView() ended with the
same steps once they had validated the response and transferred the
head pull-into's buffer: invalidate the byobRequest, then answer the
released head, commit the pending pull-intos when the stream is
closed, or count the bytes toward expectedLength, fill the head, and
check for close and pull. The two copies differed only in where the
byte count came from.

#respondInternal, named for the spec's
ReadableByteStreamControllerRespondInternal, now holds those steps.
Each caller keeps its own validation and buffer transfer. Nothing
observable changes. Dropping the accounting call from
#respondInternal fails only byteSourceExpectedLengthContract, the
step 1 pin.
DUP-23 step 3, test only. When a reader releases a pending read(view)
whose byobRequest the source holds, and the stream is then teed, the
request survives the fork. TypeScript answers it on a path of its
own, which counts the responded bytes toward expectedLength
separately. No test reached that count: every response in
byteSourceExpectedLengthContract answers a reader's own read.

byteSourceExpectedLengthHeldRequest pins an overflow through
respond() and respondWithNewView() on such a request, which errors
both branches under TypeScript and reaches both under C++, and the
exact total followed by close(), which closes both branches in both.
Dropping the count from that path fails only this test.

The overflow error and the two responses are now shared with
byteSourceExpectedLengthContract. Readable-byte ledger #42 covers the
held request and records TypeScript's errors as by design.
DUP-23 step 4, test only. The comments above
byteSourceExpectedLengthContract and
byteSourceExpectedLengthHeldRequest each restated the divergence,
which the file header and readable-byte ledger #42 already describe.
Each is now `// DIVERGENCE (ledger #42)`.
DUP-23 step 5, test only. byteSourceExpectedLengthHeldRequest pinned
an overflow and the exact total through a byobRequest held across
tee(), but not a close() short of the total, which
byteSourceExpectedLengthContract pins on a reader's own read. It now
also responds with two bytes of four and closes: under TypeScript
close() throws RangeError and both branches error with it, and under
C++ both branches read the two bytes and close. The close() check is
the one the Contract case reaches.

The underflow error is now shared by both tests, as the overflow
error is.
@jasnell
jasnell force-pushed the jasnell/ts-streams-duplicate-consolidaton-14 branch from cb6b949 to a5970ca Compare October 16, 2026 20:15
@jasnell
jasnell merged commit 29f6476 into main Oct 16, 2026
28 checks passed
@jasnell
jasnell deleted the jasnell/ts-streams-duplicate-consolidaton-14 branch October 16, 2026 21:04
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.

2 participants