@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
82 lines (80 loc) • 2.88 kB
JavaScript
import "reflect-metadata";
import { createCopilotNodeHandler } from "./node-fetch-handler.mjs";
import { logger } from "@copilotkit/shared";
import { sendResponse } from "@remix-run/node-fetch-server";
//#region src/v2/runtime/endpoints/express-fetch-bridge.ts
const METHODS_WITHOUT_BODY = new Set([
"GET",
"HEAD",
"OPTIONS"
]);
/**
* Creates a Node HTTP handler from a fetch handler, with Express body-parser
* compatibility. Use this instead of `createNodeFetchHandler` in Express adapters.
*
* When the body stream hasn't been consumed, delegates to the generic
* `createCopilotNodeHandler`. Only intercepts when Express middleware has
* pre-parsed the body.
*/
function createExpressNodeHandler(handler) {
const nodeHandler = createCopilotNodeHandler(handler);
return async (req, res) => {
const method = (req.method ?? "GET").toUpperCase();
if (METHODS_WITHOUT_BODY.has(method) || !hasPreParsedBody(req)) return nodeHandler(req, res);
try {
await sendResponse(res, await handler(buildPreParsedRequest(req, res)));
} catch (err) {
logger.error({ err }, "Error in Express fetch bridge (pre-parsed path)");
if (!res.headersSent) {
res.statusCode = 500;
res.end("Internal Server Error");
}
}
};
}
/**
* Build a Fetch Request from a Node IncomingMessage whose body stream has
* already been consumed by an Express body parser.
*/
function buildPreParsedRequest(req, res) {
const expressReq = req;
const method = (req.method ?? "GET").toUpperCase();
const url = `${req.protocol || "http"}://${req.headers.host ?? "localhost"}${req.originalUrl ?? req.url ?? ""}`;
const headers = new Headers();
for (const [key, value] of Object.entries(req.headers)) {
if (value === void 0) continue;
if (Array.isArray(value)) for (const v of value) headers.append(key, v);
else headers.set(key, value);
}
const controller = new AbortController();
res.on("close", () => {
if (!res.writableFinished) controller.abort();
});
const init = {
method,
headers,
signal: controller.signal
};
const { body, contentType } = synthesizeBody(expressReq.body);
if (contentType) headers.set("content-type", contentType);
headers.delete("content-length");
if (body !== void 0) init.body = body;
return new Request(url, init);
}
function hasPreParsedBody(req) {
if (req.body === void 0 || req.body === null) return false;
const state = req._readableState;
return Boolean(req.readableEnded || req.complete || state?.ended || state?.endEmitted);
}
function synthesizeBody(body) {
if (Buffer.isBuffer(body) || body instanceof Uint8Array) return { body };
if (typeof body === "string") return { body };
if (typeof body === "object" && body !== null) return {
body: JSON.stringify(body),
contentType: "application/json"
};
return {};
}
//#endregion
export { createExpressNodeHandler };
//# sourceMappingURL=express-fetch-bridge.mjs.map