# `service-worker` _(no submodules)_ ## 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` — 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.