UNPKG

@trpc/server

Version:

The tRPC server library

224 lines (220 loc) • 7.5 kB
import { __toESM, getErrorShape, require_objectSpread2 } from "./getErrorShape-Uhlrl4Bk.mjs"; import { TRPCError, getTRPCErrorFromUnknown, transformTRPCResponse } from "./tracked-gU3ttYjg.mjs"; import { run } from "./utils-DdbbrDku.mjs"; import { isAbortError, resolveResponse } from "./resolveResponse-CzlbRpCI.mjs"; //#region src/adapters/node-http/incomingMessageToRequest.ts function createBody(req, opts) { if ("body" in req) { if (req.body === void 0) return void 0; if (typeof req.body === "string") return req.body; return JSON.stringify(req.body); } let size = 0; let hasClosed = false; return new ReadableStream({ start(controller) { const onData = (chunk) => { size += chunk.length; if (!opts.maxBodySize || size <= opts.maxBodySize) { controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)); return; } controller.error(new TRPCError({ code: "PAYLOAD_TOO_LARGE" })); hasClosed = true; req.off("data", onData); req.off("end", onEnd); }; const onEnd = () => { if (hasClosed) return; hasClosed = true; req.off("data", onData); req.off("end", onEnd); controller.close(); }; req.on("data", onData); req.on("end", onEnd); }, cancel() { req.destroy(); } }); } function createURL(req) { try { var _ref, _req$headers$host; const protocol = req.headers[":scheme"] && req.headers[":scheme"] === "https" || req.socket && "encrypted" in req.socket && req.socket.encrypted ? "https:" : "http:"; const host = (_ref = (_req$headers$host = req.headers.host) !== null && _req$headers$host !== void 0 ? _req$headers$host : req.headers[":authority"]) !== null && _ref !== void 0 ? _ref : "localhost"; return new URL(req.url, `${protocol}//${host}`); } catch (cause) { throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid URL", cause }); } } function createHeaders(incoming) { const headers = new Headers(); for (const key in incoming) { const value = incoming[key]; if (typeof key === "string" && key.startsWith(":")) continue; if (Array.isArray(value)) for (const item of value) headers.append(key, item); else if (value != null) headers.append(key, value); } return headers; } /** * Convert an [`IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage) to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) */ function incomingMessageToRequest(req, res, opts) { var _req$socket2, _req$socket2$once; const ac = new AbortController(); const onAbort = () => { var _req$socket, _req$socket$off; res.off("close", onAbort); (_req$socket = req.socket) === null || _req$socket === void 0 || (_req$socket$off = _req$socket.off) === null || _req$socket$off === void 0 || _req$socket$off.call(_req$socket, "close", onAbort); ac.abort(); }; res.once("close", onAbort); (_req$socket2 = req.socket) === null || _req$socket2 === void 0 || (_req$socket2$once = _req$socket2.once) === null || _req$socket2$once === void 0 || _req$socket2$once.call(_req$socket2, "close", onAbort); const url = createURL(req); const init = { headers: createHeaders(req.headers), method: req.method, signal: ac.signal }; if (req.method !== "GET" && req.method !== "HEAD") { init.body = createBody(req, opts); init.duplex = "half"; } const request = new Request(url, init); return request; } //#endregion //#region src/adapters/node-http/writeResponse.ts async function writeResponseBodyChunk(res, chunk) { if (res.write(chunk) === false) await new Promise((resolve, reject) => { const onError = (err) => { reject(err); cleanup(); }; const onDrain = () => { resolve(); cleanup(); }; const cleanup = () => { res.off("error", onError); res.off("drain", onDrain); }; res.once("error", onError); res.once("drain", onDrain); }); } /** * @internal */ async function writeResponseBody(opts) { const { res } = opts; try { const writableStream = new WritableStream({ async write(chunk) { var _res$flush; await writeResponseBodyChunk(res, chunk); (_res$flush = res.flush) === null || _res$flush === void 0 || _res$flush.call(res); } }); await opts.body.pipeTo(writableStream, { signal: opts.signal }); } catch (err) { if (isAbortError(err)) return; throw err; } } /** * @internal */ async function writeResponse(opts) { const { response, rawResponse } = opts; if (rawResponse.statusCode === 200) rawResponse.statusCode = response.status; for (const [key, value] of response.headers) rawResponse.setHeader(key, value); try { if (response.body) await writeResponseBody({ res: rawResponse, signal: opts.request.signal, body: response.body }); } catch (err) { if (!rawResponse.headersSent) rawResponse.statusCode = 500; throw err; } finally { rawResponse.end(); } } //#endregion //#region src/adapters/node-http/nodeHTTPRequestHandler.ts var import_objectSpread2 = __toESM(require_objectSpread2(), 1); /** * @internal */ function internal_exceptionHandler(opts) { return (cause) => { var _opts$onError; const { res, req } = opts; const error = getTRPCErrorFromUnknown(cause); const shape = getErrorShape({ config: opts.router._def._config, error, type: "unknown", path: void 0, input: void 0, ctx: void 0 }); (_opts$onError = opts.onError) === null || _opts$onError === void 0 || _opts$onError.call(opts, { req, error, type: "unknown", path: void 0, input: void 0, ctx: void 0 }); const transformed = transformTRPCResponse(opts.router._def._config, { error: shape }); res.statusCode = shape.data.httpStatus; res.end(JSON.stringify(transformed)); }; } /** * @remark the promise never rejects */ async function nodeHTTPRequestHandler(opts) { return new Promise((resolve) => { var _opts$middleware; const handleViaMiddleware = (_opts$middleware = opts.middleware) !== null && _opts$middleware !== void 0 ? _opts$middleware : (_req, _res, next) => next(); opts.res.once("finish", () => { resolve(); }); return handleViaMiddleware(opts.req, opts.res, (err) => { run(async () => { var _opts$maxBodySize; const request = incomingMessageToRequest(opts.req, opts.res, { maxBodySize: (_opts$maxBodySize = opts.maxBodySize) !== null && _opts$maxBodySize !== void 0 ? _opts$maxBodySize : null }); const createContext = async (innerOpts) => { var _opts$createContext; return await ((_opts$createContext = opts.createContext) === null || _opts$createContext === void 0 ? void 0 : _opts$createContext.call(opts, (0, import_objectSpread2.default)((0, import_objectSpread2.default)({}, opts), innerOpts))); }; const response = await resolveResponse((0, import_objectSpread2.default)((0, import_objectSpread2.default)({}, opts), {}, { req: request, error: err ? getTRPCErrorFromUnknown(err) : null, createContext, onError(o) { var _opts$onError2; opts === null || opts === void 0 || (_opts$onError2 = opts.onError) === null || _opts$onError2 === void 0 || _opts$onError2.call(opts, (0, import_objectSpread2.default)((0, import_objectSpread2.default)({}, o), {}, { req: opts.req })); } })); await writeResponse({ request, response, rawResponse: opts.res }); }).catch(internal_exceptionHandler(opts)); }); }); } //#endregion export { createURL, incomingMessageToRequest, internal_exceptionHandler, nodeHTTPRequestHandler }; //# sourceMappingURL=node-http-Du8akt-R.mjs.map