{"version":3,"file":"express-fetch-bridge.cjs","names":["createCopilotNodeHandler"],"sources":["../../../../src/v2/runtime/endpoints/express-fetch-bridge.ts"],"sourcesContent":["/**\n * Express-aware Node ↔ Fetch bridge.\n *\n * When Express body-parsing middleware (e.g. `express.json()`) runs before the\n * CopilotKit router, the Node request stream is already consumed and `req.body`\n * holds the parsed content. The generic `createCopilotNodeHandler` (which uses\n * `@remix-run/node-fetch-server`) would hang because it tries to read from the\n * exhausted stream.\n *\n * This module detects the pre-parsed case and re-serialises `req.body` into the\n * Fetch `Request`, falling back to the generic `createCopilotNodeHandler` when the\n * stream is still available.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { sendResponse } from \"@remix-run/node-fetch-server\";\nimport { createCopilotNodeHandler } from \"./node-fetch-handler\";\nimport type { CopilotRuntimeFetchHandler } from \"../core/fetch-handler\";\nimport { logger } from \"@copilotkit/shared\";\n\nconst METHODS_WITHOUT_BODY = new Set([\"GET\", \"HEAD\", \"OPTIONS\"]);\n\nexport type ExpressNodeHandler = (\n  req: IncomingMessage,\n  res: ServerResponse,\n) => Promise<void>;\n\n/**\n * Creates a Node HTTP handler from a fetch handler, with Express body-parser\n * compatibility. Use this instead of `createNodeFetchHandler` in Express adapters.\n *\n * When the body stream hasn't been consumed, delegates to the generic\n * `createCopilotNodeHandler`. Only intercepts when Express middleware has\n * pre-parsed the body.\n */\nexport function createExpressNodeHandler(\n  handler: CopilotRuntimeFetchHandler,\n): ExpressNodeHandler {\n  const nodeHandler = createCopilotNodeHandler(handler);\n\n  return async (req: IncomingMessage, res: ServerResponse) => {\n    const method = (req.method ?? \"GET\").toUpperCase();\n\n    // Fast path: if no body parser consumed the stream, use the generic handler.\n    if (METHODS_WITHOUT_BODY.has(method) || !hasPreParsedBody(req)) {\n      return nodeHandler(req, res);\n    }\n\n    // Slow path: body was consumed by Express middleware — rebuild the Request.\n    try {\n      const fetchReq = buildPreParsedRequest(req, res);\n      const fetchRes = await handler(fetchReq);\n      await sendResponse(res, fetchRes);\n    } catch (err: unknown) {\n      logger.error({ err }, \"Error in Express fetch bridge (pre-parsed path)\");\n      if (!res.headersSent) {\n        res.statusCode = 500;\n        res.end(\"Internal Server Error\");\n      }\n    }\n  };\n}\n\n/**\n * Build a Fetch Request from a Node IncomingMessage whose body stream has\n * already been consumed by an Express body parser.\n */\nfunction buildPreParsedRequest(\n  req: IncomingMessage,\n  res: ServerResponse,\n): Request {\n  const expressReq = req as IncomingMessage & { body?: unknown };\n  const method = (req.method ?? \"GET\").toUpperCase();\n\n  const protocol = (req as any).protocol || \"http\";\n  const host = req.headers.host ?? \"localhost\";\n  const url = `${protocol}://${host}${(req as any).originalUrl ?? req.url ?? \"\"}`;\n\n  const headers = new Headers();\n  for (const [key, value] of Object.entries(req.headers)) {\n    if (value === undefined) continue;\n    if (Array.isArray(value)) {\n      for (const v of value) headers.append(key, v);\n    } else {\n      headers.set(key, value);\n    }\n  }\n\n  // Wire an AbortSignal so client disconnects propagate to the fetch handler\n  const controller = new AbortController();\n  res.on(\"close\", () => {\n    if (!res.writableFinished) controller.abort();\n  });\n\n  const init: RequestInit & { duplex?: \"half\" } = {\n    method,\n    headers,\n    signal: controller.signal,\n  };\n\n  const { body, contentType } = synthesizeBody(expressReq.body);\n  if (contentType) {\n    headers.set(\"content-type\", contentType);\n  }\n  headers.delete(\"content-length\");\n  if (body !== undefined) {\n    init.body = body;\n  }\n\n  return new Request(url, init);\n}\n\nfunction hasPreParsedBody(req: IncomingMessage & { body?: unknown }): boolean {\n  if (req.body === undefined || req.body === null) return false;\n\n  // Check if the stream has already been consumed.\n  const state = (req as any)._readableState;\n  return Boolean(\n    req.readableEnded || req.complete || state?.ended || state?.endEmitted,\n  );\n}\n\nfunction synthesizeBody(body: unknown): {\n  body?: BodyInit;\n  contentType?: string;\n} {\n  if (Buffer.isBuffer(body) || body instanceof Uint8Array) {\n    return { body };\n  }\n  if (typeof body === \"string\") {\n    return { body };\n  }\n  if (typeof body === \"object\" && body !== null) {\n    return { body: JSON.stringify(body), contentType: \"application/json\" };\n  }\n  return {};\n}\n"],"mappings":";;;;;;;AAoBA,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAU,CAAC;;;;;;;;;AAehE,SAAgB,yBACd,SACoB;CACpB,MAAM,cAAcA,oDAAyB,QAAQ;AAErD,QAAO,OAAO,KAAsB,QAAwB;EAC1D,MAAM,UAAU,IAAI,UAAU,OAAO,aAAa;AAGlD,MAAI,qBAAqB,IAAI,OAAO,IAAI,CAAC,iBAAiB,IAAI,CAC5D,QAAO,YAAY,KAAK,IAAI;AAI9B,MAAI;AAGF,wDAAmB,KADF,MAAM,QADN,sBAAsB,KAAK,IAAI,CACR,CACP;WAC1B,KAAc;AACrB,6BAAO,MAAM,EAAE,KAAK,EAAE,kDAAkD;AACxE,OAAI,CAAC,IAAI,aAAa;AACpB,QAAI,aAAa;AACjB,QAAI,IAAI,wBAAwB;;;;;;;;;AAUxC,SAAS,sBACP,KACA,KACS;CACT,MAAM,aAAa;CACnB,MAAM,UAAU,IAAI,UAAU,OAAO,aAAa;CAIlD,MAAM,MAAM,GAFM,IAAY,YAAY,OAElB,KADX,IAAI,QAAQ,QAAQ,cACI,IAAY,eAAe,IAAI,OAAO;CAE3E,MAAM,UAAU,IAAI,SAAS;AAC7B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,QAAQ,EAAE;AACtD,MAAI,UAAU,OAAW;AACzB,MAAI,MAAM,QAAQ,MAAM,CACtB,MAAK,MAAM,KAAK,MAAO,SAAQ,OAAO,KAAK,EAAE;MAE7C,SAAQ,IAAI,KAAK,MAAM;;CAK3B,MAAM,aAAa,IAAI,iBAAiB;AACxC,KAAI,GAAG,eAAe;AACpB,MAAI,CAAC,IAAI,iBAAkB,YAAW,OAAO;GAC7C;CAEF,MAAM,OAA0C;EAC9C;EACA;EACA,QAAQ,WAAW;EACpB;CAED,MAAM,EAAE,MAAM,gBAAgB,eAAe,WAAW,KAAK;AAC7D,KAAI,YACF,SAAQ,IAAI,gBAAgB,YAAY;AAE1C,SAAQ,OAAO,iBAAiB;AAChC,KAAI,SAAS,OACX,MAAK,OAAO;AAGd,QAAO,IAAI,QAAQ,KAAK,KAAK;;AAG/B,SAAS,iBAAiB,KAAoD;AAC5E,KAAI,IAAI,SAAS,UAAa,IAAI,SAAS,KAAM,QAAO;CAGxD,MAAM,QAAS,IAAY;AAC3B,QAAO,QACL,IAAI,iBAAiB,IAAI,YAAY,OAAO,SAAS,OAAO,WAC7D;;AAGH,SAAS,eAAe,MAGtB;AACA,KAAI,OAAO,SAAS,KAAK,IAAI,gBAAgB,WAC3C,QAAO,EAAE,MAAM;AAEjB,KAAI,OAAO,SAAS,SAClB,QAAO,EAAE,MAAM;AAEjB,KAAI,OAAO,SAAS,YAAY,SAAS,KACvC,QAAO;EAAE,MAAM,KAAK,UAAU,KAAK;EAAE,aAAa;EAAoB;AAExE,QAAO,EAAE"}