const CACHE_NAME = "vomebook-search-v1"; const PRECACHE_URLS = [ "/", "/static/style.css", "/static/app.js", "/manifest.json" ]; // ── Install: precache app shell ───────────────────── self.addEventListener("install", (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll(PRECACHE_URLS).catch((err) => { console.warn("[SW] precache partial failure:", err); }); }).then(() => self.skipWaiting()) ); }); // ── Activate: clean old caches ────────────────────── self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((keys) => { return Promise.all( keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)) ); }).then(() => self.clients.claim()) ); }); // ── Fetch: cache strategies ───────────────────────── self.addEventListener("fetch", (event) => { const url = new URL(event.request.url); // External domains: network only if (url.hostname !== self.location.hostname) { return; } // ── API: network first, no caching ──────────────── if (url.pathname.startsWith("/api/")) { return; } // ── Download proxy: network only ────────────────── if (url.pathname.startsWith("/txt/")) { return; } // ── App shell / static: cache first, network update event.respondWith( caches.match(event.request).then((cached) => { const fetchPromise = fetch(event.request).then((response) => { if (response.ok && response.status !== 206) { const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => { cache.put(event.request, clone); }); } return response; }).catch(() => cached); return cached || fetchPromise; }) ); });