diff --git a/app/docs.test.ts b/app/docs.test.ts
index ddc5179b..cc48add1 100644
--- a/app/docs.test.ts
+++ b/app/docs.test.ts
@@ -135,7 +135,11 @@ test("site includes core guides and production configuration", () => {
expect(config).toContain(
'"style-src": ["\'self\'", "\'unsafe-inline\'", "https://fonts.googleapis.com"]',
);
- expect(config).toContain('cacheName: "wrnexus-pwa-v2"');
+ expect(config).toContain('cacheName: "wrnexus-pwa-v3"');
+ expect(config).toContain("head: ''");
+ const recovery = readFileSync(join(root, "public", "pwa-recover.js"), "utf8");
+ expect(recovery).toContain('updateViaCache: "none"');
+ expect(recovery).toContain("registration.update()");
expect(config).toContain('pattern: "^https://wrnexusjs\\\\.dev/"');
});
diff --git a/public/pwa-recover.js b/public/pwa-recover.js
new file mode 100644
index 00000000..fc9283e3
--- /dev/null
+++ b/public/pwa-recover.js
@@ -0,0 +1,37 @@
+/* global location, navigator, sessionStorage, window */
+(function () {
+ if (!("serviceWorker" in navigator)) return;
+
+ var version = "docs-v3";
+ var workerUrl = "/sw.js?v=" + encodeURIComponent(version);
+ if (window.trustedTypes) {
+ try {
+ workerUrl = window.trustedTypes
+ .createPolicy("wrnexus-pwa-recovery", {
+ createScriptURL: function (value) {
+ return value;
+ },
+ })
+ .createScriptURL(workerUrl);
+ } catch {
+ // A previously-created policy can make creation fail; the same-origin
+ // string remains valid when Trusted Types enforcement is not active.
+ }
+ }
+
+ var reloading = false;
+ navigator.serviceWorker.addEventListener("controllerchange", function () {
+ var key = "wrnexus-pwa-controller-" + version;
+ if (reloading || sessionStorage.getItem(key)) return;
+ reloading = true;
+ sessionStorage.setItem(key, "1");
+ location.reload();
+ });
+
+ navigator.serviceWorker
+ .register(workerUrl, { updateViaCache: "none" })
+ .then(function (registration) {
+ return registration.update();
+ })
+ .catch(function () {});
+})();
diff --git a/wrnexus.config.ts b/wrnexus.config.ts
index 040d25d9..6441c442 100644
--- a/wrnexus.config.ts
+++ b/wrnexus.config.ts
@@ -1,6 +1,9 @@
import type { AppConfig } from "@wrnexus/styles";
const config: AppConfig = {
+ // Repair clients that are still controlled by an older cached service worker.
+ // This can be removed after every preview client has advanced beyond v3.
+ head: '',
mobile: {
enabled: true,
appId: "com.example.wrnexusjs",
@@ -15,7 +18,7 @@ const config: AppConfig = {
pwa: {
name: "WRNexusJS Documentation",
shortName: "WRNexusJS Docs",
- cacheName: "wrnexus-pwa-v2",
+ cacheName: "wrnexus-pwa-v3",
runtimeCaching: [
{
pattern: "^https://wrnexusjs\\.dev/",