Skip to content

v0.6.0

Choose a tag to compare

@hdae hdae released this 02 Sep 17:47
· 24 commits to main since this release

This release adds into: read a download — or a cache hit — straight into a buffer you allocated and get a prefix view of it back, so a sequential loop over dozens of large shards reuses one buffer instead of allocating one per call. It is additive: no API is removed or changed and no migration is needed. One caveat: the JavaScript floor moves up to Baseline 2024 (see below).

✨ Features

  • into — caller-owned buffers (FetchBytesOptions.into, and per file as HfFileSpec.into for fetchHfFile / fetchHfFiles — one buffer per spec, since fetchHfFiles runs in parallel). A network download is written into your buffer instead of a freshly allocated one, and a cache hit is read out of Cache Storage through its body stream into it instead of being materialized with arrayBuffer(). The return value is into.subarray(0, n); the buffer stays yours, so the view (and the raw bytes handed to validate / decode) is only valid until the next write into the same buffer. With decode, the buffer holds the stored form and the decoded form is returned separately. With into, expectedBytes is no longer an allocation hint: what remains is the pre-check that rejects a declared length the buffer cannot hold. prefetchUrl / prefetchHfFile have no use for into (they never hold the bytes).
  • Too-small buffers fail loud instead of degrading. A network download is cancelled at the chunk that overflows and nothing is cached; a cache hit throws but keeps the entry (it is neither corruption nor a cache I/O failure, so it neither self-heals nor falls back to the network); an expectedBytes larger than the buffer throws before any request is made — in the HuggingFace layer that includes the revision-resolution request. The error is a plain Error with name === "IntoCapacityError"; after any throw the buffer's contents are undefined.
  • Concurrent use of one buffer is rejected at the entrance. Passing the same buffer to two in-flight calls (for example the same into on several HfFileSpecs of a parallel fetchHfFiles) throws at the cache layer's entry point, before that call's own download starts, because interleaved writes could otherwise store bytes that do not match the recorded hash. Reuse a buffer sequentially; a SharedArrayBuffer-backed view is rejected at the same point.
  • Single-flight keeps caller-owned buffers private. A caller that joins an in-flight download never receives the leader's into buffer: when the leader used into and someone has joined, the leader copies the stored bytes once before sharing them — so that flight peaks at twice the size, and a failure to allocate that copy throws even though the download, verification and cache write all succeeded (the retry is a cache hit). A joiner with its own into gets the bytes copied into it. Nothing is copied for sharing when nobody joins. Note that a capacity failure on the leader's buffer fails the whole flight, joiners without into included.
  • sha256 verification of materialized bytes now hands partial views straight to WebCrypto (only a SharedArrayBuffer-backed view is copied first), so an into prefix view is hashed without a JS-level copy. WebCrypto still takes its own copy internally, as the spec requires.

⚡ Runtime floor

  • Baseline 2024 JavaScript. The single-flight path now uses Promise.withResolvers (Safari 17.4, Chrome 119, Firefox 121, Deno 1.38, Node.js 22 and later); 0.5.0 did not use it anywhere in the library, so on a runtime below that line calls that worked in 0.5.0 will now throw. The TypeScript floor is unchanged (5.7 or later, where Uint8Array takes a type argument) but is now visible in the public API: into is typed Uint8Array<ArrayBuffer>.

📝 Docs

  • README gains a Reusing one buffer across many reads (into) section stating what into does and does not save (a joined flight's copy, body-less runtimes, Cache implementations that do not stream, WebCrypto's own copy); the design is recorded in ADR 0009 and the contract in limitations. Runtime support now records the JavaScript and TypeScript floors above.

Full Changelog: v0.5.0...v0.6.0