diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9796bd41b..27e14814b 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -27,23 +27,61 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { Encode as TransformEncode, Decode as TransformDecode, Has as HasTransform, TransformDecodeCheckError, TransformEncodeCheckError } from '../value/transform/index' -import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard/guard' -import { Errors, ValueErrorIterator } from '../errors/errors' +import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard/index' +import { Errors, ValueErrorIterator } from '../errors/index' import { TypeSystemPolicy } from '../system/index' -import { Deref } from '../value/deref/deref' -import { Hash } from '../value/hash/hash' -import * as Types from '../type/index' +import { Deref } from '../value/deref/index' +import { Hash } from '../value/hash/index' +import { Kind } from '../type/symbols/index' -// ------------------------------------------------------------------- +import { TSchema as IsSchemaType } from '../type/guard/type' +import { TypeRegistry, FormatRegistry } from '../type/registry/index' +import { KeyOfStringResolvePattern } from '../type/keyof/index' +import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined' + +import type { TSchema } from '../type/schema/index' +import type { TAsyncIterator } from '../type/async-iterator/index' +import type { TAny } from '../type/any/index' +import type { TArray } from '../type/array/index' +import type { TBigInt } from '../type/bigint/index' +import type { TBoolean } from '../type/boolean/index' +import type { TDate } from '../type/date/index' +import type { TConstructor } from '../type/constructor/index' +import type { TFunction } from '../type/function/index' +import type { TInteger } from '../type/integer/index' +import type { TIntersect } from '../type/intersect/index' +import type { TIterator } from '../type/iterator/index' +import type { TLiteral } from '../type/literal/index' +import { Never, type TNever } from '../type/never/index' +import type { TNot } from '../type/not/index' +import type { TNull } from '../type/null/index' +import type { TNumber } from '../type/number/index' +import type { TObject } from '../type/object/index' +import type { TPromise } from '../type/promise/index' +import type { TRecord } from '../type/record/index' +import type { TRef } from '../type/ref/index' +import type { TTemplateLiteral } from '../type/template-literal/index' +import type { TThis } from '../type/recursive/index' +import type { TTuple } from '../type/tuple/index' +import type { TUnion } from '../type/union/index' +import type { TUnknown } from '../type/unknown/index' +import type { Static, StaticDecode, StaticEncode } from '../type/static/index' +import type { TString } from '../type/string/index' +import type { TSymbol } from '../type/symbol/index' +import type { TUndefined } from '../type/undefined/index' +import type { TUint8Array } from '../type/uint8array/index' +import type { TVoid } from '../type/void/index' + +// ------------------------------------------------------------------ // CheckFunction -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export type CheckFunction = (value: unknown) => boolean -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeCheck -// ------------------------------------------------------------------- -export class TypeCheck { +// ------------------------------------------------------------------ +export class TypeCheck { private readonly hasTransform: boolean - constructor(private readonly schema: T, private readonly references: Types.TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) { + constructor(private readonly schema: T, private readonly references: TSchema[], private readonly checkFunc: CheckFunction, private readonly code: string) { this.hasTransform = HasTransform(schema, references) } /** Returns the generated assertion code used to validate this type. */ @@ -55,24 +93,24 @@ export class TypeCheck { return Errors(this.schema, this.references, value) } /** Returns true if the value matches the compiled type. */ - public Check(value: unknown): value is Types.Static { + public Check(value: unknown): value is Static { return this.checkFunc(value) } /** Decodes a value or throws if error */ - public Decode(value: unknown): Types.StaticDecode { + public Decode(value: unknown): StaticDecode { if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!) return this.hasTransform ? TransformDecode(this.schema, this.references, value) : value } /** Encodes a value or throws if error */ - public Encode(value: unknown): Types.StaticEncode { + public Encode(value: unknown): StaticEncode { const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!) return encoded } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Character -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace Character { export function DollarSign(code: number) { return code === 36 @@ -87,9 +125,9 @@ namespace Character { return code >= 48 && code <= 57 } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // MemberExpression -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace MemberExpression { function IsFirstCharacterNumeric(value: string) { if (value.length === 0) return false @@ -111,9 +149,9 @@ namespace MemberExpression { return IsAccessor(key) ? `${object}.${key}` : `${object}['${EscapeHyphen(key)}']` } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Identifier -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace Identifier { export function Encode($id: string) { const buffer: string[] = [] @@ -128,30 +166,30 @@ namespace Identifier { return buffer.join('').replace(/__/g, '_') } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // LiteralString -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace LiteralString { export function Escape(content: string) { return content.replace(/'/g, "\\'") } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export class TypeCompilerUnknownTypeError extends Error { - constructor(public readonly schema: Types.TSchema) { + constructor(public readonly schema: TSchema) { super('Unknown type') } } export class TypeCompilerTypeGuardError extends Error { - constructor(public readonly schema: Types.TSchema) { + constructor(public readonly schema: TSchema) { super('Preflight validation check failed to guard for the given schema') } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Policy -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export namespace Policy { export function IsExactOptionalProperty(value: string, key: string, expression: string) { return TypeSystemPolicy.ExactOptionalPropertyTypes ? `('${key}' in ${value} ? ${expression} : true)` : `(${MemberExpression.Encode(value, key)} !== undefined ? ${expression} : true)` @@ -171,36 +209,36 @@ export namespace Policy { return TypeSystemPolicy.AllowNullVoid ? `(${value} === undefined || ${value} === null)` : `${value} === undefined` } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeCompiler -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ export type TypeCompilerLanguageOption = 'typescript' | 'javascript' export interface TypeCompilerCodegenOptions { language?: TypeCompilerLanguageOption } /** Compiles Types for Runtime Type Checking */ export namespace TypeCompiler { - // ---------------------------------------------------------------------- + // ---------------------------------------------------------------- // Guards - // ---------------------------------------------------------------------- - function IsAnyOrUnknown(schema: Types.TSchema) { - return schema[Types.Kind] === 'Any' || schema[Types.Kind] === 'Unknown' + // ---------------------------------------------------------------- + function IsAnyOrUnknown(schema: TSchema) { + return schema[Kind] === 'Any' || schema[Kind] === 'Unknown' } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // Types - // ------------------------------------------------------------------- - function* TAny(schema: Types.TAny, references: Types.TSchema[], value: string): IterableIterator { + // ---------------------------------------------------------------- + function* TAny(schema: TAny, references: TSchema[], value: string): IterableIterator { yield 'true' } - function* TArray(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { + function* TArray(schema: TArray, references: TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` const [parameter, accumulator] = [CreateParameter('value', 'any'), CreateParameter('acc', 'number')] if (IsNumber(schema.maxItems)) yield `${value}.length <= ${schema.maxItems}` if (IsNumber(schema.minItems)) yield `${value}.length >= ${schema.minItems}` const elementExpression = CreateExpression(schema.items, references, 'value') yield `${value}.every((${parameter}) => ${elementExpression})` - if (Types.TypeGuard.TSchema(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) { - const containsSchema = Types.TypeGuard.TSchema(schema.contains) ? schema.contains : Types.Type.Never() + if (IsSchemaType(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) { + const containsSchema = IsSchemaType(schema.contains) ? schema.contains : Never() const checkExpression = CreateExpression(containsSchema, references, 'value') const checkMinContains = IsNumber(schema.minContains) ? [`(count >= ${schema.minContains})`] : [] const checkMaxContains = IsNumber(schema.maxContains) ? [`(count <= ${schema.maxContains})`] : [] @@ -214,10 +252,10 @@ export namespace TypeCompiler { yield `((${parameter}) => { ${block} )(${value})` } } - function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: string): IterableIterator { + function* TAsyncIterator(schema: TAsyncIterator, references: TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && Symbol.asyncIterator in ${value})` } - function* TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: string): IterableIterator { + function* TBigInt(schema: TBigInt, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'bigint')` if (IsBigInt(schema.exclusiveMaximum)) yield `${value} < BigInt(${schema.exclusiveMaximum})` if (IsBigInt(schema.exclusiveMinimum)) yield `${value} > BigInt(${schema.exclusiveMinimum})` @@ -225,13 +263,13 @@ export namespace TypeCompiler { if (IsBigInt(schema.minimum)) yield `${value} >= BigInt(${schema.minimum})` if (IsBigInt(schema.multipleOf)) yield `(${value} % BigInt(${schema.multipleOf})) === 0` } - function* TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: string): IterableIterator { + function* TBoolean(schema: TBoolean, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'boolean')` } - function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { + function* TConstructor(schema: TConstructor, references: TSchema[], value: string): IterableIterator { yield* Visit(schema.returns, references, `${value}.prototype`) } - function* TDate(schema: Types.TDate, references: Types.TSchema[], value: string): IterableIterator { + function* TDate(schema: TDate, references: TSchema[], value: string): IterableIterator { yield `(${value} instanceof Date) && Number.isFinite(${value}.getTime())` if (IsNumber(schema.exclusiveMaximumTimestamp)) yield `${value}.getTime() < ${schema.exclusiveMaximumTimestamp}` if (IsNumber(schema.exclusiveMinimumTimestamp)) yield `${value}.getTime() > ${schema.exclusiveMinimumTimestamp}` @@ -239,10 +277,10 @@ export namespace TypeCompiler { if (IsNumber(schema.minimumTimestamp)) yield `${value}.getTime() >= ${schema.minimumTimestamp}` if (IsNumber(schema.multipleOfTimestamp)) yield `(${value}.getTime() % ${schema.multipleOfTimestamp}) === 0` } - function* TFunction(schema: Types.TFunction, references: Types.TSchema[], value: string): IterableIterator { + function* TFunction(schema: TFunction, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'function')` } - function* TInteger(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { + function* TInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'number' && Number.isInteger(${value}))` if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` @@ -250,41 +288,41 @@ export namespace TypeCompiler { if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` } - function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: string): IterableIterator { - const check1 = schema.allOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)).join(' && ') + function* TIntersect(schema: TIntersect, references: TSchema[], value: string): IterableIterator { + const check1 = schema.allOf.map((schema: TSchema) => CreateExpression(schema, references, value)).join(' && ') if (schema.unevaluatedProperties === false) { - const keyCheck = CreateVariable(`${new RegExp(Types.KeyOfStringResolvePattern(schema))};`) + const keyCheck = CreateVariable(`${new RegExp(KeyOfStringResolvePattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key))` yield `(${check1} && ${check2})` - } else if (Types.TypeGuard.TSchema(schema.unevaluatedProperties)) { - const keyCheck = CreateVariable(`${new RegExp(Types.KeyOfStringResolvePattern(schema))};`) + } else if (IsSchemaType(schema.unevaluatedProperties)) { + const keyCheck = CreateVariable(`${new RegExp(KeyOfStringResolvePattern(schema))};`) const check2 = `Object.getOwnPropertyNames(${value}).every(key => ${keyCheck}.test(key) || ${CreateExpression(schema.unevaluatedProperties, references, `${value}[key]`)})` yield `(${check1} && ${check2})` } else { yield `(${check1})` } } - function* TIterator(schema: Types.TIterator, references: Types.TSchema[], value: string): IterableIterator { + function* TIterator(schema: TIterator, references: TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && Symbol.iterator in ${value})` } - function* TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* TLiteral(schema: TLiteral, references: TSchema[], value: string): IterableIterator { if (typeof schema.const === 'number' || typeof schema.const === 'boolean') { yield `(${value} === ${schema.const})` } else { yield `(${value} === '${LiteralString.Escape(schema.const)}')` } } - function* TNever(schema: Types.TNever, references: Types.TSchema[], value: string): IterableIterator { + function* TNever(schema: TNever, references: TSchema[], value: string): IterableIterator { yield `false` } - function* TNot(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { + function* TNot(schema: TNot, references: TSchema[], value: string): IterableIterator { const expression = CreateExpression(schema.not, references, value) yield `(!${expression})` } - function* TNull(schema: Types.TNull, references: Types.TSchema[], value: string): IterableIterator { + function* TNull(schema: TNull, references: TSchema[], value: string): IterableIterator { yield `(${value} === null)` } - function* TNumber(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { + function* TNumber(schema: TNumber, references: TSchema[], value: string): IterableIterator { yield Policy.IsNumberLike(value) if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}` if (IsNumber(schema.exclusiveMinimum)) yield `${value} > ${schema.exclusiveMinimum}` @@ -292,7 +330,7 @@ export namespace TypeCompiler { if (IsNumber(schema.minimum)) yield `${value} >= ${schema.minimum}` if (IsNumber(schema.multipleOf)) yield `(${value} % ${schema.multipleOf}) === 0` } - function* TObject(schema: Types.TObject, references: Types.TSchema[], value: string): IterableIterator { + function* TObject(schema: TObject, references: TSchema[], value: string): IterableIterator { yield Policy.IsObjectLike(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (IsNumber(schema.maxProperties)) yield `Object.getOwnPropertyNames(${value}).length <= ${schema.maxProperties}` @@ -302,7 +340,7 @@ export namespace TypeCompiler { const property = schema.properties[knownKey] if (schema.required && schema.required.includes(knownKey)) { yield* Visit(property, references, memberExpression) - if (Types.ExtendsUndefinedCheck(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})` + if (ExtendsUndefinedCheck(property) || IsAnyOrUnknown(property)) yield `('${knownKey}' in ${value})` } else { const expression = CreateExpression(property, references, memberExpression) yield Policy.IsExactOptionalProperty(value, knownKey, expression) @@ -322,28 +360,28 @@ export namespace TypeCompiler { yield `(Object.getOwnPropertyNames(${value}).every(key => ${keys}.includes(key) || ${expression}))` } } - function* TPromise(schema: Types.TPromise, references: Types.TSchema[], value: string): IterableIterator { + function* TPromise(schema: TPromise, references: TSchema[], value: string): IterableIterator { yield `(typeof value === 'object' && typeof ${value}.then === 'function')` } - function* TRecord(schema: Types.TRecord, references: Types.TSchema[], value: string): IterableIterator { + function* TRecord(schema: TRecord, references: TSchema[], value: string): IterableIterator { yield Policy.IsRecordLike(value) if (IsNumber(schema.minProperties)) yield `Object.getOwnPropertyNames(${value}).length >= ${schema.minProperties}` if (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 = Types.TypeGuard.TSchema(schema.additionalProperties) ? CreateExpression(schema.additionalProperties, references, value) : schema.additionalProperties === false ? 'false' : 'true' + const check2 = IsSchemaType(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* TRef(schema: Types.TRef, references: Types.TSchema[], value: string): IterableIterator { + function* TRef(schema: TRef, references: TSchema[], value: string): IterableIterator { const target = 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* TString(schema: Types.TString, references: Types.TSchema[], value: string): IterableIterator { + function* TString(schema: TString, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` if (IsNumber(schema.maxLength)) yield `${value}.length <= ${schema.maxLength}` if (IsNumber(schema.minLength)) yield `${value}.length >= ${schema.minLength}` @@ -355,19 +393,19 @@ export namespace TypeCompiler { yield `format('${schema.format}', ${value})` } } - function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { + function* TSymbol(schema: TSymbol, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'symbol')` } - function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* TTemplateLiteral(schema: TTemplateLiteral, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'string')` const variable = CreateVariable(`${new RegExp(schema.pattern)};`) yield `${variable}.test(${value})` } - function* TThis(schema: Types.TThis, references: Types.TSchema[], value: string): IterableIterator { + function* TThis(schema: TThis, references: TSchema[], value: string): IterableIterator { // Note: This types are assured to be hoisted prior to this call. Just yield the function. yield `${CreateFunctionName(schema.$ref)}(${value})` } - function* TTuple(schema: Types.TTuple, references: Types.TSchema[], value: string): IterableIterator { + function* TTuple(schema: TTuple, references: TSchema[], value: string): IterableIterator { yield `Array.isArray(${value})` if (schema.items === undefined) return yield `${value}.length === 0` yield `(${value}.length === ${schema.maxItems})` @@ -376,35 +414,35 @@ export namespace TypeCompiler { yield `${expression}` } } - function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: string): IterableIterator { + function* TUndefined(schema: TUndefined, references: TSchema[], value: string): IterableIterator { yield `${value} === undefined` } - function* TUnion(schema: Types.TUnion, references: Types.TSchema[], value: string): IterableIterator { - const expressions = schema.anyOf.map((schema: Types.TSchema) => CreateExpression(schema, references, value)) + function* TUnion(schema: TUnion, references: TSchema[], value: string): IterableIterator { + const expressions = schema.anyOf.map((schema: TSchema) => CreateExpression(schema, references, value)) yield `(${expressions.join(' || ')})` } - function* TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], value: string): IterableIterator { + function* TUint8Array(schema: TUint8Array, references: TSchema[], value: string): IterableIterator { yield `${value} instanceof Uint8Array` if (IsNumber(schema.maxByteLength)) yield `(${value}.length <= ${schema.maxByteLength})` if (IsNumber(schema.minByteLength)) yield `(${value}.length >= ${schema.minByteLength})` } - function* TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: string): IterableIterator { + function* TUnknown(schema: TUnknown, references: TSchema[], value: string): IterableIterator { yield 'true' } - function* TVoid(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { + function* TVoid(schema: TVoid, references: TSchema[], value: string): IterableIterator { yield Policy.IsVoidLike(value) } - function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { + function* TKind(schema: TSchema, references: TSchema[], value: string): IterableIterator { const instance = state.instances.size state.instances.set(instance, schema) - yield `kind('${schema[Types.Kind]}', ${instance}, ${value})` + yield `kind('${schema[Kind]}', ${instance}, ${value})` } - function* Visit(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator { + function* Visit(schema: T, references: TSchema[], value: string, useHoisting: boolean = true): IterableIterator { const references_ = IsString(schema.$id) ? [...references, schema] : references const schema_ = schema as any - // ---------------------------------------------------------------------------------- + // -------------------------------------------------------------- // Hoisting - // ---------------------------------------------------------------------------------- + // -------------------------------------------------------------- if (useHoisting && IsString(schema.$id)) { const functionName = CreateFunctionName(schema.$id) if (state.functions.has(functionName)) { @@ -415,7 +453,7 @@ export namespace TypeCompiler { return yield `${functionName}(${value})` } } - switch (schema_[Types.Kind]) { + switch (schema_[Kind]) { case 'Any': return yield* TAny(schema_, references_, value) case 'Array': @@ -477,24 +515,24 @@ export namespace TypeCompiler { case 'Void': return yield* TVoid(schema_, references_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) + if (!TypeRegistry.Has(schema_[Kind])) throw new TypeCompilerUnknownTypeError(schema) return yield* TKind(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 + 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: Types.TSchema, references: Types.TSchema[], value: string, useHoisting: boolean = true): string { + // ---------------------------------------------------------------- + function CreateExpression(schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): string { return `(${[...Visit(schema, references, value, useHoisting)].join(' && ')})` } function CreateFunctionName($id: string) { @@ -505,7 +543,7 @@ export namespace TypeCompiler { state.variables.set(variableName, `const ${variableName} = ${expression}`) return variableName } - function CreateFunction(name: string, schema: Types.TSchema, references: Types.TSchema[], value: string, useHoisting: boolean = true): string { + function CreateFunction(name: string, schema: TSchema, references: TSchema[], value: string, useHoisting: boolean = true): string { const [newline, pad] = ['\n', (length: number) => ''.padStart(length, ' ')] const parameter = CreateParameter('value', 'any') const returns = CreateReturns('boolean') @@ -519,10 +557,10 @@ export namespace TypeCompiler { function CreateReturns(type: string) { return state.language === 'typescript' ? `: ${type}` : '' } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------- // Compile - // ------------------------------------------------------------------- - function Build(schema: T, references: Types.TSchema[], options: TypeCompilerCodegenOptions): string { + // ---------------------------------------------------------------- + function Build(schema: T, references: TSchema[], options: TypeCompilerCodegenOptions): string { const functionCode = CreateFunction('check', schema, references, 'value') // will populate functions and variables const parameter = CreateParameter('value', 'any') const returns = CreateReturns('boolean') @@ -535,9 +573,9 @@ export namespace TypeCompiler { return [...variables, ...functions, checkFunction].join('\n') } /** Generates the code used to assert this type and returns it as a string */ - export function Code(schema: T, references: Types.TSchema[], options?: TypeCompilerCodegenOptions): string + export function Code(schema: T, references: TSchema[], options?: TypeCompilerCodegenOptions): string /** Generates the code used to assert this type and returns it as a string */ - export function Code(schema: T, options?: TypeCompilerCodegenOptions): string + export function Code(schema: T, options?: TypeCompilerCodegenOptions): string /** Generates the code used to assert this type and returns it as a string */ export function Code(...args: any[]) { const defaults = { language: 'javascript' } @@ -554,24 +592,24 @@ export namespace TypeCompiler { state.variables.clear() state.functions.clear() state.instances.clear() - if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) - for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema) + if (!IsSchemaType(schema)) throw new TypeCompilerTypeGuardError(schema) + for (const schema of references) if (!IsSchemaType(schema)) throw new TypeCompilerTypeGuardError(schema) return Build(schema, references, options) } /** Compiles a TypeBox type for optimal runtime type checking. Types must be valid TypeBox types of TSchema */ - export function Compile(schema: T, references: Types.TSchema[] = []): TypeCheck { + export function Compile(schema: T, references: TSchema[] = []): TypeCheck { const generatedCode = Code(schema, references, { language: 'javascript' }) const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode) const instances = new Map(state.instances) function typeRegistryFunction(kind: string, instance: number, value: unknown) { - if (!Types.TypeRegistry.Has(kind) || !instances.has(instance)) return false - const checkFunc = Types.TypeRegistry.Get(kind)! + if (!TypeRegistry.Has(kind) || !instances.has(instance)) return false + const checkFunc = TypeRegistry.Get(kind)! const schema = instances.get(instance)! return checkFunc(schema, value) } function formatRegistryFunction(format: string, value: string) { - if (!Types.FormatRegistry.Has(format)) return false - const checkFunc = Types.FormatRegistry.Get(format)! + if (!FormatRegistry.Has(format)) return false + const checkFunc = FormatRegistry.Get(format)! return checkFunc(value) } function hashFunction(value: unknown) { diff --git a/src/value/clone/clone.ts b/src/value/clone/clone.ts index 7a024385d..aad338c61 100644 --- a/src/value/clone/clone.ts +++ b/src/value/clone/clone.ts @@ -26,8 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from '../guard/guard' -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/guard' +import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from '../guard/index' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' // -------------------------------------------------------------------------- // Clonable diff --git a/src/value/convert/convert.ts b/src/value/convert/convert.ts index e781ac0af..504dca684 100644 --- a/src/value/convert/convert.ts +++ b/src/value/convert/convert.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from '../guard/guard' +import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from '../guard/index' import { Clone } from '../clone/index' import { Check } from '../check/index' import { Deref } from '../deref/index' diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts index 21e09eb75..d5626349f 100644 --- a/src/value/transform/has.ts +++ b/src/value/transform/has.ts @@ -27,9 +27,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TTransform as IsTransformType, TSchema as IsSchemaType } from '../../type/guard/type' -import { Kind } from '../../type/symbols/index' import { IsString, IsUndefined } from '../guard/index' import { Deref } from '../deref/index' +import { Kind } from '../../type/symbols/index' import type { TSchema } from '../../type/schema/index' import type { TArray } from '../../type/array/index'