const ROOT = new URL(self.registration.scope).pathname; const appPath = path => `${ROOT}${String(path).replace(/^\/+/, '')}`; const CACHE_NAMESPACE = `timmy-shell:${ROOT}:`; const CACHE = `${CACHE_NAMESPACE}v7`; const ASSETS = [ '', 'index.html', 'styles.css', 'app.js', 'src/domain.js', 'src/analysis.js', 'manifest.webmanifest', 'assets/timmy.svg', 'assets/icon-192.svg', 'assets/icon-512.svg', ].map(appPath); self.addEventListener('install', event => event.waitUntil( caches.open(CACHE).then(cache => cache.addAll(ASSETS)).then(() => self.skipWaiting()), )); self.addEventListener('activate', event => event.waitUntil( caches.keys() .then(keys => Promise.all(keys.filter(key => (key.startsWith(CACHE_NAMESPACE) && key !== CACHE) || (ROOT === '/' && key === 'timmy-shell-v4') ).map(key => caches.delete(key)))) .then(() => self.clients.claim()), )); self.addEventListener('fetch', event => { if (event.request.method !== 'GET') return; const pathname = new URL(event.request.url).pathname; if (!pathname.startsWith(ROOT)) return; if (pathname.startsWith(appPath('api/'))) { event.respondWith(fetch(event.request)); return; } event.respondWith( fetch(event.request) .then(response => { if (!/\bno-store\b/i.test(response.headers.get('cache-control') || '')) { const copy = response.clone(); caches.open(CACHE).then(cache => cache.put(event.request, copy)); } return response; }) .catch(() => caches.open(CACHE).then(async cache => (await cache.match(event.request)) || cache.match(appPath('index.html')) )), ); });