From e410f09c936d2b8660bdf89e90cc196f7fca8590 Mon Sep 17 00:00:00 2001 From: Spencer Flagg Date: Sun, 3 May 2026 14:00:59 +0200 Subject: [PATCH] fix: bump SW version and switch to network-first caching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VERSION was stuck at raycer-v1 so mobile PWAs never picked up the streak fix (cache-first served the stale stats.js forever). - Bump to raycer-v2 → triggers install, purges old caches - Switch same-origin assets from cache-first to network-first so future deploys propagate immediately; cache is only used as an offline fallback - Simplify to a single SHELL_CACHE (no separate runtime cache) - Cross-origin vendor assets (Dexie CDN) stay cache-first --- frontend/public/sw.js | 54 +++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/frontend/public/sw.js b/frontend/public/sw.js index 591d64a..39239fd 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -1,14 +1,13 @@ /* raycer service worker - * - app shell: cache-first - * - /api/*: network-first with no cache fallback (data lives in Dexie) - * - dexie CDN module: cache-first (so the app boots offline) + * - app shell: network-first (so deploys propagate immediately), cache fallback for offline + * - /api/*: network-only (data lives in Dexie) + * - cross-origin (Dexie CDN): cache-first (immutable vendor assets) */ -const VERSION = 'raycer-v1'; +const VERSION = 'raycer-v2'; const SHELL_CACHE = `${VERSION}-shell`; -const RUNTIME_CACHE = `${VERSION}-runtime`; -const SHELL = [ +const PRECACHE = [ '/', '/index.html', '/manifest.webmanifest', @@ -28,7 +27,7 @@ self.addEventListener('install', (event) => { event.waitUntil( (async () => { const cache = await caches.open(SHELL_CACHE); - await cache.addAll(SHELL); + await cache.addAll(PRECACHE); self.skipWaiting(); })() ); @@ -40,7 +39,7 @@ self.addEventListener('activate', (event) => { const keys = await caches.keys(); await Promise.all( keys - .filter((k) => !k.startsWith(VERSION)) + .filter((k) => k !== SHELL_CACHE) .map((k) => caches.delete(k)) ); await self.clients.claim(); @@ -54,7 +53,7 @@ self.addEventListener('fetch', (event) => { const url = new URL(req.url); - // API: network-first, never cache + // API: network-only, never cache if (url.pathname.startsWith('/api/')) { event.respondWith( fetch(req).catch( @@ -67,39 +66,24 @@ self.addEventListener('fetch', (event) => { return; } - // Same-origin SPA navigations: serve cached shell, fall back to network - if (req.mode === 'navigate') { - event.respondWith( - (async () => { - try { - const fresh = await fetch(req); - const cache = await caches.open(SHELL_CACHE); - cache.put('/index.html', fresh.clone()).catch(() => {}); - return fresh; - } catch { - const cached = await caches.match('/index.html'); - if (cached) return cached; - return new Response('offline', { status: 503 }); - } - })() - ); - return; - } - - // Same-origin assets: cache-first + // Same-origin (app shell + assets): network-first, cache fallback for offline if (url.origin === self.location.origin) { event.respondWith( (async () => { - const cached = await caches.match(req); - if (cached) return cached; try { const fresh = await fetch(req); if (fresh.ok) { - const cache = await caches.open(RUNTIME_CACHE); + const cache = await caches.open(SHELL_CACHE); cache.put(req, fresh.clone()).catch(() => {}); } return fresh; } catch { + const cached = await caches.match(req); + if (cached) return cached; + if (req.mode === 'navigate') { + const shell = await caches.match('/index.html'); + if (shell) return shell; + } return new Response('offline', { status: 503 }); } })() @@ -107,7 +91,7 @@ self.addEventListener('fetch', (event) => { return; } - // Cross-origin (e.g. Dexie CDN): cache-first + // Cross-origin (e.g. Dexie CDN): cache-first (immutable vendor assets) event.respondWith( (async () => { const cached = await caches.match(req); @@ -115,12 +99,12 @@ self.addEventListener('fetch', (event) => { try { const fresh = await fetch(req); if (fresh.ok) { - const cache = await caches.open(RUNTIME_CACHE); + const cache = await caches.open(SHELL_CACHE); cache.put(req, fresh.clone()).catch(() => {}); } return fresh; } catch { - return cached || new Response('offline', { status: 503 }); + return new Response('offline', { status: 503 }); } })() );