Skip to content
This repository was archived by the owner on Oct 16, 2026. It is now read-only.
This repository was archived by the owner on Oct 16, 2026. It is now read-only.

refactor: unify targets validation into Zod schema (single source of truth) #909

Closed as not planned

Description

@christso

Problem

Target validation is split across two systems that must stay in sync:

  1. Zod schema (BASE_TARGET_SCHEMA in targets.ts) — parses and types target definitions
  2. 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
  • TargetDefinition type 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions