Skip to content

v0.5.0

Choose a tag to compare

@hdae hdae released this 28 Aug 18:22
· 32 commits to main since this release

This release redesigns the cache-control API around a first-class sha256 option: bytes are verified when stored, the hash is recorded on the entry, and later cache hits are decided by a plain string comparison — so multi-GB models no longer need a full re-hash on every startup. This is a breaking release — see Migrating from 0.4.0 in the README for the upgrade path.

💥 Breaking Changes

  • cacheName is gone. The namespace is a single fixed "fetch-cache". The HF layer's old "fetch-cache-hf" namespace is no longer referenced — reclaim the space once with caches.delete("fetch-cache-hf"). The admin APIs (clearCache / listCachedUrls) no longer take a positional cacheName; they take an options object ({ caches }) instead. Careful in plain JS: clearCache("...") does not error — the string is ignored and the default namespace is cleared.
  • cacheName-based partitioning has no direct successor. evict prefixes only address the HF content keys; to partition per app / per tenant, pass a custom caches implementation (e.g. a thin wrapper remapping open(name)) to every call, or keep the URLs distinct.
  • verifiedMarker / trustCachedSha256 are gone, absorbed by sha256 + recheck — and the default is reversed: 0.4.0 validated every hit unless you opted in; 0.5.0 trusts a matching recorded hash unless you pass recheck: true. The old x-fetch-cache-verified header is not read (treated as "no record"): such entries are re-hashed once on their first verified read and the record is backfilled, after which hits are string comparisons again.
  • HF default cache keys changed. Files that declare a sha256 are cached under a content key ["hf", kind, repo, path, sha256] — a README-only revision bump is still a cache hit, and switching revisions keeps both contents side by side. Entries cached by 0.4.0 simply miss and are re-downloaded (it is a cache; no data is lost).
  • An explicit expectedBytes now fails loud when the receive buffer cannot be allocated, throwing before a single byte is read instead of downloading everything and failing at the end for the same reason (ADR 0007). A server-declared content-length still degrades as before.

✨ Features

  • First-class sha256 with a recorded hash — fetchBytes now takes sha256 directly (0.4.0 had it only in the HF layer): verify on download, record the hash on the entry, and decide later hits by string comparison. A mismatching record means "the content changed" and self-heals (evict + refetch); recheck: true re-hashes the bytes when you don't want to trust local storage. Entries without a record are hashed once on their first verified read and backfilled.
  • prefetchUrl / prefetchHfFile now bake the recorded hash into entries they verify in flight (replacing the 0.4.0 marker), and compare an existing entry's record against the expectation — a stale or unverified entry is re-warmed with verification, and a failed re-warm leaves the existing entry intact.
  • Prefix management — new evict(prefix) / listKeys(prefix) operate on the HF content keys (evict(["hf", "model", "owner/name"]) frees a repo's sha256-declared files). Files fetched without a sha256 are URL-keyed; sweep them with listCachedUrls + evictUrl (recipe in the README).
  • caches injection on all five admin APIs (CacheAdminOptions) — for test isolation, fault injection, and partitioning.
  • ./hf now re-exports the cache-layer types used in its public signatures (FetchProgress, CacheErrorContext, ValidateBytes, DecodeBytes).

🐛 Fixes

  • The single-flight join key now matches the Cache API's URL normalization — concurrent calls for differently-spelled URLs of the same resource (uppercase host, default port, fragment) join one download instead of fetching twice.
  • The HF entry points validate the file spec (sha256 format) before the revision-resolution request — a malformed declaration no longer reaches the network (0.4.0 leaked one resolution request for mutable refs, and fetchHfFiles could start sibling downloads).
  • A trailing slash in hubUrl is absorbed (no more double-slash URLs causing 404s or duplicate cache keys).
  • A failing insurance delete in prefetchUrl is no longer swallowed; it is reported together with the integrity error as an AggregateError.
  • A caller joining an in-flight download during a progress notification no longer receives the same progress event twice.

📝 Docs

Full Changelog: v0.4.0...v0.5.0