1/** 2 * Service Worker for U.S. Department of the Treasury 3 * Provides offline caching and performance improvements 4 */ 5 6const CACHE_VERSION = 'treasury-v1'; 7const STATIC_CACHE = `${CACHE_VERSION}-static`; 8const PAGES_CACHE = `${CACHE_VERSION}-pages`; 9const IMAGES_CACHE = `${CACHE_VERSION}-images`; 10const RUNTIME_ARTIFACT_PREFIXES = [ 11 '/api/rates/', 12 '/sites/default/files/interest-rates/', 13 '/resource-center/data-chart-center/interest-rates/TextView', 14 '/resource-center/data-chart-center/interest-rates/daily-treasury-rates.csv/', 15 '/resource-center/data-chart-center/interest-rates/pages/xml', 16 '/resource-center/data-chart-center/interest-rates/current-month/', 17 '/resource-center/data-chart-center/interest-rates/daily-treasury-rate-archives' 18]; 19 20// Static assets to cache immediately 21const STATIC_ASSETS = [ 22 '/', 23 '/news/press-releases/', 24 '/css/treasury.css', 25 '/js/treasury.js', 26 '/images/treasury-seal.svg', 27 '/images/treasury-seal.webp', 28 '/images/treasury-seal-green.webp', 29 '/images/favicon.png' 30]; 31 32// Install event - cache static assets 33self.addEventListener('install', (event) => { 34 event.waitUntil( 35 caches.open(STATIC_CACHE) 36 .then((cache) => { 37 console.log('[SW] Caching static assets'); 38 return cache.addAll(STATIC_ASSETS); 39 }) 40 .then(() => self.skipWaiting()) 41 ); 42}); 43 44// Activate event - clean up old caches 45self.addEventListener('activate', (event) => { 46 event.waitUntil( 47 caches.keys() 48 .then((cacheNames) => { 49 return Promise.all( 50 cacheNames 51 .filter((name) => name.startsWith('treasury-') && !name.startsWith(CACHE_VERSION)) 52 .map((name) => { 53 console.log('[SW] Deleting old cache:', name); 54 return caches.delete(name); 55 }) 56 ); 57 }) 58 .then(() => self.clients.claim()) 59 ); 60}); 61 62// Fetch event - serve from cache, fallback to network 63self.addEventListener('fetch', (event) => { 64 const { request } = event; 65 const url = new URL(request.url); 66 67 // Only handle same-origin requests 68 if (url.origin !== location.origin) { 69 return; 70 } 71 72 // Skip non-GET requests 73 if (request.method !== 'GET') { 74 return; 75 } 76 77 if (isRuntimeArtifact(url.pathname)) { 78 event.respondWith(fetch(request, { cache: 'no-store' })); 79 return; 80 } 81 82 // Strategy based on resource type 83 if (isStaticAsset(url.pathname)) { 84 // Cache-first for static assets 85 event.respondWith(cacheFirst(request, STATIC_CACHE)); 86 } else if (isImage(url.pathname)) { 87 // Cache-first for images 88 event.respondWith(cacheFirst(request, IMAGES_CACHE)); 89 } else if (isHTMLPage(request)) { 90 // Network-first for HTML pages (stale-while-revalidate) 91 event.respondWith(networkFirst(request, PAGES_CACHE)); 92 } 93}); 94 95// Cache-first strategy 96async function cacheFirst(request, cacheName) { 97 const cachedResponse = await caches.match(request); 98 if (cachedResponse) { 99 return cachedResponse; 100 } 101 102 try { 103 const networkResponse = await fetch(request); 104 if (networkResponse.ok) { 105 const cache = await caches.open(cacheName); 106 cache.put(request, networkResponse.clone()); 107 } 108 return networkResponse; 109 } catch (error) { 110 console.log('[SW] Fetch failed:', error); 111 return new Response('Offline', { status: 503 }); 112 } 113} 114 115// Network-first strategy with cache fallback 116async function networkFirst(request, cacheName) { 117 try { 118 const networkResponse = await fetch(request); 119 if (networkResponse.ok) { 120 const cache = await caches.open(cacheName); 121 cache.put(request, networkResponse.clone()); 122 } 123 return networkResponse; 124 } catch (error) { 125 console.log('[SW] Network failed, trying cache'); 126 const cachedResponse = await caches.match(request); 127 if (cachedResponse) { 128 return cachedResponse; 129 } 130 // Return offline page 131 return caches.match('/'); 132 } 133} 134 135// Helper functions 136function isStaticAsset(pathname) { 137 return /\.(css|js|woff2?|ttf|eot)$/i.test(pathname); 138} 139 140function isImage(pathname) { 141 return /\.(png|jpg|jpeg|gif|svg|webp|ico)$/i.test(pathname); 142} 143 144function isHTMLPage(request) { 145 return request.headers.get('accept')?.includes('text/html'); 146} 147 148function isRuntimeArtifact(pathname) { 149 return pathname === '/api/x-feed.json' || 150 pathname === '/api/x-feed.manifest.json' || 151 RUNTIME_ARTIFACT_PREFIXES.some((prefix) => pathname.startsWith(prefix)); 152}
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.