@pulzar/core
Version:
Next-generation Node.js framework for ultra-fast web applications with zero-reflection DI, GraphQL, WebSockets, events, and edge runtime support
328 lines • 10 kB
JavaScript
import { z } from "zod";
export class ZodToOpenAPI {
schemas = new Map();
components = {};
/**
* Convert a Zod schema to OpenAPI schema
*/
convert(schema, name) {
if (schema instanceof z.ZodString) {
return this.convertString(schema);
}
if (schema instanceof z.ZodNumber) {
return this.convertNumber(schema);
}
if (schema instanceof z.ZodBoolean) {
return this.convertBoolean(schema);
}
if (schema instanceof z.ZodArray) {
return this.convertArray(schema);
}
if (schema instanceof z.ZodObject) {
return this.convertObject(schema, name);
}
if (schema instanceof z.ZodEnum) {
return this.convertEnum(schema);
}
if (schema instanceof z.ZodUnion) {
return this.convertUnion(schema);
}
if (schema instanceof z.ZodIntersection) {
return this.convertIntersection(schema);
}
if (schema instanceof z.ZodOptional) {
return this.convertOptional(schema);
}
if (schema instanceof z.ZodNullable) {
return this.convertNullable(schema);
}
if (schema instanceof z.ZodDefault) {
return this.convertDefault(schema);
}
if (schema instanceof z.ZodLiteral) {
return this.convertLiteral(schema);
}
if (schema instanceof z.ZodDate) {
return this.convertDate(schema);
}
if (schema instanceof z.ZodRecord) {
return this.convertRecord(schema);
}
if (schema instanceof z.ZodTuple) {
return this.convertTuple(schema);
}
if (schema instanceof z.ZodSet) {
return this.convertSet(schema);
}
if (schema instanceof z.ZodMap) {
return this.convertMap(schema);
}
if (schema instanceof z.ZodPromise) {
return this.convertPromise(schema);
}
if (schema instanceof z.ZodEffects) {
return this.convertEffects(schema);
}
if (schema instanceof z.ZodNativeEnum) {
return this.convertNativeEnum(schema);
}
if (schema instanceof z.ZodDiscriminatedUnion) {
return this.convertDiscriminatedUnion(schema);
}
if (schema instanceof z.ZodLazy) {
return this.convertLazy(schema);
}
if (schema instanceof z.ZodBranded) {
return this.convertBranded(schema);
}
if (schema instanceof z.ZodPipeline) {
return this.convertPipeline(schema);
}
if (schema instanceof z.ZodReadonly) {
return this.convertReadonly(schema);
}
if (schema instanceof z.ZodCatch) {
return this.convertCatch(schema);
}
// Fallback for unknown schemas
return { type: "string" };
}
convertString(schema) {
const result = { type: "string" };
if (schema._def.checks) {
for (const check of schema._def.checks) {
switch (check.kind) {
case "min":
result.minLength = check.value;
break;
case "max":
result.maxLength = check.value;
break;
case "regex":
result.pattern = check.regex.source;
break;
case "email":
result.format = "email";
break;
case "url":
result.format = "uri";
break;
case "uuid":
result.format = "uuid";
break;
case "datetime":
result.format = "date-time";
break;
case "date":
result.format = "date";
break;
case "time":
result.format = "time";
break;
case "ip":
result.format = "ip";
break;
}
}
}
return result;
}
convertNumber(schema) {
const result = { type: "number" };
if (schema._def.checks) {
for (const check of schema._def.checks) {
switch (check.kind) {
case "min":
result.minimum = check.value;
result.exclusiveMinimum = check.inclusive ? false : check.value;
break;
case "max":
result.maximum = check.value;
result.exclusiveMaximum = check.inclusive ? false : check.value;
break;
case "int":
result.type = "integer";
break;
case "multipleOf":
result.multipleOf = check.value;
break;
}
}
}
return result;
}
convertBoolean(schema) {
return { type: "boolean" };
}
convertArray(schema) {
return {
type: "array",
items: this.convert(schema.element),
};
}
convertObject(schema, name) {
const shape = schema.shape;
const properties = {};
const required = [];
for (const [key, value] of Object.entries(shape)) {
properties[key] = this.convert(value);
// Check if field is required (not optional)
if (!(value instanceof z.ZodOptional)) {
required.push(key);
}
}
const result = {
type: "object",
properties,
additionalProperties: false,
};
if (required.length > 0) {
result.required = required;
}
// If name is provided, store in components for reuse
if (name) {
this.schemas.set(name, result);
if (!this.components.schemas) {
this.components.schemas = {};
}
this.components.schemas[name] = result;
}
return result;
}
convertEnum(schema) {
return {
type: "string",
enum: schema._def.values,
};
}
convertUnion(schema) {
return {
oneOf: schema._def.options.map((option) => this.convert(option)),
};
}
convertIntersection(schema) {
return {
allOf: [this.convert(schema._def.left), this.convert(schema._def.right)],
};
}
convertOptional(schema) {
return this.convert(schema.unwrap());
}
convertNullable(schema) {
const result = this.convert(schema.unwrap());
result.nullable = true;
return result;
}
convertDefault(schema) {
const result = this.convert(schema.removeDefault());
result.default = schema._def.defaultValue();
return result;
}
convertLiteral(schema) {
return {
type: typeof schema._def.value,
enum: [schema._def.value],
};
}
convertDate(schema) {
return {
type: "string",
format: "date-time",
};
}
convertRecord(schema) {
return {
type: "object",
additionalProperties: this.convert(schema._def.valueType),
};
}
convertTuple(schema) {
return {
type: "array",
items: {
oneOf: schema._def.items.map((item) => this.convert(item)),
},
minItems: schema._def.items.length,
maxItems: schema._def.items.length,
};
}
convertSet(schema) {
return {
type: "array",
items: this.convert(schema._def.valueType),
uniqueItems: true,
};
}
convertMap(schema) {
return {
type: "object",
additionalProperties: this.convert(schema._def.valueType),
};
}
convertPromise(schema) {
return this.convert(schema.unwrap());
}
convertEffects(schema) {
return this.convert(schema.innerType());
}
convertNativeEnum(schema) {
return {
type: "string",
enum: Object.values(schema._def.values),
};
}
convertDiscriminatedUnion(schema) {
return {
oneOf: schema._def.options.map((option) => this.convert(option)),
};
}
convertLazy(schema) {
return this.convert(schema._def.getter());
}
convertBranded(schema) {
return this.convert(schema.unwrap());
}
convertPipeline(schema) {
return this.convert(schema._def.out);
}
convertReadonly(schema) {
const result = this.convert(schema.unwrap());
result.readOnly = true;
return result;
}
convertCatch(schema) {
return this.convert(schema._def.innerType);
}
/**
* Get all registered schemas
*/
getSchemas() {
return Object.fromEntries(this.schemas);
}
/**
* Get OpenAPI components
*/
getComponents() {
return this.components;
}
/**
* Clear all registered schemas
*/
clear() {
this.schemas.clear();
this.components = {};
}
}
// Create default instance
export const zodToOpenAPI = new ZodToOpenAPI();
// Convenience functions
export function convertZodToOpenAPI(schema, name) {
return zodToOpenAPI.convert(schema, name);
}
export function getOpenAPISchemas() {
return zodToOpenAPI.getSchemas();
}
export function getOpenAPIComponents() {
return zodToOpenAPI.getComponents();
}
//# sourceMappingURL=zod-to-openapi.js.map