mirror of
https://github.com/hanzoai/trpc-openapi.git
synced 2026-10-30 18:18:55 +00:00
163 lines
8.4 KiB
JavaScript
163 lines
8.4 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.createOpenApiNodeHttpHandler = void 0;
|
|
const server_1 = require("@trpc/server");
|
|
const unstable_core_do_not_import_1 = require("@trpc/server/unstable-core-do-not-import");
|
|
const zod_1 = require("zod");
|
|
const generator_1 = require("../../generator");
|
|
const utils_1 = require("../../utils");
|
|
const errors_1 = require("./errors");
|
|
const input_1 = require("./input");
|
|
const procedures_1 = require("./procedures");
|
|
const createOpenApiNodeHttpHandler = (opts) => {
|
|
const router = Object.assign({}, opts.router);
|
|
// Validate router
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
(0, generator_1.generateOpenApiDocument)(router, { title: '', version: '', baseUrl: '' });
|
|
}
|
|
const { createContext, responseMeta, onError, maxBodySize } = opts;
|
|
const getProcedure = (0, procedures_1.createProcedureCache)(router);
|
|
return async (req, res, next) => {
|
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
const sendResponse = (statusCode, headers, body) => {
|
|
res.statusCode = statusCode;
|
|
res.setHeader('Content-Type', 'application/json');
|
|
for (const [key, value] of Object.entries(headers)) {
|
|
if (typeof value !== 'undefined') {
|
|
res.setHeader(key, value);
|
|
}
|
|
}
|
|
res.end(JSON.stringify(body));
|
|
};
|
|
const method = req.method;
|
|
const reqUrl = req.url;
|
|
const url = new URL(reqUrl.startsWith('/') ? `http://127.0.0.1${reqUrl}` : reqUrl);
|
|
const path = (0, utils_1.normalizePath)(url.pathname);
|
|
let input = undefined;
|
|
let ctx = undefined;
|
|
let info = undefined;
|
|
let data = undefined;
|
|
const { procedure, pathInput } = (_a = getProcedure(method, path)) !== null && _a !== void 0 ? _a : {};
|
|
try {
|
|
if (!procedure) {
|
|
if (next) {
|
|
return next();
|
|
}
|
|
// Can be used for warmup
|
|
if (method === 'HEAD') {
|
|
sendResponse(204, {}, undefined);
|
|
return;
|
|
}
|
|
throw new server_1.TRPCError({
|
|
message: 'Not found',
|
|
code: 'NOT_FOUND',
|
|
});
|
|
}
|
|
const contentType = (0, utils_1.getContentType)(req);
|
|
const useBody = (0, utils_1.acceptsRequestBody)(method);
|
|
if (useBody && !(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json'))) {
|
|
throw new server_1.TRPCError({
|
|
code: 'UNSUPPORTED_MEDIA_TYPE',
|
|
message: contentType
|
|
? `Unsupported content-type "${contentType}`
|
|
: 'Missing content-type header',
|
|
});
|
|
}
|
|
const { inputParser } = (0, utils_1.getInputOutputParsers)(procedure.procedure);
|
|
const unwrappedSchema = (0, utils_1.unwrapZodType)(inputParser, true);
|
|
// input should stay undefined if z.void()
|
|
if (!(0, utils_1.instanceofZodTypeLikeVoid)(unwrappedSchema)) {
|
|
input = Object.assign(Object.assign({}, (useBody ? await (0, input_1.getBody)(req, maxBodySize) : (0, input_1.getQuery)(req, url))), pathInput);
|
|
}
|
|
// if supported, coerce all string values to correct types
|
|
if (utils_1.zodSupportsCoerce && (0, utils_1.instanceofZodTypeObject)(unwrappedSchema)) {
|
|
if (!useBody) {
|
|
for (const [key, shape] of Object.entries(unwrappedSchema.shape)) {
|
|
let isArray = false;
|
|
// Check if it's a direct array
|
|
if (shape instanceof zod_1.ZodArray) {
|
|
isArray = true;
|
|
}
|
|
// Check if it's an optional array
|
|
else if ((0, utils_1.instanceofZodTypeOptional)(shape)) {
|
|
const innerType = shape.unwrap();
|
|
if (innerType instanceof zod_1.ZodArray) {
|
|
isArray = true;
|
|
}
|
|
}
|
|
if (isArray && input[key] !== undefined && !Array.isArray(input[key])) {
|
|
input[key] = [input[key]];
|
|
}
|
|
}
|
|
}
|
|
(0, utils_1.coerceSchema)(unwrappedSchema);
|
|
}
|
|
info = {
|
|
isBatchCall: false,
|
|
accept: null,
|
|
calls: [],
|
|
type: procedure.type,
|
|
connectionParams: null,
|
|
signal: (0, utils_1.getRequestSignal)(req, res, maxBodySize),
|
|
url,
|
|
};
|
|
ctx = await (createContext === null || createContext === void 0 ? void 0 : createContext({ req, res, info }));
|
|
const caller = router.createCaller(ctx);
|
|
const segments = procedure.path.split('.');
|
|
const procedureFn = segments.reduce((acc, curr) => acc[curr], caller);
|
|
data = await procedureFn(input);
|
|
const meta = responseMeta === null || responseMeta === void 0 ? void 0 : responseMeta({
|
|
type: procedure.type,
|
|
paths: [procedure.path],
|
|
ctx,
|
|
data: [data],
|
|
errors: [],
|
|
info,
|
|
eagerGeneration: true,
|
|
});
|
|
const statusCode = (_b = meta === null || meta === void 0 ? void 0 : meta.status) !== null && _b !== void 0 ? _b : 200;
|
|
const headers = (_c = meta === null || meta === void 0 ? void 0 : meta.headers) !== null && _c !== void 0 ? _c : {};
|
|
const body = data;
|
|
sendResponse(statusCode, headers, body);
|
|
}
|
|
catch (cause) {
|
|
const error = (0, errors_1.getErrorFromUnknown)(cause);
|
|
onError === null || onError === void 0 ? void 0 : onError({
|
|
error,
|
|
type: (_d = procedure === null || procedure === void 0 ? void 0 : procedure.type) !== null && _d !== void 0 ? _d : 'unknown',
|
|
path: procedure === null || procedure === void 0 ? void 0 : procedure.path,
|
|
input,
|
|
ctx,
|
|
req,
|
|
});
|
|
const meta = responseMeta === null || responseMeta === void 0 ? void 0 : responseMeta({
|
|
type: (_e = procedure === null || procedure === void 0 ? void 0 : procedure.type) !== null && _e !== void 0 ? _e : 'unknown',
|
|
paths: (procedure === null || procedure === void 0 ? void 0 : procedure.path) ? [procedure === null || procedure === void 0 ? void 0 : procedure.path] : undefined,
|
|
ctx,
|
|
data: [data],
|
|
errors: [error],
|
|
info,
|
|
eagerGeneration: true,
|
|
});
|
|
const errorShape = (0, unstable_core_do_not_import_1.getErrorShape)({
|
|
config: router._def._config,
|
|
error,
|
|
type: (_f = procedure === null || procedure === void 0 ? void 0 : procedure.type) !== null && _f !== void 0 ? _f : 'unknown',
|
|
path: procedure === null || procedure === void 0 ? void 0 : procedure.path,
|
|
input,
|
|
ctx,
|
|
});
|
|
const isInputValidationError = error.code === 'BAD_REQUEST' &&
|
|
error.cause instanceof Error &&
|
|
error.cause.name === 'ZodError';
|
|
const statusCode = (_h = (_g = meta === null || meta === void 0 ? void 0 : meta.status) !== null && _g !== void 0 ? _g : errors_1.TRPC_ERROR_CODE_HTTP_STATUS[error.code]) !== null && _h !== void 0 ? _h : 500;
|
|
const headers = (_j = meta === null || meta === void 0 ? void 0 : meta.headers) !== null && _j !== void 0 ? _j : {};
|
|
const body = Object.assign(Object.assign({}, errorShape), { message: isInputValidationError
|
|
? 'Input validation failed'
|
|
: ((_l = (_k = errorShape === null || errorShape === void 0 ? void 0 : errorShape.message) !== null && _k !== void 0 ? _k : error.message) !== null && _l !== void 0 ? _l : 'An error occurred'), code: error.code, issues: isInputValidationError ? error.cause.issues : undefined });
|
|
sendResponse(statusCode, headers, body);
|
|
}
|
|
};
|
|
};
|
|
exports.createOpenApiNodeHttpHandler = createOpenApiNodeHttpHandler;
|
|
//# sourceMappingURL=core.js.map
|