dilswer
Version:
Blazingly fast data validation library with TypeScript integration.
415 lines (413 loc) • 13.3 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/json-schema-parser/to-json-schema.ts
var to_json_schema_exports = {};
__export(to_json_schema_exports, {
toJsonSchema: () => toJsonSchema
});
module.exports = __toCommonJS(to_json_schema_exports);
var import_base_type = require("../data-types/base-type.cjs");
var import_Type = require("../data-types/Type.cjs");
var import_type_kind_names = require("../data-types/type-kind-names.cjs");
var import_is_defined = require("./is-defined.cjs");
var import_name_generator = require("../ts-type-generator/name-generator.cjs");
var DataTypeJsonSchemaGenerator = class {
constructor(options) {
this.options = options;
__publicField(this, "incompatibleTypes");
__publicField(this, "customParser");
__publicField(this, "definitionNames", /* @__PURE__ */ new Map());
__publicField(this, "definitions", /* @__PURE__ */ new Map());
var _a, _b;
this.incompatibleTypes = (_a = options.incompatibleTypes) != null ? _a : "throw";
this.customParser = (_b = options.customParser) != null ? _b : {};
}
throwIncompatibleTypeError(typeName) {
throw new Error(
`Cannot parse type "${typeName}" to JSON Schema. Incompatible types.`
);
}
assignMetadataToSchema(schema, type) {
const meta = import_base_type.BaseType.getOriginalMetadata(type);
if (meta.title) schema.title = meta.title;
if (meta.description) schema.description = meta.description;
if (meta.format) schema.format = meta.format;
}
parsePrimitive(type) {
var _a, _b, _c;
let schema = {};
this.assignMetadataToSchema(schema, type);
switch (type.simpleType) {
case "boolean":
schema.type = "boolean";
break;
case "integer":
schema.type = "integer";
if (type.options.max != null) {
schema.maximum = type.options.max;
}
if (type.options.min != null) {
schema.minimum = type.options.min;
}
break;
case "null":
schema.type = "null";
break;
case "number":
schema.type = "number";
if (type.options.max != null) {
schema.maximum = type.options.max;
}
if (type.options.min != null) {
schema.minimum = type.options.min;
}
break;
case "string":
schema.type = "string";
if (type.options.max != null) {
schema.maxLength = type.options.max;
}
if (type.options.min != null) {
schema.minLength = type.options.min;
}
break;
case "stringinteger":
schema.type = "string";
schema.pattern = "^-?[0-9]+$";
break;
case "stringnumeral":
schema.type = "string";
schema.pattern = "^-?[0-9]+(\\.[0-9]+)?$";
break;
case "unknown":
break;
case "function":
if (this.customParser.Function) {
schema = this.customParser.Function(type, this.options);
break;
}
if (this.incompatibleTypes === "throw") {
this.throwIncompatibleTypeError(type.simpleType);
}
if (this.incompatibleTypes === "omit") {
schema = void 0;
break;
}
(_a = schema.title) != null ? _a : schema.title = "Function";
break;
case "symbol":
if (this.customParser.Symbol) {
schema = this.customParser.Symbol(type, this.options);
break;
}
if (this.incompatibleTypes === "throw") {
this.throwIncompatibleTypeError(type.simpleType);
}
if (this.incompatibleTypes === "omit") {
schema = void 0;
break;
}
(_b = schema.title) != null ? _b : schema.title = "Symbol";
break;
case "undefined":
if (this.customParser.Undefined) {
schema = this.customParser.Undefined(type, this.options);
break;
}
if (this.incompatibleTypes === "throw") {
this.throwIncompatibleTypeError(type.simpleType);
}
if (this.incompatibleTypes === "omit") {
schema = void 0;
break;
}
(_c = schema.title) != null ? _c : schema.title = "undefined";
break;
}
return schema;
}
parseArrayOf(type, children) {
const schema = {
type: "array",
items: children == null ? void 0 : children.filter(import_is_defined.isDefined)
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseTuple(type, children) {
var _a;
const items = (_a = children == null ? void 0 : children.map((c) => c != null ? c : {})) != null ? _a : [];
const schema = {
type: "array",
items,
required: items.map((_, i) => i.toString()),
minItems: items.length,
maxItems: items.length
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseRecordOf(type, children = []) {
const schema = {
type: "object",
properties: {},
required: []
};
this.assignMetadataToSchema(schema, type);
for (let i = 0; i < children.length; i++) {
const { child, propertyName, required } = children[i];
if (child == null) continue;
Object.assign(schema.properties, { [propertyName]: child });
if (required) {
schema.required.push(propertyName);
}
}
if (typeof this.options.additionalProperties === "boolean") {
schema.additionalProperties = this.options.additionalProperties;
}
return schema;
}
parseDict(type, children) {
const schema = {
type: "object",
additionalProperties: {
anyOf: children == null ? void 0 : children.filter(import_is_defined.isDefined)
}
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseSetOf(type, children = []) {
if (this.customParser.Set) {
return this.customParser.Set(
children.filter(import_is_defined.isDefined),
type,
this.options
);
}
if (this.incompatibleTypes === "throw") {
this.throwIncompatibleTypeError("Set");
}
if (this.incompatibleTypes === "omit") return void 0;
const schema = {
title: "Set"
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseOneOf(type, children) {
const schema = {
anyOf: children.filter(import_is_defined.isDefined)
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseAllOf(type, children) {
const schema = {
allOf: children.filter(import_is_defined.isDefined)
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseLiteral(type) {
const schema = {};
this.assignMetadataToSchema(schema, type);
if (typeof type.literal === "string") {
schema.type = "string";
schema.enum = [type.literal];
return schema;
}
if (typeof type.literal === "number") {
schema.type = "number";
schema.enum = [type.literal];
return schema;
}
if (typeof type.literal === "boolean") {
schema.type = "boolean";
schema.enum = [type.literal];
return schema;
}
throw new Error(
"Invalid literal type, literals can be only of string, number or boolean type."
);
}
parseEnum(type) {
const members = Object.entries(type.enumInstance);
const schema = {
anyOf: []
};
for (const [key, member] of members) {
const subSchema = this.parseEnumMember(
import_Type.Type.EnumMember(member)
);
subSchema.title = key;
schema.anyOf.push(subSchema);
}
this.assignMetadataToSchema(schema, type);
return schema;
}
parseEnumMember(type) {
const schema = {};
this.assignMetadataToSchema(schema, type);
if (typeof type.enumMember === "string") {
schema.type = "string";
schema.enum = [type.enumMember];
return schema;
}
if (typeof type.enumMember === "number") {
schema.type = "number";
schema.enum = [type.enumMember];
return schema;
}
throw new Error(
"Invalid enum member type, enum members can be only of string or number type."
);
}
parseInstanceOf(type) {
if (this.customParser.InstanceOf) {
return this.customParser.InstanceOf(type, this.options);
}
const constructor = type.instanceOf;
const schema = {
type: "object",
title: constructor.name
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseCustom(type) {
if (this.customParser.Custom) {
return this.customParser.Custom(type.custom, type, this.options);
}
if (this.incompatibleTypes === "throw") {
this.throwIncompatibleTypeError("Custom Validator");
}
if (this.incompatibleTypes === "omit") return void 0;
const schema = {
title: `Custom Validator (${type.custom.name || "anonymous"})`
};
this.assignMetadataToSchema(schema, type);
return schema;
}
parseStringMatching(type) {
const schema = {
type: "string",
pattern: type.pattern.source
};
if (type.pattern.flags) {
Object.assign(schema, { patternFlags: type.pattern.flags });
}
this.assignMetadataToSchema(schema, type);
return schema;
}
parseCircular(circular, children) {
const [childSchema] = children;
const child = circular.type;
if (this.definitionNames.has(child)) {
const definitionName = this.definitionNames.get(child);
this.definitions.set(definitionName, childSchema != null ? childSchema : { type: "null" });
return {
allOf: [
{
$ref: `#/definitions/${definitionName}`
}
]
};
}
return childSchema;
}
parseCircularRef(type) {
var _a;
const referencedType = type._getReferencedType();
if (this.definitionNames.has(referencedType)) {
return {
$ref: `#/definitions/${this.definitionNames.get(referencedType)}`
};
}
const metadata = import_base_type.BaseType.getOriginalMetadata(referencedType);
const title = metadata.title;
if (title) {
const typeName2 = import_name_generator.NameGenerator.generate(title);
this.definitionNames.set(referencedType, typeName2);
return {
$ref: `#/definitions/${typeName2}`
};
}
const typeName = import_name_generator.NameGenerator.generate(
(_a = import_type_kind_names.TypeKindNames.get(referencedType.kind)) != null ? _a : "Circular"
);
this.definitionNames.set(referencedType, typeName);
return {
$ref: `#/definitions/${typeName}`
};
}
visit(type, children) {
switch (type.kind) {
case "simple":
return this.parsePrimitive(type);
case "array":
return this.parseArrayOf(type, children);
case "tuple":
return this.parseTuple(type, children);
case "record":
return this.parseRecordOf(type, children);
case "dictionary":
return this.parseDict(type, children);
case "set":
return this.parseSetOf(type, children);
case "union":
return this.parseOneOf(type, children);
case "intersection":
return this.parseAllOf(type, children);
case "literal":
return this.parseLiteral(type);
case "enumUnion":
return this.parseEnum(type);
case "enumMember":
return this.parseEnumMember(type);
case "instanceOf":
return this.parseInstanceOf(type);
case "custom":
return this.parseCustom(type);
case "stringMatching":
return this.parseStringMatching(type);
case "circular":
return this.parseCircular(type, children);
case "circularRef":
return this.parseCircularRef(type);
}
}
};
var toJsonSchema = (type, options = {}, include$schemaProperty = true) => {
try {
const visitor = new DataTypeJsonSchemaGenerator(options);
const schema = type._acceptVisitor(visitor);
if (schema && visitor.definitions.size > 0) {
schema.definitions = Object.fromEntries(visitor.definitions.entries());
}
if (include$schemaProperty && schema) {
schema.$schema = "http://json-schema.org/draft-06/schema#";
}
return schema;
} finally {
import_name_generator.NameGenerator.clear();
}
};