/** * diora service worker — caches the app shell for offline use. */ const CACHE = 'diora-v1'; const SHELL = [ '/', '/static/css/app.css', '/static/js/app.js', '/static/manifest.json', ]; // Install: pre-cache the app shell self.addEventListener('install', function (event) { event.waitUntil( caches.open(CACHE).then(function (cache) { return cache.addAll(SHELL); }) ); // Activate immediately without waiting for old tabs to close self.skipWaiting(); }); // Activate: remove stale caches from previous versions self.addEventListener('activate', function (event) { event.waitUntil( caches.keys().then(function (keys) { return Promise.all( keys.filter(function (key) { return key !== CACHE; }) .map(function (key) { return caches.delete(key); }) ); }).then(function () { return self.clients.claim(); }) ); }); // Fetch: serve from cache, fall back to network self.addEventListener('fetch', function (event) { // Only handle GET requests; let POST/SSE etc. pass through if (event.request.method !== 'GET') return; // Don't intercept SSE or API requests const url = new URL(event.request.url); if (url.pathname.startsWith('/radio/sse/') || url.pathname.startsWith('/radio/record/') || url.pathname.startsWith('/radio/affiliate/') || url.pathname.startsWith('/admin/')) { return; } event.respondWith( caches.match(event.request).then(function (cached) { if (cached) return cached; return fetch(event.request).then(function (response) { // Cache successful GET responses for shell assets if (response && response.status === 200 && response.type === 'basic') { const clone = response.clone(); caches.open(CACHE).then(function (cache) { cache.put(event.request, clone); }); } return response; }); }) ); });