Optionallifetime: AbortSignal
Aborted when the owning store is disposed; cancels reads in flight.
Static ReadonlyMAX_Backstop so a pathological archive (a huge comment, a colossal directory) cannot balloon the heap. The preamble is ~141 kB at a thousand members and scales at ~70 B/member, so this holds a very large directory and still refuses to hold a whole archive.
Private ReadonlyurlPrivate Optional ReadonlylifetimeAborted when the owning store is disposed; cancels reads in flight.
Arm or disarm retention of fetched ranges.
Armed by LuxarZipStore around the directory read and disarmed
afterwards, so what survives is the archive's structure — not its data.
Fetch the archive tail with ONE suffix GET (bytes=-65557), taking the
total length from Content-Range.
Opening used to cost three serial round trips — HEAD for identity, a
length lookup, then the fixed 65,557-byte tail — before the first member
could be named. The suffix range answers all three: Content-Range carries
the total, the body IS the tail unzipit reads next (and, for all but huge
archives, the central directory inside it), and when the identity probe
already did this GET there is nothing left to fetch at all.
Anything but a valid tail 206 — a 416 (empty archive, or suffix ranges
refused), a 200, a window that is not the tail — falls back to the
getLength + explicit-offset path rather than failing, so only a host that
really cannot serve ranges is reported as one, by the checks that already
word that precisely. Missing/denied archives still fail here, as they would
there.
Read ONE member with a single ranged GET, then run body — the unzipit
member read — against it.
unzipit reads a member in two strictly serial requests: the 30-byte local
header (to learn where the payload starts), then the payload. On a 100 ms
RTT host that doubled the cost of every chunk. The caller sizes one window
from the CENTRAL directory — local header, name, extra field plus slack,
compressed payload — so both reads are answered from memory. A local extra
field that outgrows the slack simply misses the window for the payload read,
which then goes to the network as before: slower, never wrong.
Optionalpriority: FetchPriority | FetchPriorityCell
Gate class for the window fetch (the member's caller knows
whether a frame is waiting on it; unzipit's own reads do not).
Optionalsignal: AbortSignal
The member caller's abort signal. It cancels the window
fetch — queued in the gate or in flight — on top of the reader's
lifetime, so a superseded view stops holding a slot. unzipit's follow-up
reads are answered from the window, or go out under the lifetime alone.
Record what a fresh HEAD already told us, so getLength does not
repeat it. The identity probe and the length lookup ask the same question
of the same URL; paying for it twice adds a serialised round trip to every
load.
Fresh identity of the archive, bypassing every cache — ETag, else
Last-Modified + Content-Length. null means "cannot tell", which is
never evidence that the archive changed.
Lives here rather than in the caller because it is a HEAD on this exact
URL: doing it here lets the length it reports seed getLength.
Optionalsignal: AbortSignalOptionaltimeoutMs: numberTotal archive size.
Prefers HEAD, but falls back to a one-byte ranged GET when HEAD is
rejected or answers without a usable Content-Length — some static hosts
and signed-URL schemes allow only GET. The fallback doubles as an early
probe: a host that answers it with 200 is telling us it ignores Range,
and we would rather say so here than at the first chunk.
Both requests go through fetchWithRetry like every other read here: the
metadata lane of the fetch gate, the retry budget and its timeouts,
cache: 'no-store', and the reader's lifetime signal. An unread body
(the whole archive, from a host that ignores Range) is cancelled by it.
Read size bytes at offset, insisting on a real partial response.
Optionalsignal: AbortSignal
A
unzipitReaderover HTTP, structurally typed so neitherunzipitnor its types need to become a direct dependency.