1/** 2 * Service worker registration + one-time incident auto-recovery. 3 * 4 * AUTO-RESET (transparent to the user): 5 * Older builds (v19.x) shipped service workers that intercepted clean-URL 6 * navigations and cached HTML/JS aggressively. After deploying the v20.0.0 7 * safe-mode worker, devices that already had the old worker installed can 8 * still see "wonky" loading because old caches + a fresh SW briefly coexist. 9 * 10 * To avoid asking 200 members to type "?reset=1", every browser that hasn't 11 * yet seen the current INCIDENT_RESET_VERSION will, exactly once: 12 * 1. unregister every existing service worker registration on this origin, 13 * 2. delete every Cache Storage entry, 14 * 3. set a localStorage flag so this never runs again on this device, 15 * 4. reload the page so the next paint is a fully clean cold-start. 16 * 17 * After that one auto-reload the browser is on the safe-mode SW (v20.0.0) 18 * with no stale caches and behaves like plain HTML over Firebase Hosting. 19 * 20 * Once recovery is complete, this file just (a) registers the SW once per tab 21 * and (b) optionally checks for SW updates at most once every 6 hours per 22 * device. No periodic update polling. No controllerchange auto-reload loops. 23 */ 24(function () { 25 if (typeof window === 'undefined') return; 26 27 // Bump this string ONLY when another incident requires a forced device-wide 28 // reset for users who picked up a bad build. Increment the date/suffix. 29 var INCIDENT_RESET_VERSION = '2026-05-14-incident-1'; 30 var FLAG_KEY = 'atchison_incident_reset_version'; 31 var IN_PROGRESS_FLAG = 'atchison_incident_reset_in_progress'; 32 33 function safeLocalGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } } 34 function safeLocalSet(k, v) { try { localStorage.setItem(k, v); } catch (e) {} } 35 function safeSessionGet(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } } 36 function safeSessionSet(k, v) { try { sessionStorage.setItem(k, v); } catch (e) {} } 37 function safeSessionRemove(k) { try { sessionStorage.removeItem(k); } catch (e) {} } 38 39 // Belt-and-suspenders: localStorage may be disabled (incognito + extra-strict 40 // tracking protection). Also accept sessionStorage as proof that recovery ran 41 // at least once this session, so we never get stuck in a reload loop. 42 function resetAlreadyRan() { 43 return safeLocalGet(FLAG_KEY) === INCIDENT_RESET_VERSION || 44 safeSessionGet(FLAG_KEY) === INCIDENT_RESET_VERSION; 45 } 46 function markResetRan() { 47 safeLocalSet(FLAG_KEY, INCIDENT_RESET_VERSION); 48 safeSessionSet(FLAG_KEY, INCIDENT_RESET_VERSION); 49 } 50 51 // If we're mid-reset on this tab, do NOT loop. Clear the in-progress flag 52 // (it only needs to survive one reload) and continue to normal registration. 53 if (safeSessionGet(IN_PROGRESS_FLAG)) { 54 safeSessionRemove(IN_PROGRESS_FLAG); 55 markResetRan(); 56 } else if (!resetAlreadyRan()) { 57 // Mark as done BEFORE doing anything so we cannot loop even if the reload 58 // races with another script. Also set a tab-local flag so the post-reload 59 // load doesn't re-trigger when localStorage has just been written. 60 markResetRan(); 61 safeSessionSet(IN_PROGRESS_FLAG, '1'); 62 63 (async function recoverOnce() { 64 try { 65 if ('serviceWorker' in navigator) { 66 var regs = []; 67 try { regs = await navigator.serviceWorker.getRegistrations(); } catch (_) {} 68 await Promise.all((regs || []).map(function (r) { 69 try { return r.unregister(); } catch (_) { return Promise.resolve(); } 70 })); 71 } 72 } catch (_) {} 73 try { 74 if ('caches' in window) { 75 var names = []; 76 try { names = await caches.keys(); } catch (_) {} 77 await Promise.all((names || []).map(function (n) { 78 try { return caches.delete(n); } catch (_) { return Promise.resolve(); } 79 })); 80 } 81 } catch (_) {} 82 // Hard reload â bypass HTTP cache via cache-buster. 83 try { 84 var u = new URL(window.location.href); 85 u.searchParams.set('_v', Date.now()); 86 window.location.replace(u.toString()); 87 } catch (_) { 88 try { window.location.reload(); } catch (_) {} 89 } 90 })(); 91 return; // Do NOT register a SW on this load; the next load will. 92 } 93 94 // ââ Normal SW registration (post-recovery) ââââââââââââââââââââââââââââââ 95 if (!('serviceWorker' in navigator)) return; 96 if (window.__atchisonSwRegisterDone) return; 97 window.__atchisonSwRegisterDone = true; 98 99 var opts = { updateViaCache: 'none' }; 100 navigator.serviceWorker.register('./sw.js', opts).catch(function () {}); 101 102 try { 103 var k = 'atchison_sw_update_check_at'; 104 var last = parseInt(localStorage.getItem(k) || '0', 10) || 0; 105 var now = Date.now(); 106 var sixHours = 6 * 60 * 60 * 1000; 107 if (now - last >= sixHours) { 108 localStorage.setItem(k, String(now)); 109 navigator.serviceWorker.getRegistration().then(function (reg) {
110 if (reg) return reg.update(); 111 }).catch(function () {}); 112 } 113 } catch (e) {} 114})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.