first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# `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
|
||||
|
||||
```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<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.
|
||||
Reference in New Issue
Block a user