const CACHE_NAME = 'trackpull-v1'; const APP_SHELL = [ '/', '/static/favicon.ico', '/static/manifest.json', '/static/icons/icon-192x192.png', '/static/icons/icon-512x512.png', '/offline', ]; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(APP_SHELL)) .then(() => self.skipWaiting()) ); }); self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then(keys => Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))) ).then(() => self.clients.claim()) ); }); self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); // API calls: network only if (url.pathname.startsWith('/api/')) { return; } // Navigation requests: network first, cache fallback, then offline page if (event.request.mode === 'navigate') { event.respondWith( fetch(event.request) .then(response => { const clone = response.clone(); caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone)); return response; }) .catch(() => caches.match(event.request).then(r => r || caches.match('/offline'))) ); return; } // Static assets: cache first, network fallback if (url.pathname.startsWith('/static/')) { event.respondWith( caches.match(event.request).then(cached => cached || fetch(event.request).then(response => { const clone = response.clone(); caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone)); return response; }) ) ); return; } });