eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
3 lines • 4.08 kB
JavaScript
import{createConnection,createServer}from"node:net";import{loadDevelopmentEnvironmentFiles}from"#cli/dev/environment.js";import{join,resolve}from"node:path";import{existsSync}from"node:fs";import{spawn}from"node:child_process";import{setTimeout}from"node:timers/promises";const HEALTH_TIMEOUT_MS=6e4,LOCAL_SERVER_URL_PATTERN=/https?:\/\/(?:\[[^\]\s]+\]|[^\s/:[\]]+)(?::\d+)?/,WILDCARD_LISTEN_HOSTNAMES=new Set([`[::]`,`::`,`0.0.0.0`]);function resolveOutputServerEntry(e){return join(resolve(e),`.output`,`server`,`index.mjs`)}function readEnvironmentPort(){let e=process.env.PORT;if(e===void 0||e.trim()===``)return;let t=Number(e);if(!Number.isInteger(t)||t<0||t>65535)throw Error(`Invalid PORT environment variable "${e}". Expected an integer between 0 and 65535.`);return t}function normalizeServerClientUrl(e){let t=new URL(e);return WILDCARD_LISTEN_HOSTNAMES.has(t.hostname)&&(t.hostname=`127.0.0.1`),t.toString()}function formatClientHost(e){return WILDCARD_LISTEN_HOSTNAMES.has(e)?`127.0.0.1`:e.includes(`:`)&&!e.startsWith(`[`)?`[${e}]`:e}function createKnownPortUrl(e){return`http://${formatClientHost(e.host)}:${String(e.port)}/`}async function resolveListenPort(e){if(e.port!==0)return e.port;let t=createServer();return await new Promise((n,r)=>{t.once(`error`,r),t.listen(0,e.host,()=>{let e=t.address();t.close(t=>{if(t){r(t);return}if(e===null||typeof e==`string`){r(Error(`Failed to resolve an available port for eve start.`));return}n(e.port)})})})}function parseServerUrlFromOutput(e){let t=LOCAL_SERVER_URL_PATTERN.exec(e);if(t!==null)return normalizeServerClientUrl(t[0])}async function isListening(e){let t=new URL(e),n=Number(t.port||(t.protocol===`https:`?443:80));return await new Promise(e=>{let r=createConnection({host:t.hostname,port:n}),finish=t=>{r.destroy(),e(t)};r.setTimeout(2e3,()=>finish(!1)),r.once(`connect`,()=>finish(!0)),r.once(`error`,()=>finish(!1))})}async function waitForReady(e){let t=Date.now()+HEALTH_TIMEOUT_MS;for(;Date.now()<t;){let t=e.getStartError();if(t!==void 0)throw t;let n=parseServerUrlFromOutput(e.getOutput()),r=n??e.knownUrl;if(r!==void 0&&(n!==void 0||await isListening(r)))return new URL(r).toString();if(e.child.exitCode!==null||e.child.signalCode!==null)throw Error(`Built server process exited (code=${String(e.child.exitCode)}, signal=${String(e.child.signalCode)}) before printing its URL.`);await setTimeout(250)}throw Error([`Built server did not become ready within ${HEALTH_TIMEOUT_MS/1e3}s.`,`Output:`,e.getOutput()].join(`
`))}async function terminate(e){e.exitCode!==null||e.killed||(e.kill(`SIGTERM`),await Promise.race([once(e,`exit`),setTimeout(2e4).then(()=>`timeout`)])===`timeout`&&e.exitCode===null&&(e.kill(`SIGKILL`),await once(e,`exit`)),e.stdout?.destroy(),e.stderr?.destroy())}function once(e,t){return new Promise(n=>{e.once(t,()=>n())})}async function startProductionServer(e,t={}){let n=resolve(e),r=resolveOutputServerEntry(n);if(!existsSync(r))throw Error(`Missing eve build output at ${r}. Run "eve build" before "eve start".`);await loadDevelopmentEnvironmentFiles(n);let i=t.host??`0.0.0.0`,a=await resolveListenPort({host:i,port:t.port??readEnvironmentPort()??3e3}),o=createKnownPortUrl({host:i,port:a}),s=``,c=!1,l,u=spawn(process.execPath,[r],{cwd:n,env:{...process.env,HOST:i,NITRO_HOST:i,NITRO_PORT:String(a),PORT:String(a)},stdio:[`ignore`,`pipe`,`pipe`]});u.stdout?.on(`data`,e=>{s+=e.toString(`utf8`),process.stdout.write(e)}),u.stderr?.on(`data`,e=>{s+=e.toString(`utf8`),process.stderr.write(e)});let d=new Promise((e,t)=>{u.once(`error`,e=>{l=e,t(e)}),u.once(`exit`,(n,r)=>{if(c||n===0){e();return}t(Error([`Built server process exited unexpectedly (code=${String(n)}, signal=${String(r)}).`,s].join(`
`)))})});d.catch(()=>void 0);try{return{async close(){c=!0,await terminate(u)},url:await waitForReady({child:u,getStartError:()=>l,getOutput:()=>s,knownUrl:o}),async wait(){await d}}}catch(e){throw c=!0,await terminate(u),e instanceof Error&&e.name===`AbortError`?Error(`Timed out waiting for built eve server to respond.`,{cause:e}):e}}export{startProductionServer};