UNPKG

trellis

Version:

Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications

137 lines (134 loc) 4.4 kB
import { __esm } from "./chunk-2ESYSVXG.js"; // src/server/node-adapter.ts async function startNodeServer(opts) { const http = await import("http"); const httpServer = http.createServer( async (req, res) => { const claimed = Boolean( req[Symbol.for("trellis.blobClaimed")] ); if (claimed || res.headersSent || res.writableEnded) { return; } try { const fetchReq = await toFetchRequest(req); const fetchRes = await opts.fetch(fetchReq); await writeFetchResponse(res, fetchRes); } catch (err) { const msg = err instanceof Error ? err.message : String(err); res.statusCode = 500; res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify({ error: "Internal Server Error", message: msg })); } } ); let wss = null; try { const { WebSocketServer } = await import("ws"); wss = new WebSocketServer({ noServer: true }); httpServer.on("upgrade", (req, socket, head) => { const reqPath = (req.url ?? "").split("?")[0]; if (reqPath !== "/realtime") return; wss.handleUpgrade(req, socket, head, (ws) => { wss.emit("connection", ws, req); }); }); wss.on("connection", (ws) => { Promise.resolve(opts.websocket.open(ws)).catch(() => { }); ws.on("message", (raw) => { const data = Array.isArray(raw) ? Buffer.concat(raw).toString() : raw instanceof ArrayBuffer ? Buffer.from(raw).toString() : raw.toString(); Promise.resolve(opts.websocket.message(ws, data)).catch(() => { }); }); ws.on("close", () => opts.websocket.close(ws)); }); } catch { httpServer.on("upgrade", (_req, socket) => { socket.destroy(); }); } await new Promise( (resolve) => httpServer.listen(opts.port, opts.hostname, resolve) ); if (opts.attachPresenceRelay) { const { attachRealtimeRelay } = await import("./relay-server-JJPW5UT4.js"); const relayOpts = typeof opts.attachPresenceRelay === "object" ? opts.attachPresenceRelay : { path: "/rt" }; await attachRealtimeRelay(httpServer, relayOpts); } const addr = httpServer.address(); const boundPort = typeof addr === "object" && addr ? addr.port : opts.port; const boundHost = typeof addr === "object" && addr ? addr.address : opts.hostname; return wrapNodeServer(httpServer, wss, boundPort, boundHost); } async function toFetchRequest(req) { const host = req.headers.host ?? "localhost"; const protocol = req.socket?.encrypted ? "https" : "http"; const url = `${protocol}://${host}${req.url ?? "/"}`; const method = req.method ?? "GET"; const headers = new Headers(); for (const [key, value] of Object.entries(req.headers)) { if (Array.isArray(value)) { for (const v of value) headers.append(key, v); } else if (value != null) { headers.set(key, value); } } const hasBody = method !== "GET" && method !== "HEAD"; const body = hasBody ? new Uint8Array(await readBody(req)) : void 0; return new Request(url, { method, headers, body }); } function readBody(req) { return new Promise((resolve, reject) => { const chunks = []; req.on("data", (c) => chunks.push(c)); req.on("end", () => resolve(Buffer.concat(chunks))); req.on("error", reject); }); } async function writeFetchResponse(res, fetchRes) { res.statusCode = fetchRes.status; fetchRes.headers.forEach((value, key) => res.setHeader(key, value)); if (!fetchRes.body) { res.end(); return; } const reader = fetchRes.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; res.write(value); } res.end(); } function wrapNodeServer(httpServer, wss, port, hostname) { return { port, hostname: hostname ?? "localhost", stop(closeActiveConnections) { return new Promise((resolve, reject) => { const closeHttp = () => { httpServer.close((err) => err ? reject(err) : resolve()); }; if (!wss) { closeHttp(); return; } if (closeActiveConnections) { for (const client of wss.clients) client.terminate(); } wss.close(() => closeHttp()); }); } }; } var init_node_adapter = __esm({ "src/server/node-adapter.ts"() { } }); export { startNodeServer, init_node_adapter };