3.6 KiB
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
nullwhen BackgroundFetch is absent - Pluggable
backgroundFetchinjection for tests - OTel counters:
packed_resource_prefetch_success_totalandpacked_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
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);
// 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
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<BackgroundFetchRegistrationLike | null>handleBackgroundFetchSuccess(event, { caches?, cacheName?, observability }): Promise<void>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
prefetchPackedArchivenever reads thenavigatorglobal directly: production callers receive theBackgroundFetchManagerfromawait navigator.serviceWorker.readyand 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 underlyingfetch()rejects) ANDhandleBackgroundFetchFailure(for SW-side abort events) so dashboards show every failure regardless of which layer caught it.