75 lines
2.5 KiB
JavaScript
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();
|
|
}
|
|
}
|