Hashed assets are served as immutable for a year. Before the nginx MIME fix, the pdf.js worker was served as application/octet-stream, and the browser kept that response. Each new service worker then copied it from the HTTP cache into its offline cache, so PDFs still failed to render after the server was fixed. Fetching with cache: 'reload' always takes the current response from the server. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VsorPV3JeJRoZ1mD1pdvtL
63 lines
1.8 KiB
TypeScript
63 lines
1.8 KiB
TypeScript
/// <reference types="@sveltejs/kit" />
|
|
/// <reference no-default-lib="true"/>
|
|
/// <reference lib="esnext" />
|
|
/// <reference lib="webworker" />
|
|
|
|
// Offline-first: the app shell and static assets are cached on install.
|
|
// GitHub API calls are never cached (sync needs live data).
|
|
|
|
import { build, files, version } from '$service-worker';
|
|
|
|
const sw = self as unknown as ServiceWorkerGlobalScope;
|
|
const CACHE = `papure-${version}`;
|
|
const ASSETS = [...build, ...files, '/'];
|
|
|
|
sw.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.open(CACHE)
|
|
// Bypass the HTTP cache: hashed assets are cached as immutable, so a bad
|
|
// response (e.g. a wrong MIME type) would otherwise be copied in forever.
|
|
.then((c) => c.addAll(ASSETS.map((url) => new Request(url, { cache: 'reload' }))))
|
|
.then(() => sw.skipWaiting())
|
|
);
|
|
});
|
|
|
|
sw.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.keys()
|
|
.then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))))
|
|
.then(() => 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 !== sw.location.origin) return;
|
|
|
|
// Navigations: network first, fall back to the cached shell (SPA).
|
|
if (req.mode === 'navigate') {
|
|
event.respondWith(
|
|
fetch(req).catch(async () => (await caches.match('/')) ?? Response.error())
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Assets: cache first (they're versioned or fixed).
|
|
event.respondWith(
|
|
caches.match(req).then(
|
|
(hit) =>
|
|
hit ??
|
|
fetch(req).then((res) => {
|
|
if (res.ok && ASSETS.includes(url.pathname)) {
|
|
const copy = res.clone();
|
|
void caches.open(CACHE).then((c) => c.put(req, copy));
|
|
}
|
|
return res;
|
|
})
|
|
)
|
|
);
|
|
});
|