mc-dashboard/public/sw.js

87 lines
2.4 KiB
JavaScript
Raw Normal View History

// HurkiCorgi MC — minimal service worker
// Strategy: network-first for HTML/data, stale-while-revalidate for assets,
// offline fallback to the cached shell.
const VERSION = "v1";
const SHELL_CACHE = `shell-${VERSION}`;
const ASSET_CACHE = `assets-${VERSION}`;
const SHELL_URLS = ["/", "/icon.svg", "/manifest.json"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(SHELL_CACHE).then((c) => c.addAll(SHELL_URLS)).catch(() => {})
);
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
const keys = await caches.keys();
await Promise.all(
keys
.filter((k) => k !== SHELL_CACHE && k !== ASSET_CACHE)
.map((k) => caches.delete(k))
);
await self.clients.claim();
})()
);
});
function isAsset(url) {
return (
url.pathname.startsWith("/_next/static/") ||
url.pathname.endsWith(".svg") ||
url.pathname.endsWith(".woff2") ||
url.pathname.endsWith(".png") ||
url.pathname.endsWith(".ico")
);
}
self.addEventListener("fetch", (event) => {
const req = event.request;
if (req.method !== "GET") return;
const url = new URL(req.url);
if (url.origin !== self.location.origin) return;
// Never intercept API, auth, SSE, or anything under /api
if (url.pathname.startsWith("/api/")) return;
if (req.headers.get("accept")?.includes("text/event-stream")) return;
// Assets: stale-while-revalidate
if (isAsset(url)) {
event.respondWith(
caches.open(ASSET_CACHE).then(async (cache) => {
const cached = await cache.match(req);
const fetchPromise = fetch(req)
.then((res) => {
if (res.ok) cache.put(req, res.clone());
return res;
})
.catch(() => cached || Response.error());
return cached || fetchPromise;
})
);
return;
}
// HTML / navigations: network-first with cache fallback
if (req.mode === "navigate" || req.headers.get("accept")?.includes("text/html")) {
event.respondWith(
fetch(req)
.then((res) => {
if (res.ok) {
const clone = res.clone();
caches.open(SHELL_CACHE).then((c) => c.put(req, clone));
}
return res;
})
.catch(async () => {
const cached = await caches.match(req);
return cached || caches.match("/");
})
);
}
});