# 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.