// EasyStream Service Worker (lightweight, safe defaults) const CACHE_VERSION = 'es-v1'; const STATIC_CACHE = `${CACHE_VERSION}-static`; const PRECACHE = [ '/index.js', '/manifest.json' ]; self.addEventListener('install', (event) => { event.waitUntil((async () => { const cache = await caches.open(STATIC_CACHE); await cache.addAll(PRECACHE); self.skipWaiting(); })()); }); self.addEventListener('activate', (event) => { event.waitUntil((async () => { const keys = await caches.keys(); await Promise.all(keys.filter(k => !k.startsWith(CACHE_VERSION)).map(k => caches.delete(k))); self.clients.claim(); })()); }); self.addEventListener('fetch', (event) => { const url = event.request.url; // never cache uploads or HLS segments/manifests if (url.includes('upload') || url.includes('uploader') || url.includes('index.m3u8') || url.includes('.ts')) { return; // bypass SW } // Navigation requests: network-first with cache fallback for basic offline shell if (event.request.mode === 'navigate') { event.respondWith((async () => { try { const fresh = await fetch(event.request); return fresh; } catch (e) { const cache = await caches.open(STATIC_CACHE); const shell = await cache.match('/index.js'); return shell || Response.error(); } })()); return; } // Others: stale-while-revalidate event.respondWith((async () => { const cached = await caches.match(event.request); const fetchPromise = fetch(event.request).then((networkResponse) => { if (networkResponse && networkResponse.ok && event.request.method === 'GET') { const copy = networkResponse.clone(); caches.open(STATIC_CACHE).then((cache) => cache.put(event.request, copy)).catch(() => {}); } return networkResponse; }).catch(() => cached); return cached || fetchPromise; })()); });