diff --git a/packages/dev-server/src/runtime.ts b/packages/dev-server/src/runtime.ts index 7da289d0..2c7118f5 100644 --- a/packages/dev-server/src/runtime.ts +++ b/packages/dev-server/src/runtime.ts @@ -303,15 +303,32 @@ function shouldReportNotFound(pathname: string): boolean { ); } -export const PWA_CLIENT = `if ("serviceWorker" in navigator) { - var swUrl = "/sw.js"; +export function renderPwaClient(version = "2") { + return `if ("serviceWorker" in navigator) { + var swVersion = ${JSON.stringify(version)}; + var swUrl = "/sw.js?v=" + encodeURIComponent(swVersion); if (window.trustedTypes) { try { swUrl = window.trustedTypes.createPolicy("wrnexus-pwa", { createScriptURL: function(value) { return value; } }).createScriptURL(swUrl); } catch (_) {} } - addEventListener("load", () => navigator.serviceWorker.register(swUrl).catch(() => {})); + var reloading = false; + navigator.serviceWorker.addEventListener("controllerchange", function () { + var key = "wrnexus-pwa-controller-" + swVersion; + if (reloading || sessionStorage.getItem(key)) return; + reloading = true; + sessionStorage.setItem(key, "1"); + location.reload(); + }); + addEventListener("load", function () { + navigator.serviceWorker.register(swUrl, { updateViaCache: "none" }).then(function (registration) { + return registration.update(); + }).catch(function () {}); + }); }`; +} + +export const PWA_CLIENT = renderPwaClient(); export const PWA_DEV_CLEANUP_CLIENT = `if ("serviceWorker" in navigator && !sessionStorage.getItem("wrnexus-pwa-dev-cleaned")) { sessionStorage.setItem("wrnexus-pwa-dev-cleaned", "1"); @@ -1032,7 +1049,7 @@ export function createHandlers(deps: RuntimeDeps): Handlers { } if (url.pathname === "/__wrnexus/pwa.js" && pwaServiceWorkerEnabled) { return secure( - new Response(PWA_CLIENT, { + new Response(renderPwaClient(deps.assetVersion ?? "2"), { headers: { "content-type": "text/javascript; charset=utf-8", "cache-control": url.searchParams.has("v") diff --git a/packages/dev-server/test/performance-runtime.test.ts b/packages/dev-server/test/performance-runtime.test.ts index d92282ee..5662b99e 100644 --- a/packages/dev-server/test/performance-runtime.test.ts +++ b/packages/dev-server/test/performance-runtime.test.ts @@ -5,6 +5,9 @@ test("PWA registration is valid JavaScript and Trusted Types compatible", () => expect(() => new Bun.Transpiler({ loader: "js" }).transformSync(PWA_CLIENT)).not.toThrow(); expect(PWA_CLIENT).toContain('createPolicy("wrnexus-pwa"'); expect(PWA_CLIENT).toContain("createScriptURL(swUrl)"); + expect(PWA_CLIENT).toContain('register(swUrl, { updateViaCache: "none" })'); + expect(PWA_CLIENT).toContain("registration.update()"); + expect(PWA_CLIENT).toContain('addEventListener("controllerchange"'); }); test("development PWA cleanup removes stale WRNexus service workers and caches", () => {