3.5 KiB
range
(no submodules)
Purpose
Encodes HTTP Range semantics for HttpStorage.getPartial. Builds the request header from (offset, length) and interprets the response in the three documented shapes: 206 Partial Content (happy path — body returned verbatim), 200 OK (origin ignored the Range — body sliced client-side and the fallback is flagged so the caller can emit a counter), and 416 Range Not Satisfiable (request beyond EOF — mapped to STORAGE_HTTP.NOT_FOUND).
Without this module, every adapter consumer would have to re-implement the same disambiguation. Keeping it isolated also lets tests pin range behaviour without spinning up the full HttpStorage.
Features
buildRangeHeader(offset, length)— validates integer non-negativity / positivity, returnsbytes=<o>-<o+l-1>.consumeRangeResponse(response, offset, length, url)— distinguishes 206 / 200 / 416 / other, returns{ bytes, fellBack }.- Programmer-error inputs throw
STORAGE_HTTP.INVALID_OPTIONSso they surface in development, not as cryptic upstream failures.
Usage: Basic
import { buildRangeHeader, consumeRangeResponse } from "@suaveplan/storage-http";
const header = buildRangeHeader(0, 1024); // "bytes=0-1023"
const response = await fetch(url, { headers: { Range: header } });
const { bytes, fellBack } = await consumeRangeResponse(response, 0, 1024, url);
Usage: Advanced
import { buildRangeHeader, consumeRangeResponse } from "@suaveplan/storage-http";
async function readSlice(url: string, offset: number, length: number) {
const response = await fetch(url, { headers: { Range: buildRangeHeader(offset, length) } });
const { bytes, fellBack } = await consumeRangeResponse(response, offset, length, url);
if (fellBack) operatorLog.warn({ url }, "origin ignored Range — full body returned");
return bytes;
}
API Reference
buildRangeHeader(offset: number, length: number): string
Returns "bytes=<offset>-<offset+length-1>". Throws StorageHttpError(INVALID_OPTIONS) when offset is not a non-negative integer or length is not a positive integer.
interface RangeOutcome
{ bytes: Uint8Array; fellBack: boolean } — fellBack is true only when the origin returned 200 OK to a Range request and the slice was performed client-side.
consumeRangeResponse(response, offset, length, url): Promise<RangeOutcome>
206→ returns the body verbatim withfellBack = false.200→ consumes the full body, slices[offset, offset + length), returns withfellBack = true.416→ throwsStorageHttpError(NOT_FOUND)withhttpStatus: 416.- other → throws
StorageHttpError(UPSTREAM_ERROR)with the observed status.
Implementation Notes
- The
Content-Rangeresponse header is intentionally NOT parsed here — it carries the total object size, which is useful for cache metadata but not required to decode the body.HttpStorageparses it in the metadata path when needed. - Client-side slicing on
200usesMath.minclamps so out-of-bounds requests never throwRangeErroronslice; the resulting array is simply empty ifoffset >= body.byteLength. The caller decides whether to treat that as "object shorter than expected" or pass it through. 416mapping toNOT_FOUNDis a deliberate simplification: distinguishing "object missing" from "range past EOF on an existing object" requires aHEADround-trip the caller already has cheaper alternatives for (e.g.exists()or attachingIf-Rangeto disambiguate cache vs miss).