The caching store used to hold a baseUrl: string and build every chunk URL
itself (buildUrl(baseUrl, key)), which quietly hard-wired one assumption
into the whole cache stack: that a chunk is addressable as a URL. That is
true for a directory store and false for a zipped one, where a chunk is a
member INSIDE a single archive and can only be reached by asking the archive
for it. Same for a future reference/kerchunk store.
So the store now takes a ChunkSource: "give me the bytes for this key, and
tell me whether the thing you are reading still has the identity I cached".
Everything above it — L1, L2/OPFS, the segmented LRU, the prefetcher — keeps
keying on whole objects and is untouched.
Two deliberate shapes:
The source returns materialized bytes, not a Response. A zip member
has no Response, so response lifetime cannot survive as a shared source
contract. The HTTP source delegates that lifetime to fetchWithRetry,
whose consumer runs inside the shared fetch-gate lease and cancels unread
bodies before release.
bytesOverWire is separate from data.byteLength. They are equal for
plain HTTP, but a compressed archive member transfers fewer bytes than it
yields. Collapsing the two would make the bandwidth meter over-report a
DEFLATE archive by its compression ratio.
The byte source behind
MultiLevelCachingStore.The caching store used to hold a
baseUrl: stringand build every chunk URL itself (buildUrl(baseUrl, key)), which quietly hard-wired one assumption into the whole cache stack: that a chunk is addressable as a URL. That is true for a directory store and false for a zipped one, where a chunk is a member INSIDE a single archive and can only be reached by asking the archive for it. Same for a future reference/kerchunk store.So the store now takes a
ChunkSource: "give me the bytes for this key, and tell me whether the thing you are reading still has the identity I cached". Everything above it — L1, L2/OPFS, the segmented LRU, the prefetcher — keeps keying on whole objects and is untouched.Two deliberate shapes:
Response. A zip member has noResponse, so response lifetime cannot survive as a shared source contract. The HTTP source delegates that lifetime tofetchWithRetry, whose consumer runs inside the shared fetch-gate lease and cancels unread bodies before release.bytesOverWireis separate fromdata.byteLength. They are equal for plain HTTP, but a compressed archive member transfers fewer bytes than it yields. Collapsing the two would make the bandwidth meter over-report a DEFLATE archive by its compression ratio.