@sinclair/typebox
Version:
Json Schema Type Builder with Static Type Resolution for TypeScript
644 lines (642 loc) • 32 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeCompiler = exports.Policy = exports.TypeCompilerTypeGuardError = exports.TypeCompilerUnknownTypeError = exports.TypeCheck = void 0;
const index_1 = require("../value/transform/index");
const index_2 = require("../errors/index");
const index_3 = require("../system/index");
const index_4 = require("../type/error/index");
const index_5 = require("../value/deref/index");
const index_6 = require("../value/hash/index");
const index_7 = require("../type/symbols/index");
const index_8 = require("../type/registry/index");
const index_9 = require("../type/keyof/index");
const extends_undefined_1 = require("../type/extends/extends-undefined");
const index_10 = require("../type/never/index");
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
const index_11 = require("../value/guard/index");
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
const type_1 = require("../type/guard/type");
// ------------------------------------------------------------------
// TypeCheck
// ------------------------------------------------------------------
class TypeCheck {
constructor(schema, references, checkFunc, code) {
this.schema = schema;
this.references = references;
this.checkFunc = checkFunc;
this.code = code;
this.hasTransform = (0, index_1.HasTransform)(schema, references);
}
/** Returns the generated assertion code used to validate this type. */
Code() {
return this.code;
}
/** Returns an iterator for each error in this value. */
Errors(value) {
return (0, index_2.Errors)(this.schema, this.references, value);
}
/** Returns true if the value matches the compiled type. */
Check(value) {
return this.checkFunc(value);
}
/** Decodes a value or throws if error */
Decode(value) {
if (!this.checkFunc(value))
throw new index_1.TransformDecodeCheckError(this.schema, value, this.Errors(value).First());
return (this.hasTransform ? (0, index_1.TransformDecode)(this.schema, this.references, value) : value);
}
/** Encodes a value or throws if error */
Encode(value) {
const encoded = this.hasTransform ? (0, index_1.TransformEncode)(this.schema, this.references, value) : value;
if (!this.checkFunc(encoded))
throw new index_1.TransformEncodeCheckError(this.schema, value, this.Errors(value).First());
return encoded;
}
}
exports.TypeCheck = TypeCheck;
// ------------------------------------------------------------------
// Character
// ------------------------------------------------------------------
var Character;
(function (Character) {
function DollarSign(code) {
return code === 36;
}
Character.DollarSign = DollarSign;
function IsUnderscore(code) {
return code === 95;
}
Character.IsUnderscore = IsUnderscore;
function IsAlpha(code) {
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
}
Character.IsAlpha = IsAlpha;
function IsNumeric(code) {
return code >= 48 && code <= 57;
}
Character.IsNumeric = IsNumeric;
})(Character || (Character = {}));
// ------------------------------------------------------------------
// MemberExpression
// ------------------------------------------------------------------
var MemberExpression;
(function (MemberExpression) {
function IsFirstCharacterNumeric(value) {
if (value.length === 0)
return false;
return Character.IsNumeric(value.charCodeAt(0));
}
function IsAccessor(value) {
if (IsFirstCharacterNumeric(value))
return false;
for (let i = 0; i < value.length; i++) {
const code = value.charCodeAt(i);
const check = Character.IsAlpha(code) || Character.IsNumeric(code) || Character.DollarSign(code) || Character.IsUnderscore(code);
if (!check)
return false;
}
return true;
}
function EscapeHyphen(key) {
return key.replace(/'/g, "\\'");
}
function Encode(object, key) {
return IsAccessor(key) ? `${object}.${key}` : `${object}['${EscapeHyphen(key)}']`;
}
MemberExpression.Encode = Encode;
})(MemberExpression || (MemberExpression = {}));
// ------------------------------------------------------------------
// Identifier
// ------------------------------------------------------------------
var Identifier;
(function (Identifier) {
function Encode($id) {
const buffer = [];
for (let i = 0; i < $id.length; i++) {
const code = $id.charCodeAt(i);
if (Character.IsNumeric(code) || Character.IsAlpha(code)) {
buffer.push($id.charAt(i));
}
else {
buffer.push(`_${code}_`);
}
}
return buffer.join('').replace(/__/g, '_');
}
Identifier.Encode = Encode;
})(Identifier || (Identifier = {}));
// ------------------------------------------------------------------
// LiteralString
// ------------------------------------------------------------------
var LiteralString;
(function (LiteralString) {
function Escape(content) {
return content.replace(/'/g, "\\'");
}
LiteralString.Escape = Escape;
})(LiteralString || (LiteralString = {}));
// ------------------------------------------------------------------
// Errors
// ------------------------------------------------------------------
class TypeCompilerUnknownTypeError extends index_4.TypeBoxError {
constructor(schema) {
super('Unknown type');
this.schema = schema;
}
}
exports.TypeCompilerUnknownTypeError = TypeCompilerUnknownTypeError;
class TypeCompilerTypeGuardError extends index_4.TypeBoxError {
constructor(schema) {
super('Preflight validation check failed to guard for the given schema');
this.schema = schema;
}
}
exports.TypeCompilerTypeGuardError = TypeCompilerTypeGuardError;
// ------------------------------------------------------------------
// Policy
// ------------------------------------------------------------------
var Policy;
(function (Policy) {
function IsExactOptionalProperty(value, key, expression) {
return index_3.TypeSystemPolicy.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)`;
}
Policy.IsExactOptionalProperty = IsExactOptionalProperty;
function IsObjectLike(value) {
return !index_3.TypeSystemPolicy.AllowArrayObject ? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}))` : `(typeof ${value} === 'object' && ${value} !== null)`;
}
Policy.IsObjectLike = IsObjectLike;
function IsRecordLike(value) {
return !index_3.TypeSystemPolicy.AllowArrayObject
? `(typeof ${value} === 'object' && ${value} !== null && !Array.isArray(${value}) && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))`
: `(typeof ${value} === 'object' && ${value} !== null && !(${value} instanceof Date) && !(${value} instanceof Uint8Array))`;
}
Policy.IsRecordLike = IsRecordLike;
function IsNumberLike(value) {
return index_3.TypeSystemPolicy.AllowNaN ? `typeof ${value} === 'number'` : `Number.isFinite(${value})`;
}
Policy.IsNumberLike = IsNumberLike;
function IsVoidLike(value) {
return index_3.TypeSystemPolicy.AllowNullVoid ? `(${value} === undefined || ${value} === null)` : `${value} === undefined`;
}
Policy.IsVoidLike = IsVoidLike;
})(Policy || (exports.Policy = Policy = {}));
/** Compiles Types for Runtime Type Checking */
var TypeCompiler;
(function (TypeCompiler) {
// ----------------------------------------------------------------
// Guards
// ----------------------------------------------------------------
function IsAnyOrUnknown(schema) {
return schema[index_7.Kind] === 'Any' || schema[index_7.Kind] === 'Unknown';
}
// ----------------------------------------------------------------
// Types
// ----------------------------------------------------------------
function* FromAny(schema, references, value) {
yield 'true';
}
function* FromArray(schema, references, value) {
yield `Array.isArray(${value})`;
const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')];
if ((0, index_11.IsNumber)(schema.maxItems))
yield `${value}.length <= ${schema.maxItems}`;
if ((0, index_11.IsNumber)(schema.minItems))
yield `${value}.length >= ${schema.minItems}`;
const elementExpression = CreateExpression(schema.items, references, 'value');
yield `${value}.every((${parameter}) => ${elementExpression})`;
if ((0, type_1.IsSchema)(schema.contains) || (0, index_11.IsNumber)(schema.minContains) || (0, index_11.IsNumber)(schema.maxContains)) {
const containsSchema = (0, type_1.IsSchema)(schema.contains) ? schema.contains : (0, index_10.Never)();
const checkExpression = CreateExpression(containsSchema, references, 'value');
const checkMinContains = (0, index_11.IsNumber)(schema.minContains) ? [`(count >= ${schema.minContains})`] : [];
const checkMaxContains = (0, index_11.IsNumber)(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [];
const checkCount = `const count = value.reduce((${accumulator}, ${parameter}) => ${checkExpression} ? acc + 1 : acc, 0)`;
const check = [`(count > 0)`, ...checkMinContains, ...checkMaxContains].join(' && ');
yield `((${parameter}) => { ${checkCount}; return ${check}})(${value})`;
}
if (schema.uniqueItems === true) {
const check = `const hashed = hash(element); if(set.has(hashed)) { return false } else { set.add(hashed) } } return true`;
const block = `const set = new Set(); for(const element of value) { ${check} }`;
yield `((${parameter}) => { ${block} )(${value})`;
}
}
function* FromAsyncIterator(schema, references, value) {
yield `(typeof value === 'object' && Symbol.asyncIterator in ${value})`;
}
function* FromBigInt(schema, references, value) {
yield `(typeof ${value} === 'bigint')`;
if ((0, index_11.IsBigInt)(schema.exclusiveMaximum))
yield `${value} < BigInt(${schema.exclusiveMaximum})`;
if ((0, index_11.IsBigInt)(schema.exclusiveMinimum))
yield `${value} > BigInt(${schema.exclusiveMinimum})`;
if ((0, index_11.IsBigInt)(schema.maximum))
yield `${value} <= BigInt(${schema.maximum})`;
if ((0, index_11.IsBigInt)(schema.minimum))
yield `${value} >= BigInt(${schema.minimum})`;
if ((0, index_11.IsBigInt)(schema.multipleOf))
yield `(${value} % BigInt(${schema.multipleOf})) === 0`;
}
function* FromBoolean(schema, references, value) {
yield `(typeof ${value} === 'boolean')`;
}
function* FromConstructor(schema, references, value) {
yield* Visit(schema.returns, references, `${value}.prototype`);
}
function* FromDate(schema, references, value) {
yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())`;
if ((0, index_11.IsNumber)(schema.exclusiveMaximumTimestamp))
yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}`;
if ((0, index_11.IsNumber)(schema.exclusiveMinimumTimestamp))
yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}`;
if ((0, index_11.IsNumber)(schema.maximumTimestamp))
yield `${value}.getTime() <= ${schema.maximumTimestamp}`;
if ((0, index_11.IsNumber)(schema.minimumTimestamp))
yield `${value}.getTime() >= ${schema.minimumTimestamp}`;
if ((0, index_11.IsNumber)(schema.multipleOfTimestamp))
yield `(${value}.getTime() % ${schema.multipleOfTimestamp}) === 0`;
}
function* FromFunction(schema, references, value) {
yield `(typeof ${value} === 'function')`;
}
function* FromInteger(schema, references, value) {
yield `Number.isInteger(${value})`;
if ((0, index_11.IsNumber)(schema.exclusiveMaximum))
yield `${value} < ${schema.exclusiveMaximum}`;
if ((0, index_11.IsNumber)(schema.exclusiveMinimum))
yield `${value} > ${schema.exclusiveMinimum}`;
if ((0, index_11.IsNumber)(schema.maximum))
yield `${value} <= ${schema.maximum}`;
if ((0, index_11.IsNumber)(schema.minimum))
yield `${value} >= ${schema.minimum}`;
if ((0, index_11.IsNumber)(schema.multipleOf))
yield `(${value} % ${schema.multipleOf}) === 0`;
}
function* FromIntersect(schema, references, value) {
const check1 = schema.allOf.map((schema) => CreateExpression(schema, references, value)).join(' && ');
if (schema.unevaluatedProperties === false) {
const keyCheck = CreateVariable(`${new RegExp((0, index_9.KeyOfPattern)(schema))};`);
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))`;
yield `(${check1} && ${check2})`;
}
else if ((0, type_1.IsSchema)(schema.unevaluatedProperties)) {
const keyCheck = CreateVariable(`${new RegExp((0, index_9.KeyOfPattern)(schema))};`);
const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})`;
yield `(${check1} && ${check2})`;
}
else {
yield `(${check1})`;
}
}
function* FromIterator(schema, references, value) {
yield `(typeof value === 'object' && Symbol.iterator in ${value})`;
}
function* FromLiteral(schema, references, value) {
if (typeof schema.const === 'number' || typeof schema.const === 'boolean') {
yield `(${value} === ${schema.const})`;
}
else {
yield `(${value} === '${LiteralString.Escape(schema.const)}')`;
}
}
function* FromNever(schema, references, value) {
yield `false`;
}
function* FromNot(schema, references, value) {
const expression = CreateExpression(schema.not, references, value);
yield `(!${expression})`;
}
function* FromNull(schema, references, value) {
yield `(${value} === null)`;
}
function* FromNumber(schema, references, value) {
yield Policy.IsNumberLike(value);
if ((0, index_11.IsNumber)(schema.exclusiveMaximum))
yield `${value} < ${schema.exclusiveMaximum}`;
if ((0, index_11.IsNumber)(schema.exclusiveMinimum))
yield `${value} > ${schema.exclusiveMinimum}`;
if ((0, index_11.IsNumber)(schema.maximum))
yield `${value} <= ${schema.maximum}`;
if ((0, index_11.IsNumber)(schema.minimum))
yield `${value} >= ${schema.minimum}`;
if ((0, index_11.IsNumber)(schema.multipleOf))
yield `(${value} % ${schema.multipleOf}) === 0`;
}
function* FromObject(schema, references, value) {
yield Policy.IsObjectLike(value);
if ((0, index_11.IsNumber)(schema.minProperties))
yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}`;
if ((0, index_11.IsNumber)(schema.maxProperties))
yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}`;
const knownKeys = Object.getOwnPropertyNames(schema.properties);
for (const knownKey of knownKeys) {
const memberExpression = MemberExpression.Encode(value, knownKey);
const property = schema.properties[knownKey];
if (schema.required && schema.required.includes(knownKey)) {
yield* Visit(property, references, memberExpression);
if ((0, extends_undefined_1.ExtendsUndefinedCheck)(property) || IsAnyOrUnknown(property))
yield `('${knownKey}' in ${value})`;
}
else {
const expression = CreateExpression(property, references, memberExpression);
yield Policy.IsExactOptionalProperty(value, knownKey, expression);
}
}
if (schema.additionalProperties === false) {
if (schema.required && schema.required.length === knownKeys.length) {
yield `Object.getOwnPropertyNames(${value}).length === ${knownKeys.length}`;
}
else {
const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]`;
yield `Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key))`;
}
}
if (typeof schema.additionalProperties === 'object') {
const expression = CreateExpression(schema.additionalProperties, references, `${value}[key]`);
const keys = `[${knownKeys.map((key) => `'${key}'`).join(', ')}]`;
yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))`;
}
}
function* FromPromise(schema, references, value) {
yield `(typeof value === 'object' && typeof ${value}.then === 'function')`;
}
function* FromRecord(schema, references, value) {
yield Policy.IsRecordLike(value);
if ((0, index_11.IsNumber)(schema.minProperties))
yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}`;
if ((0, index_11.IsNumber)(schema.maxProperties))
yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}`;
const [patternKey, patternSchema] = Object.entries(schema.patternProperties)[0];
const variable = CreateVariable(`${new RegExp(patternKey)}`);
const check1 = CreateExpression(patternSchema, references, 'value');
const check2 = (0, type_1.IsSchema)(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true';
const expression = `(${variable}.test(key) ? ${check1} : ${check2})`;
yield `(Object.entries(${value}).every(([key, value]) => ${expression}))`;
}
function* FromRef(schema, references, value) {
const target = (0, index_5.Deref)(schema, references);
// Reference: If we have seen this reference before we can just yield and return the function call.
// If this isn't the case we defer to visit to generate and set the function for subsequent passes.
if (state.functions.has(schema.$ref))
return yield `${CreateFunctionName(schema.$ref)}(${value})`;
yield* Visit(target, references, value);
}
function* FromRegExp(schema, references, value) {
const variable = CreateVariable(`${new RegExp(schema.source, schema.flags)};`);
yield `(typeof ${value} === 'string')`;
if ((0, index_11.IsNumber)(schema.maxLength))
yield `${value}.length <= ${schema.maxLength}`;
if ((0, index_11.IsNumber)(schema.minLength))
yield `${value}.length >= ${schema.minLength}`;
yield `${variable}.test(${value})`;
}
function* FromString(schema, references, value) {
yield `(typeof ${value} === 'string')`;
if ((0, index_11.IsNumber)(schema.maxLength))
yield `${value}.length <= ${schema.maxLength}`;
if ((0, index_11.IsNumber)(schema.minLength))
yield `${value}.length >= ${schema.minLength}`;
if (schema.pattern !== undefined) {
const variable = CreateVariable(`${new RegExp(schema.pattern)};`);
yield `${variable}.test(${value})`;
}
if (schema.format !== undefined) {
yield `format('${schema.format}', ${value})`;
}
}
function* FromSymbol(schema, references, value) {
yield `(typeof ${value} === 'symbol')`;
}
function* FromTemplateLiteral(schema, references, value) {
yield `(typeof ${value} === 'string')`;
const variable = CreateVariable(`${new RegExp(schema.pattern)};`);
yield `${variable}.test(${value})`;
}
function* FromThis(schema, references, value) {
// Note: This types are assured to be hoisted prior to this call. Just yield the function.
yield `${CreateFunctionName(schema.$ref)}(${value})`;
}
function* FromTuple(schema, references, value) {
yield `Array.isArray(${value})`;
if (schema.items === undefined)
return yield `${value}.length === 0`;
yield `(${value}.length === ${schema.maxItems})`;
for (let i = 0; i < schema.items.length; i++) {
const expression = CreateExpression(schema.items[i], references, `${value}[${i}]`);
yield `${expression}`;
}
}
function* FromUndefined(schema, references, value) {
yield `${value} === undefined`;
}
function* FromUnion(schema, references, value) {
const expressions = schema.anyOf.map((schema) => CreateExpression(schema, references, value));
yield `(${expressions.join(' || ')})`;
}
function* FromUint8Array(schema, references, value) {
yield `${value} instanceof Uint8Array`;
if ((0, index_11.IsNumber)(schema.maxByteLength))
yield `(${value}.length <= ${schema.maxByteLength})`;
if ((0, index_11.IsNumber)(schema.minByteLength))
yield `(${value}.length >= ${schema.minByteLength})`;
}
function* FromUnknown(schema, references, value) {
yield 'true';
}
function* FromVoid(schema, references, value) {
yield Policy.IsVoidLike(value);
}
function* FromKind(schema, references, value) {
const instance = state.instances.size;
state.instances.set(instance, schema);
yield `kind('${schema[index_7.Kind]}', ${instance}, ${value})`;
}
function* Visit(schema, references, value, useHoisting = true) {
const references_ = (0, index_11.IsString)(schema.$id) ? [...references, schema] : references;
const schema_ = schema;
// --------------------------------------------------------------
// Hoisting
// --------------------------------------------------------------
if (useHoisting && (0, index_11.IsString)(schema.$id)) {
const functionName = CreateFunctionName(schema.$id);
if (state.functions.has(functionName)) {
return yield `${functionName}(${value})`;
}
else {
const functionCode = CreateFunction(functionName, schema, references, 'value', false);
state.functions.set(functionName, functionCode);
return yield `${functionName}(${value})`;
}
}
switch (schema_[index_7.Kind]) {
case 'Any':
return yield* FromAny(schema_, references_, value);
case 'Array':
return yield* FromArray(schema_, references_, value);
case 'AsyncIterator':
return yield* FromAsyncIterator(schema_, references_, value);
case 'BigInt':
return yield* FromBigInt(schema_, references_, value);
case 'Boolean':
return yield* FromBoolean(schema_, references_, value);
case 'Constructor':
return yield* FromConstructor(schema_, references_, value);
case 'Date':
return yield* FromDate(schema_, references_, value);
case 'Function':
return yield* FromFunction(schema_, references_, value);
case 'Integer':
return yield* FromInteger(schema_, references_, value);
case 'Intersect':
return yield* FromIntersect(schema_, references_, value);
case 'Iterator':
return yield* FromIterator(schema_, references_, value);
case 'Literal':
return yield* FromLiteral(schema_, references_, value);
case 'Never':
return yield* FromNever(schema_, references_, value);
case 'Not':
return yield* FromNot(schema_, references_, value);
case 'Null':
return yield* FromNull(schema_, references_, value);
case 'Number':
return yield* FromNumber(schema_, references_, value);
case 'Object':
return yield* FromObject(schema_, references_, value);
case 'Promise':
return yield* FromPromise(schema_, references_, value);
case 'Record':
return yield* FromRecord(schema_, references_, value);
case 'Ref':
return yield* FromRef(schema_, references_, value);
case 'RegExp':
return yield* FromRegExp(schema_, references_, value);
case 'String':
return yield* FromString(schema_, references_, value);
case 'Symbol':
return yield* FromSymbol(schema_, references_, value);
case 'TemplateLiteral':
return yield* FromTemplateLiteral(schema_, references_, value);
case 'This':
return yield* FromThis(schema_, references_, value);
case 'Tuple':
return yield* FromTuple(schema_, references_, value);
case 'Undefined':
return yield* FromUndefined(schema_, references_, value);
case 'Union':
return yield* FromUnion(schema_, references_, value);
case 'Uint8Array':
return yield* FromUint8Array(schema_, references_, value);
case 'Unknown':
return yield* FromUnknown(schema_, references_, value);
case 'Void':
return yield* FromVoid(schema_, references_, value);
default:
if (!index_8.TypeRegistry.Has(schema_[index_7.Kind]))
throw new TypeCompilerUnknownTypeError(schema);
return yield* FromKind(schema_, references_, value);
}
}
// ----------------------------------------------------------------
// Compiler State
// ----------------------------------------------------------------
// prettier-ignore
const state = {
language: 'javascript', // target language
functions: new Map(), // local functions
variables: new Map(), // local variables
instances: new Map() // exterior kind instances
};
// ----------------------------------------------------------------
// Compiler Factory
// ----------------------------------------------------------------
function CreateExpression(schema, references, value, useHoisting = true) {
return `(${[...Visit(schema, references, value, useHoisting)].join(' && ')})`;
}
function CreateFunctionName($id) {
return `check_${Identifier.Encode($id)}`;
}
function CreateVariable(expression) {
const variableName = `local_${state.variables.size}`;
state.variables.set(variableName, `const ${variableName} = ${expression}`);
return variableName;
}
function CreateFunction(name, schema, references, value, useHoisting = true) {
const [newline, pad] = ['\n', (length) => ''.padStart(length, ' ')];
const parameter = CreateParameter('value', 'any');
const returns = CreateReturns('boolean');
const expression = [...Visit(schema, references, value, useHoisting)].map((expression) => `${pad(4)}${expression}`).join(` &&${newline}`);
return `function ${name}(${parameter})${returns} {${newline}${pad(2)}return (${newline}${expression}${newline}${pad(2)})\n}`;
}
function CreateParameter(name, type) {
const annotation = state.language === 'typescript' ? `: ${type}` : '';
return `${name}${annotation}`;
}
function CreateReturns(type) {
return state.language === 'typescript' ? `: ${type}` : '';
}
// ----------------------------------------------------------------
// Compile
// ----------------------------------------------------------------
function Build(schema, references, options) {
const functionCode = CreateFunction('check', schema, references, 'value'); // will populate functions and variables
const parameter = CreateParameter('value', 'any');
const returns = CreateReturns('boolean');
const functions = [...state.functions.values()];
const variables = [...state.variables.values()];
// prettier-ignore
const checkFunction = (0, index_11.IsString)(schema.$id) // ensure top level schemas with $id's are hoisted
? `return function check(${parameter})${returns} {\n return ${CreateFunctionName(schema.$id)}(value)\n}`
: `return ${functionCode}`;
return [...variables, ...functions, checkFunction].join('\n');
}
/** Generates the code used to assert this type and returns it as a string */
function Code(...args) {
const defaults = { language: 'javascript' };
// prettier-ignore
const [schema, references, options] = (args.length === 2 && (0, index_11.IsArray)(args[1]) ? [args[0], args[1], defaults] :
args.length === 2 && !(0, index_11.IsArray)(args[1]) ? [args[0], [], args[1]] :
args.length === 3 ? [args[0], args[1], args[2]] :
args.length === 1 ? [args[0], [], defaults] :
[null, [], defaults]);
// compiler-reset
state.language = options.language;
state.variables.clear();
state.functions.clear();
state.instances.clear();
if (!(0, type_1.IsSchema)(schema))
throw new TypeCompilerTypeGuardError(schema);
for (const schema of references)
if (!(0, type_1.IsSchema)(schema))
throw new TypeCompilerTypeGuardError(schema);
return Build(schema, references, options);
}
TypeCompiler.Code = Code;
/** Compiles a TypeBox type for optimal runtime type checking. Types must be valid TypeBox types of TSchema */
function Compile(schema, references = []) {
const generatedCode = Code(schema, references, { language: 'javascript' });
const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode);
const instances = new Map(state.instances);
function typeRegistryFunction(kind, instance, value) {
if (!index_8.TypeRegistry.Has(kind) || !instances.has(instance))
return false;
const checkFunc = index_8.TypeRegistry.Get(kind);
const schema = instances.get(instance);
return checkFunc(schema, value);
}
function formatRegistryFunction(format, value) {
if (!index_8.FormatRegistry.Has(format))
return false;
const checkFunc = index_8.FormatRegistry.Get(format);
return checkFunc(value);
}
function hashFunction(value) {
return (0, index_6.Hash)(value);
}
const checkFunction = compiledFunction(typeRegistryFunction, formatRegistryFunction, hashFunction);
return new TypeCheck(schema, references, checkFunction, generatedCode);
}
TypeCompiler.Compile = Compile;
})(TypeCompiler || (exports.TypeCompiler = TypeCompiler = {}));
;