Оглавление документации
English version
@modulify/validator/json-schema предоставляет тонкий слой экспорта в JSON Schema поверх публичного descriptor contract.
Это именно derivation layer, а не вторая schema model. Exporter читает публичные descriptors и строит по ним представление в виде JSON Schema.
import {
isNumber,
isString,
meta,
optional,
shape,
} from '@modulify/validator'
import { toJsonSchema } from '@modulify/validator/json-schema'
const profile = meta(shape({
email: meta(isString, {
title: 'Email',
format: 'email',
}),
age: optional(isNumber),
}).strict(), {
title: 'Profile',
})
const jsonSchema = toJsonSchema(profile)Поток экспорта специально устроен просто:
- validators публикуют публичные descriptors через
describe(...); toJsonSchema(...)строит JSON Schema поверх этих descriptors;- unsupported или opaque nodes либо пропускаются в best-effort режиме, либо приводят к ошибке в strict режиме.
Это разделение важно, потому что:
- runtime validation остаётся runtime-first;
- поведение exporter-а остаётся явным;
- custom tooling может опираться на один публичный introspection contract.
JSON Schema export импортируется из отдельного subpath:
import {
JsonSchemaExportError,
toJsonSchema,
} from '@modulify/validator/json-schema'Root package намеренно не делает реэкспорт этого API.
Exporter покрывает built-in descriptor set, который уже участвует в публичной introspection.
Практические mappings включают:
isString->type: 'string'isNumber->type: 'number'isBoolean->type: 'boolean'isNull->type: 'null'isEmail->type: 'string'иformat: 'email'exact(...)->constoneOf(...)->enumhasLength(...)-> ограничения длины для строк и массивов
Поддерживаются wrappers:
optional(...)nullable(...)nullish(...)
optional(...) влияет на вычисление object required. nullable(...) и nullish(...) экспортируются как union с null.
Поддерживаются structural mappings:
shape(...)each(...)tuple(...)record(...)
Поддерживаются branching mappings:
union(...)discriminatedUnion(...)
Массивы constraints в одном slot экспортируются как JSON Schema allOf.
shape(...) экспортируется как JSON Schema object с:
type: 'object'propertiesrequiredadditionalProperties
Поведение unknown keys отображается так:
.strict()->additionalProperties: false- default или
.passthrough()->additionalProperties: true
Одна из важных границ касается наличия объектных полей.
Runtime layer во многих практических сценариях рассматривает как один и тот же случай:
- отсутствие ключа;
- и ключ со значением
undefined;
особенно вокруг optional(...) и derived helpers вроде partial().
JSON Schema моделирует это не так. Поэтому exporter аппроксимирует наличие полей через JSON Schema required, а не обещает точную semantic parity.
Это сделано намеренно: экспорт нужно воспринимать как interoperability layer, а не как обещание полной эквивалентности runtime semantics и JSON Schema.
Exporter не копирует всю metadata в JSON Schema автоматически.
Вместо этого используется небольшой явный whitelist:
titledescriptionformatdefaultexamplesdeprecatedreadOnlywriteOnly
Остальная metadata остаётся библиотечно-специфичной и автоматически в экспорт не попадает.
Так metadata layer остаётся явным и не превращается в implicit magic.
Best-effort режим используется по умолчанию:
const schema = toJsonSchema(profile)В этом режиме:
- unsupported или opaque nodes превращаются в permissive
{}schema nodes; - unsupported object-level shape rules отбрасываются;
- отброшенные shape rules по возможности помечаются через
$comment.
Этот режим удобен, когда нужен максимально широкий внешний schema export, даже если часть runtime semantics нельзя выразить честно.
Strict режим отклоняет unsupported nodes:
const schema = toJsonSchema(profile, { mode: 'strict' })Если export нельзя представить faithfully, exporter выбрасывает JsonSchemaExportError.
Эта ошибка содержит:
- descriptor, на котором произошёл сбой;
- machine-readable причину;
- путь внутри descriptor tree, где возникла ошибка.
Strict режим полезен, когда silent fallback был бы вводящим в заблуждение.
Не всё runtime behavior имеет faithful representation в JSON Schema.
Важные примеры:
- custom validators без поддерживаемого публичного descriptor;
- неизвестные custom descriptor kinds;
- object-level
refine(...)rules; - descriptor nodes, чья семантика зависит от runtime-only behavior;
- значения, которые нельзя практично представить как JSON Schema constants или enums.
Exporter фиксирует эти границы явно и не пытается гадать.
toJsonSchema(...) находится downstream от describe(...).
Это значит:
- JSON Schema export должен опираться на публичные descriptors;
- логика exporter-а не должна зависеть от приватных runtime internals;
- custom validators участвуют в export сначала через публичный descriptor contract.
Так архитектура остаётся тонкой и стабильной для внешнего tooling.
const profile = meta(shape({
email: meta(isString, {
title: 'Email',
format: 'email',
}),
age: optional(isNumber),
}).strict(), {
title: 'Profile',
})
const schema = toJsonSchema(profile)Практически это означает:
- metadata shape может стать schema metadata;
- metadata полей может стать metadata свойств;
- strict object behavior становится
additionalProperties: false.
const schema = shape({
publishedAt: custom({
check(value: unknown): value is string {
return typeof value === 'string'
},
run() {
return []
},
}),
})
toJsonSchema(schema, { mode: 'strict' })Здесь будет исключение, потому что custom validator остаётся opaque для exporter-а.
- воспринимайте JSON Schema export как interoperability layer;
- используйте best-effort режим, когда partial export допустим;
- используйте strict режим, когда unsupported semantics должны приводить к явной ошибке;
- если custom descriptors должны участвовать в export, держите их компактными и публичными;
- не считайте JSON Schema export заменой runtime validation semantics.