Files
2026-05-27 23:29:53 +01:00

7.0 KiB

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, <link rel=preload>, 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<Uint8Array> 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/<hash>.bundle.asset.
  • ADDED: RunServiceWorkerOptions.contentAddressed (default true) + RunServiceWorkerOptions.legacyUrlPattern — when content-addressed mode is on, SW caches under the hash; the legacy /packed/<name> 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/<name> requests transparently.

Out of scope

  • Adaptive cache sizing via navigator.storage.estimate() (§4.3) — Cluster C.
  • <link rel="preload"> 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/<feature>/<feature>.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.