1// Service Worker for Radineer PWA 2const CACHE_NAME = 'radineer-v1'; 3const OFFLINE_URL = '/offline'; 4 5// ãã£ãã·ã¥ãããªã½ã¼ã¹ 6const STATIC_ASSETS = [ 7 '/', 8 '/offline', 9 '/manifest.json', 10]; 11 12// ã¤ã³ã¹ãã¼ã«æã«ãã£ãã·ã¥ 13self.addEventListener('install', (event) => { 14 event.waitUntil( 15 caches.open(CACHE_NAME).then((cache) => { 16 console.log('Service Worker: Caching static assets'); 17 return cache.addAll(STATIC_ASSETS); 18 }) 19 ); 20 // å³åº§ã«ã¢ã¯ãã£ãã¼ã 21 self.skipWaiting(); 22}); 23 24// ã¢ã¯ãã£ãã¼ãæã«å¤ããã£ãã·ã¥ãåé¤ 25self.addEventListener('activate', (event) => { 26 event.waitUntil( 27 caches.keys().then((cacheNames) => { 28 return Promise.all( 29 cacheNames 30 .filter((name) => name !== CACHE_NAME) 31 .map((name) => { 32 console.log('Service Worker: Deleting old cache:', name); 33 return caches.delete(name); 34 }) 35 ); 36 }) 37 ); 38 // å ¨ã¦ã®ã¯ã©ã¤ã¢ã³ããå¶å¾¡ 39 self.clients.claim(); 40}); 41 42// ãã§ãããªã¯ã¨ã¹ãã®å¦ç 43self.addEventListener('fetch', (event) => { 44 const { request } = event; 45 const url = new URL(request.url); 46 47 // åããªãªã¸ã³ã®ãªã¯ã¨ã¹ãã®ã¿å¦ç 48 if (url.origin !== location.origin) { 49 return; 50 } 51 52 // APIãªã¯ã¨ã¹ãã¯ãããã¯ã¼ã¯åªå 53 if (url.pathname.startsWith('/api/')) { 54 event.respondWith(networkFirst(request)); 55 return; 56 } 57 58 // ããã²ã¼ã·ã§ã³ãªã¯ã¨ã¹ãï¼HTMLï¼ 59 if (request.mode === 'navigate') { 60 event.respondWith( 61 fetch(request) 62 .then((response) => { 63 // æåããã¬ã¹ãã³ã¹ããã£ãã·ã¥ 64 if (response.ok) { 65 const responseClone = response.clone(); 66 caches.open(CACHE_NAME).then((cache) => { 67 cache.put(request, responseClone); 68 }); 69 } 70 return response; 71 }) 72 .catch(() => { 73 // ãªãã©ã¤ã³æã¯ãã£ãã·ã¥ãã 74 return caches.match(request).then((cachedResponse) => { 75 return cachedResponse || caches.match(OFFLINE_URL); 76 }); 77 }) 78 ); 79 return; 80 } 81 82 // éçã¢ã»ããã¯ãã£ãã·ã¥åªå 83 if (isStaticAsset(url.pathname)) { 84 event.respondWith(cacheFirst(request)); 85 return; 86 } 87 88 // ãã®ä»ã¯ãããã¯ã¼ã¯åªå 89 event.respondWith(networkFirst(request)); 90}); 91 92// ãã£ãã·ã¥åªå æ¦ç¥ 93async function cacheFirst(request) { 94 const cachedResponse = await caches.match(request); 95 if (cachedResponse) { 96 return cachedResponse; 97 } 98 99 try { 100 const networkResponse = await fetch(request); 101 if (networkResponse.ok) { 102 const cache = await caches.open(CACHE_NAME); 103 cache.put(request, networkResponse.clone()); 104 } 105 return networkResponse; 106 } catch (error) { 107 console.error('Service Worker: Fetch failed:', error); 108 throw error; 109 } 110} 111 112// ãããã¯ã¼ã¯åªå æ¦ç¥ 113async function networkFirst(request) { 114 try { 115 const networkResponse = await fetch(request); 116 if (networkResponse.ok) { 117 const cache = await caches.open(CACHE_NAME); 118 cache.put(request, networkResponse.clone()); 119 } 120 return networkResponse; 121 } catch (error) { 122 const cachedResponse = await caches.match(request); 123 if (cachedResponse) { 124 return cachedResponse; 125 } 126 throw error; 127 } 128} 129 130// éçã¢ã»ãããã©ãããå¤å® 131function isStaticAsset(pathname) { 132 return ( 133 pathname.startsWith('/_next/static/') || 134 pathname.startsWith('/images/') || 135 pathname.startsWith('/icons/') || 136 pathname.endsWith('.js') || 137 pathname.endsWith('.css') || 138 pathname.endsWith('.png') || 139 pathname.endsWith('.jpg') || 140 pathname.endsWith('.jpeg') || 141 pathname.endsWith('.svg') || 142 pathname.endsWith('.webp') || 143 pathname.endsWith('.woff2') || 144 pathname.endsWith('.woff') 145 ); 146} 147 148// ããã·ã¥éç¥ï¼å°æ¥ã®æ¡å¼µç¨ï¼ 149self.addEventListener('push', (event) => { 150 if (!event.data) return; 151 152 const data = event.data.json(); 153 const options = { 154 body: data.body, 155 icon: '/icons/icon-192x192.png', 156 badge: '/icons/icon-72x72.png', 157 vibrate: [100, 50, 100], 158 data: { 159 url: data.url || '/', 160 }, 161 }; 162 163 event.waitUntil( 164 self.registration.showNotification(data.title || 'Radineer', options) 165 ); 166}); 167 168// éç¥ã¯ãªã㯠169self.addEventListener('notificationclick', (event) => { 170 event.notification.close(); 171 172 const url = event.notification.data?.url || '/'; 173 event.waitUntil( 174 clients.matchAll({ type: 'window' }).then((windowClients) => { 175 // æ¢åã®ã¦ã£ã³ãã¦ãããã°ãã©ã¼ã«ã¹ 176 for (const client of windowClients) {
177 if (client.url === url && 'focus' in client) { 178 return client.focus(); 179 } 180 } 181 // ãªããã°æ°ããã¦ã£ã³ãã¦ãéã 182 if (clients.openWindow) { 183 return clients.openWindow(url); 184 } 185 }) 186 ); 187});
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.