-
Notifications
You must be signed in to change notification settings - Fork 0
messages
The control plane: pages and worker speak a small versioned contract over postMessage, with BroadcastChannel('io') for fan-out. Constants ride in double-meh-sw/contract.js (MESSAGES, CONTRACT_VERSION, CAPABILITIES).
const post = m => navigator.serviceWorker.ready.then(r => r.active.postMessage(m));
post({type: 'io:hello', library: 'double-meh'}); // announce a library — claims bundling
post({type: 'io:invalidate', pattern: '/api/users/'}); // evict the shared tier, all tabs notified
post({type: 'io:version'}); // → {current}
post({type: 'io:upgrade'}); // → skipWaiting()-
io:hello— capability handshake. The reply carries the contract version, the appversion, and the worker's capabilities. Alibraryclaim marks that client: client-wins — its requests are never SW-bundled; the page library owns its bundling (it has named bundles and explicit flush; the worker only sees anonymous fetches). -
io:invalidate {pattern}— evicts from the cache tier and broadcastsio:invalidated {pattern, evicted}on theiochannel so every tab can drop in-page mirrors. -
io:version/io:upgrade— the version-upgrade flow: query the active worker's version; triggerskipWaiting()when the page decides to move. -
io:fetch {id, url, method?, headers?, stream?}+ aMessageChannelport — the message transport: the worker performs the fetch (kept alive bywaitUntil, so it completes even if the page navigates away), seeds GETs into the cache tier, and replies on the port with{id, status, statusText, headers, body}— the body transferred, not copied. This is the navigation-surviving prefetch, and it works even on pages the worker doesn't control yet (messaging needs no controller; interception does).urlmust be absolute. A service worker's base is its own script URL, not the page's, so a relative URL resolves here against the script. The two coincide whenever the page and the script sit in the same directory — which is why this misbehaves only for pages outside it:/deep/page.htmlwith a root/sw.jsfetches/api/xwhere the page meant/deep/api/x, silently and with a 200. Only the page knows its own base, so the page half resolves before posting; the worker relies on that and has no way to repair it.The body is an ArrayBuffer by default, and the tier is seeded before the reply is posted — which is what lets a prefetch survive an immediate navigation. Ask for
stream: trueand, where the platform can transfer aReadableStream(announced as thestreamcapability in theio:helloreply), the reply instead carries the live body plusstream: true; the tier then drains its own tee branch behind the client. A worker that cannot transfer streams silently answers with the buffer, so asking is always safe. Negotiated per request, never automatic: v1 clients that don't ask see byte-identical behaviour, which is why this did not move the contract version.
Per-request metadata travels as x-io-* headers (the data plane) and is always stripped before anything reaches the network; the message contract is the ambient control plane. Two planes, no races.
See also: The assembly, Bundle window.
Start
Modules
Project