fix(pwa): purge stale caches from app startup — clear construction-delivery-* caches directly in page context, independent of SW update

- new src/utils/pwaCacheCleanup.js: deletes all construction-delivery-* caches
  except current v115 on every app load; runs in page context so it works even
  for users still on old SW
- wire into main.jsx startup
- fixes 3GB cache bloat / 30-40s hangs for users whose SW hasn't updated
This commit is contained in:
Hermes 2026-08-18 10:52:49 +02:00
parent 981ca827a2
commit cd72507766
2 changed files with 38 additions and 0 deletions

View File

@ -8,11 +8,15 @@ import { FontSettingsProvider } from "./context/FontSettingsContext";
import ErrorBoundary from "./components/ErrorBoundary";
import { initErrorLogging } from "./utils/errorLogger";
import { registerPwaServiceWorker } from "./hooks/usePwaStatus";
import { clearStalePwaCaches } from "./utils/pwaCacheCleanup";
import "./index.css";
import "./styles/fontSettings.css";
registerPwaServiceWorker();
initErrorLogging();
// Purge stale PWA caches on every startup so bloated caches (up to 3GB from old
// SW versions) are cleared even if the SW hasn't updated yet.
clearStalePwaCaches();
ReactDOM.createRoot(document.getElementById("root")).render(
<ThemeProvider>

View File

@ -0,0 +1,34 @@
// Clears stale PWA caches directly from the page context, independent of the
// service worker. Runs on every app startup so that even users whose SW has not
// yet updated to the version with the fixed `activate` handler get their bloated
// (3GB) caches purged. Safe to run repeatedly: only `construction-delivery-*`
// caches are removed; nothing else is touched.
export const clearStalePwaCaches = async () => {
if (typeof caches === "undefined" || !("caches" in window)) {
return;
}
try {
const keys = await caches.keys();
const staleKeys = keys.filter(
(key) =>
key.startsWith("construction-delivery-") &&
// Keep only the current cache versions we ship with (v115). Any other
// version is stale and free to purge.
!["construction-delivery-static-v115", "construction-delivery-runtime-v115"].includes(key),
);
if (staleKeys.length === 0) return;
// Delete old caches first so the SW activation has less to scan next time.
await Promise.all(staleKeys.map((key) => caches.delete(key)));
// Kick the service worker to re-run its activate handler (which also prunes
// caches and reclaims control) now that old caches are gone.
if ("serviceWorker" in navigator && navigator.serviceWorker?.controller) {
navigator.serviceWorker.controller.postMessage({ type: "CLEAR_STALE_CACHES" });
}
} catch (err) {
// Best-effort cleanup — never block app startup on cache errors.
console.warn("[pwa] cache cleanup skipped:", err);
}
};