first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
# `prefetch` — BackgroundFetch integration
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<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.