UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

1 lines 5.26 kB
import{createServer}from"node:http";import{toErrorMessage}from"#shared/errors.js";import{closeServer,createPublicRequest,writeRequestError,writeResponse}from"#internal/nitro/host/dev-server-http.js";import{createNodeDevelopmentRunner}from"#internal/nitro/host/dev-runner.js";import{stampDevelopmentClientAddress}from"#internal/nitro/dev-client-address.js";const RUNNER_READY_TIMEOUT_MS=6e4;var DrainedNitroDevServer=class{#e;#t=new Set;#n=new Set;#r;#i;#a=[];#o;#s=!1;#c;#l;#u;#d=Promise.resolve();#f=0;constructor(e=console,t=createNodeDevelopmentRunner){this.#e=t,this.#r=e}setControlHandler(e){this.#l=e}setClientAddressSecret(e){this.#c=e}replaceWorker(e){let t=this.#d.catch(()=>void 0).then(async()=>await this.#p(e));return this.#d=t.catch(()=>void 0),t}async#p(e){if(this.#s)throw Error(`Development server is closed.`);let t={activeExchanges:0,dispose:e.dispose,disposed:!1,entry:e.entry,quietListeners:[],releasePromise:void 0,runner:this.#e({entry:e.entry,name:`eve-dev-${String(this.#f++)}`,workerData:e.workerData}),workerData:e.workerData};this.#u=t;try{await t.runner.waitForReady(RUNNER_READY_TIMEOUT_MS)}catch(e){throw await t.runner.close(e).catch(()=>void 0),await this.#_(t),e}finally{this.#u=void 0}if(this.#s)throw await this.#h(t),Error(`Development server closed before the worker was ready.`);let n=this.#i;this.#i=t,t.runner.onceClosed(()=>{this.#v(t)}),this.#C(),n!==void 0&&this.#y(n)}listen(t){if(this.#s)throw Error(`Development server is closed.`);let r=new Set,i=createServer((e,t)=>{this.#x(e,t)});i.on(`connection`,e=>{r.add(e),e.once(`close`,()=>r.delete(e))}),i.on(`upgrade`,(e,t,n)=>{this.#S(e,t,n)});let a,o=new Promise((e,n)=>{let onError=e=>{i.off(`listening`,onListening),n(e)},onListening=()=>{i.off(`error`,onError);let r=i.address();if(r===null||typeof r==`string`){n(Error(`Development server did not expose a TCP address.`));return}a=`http://${t.hostname.includes(`:`)?`[${t.hostname}]`:t.hostname}:${String(r.port)}/`,e()};i.once(`error`,onError),i.once(`listening`,onListening),i.listen({host:t.hostname,port:t.port})}),s,c={beginClose:()=>(s??=closeServer(i).finally(()=>{this.#n.delete(c)}),s),destroySockets:()=>{for(let e of r)e.destroy()}};return this.#n.add(c),{async close(){let e=c.beginClose();c.destroySockets(),await e},node:{server:i},ready:async()=>await o,get url(){return a}}}async waitForActiveRunner(e=RUNNER_READY_TIMEOUT_MS){let t=Date.now()+e;for(;this.#i===void 0&&!this.#s;){if(Date.now()>=t)throw Error(`Timed out waiting for the development worker to become ready.`);await new Promise(e=>{this.#a.push(e),setTimeout(e,100)})}if(this.#s)throw Error(`Development server is closed.`)}close(){return this.#o??=this.#m(),this.#o}async#m(){this.#s=!0,this.#C(),await this.#u?.runner.close(Error(`Development server is closing.`)).catch(()=>void 0),await this.#d.catch(()=>void 0);let e=[...this.#n],t=e.map(e=>e.beginClose()),n=[this.#i,...this.#t].filter(e=>e!==void 0);this.#i=void 0,this.#t.clear(),await Promise.all(n.map(async e=>await this.#h(e)));for(let t of e)t.destroySockets();await Promise.all(t)}#h(e){return e.releasePromise??=this.#g(e),e.releasePromise}async#g(e){await e.runner.close().catch(()=>void 0),await this.#_(e)}async#_(e){e.dispose===void 0||e.disposed||(e.disposed=!0,await e.dispose().catch(e=>{this.#r.error(`[eve:dev] failed to dispose a retired dev host: ${toErrorMessage(e)}`)}))}async#v(e){if(this.#s||this.#i!==e)return;this.#i=void 0,this.#r.error(`[eve:dev] dev worker exited; restarting.`);let n=this.#d.catch(()=>void 0).then(async()=>{if(this.#s||this.#i!==void 0){await this.#h(e);return}e.disposed=!0,e.releasePromise=Promise.resolve(),await this.#p({dispose:e.dispose,entry:e.entry,workerData:e.workerData})});this.#d=n.catch(()=>void 0);try{await n}catch(e){this.#r.error(`[eve:dev] dev worker restart failed: ${toErrorMessage(e)}`)}}#y(e){this.#t.add(e);let finishDrain=()=>{this.#h(e).finally(()=>this.#t.delete(e))};if(e.activeExchanges===0){finishDrain();return}e.quietListeners.push(finishDrain)}#b(e){e.activeExchanges+=1;let t=!1;return()=>{if(!t&&(t=!0,--e.activeExchanges,e.activeExchanges===0))for(let t of e.quietListeners.splice(0))t()}}async#x(e,t){let n=new AbortController,abortRequest=e=>{n.signal.aborted||n.abort(e)};e.once(`error`,abortRequest),t.once(`close`,()=>{t.writableEnded||abortRequest()});let o;try{let i=createPublicRequest(e,n.signal);stampDevelopmentClientAddress(i.headers,e.socket.remoteAddress??void 0,this.#c);let c=await this.#l?.(i);if(c!==void 0){await writeResponse(t,c,n.signal);return}await this.waitForActiveRunner();let l=this.#i;if(l===void 0)throw Error(`Development worker is unavailable.`);o=this.#b(l),await writeResponse(t,await l.runner.fetch(i,{signal:n.signal}),n.signal)}catch(e){n.signal.aborted||writeRequestError(t,e)}finally{o?.(),e.off(`error`,abortRequest)}}async#S(e,t,n){let r;try{stampDevelopmentClientAddress(e.headers,e.socket.remoteAddress??void 0,this.#c),await this.waitForActiveRunner();let i=this.#i;if(i===void 0)throw Error(`Development worker is unavailable.`);r=this.#b(i),t.once(`close`,r),t.once(`error`,r),await i.runner.upgrade({node:{head:n,req:e,socket:t}})}catch{r?.(),t.destroyed||t.destroy()}}#C(){for(let e of this.#a.splice(0))e()}};export{DrainedNitroDevServer};