var CACHE_NAME = 'xmrpay-v4'; var ASSETS = [ '/', '/index.html', '/app.min.js?v=20260326-3', '/i18n.min.js?v=20260326-3', '/style.css', '/lib/qrcode.min.js?v=20260326-3', '/favicon.svg', '/fonts/inter-400.woff2', '/fonts/jetbrains-400.woff2' // xmr-crypto.bundle.js and jspdf.min.js are lazy-loaded and runtime-cached ]; self.addEventListener('install', function (e) { e.waitUntil( caches.open(CACHE_NAME).then(function (cache) { return cache.addAll(ASSETS); }) ); self.skipWaiting(); }); self.addEventListener('activate', function (e) { e.waitUntil( caches.keys().then(function (names) { return Promise.all( names.filter(function (n) { return n !== CACHE_NAME; }) .map(function (n) { return caches.delete(n); }) ); }) ); self.clients.claim(); }); self.addEventListener('fetch', function (e) { var url = new URL(e.request.url); // API calls — network only, don't cache if (url.pathname.startsWith('/api/')) { e.respondWith(fetch(e.request)); return; } // Navigation (HTML) — network first, fall back to cached index.html for offline // Invoice data is in the URL hash, so caching the document would cause stale state if (e.request.mode === 'navigate') { e.respondWith( fetch(e.request).catch(function () { return caches.match('/index.html'); }) ); return; } // App assets — cache first, fallback to network e.respondWith( caches.match(e.request).then(function (cached) { var networkFetch = fetch(e.request).then(function (response) { if (response.ok) { var clone = response.clone(); caches.open(CACHE_NAME).then(function (cache) { cache.put(e.request, clone); }); } return response; }).catch(function () { return cached; }); return cached || networkFetch; }) ); });