125 lines
4.1 KiB
TypeScript
125 lines
4.1 KiB
TypeScript
import { z } from "zod";
|
|
import { BlueprintTarget, FieldType } from "../types/enums.js";
|
|
|
|
export const FieldOptionSchema = z.object({
|
|
value: z.string().min(1),
|
|
label: z.string().min(1),
|
|
color: z.string().optional(),
|
|
});
|
|
|
|
export const FieldValidationSchema = z.object({
|
|
min: z.number().optional(),
|
|
max: z.number().optional(),
|
|
minLength: z.number().int().optional(),
|
|
maxLength: z.number().int().optional(),
|
|
pattern: z.string().optional(),
|
|
message: z.string().optional(),
|
|
});
|
|
|
|
export const BlueprintFieldDefinitionSchema = z.object({
|
|
id: z.string().min(1),
|
|
label: z.string().min(1).max(200),
|
|
key: z.string().min(1).max(100).regex(/^[a-z_][a-z0-9_]*$/, "Must be snake_case"),
|
|
type: z.nativeEnum(FieldType),
|
|
required: z.boolean().default(false),
|
|
description: z.string().optional(),
|
|
placeholder: z.string().optional(),
|
|
defaultValue: z.unknown().optional(),
|
|
options: z.array(FieldOptionSchema).optional(),
|
|
validation: FieldValidationSchema.optional(),
|
|
order: z.number().int().min(0),
|
|
group: z.string().optional(),
|
|
});
|
|
|
|
export const CreateBlueprintSchema = z.object({
|
|
name: z.string().min(1).max(200),
|
|
target: z.nativeEnum(BlueprintTarget),
|
|
description: z.string().optional(),
|
|
fieldDefs: z.array(BlueprintFieldDefinitionSchema).default([]),
|
|
defaults: z.record(z.string(), z.unknown()).default({}),
|
|
validationRules: z.array(z.object({
|
|
field: z.string(),
|
|
rule: z.enum(["required_if", "unique", "min", "max"]),
|
|
params: z.unknown().optional(),
|
|
message: z.string().optional(),
|
|
})).default([]),
|
|
});
|
|
|
|
export const UpdateBlueprintSchema = CreateBlueprintSchema.partial();
|
|
|
|
export type CreateBlueprintInput = z.infer<typeof CreateBlueprintSchema>;
|
|
export type UpdateBlueprintInput = z.infer<typeof UpdateBlueprintSchema>;
|
|
|
|
/** Generate a Zod schema from blueprint field definitions at runtime */
|
|
export function generateDynamicZodSchema(
|
|
fieldDefs: z.infer<typeof BlueprintFieldDefinitionSchema>[],
|
|
): z.ZodObject<Record<string, z.ZodTypeAny>> {
|
|
const shape: Record<string, z.ZodTypeAny> = {};
|
|
|
|
for (const field of fieldDefs) {
|
|
let fieldSchema: z.ZodTypeAny;
|
|
|
|
switch (field.type) {
|
|
case FieldType.TEXT:
|
|
case FieldType.TEXTAREA:
|
|
case FieldType.URL:
|
|
case FieldType.EMAIL:
|
|
fieldSchema = z.string();
|
|
if (field.validation?.minLength !== undefined) {
|
|
fieldSchema = (fieldSchema as z.ZodString).min(field.validation.minLength);
|
|
}
|
|
if (field.validation?.maxLength !== undefined) {
|
|
fieldSchema = (fieldSchema as z.ZodString).max(field.validation.maxLength);
|
|
}
|
|
if (field.type === FieldType.EMAIL) {
|
|
fieldSchema = (fieldSchema as z.ZodString).email();
|
|
}
|
|
if (field.type === FieldType.URL) {
|
|
fieldSchema = (fieldSchema as z.ZodString).url();
|
|
}
|
|
break;
|
|
case FieldType.NUMBER:
|
|
fieldSchema = z.number();
|
|
if (field.validation?.min !== undefined) {
|
|
fieldSchema = (fieldSchema as z.ZodNumber).min(field.validation.min);
|
|
}
|
|
if (field.validation?.max !== undefined) {
|
|
fieldSchema = (fieldSchema as z.ZodNumber).max(field.validation.max);
|
|
}
|
|
break;
|
|
case FieldType.BOOLEAN:
|
|
fieldSchema = z.boolean();
|
|
break;
|
|
case FieldType.DATE:
|
|
fieldSchema = z.coerce.date();
|
|
break;
|
|
case FieldType.SELECT:
|
|
if (field.options && field.options.length > 0) {
|
|
const values = field.options.map((o) => o.value) as [string, ...string[]];
|
|
fieldSchema = z.enum(values);
|
|
} else {
|
|
fieldSchema = z.string();
|
|
}
|
|
break;
|
|
case FieldType.MULTI_SELECT:
|
|
if (field.options && field.options.length > 0) {
|
|
const values = field.options.map((o) => o.value) as [string, ...string[]];
|
|
fieldSchema = z.array(z.enum(values));
|
|
} else {
|
|
fieldSchema = z.array(z.string());
|
|
}
|
|
break;
|
|
default:
|
|
fieldSchema = z.unknown();
|
|
}
|
|
|
|
if (!field.required) {
|
|
fieldSchema = fieldSchema.optional();
|
|
}
|
|
|
|
shape[field.key] = fieldSchema;
|
|
}
|
|
|
|
return z.object(shape);
|
|
}
|