diff --git a/src/main.jsx b/src/main.jsx index 391bef8..8c23890 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -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( diff --git a/src/utils/pwaCacheCleanup.js b/src/utils/pwaCacheCleanup.js new file mode 100644 index 0000000..18e9f35 --- /dev/null +++ b/src/utils/pwaCacheCleanup.js @@ -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); + } +};