fastify-zod-openapi
Version:
Fastify plugin for zod-openapi
327 lines (326 loc) • 9.81 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const fp = require("fastify-plugin");
const api = require("zod-openapi/api");
const fastJsonStringify = require("fast-json-stringify");
const zodOpenapi = require("zod-openapi");
const error = require("@fastify/error");
const _interopDefaultCompat = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
const fp__default = /* @__PURE__ */ _interopDefaultCompat(fp);
const fastJsonStringify__default = /* @__PURE__ */ _interopDefaultCompat(fastJsonStringify);
const FASTIFY_ZOD_OPENAPI_CONFIG = Symbol("fastify-zod-openapi-config");
const FASTIFY_ZOD_OPENAPI_COMPONENTS = Symbol(
"fastify-zod-openapi-components"
);
const fastifyZodOpenApi = async (fastify, opts) => {
const components = api.getDefaultComponents(opts.components);
fastify.addHook("onRoute", ({ schema }) => {
if (!schema || schema.hide) {
return;
}
schema[FASTIFY_ZOD_OPENAPI_CONFIG] ?? (schema[FASTIFY_ZOD_OPENAPI_CONFIG] = {
components,
documentOpts: opts.documentOpts
});
});
};
const fastifyZodOpenApiPlugin = fp__default.default(fastifyZodOpenApi, {
name: "fastify-zod-openapi"
});
const isZodType = (object) => {
var _a;
return Boolean(
object && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
((_a = Object.getPrototypeOf(object == null ? void 0 : object.constructor)) == null ? void 0 : _a.name) === "ZodType"
);
};
const isZodObject = (object) => {
var _a;
return Boolean(object && ((_a = object == null ? void 0 : object.constructor) == null ? void 0 : _a.name) === "ZodObject");
};
const createParams = (querystring, type, components, path, doucmentOpts) => Object.entries(querystring.shape).reduce(
(acc, [key, value]) => {
const parameter = api.createParamOrRef(
value,
components,
[...path, key],
type,
key,
doucmentOpts
);
if ("$ref" in parameter || !parameter.schema) {
throw new Error("References not supported");
}
acc[key] = {
...parameter.schema,
...parameter.required && { required: true }
};
return acc;
},
{}
);
const createResponseSchema = (schema, components, path, documentOpts) => {
if (isZodType(schema)) {
return api.createMediaTypeSchema(
schema,
components,
"output",
[...path, "schema"],
documentOpts
);
}
return schema;
};
const createContent = (content, components, path, documentOpts) => {
if (typeof content !== "object" || content == null) {
return content;
}
return Object.entries(content).reduce(
(acc, [key, value]) => {
if (typeof value === "object" && value !== null && "schema" in value) {
const schema = createResponseSchema(
value.schema,
components,
[...path, "schema"],
documentOpts
);
acc[key] = {
...value,
schema
};
return acc;
}
acc[key] = value;
return acc;
},
{}
);
};
const createResponse = (response, components, path, documentOpts) => {
if (typeof response !== "object" || response == null) {
return response;
}
return Object.entries(response).reduce(
(acc, [key, value]) => {
if (isZodType(value)) {
acc[key] = api.createMediaTypeSchema(
value,
components,
"output",
[...path, key],
documentOpts
);
return acc;
}
if (typeof value === "object" && value !== null && "content" in value) {
const content = createContent(
value.content,
components,
[...path, "content"],
documentOpts
);
acc[key] = {
...value,
content
};
return acc;
}
acc[key] = value;
return acc;
},
{}
);
};
const fastifyZodOpenApiTransform = ({
schema,
url,
...opts
}) => {
var _a, _b;
if (!schema || schema.hide) {
return {
schema,
url
};
}
const { response, headers, querystring, body, params } = schema;
if (!("openapiObject" in opts)) {
throw new Error("openapiObject was not found in the options");
}
const config = schema[FASTIFY_ZOD_OPENAPI_CONFIG];
if (!config) {
throw new Error("Please register the fastify-zod-openapi plugin");
}
const { components, documentOpts } = config;
(_a = opts.openapiObject)[FASTIFY_ZOD_OPENAPI_COMPONENTS] ?? (_a[FASTIFY_ZOD_OPENAPI_COMPONENTS] = config.components);
if (opts.openapiObject.openapi) {
components.openapi = opts.openapiObject.openapi;
}
(_b = opts.openapiObject)[FASTIFY_ZOD_OPENAPI_COMPONENTS] ?? (_b[FASTIFY_ZOD_OPENAPI_COMPONENTS] = components);
const transformedSchema = {
...schema
};
if (isZodType(body)) {
transformedSchema.body = api.createMediaTypeSchema(
body,
components,
"input",
[url, "body"],
documentOpts
);
}
const maybeResponse = createResponse(
response,
components,
[url, "response"],
documentOpts
);
if (maybeResponse) {
transformedSchema.response = maybeResponse;
}
if (isZodType(querystring)) {
const queryStringSchema = api.getZodObject(
querystring,
"input"
);
transformedSchema.querystring = createParams(
queryStringSchema,
"query",
components,
[url, "querystring"],
documentOpts
);
}
if (isZodType(params)) {
const paramsSchema = api.getZodObject(params, "input");
transformedSchema.params = createParams(paramsSchema, "path", components, [
url,
"params"
]);
}
if (isZodType(headers)) {
const headersSchema = api.getZodObject(headers, "input");
transformedSchema.headers = createParams(
headersSchema,
"header",
components,
[url, "headers"]
);
}
return {
schema: transformedSchema,
url
};
};
const fastifyZodOpenApiTransformObject = (opts) => {
if ("swaggerObject" in opts) {
return opts.swaggerObject;
}
const components = opts.openapiObject[FASTIFY_ZOD_OPENAPI_COMPONENTS];
if (!components) {
return opts.openapiObject;
}
return {
...opts.openapiObject,
components: api.createComponents(
opts.openapiObject.components ?? {},
components
)
};
};
class RequestValidationError extends Error {
constructor(keyword, instancePath, schemaPath, message, params) {
super(message, {
cause: params.issue
});
__publicField(this, "cause");
this.keyword = keyword;
this.instancePath = instancePath;
this.schemaPath = schemaPath;
this.message = message;
this.params = params;
}
}
class ResponseSerializationError extends error.createError(
"FST_ERR_RESPONSE_SERIALIZATION",
"Response does not match the schema",
500
) {
constructor(method, url, options) {
super();
__publicField(this, "cause");
this.method = method;
this.url = url;
this.cause = options.cause;
}
}
const createSerializerCompiler = (opts) => (routeSchema) => {
const { schema, url, method } = routeSchema;
if (!isZodType(schema)) {
return (opts == null ? void 0 : opts.fallbackSerializer) ? opts.fallbackSerializer(routeSchema) : fastJsonStringify__default.default(schema);
}
let stringify = opts == null ? void 0 : opts.stringify;
if (!stringify) {
const { schema: jsonSchema, components } = zodOpenapi.createSchema(schema, {
components: opts == null ? void 0 : opts.components,
componentRefPath: "#/definitions/"
});
const maybeDefinitions = components ? {
definitions: components
} : void 0;
stringify = fastJsonStringify__default.default({
...jsonSchema,
...maybeDefinitions
});
}
return (value) => {
const result = schema.safeParse(value);
if (!result.success) {
throw new ResponseSerializationError(method, url, {
cause: result.error
});
}
return stringify(result.data);
};
};
const serializerCompiler = createSerializerCompiler();
const validatorCompiler = ({ schema }) => (value) => {
const result = schema.safeParse(value);
if (!result.success) {
return {
error: result.error.errors.map(
(issue) => new RequestValidationError(
issue.code,
`/${issue.path.join("/")}`,
`#/${issue.path.join("/")}/${issue.code}`,
issue.message,
{
issue,
error: result.error
}
)
)
// Types are wrong https://github.com/fastify/fastify/pull/5787
};
}
return { value: result.data };
};
exports.FASTIFY_ZOD_OPENAPI_COMPONENTS = FASTIFY_ZOD_OPENAPI_COMPONENTS;
exports.FASTIFY_ZOD_OPENAPI_CONFIG = FASTIFY_ZOD_OPENAPI_CONFIG;
exports.RequestValidationError = RequestValidationError;
exports.ResponseSerializationError = ResponseSerializationError;
exports.createContent = createContent;
exports.createParams = createParams;
exports.createResponse = createResponse;
exports.createResponseSchema = createResponseSchema;
exports.createSerializerCompiler = createSerializerCompiler;
exports.fastifyZodOpenApiPlugin = fastifyZodOpenApiPlugin;
exports.fastifyZodOpenApiTransform = fastifyZodOpenApiTransform;
exports.fastifyZodOpenApiTransformObject = fastifyZodOpenApiTransformObject;
exports.isZodObject = isZodObject;
exports.isZodType = isZodType;
exports.serializerCompiler = serializerCompiler;
exports.validatorCompiler = validatorCompiler;