Expand description
The service worker: opt-in, per application, and retired everywhere else.
§The setting
A worker is an application’s choice, made once at start, before the first
page renders and before /sw.js is served:
use forma_server::sw::{self, ServiceWorker};
// Ledger: pages with JS entries register /sw.js, which serves the built worker.
// After loading and validating the manifest, before binding:
sw::configure_for_manifest(ServiceWorker::for_app("ledger")?.enabled(), manifest)?;Left unconfigured, the setting is ServiceWorker::off: no page registers
a worker, and /sw.js serves the retiring worker in place of whatever
the build embedded. A browser that installed a worker from an earlier
release checks /sw.js for an update on its next visits, receives the
retiring worker, which deletes Forma’s caches and unregisters, and from then
on loads every page and asset from the network. This is the kill switch
that keeps turning the worker on a two-way door; it is served through 3.x.
Static SSR pages emit no registration even when this setting is enabled. A static-only app therefore does not newly install a worker. Existing clients may still check the unchanged endpoint through the browser’s worker lifecycle.
Rolling back an application’s worker is turning .enabled() off and
keeping the name: its retiring worker then deletes that application’s own
caches too. Turning it back on re-installs it on the next JS-enabled page visit.
§The cache-name contract (forma-server 0.2.2, @getforma/build 0.3.1)
The build writes the production worker and the cache names; forma-server writes the registration and the retiring worker. They meet here:
- Application name: 1 to 63 of
a-z,0-9and-, starting with a letter or digit; the same string in the build’s configuration and inServiceWorker::for_app. - Cache name:
forma:<app>:<build>, where<build>is the build’s identity as the build writes it (today the first 12 hex digits ofbuild) and carries no:. Theforma:prefix (a colon, not v1’s hyphen) is new, so no v1 or 0.3.0 worker, all of which deleteforma-names, ever deletes one. - v1’s names (builds up to 0.3.0):
forma-dev,forma-and 12 lowercase hex digits, and either with-on-demand-v1; exactly^forma-(?:dev|[0-9a-f]{12})(?:-on-demand-v1)?$. They cannot be traced to an application, so every Forma worker retires them. - The production worker of application
Xreads only its own cache, and on activation deletes v1’s names andforma:X:caches other than its own; never another application’s (forma:Y:,forma:Xy:) and never a cache outside these patterns. It calls neitherskipWaiting()norclients.claim(): a new worker waits until the previous one controls no tab. - The retiring worker deletes v1’s names and, for a named application,
forma:X:caches; then unregisters. It answers no request. It does callskipWaiting(), as a kill switch must: an open tab of the old build is handed to it at once and loses the old worker’s cache, so its requests go to the network (and to the skew path if its build’s assets are gone). That is the trade made on purpose: a planted entry stops being served from the navigation after the browser’s update check, not when the last old tab closes.
§One origin, one setting
/sw.js and its scope / belong to the origin, so every process serving
one origin must agree on the setting: during a rolling deploy from 0.2.1 a
browser can alternate between registering and retiring until every
instance runs the new release; and of two applications behind one proxy on
one origin, only one can enable the worker, under its own name.
tests/js/retiring_worker.mjs holds the contract’s vectors as a table;
the build’s tests are to share them.
Structs§
- Already
Configured configurewas called after the setting was fixed: by an earlierconfigure, or by the first page or/sw.jsrequest, which read the default.- AppName
Error - An application name the cache-name contract cannot carry.
- Service
Worker - An application’s service-worker setting. See the module docs.
Enums§
- Configure
Error - A refused startup setting; the current setting is never changed on error.
Functions§
- configure
- Configure an off worker at startup. Enabled workers require
configure_for_manifestso application identity is checked before binding. - configure_
for_ manifest - Validate the manifest and match an enabled worker’s application before fixing
the setting. Call at startup and refuse to bind on error. Validation does not
read
configured()or seal an unconfigured process’s default. - configured
- This process’s setting: what
configureset, orServiceWorker::off. Reading it fixes it. - serve_
sw - Axum handler: serve
/sw.jswith no-cache headers.