const STATIC_CACHE_NAME = "vitrown-static-v2"; const DYNAMIC_CACHE_NAME = "vitrown-dynamic-v2"; const STATIC_ASSETS = [ "/", "/resources/manifest.webmanifest", "/favicon.ico", "/icon-192x192.png", "/icon-512x512.png", "/apple-touch-icon.png", ]; // Install event - cache static assets self.addEventListener("install", (event) => { event.waitUntil( caches.open(STATIC_CACHE_NAME).then((cache) => { return cache.addAll(STATIC_ASSETS); }) ); self.skipWaiting(); }); // Activate event - clean up old caches self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((cacheName) => { return ( cacheName.startsWith("vitrown-") && cacheName !== STATIC_CACHE_NAME && cacheName !== DYNAMIC_CACHE_NAME ); }) .map((cacheName) => caches.delete(cacheName)) ); }) ); self.clients.claim(); }); // Fetch event - network first, cache fallback strategy self.addEventListener("fetch", (event) => { const { request } = event; const url = new URL(request.url); // Skip non-GET requests if (request.method !== "GET") { return; } // Skip non-HTTP(S) requests (chrome-extension://, etc.) if (!url.protocol.startsWith("http")) { return; } // Skip WebSocket, SSE, and API requests if ( url.pathname.startsWith("/api/") || url.protocol === "ws:" || url.protocol === "wss:" || request.headers.get("accept")?.includes("text/event-stream") ) { return; } // For navigation requests, try network first if (request.mode === "navigate") { event.respondWith( fetch(request) .then((response) => { const responseToCache = response.clone(); caches.open(DYNAMIC_CACHE_NAME).then((cache) => { cache.put(request, responseToCache); }); return response; }) .catch(() => { return caches.match(request); }) ); return; } // For static assets (CSS, JS, images), use cache first if ( request.destination === "style" || request.destination === "script" || request.destination === "image" || request.destination === "font" ) { event.respondWith( caches.match(request).then((cachedResponse) => { if (cachedResponse) { // Update cache in background fetch(request).then((response) => { if (response.status === 200) { const responseToCache = response.clone(); caches.open(STATIC_CACHE_NAME).then((cache) => { cache.put(request, responseToCache); }); } }); return cachedResponse; } return fetch(request).then((response) => { if (response.status === 200) { const responseToCache = response.clone(); caches.open(STATIC_CACHE_NAME).then((cache) => { cache.put(request, responseToCache); }); } return response; }); }) ); return; } // For other requests, network first event.respondWith( fetch(request) .then((response) => { if (response.status === 200) { const responseToCache = response.clone(); caches.open(DYNAMIC_CACHE_NAME).then((cache) => { cache.put(request, responseToCache); }); } return response; }) .catch(() => { return caches.match(request); }) ); }); // Listen for messages from the client self.addEventListener("message", (event) => { if (event.data && event.data.type === "SKIP_WAITING") { self.skipWaiting(); } });