# `loader` _(no submodules)_ ## Purpose `loader.ts` is the fetch → verify → unpack → Blob-URL pipeline for packed resources. It is the only place in the package that issues a network request, the only place that runs `Unpacker.unpack`, and the only place that allocates a Blob URL. Splitting it out from `PackedResourceManager` keeps every concern testable in isolation — fetch retry, signature failure, abort propagation, and OTel histogram emission can each be exercised without instantiating a cache. ## Features - `fetch` routed through `@suaveplan/retry`'s `withRetry` with a single-attempt default policy; callers opt in to retry by passing `retryConfig`. - `AbortSignal` honoured at every yield point — pre-fetch, post-fetch, post-buffer, post-verify, and pre-unpack. - Optional Ed25519 signature verification when `verifyKey` is supplied and the response carries an `X-Archive-Signature` header. - OTel emission: increments `packed_resource_fetch_total` on every attempt; records `packed_resource_decode_ms` on every successful pipeline run. - Typed error surface: every failure produces `PackerBrowserError` with a `PACKER_BROWSER.*` code and an i18n-resolved message. - Injectable `fetch` for tests — no `vi.mock` required. ## Usage: Basic ```ts import { createPackerBrowserObservability, PackedResourceLoader, resolveRuntime, } from "@suaveplan/packer-browser"; const loader = new PackedResourceLoader({ runtime: resolveRuntime(), observability: createPackerBrowserObservability(), }); const { url, metadata } = await loader.load("/banner.packed"); img.src = url; ``` ## Usage: Advanced ```ts import { createConstantBackoff } from "@suaveplan/retry"; import { PackedResourceLoader } from "@suaveplan/packer-browser"; const controller = new AbortController(); const loader = new PackedResourceLoader({ runtime: resolveRuntime(), observability, fetch: async (url) => recordedFetch(url), }); const out = await loader.load("/banner.packed", { verifyKey: trustAnchorPublicKey, signal: controller.signal, retryConfig: { maxAttempts: 3, delay: createConstantBackoff({ delay: 250 }), }, }); ``` ## API Reference - `new PackedResourceLoader(opts: PackedResourceLoaderOptions)`. - `loader.load(url: string, opts?: PackedResourceLoadOptions): Promise`. - Throws `RESOURCE_NOT_FOUND` for non-2xx. - Throws `ARCHIVE_SIGNATURE_INVALID` for a non-verifying signature when `verifyKey` is supplied. - Throws `LOAD_FAILED` when `Unpacker.unpack` rejects. - Throws `ABORTED` when the supplied signal fires. - `PackedResourceLoadOptions`: - `verifyChecksum?: boolean` — default `false`. - `verifyKey?: Uint8Array` — optional Ed25519 public key. - `signal?: AbortSignal` — checked at every yield. - `retryConfig?: RetryConfig` — default: single attempt. - `messageResolver?: MessageResolver` — overrides the constructor's resolver for this call. ## Implementation Notes Cluster A's loader treats every fetched payload as a raw `PackedData` whose metadata is reconstructed from the bytes; signature verification covers the **archive transport** layer, while integrity verification is delegated to the `Unpacker` and currently skipped (`verifyIntegrity: false`) because Cluster A archives do not carry the BLAKE3 integrity bundle. Cluster B introduces the formal manifest decoder and re-enables integrity. The `X-Archive-Signature` header contract is unchanged across clusters — switching to a separate signature manifest is a Cluster B concern only.