# `sw-shim` ## Purpose `sw-shim.ts` provides `createServiceWorkerShim()`, a hand-rolled in-memory implementation of `ServiceWorkerGlobalScope` sufficient to register and exercise the production SW handlers in a Node test process. The shim is a **real implementation** — every method does the work it claims to do, no `vi.mock` is permitted by CLAUDE.md rule 2. Tests register the production `runPackedResourceServiceWorker` against the shim and dispatch `FetchEvent` / `ExtendableEvent` / `MessageEvent` instances built from `globalThis` constructors. ## Features - `addEventListener(type, handler)` registers the handler against a type-keyed `Map>`. - `dispatchEvent(event)` walks the matching set and invokes each handler synchronously, returning the standard `boolean`. - `caches` is a real `Map>`-backed `CacheStorage`; `open` / `match` / `put` / `keys` / `delete` all behave like the platform API. - `clients.claim()` and `skipWaiting()` resolve immediately and record their call counts on `record` so tests can assert lifecycle invariants like "skipWaiting was called once". - No `vi.fn` / `vi.mock` anywhere — the entire test surface observes real method behaviour via the `record` snapshot or the cache state. ## Usage: Basic ```ts import { createServiceWorkerShim, runPackedResourceServiceWorker, } from "@suaveplan/packer-browser"; const scope = createServiceWorkerShim(); runPackedResourceServiceWorker(scope); scope.dispatchEvent(new Event("install")); // scope.record.skipWaitingCalls === 1 ``` ## Usage: Advanced ```ts import { createServiceWorkerShim } from "@suaveplan/packer-browser"; const scope = createServiceWorkerShim(); const cache = await scope.caches.open("ephemeral"); await cache.put("/a", new Response("payload")); const hit = await cache.match("/a"); const text = await hit?.text(); // text === "payload" // Dispatch into a custom listener scope.addEventListener("custom", (ev) => observed.push(ev.type)); scope.dispatchEvent(new Event("custom")); ``` ## API Reference - `createServiceWorkerShim(): ServiceWorkerShim`. - `ServiceWorkerShim`: - `addEventListener(type, handler)` / `dispatchEvent(event)`. - `skipWaiting(): Promise` (records call). - `clients: { claim(): Promise }` (records call). - `caches: ShimCacheStorage`. - `record: ShimRecord` — `{ skipWaitingCalls, clientsClaimCalls }`. ## Implementation Notes The shim deliberately does NOT reproduce browser quirks like Cache Storage partitioning by origin, the FetchEvent network fall-through, or the per-message-port `start()` requirement. Those behaviours are covered by the Playwright E2E suite, not the unit tests; the unit tests are concerned with the SW handler logic ("a packed URL intercepted by the production code path produces a cache write") which the shim faithfully exposes. Tests construct `Event`, `Request`, and `Response` objects via `globalThis` (happy-dom in vitest) rather than reinventing them inside the shim.