Files
effectcraft/apps/effectcraft-web/web/sw.js
T
G.J.R. Timmer c334c36645 feat(web): startup option in effectcraft-config.json
A startup key (last, empty or demo) chooses the project the page opens on load. empty restores the last visit's project and otherwise starts blank, never with the demo; demo always opens the demo project; last, or no key, is the current behaviour. ?empty and ?demo still win. The config file is fetched once per page load and the service worker never caches it.
2026-10-29 07:11:48 -04:00

50 lines
1.9 KiB
JavaScript

// EffectCraft service worker: the app works offline once loaded, and installs as an app.
//
// `cargo xtask web` fills in the build's file list and version. Every file is precached into a
// cache named after the version, served cache-first (responses keep the server's headers, so
// the page stays cross-origin isolated: COOP/COEP), and old versions are dropped once the new
// one activates. Anything else same-origin is fetched from the network and cached for next time,
// except the config file, which must stay live.
const VERSION = "__EC_VERSION__";
const FILES = __EC_FILES__;
const CACHE = `effectcraft-${VERSION}`;
self.addEventListener("install", (event) => {
event.waitUntil(
caches
.open(CACHE)
.then((c) => c.addAll(FILES.map((f) => new Request(f, { cache: "reload" }))))
.then(() => self.skipWaiting()),
);
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((keys) => Promise.all(keys.filter((k) => k.startsWith("effectcraft-") && k !== CACHE).map((k) => caches.delete(k))))
.then(() => self.clients.claim()),
);
});
self.addEventListener("fetch", (event) => {
const req = event.request;
const url = new URL(req.url);
if (req.method !== "GET" || url.origin !== self.location.origin) return;
if (url.pathname.endsWith("/effectcraft-config.json")) return;
event.respondWith(
(async () => {
const cache = await caches.open(CACHE).catch(() => null);
if (cache) {
// The page itself regardless of its query (?empty, ?demo…).
const key = req.mode === "navigate" ? new URL("./", self.registration.scope).href : req;
const hit = await cache.match(key, { ignoreSearch: req.mode === "navigate" }).catch(() => null);
if (hit) return hit;
}
const res = await fetch(req);
if (cache && res.ok && res.type === "basic") void cache.put(req, res.clone()).catch(() => {});
return res;
})(),
);
});