From 4c5465eb35d4298cf7f385aa13ad4d8810c1b8b4 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Tue, 16 Jun 2026 19:42:47 -0400 Subject: [PATCH] build: auto-stamp service worker cache version with the build hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SW cache key (bitvid-sw-YYYY-MM-DD) and the informational meta in index.html/embed.html were hand-bumped, so client caches only invalidated when someone remembered to change the date — stale app shells (and stale footer versions) persisted across deploys. build-dist now stamps the dist artifacts with `bitvid-sw--` (same content hash shown in the footer), so every content change yields a new cache name and the SW's activate step drops the old cache automatically. Only the dist output is rewritten — the committed (minified) sw.min.js source is left as-is, so the sw-compat guard and tests that reference the source value are unaffected. Build verified: all three dist files share one stamped version, dist/sw.min.js still parses, verify:dist:deploy-artifact passes. Also confirmed `npm run build` is reproducible (same inputs => same hash), so deploys are deterministic. Co-Authored-By: Claude Opus 4.8 --- scripts/build-dist.mjs | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/scripts/build-dist.mjs b/scripts/build-dist.mjs index dcdd9f51..71651902 100644 --- a/scripts/build-dist.mjs +++ b/scripts/build-dist.mjs @@ -190,12 +190,49 @@ function injectVersionInfo() { } } } + + // Return the build-derived service worker cache version so the SW + meta + // can be stamped with the same hash (see stampServiceWorkerVersion). + return `bitvid-sw-${date}-${hash.slice(0, 8)}`; } catch (error) { console.error('Failed to inject version info:', error); process.exit(1); } } +// The service worker cache key (and the informational meta in the entry HTML) +// was a hand-bumped date string, so caches only invalidated when someone +// remembered to change it. Stamp it with the build hash at build time so EVERY +// content change produces a new cache name -> the SW's activate step drops the +// stale cache automatically. We rewrite only the dist artifacts, leaving the +// committed (minified) source untouched so the sw-compat guard and unit tests +// that reference the source value are unaffected. +const SW_VERSION_PATTERN = /bitvid-sw-\d{4}-\d{2}-\d{2}(?:-[0-9a-f]{6,})?/g; +const SW_VERSIONED_DIST_FILES = ['sw.min.js', 'index.html', 'embed.html']; + +function stampServiceWorkerVersion(version) { + if (!version || !/^bitvid-sw-/.test(version)) { + throw new Error(`Refusing to stamp an invalid service worker version: ${version}`); + } + console.log(`Stamping service worker cache version: ${version}`); + for (const file of SW_VERSIONED_DIST_FILES) { + const filePath = path.join(DIST, file); + if (!fs.existsSync(filePath)) { + throw new Error(`Cannot stamp SW version: dist/${file} is missing.`); + } + const original = fs.readFileSync(filePath, 'utf8'); + const matches = original.match(SW_VERSION_PATTERN); + if (!matches || matches.length === 0) { + throw new Error( + `Cannot stamp SW version: no 'bitvid-sw-*' cache key found in dist/${file}. ` + + 'The service worker cache-busting contract changed — update build-dist.mjs.' + ); + } + fs.writeFileSync(filePath, original.replace(SW_VERSION_PATTERN, version)); + console.log(` ${file}: stamped ${matches.length} reference(s)`); + } +} + function main() { console.log('Validating service worker compatibility guard...'); try { @@ -257,7 +294,8 @@ function main() { console.log('Rewriting HTML entry points with hashed assets...'); rewriteEntryHtmlAssetPaths(manifest); - injectVersionInfo(); + const swVersion = injectVersionInfo(); + stampServiceWorkerVersion(swVersion); console.log('Build complete.'); }