Spaces:
Running
Running
fix(a11oy app): null-safe DOM writes (eliminate innerHTML race when navigating during async fetch)
Browse files- pages/console.html +20 -17
pages/console.html
CHANGED
|
@@ -150,6 +150,9 @@ async function postJSON(p,b){const r=await fetch(p,{method:'POST',headers:{'Cont
|
|
| 150 |
function esc(s){return String(s==null?'':s).replace(/[&<>]/g,c=>({'&':'&','<':'<','>':'>'}[c]));}
|
| 151 |
function el(id){return document.getElementById(id);}
|
| 152 |
function setOut(id,obj){const e=el(id);if(e)e.textContent=typeof obj==='string'?obj:JSON.stringify(obj,null,2);}
|
|
|
|
|
|
|
|
|
|
| 153 |
|
| 154 |
const HONEST='<div class="honesty"><b>Honesty.</b> Every panel reads a live a11oy endpoint — no mock data. Λ is <b>Conjecture 1</b>, never a theorem. Proved formulas = <b>5</b> {F1,F11,F12,F18,F19}. SLSA <b>Build L2</b> on all 5 organ images. No L3 / FedRAMP / Iron Bank / CMMC. Receipts are real DSSE when a key is present, honest UNSIGNED otherwise.</div>';
|
| 155 |
|
|
@@ -169,19 +172,19 @@ const VIEWS={
|
|
| 169 |
</div>${HONEST}`;
|
| 170 |
// fleet
|
| 171 |
try{const d=await getJSON('/v4/fleet');const peers=d.peers||d.organs||{};const entries=Array.isArray(peers)?peers.map(p=>[p.flagship||p.name,p]):Object.entries(peers);
|
| 172 |
-
let ok=0;
|
| 173 |
entries.forEach(([n,p])=>{const up=(p.status||'').toLowerCase()==='ok'||p.reachable===true||p.http_code===200;if(up)ok++;
|
| 174 |
-
|
| 175 |
-
|
| 176 |
// mesh
|
| 177 |
-
try{const d=await getJSON(API+'/v1/mesh/state');const w=d.wires||{};
|
| 178 |
-
Object.entries(w).forEach(([k,v])=>
|
| 179 |
// evidence
|
| 180 |
-
try{const d=await getJSON(API+'/v1/evidence');if(d.passed!=null)
|
| 181 |
-
|
| 182 |
<div class="row"><span>Axioms</span><span class="spacer b-gold badge">${d.canonical?.axioms??'—'}</span></div>
|
| 183 |
<div class="row"><span>Assertions passed</span><span class="spacer b-live badge">${d.passed}/${d.total_assertions}</span></div>
|
| 184 |
-
<div class="row"><span>Lean repo</span><span class="spacer mono dim">lutar-lean</span></div>`;}catch(e){
|
| 185 |
}},
|
| 186 |
superpowers:{title:'Five Superpowers',badge:'LIVE',sub:'Five screenshot-provable superpowers. Each button POSTs/GETs a real a11oy endpoint and renders the raw response — Decision Replay, Tamper Theater, Λ-collapse, Resurrection, One-Signed-Organism.',
|
| 187 |
render:(c)=>{c.innerHTML=`
|
|
@@ -233,17 +236,17 @@ async function wh_one(key,out){try{setOut(out,'launching…');const d=await post
|
|
| 233 |
async function wh_all(){for(const k of ['cannonico','tychee','hangar2apps','cyber-rts','raven']){await wh_one(k,'o-'+k);}}
|
| 234 |
// observability
|
| 235 |
async function obs_load(){try{const d=await getJSON(API+'/v1/observability/summary');const raw=JSON.stringify(d);const m=raw.match(/"organs_reachable":\s*(\d+)/),t=raw.match(/"organs_total":\s*(\d+)/);
|
| 236 |
-
|
| 237 |
-
Object.entries(mr).forEach(([k,v])=>
|
| 238 |
-
if(!Object.keys(mr).length)
|
| 239 |
-
try{const s=await getJSON(API+'/v1/observability/spans');
|
| 240 |
// wires
|
| 241 |
-
async function wires_load(){
|
| 242 |
-
Object.entries(w).forEach(([k,v])=>
|
| 243 |
// mesh
|
| 244 |
-
async function mesh_load(){
|
| 245 |
-
if(!Object.keys(mr).length){
|
| 246 |
-
Object.entries(mr).forEach(([k,v])=>
|
| 247 |
|
| 248 |
function go(view){
|
| 249 |
document.querySelectorAll('.nav-item').forEach(n=>n.classList.toggle('active',n.dataset.view===view));
|
|
|
|
| 150 |
function esc(s){return String(s==null?'':s).replace(/[&<>]/g,c=>({'&':'&','<':'<','>':'>'}[c]));}
|
| 151 |
function el(id){return document.getElementById(id);}
|
| 152 |
function setOut(id,obj){const e=el(id);if(e)e.textContent=typeof obj==='string'?obj:JSON.stringify(obj,null,2);}
|
| 153 |
+
function setHTML(id,html){const e=el(id);if(e)e.innerHTML=html;}
|
| 154 |
+
function setTxt(id,t){const e=el(id);if(e)e.textContent=t;}
|
| 155 |
+
function addHTML(id,html){const e=el(id);if(e)e.insertAdjacentHTML('beforeend',html);}
|
| 156 |
|
| 157 |
const HONEST='<div class="honesty"><b>Honesty.</b> Every panel reads a live a11oy endpoint — no mock data. Λ is <b>Conjecture 1</b>, never a theorem. Proved formulas = <b>5</b> {F1,F11,F12,F18,F19}. SLSA <b>Build L2</b> on all 5 organ images. No L3 / FedRAMP / Iron Bank / CMMC. Receipts are real DSSE when a key is present, honest UNSIGNED otherwise.</div>';
|
| 158 |
|
|
|
|
| 172 |
</div>${HONEST}`;
|
| 173 |
// fleet
|
| 174 |
try{const d=await getJSON('/v4/fleet');const peers=d.peers||d.organs||{};const entries=Array.isArray(peers)?peers.map(p=>[p.flagship||p.name,p]):Object.entries(peers);
|
| 175 |
+
let ok=0;setHTML('fleet','');
|
| 176 |
entries.forEach(([n,p])=>{const up=(p.status||'').toLowerCase()==='ok'||p.reachable===true||p.http_code===200;if(up)ok++;
|
| 177 |
+
addHTML('fleet',`<div class="row"><span class="badge ${up?'b-live':'b-err'}">${up?'OK':'DOWN'}</span><span>${esc(n||'organ')}</span><span class="spacer mono dim">${p.latency_ms?Math.round(p.latency_ms)+'ms':''}</span></div>`);});
|
| 178 |
+
setTxt('k-fleet',entries.length?ok+'/'+entries.length:'—');}catch(e){setHTML('fleet','<div class="row mono dim">fleet retrying… (transient egress)</div>');}
|
| 179 |
// mesh
|
| 180 |
+
try{const d=await getJSON(API+'/v1/mesh/state');const w=d.wires||{};setTxt('k-wires',Object.keys(w).length);setHTML('cmesh','');
|
| 181 |
+
Object.entries(w).forEach(([k,v])=>addHTML('cmesh',`<div class="row"><span class="badge b-teal">${k}</span><span>${esc(v.edge)}</span><span class="spacer badge ${String(v.status).includes('PROCESS')?'b-gold':'b-live'}">${esc(v.status)}</span></div>`));}catch(e){setHTML('cmesh','<div class="row mono dim">mesh retrying…</div>');}
|
| 182 |
// evidence
|
| 183 |
+
try{const d=await getJSON(API+'/v1/evidence');if(d.passed!=null)setTxt('k-evid',d.passed+'/'+d.total_assertions);
|
| 184 |
+
setHTML('cevid',`<div class="row"><span>Declarations</span><span class="spacer b-gold badge">${d.canonical?.declarations??'—'}</span></div>
|
| 185 |
<div class="row"><span>Axioms</span><span class="spacer b-gold badge">${d.canonical?.axioms??'—'}</span></div>
|
| 186 |
<div class="row"><span>Assertions passed</span><span class="spacer b-live badge">${d.passed}/${d.total_assertions}</span></div>
|
| 187 |
+
<div class="row"><span>Lean repo</span><span class="spacer mono dim">lutar-lean</span></div>`);}catch(e){setHTML('cevid','<div class="row mono dim">evidence retrying…</div>');}
|
| 188 |
}},
|
| 189 |
superpowers:{title:'Five Superpowers',badge:'LIVE',sub:'Five screenshot-provable superpowers. Each button POSTs/GETs a real a11oy endpoint and renders the raw response — Decision Replay, Tamper Theater, Λ-collapse, Resurrection, One-Signed-Organism.',
|
| 190 |
render:(c)=>{c.innerHTML=`
|
|
|
|
| 236 |
async function wh_all(){for(const k of ['cannonico','tychee','hangar2apps','cyber-rts','raven']){await wh_one(k,'o-'+k);}}
|
| 237 |
// observability
|
| 238 |
async function obs_load(){try{const d=await getJSON(API+'/v1/observability/summary');const raw=JSON.stringify(d);const m=raw.match(/"organs_reachable":\s*(\d+)/),t=raw.match(/"organs_total":\s*(\d+)/);
|
| 239 |
+
setTxt('ok-mesh',(m?m[1]:'?')+'/'+(t?t[1]:'?'));const mr=d.mesh_reach||{};setHTML('obs-mesh','');
|
| 240 |
+
Object.entries(mr).forEach(([k,v])=>addHTML('obs-mesh',`<div class="row"><span class="badge ${(v.status==='ok')?'b-live':'b-err'}">${esc(v.status)}</span><span>${esc(k)}</span><span class="spacer mono dim">${v.latency_ms?Math.round(v.latency_ms)+'ms':''} ${esc(v.url||'')}</span></div>`));
|
| 241 |
+
if(!Object.keys(mr).length)setHTML('obs-mesh','<div class="row mono dim">summary loaded — see KPI</div>');}catch(e){setHTML('obs-mesh','<div class="row mono dim">retry: '+e.message+'</div>');}
|
| 242 |
+
try{const s=await getJSON(API+'/v1/observability/spans');setTxt('ok-spans',(s.spans||s.nodes||[]).length||s.signed_spans||'—');setTxt('ok-chain',s.chain_verified?'YES':(s.chain_verified===false?'no':'—'));setOut('obs-spans',s);}catch(e){setOut('obs-spans','retry: '+e.message);}}
|
| 243 |
// wires
|
| 244 |
+
async function wires_load(){try{const d=await getJSON(API+'/v1/mesh/state');const w=d.wires||{};setHTML('wires-host','');
|
| 245 |
+
Object.entries(w).forEach(([k,v])=>addHTML('wires-host',`<div class="card"><div class="card-h"><span class="badge b-teal">Wire ${k}</span><span class="card-t">${esc(v.edge)}</span><span class="spacer badge ${String(v.status).includes('PROCESS')?'b-gold':'b-live'}">${esc(v.status)}</span></div><div class="mono" style="font-size:11.5px;color:var(--paragraph)">${esc(v.detail)}</div></div>`));}catch(e){setHTML('wires-host','<div class="row mono dim">retry: '+e.message+'</div>');}}
|
| 246 |
// mesh
|
| 247 |
+
async function mesh_load(){try{const d=await getJSON(API+'/v1/observability/summary');const mr=d.mesh_reach||{};setHTML('mesh-host','');
|
| 248 |
+
if(!Object.keys(mr).length){setHTML('mesh-host','<pre class="out">'+esc(JSON.stringify(d,null,2))+'</pre>');return;}
|
| 249 |
+
Object.entries(mr).forEach(([k,v])=>addHTML('mesh-host',`<div class="row"><span class="badge ${(v.status==='ok')?'b-live':'b-err'}">${esc(v.status)}</span><span>${esc(k)}</span><span class="spacer mono dim">${v.latency_ms?Math.round(v.latency_ms)+'ms':''} · ${esc(v.url||'')}</span></div>`));}catch(e){setHTML('mesh-host','<div class="row mono dim">retry: '+e.message+'</div>');}}
|
| 250 |
|
| 251 |
function go(view){
|
| 252 |
document.querySelectorAll('.nav-item').forEach(n=>n.classList.toggle('active',n.dataset.view===view));
|