/// import { build, files, version } from '$service-worker'; // Create a unique cache name for this deployment const CACHE = `cache-${version}`; const ASSETS = [ ...build, // the app itself // Exclude /fonts/ — 667 font files totalling ~134MB. Only 3 are used by the // browser (app.css); the rest are badge-print fonts for server/Electron use. // cache.addAll() is atomic, so including them would silently abort the entire // SW install on most mobile devices (quota ~50-100MB). The browser's normal // HTTP cache handles font requests when the print page is actually visited. ...files.filter(f => !f.startsWith('/fonts/')) ]; self.addEventListener('install', (event) => { // Create a new cache and add all files to it async function addFilesToCache() { const cache = await caches.open(CACHE); await cache.addAll(ASSETS); } event.waitUntil(addFilesToCache()); // Skip the waiting phase so the new service worker activates immediately // instead of waiting for all existing tabs to close first. // WHY: Without this, a user who leaves idaa.org open all day (common for // IDAA members) will run the old JS bundle for hours after a fix is deployed. // The new SW is installed in the background but sits idle until every tab // using the old version is closed — which may never happen in practice. self.skipWaiting(); }); self.addEventListener('activate', (event) => { // Remove previous cached data from other screens async function deleteOldCaches() { for (const key of await caches.keys()) { if (key !== CACHE) await caches.delete(key); } } event.waitUntil(deleteOldCaches()); // Take control of all open tabs immediately after activation so they use // the new service worker (and new cached assets) without needing a reload. self.clients.claim(); }); self.addEventListener('fetch', (event) => { // Only handle same-origin GET requests for the app shell and static assets. // Chromium can surface private-network/CORS failures on cross-origin API calls, // so we intentionally leave those requests to the browser untouched here. if (event.request.method !== 'GET') return; if (!event.request.url.startsWith('http')) return; // Skip CDN/API/extension requests. This worker should only cache the app origin. const request_url = new URL(event.request.url); if (request_url.origin !== self.location.origin) return; async function respond() { const cache = await caches.open(CACHE); // App build assets and static files are safe to serve directly from cache. if (ASSETS.includes(request_url.pathname)) { const cachedResponse = await cache.match(request_url.pathname); if (cachedResponse) return cachedResponse; } // For same-origin runtime requests, prefer the network and fall back to cache if offline. try { const response = await fetch(event.request); if (response.status === 200) { try { await cache.put(event.request, response.clone()); } catch (err) { console.warn('Service worker cache put skipped:', err); } } return response; } catch (err) { const cachedResponse = await cache.match(event.request); if (cachedResponse) return cachedResponse; throw err; } } event.respondWith(respond()); });