tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

asset-cache-probe.mjs (4629B)


      1 // Count requests at the server: cached DevTools entries are not network traffic.
      2 const el=id=>document.getElementById(id),delay=ms=>new Promise(r=>setTimeout(r,ms));
      3 const assert=(ok,message)=>{if(!ok)throw Error(message);};
      4 async function status(wanted){for(let i=0;i<4500;i++){if(el('status').textContent===wanted)return;await delay(20);}throw Error('Waiting for '+wanted+': '+el('status').textContent+' '+el('compile-diagnostic').textContent);}
      5 const change=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('change'));};
      6 const input=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('input'));};
      7 export async function run(control,backend='haskell-wasm'){
      8  await status('Ready');
      9  if(el('runtime-backend').value!==backend){change('runtime-backend',backend);await status('Ready');}
     10  const base=new URL('../',document.querySelector('script[type=module][src]').src);
     11  assert(/\/assets\/[a-f0-9]{64}\/$/.test(base.pathname),'Testing a versioned release');
     12  const html=await fetch(new URL('index.html?noProbe=1',location.href));
     13  assert(html.headers.get('Cache-Control')==='no-cache','HTML selects the current release through revalidation');
     14  const metadata=await fetch(new URL('compiler.json',base));
     15  assert(metadata.headers.get('Cache-Control')==='public, max-age=31536000, immutable','Metadata shares the immutable release');
     16  const requests=async()=>await(await fetch(control+'/requests')).json();
     17  const code=paths=>paths.filter(p=>p.startsWith(base.pathname)&&/\.(?:js|mjs|wasm)(?:\?|$)/.test(p));
     18  const evaluate=async()=>{input('expression-editor','42');change('output-type','number');change('mode','pure');el('run').click();await status('Finished');assert(el('result').textContent==='42','Evaluation works');};
     19  await evaluate(); // Warm the compiler and expression path too.
     20  const warm=code(await requests());assert(warm.length>=5,'Network observed the initial dependency graph');
     21  for(const name of ['rgb','microscope','names-values']){
     22   change('program-picker','demo:'+name);await status('Ready');await evaluate();
     23  }
     24  input('source-editor','!export answer\nanswer = 42');await status('Compiled');
     25  await evaluate();
     26  // A compile may introduce additional code; warm it before the repeat check.
     27  const compiled=code(await requests());
     28  input('source-editor','!export answer\nanswer = 43');await status('Compiled');await evaluate();
     29  input('expression-editor','(x : x x) (x : x x)');el('run').click();await status('Running');
     30  el('stop-run').click();await status('Stopped');
     31  input('source-editor','!export answer\nanswer = 44');await status('Compiled');await evaluate();
     32  // The separately cancellable local-file validator has its own module Worker.
     33  const catalogue=await(await fetch(new URL('catalogue.json',base))).json();
     34  const bytes=await(await fetch('/_arboricx/bundle/hash/'+catalogue[0].hash)).arrayBuffer();
     35  const open=()=>{const transfer=new DataTransfer();transfer.items.add(new File([bytes],'module.arboricx'));el('local-module-file').files=transfer.files;el('local-module-file').dispatchEvent(new Event('change'));};
     36  const checked=async()=>{for(let i=0;i<3000;i++){if(!el('local-module-file').disabled&&el('local-module-list').children.length===1)return;await delay(20);}throw Error('Local validation failed');};
     37  // Check runtime code reuse before warming the additional validator entrypoint.
     38  const afterStop=code(await requests());
     39  assert(JSON.stringify(afterStop)===JSON.stringify(compiled),'Fresh compile/Stop Workers reuse cached code: '+JSON.stringify(afterStop.slice(compiled.length)));
     40  open();await checked();const localWarm=code(await requests());open();await checked();
     41  const final=code(await requests());
     42  assert(JSON.stringify(final)===JSON.stringify(localWarm),'Local validator Workers reuse cached code: '+JSON.stringify(final.slice(localWarm.length)));
     43 
     44  const counts=new Map();for(const p of final)counts.set(p,(counts.get(p)||0)+1);
     45  assert([...counts.values()].every(n=>n===1),'Demo changes and fresh Workers fetch each code URL once: '+JSON.stringify([...counts].filter(([,n])=>n>1)));
     46  // Errors remain retryable; an unknown release must not alias current bytes.
     47  const missing=new URL(base.href.replace(/assets\/[a-f0-9]{64}/,'assets/'+'0'.repeat(64))+'dist/app.js');
     48  const response=await fetch(missing);assert(response.status===404,'Unknown release is missing');assert(response.headers.get('Cache-Control')==='no-store','Errors are not cached');
     49  return {userAgent:navigator.userAgent,checks:['versioned graph; demo switching; Stop/recompile; local validation; each code URL fetched once; missing release no-store'],codeRequests:final.length};
     50 }