we (web engine): Experimental web browser project to understand the limits of Claude
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Streams API: pipeTo between two TransformStreams loses subsequent reads #200

open opened by pierrelf.com

Summary#

When piping firstTs.readable.pipeTo(secondTs.writable) between two TransformStream instances, the first chunk flows through correctly but subsequent read() calls on secondTs.readable never resolve. The pipe's returned promise also never settles.

Discovered while working on Phase 20's built-in transform streams (PR for "Implement built-in transform streams").

Minimal repro#

var first = new TransformStream({});  // identity passthrough
var second = new TransformStream({});  // identity passthrough
var w = first.writable.getWriter();
w.write(\"x\");
w.close();
first.readable.pipeTo(second.writable).then(
    function() { console.log(\"piped\"); },
    function(e) { console.log(\"err:\" + e); }
);
var r = second.readable.getReader();
r.read().then(function(res1) {
    console.log(\"r1:\" + (res1.done ? \"done\" : res1.value));
    r.read().then(function(res2) {
        console.log(\"r2:\" + (res2.done ? \"done\" : res2.value));
    });
});

Expected output: `r1:x`, then `r2:done`, then `piped`. Actual output: only `r1:x` is logged; the second `r.read()` never resolves.

Workarounds in use#

The Phase 20 transform-streams tests sidestep the bug by:

  • Feeding pre-compressed bytes directly to `DecompressionStream.writable` rather than piping `CompressionStream.readable` → `DecompressionStream.writable`.
  • Storing `Uint8Array(...)` results in a local variable before passing to `writer.write()` (related: nested method-call arg quirk).

Possible causes (untested)#

  • A microtask scheduled when the second `writer.ready` resolves never runs, even though the .then was registered on the same promise that is later resolved by `refreshReady`. Suggests the promise's reaction list (or the promise itself) is being collected or replaced between the .then registration and the resolve call.
  • The single-chunk variant happens to work for `deflate-raw` because the source emits exactly one chunk (so only one pipe iteration is needed).

Acceptance#

A regression test in `crates/js/src/streams.rs` that runs the repro above and asserts both `r2:done` and `piped` are logged.

sign up or login to add to the discussion
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:meotu43t6usg4qdwzenk4s2t/sh.tangled.repo.issue/3mlzv3cq2uh2p