# enhance-packer-browser-streaming — Cluster B §4 streaming enhancements **Package**: `@suaveplan/packer-browser` **Status**: PROPOSED **Type**: feature **Design**: not applicable (Cluster B; architecture captured inline in `tasks.md` and the spec delta, mirroring Cluster A). **Spec delta**: ./specs/packer-browser/spec.md ## Why Cluster A (`2026-05-19-add-packer-browser-core`) landed the production-quality core (PACKER-BROWSER-1..12, 153 tests, 100/100/100/100 coverage). The upstream audit (`../suaveplan/enterprise/packer-browser.md` §4) enumerates ten feature-rich enhancements; Cluster B picks up the four "streaming" items so the package can serve real CDN-backed packed media: progressive (entry-by- entry) unpack, Range-request support inside the Service Worker, encrypted archives with a pluggable keyring, and content-addressed versioned archive URLs with automatic SW cache invalidation. These four are foundational for large media (audio/video) and multi-tenant deployments; remaining §4 items (adaptive cache, ``, multi-origin SW, BackgroundFetch, diagnostics overlay, additional subpath splits, full Playwright sweep) ship in Cluster C. ## What changes Pure additions. Cluster A's public surface (PACKER-BROWSER-1..12) remains intact. The Cluster A entrypoint (default `.` export) gains five new exports; no removals. - ADDED: `PackedResourceLoader.loadStream(inputs, opts?)` — async iterator that yields each `LoadedResource` as soon as its packed bytes are decoded, honouring `AbortSignal` at every yield. - ADDED: `PackedResourceManager.loadStream(inputs, opts?)` — thin pass-through that also writes each decoded entry into the cache as it arrives. - ADDED: `serveRangeResponse(request, body, opts?)` — RFC 7233 §2.1 single-range helper returning `Response` with `206`/`200`/`412`/`416` semantics (`Content-Range`, `Accept-Ranges: bytes`, `Content-Length`, `If-Range` ETag handling). - ADDED: `RunServiceWorkerOptions.rangeThreshold` (default `262144` — 256 KiB) — fetch handler emits a `206` for matching requests whose cached body exceeds the threshold; multi-range requests fall back to `416`. - ADDED: `KeyringProvider` contract — `resolveKey(keyId): Promise` with a default `emptyKeyring()` factory that rejects all key ids. - ADDED: `encodeEncryptedArchive(plaintext, keyId, key, nonce, opts?)` / `decodeEncryptedArchiveHeader(bytes)` — wire format with stable `SP-ENC1\x01` magic prefix, framed JSON header, and AES-256-GCM ciphertext+tag. Header carries `{ alg: "AES-256-GCM", keyId, nonce, aad? }`. - ADDED: `PackedResourceLoaderOptions.keyring` — when supplied, the loader detects the `SP-ENC1` prefix, resolves the key, AES-GCM decrypts, then proceeds with the standard verify+unpack pipeline. - ADDED: `PACKER_BROWSER_CODES.KEY_UNAVAILABLE` (new error code) + matching i18n keys (`packer-browser.error.KEY_UNAVAILABLE.no-key`, `packer-browser.error.KEY_UNAVAILABLE.decrypt-failed`). - ADDED: `computeArchiveContentHash(entries, formatVersion)` — BLAKE3 Merkle root over canonical entry digests with the format-version byte appended; reuses `@suaveplan/packer`'s `entryDigestBlake3`/`merkleRootBlake3`. - ADDED: `buildContentAddressedUrl(prefix, hash, ext?)` — content-addressed URL builder; defaults to `/packed/.bundle.asset`. - ADDED: `RunServiceWorkerOptions.contentAddressed` (default `true`) + `RunServiceWorkerOptions.legacyUrlPattern` — when content-addressed mode is on, SW caches under the hash; the legacy `/packed/` pattern is still recognised when `legacyUrlPattern` is supplied. - ADDED: `cleanupOutdatedCaches(scope, prefix, keep)` — SW helper that deletes every cache whose name starts with `prefix` and is not in `keep`; invoked from the `activate` handler when content-addressed mode is on so a new manifest hash invalidates old cache entries. - ADDED: `PACKER_BROWSER_FLAGS.URL_CONTENT_ADDRESSED` — registered against the manager's injected `FlagProvider` so adopters can flip the URL form at runtime; default-on for new manifests, default-off honours legacy `/packed/` requests transparently. ## Out of scope - Adaptive cache sizing via `navigator.storage.estimate()` (§4.3) — Cluster C. - `` generation (§4.5) — Cluster C. - Multi-origin SW helper (§4.7) — Cluster C. - BackgroundFetch API integration (§4.8) — Cluster C. - Diagnostics dev overlay (§4.9) — Cluster C. - Subpath splits beyond Cluster A's five (`.`, `/manager`, `/service-worker`, `/utils`, `/singleton`) (§4.10) — Cluster C. - Full Playwright E2E coverage across all features — Cluster C (Cluster A's Playwright stub remains; new tests are vitest unit only). ## Source mapping This change derives from `../suaveplan/enterprise/packer-browser.md`. | Requirement | Source lines | Notes | |---|---|---| | PACKER-BROWSER-13 — Progressive streaming unpack | L69-L69 | §4.1 progressive streaming; per-entry yield with `AbortSignal` honouring. | | PACKER-BROWSER-14 — Range-request SW support | L70-L70 | §4.2 range requests; single-range 206 with `Content-Range`/`Accept-Ranges`/`If-Range`. | | PACKER-BROWSER-15 — Encrypted archives with pluggable keyring | L72-L72 | §4.4 encrypted archives; pluggable `KeyringProvider`, AES-256-GCM via `@suaveplan/crypto`, new `KEY_UNAVAILABLE` error code. | | PACKER-BROWSER-16 — Content-addressed versioned archive URLs | L74-L74 | §4.6 versioned archives; BLAKE3 hash over entry digests + format-version byte; `cleanupOutdatedCaches` from SW activate; flag-gated URL form. | ## Verification approach Every requirement carries `**Verified by:**` entries pointing at concrete `it(...)` titles in the listed test files. Tests are co-located with implementation (`src//.test.ts`). - `src/loader/loader-stream.test.ts` — covers PACKER-BROWSER-13. - `src/manager/manager.test.ts` — covers PACKER-BROWSER-13 (manager pass-through). - `src/range-response/range-response.test.ts` — covers PACKER-BROWSER-14. - `src/service-worker/service-worker.test.ts` — covers PACKER-BROWSER-14 (SW wiring). - `src/keyring/keyring.test.ts` — covers PACKER-BROWSER-15. - `src/encrypted-archive/encrypted-archive.test.ts` — covers PACKER-BROWSER-15 (wire format). - `src/loader/loader.test.ts` — covers PACKER-BROWSER-15 (decrypt-then-unpack path). - `src/content-address/content-address.test.ts` — covers PACKER-BROWSER-16. - `src/service-worker/service-worker.test.ts` — covers PACKER-BROWSER-16 (cleanupOutdatedCaches). - `src/error-codes.test.ts` — covers PACKER-BROWSER-15 (new code reachability). ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/packer-browser` to enumerate. - No dependents (verified by `grep -rln "@suaveplan/packer-browser" packages/ apps/` — the package is still pre-1.0 with no in-tree consumers, per Cluster A archive). All additions are additive; no consumer migration required. ### Migration Not applicable — additive changes only. Existing Cluster A consumers continue to work unchanged; the new APIs are opt-in via additional options.