browser-client.mjs (1890B)
1 // Single-flight Worker ownership. Termination rejects the active request and 2 // discards the entire runtime; no attempt to interrupt/reuse a poisoned RTS. 3 export class CompilerWorker { 4 constructor(){ 5 this.worker=new Worker(new URL('./worker.mjs',import.meta.url),{type:'module'}); 6 this.next=0;this.pending=null;this.disposed=false; 7 this.worker.onmessage=({data})=>{ 8 const pending=this.pending;if(!pending||pending.id!==data.id)return; 9 clearTimeout(pending.timer);this.pending=null; 10 if(data.ok){ 11 data.text=new TextDecoder('utf-8',{fatal:true}).decode(data.bytes); 12 data.roundtripMs=performance.now()-pending.start; 13 pending.resolve(data); 14 }else{ 15 pending.reject(Error(data.error)); 16 if(data.fatal)this.dispose('Fatal runtime error'); 17 } 18 }; 19 this.worker.onerror=event=>{event.preventDefault();this.dispose(event.message||'Worker failed');}; 20 } 21 request(message,timeout=null){ 22 if(this.disposed)return Promise.reject(Error('Disposed Worker')); 23 if(this.pending)return Promise.reject(Error('Worker is busy')); 24 if(timeout!==null&&timeout!==Infinity&&(!Number.isFinite(timeout)||timeout<=0))return Promise.reject(Error('Invalid request deadline')); 25 const id=++this.next,start=performance.now(); 26 return new Promise((resolve,reject)=>{ 27 const timer=timeout===null||timeout===Infinity?undefined:setTimeout(()=>this.dispose('Worker deadline exceeded'),timeout); 28 this.pending={id,resolve,reject,timer,start}; 29 try{this.worker.postMessage({...message,id});} 30 catch(error){this.dispose('Request transport failed: '+error.message);} 31 }); 32 } 33 dispose(reason='Worker terminated'){ 34 if(this.disposed)return; 35 this.disposed=true;this.worker.terminate(); 36 if(this.pending){clearTimeout(this.pending.timer);this.pending.reject(Error(reason));this.pending=null;} 37 } 38 }