first commit

This commit is contained in:
2026-07-12 15:55:18 +05:30
commit ee98026cc5
404 changed files with 44522 additions and 0 deletions
+113
View File
@@ -0,0 +1,113 @@
/**
* node:http adapter — bridge a WinterCG `fetch(request) => Response` handler
* onto a Node HTTP server, with no external dependencies. Converts a Node
* `IncomingMessage` into a web `Request` and writes a web `Response` back into a
* `ServerResponse` (preserving multiple `Set-Cookie` headers).
*
* Caveat: the production handler uses Bun-native APIs (Bun.file for assets,
* Bun.serve for websockets, Bun.SQL / bun:sqlite for the database), so running
* the FULL app under plain Node needs Bun-compatible globals. This adapter is
* for WinterCG hosts and for embedding the handler behind an existing
* `node:http` server; the Request/Response conversion itself is fully portable.
*/
import type { IncomingMessage, ServerResponse, Server } from "node:http";
export type FetchHandler = (req: Request) => Response | undefined | Promise<Response | undefined>;
/** Convert a Node IncomingMessage into a web Request (buffers the body). */
export async function toRequest(
req: IncomingMessage,
opts: { origin?: string } = {},
): Promise<Request> {
const method = req.method ?? "GET";
const host = req.headers.host ?? "localhost";
const proto = (asString(req.headers["x-forwarded-proto"]) ?? "http").split(",")[0]!.trim();
const origin = opts.origin ?? `${proto}://${host}`;
const url = new URL(req.url ?? "/", origin);
const headers = new Headers();
for (const [key, value] of Object.entries(req.headers)) {
if (value === undefined) continue;
if (Array.isArray(value)) for (const v of value) headers.append(key, v);
else headers.set(key, value);
}
const hasBody = method !== "GET" && method !== "HEAD";
const body = hasBody ? ((await readBody(req)) as BodyInit) : undefined;
return new Request(url, { method, headers, body });
}
function asString(value: string | string[] | undefined): string | undefined {
return Array.isArray(value) ? value[0] : value;
}
function readBody(req: IncomingMessage): Promise<Uint8Array> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
req.on("data", (c: Buffer) => chunks.push(c));
req.on("end", () => resolve(new Uint8Array(Buffer.concat(chunks))));
req.on("error", reject);
});
}
/** Write a web Response into a Node ServerResponse. */
export async function writeResponse(res: ServerResponse, response: Response): Promise<void> {
const headers: Record<string, string | string[]> = {};
response.headers.forEach((value, key) => {
if (key.toLowerCase() !== "set-cookie") headers[key] = value;
});
// Multiple Set-Cookie headers must stay separate (Headers.forEach joins them).
const getSetCookie = (response.headers as { getSetCookie?: () => string[] }).getSetCookie;
const cookies = typeof getSetCookie === "function" ? getSetCookie.call(response.headers) : [];
if (cookies.length) headers["set-cookie"] = cookies;
res.writeHead(response.status, headers);
if (response.body) {
const reader = response.body.getReader();
for (;;) {
const { done, value } = await reader.read();
if (done) break;
res.write(value);
}
} else {
const buf = new Uint8Array(await response.arrayBuffer());
if (buf.length) res.write(buf);
}
res.end();
}
/** A `node:http` request listener that dispatches to a fetch handler. */
export function nodeListener(handler: FetchHandler, opts: { origin?: string } = {}) {
return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {
try {
const response = await handler(await toRequest(req, opts));
if (!response) {
// A missing response means the handler expected a protocol upgrade
// (e.g. a WebSocket), which this HTTP adapter does not perform.
res.writeHead(426, { "content-type": "text/plain" });
res.end("Upgrade Required");
return;
}
await writeResponse(res, response);
} catch (err) {
if (!res.headersSent) res.writeHead(500, { "content-type": "text/plain" });
res.end("Internal Server Error");
console.error("[wrnexus] node adapter error:", err);
}
};
}
/** Create and start a `node:http` server for a fetch handler. */
export async function serveNode(
handler: FetchHandler,
opts: { port?: number; hostname?: string } = {},
): Promise<Server> {
const { createServer } = await import("node:http");
const server = createServer(nodeListener(handler, {}));
const port = opts.port ?? 3000;
server.listen(port, opts.hostname ?? "0.0.0.0");
console.log(`WrNexus (node adapter) listening on http://localhost:${port}`);
return server;
}