const CACHE = 'tanken-v7'; const STATIC = ['/', '/style.css', '/app.js', '/coverage-outlines.js', '/web-haptics.js']; self.addEventListener('install', (e) => { e.waitUntil(caches.open(CACHE).then((c) => c.addAll(STATIC))); self.skipWaiting(); }); self.addEventListener('activate', (e) => { e.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))) ) ); self.clients.claim(); }); self.addEventListener('fetch', (e) => { const url = new URL(e.request.url); if (url.pathname.startsWith('/api/')) { // Network-first for API calls e.respondWith( fetch(e.request) .then((res) => { const clone = res.clone(); caches.open(CACHE).then((c) => c.put(e.request, clone)); return res; }) .catch(() => caches.match(e.request)) ); } else if (e.request.mode === 'navigate') { // Tab deep links (/stats, /history, …) are client-routed on top of the // app shell. Offline, the exact path isn't cached — fall back to the // cached shell ("/") so installed/offline deep links still open. e.respondWith( fetch(e.request) .then((res) => { const clone = res.clone(); caches.open(CACHE).then((c) => c.put(e.request, clone)); return res; }) .catch(() => caches.match(e.request).then((hit) => hit || caches.match('/'))) ); } else { // Network-first for static assets // Always fetch fresh content; fall back to cache when offline e.respondWith( fetch(e.request) .then((res) => { const clone = res.clone(); caches.open(CACHE).then((c) => c.put(e.request, clone)); return res; }) .catch(() => caches.match(e.request)) ); } });