import { ZodType, ZodSchema } from 'zod'; import { FormFieldConfig, ZodSchemaWithRender } from './types'; /** * Extends a Zod schema with render configuration for form generation */ export function extendZodSchema(schema: ZodType): ZodSchemaWithRender { const extendedSchema = schema as ZodSchemaWithRender; extendedSchema.withRender = function(config: FormFieldConfig): ZodSchemaWithRender { const newSchema = this._def ? this._def.clone() : this; (newSchema as ZodSchemaWithRender)._renderConfig = config; (newSchema as ZodSchemaWithRender).withRender = extendedSchema.withRender; return newSchema as ZodSchemaWithRender; }; return extendedSchema; } /** * Checks if a schema has render configuration */ export function hasRenderConfig(schema: unknown): schema is ZodSchemaWithRender { return schema && typeof (schema as any).withRender === 'function'; } /** * Extracts render configuration from a schema */ export function getRenderConfig(schema: ZodType): FormFieldConfig | undefined { if (hasRenderConfig(schema)) { return schema._renderConfig; } return undefined; } /** * Infers form field type from Zod schema type */ export function inferFieldTypeFromSchema(schema: ZodType): string { const typeName = (schema._def as Record)?.typeName; switch (typeName) { case 'ZodString': return 'text'; case 'ZodNumber': case 'ZodBigInt': return 'number'; case 'ZodBoolean': return 'checkbox'; case 'ZodEnum': case 'ZodNativeEnum': return 'select'; case 'ZodArray': return 'select'; case 'ZodUnion': case 'ZodDiscriminatedUnion': return 'select'; case 'ZodDate': return 'text'; case 'ZodObject': return 'object'; default: return 'text'; } } /** * Extracts options from enum or union schemas */ export function extractOptionsFromSchema(schema: ZodType): Array<{ value: unknown; label: string }> | undefined { const typeName = (schema._def as Record)?.typeName; if (typeName === 'ZodEnum') { const values = (schema._def as Record)?.values; if (Array.isArray(values)) { return values.map((value: string) => ({ value, label: value, })); } } if (typeName === 'ZodNativeEnum') { const enumObject = (schema._def as Record)?.values; if (enumObject) { return Object.entries(enumObject).map(([key, value]) => ({ value, label: key, })); } } if (typeName === 'ZodUnion') { const options: Array<{ value: unknown; label: string }> = []; const unionOptions = (schema._def as Record)?.options; if (Array.isArray(unionOptions)) { for (const option of unionOptions) { if ((option._def as Record)?.typeName === 'ZodLiteral') { const value = (option._def as any)?.value; if (value !== undefined) { options.push({ value, label: String(value), }); } } } } return options.length > 0 ? options : undefined; } return undefined; } /** * Checks if a schema is optional */ export function isOptionalSchema(schema: ZodType): boolean { const typeName = (schema._def as Record)?.typeName; return typeName === 'ZodOptional' || typeName === 'ZodDefault'; } /** * Gets the inner schema from optional/default wrappers */ export function getInnerSchema(schema: ZodType): ZodType { const typeName = (schema._def as Record)?.typeName; if (typeName === 'ZodOptional' || typeName === 'ZodDefault') { const innerType = (schema._def as any)?.innerType; return innerType || schema; } return schema; } /** * Extracts validation constraints from a schema */ export function extractValidationConstraints(schema: ZodType) { const innerSchema = getInnerSchema(schema); const typeName = (innerSchema._def as any)?.typeName; const constraints: Record = {}; if (typeName === 'ZodString') { const def = innerSchema._def as any; if (def.checks && Array.isArray(def.checks)) { for (const check of def.checks) { switch (check.kind) { case 'min': constraints.minLength = check.value; break; case 'max': constraints.maxLength = check.value; break; case 'email': constraints.type = 'email'; break; case 'url': constraints.type = 'url'; break; case 'regex': constraints.pattern = check.regex; break; } } } } if (typeName === 'ZodNumber') { const def = innerSchema._def as any; if (def.checks && Array.isArray(def.checks)) { for (const check of def.checks) { switch (check.kind) { case 'min': constraints.min = check.value; break; case 'max': constraints.max = check.value; break; case 'int': constraints.step = 1; break; } } } } return constraints; } /** * Gets default value from schema */ export function getDefaultValue(schema: ZodType): any { const typeName = (schema._def as Record)?.typeName; if (typeName === 'ZodDefault') { const defaultValue = (schema._def as any)?.defaultValue; if (typeof defaultValue === 'function') { return defaultValue(); } return defaultValue; } const innerSchema = getInnerSchema(schema); const innerTypeName = (innerSchema._def as any)?.typeName; switch (innerTypeName) { case 'ZodString': return ''; case 'ZodNumber': case 'ZodBigInt': return 0; case 'ZodBoolean': return false; case 'ZodArray': return []; case 'ZodObject': return {}; case 'ZodDate': return new Date(); default: return undefined; } }