6.4 KiB
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-facerule and waits fordocument.fonts.load. Returns the generic shape plusfontLoaded: boolean. - ADDED:
PackedResourceProvider— React context that supplies aPackedResourceManagerand (optionally) aMessageResolverto 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 upstreamenterprise/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 (allowedMimePrefixesenforcement, 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-faceinjection, cleanup,fontLoadedflag,document.fonts.loadfailure 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,MessageResolverpropagation).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/nullreturning no matches at proposal time).