const CACHE_NAME = 'dgray-v32'; const STATIC_ASSETS = [ '/', '/index.html', '/css/fonts.css', '/css/variables.css', '/css/base.css', '/css/components.css', '/js/app.js', '/js/router.js', '/js/i18n.js', '/js/services/crypto.js', '/js/services/chat.js', '/js/services/directus.js', '/js/services/auth.js', '/js/components/chat-widget.js', '/js/components/listing-card.js', '/js/components/search-box.js', '/js/components/error-boundary.js', '/js/services/currency.js', '/locales/de.json', '/locales/en.json', '/locales/fr.json', '/manifest.json', '/assets/fonts/Inter-Regular.woff2', '/assets/fonts/Inter-Medium.woff2', '/assets/fonts/Inter-SemiBold.woff2', '/assets/fonts/Inter-Bold.woff2', '/assets/fonts/SpaceGrotesk-Medium.woff2', '/assets/fonts/SpaceGrotesk-Bold.woff2' ]; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then((cache) => cache.addAll(STATIC_ASSETS)) ); }); 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()) ); }); self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); if (request.method !== 'GET') return; // API calls: Network First (external API domain) if (url.hostname === 'api.dgray.io') { event.respondWith(networkFirst(request)); return; } // Legacy: API calls via path if (url.pathname.includes('/api/')) { event.respondWith(networkFirst(request)); return; } // HTML and JS: Stale-While-Revalidate (show cached, update in background) if (url.pathname.endsWith('.html') || url.pathname.endsWith('.js') || url.pathname === '/') { event.respondWith(staleWhileRevalidate(request)); return; } // Everything else: Cache First event.respondWith(cacheFirst(request)); }); async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; try { const response = await fetch(request); if (response.ok) { const cache = await caches.open(CACHE_NAME); cache.put(request, response.clone()); } return response; } catch { return new Response('Offline', { status: 503 }); } } async function networkFirst(request) { try { const response = await fetch(request); if (response.ok) { const cache = await caches.open(CACHE_NAME); cache.put(request, response.clone()); } return response; } catch { const cached = await caches.match(request); if (cached) return cached; return new Response(JSON.stringify({ error: 'Offline' }), { status: 503, headers: { 'Content-Type': 'application/json' } }); } } async function staleWhileRevalidate(request) { const cache = await caches.open(CACHE_NAME); const cached = await cache.match(request); const fetchPromise = fetch(request).then((response) => { if (response.ok) { cache.put(request, response.clone()); } return response; }).catch(() => cached); return cached || fetchPromise; }