# control-surface-schema (Zod) _(no submodules)_ ## Purpose This module owns the Zod schemas that mirror every TypeScript type in the package: `TransformSchema`, `BindingSchema`, `RangeSchema`, `AppearanceSchema`, `BehaviourSchema`, `GroupLayoutSchema`, `ControlNodeSchema`, `ControlSurfaceSchema`, and the loose `ControlSurfaceVersionedSchema` used inside the upgrade pipeline. The schemas are the runtime source of truth for `validateSurface` and `upgradeSurface`. ## Features - One schema per exported type — symmetrical with the type module. - Discriminated unions on `kind` (`Binding`, `GroupLayout`, `Action`) and on `type` (`ControlNode`), so parse errors point at the precise variant that failed. - Recursive `ControlNodeSchema` via `z.lazy` so groups can nest to any depth without exploding the schema graph. - MIDI / DMX numeric ranges encoded with `.int().min().max()` matching the hardware envelope; the validator rejects out-of-range channel and CC values at parse time. - Compound bindings use `LeafBindingSchema` (no nested compound) to enforce the flat-only invariant declaratively. - `ControlSurfaceSchema` pins `version` to `CURRENT_SCHEMA_VERSION` so a document at a different version fails parse; `ControlSurfaceVersionedSchema` loosens this to any non-empty string for use inside migrations. ## Usage: Basic Example Parse an unknown document via `safeParse`: ```ts import { ControlSurfaceSchema } from "@suaveplan/control-surface-schema"; const result = ControlSurfaceSchema.safeParse(raw); if (!result.success) { for (const issue of result.error.issues) { console.error(issue.path.join("."), issue.message); } } ``` ## Usage: Advanced Example Compose a sub-schema for an editor that only wants to validate the root group, not the full surface envelope: ```ts import { ControlNodeSchema, GroupLayoutSchema } from "@suaveplan/control-surface-schema"; import z from "zod"; const RootGroupOnlySchema = z.object({ layout: GroupLayoutSchema, children: z.array(ControlNodeSchema), }); const parsed = RootGroupOnlySchema.parse(payload); ``` ## API Reference - `TransformSchema`, `RangeSchema`, `AppearanceSchema`, `BehaviourSchema`, `BindingSchema`, `GroupLayoutSchema`, `ControlKindSchema`, `ControlNodeSchema`, `ControlSurfaceSchema`, `ControlSurfaceVersionedSchema` — Zod schemas mirroring the corresponding types in `src/types/`. ## Implementation Notes Validators downstream call `safeParse`, never `parse`, so a malformed input becomes a typed `SchemaError("INVALID_DOCUMENT")` rather than a raw thrown Zod error. The recursive `ControlNodeSchema` is typed as `z.ZodType` so the `exactOptionalPropertyTypes` mismatch between Zod's `.optional()` (which yields `T | undefined`) and the schema's `T?` fields doesn't force the lazy boundary to a less precise type; the runtime check still rejects malformed nodes, and the consumer casts `parsed.data` to `ControlSurface` after the structural checks (z-axis, duplicate ids, binding refs) confirm the document is valid.