-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: unify targets validation into Zod schema (single source of truth) #909
Copy link
Copy link
Open
Labels
Description
Problem
Target validation is split across two systems that must stay in sync:
- Zod schema (
BASE_TARGET_SCHEMAintargets.ts) — parses and types target definitions - Manual validator (
targets-validator.ts) — pre-resolution checks (env var syntax, unknown fields, provider validation)
Adding use_target required changes in 3 places: TargetDefinition interface, Zod schema, and the manual validator. This is brittle — each new field risks the two systems diverging.
Proposal
Make the Zod schema the single source of truth. Move validator logic into Zod .refine() and .superRefine():
const TargetSchema = z.object({
name: z.string().min(1),
provider: z.string().optional(),
use_target: z.string().optional(),
grader_target: z.string().optional(),
// ...
}).superRefine((data, ctx) => {
// Provider required unless use_target is set
if (!data.use_target && !data.provider) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Either 'provider' or 'use_target' is required",
path: ['provider'],
});
}
// Env var syntax validation
if (data.provider && /^\$\{\{/.test(data.provider)) {
// validate env var format
}
});Benefits
- One schema defines structure, types, AND validation rules
TargetDefinitiontype is inferred from schema (z.infer<typeof TargetSchema>)- Adding a new field = one change in one place
- Validation errors include Zod's structured path information
Prior art
- Vitest: single Zod schema for config validation + typing
- tRPC: Zod schemas as single source of truth for input validation
- Astro: Zod content collections schema (validates + types in one pass)
Reactions are currently unavailable