From b9ece021d4f8637a8268defcf99ed2f895d8d3c6 Mon Sep 17 00:00:00 2001 From: ergosteur <1992147+ergosteur@users.noreply.github.com> Date: Fri, 14 Aug 2026 13:16:25 -0400 Subject: [PATCH] fix: make server header changes reach installed PWA clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The service worker precaches index.html together with its response headers, so a server-only change never reaches an installed client: the client build is byte-identical, the precache manifest is unchanged, and the worker has no reason to update. That is why the CSP fix in 1.6.1 did not reach a browser that already had the app cached — it kept replaying a cached shell carrying the old, broken CSP, indefinitely. The release version is now compiled into the client, which makes every release change the bundle hash, hence index.html, hence its precache revision, hence sw.js itself — the bytes browsers compare to decide whether to update. Verified by bumping only the version: index-DYufL2Fa.js -> index-60N_3d5j.js, with the new name carried into the sw.js manifest. It also surfaces in the footer, so the deployed version is visible without digging through devtools. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011uBWhwV3wFQ5MBCcMHHem7 --- package-lock.json | 4 ++-- package.json | 2 +- src/App.tsx | 2 +- src/main.tsx | 2 +- src/types/globals.d.ts | 9 +++++++++ vite.config.ts | 15 +++++++++++++++ 6 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/types/globals.d.ts diff --git a/package-lock.json b/package-lock.json index bcde33d..248d735 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "instaarchive-viewer", - "version": "1.6.1", + "version": "1.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "instaarchive-viewer", - "version": "1.6.1", + "version": "1.6.2", "dependencies": { "@tailwindcss/vite": "^4.1.14", "@vitejs/plugin-react": "^5.0.4", diff --git a/package.json b/package.json index 4c77021..a157903 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "instaarchive-viewer", "private": true, - "version": "1.6.1", + "version": "1.6.2", "type": "module", "scripts": { "dev": "vite --port=3000 --host=0.0.0.0", diff --git a/src/App.tsx b/src/App.tsx index 23cdd05..ec918c0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -632,7 +632,7 @@ export default function App() { {!isScanning && ( )} diff --git a/src/main.tsx b/src/main.tsx index b1e7fbf..4990a4f 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -14,7 +14,7 @@ const updateSW = registerSW({ setInterval(() => { r.update(); }, 60 * 60 * 1000); - console.log('[PWA] Service Worker registered and update interval set.'); + console.log(`[PWA] v${__APP_VERSION__} registered; hourly update checks enabled.`); } }, onNeedRefresh() { diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts new file mode 100644 index 0000000..ead0908 --- /dev/null +++ b/src/types/globals.d.ts @@ -0,0 +1,9 @@ +/** + * Build-time constants. + * + * This file deliberately has no imports or exports: that keeps it an ambient + * script rather than a module, so the declarations below are global. + */ + +/** Release version, injected by `define` in vite.config.ts. */ +declare const __APP_VERSION__: string; diff --git a/vite.config.ts b/vite.config.ts index 3e2fcfa..78c9506 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,9 +3,24 @@ import react from '@vitejs/plugin-react'; import path from 'path'; import {defineConfig} from 'vite'; import { VitePWA } from 'vite-plugin-pwa'; +import { createRequire } from 'module'; + +const { version } = createRequire(import.meta.url)('./package.json'); export default defineConfig(() => { return { + /** + * The release version, compiled into the client. + * + * This is load-bearing, not cosmetic. The service worker precaches + * index.html *including its response headers*, so a server-side header + * change (a CSP fix, say) never reaches an installed PWA: nothing in the + * client build changed, the precache manifest is byte-identical, and the + * worker has no reason to update. Baking the version in means every release + * changes the bundle hash, which changes index.html, which invalidates the + * precache and re-fetches the shell with current headers. + */ + define: { __APP_VERSION__: JSON.stringify(version) }, plugins: [ react(), tailwindcss(),