const CACHE_NAME = "notify-v1"; const PRECACHE_URLS = ["/"]; self.addEventListener("install", (event) => { event.waitUntil( caches .open(CACHE_NAME) .then((cache) => cache.addAll(PRECACHE_URLS)) .then(() => self.skipWaiting()) ); }); self.addEventListener("activate", (event) => { event.waitUntil( caches .keys() .then((names) => Promise.all( names .filter((name) => name !== CACHE_NAME) .map((name) => caches.delete(name)) ) ) .then(() => self.clients.claim()) ); }); self.addEventListener("fetch", (event) => { const { request } = event; // Skip cross-origin and non-GET requests if (request.method !== "GET" || !request.url.startsWith(self.location.origin)) { return; } const url = new URL(request.url); // Skip API calls if (url.pathname.startsWith("/api/")) { return; } // Next.js static chunks — stale-while-revalidate if (url.pathname.startsWith("/_next/")) { event.respondWith( caches.open(CACHE_NAME).then((cache) => cache.match(request).then((cached) => { const fetched = fetch(request).then((response) => { if (response.ok) { cache.put(request, response.clone()); } return response; }); return cached || fetched; }) ) ); return; } // Navigation requests — network-first with cache fallback if (request.mode === "navigate") { event.respondWith( fetch(request) .then((response) => { const clone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, clone)); return response; }) .catch(() => caches.match(request)) ); return; } // Other static assets — cache-first event.respondWith( caches.open(CACHE_NAME).then((cache) => cache.match(request).then((cached) => { if (cached) return cached; return fetch(request).then((response) => { if (response.ok) { cache.put(request, response.clone()); } return response; }); }) ) ); });