docs(typedef): add $ref normalization step for TypeBox interop

TypeBox generates bare-name $ref values ("$ref": "Read") within
$defs blocks. The jsonschema crate requires full JSON Pointer paths
("$ref": "#/$defs/Read"). Verified by generating actual TypeBox
output and testing against jsonschema v0.46.5 — bare-name refs fail
with 'Resource is not present in a registry'.

Add a ~20-line normalize_refs() pre-processing step that rewrites
bare-name refs to full JSON Pointer paths at schema load time. The
normalization is idempotent — full paths pass through unchanged.
This commit is contained in:
deepseek-v4-pro committed 2026-07-20 12:27:52 +00:00
1 parent 85c5590001
commit a941d86c3a
1 file changed
+32 -6
@@ -175,8 +175,8 @@ validator implementations.
This is the same pattern as TypeBox's `TypeRegistry.Set` on the JS side. This is the same pattern as TypeBox's `TypeRegistry.Set` on the JS side.
Same semantics, different language, same JSON Schema wire format. A Same semantics, different language, same JSON Schema wire format. A
TypeBox schema serialized to JSON feeds directly into TypeBox schema serialized to JSON feeds into the typedef engine after a
`jsonschema::validator_for(&schema)` on the Rust side — zero translation. single pre-processing step: normalizing `$ref` values (see below).
## TypeBox Interop ## TypeBox Interop
@@ -192,10 +192,36 @@ const TensorRef = Type.Object({
``` ```
serialized to JSON is a standard JSON Schema with `type: "object"`, serialized to JSON is a standard JSON Schema with `type: "object"`,
`properties`, and `required`. That JSON feeds directly into the typedef `properties`, and `required`. That JSON feeds into the typedef engine
engine. The `TypeDef:*` custom keywords are added by TypeBox's after `$ref` normalization. The `TypeDef:*` custom keywords are added by
`TypeRegistry.Set` — they appear in the serialized JSON as additional TypeBox's `TypeRegistry.Set` — they appear in the serialized JSON as
properties on the schema object. additional properties on the schema object.
### `$ref` normalization
TypeBox generates bare-name `$ref` values (e.g., `"$ref": "Read"`),
referencing sibling definitions within the same `$defs` block. The
`jsonschema` crate requires full JSON Pointer paths (e.g.,
`"$ref": "#/$defs/Read"`). The typedef engine normalizes TypeBox-style
refs at schema load time:
```rust
fn normalize_refs(schema: &mut Value) {
// Walk the schema tree. For every "$ref" whose value is a bare name
// (no "#" prefix), rewrite it to "#/$defs/<name>".
// "$ref": "Read" → "$ref": "#/$defs/Read"
}
```
This is a ~20-line recursive walk of the schema JSON. It runs once at
load time, before the schema is passed to `jsonschema::validator_for`
or the offset computation. The normalization is idempotent — full JSON
Pointer refs pass through unchanged.
**Verification:** The jsonschema crate (v0.46.5) rejects bare-name refs
with `Resource 'Read' is not present in a registry`. Full JSON Pointer
refs (`#/$defs/Read`) resolve correctly. The normalization step bridges
the gap between TypeBox's output and jsonschema's input.
The typedef engine does not depend on TypeBox or any JS toolchain. It The typedef engine does not depend on TypeBox or any JS toolchain. It
consumes JSON — whether that JSON was authored in TypeBox, generated by consumes JSON — whether that JSON was authored in TypeBox, generated by