const CACHE_NAME = 'coppertone-cache-v4'; const urlsToCache = [ '/', '/index.html', '/favicon.ico', ]; self.addEventListener('install', (event) => { // Skip waiting to activate immediately self.skipWaiting(); event.waitUntil( caches.open(CACHE_NAME).then((cache) => { // Only cache what exists, ignore failures return Promise.allSettled(urlsToCache.map(url => cache.add(url))); }) ); }); self.addEventListener('fetch', (event) => { // Skip non-GET requests and chrome-extension URLs if (event.request.method !== 'GET' || event.request.url.startsWith('chrome-extension://')) { return; } // Skip API requests - let them go directly to network if (event.request.url.includes('/api/')) { return; } // Network-first strategy for JS/CSS assets (hashed filenames change on rebuild) if (event.request.url.includes('/assets/')) { event.respondWith( fetch(event.request) .then((response) => { // Clone and cache successful responses if (response.ok) { const responseClone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(event.request, responseClone)); } return response; }) .catch(() => { return caches.match(event.request).then((cachedResponse) => { // Always return a valid Response - never undefined return cachedResponse || new Response('Asset not available offline', { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'text/plain' } }); }); }) ); return; } // Cache-first for static assets event.respondWith( caches.match(event.request).then((response) => { if (response) return response; return fetch(event.request) .then((networkResponse) => { // Cache successful responses for static content if (networkResponse.ok && !event.request.url.includes('/api/')) { const responseClone = networkResponse.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(event.request, responseClone)); } return networkResponse; }) .catch(() => { // Return a fallback for navigation requests if (event.request.mode === 'navigate') { return caches.match('/index.html').then((cachedIndex) => { return cachedIndex || new Response('Page not available offline', { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'text/html' } }); }); } return new Response('Network error', { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'text/plain' } }); }); }) ); }); self.addEventListener('activate', (event) => { // Claim clients immediately event.waitUntil( Promise.all([ self.clients.claim(), // Clear old caches caches.keys().then((cacheNames) => Promise.all( cacheNames.map((cacheName) => { if (cacheName !== CACHE_NAME) { return caches.delete(cacheName); } return undefined; }) ) ) ]) ); });