# @suaveplan/control-surface-schema - Universal canonical schema for DAW-style control surfaces: ControlSurface root, ControlNode (Group | Control) discriminated union, 18 ControlKind variants, 6 Binding variants, 4 GroupLayout variants, Zod validators, forward migrations, DFS traversal, duplicate-id and binding-reference cross-check. Zero runtime deps beyond error, types, tslib, zod. Universal canonical schema for DAW-style control surfaces. Owns the `ControlSurface` root document, the `ControlNode = Group | Control` discriminated tree, the 18 `ControlKind` variants, the 6 `Binding` variants, the 4 `GroupLayout` variants, and the Zod schemas / validators / forward migrations every renderer reads. Zero runtime dependencies beyond `@suaveplan/error`, `@suaveplan/types`, `tslib`, and `zod`. ## Documentation tree - [constants](./src/constants/constants.md) — The constants module owns the single source of truth for the current schema version of `@suaveplan/control-surface-sche… - [constants](./src/constants/constants.md) — The constants module owns the single source of truth for the current schema version of `@suaveplan/control-surface-sche… - [error-codes](./src/error-codes/error-codes.md) — This module owns the stable error-code registry for `@suaveplan/control-surface-schema`. Every entrypoint of the packag… - [error-codes](./src/error-codes/error-codes.md) — This module owns the stable error-code registry for `@suaveplan/control-surface-schema`. Every entrypoint of the packag… - [identity](./src/identity/identity.md) — The identity module owns the structural cross-checks that run after a candidate surface passes its Zod parse: duplicate… - [identity](./src/identity/identity.md) — The identity module owns the structural cross-checks that run after a candidate surface passes its Zod parse: duplicate… - [schemas](./src/schemas/control-surface-schema.md) — This module owns the Zod schemas that mirror every TypeScript type in the package: `TransformSchema`, `BindingSchema`,… - [control-surface-schema](./src/schemas/control-surface-schema.md) — This module owns the Zod schemas that mirror every TypeScript type in the package: `TransformSchema`, `BindingSchema`,… - [upgrade](./src/upgrade/upgrade.md) — This module owns `upgradeSurface` and the `MigrationRegistry` chain that walks an older schema-version document forward… - [upgrade](./src/upgrade/upgrade.md) — This module owns `upgradeSurface` and the `MigrationRegistry` chain that walks an older schema-version document forward… - [validate](./src/validate/validate.md) — This module exports `validateSurface` — the canonical entrypoint for parsing and structurally cross-validating a `Contr… - [validate](./src/validate/validate.md) — This module exports `validateSurface` — the canonical entrypoint for parsing and structurally cross-validating a `Contr… - [walk](./src/walk/walk.md) — This module exports `walkSurface` — the canonical depth-first traversal helper for `ControlSurface` documents. Every cr… - [walk](./src/walk/walk.md) — This module exports `walkSurface` — the canonical depth-first traversal helper for `ControlSurface` documents. Every cr… ## Install ```bash bun add @suaveplan/control-surface-schema ``` ## Quick start ```ts import { CURRENT_SCHEMA_VERSION, validateSurface, walkSurface, } from "@suaveplan/control-surface-schema"; const result = validateSurface({ id: "surface-1", name: "Empty", version: CURRENT_SCHEMA_VERSION, createdAt: Date.now(), updatedAt: Date.now(), skin: { id: "default" }, root: { type: "group", id: "root", layout: { kind: "absolute" }, transform: { position: { x: 0, y: 0 } }, children: [], }, }); if (result.error !== undefined) throw result.error; walkSurface(result.surface, (node) => console.log(node.id)); ``` ## Surface document shape ```text ControlSurface ├── id, name, version, createdAt, updatedAt ├── skin: { id; vars? } └── root: ControlNode ├── Group { type; id; layout; transform; children[]; label?; collapsible? } └── Control { type; id; kind; transform; label?; appearance?; binding?; range?; behaviour? } ``` The 18 `ControlKind` literals: `knob`, `slider-v`, `slider-h`, `button-momentary`, `button-latching`, `switch-2`, `switch-3`, `led`, `digital-display`, `meter-vu`, `meter-peak`, `meter-spectral`, `xy-pad`, `jog-wheel`, `piano-key`, `drum-pad`, `text-label`, `image`. The 6 `Binding` variants: `pluginParam`, `transport`, `midiOut`, `midiIn`, `dmx`, `compound` (flat-only). The 4 `GroupLayout` variants: `grid`, `absolute`, `stack`, `free3d` — only `free3d` admits z-axis transforms. ## Worked examples ### 4-pad drum machine (grid layout, drum-pad kind) ```ts const drumMachine = { id: "drum-4", name: "4-pad", version: CURRENT_SCHEMA_VERSION, createdAt: 0, updatedAt: 0, skin: { id: "default" }, root: { type: "group", id: "root", layout: { kind: "grid", columns: 4, rows: 1, gap: 8 }, transform: { position: { x: 0, y: 0 } }, children: Array.from({ length: 4 }, (_, i) => ({ type: "control", id: `pad-${i}`, kind: "drum-pad", transform: { position: { x: 0, y: 0 } }, binding: { kind: "midiOut", portId: "out", channel: 9, cc: 60 + i }, })), }, }; ``` ### 8-channel mixer (stack-row of stack-columns) ```ts const mixer = { /* … */ root: { type: "group", id: "mixer", layout: { kind: "stack", direction: "row", gap: 4 }, transform: { position: { x: 0, y: 0 } }, children: Array.from({ length: 8 }, (_, ch) => ({ type: "group", id: `ch-${ch}`, layout: { kind: "stack", direction: "column", gap: 2 }, transform: { position: { x: 0, y: 0 } }, children: [ { type: "control", id: `ch-${ch}-vol`, kind: "slider-v", transform: { position: { x: 0, y: 0 } }, binding: { kind: "pluginParam", pluginId: "mixer", paramPath: `channels/${ch}/volume`, curve: "log", }, }, ], })), }, }; ``` ### Piano keyboard (absolute layout, piano-key kind) ```ts const piano = { /* … */ root: { type: "group", id: "keyboard", layout: { kind: "absolute" }, transform: { position: { x: 0, y: 0 } }, children: Array.from({ length: 88 }, (_, key) => ({ type: "control", id: `key-${21 + key}`, kind: "piano-key", transform: { position: { x: key * 22, y: 0 } }, binding: { kind: "midiOut", portId: "out", channel: 0, cc: 21 + key }, })), }, }; ``` ### Traktor-style DJ surface (3D free layout) ```ts const traktor = { /* … */ root: { type: "group", id: "deck", layout: { kind: "free3d" }, transform: { position: { x: 0, y: 0 } }, children: [ { type: "control", id: "jog", kind: "jog-wheel", transform: { position: { x: 0, y: 0, z: 0.1 } }, binding: { kind: "midiOut", portId: "deck-a", channel: 0, cc: 32 }, }, ], }, }; ``` ## Modules - [Constants](./src/constants.md) — `CURRENT_SCHEMA_VERSION`. - [Error codes](./src/error-codes.md) — `CONTROL_SURFACE_SCHEMA_CODES`, `SchemaError`. - [Transform types](./src/types/transform.md) - [Binding types](./src/types/binding.md) - [Range types](./src/types/range.md) - [Action types](./src/types/action.md) - [Behaviour types](./src/types/behaviour.md) - [Appearance types](./src/types/appearance.md) - [Control-kind types](./src/types/control-kind.md) - [Group-layout types](./src/types/group-layout.md) - [Control-node types](./src/types/control-node.md) - [Control-surface types](./src/types/control-surface.md) - [Validate](./src/validate/validate.md) - [Upgrade](./src/upgrade/upgrade.md) - [Walk](./src/walk/walk.md) - [Identity](./src/identity/identity.md)