/* Service worker for the Work Package Suite PWA. Goal: let the app (and especially the field view) load and run offline. Data durability is already handled by the sync outbox in project-data.js — this worker only caches the static app shell so the pages open without a network. Strategy: • /api/* and non-GET → never touched (pass straight to the network; offline reads fall back to the app's localStorage cache, writes queue in the outbox). • same-origin GET → stale-while-revalidate (instant from cache, refreshed in the background when online). */ 'use strict'; // Bumped when the shell file list changes, so clients fetch the new assets // instead of serving a half-old shell from the previous cache. const CACHE = 'wp-suite-shell-v2'; const SHELL = [ '/', '/index.html', '/work-package-suite.html', '/wp-creation-index.html', '/field.html', '/login.html', '/admin.html', '/theme-light.css', '/work-package-suite-styles.css', '/wp-creation-styles.css', '/wp-chrome.css', '/auth-guard.js', '/project-data.js', '/feedback-config.js', '/help.js', '/work-package-suite-app.js', '/wp-creation-app.js', '/field.js', '/wp-chrome.js', '/wp-format.js', '/login.js', '/admin.js', '/prime-controls-logo.jpg', '/favicon.ico', '/manifest.webmanifest', '/icon-192.png', '/icon-512.png', ]; self.addEventListener('install', (e) => { // Cache each shell asset individually so one missing file doesn't abort install. e.waitUntil( caches.open(CACHE) .then((c) => Promise.all(SHELL.map((u) => c.add(u).catch(() => {})))) .then(() => self.skipWaiting()) ); }); self.addEventListener('activate', (e) => { e.waitUntil( caches.keys() .then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) .then(() => self.clients.claim()) ); }); self.addEventListener('fetch', (e) => { const req = e.request; if (req.method !== 'GET') return; // outbox owns writes const url = new URL(req.url); if (url.origin !== self.location.origin) return; // third-party: default if (url.pathname.startsWith('/api/')) return; // never cache the API e.respondWith( caches.match(req).then((cached) => { const network = fetch(req) .then((res) => { if (res && res.ok) { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(req, copy)); } return res; }) .catch(() => cached); // offline → cached copy return cached || network; // cache-first, then refresh }) ); });