1/** 2 * Hydrovac News service worker. 3 * 4 * Strategy: 5 * - /_next/static/*, /images/*, /pdfs/*, favicon/icon assets, /sw.js itself 6 * â cache-first with a long TTL. These are content-hashed or rarely change. 7 * - Everything else (HTML, API responses, dynamic next/image transforms) â 8 * network-only. We never cache HTML to avoid serving stale pages. 9 * 10 * Update strategy: 11 * - Bump CACHE_VERSION when changing this file. Old caches are deleted on activate. 12 * - The Cache-Control on /sw.js (set in vercel.json) forces revalidation on 13 * every page load, so this file itself updates immediately. 14 * - We call self.skipWaiting() so the new SW takes over without requiring a 15 * second tab close. 16 */ 17const CACHE_VERSION = 'v1'; 18const STATIC_CACHE = `hvn-static-${CACHE_VERSION}`; 19 20// Paths that are safe to serve cache-first. Each must be content-hashed or 21// immutable in practice (we expect the underlying file never changes). 22const STATIC_PATTERNS = [ 23 /^\/_next\/static\//, 24 /^\/images\//, 25 /^\/pdfs\//, 26 /^\/(favicon-32|favicon-64|icon-192|icon-512|apple-icon|logo|og-image)\.(png|svg)$/, 27 /^\/pdf\.worker\.min\.mjs$/, 28]; 29 30function isStaticAsset(url) { 31 return STATIC_PATTERNS.some((rx) => rx.test(url.pathname)); 32} 33 34self.addEventListener('install', (event) => { 35 // Take over immediately on first install 36 self.skipWaiting(); 37}); 38 39self.addEventListener('activate', (event) => { 40 event.waitUntil( 41 (async () => { 42 const keys = await caches.keys(); 43 await Promise.all( 44 keys.filter((k) => k !== STATIC_CACHE).map((k) => caches.delete(k)) 45 ); 46 await self.clients.claim(); 47 })() 48 ); 49}); 50 51self.addEventListener('fetch', (event) => { 52 const request = event.request; 53 if (request.method !== 'GET') return; 54 55 const url = new URL(request.url); 56 57 // Only handle same-origin requests 58 if (url.origin !== self.location.origin) return; 59 60 if (!isStaticAsset(url)) return; 61 62 event.respondWith( 63 (async () => { 64 const cache = await caches.open(STATIC_CACHE); 65 const cached = await cache.match(request); 66 if (cached) return cached; 67 68 try { 69 const response = await fetch(request); 70 // Only cache successful, basic responses 71 if (response && response.ok && response.type === 'basic') { 72 cache.put(request, response.clone()); 73 } 74 return response; 75 } catch (err) { 76 // If offline and not cached, let the browser handle the error 77 throw err; 78 } 79 })() 80 ); 81});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.