tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

backend-resume-probe.mjs (3732B)


      1 // Actual page-facing step IDs and run-bound v2 terminal replies.
      2 export async function probeResume(backend){
      3  const base=new URL('../',document.querySelector('script[type=module][src]').src);
      4  const api=await import(new URL('dist/probe-api.js',base)),{Terminal}=api.ioTerminal;
      5  const releases=api.packages.packageIndex(await(await fetch(new URL('packages.json',base))).json());
      6  const lib=name=>api.packages.packageReleases(releases,'tricu-lib-'+name)[0].manifest;
      7  const worker=new Worker(new URL('./wasm-worker.js?backend='+backend,document.querySelector('script[type=module][src]').src),{type:'module'}),events=[];
      8  worker.onmessage=e=>events.push(e.data);
      9  const assert=(ok,message)=>{if(!ok)throw Error(message);},sleep=()=>new Promise(r=>setTimeout(r,40));
     10  let consumedStep=0;
     11  const wait=async(predicate,start=0,stepping=false)=>{
     12   const deadline=performance.now()+30000;
     13   while(performance.now()<deadline){
     14    const messages=events.slice(start),failure=messages.find(x=>x.type==='error');if(failure)throw Error(failure.message);
     15    const found=messages.find(predicate);if(found)return found;
     16    if(stepping){const step=messages.find(x=>x.type==='state'&&x.state==='step'&&x.id>consumedStep);if(step){consumedStep=step.id;worker.postMessage({type:'resume',id:step.id});}}
     17    await sleep();
     18   }
     19   throw Error('Resume probe timed out');
     20  };
     21  const reply=value=>worker.postMessage({type:'io-command',command:{run:value.run,kind:'reply',reply:value}});
     22  try{
     23   worker.postMessage({type:'compile',source:`!import ${lib('io')} as I
     24 !import ${lib('io-text')} as T
     25 !export main
     26 main = I.io (I.require I.stdin (reader : I.require (T.readLine (T.lineCursor reader) 256) (reply : case reply of
     27   [["line" value _] _] = I.pure value
     28   _ = I.fail "Invalid reply")))`});
     29   await wait(x=>x.type==='compiled');
     30   const run={type:'run-io',name:'main',output:'string',step:true};
     31   worker.postMessage(run);const step=await wait(x=>x.type==='state'&&x.state==='step');
     32   worker.postMessage({type:'resume',id:step.id,value:'wrong-kind'});
     33   worker.postMessage({type:'resume',id:step.id+999});await sleep();
     34   assert(!events.some(x=>x.type==='error'||x.type==='done'||x.type==='io-event'),'Malformed/stale step consumed wait');
     35   const input=(await wait(x=>x.type==='io-event'&&x.event.kind==='read',0,true)).event;
     36   const terminal=new Terminal(input.run);terminal.handle(input);
     37   for(const value of [undefined,'bad\nline','\ud800','a'.repeat(257)])assert(!terminal.line(input.run,input.id,value),'Invalid UI line admitted');
     38   for(const invalid of [
     39    {run:input.run,id:input.id,kind:'write',count:1},
     40    {run:input.run,id:input.id,kind:'read',bytes:[256],eof:false},
     41    {run:input.run,id:input.id,kind:'read',bytes:[],eof:false},
     42    {run:input.run,id:input.id+999,kind:'read',bytes:[65],eof:false},
     43   ])reply(invalid);
     44   worker.postMessage({type:'resume',id:step.id,value:'stale'});await sleep();
     45   assert(!events.some(x=>x.type==='done'||x.type==='error'),'Invalid reply consumed wait');
     46   const accepted=terminal.line(input.run,input.id,'λ雪🙂');reply(accepted);
     47   assert((await wait(x=>x.type==='done',0,true)).result.value==='λ雪🙂','Fresh reply mismatch');
     48   const mark=events.length;worker.postMessage({...run,step:false});
     49   const next=(await wait(x=>x.type==='io-event'&&x.event.kind==='read',mark)).event;
     50   assert(next.run!==input.run,'Run identity reused');reply(accepted);await sleep();
     51   assert(!events.slice(mark).some(x=>x.type==='done'||x.type==='error'),'Previous run reply accepted');
     52   const fresh=new Terminal(next.run);fresh.handle(next);reply(fresh.line(next.run,next.id,'fresh'));
     53   assert((await wait(x=>x.type==='done',mark)).result.value==='fresh','Fresh run failed');
     54  }finally{worker.terminate();}
     55 }