UNPKG

@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
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