Files
skills/docs/foundation/data/packer-browser/src/prefetch/prefetch.md
2026-05-29 08:20:56 +01:00

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 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

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

  • 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.