← Public packages
@kentcdodds/lineage
Interactive commit-level lines of kody, minus generated noise, as an offline-ready chart.
src/ui/sw-script.ts
77 lines · 2.4 KB · TypeScriptimport { scopedAssetPath } from './app-url.ts'
export function serviceWorkerScript(scope: string) {
const root = scope.endsWith('/') ? scope : `${scope}/`
const shell = [
scopedAssetPath(root, 'manifest.webmanifest'),
scopedAssetPath(root, 'favicon.png'),
scopedAssetPath(root, 'apple-touch-icon.png'),
scopedAssetPath(root, 'pwa-192.png'),
scopedAssetPath(root, 'icon.svg'),
]
return `const SCOPE = ${JSON.stringify(root)};
const VERSION = 'lineage-v9';
const SHELL = ${JSON.stringify(shell)};
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(VERSION).then((cache) =>
Promise.all(SHELL.map((url) => cache.add(url).catch(() => undefined))),
).then(() => {
if (!self.registration.active) return self.skipWaiting();
}),
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((key) => key !== VERSION).map((key) => caches.delete(key))),
).then(() => self.clients.claim()),
);
});
self.addEventListener('message', (event) => {
if (event.data === 'SKIP_WAITING') self.skipWaiting();
});
async function staleWhileRevalidate(request, cacheFirst) {
const cache = await caches.open(VERSION);
const cached = await cache.match(request);
const network = fetch(request).then((response) => {
if (response.ok) cache.put(request, response.clone());
return response;
});
if (cacheFirst && cached) {
network.catch(() => undefined);
return cached;
}
try {
return await network;
} catch (error) {
if (cached) return cached;
throw error;
}
}
self.addEventListener('fetch', (event) => {
const request = event.request;
if (request.method !== 'GET') return;
const url = new URL(request.url);
const isApi = url.pathname.includes('/api/');
const isDocument = request.mode === 'navigate' || url.pathname === SCOPE || url.pathname === SCOPE.slice(0, -1);
if (isApi || isDocument) {
const cacheFirst = isDocument || request.cache === 'default' || request.cache === 'force-cache';
event.respondWith(staleWhileRevalidate(request, cacheFirst));
return;
}
event.respondWith(
caches.match(request).then((cached) => cached || fetch(request).then((response) => {
const copy = response.clone();
caches.open(VERSION).then((cache) => cache.put(request, copy));
return response;
})),
);
});
`
}