| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- // PackIt service worker.
- //
- // A packing list is read where there is no signal, so anything already visited
- // has to keep working offline. Navigations are network-first (the list must
- // reflect the latest admin changes when we can reach the server) with the
- // cached copy as the fallback; static assets are cache-first.
- //
- // Bump CACHE_VERSION to retire every previously cached response.
- const CACHE_VERSION = 'packit-v1';
- const OFFLINE_URL = '/offline.html';
- // Small and always needed. index.php is added opportunistically because it
- // depends on the database and must not be allowed to fail the install.
- const PRECACHE = [
- OFFLINE_URL,
- '/manifest.webmanifest',
- '/img/icon-192.png',
- '/img/icon-512.png',
- ];
- self.addEventListener('install', (event) => {
- event.waitUntil((async () => {
- const cache = await caches.open(CACHE_VERSION);
- await cache.addAll(PRECACHE);
- await cache.add('/index.php').catch(() => {});
- self.skipWaiting();
- })());
- });
- self.addEventListener('activate', (event) => {
- event.waitUntil((async () => {
- const keys = await caches.keys();
- await Promise.all(keys.filter((k) => k !== CACHE_VERSION).map((k) => caches.delete(k)));
- await self.clients.claim();
- })());
- });
- async function networkFirst(request) {
- const cache = await caches.open(CACHE_VERSION);
- try {
- const response = await fetch(request);
- // A redirected response (list.php -> packing.php) cannot be replayed from
- // the cache for a navigation, so only store final ones.
- if (response.ok && !response.redirected) {
- cache.put(request, response.clone());
- }
- return response;
- } catch (err) {
- const cached = await cache.match(request);
- return cached || (await cache.match(OFFLINE_URL)) || Response.error();
- }
- }
- async function cacheFirst(request) {
- const cache = await caches.open(CACHE_VERSION);
- const cached = await cache.match(request);
- if (cached) return cached;
- const response = await fetch(request);
- if (response.ok || response.type === 'opaque') {
- cache.put(request, response.clone());
- }
- return response;
- }
- async function staleWhileRevalidate(request) {
- const cache = await caches.open(CACHE_VERSION);
- const cached = await cache.match(request);
- const network = fetch(request).then((response) => {
- if (response.ok || response.type === 'opaque') cache.put(request, response.clone());
- return response;
- }).catch(() => cached);
- return cached || network;
- }
- self.addEventListener('fetch', (event) => {
- const request = event.request;
- if (request.method !== 'GET') return;
- const url = new URL(request.url);
- // Fonts live on Google's CDN; keep them usable offline.
- if (url.origin === 'https://fonts.googleapis.com' || url.origin === 'https://fonts.gstatic.com') {
- event.respondWith(staleWhileRevalidate(request));
- return;
- }
- if (url.origin !== self.location.origin) return;
- // The admin panel is session-protected and always changing – never cache it,
- // and never serve a stale page to someone editing content.
- if (url.pathname.startsWith('/admin/')) return;
- if (request.mode === 'navigate') {
- event.respondWith(networkFirst(request));
- return;
- }
- event.respondWith(cacheFirst(request));
- });
|