Files
2026-07-13 13:12:00 +08:00

53 lines
2.5 KiB
JavaScript

// Omnigent installability / update-only service worker (hand-rolled).
//
// Omnigent is a cloud app with NO offline mode, so this worker deliberately:
// - does NOT precache or serve the app shell, and
// - does NOT intercept navigations — every navigation hits the network, so a
// deploy is never masked behind a stale cached shell.
// It exists only to (a) make the app installable and (b) drive the in-app
// "new version → Reload" prompt (see src/components/pwa/useServiceWorkerUpdate).
//
// BUILD_VERSION is replaced at build time (vite.config.ts → emitPwaAssets) with
// a fingerprint of the hashed JS/CSS outputs, so this file's bytes change on
// every code/style deploy. That byte change is what the browser's update
// algorithm (via workbox-window in the page) detects to fire the prompt.
const BUILD_VERSION = "__BUILD_VERSION__";
const CACHE_NAME = `omnigent-pwa-${BUILD_VERSION}`;
self.addEventListener("install", (event) => {
// Precache ONLY version.json. Two reasons: it gives the worker a real
// (non-empty) fetch handler — Chrome's automatic install prompt ignores
// no-op handlers — and the per-build cache name means each deploy starts a
// fresh cache. We do NOT call skipWaiting(): a new build waits in the
// background until the user accepts the prompt.
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.add("/version.json")));
});
self.addEventListener("activate", (event) => {
// Drop caches from prior builds. No clients.claim(): in prompt mode the new
// worker must not take control of open pages until the user accepts.
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))),
),
);
});
self.addEventListener("message", (event) => {
// workbox-window's messageSkipWaiting() posts this when the user clicks Reload.
if (event.data && event.data.type === "SKIP_WAITING") self.skipWaiting();
});
self.addEventListener("fetch", (event) => {
// Respond ONLY for the version sentinel (cache-first, network fallback).
// Everything else — navigations, hashed assets, everything — falls through
// with no respondWith(), i.e. straight to the network. This keeps a real
// fetch handler without ever serving a stale app shell.
const url = new URL(event.request.url);
if (url.pathname === "/version.json") {
event.respondWith(caches.match(event.request).then((cached) => cached || fetch(event.request)));
}
});