homeos/public/sw.js

75 lines
2.5 KiB
JavaScript

/**
* HomeOS service worker. Deliberately conservative for an authenticated Livewire app:
* - never intercepts non-GET (login, Livewire updates, broadcasting/auth stay online-only)
* - cache-first only for fingerprinted, immutable assets under /build/ and static icons
* - navigations are network-first with an offline fallback, so authenticated HTML is never
* served stale from cache
* Bump VERSION to invalidate old caches on the next activate.
*/
const VERSION = 'homeos-v1';
const STATIC_CACHE = `${VERSION}-static`;
const OFFLINE_URL = '/offline.html';
const PRECACHE = [OFFLINE_URL, '/icons/icon-192.png', '/icons/icon-512.png'];
self.addEventListener('install', (event) => {
event.waitUntil(
(async () => {
const cache = await caches.open(STATIC_CACHE);
await cache.addAll(PRECACHE);
await self.skipWaiting();
})(),
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
(async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => !k.startsWith(VERSION)).map((k) => caches.delete(k)));
await self.clients.claim();
})(),
);
});
self.addEventListener('message', (event) => {
if (event.data === 'skipWaiting') self.skipWaiting();
});
self.addEventListener('fetch', (event) => {
const req = event.request;
if (req.method !== 'GET') return; // login / Livewire / broadcasting are POST — stay online
const url = new URL(req.url);
if (url.origin !== self.location.origin) return; // don't touch cross-origin
// Immutable, fingerprinted build output + static icons → cache-first.
if (url.pathname.startsWith('/build/') || url.pathname.startsWith('/icons/')) {
event.respondWith(cacheFirst(req));
return;
}
// Page loads → network-first, fall back to the offline shell only when truly offline.
if (req.mode === 'navigate') {
event.respondWith(networkFirst(req));
}
});
async function cacheFirst(req) {
const cache = await caches.open(STATIC_CACHE);
const hit = await cache.match(req);
if (hit) return hit;
const res = await fetch(req);
if (res && res.ok) cache.put(req, res.clone());
return res;
}
async function networkFirst(req) {
try {
return await fetch(req);
} catch (e) {
const cache = await caches.open(STATIC_CACHE);
const offline = await cache.match(OFFLINE_URL);
return offline || Response.error();
}
}