betterwithage commited on
Commit
8b396f1
·
verified ·
1 Parent(s): 688a973

fix(a11oy app): null-safe DOM writes (eliminate innerHTML race when navigating during async fetch)

Browse files
Files changed (1) hide show
  1. 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=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[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;const host=el('fleet');host.innerHTML='';
173
  entries.forEach(([n,p])=>{const up=(p.status||'').toLowerCase()==='ok'||p.reachable===true||p.http_code===200;if(up)ok++;
174
- host.insertAdjacentHTML('beforeend',`<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>`);});
175
- el('k-fleet').textContent=entries.length?ok+'/'+entries.length:'—';}catch(e){el('fleet').innerHTML='<div class="row mono dim">fleet retrying… (transient egress)</div>';}
176
  // mesh
177
- try{const d=await getJSON(API+'/v1/mesh/state');const w=d.wires||{};el('k-wires').textContent=Object.keys(w).length;const h=el('cmesh');h.innerHTML='';
178
- Object.entries(w).forEach(([k,v])=>h.insertAdjacentHTML('beforeend',`<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){el('cmesh').innerHTML='<div class="row mono dim">mesh retrying…</div>';}
179
  // evidence
180
- try{const d=await getJSON(API+'/v1/evidence');if(d.passed!=null)el('k-evid').textContent=d.passed+'/'+d.total_assertions;const h=el('cevid');
181
- h.innerHTML=`<div class="row"><span>Declarations</span><span class="spacer b-gold badge">${d.canonical?.declarations??'—'}</span></div>
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){el('cevid').innerHTML='<div class="row mono dim">evidence retrying…</div>';}
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
- el('ok-mesh').textContent=(m?m[1]:'?')+'/'+(t?t[1]:'?');const mr=d.mesh_reach||{};const h=el('obs-mesh');h.innerHTML='';
237
- Object.entries(mr).forEach(([k,v])=>h.insertAdjacentHTML('beforeend',`<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>`));
238
- if(!Object.keys(mr).length)h.innerHTML='<div class="row mono dim">summary loaded — see KPI</div>';}catch(e){el('obs-mesh').innerHTML='<div class="row mono dim">retry: '+e.message+'</div>';}
239
- try{const s=await getJSON(API+'/v1/observability/spans');el('ok-spans').textContent=(s.spans||s.nodes||[]).length||s.signed_spans||'—';el('ok-chain').textContent=s.chain_verified?'YES':(s.chain_verified===false?'no':'—');setOut('obs-spans',s);}catch(e){setOut('obs-spans','retry: '+e.message);}}
240
  // wires
241
- async function wires_load(){const h=el('wires-host');try{const d=await getJSON(API+'/v1/mesh/state');const w=d.wires||{};h.innerHTML='';
242
- Object.entries(w).forEach(([k,v])=>h.insertAdjacentHTML('beforeend',`<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){h.innerHTML='<div class="row mono dim">retry: '+e.message+'</div>';}}
243
  // mesh
244
- async function mesh_load(){const h=el('mesh-host');try{const d=await getJSON(API+'/v1/observability/summary');const mr=d.mesh_reach||{};h.innerHTML='';
245
- if(!Object.keys(mr).length){h.innerHTML='<pre class="out">'+esc(JSON.stringify(d,null,2))+'</pre>';return;}
246
- Object.entries(mr).forEach(([k,v])=>h.insertAdjacentHTML('beforeend',`<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){h.innerHTML='<div class="row mono dim">retry: '+e.message+'</div>';}}
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=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[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));