tricu

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

io-v2-session-probe.mjs (4998B)


      1 // Native tree sibling cancellation through cloneable events, with a real DOM
      2 // provider. No raw handles leave the worker/session boundary.
      3 export async function run(_control,backend){
      4  const base=new URL('../',document.querySelector('script[type=module][src]').src),fixture=(await(await fetch('/zig-negative.json')).json())['v2-session-streams'];
      5  const assert=(ok,message)=>{if(!ok)throw Error(message);};
      6  async function exercise(start,turn,stop){
      7   let report=await start();const events=[];
      8   for(let i=0;i<30&&!events.some(e=>e.kind==='write');i++){events.push(...report.events);if(!events.some(e=>e.kind==='write'))report=await turn({run:report.run,kind:'poll'});}
      9   const read=events.find(e=>e.kind==='read'),write=events.find(e=>e.kind==='write');assert(read&&write,'pending read blocked sibling write');
     10   const input=document.createElement('input');document.body.append(input);let hits=0;
     11   const listener=()=>{hits++;};input.addEventListener('input',listener);
     12   try{
     13    if(stop){
     14     report=await turn({run:report.run,kind:'stop'});
     15     assert(!report.done&&report.events.filter(e=>e.kind==='cancel').length===2,'stop did not cancel both providers');
     16     input.removeEventListener('input',listener);input.remove();
     17     report=await turn({run:report.run,kind:'reply',reply:{...read,kind:'cancelled'}});
     18     assert(!report.done,'stop forgot pending output acknowledgement');
     19     report=await turn({run:report.run,kind:'reply',reply:{...write,kind:'cancelled'}});
     20     for(let i=0;i<30&&!report.done;i++)report=await turn({run:report.run,kind:'poll'});
     21     assert(report.done&&report.outcome.kind==='cancelled','structured session stop did not settle');
     22     assert(Object.values(report.counts).every(group=>Object.values(group).every(value=>value===0)),'stop retained lifecycle state');
     23     return;
     24    }
     25    let rejected=false;try{await turn({run:'999',kind:'stop'});}catch{rejected=true;}assert(rejected,'wrong run accepted');
     26    report=await turn({run:report.run,kind:'reply',reply:{...read,run:'999',bytes:[1],eof:false}});assert(report.accepted===false,'wrong reply consumed wait');
     27    assert(write.bytes.length===1&&write.bytes[0]===65,'write payload mismatch');
     28    report=await turn({run:report.run,kind:'reply',reply:{run:write.run,id:write.id,kind:'write',count:1}});
     29    let cancel=report.events.find(e=>e.kind==='cancel');
     30    for(let i=0;i<30&&!cancel;i++){report=await turn({run:report.run,kind:'poll'});cancel=report.events.find(e=>e.kind==='cancel');}
     31    assert(cancel?.id===read.id,'missing read cancellation');assert(!report.done&&report.counts.events.pending===1,'abort delivery settled provider');
     32    input.removeEventListener('input',listener);input.dispatchEvent(new Event('input'));assert(hits===0,'listener survived cancellation');
     33    report=await turn({run:report.run,kind:'poll'});assert(!report.done&&input.isConnected,'unacknowledged cleanup retired run');
     34    input.remove();report=await turn({run:report.run,kind:'reply',reply:{...cancel,kind:'cancelled'}});
     35    for(let i=0;i<30&&!report.done;i++)report=await turn({run:report.run,kind:'poll'});
     36    assert(report.done&&report.outcome.kind==='completed'&&report.outcome.value==='42','tree run result mismatch');
     37    assert(Object.values(report.counts).every(group=>Object.values(group).every(value=>value===0)),'historical transport/lifecycle state retained');
     38   }finally{input.removeEventListener('input',listener);input.remove();}
     39  }
     40  for(const stop of [false,true]){
     41  if(backend==='haskell-wasm'){
     42   const {CompilerWorker}=await import(new URL('runtime/haskell/client.mjs',base)),client=new CompilerWorker();
     43   try{await client.request({type:'init',compiler:false});await client.request({type:'load',bytes:new Uint8Array(fixture.bytes).buffer,hash:fixture.hash});
     44    await exercise(async()=>JSON.parse((await client.request({type:'io-v2-session-start',name:'root',arguments:[]})).text),async command=>JSON.parse((await client.request({type:'io-v2-session-turn',...command})).text),stop);
     45   }finally{client.dispose();}
     46  }else{
     47   const {WasmRuntime}=await import('/io-v2-runtime.mjs'),{ioV2Private,ioV2Session}=await import(new URL('dist/zig-helpers.js',base));
     48   const g=await WasmRuntime.create(new Uint8Array(await(await fetch(new URL('runtime/zig/arboricx.wasm',base))).arrayBuffer()));
     49   try{await g.scopeAsync(async()=>{
     50    const action=g.load(new Uint8Array(fixture.bytes),'root'),tree=ioV2Private.constructors({leaf:g.leaf,observe:g.observe.bind(g),stem:g.stem.bind(g),fork:g.fork.bind(g),app:g.app.bind(g)});
     51    const session=new ioV2Session.Session(tree,action,'1',roots=>g.sweepScope(roots));
     52    await exercise(()=>session.turn({run:'1',kind:'poll'}),command=>session.turn(command),stop);
     53   });}finally{g.dispose();}
     54  }
     55  }
     56  return {backend,checks:['native tree read permits sibling write','wrong-run stop and reply reject','tree cancellation waits for DOM cleanup acknowledgement','complete42; no retained task/resource/event records','external structured stop awaits both provider acknowledgements']};
     57 }