@sinclair/typebox
Version:
Json Schema Type Builder with Static Type Resolution for TypeScript
464 lines (462 loc) • 17.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValueCheckUnknownTypeError = void 0;
exports.Check = Check;
const index_1 = require("../../system/index");
const index_2 = require("../deref/index");
const index_3 = require("../hash/index");
const index_4 = require("../../type/symbols/index");
const index_5 = require("../../type/keyof/index");
const index_6 = require("../../type/extends/index");
const index_7 = require("../../type/registry/index");
const index_8 = require("../../type/error/index");
const index_9 = require("../../type/never/index");
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
const index_10 = require("../guard/index");
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
const type_1 = require("../../type/guard/type");
// ------------------------------------------------------------------
// Errors
// ------------------------------------------------------------------
class ValueCheckUnknownTypeError extends index_8.TypeBoxError {
constructor(schema) {
super(`Unknown type`);
this.schema = schema;
}
}
exports.ValueCheckUnknownTypeError = ValueCheckUnknownTypeError;
// ------------------------------------------------------------------
// TypeGuards
// ------------------------------------------------------------------
function IsAnyOrUnknown(schema) {
return schema[index_4.Kind] === 'Any' || schema[index_4.Kind] === 'Unknown';
}
// ------------------------------------------------------------------
// Guards
// ------------------------------------------------------------------
function IsDefined(value) {
return value !== undefined;
}
// ------------------------------------------------------------------
// Types
// ------------------------------------------------------------------
function FromAny(schema, references, value) {
return true;
}
function FromArray(schema, references, value) {
if (!(0, index_10.IsArray)(value))
return false;
if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) {
return false;
}
if (IsDefined(schema.maxItems) && !(value.length <= schema.maxItems)) {
return false;
}
if (!value.every((value) => Visit(schema.items, references, value))) {
return false;
}
// prettier-ignore
if (schema.uniqueItems === true && !((function () { const set = new Set(); for (const element of value) {
const hashed = (0, index_3.Hash)(element);
if (set.has(hashed)) {
return false;
}
else {
set.add(hashed);
}
} return true; })())) {
return false;
}
// contains
if (!(IsDefined(schema.contains) || (0, index_10.IsNumber)(schema.minContains) || (0, index_10.IsNumber)(schema.maxContains))) {
return true; // exit
}
const containsSchema = IsDefined(schema.contains) ? schema.contains : (0, index_9.Never)();
const containsCount = value.reduce((acc, value) => (Visit(containsSchema, references, value) ? acc + 1 : acc), 0);
if (containsCount === 0) {
return false;
}
if ((0, index_10.IsNumber)(schema.minContains) && containsCount < schema.minContains) {
return false;
}
if ((0, index_10.IsNumber)(schema.maxContains) && containsCount > schema.maxContains) {
return false;
}
return true;
}
function FromAsyncIterator(schema, references, value) {
return (0, index_10.IsAsyncIterator)(value);
}
function FromBigInt(schema, references, value) {
if (!(0, index_10.IsBigInt)(value))
return false;
if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
return false;
}
if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
return false;
}
if (IsDefined(schema.maximum) && !(value <= schema.maximum)) {
return false;
}
if (IsDefined(schema.minimum) && !(value >= schema.minimum)) {
return false;
}
if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === BigInt(0))) {
return false;
}
return true;
}
function FromBoolean(schema, references, value) {
return (0, index_10.IsBoolean)(value);
}
function FromConstructor(schema, references, value) {
return Visit(schema.returns, references, value.prototype);
}
function FromDate(schema, references, value) {
if (!(0, index_10.IsDate)(value))
return false;
if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) {
return false;
}
if (IsDefined(schema.exclusiveMinimumTimestamp) && !(value.getTime() > schema.exclusiveMinimumTimestamp)) {
return false;
}
if (IsDefined(schema.maximumTimestamp) && !(value.getTime() <= schema.maximumTimestamp)) {
return false;
}
if (IsDefined(schema.minimumTimestamp) && !(value.getTime() >= schema.minimumTimestamp)) {
return false;
}
if (IsDefined(schema.multipleOfTimestamp) && !(value.getTime() % schema.multipleOfTimestamp === 0)) {
return false;
}
return true;
}
function FromFunction(schema, references, value) {
return (0, index_10.IsFunction)(value);
}
function FromInteger(schema, references, value) {
if (!(0, index_10.IsInteger)(value)) {
return false;
}
if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
return false;
}
if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
return false;
}
if (IsDefined(schema.maximum) && !(value <= schema.maximum)) {
return false;
}
if (IsDefined(schema.minimum) && !(value >= schema.minimum)) {
return false;
}
if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) {
return false;
}
return true;
}
function FromIntersect(schema, references, value) {
const check1 = schema.allOf.every((schema) => Visit(schema, references, value));
if (schema.unevaluatedProperties === false) {
const keyPattern = new RegExp((0, index_5.KeyOfPattern)(schema));
const check2 = Object.getOwnPropertyNames(value).every((key) => keyPattern.test(key));
return check1 && check2;
}
else if ((0, type_1.IsSchema)(schema.unevaluatedProperties)) {
const keyCheck = new RegExp((0, index_5.KeyOfPattern)(schema));
const check2 = Object.getOwnPropertyNames(value).every((key) => keyCheck.test(key) || Visit(schema.unevaluatedProperties, references, value[key]));
return check1 && check2;
}
else {
return check1;
}
}
function FromIterator(schema, references, value) {
return (0, index_10.IsIterator)(value);
}
function FromLiteral(schema, references, value) {
return value === schema.const;
}
function FromNever(schema, references, value) {
return false;
}
function FromNot(schema, references, value) {
return !Visit(schema.not, references, value);
}
function FromNull(schema, references, value) {
return (0, index_10.IsNull)(value);
}
function FromNumber(schema, references, value) {
if (!index_1.TypeSystemPolicy.IsNumberLike(value))
return false;
if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
return false;
}
if (IsDefined(schema.exclusiveMinimum) && !(value > schema.exclusiveMinimum)) {
return false;
}
if (IsDefined(schema.minimum) && !(value >= schema.minimum)) {
return false;
}
if (IsDefined(schema.maximum) && !(value <= schema.maximum)) {
return false;
}
if (IsDefined(schema.multipleOf) && !(value % schema.multipleOf === 0)) {
return false;
}
return true;
}
function FromObject(schema, references, value) {
if (!index_1.TypeSystemPolicy.IsObjectLike(value))
return false;
if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) {
return false;
}
if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) {
return false;
}
const knownKeys = Object.getOwnPropertyNames(schema.properties);
for (const knownKey of knownKeys) {
const property = schema.properties[knownKey];
if (schema.required && schema.required.includes(knownKey)) {
if (!Visit(property, references, value[knownKey])) {
return false;
}
if (((0, index_6.ExtendsUndefinedCheck)(property) || IsAnyOrUnknown(property)) && !(knownKey in value)) {
return false;
}
}
else {
if (index_1.TypeSystemPolicy.IsExactOptionalProperty(value, knownKey) && !Visit(property, references, value[knownKey])) {
return false;
}
}
}
if (schema.additionalProperties === false) {
const valueKeys = Object.getOwnPropertyNames(value);
// optimization: value is valid if schemaKey length matches the valueKey length
if (schema.required && schema.required.length === knownKeys.length && valueKeys.length === knownKeys.length) {
return true;
}
else {
return valueKeys.every((valueKey) => knownKeys.includes(valueKey));
}
}
else if (typeof schema.additionalProperties === 'object') {
const valueKeys = Object.getOwnPropertyNames(value);
return valueKeys.every((key) => knownKeys.includes(key) || Visit(schema.additionalProperties, references, value[key]));
}
else {
return true;
}
}
function FromPromise(schema, references, value) {
return (0, index_10.IsPromise)(value);
}
function FromRecord(schema, references, value) {
if (!index_1.TypeSystemPolicy.IsRecordLike(value)) {
return false;
}
if (IsDefined(schema.minProperties) && !(Object.getOwnPropertyNames(value).length >= schema.minProperties)) {
return false;
}
if (IsDefined(schema.maxProperties) && !(Object.getOwnPropertyNames(value).length <= schema.maxProperties)) {
return false;
}
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0];
const regex = new RegExp(patternKey);
// prettier-ignore
const check1 = Object.entries(value).every(([key, value]) => {
return (regex.test(key)) ? Visit(patternSchema, references, value) : true;
});
// prettier-ignore
const check2 = typeof schema.additionalProperties === 'object' ? Object.entries(value).every(([key, value]) => {
return (!regex.test(key)) ? Visit(schema.additionalProperties, references, value) : true;
}) : true;
const check3 = schema.additionalProperties === false
? Object.getOwnPropertyNames(value).every((key) => {
return regex.test(key);
})
: true;
return check1 && check2 && check3;
}
function FromRef(schema, references, value) {
return Visit((0, index_2.Deref)(schema, references), references, value);
}
function FromRegExp(schema, references, value) {
const regex = new RegExp(schema.source, schema.flags);
if (IsDefined(schema.minLength)) {
if (!(value.length >= schema.minLength))
return false;
}
if (IsDefined(schema.maxLength)) {
if (!(value.length <= schema.maxLength))
return false;
}
return regex.test(value);
}
function FromString(schema, references, value) {
if (!(0, index_10.IsString)(value)) {
return false;
}
if (IsDefined(schema.minLength)) {
if (!(value.length >= schema.minLength))
return false;
}
if (IsDefined(schema.maxLength)) {
if (!(value.length <= schema.maxLength))
return false;
}
if (IsDefined(schema.pattern)) {
const regex = new RegExp(schema.pattern);
if (!regex.test(value))
return false;
}
if (IsDefined(schema.format)) {
if (!index_7.FormatRegistry.Has(schema.format))
return false;
const func = index_7.FormatRegistry.Get(schema.format);
return func(value);
}
return true;
}
function FromSymbol(schema, references, value) {
return (0, index_10.IsSymbol)(value);
}
function FromTemplateLiteral(schema, references, value) {
return (0, index_10.IsString)(value) && new RegExp(schema.pattern).test(value);
}
function FromThis(schema, references, value) {
return Visit((0, index_2.Deref)(schema, references), references, value);
}
function FromTuple(schema, references, value) {
if (!(0, index_10.IsArray)(value)) {
return false;
}
if (schema.items === undefined && !(value.length === 0)) {
return false;
}
if (!(value.length === schema.maxItems)) {
return false;
}
if (!schema.items) {
return true;
}
for (let i = 0; i < schema.items.length; i++) {
if (!Visit(schema.items[i], references, value[i]))
return false;
}
return true;
}
function FromUndefined(schema, references, value) {
return (0, index_10.IsUndefined)(value);
}
function FromUnion(schema, references, value) {
return schema.anyOf.some((inner) => Visit(inner, references, value));
}
function FromUint8Array(schema, references, value) {
if (!(0, index_10.IsUint8Array)(value)) {
return false;
}
if (IsDefined(schema.maxByteLength) && !(value.length <= schema.maxByteLength)) {
return false;
}
if (IsDefined(schema.minByteLength) && !(value.length >= schema.minByteLength)) {
return false;
}
return true;
}
function FromUnknown(schema, references, value) {
return true;
}
function FromVoid(schema, references, value) {
return index_1.TypeSystemPolicy.IsVoidLike(value);
}
function FromKind(schema, references, value) {
if (!index_7.TypeRegistry.Has(schema[index_4.Kind]))
return false;
const func = index_7.TypeRegistry.Get(schema[index_4.Kind]);
return func(schema, value);
}
function Visit(schema, references, value) {
const references_ = IsDefined(schema.$id) ? [...references, schema] : references;
const schema_ = schema;
switch (schema_[index_4.Kind]) {
case 'Any':
return FromAny(schema_, references_, value);
case 'Array':
return FromArray(schema_, references_, value);
case 'AsyncIterator':
return FromAsyncIterator(schema_, references_, value);
case 'BigInt':
return FromBigInt(schema_, references_, value);
case 'Boolean':
return FromBoolean(schema_, references_, value);
case 'Constructor':
return FromConstructor(schema_, references_, value);
case 'Date':
return FromDate(schema_, references_, value);
case 'Function':
return FromFunction(schema_, references_, value);
case 'Integer':
return FromInteger(schema_, references_, value);
case 'Intersect':
return FromIntersect(schema_, references_, value);
case 'Iterator':
return FromIterator(schema_, references_, value);
case 'Literal':
return FromLiteral(schema_, references_, value);
case 'Never':
return FromNever(schema_, references_, value);
case 'Not':
return FromNot(schema_, references_, value);
case 'Null':
return FromNull(schema_, references_, value);
case 'Number':
return FromNumber(schema_, references_, value);
case 'Object':
return FromObject(schema_, references_, value);
case 'Promise':
return FromPromise(schema_, references_, value);
case 'Record':
return FromRecord(schema_, references_, value);
case 'Ref':
return FromRef(schema_, references_, value);
case 'RegExp':
return FromRegExp(schema_, references_, value);
case 'String':
return FromString(schema_, references_, value);
case 'Symbol':
return FromSymbol(schema_, references_, value);
case 'TemplateLiteral':
return FromTemplateLiteral(schema_, references_, value);
case 'This':
return FromThis(schema_, references_, value);
case 'Tuple':
return FromTuple(schema_, references_, value);
case 'Undefined':
return FromUndefined(schema_, references_, value);
case 'Union':
return FromUnion(schema_, references_, value);
case 'Uint8Array':
return FromUint8Array(schema_, references_, value);
case 'Unknown':
return FromUnknown(schema_, references_, value);
case 'Void':
return FromVoid(schema_, references_, value);
default:
if (!index_7.TypeRegistry.Has(schema_[index_4.Kind]))
throw new ValueCheckUnknownTypeError(schema_);
return FromKind(schema_, references_, value);
}
}
/** Returns true if the value matches the given type. */
function Check(...args) {
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]);
}