const CACHE_NAME = 'blue-mobile-v1'; const ASSETS_TO_CACHE = [ './', './index.html', './offline.html', './styles.css', './app.js', './products.json', './icons/icon-192.png', './icons/icon-512.png', 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/css/bootstrap.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.2/js/bootstrap.bundle.min.js' ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { return cache.addAll(ASSETS_TO_CACHE); }) ); 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)) )) ); self.clients.claim(); }); self.addEventListener('fetch', event => { const url = new URL(event.request.url); // Network-first for API (products.json) with cache fallback if (url.pathname.endsWith('/products.json')) { event.respondWith( fetch(event.request).then(resp => { const copy = resp.clone(); caches.open(CACHE_NAME).then(cache => cache.put(event.request, copy)); return resp; }).catch(() => caches.match(event.request)) ); return; } // Navigation requests: try network first, fallback to cached offline page if (event.request.mode === 'navigate') { event.respondWith( fetch(event.request).then(resp => { // update cache for navigation const copy = resp.clone(); caches.open(CACHE_NAME).then(cache => cache.put(event.request, copy)); return resp; }).catch(() => caches.match('./offline.html')) ); return; } // For images, styles, scripts: cache-first (fast) if (event.request.destination === 'image') { // Try to serve WebP variant when possible const url = new URL(event.request.url); const webpUrl = url.pathname + '.webp'; event.respondWith( caches.match(webpUrl).then(cachedWebp => { if (cachedWebp) return cachedWebp; return caches.match(event.request).then(cached => cached || fetch(event.request).then(resp => { const copy = resp.clone(); caches.open(CACHE_NAME).then(cache => cache.put(event.request, copy)); return resp; })); }) ); return; } if (event.request.destination === 'style' || event.request.destination === 'script' || event.request.destination === 'font') { event.respondWith( caches.match(event.request).then(cached => cached || fetch(event.request).then(resp => { const copy = resp.clone(); caches.open(CACHE_NAME).then(cache => cache.put(event.request, copy)); return resp; })) ); return; } // Default: try cache, then network event.respondWith( caches.match(event.request).then(cached => cached || fetch(event.request)).catch(() => {}) ); });