{"version":3,"file":"node.mjs","names":[],"sources":["../src/server/http-server.errors.ts","../src/server/node-server.ts"],"sourcesContent":["import { VennError } from \"@venn-lang/contracts\";\nimport { PLUGIN_CODES } from \"@venn-lang/sdk\";\n\n/** VN7020: the address the flow asked for is already taken by something else. */\nexport function portInUse(args: { port: number; host: string }): VennError {\n  return new VennError({\n    code: PLUGIN_CODES.VN7020_PORT_TAKEN,\n    message: `Port ${args.port} is already in use, stop whatever is listening on ${args.host}:${args.port}, or ask for \"port: 0\" to take any free one.`,\n    detail: { port: args.port, host: args.host, cause: \"EADDRINUSE\" },\n  });\n}\n\n/** VN7021: the socket refused to bind for any other reason. */\nexport function listenFailed(args: { port: number; host: string; cause: string }): VennError {\n  return new VennError({\n    code: PLUGIN_CODES.VN7021_CANNOT_BIND,\n    message: `Could not listen on ${args.host}:${args.port}, ${args.cause}.`,\n    detail: { port: args.port, host: args.host, cause: args.cause },\n  });\n}\n\n/**\n * Whatever the socket threw, as a Venn error: VN7020 for `EADDRINUSE`, VN7021\n * for anything else.\n *\n * The translation lives at the producer so no caller has to read a `node:net`\n * errno to know what went wrong.\n */\nexport function asListenError(args: { port: number; host: string; error: unknown }): VennError {\n  const error = args.error as { code?: string; message?: string } | undefined;\n  if (error?.code === \"EADDRINUSE\") return portInUse({ port: args.port, host: args.host });\n  return listenFailed({\n    port: args.port,\n    host: args.host,\n    cause: error?.message ?? String(args.error),\n  });\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { asListenError } from \"./http-server.errors.js\";\nimport type {\n  HttpServer,\n  RequestHandler,\n  RunningServer,\n  ServerReply,\n  ServerRequest,\n} from \"./http-server.types.js\";\n\n/** A node HttpServer, plus the way to hang up everything it still has open. */\nexport interface NodeHttpServer extends HttpServer {\n  /**\n   * Close every server started here that is still listening.\n   *\n   * A process owns its sockets, so whoever owns the process (the CLI) needs one\n   * call to give them all back on the way out.\n   */\n  closeAll(): Promise<void>;\n}\n\n/**\n * The real implementation: a bound socket, on the port the OS gave it.\n *\n * Only this file reaches for `node:http`, which is why it sits behind the\n * `@venn-lang/http/node` subpath. The rest of the package stays platform-neutral and\n * runs wherever the language runs, the editor's worker included.\n *\n * @throws VN7020 if the port is taken, VN7021 if the socket refuses to bind.\n */\nexport function createNodeServer(): NodeHttpServer {\n  const open = new Set<RunningServer>();\n  return {\n    listen: async (args) => {\n      const server = await bind(args);\n      open.add(server);\n      return { port: server.port, close: () => forget(open, server) };\n    },\n    closeAll: async () => {\n      for (const server of [...open]) await forget(open, server);\n    },\n  };\n}\n\n/** Closing is idempotent from the set's point of view: gone is gone. */\nasync function forget(open: Set<RunningServer>, server: RunningServer): Promise<void> {\n  open.delete(server);\n  await server.close();\n}\n\nfunction bind(args: {\n  port: number;\n  host?: string;\n  handle: RequestHandler;\n}): Promise<RunningServer> {\n  const { port, host, handle } = args;\n  return new Promise<RunningServer>((resolve, reject) => {\n    const at = host ?? \"127.0.0.1\";\n    const server = createServer((request, response) => {\n      void answer(request, response, handle);\n    });\n    // A busy port is the flow's problem, not a `node:net` errno: translate here.\n    server.once(\"error\", (error) => reject(asListenError({ port, host: at, error })));\n    server.listen(port, at, () => resolve(running(server)));\n  });\n}\n\nfunction running(server: ReturnType<typeof createServer>): RunningServer {\n  const address = server.address();\n  return {\n    port: typeof address === \"object\" && address ? address.port : 0,\n    close: () =>\n      new Promise<void>((resolve) => {\n        server.closeAllConnections?.();\n        server.close(() => resolve());\n      }),\n  };\n}\n\n/** A handler that throws becomes a 500: one bad request must not kill the server. */\nasync function answer(\n  request: IncomingMessage,\n  response: ServerResponse,\n  handle: RequestHandler,\n): Promise<void> {\n  try {\n    send(response, (await handle(await read(request))) ?? { status: 204 });\n  } catch (error) {\n    send(response, { status: 500, body: { error: String((error as Error)?.message ?? error) } });\n  }\n}\n\nfunction send(response: ServerResponse, reply: ServerReply): void {\n  const json = typeof reply.body !== \"string\" && reply.body !== undefined;\n  const headers = { ...(json ? { \"content-type\": \"application/json\" } : {}), ...reply.headers };\n  response.writeHead(reply.status ?? 200, headers);\n  response.end(body(reply.body, json));\n}\n\nfunction body(value: unknown, json: boolean): string {\n  if (value === undefined) return \"\";\n  return json ? JSON.stringify(value) : String(value);\n}\n\nasync function read(request: IncomingMessage): Promise<ServerRequest> {\n  const chunks: Buffer[] = [];\n  for await (const chunk of request) chunks.push(chunk as Buffer);\n  return {\n    method: request.method ?? \"GET\",\n    url: request.url ?? \"/\",\n    headers: plain(request.headers),\n    body: Buffer.concat(chunks).toString(\"utf8\"),\n  };\n}\n\nfunction plain(headers: IncomingMessage[\"headers\"]): Record<string, string> {\n  const out: Record<string, string> = {};\n  for (const [key, value] of Object.entries(headers)) {\n    if (value !== undefined) out[key] = Array.isArray(value) ? value.join(\", \") : value;\n  }\n  return out;\n}\n"],"mappings":"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"}