meta.ts•1.65 kB
import { z } from 'zod';
function unwrapOptional(schema: z.ZodTypeAny): z.ZodTypeAny {
if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {
// @ts-ignore accessing .unwrap is valid at runtime
return unwrapOptional(schema.unwrap());
}
return schema;
}
export function schemaToMeta(schema: z.ZodTypeAny): any {
const s = unwrapOptional(schema);
if (s instanceof z.ZodString) return { type: 'string' };
if (s instanceof z.ZodNumber) return { type: 'number' };
if (s instanceof z.ZodBoolean) return { type: 'boolean' };
if (s instanceof z.ZodEnum) return { type: 'string', enum: s.options };
if (s instanceof z.ZodLiteral) return { const: s.value } as any;
if (s instanceof z.ZodArray) return { type: 'array', items: schemaToMeta(s.element) } as any;
if (s instanceof z.ZodUnion) return { anyOf: s._def.options.map((o: any) => schemaToMeta(o)) } as any;
if (s instanceof z.ZodObject) {
const shape = s.shape;
const properties: Record<string, any> = {};
const required: string[] = [];
for (const key of Object.keys(shape)) {
const field = (shape as any)[key] as z.ZodTypeAny;
properties[key] = schemaToMeta(field);
const unwrapped = unwrapOptional(field);
const isOptional = field instanceof z.ZodOptional || field instanceof z.ZodNullable;
if (!isOptional) required.push(key);
}
const meta: any = { type: 'object', properties };
if (required.length) meta.required = required;
return meta;
}
if (s instanceof z.ZodRecord) return { type: 'object', additionalProperties: true } as any;
// Fallback
return { type: 'unknown' };
}