/// declare const self: ServiceWorkerGlobalScope; const CACHE_VERSION = "rspace-v1"; const STATIC_CACHE = `${CACHE_VERSION}-static`; const HTML_CACHE = `${CACHE_VERSION}-html`; // Vite-hashed assets are immutable (content hash in filename) const IMMUTABLE_PATTERN = /\/assets\/.*\.[a-f0-9]{8}\.(js|css|wasm)$/; // App shell to precache on install const PRECACHE_URLS = ["/", "/canvas.html"]; self.addEventListener("install", (event) => { event.waitUntil( caches.open(HTML_CACHE).then((cache) => cache.addAll(PRECACHE_URLS)) ); self.skipWaiting(); }); self.addEventListener("activate", (event) => { // Clean up old versioned caches event.waitUntil( caches .keys() .then((keys) => Promise.all( keys .filter((key) => !key.startsWith(CACHE_VERSION)) .map((key) => caches.delete(key)) ) ) .then(() => self.clients.claim()) ); }); self.addEventListener("fetch", (event) => { const url = new URL(event.request.url); // Skip WebSocket and API requests entirely if ( event.request.url.startsWith("ws://") || event.request.url.startsWith("wss://") || url.pathname.startsWith("/ws/") || url.pathname.startsWith("/api/") ) { return; } // Immutable hashed assets: cache-first (they never change) if (IMMUTABLE_PATTERN.test(url.pathname)) { event.respondWith( caches.match(event.request).then((cached) => { if (cached) return cached; return fetch(event.request).then((response) => { if (response.ok) { const clone = response.clone(); caches.open(STATIC_CACHE).then((cache) => cache.put(event.request, clone)); } return response; }); }) ); return; } // HTML pages: network-first with cache fallback if ( event.request.mode === "navigate" || event.request.headers.get("accept")?.includes("text/html") ) { event.respondWith( fetch(event.request) .then((response) => { if (response.ok) { const clone = response.clone(); caches.open(HTML_CACHE).then((cache) => cache.put(event.request, clone)); } return response; }) .catch(() => { return caches .match(event.request) .then((cached) => cached || caches.match("/canvas.html")) as Promise; }) ); return; } // Other assets (images, fonts, etc.): stale-while-revalidate event.respondWith( caches.match(event.request).then((cached) => { const fetchPromise = fetch(event.request) .then((response) => { if (response.ok) { const clone = response.clone(); caches.open(STATIC_CACHE).then((cache) => cache.put(event.request, clone)); } return response; }) .catch(() => cached as Response); return cached || fetchPromise; }) ); });