12 KiB
add-packer-browser-core — Cluster A migration of @suaveplan/packer-browser
Package: @suaveplan/packer-browser
Status: PROPOSED
Type: new-package
Design: not applicable (Cluster A core; no separate design.md — architecture is captured inline in tasks.md and the spec delta).
Spec delta: ./specs/packer-browser/spec.md
Why
@suaveplan/packer-browser is the browser-tier runtime for whole-file packed
archives produced by @suaveplan/packer. The upstream form (audit:
../suaveplan/enterprise/packer-browser.md lines 1–115) carries a
production-grade PackedResourceManager (LRU + ref-count + Blob-URL
bookkeeping), a Service Worker interceptor, and lifecycle helpers — but
violates four genesis quality bars: (1) PackedResourceManager.ts is 318 LOC
(over the 300-line hard cap), (2) the Service Worker is untested, (3) the
package ships React hooks as a runtime dependency, and (4) it has no archive
signature verification, no OTel metrics, no injected Clock/Rng, no
MessageResolver-keyed errors, no AbortSignal propagation, and no
@suaveplan/retry on the IO source-pull path. Cluster A lands the
production-quality core that fixes every cross-cutting gap and the §3 audit
items; Clusters B and C land §4 enhancements (progressive streaming,
range-request SW, encrypted archives, versioned URLs, BackgroundFetch,
diagnostics overlay).
The package is NEW on genesis — STATUS.md marks it as pending under
the bitpacker section (line 129). This change also relocates it under
data: in scripts/folder-reorg/assignment.yml between packer and
pagination, mirroring @suaveplan/packer's home (the upstream bitpacker
grouping is historical and no longer matches its dep graph).
What changes
This change adds a new package; every requirement is ## ADDED. The public
surface is:
- ADDED:
PackedResourceManager(class) — lifecycle façade over the cache- loader; emits typed
EventEmitterevents forload/cacheHit/cacheMiss/cacheEvict/error.
- loader; emits typed
- ADDED:
createPackedResourceManager(options?)— explicit factory; the default.export does NOT construct a singleton at import time. - ADDED:
packedResourceManager— module-singleton, hosted behind the/singletonsubpath export so the default surface stays import-cost-free. - ADDED:
PackedResourceCache+PackedResourceLoader— the two collaborators behind the manager. Available via/managersubpath for consumers that need surgical imports. - ADDED:
runPackedResourceServiceWorker(scope)+ handlers forinstall/activate/fetch/message, exposed via/service-workersubpath along with the hand-rolledServiceWorkerGlobalScopeshim (createServiceWorkerShim) used by the unit-test harness. - ADDED:
registerPackedResourceServiceWorker/unregisterPackedResourceServiceWorker/clearServiceWorkerCache/getServiceWorkerCacheStatus, via/utilssubpath. - ADDED:
verifyArchiveSignature(manifestBytes, signature, publicKey)— Ed25519 verification gate run before any packed resource is unpacked. - ADDED:
PACKER_BROWSER_CODES+PackerBrowserError(extendsSuaveplanError) — ≥6 stable codes:RESOURCE_NOT_FOUND,SW_REGISTRATION_FAILED,ARCHIVE_SIGNATURE_INVALID,UNSUPPORTED_RESOURCE_TYPE,ABORTED,LOAD_FAILED. - ADDED:
createPackerBrowserObservability+PackerBrowserObservability— OTel meter/tracer bundle exposingpacked_resource_fetch_total,packed_resource_cache_hits_total,packed_resource_cache_miss_totalcounters andpacked_resource_decode_mshistogram. - ADDED:
PACKER_BROWSER_MESSAGES+defaultPackerBrowserResolver— English i18n catalog keyedpacker-browser.error.<CODE>.<scenario>. - ADDED:
resolveRuntime+defaultClock/defaultRng— injectable platform primitives (mirrors@suaveplan/packer/runtime).
Out of scope
- React hooks (
usePackedResource,usePackedAudio,usePackedFont,usePackedImage,usePackedVideo) — these belong in a separate@suaveplan/react-packercompanion (assignment task #5 in the upstream enterprise audit). Adding React as a runtime dep would force the browser-tier package onto every consumer; the audit explicitly recommends the split. - Progressive streaming unpack (§4.1) — Cluster B.
- Range-request SW support (§4.2) — Cluster B.
- Adaptive cache sizing via
navigator.storage.estimate()(§4.3) — Cluster B. - Encrypted-archive pluggable keyring (§4.4) — Cluster B. (Basic AEAD
decryption via the existing
@suaveplan/crypto/@suaveplan/packersurface is supported on the loader; the pluggable keyring is what Cluster B adds.) <link rel="preload">generation (§4.5) — Cluster C.- Content-addressed versioned URLs + invalidation (§4.6) — 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
.,/singleton,/manager,/service-worker,/utils(§4.10) — Cluster C.
Source mapping
This change derives from ../suaveplan/enterprise/packer-browser.md.
| Requirement | Source lines | Notes |
|---|---|---|
| PACKER-BROWSER-1 — Manager split (lifecycle / cache / loader) | L20-L22, L35-L36, L45-L45 | Splits the 318-LOC PackedResourceManager.ts into manager.ts, cache.ts, loader.ts, each ≤ 300 LOC. |
| PACKER-BROWSER-2 — Service Worker is unit-tested via a real SW-scope shim | L29-L30, L40-L41, L46-L46 | Hand-rolled createServiceWorkerShim(); no vi.mock. |
| PACKER-BROWSER-3 — Archive signature verification (Ed25519) before unpack | L38-L39, L48-L48 | Run via @suaveplan/crypto ed25519.verify before the loader hands bytes to Unpacker. |
| PACKER-BROWSER-4 — OTel metrics emitted from production paths | L37-L38, L49-L49 | Counters + histogram on the loader/cache; asserted via real in-memory MeterProvider. |
PACKER-BROWSER-5 — Factory + singleton split (/singleton subpath) |
L36-L37, L51-L51 | createPackedResourceManager(opts) plus a ./singleton module-side export. |
PACKER-BROWSER-6 — Error hierarchy via @suaveplan/error |
L62-L63 | PACKER_BROWSER_CODES ≥ 6 codes; PackerBrowserError extends SuaveplanError. |
PACKER-BROWSER-7 — AbortSignal propagation on every async entrypoint |
(cross-cutting; mirrors PACKER-24) | All async public APIs accept signal? and check at every yield point. |
| PACKER-BROWSER-8 — i18n MessageResolver keys (no hard-coded English) | (cross-cutting; mirrors PACKER-25 i18n cluster) | PACKER_BROWSER_MESSAGES catalog + defaultPackerBrowserResolver. |
PACKER-BROWSER-9 — Injected Clock/Rng (no Date.now/Math.random outside runtime defaults) |
(cross-cutting; mirrors PACKER-25) | resolveRuntime + defaultClock/defaultRng. |
PACKER-BROWSER-10 — @suaveplan/retry on IO source-pull path |
(cross-cutting; mirrors PACKER-22) | Loader's fetch step routes through withRetry with single-attempt default. |
PACKER-BROWSER-11 — @suaveplan/feature-flags provider injected |
(cross-cutting; reserved for Cluster B/C) | No-op default; flags slot present so Cluster B can opt in without API churn. |
PACKER-BROWSER-12 — Subpath exports ./manager, ./service-worker, ./utils, ./singleton |
L78-L78, L90-L91 | Tree-shakeable surface for the §3 minimum. |
Requirements with no source line
| Requirement | Justification |
|---|---|
| PACKER-BROWSER-7 / -8 / -9 / -10 / -11 | Cross-cutting genesis baselines (CLAUDE.md §0a parity rules — every migrated package must adopt the runtime/observability/i18n/retry/flag scaffolding regardless of whether the audit explicitly called it out). |
Verification approach
Every requirement carries one or more **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/manager/manager.test.ts— covers PACKER-BROWSER-1, -5, -7, -11.src/cache/cache.test.ts— covers PACKER-BROWSER-1, -4, -9.src/loader/loader.test.ts— covers PACKER-BROWSER-1, -3, -4, -7, -10.src/service-worker/service-worker.test.ts— covers PACKER-BROWSER-2.src/service-worker/sw-shim.test.ts— covers PACKER-BROWSER-2.src/signature/signature.test.ts— covers PACKER-BROWSER-3.src/error-codes.test.ts— covers PACKER-BROWSER-6.src/i18n/messages.test.ts— covers PACKER-BROWSER-8.src/runtime/runtime.test.ts— covers PACKER-BROWSER-9.src/observability/observability.test.ts— covers PACKER-BROWSER-4.src/utils/register-service-worker.test.ts— covers PACKER-BROWSER-2.src/singleton.test.ts— covers PACKER-BROWSER-5.src/flags/flags.test.ts— covers PACKER-BROWSER-11.tests/e2e/sw.spec.ts— Playwright sweep proving SW registration + packed URL interception in a real Chromium tab.
Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
PackedResourceManager class (LRU, refCount, Blob bookkeeping) |
✅ | ⬆️ | Migrated; split into manager + cache + loader; now factory-constructed. |
Module-singleton packedResourceManager |
✅ | ⬆️ | Migrated to /singleton subpath; default . export no longer auto-constructs. |
| Service Worker interceptor (install / activate / fetch / message) | ✅ | ⬆️ | Migrated; now unit-tested via hand-rolled ServiceWorkerGlobalScope shim. |
| Lifecycle helpers (register/unregister/clearCache/getCacheStatus) | ✅ | ✅ | Migrated 1:1 under /utils. |
React hooks (usePackedResource*) |
✅ | 🚫 | Out of scope per audit recommendation: relocated to companion @suaveplan/react-packer package (assignment.md task #5). Excluding React from the runtime surface is the explicit audit ask. |
| Archive signature verification | 🚫 | ⬆️ | Added: verifyArchiveSignature via @suaveplan/crypto's ed25519.verify. Audit §3 ask. |
| OTel metrics (fetch/hit/miss/decode) | 🚫 | ⬆️ | Added: real MeterProvider-asserted counters + histogram. Audit §3 ask. |
MessageResolver-keyed errors |
🚫 | ⬆️ | Added: PACKER_BROWSER_MESSAGES catalog. Cross-cutting genesis baseline. |
Injected Clock / Rng |
🚫 | ⬆️ | Added: resolveRuntime mirrors packer's pattern. |
AbortSignal on every async entrypoint |
🚫 | ⬆️ | Added: load / preload / verify / SW fetch all accept and check signal. |
@suaveplan/retry on IO source-pull |
🚫 | ⬆️ | Added: loader routes fetch through withRetry. |
@suaveplan/feature-flags provider |
🚫 | ⬆️ | Added: no-op default; reserved for Cluster B/C feature gates. |
| Progressive streaming unpack | 🚫 | 🚫 | Cluster B (audit §4.1). |
| Range-request SW support | 🚫 | 🚫 | Cluster B (audit §4.2). |
| Adaptive cache sizing | 🚫 | 🚫 | Cluster B (audit §4.3). |
| Encrypted-archive pluggable keyring | 🚫 | 🚫 | Cluster B (audit §4.4). |
<link rel="preload"> hints |
🚫 | 🚫 | Cluster C (audit §4.5). |
| Versioned content-addressed URLs | 🚫 | 🚫 | Cluster C (audit §4.6). |
| Multi-origin SW helper | 🚫 | 🚫 | Cluster C (audit §4.7). |
| BackgroundFetch integration | 🚫 | 🚫 | Cluster C (audit §4.8). |
| Diagnostics dev overlay | 🚫 | 🚫 | Cluster C (audit §4.9). |
| Subpath splits beyond minimum five | 🚫 | 🚫 | Cluster C (audit §4.10). |
Impact
Affected dependents
Run bunx turbo ls --filter=...@suaveplan/packer-browser to enumerate. The
package is NEW on genesis with zero existing consumers:
- No dependents (verified by
grep -rln "@suaveplan/packer-browser" packages/ apps/).
Migration
Not applicable — no genesis consumer code exists to migrate. Future React
consumers should target the planned @suaveplan/react-packer companion
package (out of scope here).