import { z } from "zod"; import type { ScriptConfig, VariableConfig } from "../config/schema"; function fieldSchema(variable: VariableConfig): z.ZodTypeAny { let field: z.ZodTypeAny; switch (variable.type) { case "string": { let s = z.string(); // `required` alone has to reject "" even when the author didn't set an explicit // minLength - otherwise an empty string satisfies a bare z.string() and the run starts // with a "required" field effectively unset. const minLength = variable.minLength ?? (variable.required ? 1 : undefined); if (minLength !== undefined) s = s.min(minLength); if (variable.maxLength !== undefined) s = s.max(variable.maxLength); if (variable.pattern) s = s.regex(new RegExp(variable.pattern)); field = s; break; } case "number": { let n = z.number(); if (variable.min !== undefined) n = n.min(variable.min); if (variable.max !== undefined) n = n.max(variable.max); field = n; break; } case "boolean": field = z.boolean(); break; case "enum": field = z.enum(variable.choices as [string, ...string[]]); break; case "multiselect": { let arr = z.array(z.enum(variable.choices as [string, ...string[]])); if (variable.required) arr = arr.min(1); field = arr; break; } } if (!variable.required) { field = field.optional().or(z.literal("")); } return field; } /** Builds one Zod object schema from a list of variable definitions - the single source of truth * imported by both the client form resolver and the server-side run-creation handler. */ export function buildVariableSchemaFromList( variables: readonly VariableConfig[], ) { const shape: Record = {}; for (const variable of variables) { shape[variable.name] = fieldSchema(variable); } return z.object(shape); } export function buildVariableSchema(script: ScriptConfig) { return buildVariableSchemaFromList(script.variables); } export type VariableValues = Record;