const TILE_CACHE = 'lageplan-tiles-v2' const STATIC_CACHE = 'lageplan-static-v2' const APP_CACHE = 'lageplan-app-v2' // Pre-cache essential app shell on install self.addEventListener('install', (event) => { event.waitUntil( caches.open(APP_CACHE).then((cache) => cache.addAll([ '/app', '/logo.svg', '/logo-icon.png', '/manifest.json', ]).catch(() => {}) ) ) self.skipWaiting() }) // Cache strategy: Network First for API, Cache First for tiles, Stale While Revalidate for static assets self.addEventListener('fetch', (event) => { const url = event.request.url const { pathname } = new URL(url) // Skip non-GET requests if (event.request.method !== 'GET') return // API requests: network only (don't cache dynamic data) if (pathname.startsWith('/api/')) return // Cache map tiles from OpenStreetMap (Cache First) if (url.includes('tile.openstreetmap.org') || url.includes('api.maptiler.com')) { event.respondWith( caches.open(TILE_CACHE).then((cache) => cache.match(event.request).then((cached) => { if (cached) return cached return fetch(event.request).then((response) => { if (response.ok) { cache.put(event.request, response.clone()) } return response }).catch(() => new Response('', { status: 503 })) }) ) ) return } // Static assets (JS, CSS, images): Stale While Revalidate if (pathname.match(/\.(js|css|png|jpg|jpeg|svg|ico|woff2?)$/)) { event.respondWith( caches.open(STATIC_CACHE).then((cache) => cache.match(event.request).then((cached) => { const fetchPromise = fetch(event.request).then((response) => { if (response.ok) cache.put(event.request, response.clone()) return response }).catch(() => cached || new Response('', { status: 503 })) return cached || fetchPromise }) ) ) return } // App pages: Network First with cache fallback if (pathname === '/app' || pathname === '/' || pathname.startsWith('/app')) { event.respondWith( fetch(event.request).then((response) => { if (response.ok) { const clone = response.clone() caches.open(APP_CACHE).then((cache) => cache.put(event.request, clone)) } return response }).catch(() => caches.match(event.request).then((cached) => cached || caches.match('/app')) ) ) return } }) // Clean old caches on activation self.addEventListener('activate', (event) => { const currentCaches = [TILE_CACHE, STATIC_CACHE, APP_CACHE] event.waitUntil( caches.keys().then((keys) => Promise.all( keys .filter((k) => !currentCaches.includes(k)) .map((k) => caches.delete(k)) ) ).then(() => self.clients.claim()) ) })