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,90 @@
# `service-worker`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`service-worker.ts` is the production Service Worker entrypoint
exposed via the `/service-worker` subpath. Its only public function,
`runPackedResourceServiceWorker(scope, opts?)`, registers four
handlers — `install`, `activate`, `fetch`, and `message` — against
the supplied scope. In production the scope is the real
`ServiceWorkerGlobalScope`; in tests it is the in-memory shim from
`sw-shim.ts`. Cluster A intentionally caches the **packed** bytes in
the SW (un-tagged) and lets the `PackedResourceManager` on the page
do the unpack; Cluster B adds SW-side unpack with range support.
## Features
- `install` → `scope.skipWaiting()` so the new worker activates on
the first navigation rather than waiting for every tab to close.
- `activate` → deletes every cache whose name is not the configured
`cacheName`, then calls `scope.clients.claim()` so existing tabs
start routing through the new worker immediately.
- `fetch` → matches `packedPatterns` (default
`[/\.packed$/, /\?packed=true/]`) and intercepts only those URLs.
Cache hits are served verbatim; misses trigger an upstream fetch
and a cache write tagged `X-Unpacked: true`.
- `message` → handles `CACHE_CLEAR` (deletes the cache) and
`CACHE_STATUS` (replies with `{ cacheSize, urls }` on
`event.ports[0]`).
- Injectable `fetch` for tests — no `vi.mock` required.
## Usage: Basic
In your production SW bundle (`service-worker.js`):
```ts
import { runPackedResourceServiceWorker } from "@suaveplan/packer-browser/service-worker";
runPackedResourceServiceWorker(
self as unknown as ServiceWorkerGlobalScope
);
```
## Usage: Advanced
```ts
import {
createServiceWorkerShim,
runPackedResourceServiceWorker,
} from "@suaveplan/packer-browser/service-worker";
const scope = createServiceWorkerShim();
runPackedResourceServiceWorker(scope, {
cacheName: "test-cache",
packedPatterns: [/^https:\/\/cdn\.example\.com\/.+\.packed/],
fetch: async (url) => new Response(testBytes),
});
const request = new Request("https://cdn.example.com/a.packed");
const fetchEvent = makeFetchEventLike(request);
scope.dispatchEvent(fetchEvent);
const response = await fetchEvent.responsePromise;
// response.headers.get("X-Unpacked") === "true"
```
## API Reference
- `runPackedResourceServiceWorker(scope, opts?: RunServiceWorkerOptions): void`.
- `RunServiceWorkerOptions`:
- `cacheName?: string` — default `"packed-resources-v1"`.
- `packedPatterns?: ReadonlyArray<RegExp>` — default
`[/\.packed$/, /\?packed=true/]`.
- `fetch?: FetchLike` — default `globalThis.fetch`.
- Errors raised inside the fetch handler are `PackerBrowserError`
with `LOAD_FAILED` (network) or `RESOURCE_NOT_FOUND` (non-2xx)
codes; they propagate out of `respondWith` as a rejected promise.
## Implementation Notes
The SW deliberately does not call into `PackedResourceManager` from
inside its scope — the manager owns Blob URL allocation, which is a
page-scope concern (a SW that calls `URL.createObjectURL` produces
URLs the page cannot resolve). The SW's role is purely to cache the
**packed** bytes and short-circuit re-fetch. The page-side manager
sees the cached response (or a fresh network response) and runs the
unpack. This split keeps the SW small enough to register on a flaky
connection without bundling the entire Unpacker pipeline.