mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 15:40:12 +01:00
85 lines
2.8 KiB
JavaScript
85 lines
2.8 KiB
JavaScript
// Service Worker for MTG Deckbuilder
|
|
// Versioned via ?v=<catalog_hash> appended at registration time.
|
|
// Strategies:
|
|
// 1. Precache core shell assets (app shell + styles + manifest).
|
|
// 2. Runtime cache (stale-while-revalidate) for theme list & preview fragments.
|
|
// 3. Version bump (catalog hash change) triggers old cache purge.
|
|
|
|
const VERSION = (new URL(self.location.href)).searchParams.get('v') || 'dev';
|
|
const PRECACHE = `precache-v${VERSION}`;
|
|
const RUNTIME = `runtime-v${VERSION}`;
|
|
const CORE_ASSETS = [
|
|
'/',
|
|
'/themes/',
|
|
'/static/styles.css',
|
|
'/static/app.js',
|
|
'/static/manifest.webmanifest',
|
|
'/static/favicon.png'
|
|
];
|
|
|
|
// Utility: limit entries in a cache (simple LRU-esque trim by deletion order)
|
|
async function trimCache(cacheName, maxEntries){
|
|
const cache = await caches.open(cacheName);
|
|
const keys = await cache.keys();
|
|
if(keys.length <= maxEntries) return;
|
|
const remove = keys.slice(0, keys.length - maxEntries);
|
|
await Promise.all(remove.map(k => cache.delete(k)));
|
|
}
|
|
|
|
self.addEventListener('install', event => {
|
|
event.waitUntil(
|
|
caches.open(PRECACHE).then(cache => cache.addAll(CORE_ASSETS)).then(() => self.skipWaiting())
|
|
);
|
|
});
|
|
|
|
self.addEventListener('activate', event => {
|
|
event.waitUntil((async () => {
|
|
// Remove old versioned caches
|
|
const keys = await caches.keys();
|
|
await Promise.all(keys.filter(k => (k.startsWith('precache-v') || k.startsWith('runtime-v')) && !k.endsWith(VERSION)).map(k => caches.delete(k)));
|
|
await clients.claim();
|
|
})());
|
|
});
|
|
|
|
function isPreviewRequest(url){
|
|
return /\/themes\/preview\//.test(url.pathname);
|
|
}
|
|
function isThemeList(url){
|
|
return url.pathname === '/themes/' || url.pathname.startsWith('/themes?');
|
|
}
|
|
|
|
self.addEventListener('fetch', event => {
|
|
const req = event.request;
|
|
const url = new URL(req.url);
|
|
if(req.method !== 'GET') return; // Non-GET pass-through
|
|
|
|
// Core assets: cache-first
|
|
if(CORE_ASSETS.includes(url.pathname)){
|
|
event.respondWith(
|
|
caches.open(PRECACHE).then(cache => cache.match(req).then(found => {
|
|
return found || fetch(req).then(resp => { cache.put(req, resp.clone()); return resp; });
|
|
}))
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Theme list / preview fragments: stale-while-revalidate
|
|
if(isPreviewRequest(url) || isThemeList(url)){
|
|
event.respondWith((async () => {
|
|
const cache = await caches.open(RUNTIME);
|
|
const cached = await cache.match(req);
|
|
const fetchPromise = fetch(req).then(resp => {
|
|
if(resp && resp.status === 200){ cache.put(req, resp.clone()); trimCache(RUNTIME, 120).catch(()=>{}); }
|
|
return resp;
|
|
}).catch(() => cached);
|
|
return cached || fetchPromise;
|
|
})());
|
|
return;
|
|
}
|
|
});
|
|
|
|
self.addEventListener('message', event => {
|
|
if(event.data && event.data.type === 'SKIP_WAITING'){
|
|
self.skipWaiting();
|
|
}
|
|
});
|