129 lines
6.4 KiB
Markdown
129 lines
6.4 KiB
Markdown
# Add `@suaveplan/react-packer` — React 19 companion for `@suaveplan/packer-browser`
|
|
|
|
**Package**: `@suaveplan/react-packer`
|
|
**Status**: PROPOSED
|
|
**Type**: new-package
|
|
|
|
- **Design**: ./design.md
|
|
- **Spec delta**: ./specs/react-packer/spec.md
|
|
|
|
## Why
|
|
|
|
`@suaveplan/packer-browser` (genesis Cluster A) ships the manager, cache,
|
|
loader, signature verification, and Service-Worker interceptor with **no
|
|
React runtime dep**. Upstream `packer-browser@1.0.17` carried the React
|
|
hooks (`usePackedResource`, `usePackedImage`, `usePackedAudio`,
|
|
`usePackedVideo`, `usePackedFont`) inside the same package, forcing every
|
|
non-React consumer (server bundles, R3F apps that do their own loading,
|
|
Web-Worker scripts) to ship React via transitive dependency. Upstream
|
|
`enterprise/packer-browser.md` §3.3 specifically calls for splitting the
|
|
hooks into a React companion package; that split has been deferred to
|
|
`@suaveplan/react-packer`, this change.
|
|
|
|
This package provides the hooks at genesis quality — `AbortSignal` on
|
|
every action, `MessageResolver` plumbed through, `PackedResourceProvider`
|
|
React context for explicit dependency injection of a
|
|
`PackedResourceManager`, sensible fallback to the
|
|
`@suaveplan/packer-browser/singleton` for adopters that want zero-config,
|
|
and 100/100/100/100 coverage against a real (non-mock) manager backed by
|
|
real `Response` + `Blob` fixtures.
|
|
|
|
## What changes
|
|
|
|
This is a brand-new package — every export is additive. No changes to
|
|
`@suaveplan/packer-browser`'s surface.
|
|
|
|
- ADDED: `usePackedResource(packedUrl, opts?)` — generic hook returning
|
|
`{ url, blob, loading, error, status, reload }`.
|
|
- ADDED: `usePackedImage(packedUrl, opts?)` — image-tuned defaults (cache
|
|
on, `allowedMimePrefixes = ["image/"]`).
|
|
- ADDED: `usePackedAudio(packedUrl, opts?)` — audio-tuned defaults.
|
|
- ADDED: `usePackedVideo(packedUrl, opts?)` — video-tuned defaults (cache
|
|
off — videos are large).
|
|
- ADDED: `usePackedFont(packedUrl, fontFamily?, opts?)` — injects a
|
|
`@font-face` rule and waits for `document.fonts.load`. Returns the
|
|
generic shape plus `fontLoaded: boolean`.
|
|
- ADDED: `PackedResourceProvider` — React context that supplies a
|
|
`PackedResourceManager` and (optionally) a `MessageResolver` to every
|
|
hook in its subtree.
|
|
- ADDED: `usePackedResourceManager()` — internal hook that resolves the
|
|
manager from context with a documented fallback to
|
|
`@suaveplan/packer-browser/singleton`.
|
|
- ADDED: `REACT_PACKER_CODES` + `ReactPackerError` — stable error code
|
|
registry (`LOAD_FAILED`, `FONT_LOAD_FAILED`, `ABORTED`).
|
|
|
|
## Out of scope
|
|
|
|
- Service-Worker registration UI — adopters use
|
|
`@suaveplan/packer-browser`'s lifecycle helpers directly.
|
|
- A `<PackedImage>` / `<PackedVideo>` / `<PackedAudio>` React component
|
|
wrapper. The hooks are the public surface; consumers compose their own
|
|
presentational components. This keeps the package focused on the carve
|
|
upstream `enterprise/packer-browser.md` §3.3 explicitly asked for.
|
|
|
|
## Source mapping
|
|
|
|
This change derives from upstream
|
|
`../suaveplan/packages/browser/packer-browser/src/hooks/usePackedResource.ts`
|
|
and `../suaveplan/enterprise/packer-browser.md` §3.3 ("Move React hooks
|
|
into a new React companion package"). The genesis rewrite preserves the
|
|
upstream public symbol names and per-variant defaults while bringing
|
|
every requirement to enterprise quality (no module singleton, real
|
|
`AbortSignal`, real `MessageResolver`, real error hierarchy).
|
|
|
|
| Requirement | Source lines | Notes |
|
|
|---|---|---|
|
|
| REACT-PACKER-1 (`usePackedResource`) | upstream `usePackedResource.ts` L22-L87 | Rewritten to consume injected manager; adds AbortSignal + error-code wrapping. |
|
|
| REACT-PACKER-2 (`usePackedImage`) | upstream L89-L101 | Same defaults (cache on); adds `allowedMimePrefixes`. |
|
|
| REACT-PACKER-3 (`usePackedAudio`) | upstream L103-L115 | Same defaults (cache on). |
|
|
| REACT-PACKER-4 (`usePackedVideo`) | upstream L117-L129 | Same defaults (cache off). |
|
|
| REACT-PACKER-5 (`usePackedFont`) | upstream L131-L198 | Preserves `@font-face` injection + cleanup. |
|
|
| REACT-PACKER-6 (`PackedResourceProvider`) | enterprise/packer-browser.md L42-L51 ("Move React hooks…") | Adds explicit DI surface that upstream did not have (module singleton only). |
|
|
| REACT-PACKER-7 (error hierarchy) | enterprise/packer-browser.md §63 | Standard genesis bar; upstream raised plain `Error`. |
|
|
| REACT-PACKER-8 (singleton fallback) | enterprise/packer-browser.md L51 | Adopters who omit `<PackedResourceProvider>` fall back to `@suaveplan/packer-browser/singleton`. |
|
|
|
|
### Requirements with no source line
|
|
|
|
None — every requirement either ports an upstream behaviour or implements
|
|
an enterprise gap explicitly named in `enterprise/packer-browser.md`.
|
|
|
|
## Verification approach
|
|
|
|
Every requirement carries a `**Verified by:**` line pointing at a concrete
|
|
`it(...)` title in the planned test files.
|
|
|
|
- `src/hooks/use-packed-resource/use-packed-resource.test.tsx` — covers
|
|
REACT-PACKER-1 (loading, success, error, reload, abort on unmount,
|
|
abort on URL change, signal propagation).
|
|
- `src/hooks/use-packed-image/use-packed-image.test.tsx` — covers
|
|
REACT-PACKER-2 (`allowedMimePrefixes` enforcement, image defaults).
|
|
- `src/hooks/use-packed-audio/use-packed-audio.test.tsx` — covers
|
|
REACT-PACKER-3 (audio defaults).
|
|
- `src/hooks/use-packed-video/use-packed-video.test.tsx` — covers
|
|
REACT-PACKER-4 (video defaults including cache off).
|
|
- `src/hooks/use-packed-font/use-packed-font.test.tsx` — covers
|
|
REACT-PACKER-5 (`@font-face` injection, cleanup, `fontLoaded` flag,
|
|
`document.fonts.load` failure path).
|
|
- `src/provider/packed-resource-provider/packed-resource-provider.test.tsx`
|
|
— covers REACT-PACKER-6 + REACT-PACKER-8 (context wiring, singleton
|
|
fallback when no provider, `MessageResolver` propagation).
|
|
- `src/error-codes/error-codes.test.ts` — covers REACT-PACKER-7 (code
|
|
registry, `ReactPackerError.is(...)` brand check).
|
|
|
|
## Migration impact
|
|
|
|
None — additive only. `@suaveplan/packer-browser` is not modified.
|
|
Consumers that previously imported hooks from upstream `packer-browser`
|
|
(none on genesis — packer-browser shipped without hooks per its own
|
|
proposal) migrate by adding `@suaveplan/react-packer` as a dep and
|
|
importing from there.
|
|
|
|
## Impact
|
|
|
|
### Affected dependents
|
|
|
|
Run `bunx turbo ls --filter=...@suaveplan/react-packer` to enumerate.
|
|
This is a new package with zero existing dependents on genesis:
|
|
|
|
- No dependents (verified by `grep -rln "@suaveplan/react-packer" packages/ apps/ 2>/dev/null` returning no matches at proposal time).
|