// Minimal service worker for Guest PWA queue sync self.addEventListener('install', (event) => { self.skipWaiting(); }); self.addEventListener('activate', (event) => { event.waitUntil(self.clients.claim()); }); const ASSETS_CACHE = 'guest-assets-v1'; const IMAGES_CACHE = 'guest-images-v1'; self.addEventListener('fetch', (event) => { const req = event.request; if (req.method !== 'GET') return; const url = new URL(req.url); // Only handle same-origin requests if (url.origin !== self.location.origin) return; // Never cache API calls; let them hit network directly if (url.pathname.startsWith('/api/')) return; // Cache-first for images if (req.destination === 'image' || /\.(png|jpg|jpeg|webp|avif|gif|svg)(\?.*)?$/i.test(url.pathname)) { event.respondWith((async () => { const cache = await caches.open(IMAGES_CACHE); const cached = await cache.match(req); if (cached) return cached; try { const res = await fetch(req, { credentials: 'same-origin' }); if (res.ok) cache.put(req, res.clone()); return res; } catch (e) { return cached || Response.error(); } })()); return; } // Stale-while-revalidate for CSS/JS assets if (req.destination === 'style' || req.destination === 'script') { event.respondWith((async () => { const cache = await caches.open(ASSETS_CACHE); const cached = await cache.match(req); const networkPromise = fetch(req, { credentials: 'same-origin' }) .then((res) => { if (res.ok) cache.put(req, res.clone()); return res; }) .catch(() => null); return cached || (await networkPromise) || Response.error(); })()); return; } }); self.addEventListener('sync', (event) => { if (event.tag === 'upload-queue') { event.waitUntil( (async () => { const clients = await self.clients.matchAll({ includeUncontrolled: true, type: 'window' }); for (const client of clients) { client.postMessage({ type: 'sync-queue' }); } })() ); } });