feat(taildrop): stream files via ReadableStream on send and receive #11

Merged
codinget merged 1 commits from streaming-taildrop into webnet 2026-06-12 00:26:17 +02:00
Owner

Removes whole-file buffering on both send and receive paths by wiring up the Web Streams API throughout the WASM/JS Taildrop bridge.

Changes

SendsendFile now accepts a ReadableStreamDefaultReader stream + declaredSize. A new jsStreamReader (io.ReadCloser) pulls chunks from the JS stream by awaiting reader.read() Promises via the channel+js.FuncOf pattern. The full file is never copied into Go memory.

ReceiveopenWaitingFile resolves with a JS ReadableStream instead of a Uint8Array. A new jsReadableStream(rc io.ReadCloser) helper creates a pull-based stream: each pull returns a Promise; a goroutine reads up to 64 KiB and enqueues the chunk. No io.ReadAll.

FileOpsjsFileOps.OpenReader now expects JS to return a ReadableStream; it wraps it in jsStreamReader for streaming delivery to Go.

JS API changes (breaking)

  • sendFile(stableNodeID, filename, data: Uint8Array)sendFile(stableNodeID, filename, stream: ReadableStream<Uint8Array>, declaredSize: number)
  • openWaitingFile(name) resolves with ReadableStream<Uint8Array> instead of Uint8Array
  • IPNFileOps.openReader callback now passes ReadableStream<Uint8Array> instead of Uint8Array

Companion PR in webnet for the JS/TS side: TBD

Removes whole-file buffering on both send and receive paths by wiring up the Web Streams API throughout the WASM/JS Taildrop bridge. ## Changes **Send** — `sendFile` now accepts a `ReadableStreamDefaultReader` stream + `declaredSize`. A new `jsStreamReader` (`io.ReadCloser`) pulls chunks from the JS stream by awaiting `reader.read()` Promises via the channel+`js.FuncOf` pattern. The full file is never copied into Go memory. **Receive** — `openWaitingFile` resolves with a JS `ReadableStream` instead of a `Uint8Array`. A new `jsReadableStream(rc io.ReadCloser)` helper creates a pull-based stream: each `pull` returns a Promise; a goroutine reads up to 64 KiB and enqueues the chunk. No `io.ReadAll`. **FileOps** — `jsFileOps.OpenReader` now expects JS to return a `ReadableStream`; it wraps it in `jsStreamReader` for streaming delivery to Go. ## JS API changes (breaking) - `sendFile(stableNodeID, filename, data: Uint8Array)` → `sendFile(stableNodeID, filename, stream: ReadableStream<Uint8Array>, declaredSize: number)` - `openWaitingFile(name)` resolves with `ReadableStream<Uint8Array>` instead of `Uint8Array` - `IPNFileOps.openReader` callback now passes `ReadableStream<Uint8Array>` instead of `Uint8Array` Companion PR in webnet for the JS/TS side: TBD
codinget added 1 commit 2026-06-11 23:21:11 +02:00
Send: accept a ReadableStreamDefaultReader instead of a Uint8Array.
jsStreamReader (new io.ReadCloser) awaits reader.read() Promises via the
channel+FuncOf pattern, feeding chunks directly to the HTTP PUT body.
No js.CopyBytesToGo of the full file.

Receive: openWaitingFile now returns a pull-based ReadableStream backed by
the Go io.ReadCloser (jsReadableStream helper). Each pull call reads up
to 64 KiB and enqueues a Uint8Array chunk; no io.ReadAll.

jsFileOps.OpenReader: JS now returns a ReadableStream instead of a
Uint8Array; Go wraps it in jsStreamReader for streaming delivery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
codinget marked the pull request as ready for review 2026-06-12 00:24:23 +02:00
codinget merged commit 21d0f11d85 into webnet 2026-06-12 00:26:17 +02:00
codinget deleted branch streaming-taildrop 2026-06-12 00:26:17 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: webnet/tailscale#11