muddle/adapters/vercel-vercel.ts
2026-10-23 08:53:07 -04:00

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);
}