first commit
This commit is contained in:
84
docs/foundation/flow/cbor/src/cddl/validate.md
Normal file
84
docs/foundation/flow/cbor/src/cddl/validate.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# `validate`
|
||||
|
||||
## Purpose
|
||||
|
||||
Walk a decoded CBOR value against a parsed CDDL schema and report the first
|
||||
mismatch (with a JSON-pointer-like path). The validator is the run-time
|
||||
counterpart to the parser — given a typed AST and a value, return
|
||||
`{ valid, errors }` describing whether the value conforms.
|
||||
|
||||
## Features
|
||||
|
||||
- All primitive prelude types: `uint`, `nint`, `int`, `bstr`, `tstr`, `bool`,
|
||||
`nil`, `null`, `undefined`, `any`, `number`, `text`, `bytes`, `float` /
|
||||
`float16` / `float32` / `float64`, `tdate` (RFC 3339 string), `time`
|
||||
(number or bigint epoch).
|
||||
- Literal-value matching: text, byte, integer (signed and unsigned),
|
||||
bool, null, undefined.
|
||||
- Range types: inclusive (`..`) and exclusive (`...`).
|
||||
- Choice: tries each alternative in isolation; first match wins.
|
||||
- Arrays: positional `exact`-occurrence entries, optional, one-or-more,
|
||||
zero-or-more, and `M*N` ranges. Extra entries past the schema are rejected.
|
||||
- Maps: text and integer keys; required (`exact`) and optional (`?`) keys;
|
||||
reports the missing key in error messages.
|
||||
- Tagged items via `#6.<n>(T)` parse syntax → typed `{ kind: "tagged" }` AST.
|
||||
- Named references: walks `name = T` rules transparently; entry rule is
|
||||
selectable via `options.rule`.
|
||||
- Control operators: `.size <int|range>` (string or bytes length),
|
||||
`.regexp "pattern"` (RegExp match on text), `.cbor T` (left side is bytes).
|
||||
Unknown control operators pass through (treated as informational
|
||||
rather than fail-closed).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { validateCbor } from "@suaveplan/cbor/cddl";
|
||||
|
||||
const r = validateCbor({ iss: "alice", exp: 1700000000 }, `
|
||||
cwt = { iss: tstr, ? exp: int }
|
||||
`);
|
||||
console.log(r.valid); // true
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { parseCddl, validateCbor } from "@suaveplan/cbor/cddl";
|
||||
|
||||
const schema = parseCddl(`
|
||||
message = #6.18(cose-sign1)
|
||||
cose-sign1 = [ bstr, { * any => any }, bstr / nil, bstr ]
|
||||
`);
|
||||
|
||||
import { decode } from "@suaveplan/cbor";
|
||||
import { Tagged } from "@suaveplan/cbor/tags";
|
||||
|
||||
const value = decode(wireBytes, { tagHandling: "raw" });
|
||||
const r = validateCbor(value, schema, { rule: "message" });
|
||||
if (!r.valid) {
|
||||
for (const err of r.errors) {
|
||||
console.error(`at ${err.path.join(".")}: expected ${err.expected}, got ${err.got}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `validateCbor(value: unknown, schema: CddlAst | string, options?: { rule?: string }): CddlValidationResult`
|
||||
- `interface CddlValidationResult { valid: boolean; errors: CddlValidationError[] }`
|
||||
- `interface CddlValidationError { path: (string | number)[]; expected: string; got: string }`
|
||||
|
||||
Throws `CborError(CDDL_RULE_NOT_FOUND)` if `options.rule` names a rule
|
||||
that doesn't exist; throws `CborError(CDDL_UNDEFINED_REFERENCE)` if a rule
|
||||
body references an undefined name.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The walk is a recursive AST switch dispatching on `type.kind`. Choice
|
||||
validation runs each alternative against a fresh sub-context — the first
|
||||
alternative whose walk produces zero errors wins. Map validation accepts
|
||||
both `Map` instances and plain objects; the latter is wrapped via
|
||||
`Object.entries` for uniform key lookup. Array entries are matched
|
||||
greedily within their occurrence-range bound — no backtracking — which
|
||||
is correct for the schemas this package targets but a future extension
|
||||
could add limited backtracking for more permissive schemas.
|
||||
Reference in New Issue
Block a user