# `validate`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `configurator-runtime.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Function · `import { validate } from '@holostep-configurators/runtime-client';` · stable · since 2.0.0 · section 22. Pure helpers (root of the package; run in Node and the fake)

Validates a value against the package's JSON Schemas. Sync, pure; never throws. 'document'
reports a version-1 key with `didYouMean` naming its 2.0 home.
'collection', and each collection a 'scene' lists, also reports what [`Collection.set`](Collection.set.md) and
[`apply`](Writer.apply.md) check across items, at the paths their HoloInputError names: a repeated item key, a
parent that names no other item, and a parent cycle. The value ranges only the call checks (a
size or radius below 0, a cylinder's `segments`, an outline's first entry or arc, a light's
angles, a mesh's index range) it leaves to the call: `set(spec, { dryRun: true })` or
[`apply(scene, { dryRun: true })`](Writer.apply.md) checks a draft against all of them and changes nothing.
'material' (and a material inside 'collection' or 'scene') checks each field's type and that a
colour string starts with '#'; the hex length ('#rgb' or '#rrggbb') and the 0..1 ranges
(`metallic`, `roughness`, `opacity`, …) are the call's checks, as the ranges above: so
`{ baseColor: '#12' }` and `{ metallic: 2 }` pass here and reject at `part.set`, [`apply`](Writer.apply.md) and
[`materials.create`](Materials.create.md). A value that matches no arm of a union (an item's `shape`, a `material` in
either form) is reported at the union's path as 'one of N alternatives', naming no field: to
find the field, validate that value on its own kind (`validate('material', item.material)`) or
dry-run the call, whose HoloInputError names it.
'configurator' checks what loads and what can be stored, not every field this file types as
required: it requires `root` and each node's `type` and unique `nodeId`, and leaves optional
what stored documents lack and [`setSceneModel`](Writer.setSceneModel.md) tolerates (a transform or part of one, `glbUrl`,
`prices`, `enabled`, `defaultView`, `defaultCurrency`; undeclared fields and `attributes` values
of any JSON type). So a stored document that loads passes unless it holds `space: 'parent'`
('space-not-storable') or a value the stored format can't hold ('not-storable'); a stored zero
scale passes. A ModelNode with no `enabled` passes too, and is not drawn
([`IConfiguratorNode.enabled`](IConfiguratorNode.enabled.md)): set `enabled: true` on a node you build.
Errors: none: problems are in `errors`.

## Declaration

```ts
export declare function validate(kind: ValidationKind, value: unknown): ValidationResult;
```

## Example

Imports: values (`has`, `validate`, the `Holo*Error` classes, the pure helpers) from `@holostep-configurators/runtime-client`, types from `@holostep-configurators/runtime-client/contract`, `/math` and `/testing` names from those entries; `boot` from the runtime module the root's `loadRuntimeAtFloor` resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (`examples/declaration/22-validate.ts`):

```ts
import { validate } from '@holostep-configurators/runtime-client';
import type { SceneSpec, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const draftJson: string;                           // e.g. an assistant's proposed look
const result = validate('scene', JSON.parse(draftJson));
if (result.ok) await session.apply(JSON.parse(draftJson) as SceneSpec);
else {
  for (const e of result.errors) {
    console.warn(`${e.path}: expected ${e.expected}, got ${e.received}`, e.didYouMean ?? []);
  }
}
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `validate` names no product |
| 2. A precedent | not recorded: no ledger row (coverage.json) names this member or its owner |
| 3. Two apps, or labs | not recorded: no ledger row names this member or its owner |
| 4. The cheaper fix first | not recorded per member (Appendix B decides it per need) |
