fix(sw): complete cache-clearing + add controllerchange auto-reload
All cache-clearing buttons and the IDAA clear-caches page previously cleared IDB/localStorage but left service worker registrations and Cache Storage intact. On the next reload the SW re-served the old JS bundle, leaving users stuck on stale code despite appearing to reload. This caused recurring stale-state reports from IDAA and other clients for 4+ months. Two gaps closed: 1. Every clear path (root page buttons, sys bar, help tech, idaa/clear-caches) now unregisters SW registrations and clears Cache Storage before touching IDB and localStorage. Order: SW → Cache Storage → IDB → localStorage. 2. Added controllerchange listener in +layout.svelte effect 4. When the new SW activates and calls clients.claim(), this listener reloads the page so open tabs run the new JS bundle instead of keeping old code in memory indefinitely. Without this, skipWaiting + clients.claim work correctly on the SW side but the page side never picks up the update. Also added thorough code comments and updated REFERENCE__Common_Agent_Mistakes (#15) and BOOTSTRAP doc (#8) to document the full root cause so this cannot be silently re-broken by a future agent or refactor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,8 @@ interface ClearStep {
|
||||
}
|
||||
|
||||
let steps: ClearStep[] = $state([
|
||||
{ label: 'Service workers', status: 'pending', detail: '' },
|
||||
{ label: 'Service worker caches', status: 'pending', detail: '' },
|
||||
{ label: 'IndexedDB databases', status: 'pending', detail: '' },
|
||||
{ label: 'Local storage', status: 'pending', detail: '' },
|
||||
{ label: 'Session storage', status: 'pending', detail: '' }
|
||||
@@ -20,45 +22,79 @@ let overall_done = $state(false);
|
||||
let had_error = $state(false);
|
||||
|
||||
async function clear_all_caches() {
|
||||
// IDB — enumerate and delete every database on this origin
|
||||
// Service workers
|
||||
steps[0].status = 'running';
|
||||
try {
|
||||
const db_list = await indexedDB.databases();
|
||||
for (const db of db_list) {
|
||||
if (db.name) indexedDB.deleteDatabase(db.name);
|
||||
if ('serviceWorker' in navigator) {
|
||||
const registrations = await navigator.serviceWorker.getRegistrations();
|
||||
for (const reg of registrations) await reg.unregister();
|
||||
steps[0].detail = `${registrations.length} unregistered`;
|
||||
} else {
|
||||
steps[0].detail = 'Not supported';
|
||||
}
|
||||
steps[0].status = 'done';
|
||||
steps[0].detail = `${db_list.length} database${db_list.length !== 1 ? 's' : ''} cleared`;
|
||||
} catch (e) {
|
||||
steps[0].status = 'error';
|
||||
steps[0].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// localStorage
|
||||
// Cache Storage (SW asset caches)
|
||||
steps[1].status = 'running';
|
||||
try {
|
||||
localStorage.clear();
|
||||
if ('caches' in window) {
|
||||
const cache_keys = await caches.keys();
|
||||
for (const key of cache_keys) await caches.delete(key);
|
||||
steps[1].detail = `${cache_keys.length} cache${cache_keys.length !== 1 ? 's' : ''} cleared`;
|
||||
} else {
|
||||
steps[1].detail = 'Not supported';
|
||||
}
|
||||
steps[1].status = 'done';
|
||||
steps[1].detail = 'Cleared';
|
||||
} catch (e) {
|
||||
steps[1].status = 'error';
|
||||
steps[1].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// sessionStorage
|
||||
// IDB — enumerate and delete every database on this origin
|
||||
steps[2].status = 'running';
|
||||
try {
|
||||
sessionStorage.clear();
|
||||
const db_list = await indexedDB.databases();
|
||||
for (const db of db_list) {
|
||||
if (db.name) indexedDB.deleteDatabase(db.name);
|
||||
}
|
||||
steps[2].status = 'done';
|
||||
steps[2].detail = 'Cleared';
|
||||
steps[2].detail = `${db_list.length} database${db_list.length !== 1 ? 's' : ''} cleared`;
|
||||
} catch (e) {
|
||||
steps[2].status = 'error';
|
||||
steps[2].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// localStorage
|
||||
steps[3].status = 'running';
|
||||
try {
|
||||
localStorage.clear();
|
||||
steps[3].status = 'done';
|
||||
steps[3].detail = 'Cleared';
|
||||
} catch (e) {
|
||||
steps[3].status = 'error';
|
||||
steps[3].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
// sessionStorage
|
||||
steps[4].status = 'running';
|
||||
try {
|
||||
sessionStorage.clear();
|
||||
steps[4].status = 'done';
|
||||
steps[4].detail = 'Cleared';
|
||||
} catch (e) {
|
||||
steps[4].status = 'error';
|
||||
steps[4].detail = String(e);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
overall_done = true;
|
||||
|
||||
// Notify parent window (Novi page) that the clear is complete.
|
||||
|
||||
Reference in New Issue
Block a user