Initial Commit
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# validate
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module exports `validateSurface` — the canonical entrypoint for
|
||||
parsing and structurally cross-validating a `ControlSurface` document.
|
||||
Every save and load boundary in the wider system calls this function
|
||||
before trusting an unknown payload. The return type is an explicit
|
||||
discriminated union so callers always handle failure without throwing.
|
||||
|
||||
## Features
|
||||
|
||||
- Zod safe-parse against the strict-root `ControlSurfaceSchema`
|
||||
(`version` pinned to `CURRENT_SCHEMA_VERSION`).
|
||||
- Z-axis discipline check: rejects nonzero `transform.position.z` on
|
||||
any descendant of a non-`free3d` layout.
|
||||
- Duplicate-id detection across the entire tree.
|
||||
- Optional binding-reference cross-check when `options.pluginCatalog`
|
||||
is supplied.
|
||||
- Typed `ValidateResult` union — `{ surface }` on success, `{ error }`
|
||||
on any failure, with `SchemaError.code` carrying the precise reason.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
Parse a surface from disk and reject on any failure:
|
||||
|
||||
```ts
|
||||
import { validateSurface } from "@suaveplan/control-surface-schema";
|
||||
|
||||
const raw = JSON.parse(await fs.readFile(path, "utf8"));
|
||||
const result = validateSurface(raw);
|
||||
if (result.error !== undefined) {
|
||||
throw result.error;
|
||||
}
|
||||
const surface = result.surface;
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Cross-check plugin references against a runtime catalog:
|
||||
|
||||
```ts
|
||||
import {
|
||||
CONTROL_SURFACE_SCHEMA_CODES,
|
||||
validateSurface,
|
||||
} from "@suaveplan/control-surface-schema";
|
||||
|
||||
const catalog = new Set(plugins.map((p) => p.id));
|
||||
const result = validateSurface(raw, { pluginCatalog: catalog });
|
||||
if (result.error?.code === CONTROL_SURFACE_SCHEMA_CODES.BINDING_REFERENCE_MISSING) {
|
||||
showMissingPluginToast(result.error.context);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface ValidateOptions` — `{ pluginCatalog?: ReadonlySet<string> }`.
|
||||
- `type ValidateResult` — `{ surface: ControlSurface; error?: undefined }`
|
||||
on success, `{ surface?: undefined; error: SchemaError }` on failure.
|
||||
- `validateSurface(input: unknown, options?: ValidateOptions): ValidateResult`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The function never throws — every failure is captured and re-emitted as
|
||||
a `SchemaError` on `result.error`. The Zod parse runs first because it
|
||||
is the cheapest fail-fast; the structural checks (z-axis discipline,
|
||||
duplicate id, binding reference) only see well-formed documents and can
|
||||
therefore use the typed `ControlSurface` view directly. The pluginCatalog
|
||||
check is opt-in because validating the surface against a catalog at load
|
||||
time is a different concern from validating that the surface itself is
|
||||
internally consistent — server-side persistence often skips the catalog
|
||||
check until the document is mounted in a runtime.
|
||||
Reference in New Issue
Block a user