From 59fc7cabc6908752fd25f3b1f1939a98052ce373 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 3 Jun 2026 18:35:34 -0400 Subject: [PATCH] fix(service-worker): add skipWaiting + clients.claim for immediate activation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without these two calls, a new service worker installs in the background but sits in 'waiting' state until every tab running the old version is closed. Users who leave idaa.org open all day (common for IDAA members in the iframe) would run the old JS bundle for hours or days after a fix is deployed, with no indication that an update exists. skipWaiting() — new SW activates immediately after install instead of waiting clients.claim() — new SW takes control of all open tabs right away This is the most likely root cause of "can't reproduce in testing but users keep seeing the error": developers refresh/close tabs constantly, end users don't. The old broken code kept running in their long-lived browser sessions. Co-Authored-By: Claude Sonnet 4.6 --- src/service-worker.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/service-worker.js b/src/service-worker.js index a8006217..fa352b39 100644 --- a/src/service-worker.js +++ b/src/service-worker.js @@ -17,6 +17,14 @@ self.addEventListener('install', (event) => { } event.waitUntil(addFilesToCache()); + + // Skip the waiting phase so the new service worker activates immediately + // instead of waiting for all existing tabs to close first. + // WHY: Without this, a user who leaves idaa.org open all day (common for + // IDAA members) will run the old JS bundle for hours after a fix is deployed. + // The new SW is installed in the background but sits idle until every tab + // using the old version is closed — which may never happen in practice. + self.skipWaiting(); }); self.addEventListener('activate', (event) => { @@ -28,6 +36,10 @@ self.addEventListener('activate', (event) => { } event.waitUntil(deleteOldCaches()); + + // Take control of all open tabs immediately after activation so they use + // the new service worker (and new cached assets) without needing a reload. + self.clients.claim(); }); self.addEventListener('fetch', (event) => {