68 lines
2 KiB
TypeScript
68 lines
2 KiB
TypeScript
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
import type { App } from "../core/app.ts";
|
|
import { existsSync } from "node:fs";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
|
|
export type NodeHandler = (
|
|
request: IncomingMessage,
|
|
response: ServerResponse,
|
|
) => Promise<void>;
|
|
|
|
/** Creates a minimal Vercel configuration when the project has none. */
|
|
export async function setupVercel(root: string): Promise<string> {
|
|
const path = join(resolve(root), "vercel.json");
|
|
if (existsSync(path)) return path;
|
|
await writeFile(path, `${JSON.stringify({ cleanUrls: true }, null, 2)}\n`, "utf8");
|
|
return path;
|
|
}
|
|
|
|
/** Creates the node handler of the application. */
|
|
export function createHandler(app: App): NodeHandler {
|
|
return async (request, response) => {
|
|
const url = `http://${request.headers.host ?? "localhost"}${request.url ?? "/"}`;
|
|
const method = (request.method ?? "GET").toUpperCase();
|
|
|
|
const headers = new Headers();
|
|
for (const [name, value] of Object.entries(request.headers)) {
|
|
if (value === undefined) continue;
|
|
if (Array.isArray(value)) {
|
|
for (const entry of value) headers.append(name, entry);
|
|
continue;
|
|
}
|
|
headers.set(name, value);
|
|
}
|
|
|
|
const body =
|
|
method === "GET" || method === "HEAD"
|
|
? undefined
|
|
: await readBody(request);
|
|
|
|
const fetchRequest = new Request(url, {
|
|
method,
|
|
headers,
|
|
body,
|
|
duplex: body ? "half" : undefined,
|
|
} as RequestInit);
|
|
|
|
const result = await app.fetch(fetchRequest);
|
|
response.statusCode = result.status;
|
|
result.headers.forEach((value, name) => {
|
|
response.setHeader(name, value);
|
|
});
|
|
if (result.body) {
|
|
const buffer = Buffer.from(await result.arrayBuffer());
|
|
response.end(buffer);
|
|
return;
|
|
}
|
|
response.end();
|
|
};
|
|
}
|
|
|
|
async function readBody(request: IncomingMessage): Promise<Buffer> {
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of request) {
|
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
}
|
|
return Buffer.concat(chunks);
|
|
}
|