/* Mineflappy offline cache. The app shell (index.html) is fetched NETWORK-FIRST so a new deploy reaches the player immediately; if the network is down it falls back to the cached copy. The icons and manifest are cache-first because they only change with a version bump. */ const CACHE = 'mineflappy-v2'; const ASSETS = [ './', './index.html', './manifest.webmanifest', './icon-192.png', './icon-512.png', './apple-touch-icon.png', './favicon-32.png' ]; self.addEventListener('install', e => { e.waitUntil( caches.open(CACHE) .then(c => c.addAll(ASSETS)) .then(() => self.skipWaiting()) ); }); self.addEventListener('activate', e => { e.waitUntil( caches.keys() .then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k)))) .then(() => self.clients.claim()) ); }); function isAppShell(url) { return url.pathname.endsWith('/') || url.pathname.endsWith('/index.html') || url.pathname.endsWith('/sw.js'); } self.addEventListener('fetch', e => { const req = e.request; if (req.method !== 'GET') return; const url = new URL(req.url); if (url.origin !== location.origin) return; if (isAppShell(url)) { /* network first - a redeploy shows up straight away */ e.respondWith( fetch(req).then(res => { if (res && res.status === 200) { const copy = res.clone(); caches.open(CACHE).then(c => c.put(req, copy)); } return res; }).catch(() => caches.match(req).then(hit => hit || caches.match('./index.html'))) ); return; } /* everything else: cache first, refresh in the background */ e.respondWith( caches.match(req).then(hit => { const network = fetch(req).then(res => { if (res && res.status === 200 && res.type === 'basic') { const copy = res.clone(); caches.open(CACHE).then(c => c.put(req, copy)); } return res; }).catch(() => hit); return hit || network; }) ); });