Version-stamped static URLs cached forever, thumbnail cache 25000 with throttled trimming
This commit is contained in:
@@ -46,7 +46,7 @@ const THUMB_HOSTS = new Set([
|
||||
]);
|
||||
|
||||
// Thumbnails accumulate with every card ever scrolled past — keep it bounded.
|
||||
const THUMB_CACHE_MAX = 2500;
|
||||
const THUMB_CACHE_MAX = 25000;
|
||||
function isVersionedShellCache(key) {
|
||||
return key.startsWith(SHELL_CACHE_PREFIX) && !UTILITY_CACHES.has(key);
|
||||
}
|
||||
@@ -222,7 +222,10 @@ self.addEventListener('message', (e) => {
|
||||
// Cache-first: serve from cache; if missing, fetch, cache, return.
|
||||
async function cacheFirst(request) {
|
||||
const cache = await caches.open(CACHE);
|
||||
const cached = await cache.match(request);
|
||||
// Shell files are precached under their plain URL; index.html asks for them as
|
||||
// `file?v=<build>` (so the HTTP cache can keep them forever) — match ignoring it.
|
||||
const versioned = /[?&]v=/.test(request.url);
|
||||
const cached = await cache.match(request, versioned ? { ignoreSearch: true } : undefined);
|
||||
if (cached) return cached;
|
||||
try {
|
||||
const response = await fetch(request);
|
||||
@@ -262,7 +265,7 @@ async function thumbnail(request) {
|
||||
const cors = await fetch(request.url, { mode: 'cors', credentials: 'omit' });
|
||||
if (cors && cors.ok) {
|
||||
await cache.put(request, cors.clone()).catch(() => {});
|
||||
trimThumbCache();
|
||||
if (++_thumbPuts % 100 === 0) trimThumbCache();
|
||||
return cors;
|
||||
}
|
||||
} catch { /* CORS refused or offline — fall through to the raw request */ }
|
||||
@@ -271,7 +274,8 @@ async function thumbnail(request) {
|
||||
const raw = await fetch(request);
|
||||
if (raw && (raw.status === 200 || raw.status === 0)) {
|
||||
await cache.put(request, raw.clone()).catch(() => {});
|
||||
trimThumbCache();
|
||||
// keys() on a 25k-entry cache is not free — trim every 100th put, not each one.
|
||||
if (++_thumbPuts % 100 === 0) trimThumbCache();
|
||||
}
|
||||
return raw;
|
||||
} catch {
|
||||
@@ -282,6 +286,7 @@ async function thumbnail(request) {
|
||||
|
||||
// Cache.keys() is insertion-ordered, so the front of the list is the oldest.
|
||||
let _trimmingThumbs = false;
|
||||
let _thumbPuts = 0;
|
||||
async function trimThumbCache() {
|
||||
if (_trimmingThumbs) return;
|
||||
_trimmingThumbs = true;
|
||||
|
||||
@@ -2187,7 +2187,10 @@ function indexHtml(c) {
|
||||
try { _indexSource = readFileSync('./public/index.html', 'utf8'); }
|
||||
catch { return c.text('index.html not found', 404); }
|
||||
}
|
||||
const html = _indexSource.replace('__BUILD_TAG__', BUILD_TAG);
|
||||
// Local css/js get `?v=<build>`: that URL's bytes never change, so the static
|
||||
// handler below lets browsers keep it forever; a new build is a new URL.
|
||||
const html = _indexSource.replace('__BUILD_TAG__', BUILD_TAG)
|
||||
.replace(/((?:href|src)=")((?![a-z]+:|\/)[^"?]+\.(?:css|js))(")/g, `$1$2?v=${BUILD_TAG}$3`);
|
||||
return sendCompressed(c, compressedEntry('index.html', Buffer.from(html), MIME.html), 'no-cache');
|
||||
}
|
||||
app.get('/', indexHtml);
|
||||
@@ -2203,7 +2206,11 @@ app.get('/*', async (c, next) => {
|
||||
if (!(await file.exists())) return next();
|
||||
const raw = Buffer.from(await file.arrayBuffer());
|
||||
const ext = p.slice(p.lastIndexOf('.') + 1).toLowerCase();
|
||||
return sendCompressed(c, compressedEntry(p, raw, MIME[ext] || 'application/octet-stream'), 'no-cache');
|
||||
// `?v=` equal to the running build → forever (a new build changes the URL).
|
||||
// Any other / missing v keeps revalidating.
|
||||
const forever = c.req.query('v') === BUILD_TAG;
|
||||
return sendCompressed(c, compressedEntry(p, raw, MIME[ext] || 'application/octet-stream'),
|
||||
forever ? 'public, max-age=31536000, immutable' : 'no-cache');
|
||||
});
|
||||
// Self-hosted fonts and icons rarely change: let the browser keep them for 30 days
|
||||
// (the service worker precaches the shell anyway); everything else revalidates.
|
||||
|
||||
Reference in New Issue
Block a user