Service worker caches app shell + images for offline recipe access in the kitchen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
/// <reference types="@sveltejs/kit" />
|
|
/// <reference no-default-lib="true"/>
|
|
/// <reference lib="esnext" />
|
|
/// <reference lib="webworker" />
|
|
|
|
import { build, files, version } from '$service-worker';
|
|
|
|
const sw = self as unknown as ServiceWorkerGlobalScope;
|
|
|
|
const APP_CACHE = `kochwas-app-${version}`;
|
|
const IMAGE_CACHE = `kochwas-images-v1`;
|
|
const APP_ASSETS = [...build, ...files];
|
|
|
|
sw.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(APP_CACHE).then((cache) => cache.addAll(APP_ASSETS))
|
|
);
|
|
// Activate new worker without waiting for old clients to close.
|
|
void sw.skipWaiting();
|
|
});
|
|
|
|
sw.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
(async () => {
|
|
const keys = await caches.keys();
|
|
await Promise.all(
|
|
keys
|
|
.filter((k) => k.startsWith('kochwas-app-') && k !== APP_CACHE)
|
|
.map((k) => caches.delete(k))
|
|
);
|
|
await sw.clients.claim();
|
|
})()
|
|
);
|
|
});
|
|
|
|
sw.addEventListener('fetch', (event) => {
|
|
const req = event.request;
|
|
if (req.method !== 'GET') return;
|
|
|
|
const url = new URL(req.url);
|
|
if (url.origin !== location.origin) return;
|
|
|
|
// Images served from /images/* — cache-first with background update
|
|
if (url.pathname.startsWith('/images/')) {
|
|
event.respondWith(
|
|
(async () => {
|
|
const cache = await caches.open(IMAGE_CACHE);
|
|
const cached = await cache.match(req);
|
|
const network = fetch(req)
|
|
.then((res) => {
|
|
if (res.ok) void cache.put(req, res.clone());
|
|
return res;
|
|
})
|
|
.catch(() => undefined);
|
|
return cached ?? (await network) ?? new Response('Offline', { status: 503 });
|
|
})()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// App shell assets (build/* and static files) — cache-first
|
|
if (APP_ASSETS.includes(url.pathname)) {
|
|
event.respondWith(
|
|
(async () => {
|
|
const cache = await caches.open(APP_CACHE);
|
|
const cached = await cache.match(req);
|
|
return cached ?? fetch(req);
|
|
})()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// API and HTML pages — network-first, fall back to cache for HTML
|
|
if (req.destination === 'document') {
|
|
event.respondWith(
|
|
(async () => {
|
|
try {
|
|
const res = await fetch(req);
|
|
const cache = await caches.open(APP_CACHE);
|
|
if (res.ok) void cache.put(req, res.clone());
|
|
return res;
|
|
} catch {
|
|
const cached = await caches.match(req);
|
|
return cached ?? new Response('Offline', { status: 503 });
|
|
}
|
|
})()
|
|
);
|
|
}
|
|
});
|