an npmx inspired game for PICO-8
npicomx.vinnymac.dev
games
pico-8
adventure
npmx
1// npicomx service worker - installable PWA + offline app shell.
2
3const CACHE = "npicomx-v3";
4
5// Lightweight app shell - everything except the cart.
6const PRECACHE = [
7 "/",
8 "/index.html",
9 "/crt.js",
10 "/fonts/PICO-8.ttf",
11 "/fonts/PICO-8.eot",
12 "/manifest.webmanifest",
13 "/favicons/icon.svg",
14 "/favicons/favicon.ico",
15 "/favicons/apple-touch-icon.png",
16 "/favicons/icon-192.png",
17 "/favicons/icon-512.png",
18 "/og.png",
19];
20
21self.addEventListener("install", (event) => {
22 event.waitUntil(
23 caches.open(CACHE).then((cache) => cache.addAll(PRECACHE)).then(() => self.skipWaiting())
24 );
25});
26
27self.addEventListener("activate", (event) => {
28 event.waitUntil(
29 caches
30 .keys()
31 .then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
32 .then(() => self.clients.claim())
33 );
34});
35
36// Query-stripped key, so `npicomx.js?v=123` and `PICO-8.eot?#iefix` map to one
37// cached entry instead of growing the cache on every load.
38function normalize(request) {
39 const url = new URL(request.url);
40 return new Request(url.origin + url.pathname, { headers: request.headers });
41}
42
43self.addEventListener("fetch", (event) => {
44 const { request } = event;
45
46 // Only same-origin GETs are ours; everything else passes through to the network.
47 if (request.method !== "GET") return;
48 if (new URL(request.url).origin !== self.location.origin) return;
49
50 // Navigations: network-first so a fresh deploy wins; cached shell when offline.
51 if (request.mode === "navigate") {
52 event.respondWith(fetch(request).catch(() => caches.match("/", { ignoreSearch: true })));
53 return;
54 }
55
56 // Static assets (incl. the cart): cache-first ignoring the query, then network
57 // + store under the normalized key.
58 event.respondWith(
59 caches.match(request, { ignoreSearch: true }).then((hit) => {
60 if (hit) return hit;
61 return fetch(request).then((response) => {
62 if (response && response.ok && response.type === "basic") {
63 const copy = response.clone();
64 caches.open(CACHE).then((cache) => cache.put(normalize(request), copy));
65 }
66 return response;
67 });
68 })
69 );
70});