# 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 `EventEmitter` events for `load` / `cacheHit` / `cacheMiss` / `cacheEvict` / `error`. - ADDED: `createPackedResourceManager(options?)` — explicit factory; the default `.` export does NOT construct a singleton at import time. - ADDED: `packedResourceManager` — module-singleton, hosted behind the `/singleton` subpath export so the default surface stays import-cost-free. - ADDED: `PackedResourceCache` + `PackedResourceLoader` — the two collaborators behind the manager. Available via `/manager` subpath for consumers that need surgical imports. - ADDED: `runPackedResourceServiceWorker(scope)` + handlers for `install` / `activate` / `fetch` / `message`, exposed via `/service-worker` subpath along with the hand-rolled `ServiceWorkerGlobalScope` shim (`createServiceWorkerShim`) used by the unit-test harness. - ADDED: `registerPackedResourceServiceWorker` / `unregisterPackedResourceServiceWorker` / `clearServiceWorkerCache` / `getServiceWorkerCacheStatus`, via `/utils` subpath. - ADDED: `verifyArchiveSignature(manifestBytes, signature, publicKey)` — Ed25519 verification gate run before any packed resource is unpacked. - ADDED: `PACKER_BROWSER_CODES` + `PackerBrowserError` (extends `SuaveplanError`) — ≥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 exposing `packed_resource_fetch_total`, `packed_resource_cache_hits_total`, `packed_resource_cache_miss_total` counters and `packed_resource_decode_ms` histogram. - ADDED: `PACKER_BROWSER_MESSAGES` + `defaultPackerBrowserResolver` — English i18n catalog keyed `packer-browser.error..`. - 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-packer` companion (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/packer` surface is supported on the loader; the **pluggable keyring** is what Cluster B adds.) - `` 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//.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). | | `` 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).