// Minimal service worker: caches the app shell so the cockpit launches fast and // survives brief network blips. Live data (the bridge WebSocket, /api, and map // tiles) is never cached — only same-origin GET app assets. const CACHE = 'g1000-shell-v2'; self.addEventListener('install', () => self.skipWaiting()); self.addEventListener('activate', (e) => { e.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) .then(() => self.clients.claim()) ); }); self.addEventListener('fetch', (e) => { const url = new URL(e.request.url); // Only same-origin GET app shell; skip the API and let the WS pass through. if (e.request.method !== 'GET' || url.origin !== location.origin) return; if (url.pathname.startsWith('/api') || url.pathname === '/ws') return; // The HTML entry is NETWORK-FIRST: a reload always gets the latest build (and // thus the latest hashed assets). Falls back to cache only when offline. const isDoc = e.request.mode === 'navigate' || url.pathname === '/' || url.pathname.endsWith('.html'); if (isDoc) { e.respondWith( fetch(e.request) .then((res) => { caches.open(CACHE).then((c) => c.put(e.request, res.clone())); return res; }) .catch(() => caches.match(e.request).then((c) => c || caches.match('/'))) ); return; } // Hashed assets are immutable → stale-while-revalidate (fast + self-healing). e.respondWith( caches.open(CACHE).then(async (cache) => { const cached = await cache.match(e.request); const network = fetch(e.request) .then((res) => { if (res && res.ok) cache.put(e.request, res.clone()); return res; }) .catch(() => cached); return cached || network; }) ); });