shopstack / static /sw.js
pranaysuyash's picture
Sync ShopStack HEAD 6f8adfc
d999bba verified
Raw
History Blame Contribute Delete
2.65 kB
/* ShopStack service worker β€” Phase 4 #5 PWA shell.
*
* Strategy: cache-first for the app shell (HTML, CSS, JS, icons) so the
* app can boot offline. Network-first for everything else (Gradio API
* calls, market data, OCR) β€” those will fail offline and the user will
* see the cached "offline" UI.
*
* This is intentionally minimal. Real production PWA would also
* implement background sync, push notifications, and stale-while-revalidate.
*/
const CACHE_NAME = "shopstack-shell-v1";
const SHELL_ASSETS = [
"/",
"/static/manifest.json",
"/static/icon-192.svg",
"/static/icon-512.svg",
];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
// Best-effort: add each shell asset individually so a single
// 404 doesn't abort the whole install.
return Promise.all(
SHELL_ASSETS.map((url) =>
cache.add(url).catch((err) => {
console.warn("[sw] failed to cache", url, err);
})
)
);
})
);
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
)
)
);
self.clients.claim();
});
self.addEventListener("fetch", (event) => {
const req = event.request;
if (req.method !== "GET") return;
const url = new URL(req.url);
// Don't intercept Gradio's WebSocket / API / SSE endpoints β€” those need
// network. Only cache static assets and the app shell.
const isShellAsset =
SHELL_ASSETS.includes(url.pathname) ||
url.pathname.startsWith("/static/") ||
url.pathname === "/";
if (!isShellAsset) {
return; // let the browser fetch normally
}
event.respondWith(
caches.match(req).then((cached) => {
if (cached) {
return cached;
}
return fetch(req)
.then((resp) => {
// Opportunistically cache successful responses
if (resp.ok && resp.status === 200) {
const copy = resp.clone();
caches.open(CACHE_NAME).then((cache) => cache.put(req, copy));
}
return resp;
})
.catch(() => {
// Offline fallback: return the cached root if available
return caches.match("/").then((fallback) => {
return (
fallback ||
new Response("Offline β€” ShopStack shell not cached.", {
status: 503,
statusText: "Service Unavailable",
})
);
});
});
})
);
});