/** * Service Worker for LED Grab PWA. * * Strategy: * - Static assets (/static/): stale-while-revalidate * - API / config requests: network-only (device control must be live) * - Navigation: network-first with offline fallback */ const CACHE_NAME = 'ledgrab-v14'; // Only pre-cache static assets (no auth required). // Do NOT pre-cache '/' — it requires API key auth and would cache an error page. const PRECACHE_URLS = [ '/static/css/base.css', '/static/css/layout.css', '/static/css/components.css', '/static/css/cards.css', '/static/css/modal.css', '/static/css/calibration.css', '/static/css/dashboard.css', '/static/css/streams.css', '/static/css/patterns.css', '/static/css/automations.css', '/static/css/tutorials.css', '/static/css/mobile.css', '/static/icons/icon-192.png', '/static/icons/icon-512.png', ]; // Install: pre-cache core shell self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then((cache) => cache.addAll(PRECACHE_URLS)) .then(() => self.skipWaiting()) ); }); // Activate: clean old caches 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()) ); }); // Fetch handler self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); // API and config: always network (device control must be live) if (url.pathname.startsWith('/api/') || url.pathname.startsWith('/config/')) { return; // fall through to default network fetch } // Static assets: stale-while-revalidate if (url.pathname.startsWith('/static/')) { event.respondWith( caches.open(CACHE_NAME).then((cache) => cache.match(event.request).then((cached) => { const fetchPromise = fetch(event.request).then((response) => { if (response.ok) { cache.put(event.request, response.clone()); } return response; }).catch(() => cached); return cached || fetchPromise; }) ) ); return; } // Navigation: network-only (page requires auth, no useful offline fallback) if (event.request.mode === 'navigate') { event.respondWith( fetch(event.request).catch(() => new Response( '' + '

LED Grab

Cannot reach the server. Check that it is running and you are on the same network.

' + '' + '', { status: 503, headers: { 'Content-Type': 'text/html' } } ) ) ); return; } });