Skip to content

Schema Derivation

JsonSchema.output derives a JSON Schema from a Spec or Plan without executing it. The schema reflects the output structure, including types, required fields, and nested objects.

import { JsonSchema } from '@origints/core'
// From a Plan
const schema = JsonSchema.output(plan)
// { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } }, required: ['name', 'age'] }
import { extract } from '@origints/core'
// From a Spec
const spec = extract($ => ({
name: $.get('name').string(),
age: $.get('age').number(),
}))
const schema = JsonSchema.output(spec)
JsonSchema.output(plan, {
draft: 'draft-07',
id: 'https://example.com/user.schema.json',
title: 'User',
description: 'A user record',
deduplicate: true,
})
OptionEffect
draftJSON Schema dialect: 'draft-07', '2019-09', or '2020-12' (default). Draft-07 uses definitions; 2019-09 and 2020-12 use $defs.
idSets $id on root schema
titleSets title on root schema
descriptionSets description on root schema
deduplicateExtracts repeated sub-schemas into $defs/definitions with $ref pointers

describe(path, meta) attaches annotations to an extraction. These appear in the derived JSON Schema.

.emit((out, $) => out
.add('email', $.get('email').string())
.describe('email', { description: 'User email', format: 'email' })
.add('age', $.get('age').number())
.describe('age', { minimum: 0, maximum: 150 })
)

Supported OutputMeta fields: description, title, examples, deprecated, format, pattern, minimum, maximum, minLength, maxLength, enum.

When deduplicate: true, repeated sub-schemas are extracted into $defs (or definitions for Draft-07) and replaced with $ref pointers.

Naming strategy:

  1. Property key — the parent property name (e.g., property home → def Home)
  2. Singularized array items — array item schemas use a singularized form (e.g., addressesAddress)
  3. Property-set fallback — object schemas with ≤4 properties use PascalCase property names joined (e.g., NameAge)
  4. Counter fallbackSchema1, Schema2, etc.

Manual override via $defName in OutputMeta:

.describe('addresses', { $defName: 'PostalAddress' })
SpecSchema
extract (string/number/boolean/null/date/datetime){ type: T } with optional default
literal{ const: value }
array{ type: 'array', items: ... }
object{ type: 'object', properties: ..., required: [...] }
matchUnion of branch schemas via anyOf or type merge
concat{ type: 'array', items: merged }
tryUnion of all fallback schemas
mapSchema of the source spec
guardSchema of the source spec
panic{} (never produces output)