// projax service worker — Phase 3j install affordance only. // // Strategy: // * On install: pre-cache /static/style.css + manifest + icons so the // standalone shell renders even when offline. // * On fetch (GET only): network-first. On network failure, fall back to // cache. POSTs, the /mcp/ surface, and everything else go straight to the // network so writeback semantics are preserved. // * On activate: prune stale caches from earlier versions. // // This is NOT full offline mode. It's enough to make the icon work as a // real PWA + keep static assets warm. Mutations (CalDAV / Gitea writeback) // still require connectivity. const CACHE_NAME = 'projax-shell-v1'; const SHELL_ASSETS = [ '/static/style.css', '/static/manifest.webmanifest', '/static/icon-192.png', '/static/icon-512.png', '/static/icon-maskable.png', ]; self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => cache.addAll(SHELL_ASSETS)) ); 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; // Only GET — never cache POST/PATCH/DELETE/PUT (writeback + MCP RPC). if (req.method !== 'GET') return; // Skip MCP RPC entirely — it's stateful JSON-RPC. if (new URL(req.url).pathname.startsWith('/mcp/')) return; event.respondWith( fetch(req) .then((res) => { // Stash successful GETs into the cache for offline fallback. if (res.ok && req.url.startsWith(self.location.origin)) { const copy = res.clone(); caches.open(CACHE_NAME).then((c) => c.put(req, copy)); } return res; }) .catch(() => caches.match(req)) ); });