Something went wrong. Try again.
Service Worker for joodaloop.com
Something went wrong. Try again.
4.6 kB · 135 lines · Created 3mo ago
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136const CACHE_NAME = 'site-cache';const MANIFEST_URL = '/sw-manifest.json';
function pageVariants(url) { if (url === '/') return [url]; return [url, url.endsWith('/') ? url.slice(0, -1) : url + '/'];}
async function fetchChecked(url) { const res = await fetch(url); if (!res.ok) throw new Error(`${url} ${res.status}`); return res;}
function stageContent(pageUrls, assetUrls) { return Promise.all([ ...pageUrls.map(url => fetchChecked(url).then(res => [url, res, true])), ...assetUrls.map(url => fetchChecked(url).then(res => [url, res, false])) ]);}
async function commitContent(cache, staged) { const assets = staged.filter(([, , isPage]) => !isPage); const pages = staged.filter(([, , isPage]) => isPage);
await Promise.all(assets.map(([url, res]) => cache.put(url, res))); await Promise.all(pages.flatMap(([url, res]) => { const keys = pageVariants(url); return keys.map((k, i) => cache.put(k, i === keys.length - 1 ? res : res.clone())); }));}
async function updateContent(cache, oldManifest, newManifest) { const oldPages = new Map((oldManifest?.pages ?? []).map(p => [p.url, p.hash])); const oldAssets = new Map((oldManifest?.assets ?? []).map(a => [a.url, a.hash])); const newPageUrls = new Set(newManifest.pages.map(p => p.url)); const newAssetUrls = new Set(newManifest.assets.map(a => a.url));
const changedPages = newManifest.pages .filter(p => oldPages.get(p.url) !== p.hash) .map(p => p.url); const changedAssets = newManifest.assets .filter(a => oldAssets.get(a.url) !== a.hash) .map(a => a.url);
const staged = await stageContent(changedPages, changedAssets); await commitContent(cache, staged);
const allowedUrls = new Set([ MANIFEST_URL, ...newManifest.pages.flatMap(page => pageVariants(page.url)), ...newManifest.assets.map(asset => asset.url) ].map(url => new URL(url, self.location.origin).href)); const obsoleteRequests = (await cache.keys()) .filter(request => !allowedUrls.has(request.url)); await Promise.all(obsoleteRequests.map(request => cache.delete(request)));
await cache.put(MANIFEST_URL, new Response(JSON.stringify(newManifest)));
const removedPages = [...oldPages.keys()].filter(url => !newPageUrls.has(url)); const removedAssets = [...oldAssets.keys()].filter(url => !newAssetUrls.has(url)); return { changedPages: [...changedPages, ...removedPages], changedAssets: [...changedAssets, ...removedAssets] };}
self.addEventListener('install', event => { event.waitUntil((async () => { const manifest = await (await fetchChecked(MANIFEST_URL)).json(); const cache = await caches.open(CACHE_NAME); const oldResponse = await cache.match(MANIFEST_URL); const oldManifest = oldResponse ? await oldResponse.json() : null; await updateContent(cache, oldManifest, manifest); await self.skipWaiting(); })());});
self.addEventListener('activate', event => { event.waitUntil(self.clients.claim());});
self.addEventListener('fetch', event => { if (event.request.method !== 'GET') return; if (new URL(event.request.url).origin !== self.location.origin) return;
const ignoreSearch = event.request.mode === 'navigate';
event.respondWith((async () => { const cache = await caches.open(CACHE_NAME); const cached = await cache.match(event.request, { ignoreSearch });
if (cached) return cached;
return fetch(event.request); })());});
let lastUpdateCheck = 0;const UPDATE_CHECK_INTERVAL = 15 * 60 * 1000;
self.addEventListener('message', event => { if (event.data === 'check-update') { const now = Date.now(); if (now - lastUpdateCheck < UPDATE_CHECK_INTERVAL) return; lastUpdateCheck = now; event.waitUntil(checkForUpdates()); }});
async function checkForUpdates() { try { const newManifest = await (await fetchChecked(MANIFEST_URL)).json();
const cache = await caches.open(CACHE_NAME); const oldResponse = await cache.match(MANIFEST_URL); const oldManifest = oldResponse ? await oldResponse.json() : null;
if (oldManifest?.version === newManifest.version) return;
const { changedPages, changedAssets } = await updateContent( cache, oldManifest, newManifest );
const notifyUrls = [...changedPages.flatMap(pageVariants), ...changedAssets]; const clients = await self.clients.matchAll(); for (const client of clients) { client.postMessage({ type: 'content-updated', urls: notifyUrls }); } } catch (e) { console.warn('[SW] update check failed:', e); }}