// MacroPad PWA Service Worker const CACHE_NAME = 'macropad-v4'; const ASSETS_TO_CACHE = [ '/', '/static/css/styles.css', '/static/js/app.js', '/static/icons/icon-192.png', '/static/icons/icon-512.png', '/manifest.json' ]; // Install event - pre-cache the app shell self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then((cache) => { console.log('Caching app assets'); return cache.addAll(ASSETS_TO_CACHE); }) .then(() => self.skipWaiting()) ); }); // Activate event - clean old caches self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((name) => name !== CACHE_NAME) .map((name) => caches.delete(name)) ); }).then(() => self.clients.claim()) ); }); // Network-first for navigations + app code so deployed fixes reach clients. async function networkFirst(request) { const cache = await caches.open(CACHE_NAME); try { const networkResponse = await fetch(request); if (networkResponse && networkResponse.status === 200 && networkResponse.type === 'basic') { cache.put(request, networkResponse.clone()); } return networkResponse; } catch (err) { const cached = await cache.match(request); if (cached) { return cached; } // Offline fallback for navigations: serve the cached shell. if (request.mode === 'navigate') { const shell = await cache.match('/'); if (shell) { return shell; } } return new Response('Offline', { status: 503, statusText: 'Offline', headers: { 'Content-Type': 'text/plain' } }); } } self.addEventListener('fetch', (event) => { const request = event.request; // Only handle GET; let the browser deal with POST/PUT/DELETE directly. if (request.method !== 'GET') { return; } const url = new URL(request.url); // API requests always go to the network; never serve stale data. // Return a synthetic offline JSON response instead of an unhandled undefined. if (url.pathname.startsWith('/api/')) { event.respondWith( fetch(request).catch(() => new Response( JSON.stringify({ error: 'offline', detail: 'Network unavailable' }), { status: 503, headers: { 'Content-Type': 'application/json' } } )) ); return; } // Navigations and static app assets: network-first with cache fallback. event.respondWith(networkFirst(request)); });