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

3.0 KiB

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<string, Set<EventListener>>.
  • dispatchEvent(event) walks the matching set and invokes each handler synchronously, returning the standard boolean.
  • caches is a real Map<string, Map<url, Response>>-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

import {
    createServiceWorkerShim,
    runPackedResourceServiceWorker,
} from "@suaveplan/packer-browser";

const scope = createServiceWorkerShim();
runPackedResourceServiceWorker(scope);
scope.dispatchEvent(new Event("install"));
// scope.record.skipWaitingCalls === 1

Usage: Advanced

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<void> (records call).
    • clients: { claim(): Promise<void> } (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.