fix(pwa): network-first navigation so a new deploy loads online immediately
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
The C1 service worker served the precached shell for every navigation, even online, so a new deploy only reached a client after the worker updated its precache in the background (a one-launch lag): a cold online start showed the old version until then, and only a reinstall forced it fresh. - sw.ts: navigations are now network-first — fetch the fresh (no-cache) shell from the server on each online launch (it references the new hashed assets, fetched fresh), with a 3s timeout falling back to the precached shell when the network is unreachable or too slow. Only the tiny HTML is re-fetched; the immutable hashed assets stay cache-first and re-download only when their hash (the version) changes. Offline cold-launch still works via the fallback. - webui.go: serve sw.js with Cache-Control: no-cache so the browser reliably detects a new deploy's worker (regression-tested). Online launch is contour-verified (the mock e2e disables the SW); e2e 196. No new deps — the network-first handler is hand-written (~12 lines).
This commit is contained in:
@@ -71,6 +71,11 @@ func handlerFor(content fs.FS, stripPrefix, indexName string) http.Handler {
|
||||
// app (notably a relaunched Telegram Mini App) is a cache hit, not a re-download.
|
||||
if strings.HasPrefix(name, "assets/") {
|
||||
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
|
||||
} else if name == "sw.js" {
|
||||
// The service worker must be revalidated on every load so a new deploy's worker (and its
|
||||
// fresh precache manifest) is picked up promptly; a cached sw.js would strand clients on
|
||||
// the old build. The worker's network-first navigation then serves the fresh shell online.
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
}
|
||||
files.ServeHTTP(w, r)
|
||||
})
|
||||
|
||||
@@ -94,6 +94,10 @@ func TestHandlerServesServiceWorkerAsJavaScript(t *testing.T) {
|
||||
if ct := resp.Header.Get("Content-Type"); !strings.HasPrefix(ct, "text/javascript") {
|
||||
t.Errorf("Content-Type = %q, want text/javascript", ct)
|
||||
}
|
||||
// Revalidated on every load so a new deploy's worker (and its fresh precache) is picked up.
|
||||
if cc := resp.Header.Get("Cache-Control"); cc != "no-cache" {
|
||||
t.Errorf("sw.js Cache-Control = %q, want no-cache", cc)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandlerFallsBackToShellForUnknownManifestPath guards the trap: a manifest/sw path NOT emitted
|
||||
|
||||
Reference in New Issue
Block a user