# `prefetch` — BackgroundFetch integration _(no submodules)_ ## Purpose The `prefetch` module wraps the Chromium `BackgroundFetchManager` API so an application can preload an archive on idle without holding the page open. The page side calls `prefetchPackedArchive(url, opts)` to kick off the registration; the Service Worker listens for `backgroundfetchsuccess` and calls `handleBackgroundFetchSuccess` to drain the records into the same cache the rest of `@suaveplan/packer-browser` uses. Failures and aborts route through `handleBackgroundFetchFailure`. These helpers are PACKER-BROWSER-20's production surface. They are feature-detected — if the browser does not implement BackgroundFetch (`navigator.serviceWorker.ready.backgroundFetch` is undefined and no `backgroundFetch` is injected), `prefetchPackedArchive` resolves with `null` and the caller's flow continues with a regular fetch. ## Features - API feature-detection — graceful `null` when BackgroundFetch is absent - Pluggable `backgroundFetch` injection for tests - OTel counters: `packed_resource_prefetch_success_total` and `packed_resource_prefetch_failure_total` - Cache-drain helper writes every record's response into the configured cache (default `packed-resources-v1`) so the SW's existing fetch handler hits cached entries on the next request ## Usage: Basic ```ts import { prefetchPackedArchive } from "@suaveplan/packer-browser/prefetch"; // Page side: const reg = await prefetchPackedArchive("/big.bundle.asset"); if (reg !== null) console.log("BG fetch started:", reg.id); ``` ```ts // Service worker side: import { handleBackgroundFetchSuccess } from "@suaveplan/packer-browser/prefetch"; self.addEventListener("backgroundfetchsuccess", (event) => { event.waitUntil( handleBackgroundFetchSuccess(event, { observability, cacheName: "packed-resources-v1", }) ); }); ``` ## Usage: Advanced ```ts import { handleBackgroundFetchFailure, handleBackgroundFetchSuccess, prefetchPackedArchive, } from "@suaveplan/packer-browser/prefetch"; const reg = await prefetchPackedArchive("/heavy.bundle.asset", { id: "heavy-asset", options: { title: "Pre-loading heavy asset", icons: [{ src: "/icon-192.png", sizes: "192x192" }], }, observability, }); // Failure listener — counts every abort. self.addEventListener("backgroundfetchfail", (event) => handleBackgroundFetchFailure(event, { observability }) ); ``` ## API Reference - `prefetchPackedArchive(archiveUrl, opts?): Promise` - `handleBackgroundFetchSuccess(event, { caches?, cacheName?, observability }): Promise` - `handleBackgroundFetchFailure(event, { observability }): void` Every helper accepts a fully injectable cache storage / background fetch manager so tests can exercise real failure modes (cache write rejection, missing API, registration with zero records) without any mocking. ## Implementation Notes - `prefetchPackedArchive` never reads the `navigator` global directly: production callers receive the `BackgroundFetchManager` from `await navigator.serviceWorker.ready` and pass it in. This keeps the helper safe to call from non-DOM contexts (workers, tests). - The success handler clones every response before writing to the cache so the body stream is consumed exactly once. - Failure counter increments are emitted from BOTH `prefetchPackedArchive` (when the underlying `fetch()` rejects) AND `handleBackgroundFetchFailure` (for SW-side abort events) so dashboards show every failure regardless of which layer caught it.