tricu

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

ide-probe.mjs (7948B)


      1 import {syntaxProbe} from './syntax-probe.mjs';
      2 // Real single-page workspace. Independent expectations, no substitute evaluator.
      3 const el=id=>document.getElementById(id);
      4 const assert=(ok,message)=>{if(!ok)throw Error(message);};
      5 const delay=ms=>new Promise(r=>setTimeout(r,ms));
      6 async function wait(fn){const end=Date.now()+90000;while(Date.now()<end){if(fn())return;await delay(20);}throw Error('Timed out: '+el('status').textContent+' / '+el('result').textContent);}
      7 const status=s=>wait(()=>el('status').textContent===s);
      8 const change=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('change'));};
      9 const input=(id,value)=>{el(id).value=value;el(id).dispatchEvent(new Event('input'));};
     10 export async function run(control,backend='haskell-wasm'){
     11  await status('Ready');
     12  await syntaxProbe();
     13  assert([...el('runtime-backend').options].map(o=>o.value).join(',')==='haskell-wasm,zig-wasm','Only Haskell and Zig are production runtimes');
     14  for(const path of ['worker.js','compiler-worker.js','conformance/worker.js','conformance/compiler-worker.js'])assert((await fetch(new URL('./'+path,document.querySelector('script[type=module][src]').src))).status===404,'JS conformance Workers are not shipped: '+path);
     15  assert(el('program-picker').value==='demo:names-values','Names and values loads on arrival');
     16  assert(!document.querySelector('[role=tab],#graph-result,#export-definition'),'No tabs or graph-JSON surface');
     17  assert(!el('result').textContent&&!el('terminal').textContent,'Arrival does not execute');
     18  assert(document.querySelector('footer a[href="https://eversole.co"]').textContent==='James Eversole','Copyright author link');
     19  assert(el('editor').compareDocumentPosition(el('ecosystem'))&Node.DOCUMENT_POSITION_FOLLOWING,'Ecosystem follows editor');
     20  el('settings-toggle').click();assert(!el('settings-panel').hidden&&el('settings-toggle').getAttribute('aria-expanded')==='true','Inspector opens');
     21  el('settings-close').click();assert(el('settings-panel').hidden&&document.activeElement===el('settings-toggle'),'Close returns focus');
     22  change('runtime-backend',backend);await status('Ready');
     23  const requests=async()=>await(await fetch(control+'/requests')).json();
     24  const before=(await requests()).filter(x=>x.endsWith('/compiler.json')||x.endsWith('/data/compiler.dag')).length;
     25  assert(before===0,'Arrival does not load compiler assets');
     26  change('program-picker','demo:language-reference');await status('Ready');
     27  change('output-target','definition');change('export','leaf');
     28  assert(el('run').textContent==='Show definition'&&el('mode').disabled,'Stored target is visibly distinct from execution');
     29  for(const [format,want] of [['tricu','t'],['triangle','△'],['dag','tree △\ntree\n'],['ternary','0']]){
     30   change('output-type',format);el('run').click();await wait(()=>!el('copy-output').disabled);
     31   assert(el('result').textContent===want,'Stored '+format);
     32   assert(/Stored definition leaf.*SHA-256/.test(el('output-provenance').textContent),'Stored provenance');
     33  }
     34  assert((await requests()).filter(x=>x.endsWith('/compiler.json')||x.endsWith('/data/compiler.dag')).length===before,'Stored output never initializes the compiler');
     35  change('output-target','result');
     36  const evaluate=async(source,format)=>{input('expression-editor',source);change('output-type',format);el('run').click();await status('Finished');};
     37  for(const [format,want] of [['tricu','(t t t)'],['triangle','(△ △ △)'],['ternary','200']]){
     38   await evaluate('identity (t t t)',format);assert(el('result').textContent===want,'Applied result '+format);
     39   assert(/Evaluated result/.test(el('output-provenance').textContent),'Result not mislabeled as a definition');
     40  }
     41  await evaluate('identity (t t t)','dag');assert(/tree :n\d+\ntree\n$/.test(el('result').textContent),'Result DAG interface');
     42  change('mode','io');await evaluate('t "tricuIO" (t 1 (t 0 (t t t)))','tricu');assert(el('result').textContent==='(t t t)','IO result, not IO envelope');change('mode','pure');
     43  let copied;const clipboard=Object.getOwnPropertyDescriptor(navigator,'clipboard');
     44  Object.defineProperty(navigator,'clipboard',{configurable:true,value:{writeText:async value=>{copied=value;}}});
     45  const createURL=URL.createObjectURL,click=HTMLAnchorElement.prototype.click;let blob,download;
     46  URL.createObjectURL=value=>{blob=value;return createURL(value);};HTMLAnchorElement.prototype.click=function(){download=this.download;};
     47  try{
     48   await evaluate(JSON.stringify('a'.repeat(600)),'tricu');assert(el('result').textContent.length===16384,'Large result has a bounded UI preview');
     49   assert(/Preview: first 16,384/.test(el('output-provenance').textContent),'Partial preview is explicit');
     50   el('copy-output').click();await wait(()=>copied!==undefined);assert(copied.length>16384&&copied.startsWith(el('result').textContent),'Copy contains complete source');
     51   el('download-output').click();assert(download==='result.tri'&&await blob.text()===copied,'Download contains the identical complete source');
     52   const previousOutput=el('result').textContent;
     53   const expectedHash=el('bundle-hash').value;el('download-bundle').click();await wait(()=>/Downloaded original/.test(el('bundle-download-status').textContent));
     54   const actual=[...new Uint8Array(await crypto.subtle.digest('SHA-256',await blob.arrayBuffer()))].map(x=>x.toString(16).padStart(2,'0')).join('');
     55   assert(el('result').textContent===previousOutput,'Bundle download does not replace evaluated output');
     56   assert(actual===expectedHash&&download===expectedHash+'.arboricx','Bundle download remains original authenticated bytes');
     57  }finally{URL.createObjectURL=createURL;HTMLAnchorElement.prototype.click=click;if(clipboard)Object.defineProperty(navigator,'clipboard',clipboard);else delete navigator.clipboard;}
     58  // An exponentially expanded value stays small as a physically shared DAG.
     59  const bindings=['x0 = t t t'];for(let i=1;i<=28;i++)bindings.push(`x${i} = t x${i-1} x${i-1}`);
     60  const shared='let\n  '+bindings.join('\n  ')+'\n  in x28';
     61  await evaluate(shared,'dag');assert(el('result').textContent.length<20000,'Shared result is not flattened');
     62  input('expression-editor',shared);change('output-type','tricu');el('run').click();await status('Stopped');
     63  assert(/Expanded tree exceeds 32 MiB/.test(el('result').textContent),'Oversized expansion rejected before building output');
     64  assert(el('copy-output').disabled&&el('download-output').disabled,'No partial result masquerades as a file');
     65  await evaluate('identity 52','number');assert(el('result').textContent==='52','Recovery after result-rendering error');
     66  // Fresh lazy compiler initialization honors the site pin, with no fallback.
     67  change('runtime-backend',backend);await status('Ready');
     68  await fetch(control+'/fault',{method:'POST',body:JSON.stringify({compilerPin:true})});
     69  input('expression-editor','identity 53');el('run').click();await status('Stopped');
     70  assert(/pin mismatch|HTTP|fetch/i.test(el('result').textContent),'Expression compiler pin failure is visible');
     71  await fetch(control+'/fault',{method:'POST',body:JSON.stringify({compilerPin:false})});
     72  await evaluate('identity 53','number');assert(el('result').textContent==='53','Retry authenticates the compiler');
     73  input('bundle-hash','not-a-hash');el('program-hash').requestSubmit();await status('Stopped');
     74  assert(el('run').disabled&&el('copy-output').disabled,'Invalid replacement never falls back to an earlier package/output');
     75  change('program-picker','demo:language-reference');await status('Ready');
     76  const live=await(await import('/live-ux-probe.mjs')).run(control,backend,true);
     77  change('program-picker','demo:language-reference');await status('Ready');
     78  await(await import('/evaluation-layout-probe.mjs')).evaluationLayout();
     79  return {userAgent:navigator.userAgent,checks:[...live.checks,'single-page arrival/inspector; stored vs applied outputs; t/△/DAG/ternary; IO result; complete copy/download; expansion refusal; immutable bundle; pin failure/retry']};
     80 }