// GuestGuard Scanner — minimal service worker. // // Why so light: the scanner is an online tool. Letting the SW cache and // then serve stale check-in submissions would create double-arrivals // and lost data. So this SW only: // // 1. Claims clients on install so the "Add to home screen" launcher // opens the latest version straight away. // 2. Caches the bare app shell (the page itself + jsQR) so the // volunteer can re-open the scanner if the phone briefly drops // 4G. Auth + check-in calls always go to network. // // Anything fancier (background sync queues, IndexedDB cache of pending // check-ins) belongs in a later iteration once we have a story for // merging conflicts. const SHELL_CACHE = 'gg-scanner-shell-v1' const SHELL_URLS = [ '/scanner', 'https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.js', ] self.addEventListener('install', (event) => { event.waitUntil( caches.open(SHELL_CACHE).then((cache) => cache.addAll(SHELL_URLS)).catch(() => {}), ) self.skipWaiting() }) self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== SHELL_CACHE).map((k) => caches.delete(k))), ), ) self.clients.claim() }) self.addEventListener('fetch', (event) => { const url = new URL(event.request.url) // Never cache API calls — stale check-ins are worse than offline. if (url.pathname.startsWith('/events/') || url.pathname.startsWith('/auth/')) { return } // Network-first for the shell URLs so a deployment is reflected on // the next reload; fall back to cache only when offline. if (event.request.mode === 'navigate' || SHELL_URLS.some((u) => event.request.url.endsWith(u))) { event.respondWith( fetch(event.request) .then((res) => { const copy = res.clone() caches.open(SHELL_CACHE).then((c) => c.put(event.request, copy)).catch(() => {}) return res }) .catch(() => caches.match(event.request).then((r) => r || Response.error())), ) } })