Skip to content

Schema Introspection and Round-Trips

Problem

Tooling layers need a serializable schema format. Runtime schema instances cannot cross process boundaries or live in static metadata directly.

Solution

Use definition() as canonical portable shape, then export it through fromDefinition() from @vielzeug/spell/json.

ts
import { s } from '@vielzeug/spell';
import { fromDefinition } from '@vielzeug/spell/json';

const Invoice = s
  .object({
    id: s.string().uuid(),
    notes: s.string().max(500).optional().nullable(),
    total: s.number().positive().multipleOf(0.01),
  })
  .label('Invoice');

const definition = Invoice.definition();
const jsonSchema = fromDefinition(definition);

console.log(jsonSchema.title); // 'Invoice'
console.log(jsonSchema.properties?.id);

Pitfalls

  • Definitions preserve declarative structure such as descriptions, optionality, nullability, and built-in annotations.
  • Use fromDefinition() for external consumers. Do not hand Spell definitions to tools that expect JSON Schema.
  • Runtime checks, transforms, defaults, catches, and preprocessors intentionally have no definition.