build: auto-stamp service worker cache version with the build hash

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-<buildDate>-<hash8>`
(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 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.8 committed 2026-06-16 19:42:47 -04:00
1 parent 066d98febb
commit 4c5465eb35
1 file changed
+39 -1
+39 -1
View File
@@ -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.');
}