vercel-labs / vercel-labs/native
Design: add pull-based HTTP and file byte streams
Nobody has claimed this yet.
- Dominant language
- Zig
- Stars
- 7.7k
- Forks
- 314
- Avg merge
- 5h
- Merged PRs (30d)
- 13
Description
Problem
Buffered Cmd.fetch caps responses at 256 KiB, and whole-file Cmd.readFile caps files at 1 MiB. Applications therefore cannot incrementally consume larger sources—such as IMDb’s title.ratings.tsv.gz archive, as one example—without custom native code.
Proposed API
Keep stream creation close to the existing non-streaming operations:
Cmd.fetchStream(
{
url,
method,
headers,
body,
timeoutMs,
},
{
key: "download",
ok: "fetch_stream_opened",
err: "stream_failed",
},
);
Cmd.fileStream(
path,
{
key: "input",
ok: "file_stream_opened",
err: "stream_failed",
},
);
Both sources then use the same operations:
Cmd.streamRead(
"download",
64 * 1024,
{
ok: "stream_chunk",
err: "stream_failed",
},
);
Cmd.streamCancel("download");
fetchStream accepts the same FetchSpec as fetch. fileStream accepts the same path and route shape as readFile, except that stream keys are required.
The SDK defines payload shapes without fixing application Msg.kind values:
interface FetchStreamOpenedArm {
readonly key: Uint8Array;
readonly status: number;
readonly contentLength: number;
readonly finalUrl: Uint8Array;
readonly headers: Uint8Array;
}
interface FileStreamOpenedArm {
readonly key: Uint8Array;
readonly size: number;
}
interface StreamReadArm {
readonly key: Uint8Array;
readonly offset: number;
readonly bytes: Uint8Array;
readonly endOfStream: boolean;
}
Contract
- HTTP and file streams share one keyed session namespace.
- Only one read may be outstanding per key.
- HTTP pauses response consumption between reads; files perform no read-ahead.
- Each result contains at most the requested bytes.
- The final result may contain bytes and
endOfStream: true. - EOF automatically retires the stream.
streamCancelreleases the connection or file handle; unknown keys are no-ops.- Returned bytes are borrowed for the receiving
updatecall. - Non-2xx HTTP statuses are successful response openings, matching
fetch. - Record/replay journals open results and only chunks delivered by
streamRead.
The HTTP stream can compose with the atomic writer proposed in #263: read one chunk, append it, wait for acknowledgement, then request the next chunk.
Acceptance criteria
- TypeScript cores can consume HTTP responses and files larger than existing whole-value limits with bounded memory.
- HTTP and files use the same read and cancellation commands.
- Slow consumers pause HTTP rather than losing data or failing.
- TypeScript and Zig expose equivalent behavior.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the existing Cmd.fetch and Cmd.readFile operations and the proposed TypeScript payload interfaces. Then review the HTTP and file-stream contract, including the record/replay behavior and the related atomic-writer proposal in #263. Done means TypeScript cores and Zig expose equivalent bounded-memory behavior for HTTP and file streams, including reads, cancellation, and slow consumers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript, zig
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100