function log(...params) { console.log.apply(this, [...["%c[SW]: %c", "color: #f4b942;", "color:unset;"], ...params]) } const CACHE = "offline"; let precacheFiles = [ "/", "/index.html", "/main.js", "/main.css", "/serviceworker.js" ] //Install stage sets up the cache-array to configure pre-cache content self.addEventListener('install', (evt) => { log('The service worker is being installed.'); evt.waitUntil(precache().then(() => { log('Skip waiting on install'); }).catch(log).then(() => self.skipWaiting())); }); //allow sw to control of current page self.addEventListener('activate', (event) => { log('Claiming clients for current page'); return self.clients.claim(); }); self.addEventListener('message', (event) => { if (event.data === "clear_cache") { log("Clearing cache"); caches.delete(CACHE); } if (event.data === "update_index") { log("Updating index"); event.waitUntil(caches.open(CACHE).then((cache) => { return cache.addAll(["/", "/index.html"]).then(() => { event.ports[0].postMessage("reload"); }); })) } }); var Types; (function (Types) { Types[Types["CACHE"] = 0] = "CACHE"; Types[Types["NOCACHE"] = 1] = "NOCACHE"; Types[Types["REFRESH"] = 2] = "REFRESH"; Types[Types["INDEX"] = 3] = "INDEX"; })(Types || (Types = {})); let rules = [{ match: (url) => { return url.indexOf("/api/") >= 0; }, type: Types.NOCACHE }, { match: (url) => { return url.indexOf("/share") >= 0; }, type: Types.INDEX }, { match: () => { return true; }, type: Types.REFRESH } ] self.addEventListener('fetch', (evt) => { if (evt.request.method != 'GET') return; // Dont care about POST requests let rule = rules.find(rule => rule.match(evt.request.url)); evt.respondWith((async () => { log("Cache:", Types[rule.type]); switch (rule.type) { case Types.CACHE: return fromCache(evt.request); case Types.REFRESH: return refresh(evt.request).then(r => { evt.waitUntil(r.refresh.catch(_ => {})); return r.result; }); case Types.NOCACHE: return fetch(evt.request); case Types.INDEX: return refresh(new Request("/")).then(r => { evt.waitUntil(r.refresh.catch(_ => {})); return r.result; }) } })()); }); async function fromCache(request) { let cache = await caches.open(CACHE); let matching = await cache.match(request); if (matching) return matching let res = await fetch(request.clone()); await cache.put(request, { match: (url) => { return url.indexOf("/version_hash") >= 0; }, type: Types.NOCACHE }, res); return await cache.match(request); } async function refresh(request) { let cache = await caches.open(CACHE); let web = fetch(request.clone()).then(res => { return cache.put(request, res).then(() => { return cache.match(request); }) }) let matching = await cache.match(request); return { result: matching ? matching : web, refresh: web } } function precache() { return caches.open(CACHE).then(function (cache) { return cache.addAll(precacheFiles); }); }