mirror of
https://github.com/zoriya/typebox.git
synced 2025-12-06 06:46:10 +00:00
Send Kind To TypeRegistry Function (#522)
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.30.2",
|
||||
"version": "0.30.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.30.2",
|
||||
"version": "0.30.3",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@sinclair/hammer": "^0.17.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.30.2",
|
||||
"version": "0.30.3",
|
||||
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
|
||||
"keywords": [
|
||||
"typescript",
|
||||
|
||||
@@ -375,7 +375,9 @@ export namespace TypeCompiler {
|
||||
yield IsVoidCheck(value)
|
||||
}
|
||||
function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator<string> {
|
||||
yield `kind('${schema[Types.Kind]}', ${value})`
|
||||
const instance = state.instances.size
|
||||
state.instances.set(instance, schema)
|
||||
yield `kind('${schema[Types.Kind]}', ${instance}, ${value})`
|
||||
}
|
||||
function* Visit<T extends Types.TSchema>(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator<string> {
|
||||
const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references
|
||||
@@ -470,6 +472,7 @@ export namespace TypeCompiler {
|
||||
language: 'javascript', // target language
|
||||
functions: new Map<string, string>(), // local functions
|
||||
variables: new Map<string, string>(), // local variables
|
||||
instances: new Map<number, Types.TKind>() // exterior kind instances
|
||||
}
|
||||
// -------------------------------------------------------------------
|
||||
// Compiler Factory
|
||||
@@ -533,6 +536,7 @@ export namespace TypeCompiler {
|
||||
state.language = options.language
|
||||
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)
|
||||
return Build(schema, references, options)
|
||||
@@ -541,8 +545,9 @@ export namespace TypeCompiler {
|
||||
export function Compile<T extends Types.TSchema>(schema: T, references: Types.TSchema[] = []): TypeCheck<T> {
|
||||
const generatedCode = Code(schema, references, { language: 'javascript' })
|
||||
const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode)
|
||||
function typeRegistryFunction(kind: string, value: unknown) {
|
||||
if (!Types.TypeRegistry.Has(kind)) return false
|
||||
function typeRegistryFunction(kind: string, instance: number, value: unknown) {
|
||||
if (!Types.TypeRegistry.Has(kind) || !state.instances.has(instance)) return false
|
||||
const schema = state.instances.get(instance)
|
||||
const checkFunc = Types.TypeRegistry.Get(kind)!
|
||||
return checkFunc(schema, value)
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Type, Kind, TypeRegistry } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('type/compiler/Custom', () => {
|
||||
TypeRegistry.Set('BigInt', (schema, value) => typeof value === 'bigint')
|
||||
it('Should validate bigint', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'BigInt' })
|
||||
Ok(T, 1n)
|
||||
})
|
||||
it('Should not validate bigint', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'BigInt' })
|
||||
Fail(T, 1)
|
||||
})
|
||||
it('Should validate bigint nested', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'BigInt' }),
|
||||
})
|
||||
Ok(T, { x: 1n })
|
||||
})
|
||||
it('Should not validate bigint nested', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'BigInt' }),
|
||||
})
|
||||
Fail(T, { x: 1 })
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,6 @@ import './async-iterator'
|
||||
import './bigint'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './custom'
|
||||
import './date'
|
||||
import './unicode'
|
||||
import './enum'
|
||||
@@ -12,6 +11,7 @@ import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './modifier'
|
||||
import './never'
|
||||
|
||||
72
test/runtime/compiler/kind.ts
Normal file
72
test/runtime/compiler/kind.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox'
|
||||
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
||||
import { Ok, Fail } from './validate'
|
||||
import { Assert } from '../assert'
|
||||
|
||||
describe('type/compiler/Kind', () => {
|
||||
// ------------------------------------------------------------
|
||||
// Fixtures
|
||||
// ------------------------------------------------------------
|
||||
beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI))
|
||||
afterEach(() => TypeRegistry.Delete('PI'))
|
||||
// ------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------
|
||||
it('Should validate', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'PI' })
|
||||
Ok(T, Math.PI)
|
||||
})
|
||||
it('Should not validate', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'PI' })
|
||||
Fail(T, Math.PI * 2)
|
||||
})
|
||||
it('Should validate in object', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'PI' }),
|
||||
})
|
||||
Ok(T, { x: Math.PI })
|
||||
})
|
||||
it('Should not validate in object', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'PI' }),
|
||||
})
|
||||
Fail(T, { x: Math.PI * 2 })
|
||||
})
|
||||
it('Should validate in array', () => {
|
||||
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
|
||||
Ok(T, [Math.PI])
|
||||
})
|
||||
it('Should not validate in array', () => {
|
||||
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
|
||||
Fail(T, [Math.PI * 2])
|
||||
})
|
||||
it('Should validate in tuple', () => {
|
||||
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
|
||||
Ok(T, [Math.PI])
|
||||
})
|
||||
it('Should not validate in tuple', () => {
|
||||
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
|
||||
Fail(T, [Math.PI * 2])
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Instances
|
||||
// ------------------------------------------------------------
|
||||
it('Should receive kind instance on registry callback', () => {
|
||||
const stack: string[] = []
|
||||
TypeRegistry.Set('Kind', (schema: unknown) => {
|
||||
// prettier-ignore
|
||||
return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string')
|
||||
? (() => { stack.push(schema.$id); return true })()
|
||||
: false
|
||||
})
|
||||
const A = { [Kind]: 'Kind', $id: 'A' } as TSchema
|
||||
const B = { [Kind]: 'Kind', $id: 'B' } as TSchema
|
||||
const T = Type.Object({ a: A, b: B })
|
||||
const C = TypeCompiler.Compile(T)
|
||||
const R = C.Check({ a: null, b: null })
|
||||
Assert.IsTrue(R)
|
||||
Assert.IsEqual(stack[0], 'A')
|
||||
Assert.IsEqual(stack[1], 'B')
|
||||
TypeRegistry.Delete('Kind')
|
||||
})
|
||||
})
|
||||
@@ -16,6 +16,7 @@ import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './lowercase'
|
||||
import './not'
|
||||
|
||||
13
test/runtime/type/guard/kind.ts
Normal file
13
test/runtime/type/guard/kind.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { TypeGuard, Kind } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('type/guard/TKind', () => {
|
||||
it('Should guard 1', () => {
|
||||
const T = { [Kind]: 'Kind' }
|
||||
Assert.IsTrue(TypeGuard.TKind(T))
|
||||
})
|
||||
it('Should guard 2', () => {
|
||||
const T = {}
|
||||
Assert.IsFalse(TypeGuard.TKind(T))
|
||||
})
|
||||
})
|
||||
@@ -4,13 +4,13 @@ import './async-iterator'
|
||||
import './bigint'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './custom'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './never'
|
||||
import './not'
|
||||
|
||||
@@ -2,14 +2,16 @@ import { Value } from '@sinclair/typebox/value'
|
||||
import { Type, Kind, TypeRegistry } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/cast/Custom', () => {
|
||||
before(() => {
|
||||
TypeRegistry.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world')
|
||||
})
|
||||
after(() => {
|
||||
TypeRegistry.Clear()
|
||||
})
|
||||
const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' })
|
||||
describe('value/cast/Kind', () => {
|
||||
// ---------------------------------------------------------
|
||||
// Fixtures
|
||||
// ---------------------------------------------------------
|
||||
before(() => TypeRegistry.Set('Kind', (schema, value) => value === 'hello' || value === 'world'))
|
||||
after(() => TypeRegistry.Clear())
|
||||
// ---------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------
|
||||
const T = Type.Unsafe({ [Kind]: 'Kind', default: 'hello' })
|
||||
const E = 'hello'
|
||||
it('Should upcast from string', () => {
|
||||
const value = 'hello'
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type, Kind, TypeRegistry } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('type/check/Custom', () => {
|
||||
const FooBar = Type.Unsafe({ [Kind]: 'FooBar' })
|
||||
before(() => {
|
||||
TypeRegistry.Set('FooBar', (schema, value) => value === 'foobar')
|
||||
})
|
||||
after(() => {
|
||||
TypeRegistry.Delete('FooBar')
|
||||
})
|
||||
it('Should validate foobar', () => {
|
||||
Assert.IsEqual(Value.Check(FooBar, 'foobar'), true)
|
||||
})
|
||||
it('Should not validate foobar', () => {
|
||||
Assert.IsEqual(Value.Check(FooBar, 1), false)
|
||||
})
|
||||
it('Should validate foobar nested', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.Object({ x: FooBar })
|
||||
Assert.IsEqual(Value.Check(T, { x: 'foobar' }), true)
|
||||
})
|
||||
it('Should not validate foobar nested', () => {
|
||||
// prettier-ignore
|
||||
const T = Type.Object({ x: FooBar })
|
||||
Assert.IsEqual(Value.Check(T, { x: 1 }), false)
|
||||
})
|
||||
})
|
||||
@@ -4,13 +4,13 @@ import './async-iterator'
|
||||
import './bigint'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './custom'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './never'
|
||||
import './not'
|
||||
|
||||
70
test/runtime/value/check/kind.ts
Normal file
70
test/runtime/value/check/kind.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('value/check/Kind', () => {
|
||||
// ------------------------------------------------------------
|
||||
// Fixtures
|
||||
// ------------------------------------------------------------
|
||||
beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI))
|
||||
afterEach(() => TypeRegistry.Delete('PI'))
|
||||
// ------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------
|
||||
it('Should validate', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'PI' })
|
||||
Assert.IsTrue(Value.Check(T, Math.PI))
|
||||
})
|
||||
it('Should not validate', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'PI' })
|
||||
Assert.IsFalse(Value.Check(T, Math.PI * 2))
|
||||
})
|
||||
it('Should validate in object', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'PI' }),
|
||||
})
|
||||
Assert.IsTrue(Value.Check(T, { x: Math.PI }))
|
||||
})
|
||||
it('Should not validate in object', () => {
|
||||
const T = Type.Object({
|
||||
x: Type.Unsafe({ [Kind]: 'PI' }),
|
||||
})
|
||||
Assert.IsFalse(Value.Check(T, { x: Math.PI * 2 }))
|
||||
})
|
||||
it('Should validate in array', () => {
|
||||
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
|
||||
Assert.IsTrue(Value.Check(T, [Math.PI]))
|
||||
})
|
||||
it('Should not validate in array', () => {
|
||||
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
|
||||
Assert.IsFalse(Value.Check(T, [Math.PI * 2]))
|
||||
})
|
||||
it('Should validate in tuple', () => {
|
||||
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
|
||||
Assert.IsTrue(Value.Check(T, [Math.PI]))
|
||||
})
|
||||
it('Should not validate in tuple', () => {
|
||||
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
|
||||
Assert.IsFalse(Value.Check(T, [Math.PI * 2]))
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Instances
|
||||
// ------------------------------------------------------------
|
||||
it('Should receive kind instance on registry callback', () => {
|
||||
const stack: string[] = []
|
||||
TypeRegistry.Set('Kind', (schema: unknown) => {
|
||||
// prettier-ignore
|
||||
return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string')
|
||||
? (() => { stack.push(schema.$id); return true })()
|
||||
: false
|
||||
})
|
||||
const A = { [Kind]: 'Kind', $id: 'A' } as TSchema
|
||||
const B = { [Kind]: 'Kind', $id: 'B' } as TSchema
|
||||
const T = Type.Object({ a: A, b: B })
|
||||
const R = Value.Check(T, { a: null, b: null })
|
||||
Assert.IsTrue(R)
|
||||
Assert.IsEqual(stack[0], 'A')
|
||||
Assert.IsEqual(stack[1], 'B')
|
||||
TypeRegistry.Delete('Kind')
|
||||
})
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { TypeSystem } from '@sinclair/typebox/system'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/convert/Custom', () => {
|
||||
it('Should not convert 1', () => {
|
||||
const Custom = TypeSystem.Type('type/convert/Custom/1', () => true)
|
||||
const T = Custom()
|
||||
const R = Value.Convert(T, true)
|
||||
Assert.IsEqual(R, true)
|
||||
})
|
||||
it('Should not convert 2', () => {
|
||||
const Custom = TypeSystem.Type('type/convert/Custom/2', () => true)
|
||||
const T = Custom()
|
||||
const R = Value.Convert(T, 42)
|
||||
Assert.IsEqual(R, 42)
|
||||
})
|
||||
it('Should not convert 3', () => {
|
||||
const Custom = TypeSystem.Type('type/convert/Custom/3', () => true)
|
||||
const T = Custom()
|
||||
const R = Value.Convert(T, 'hello')
|
||||
Assert.IsEqual(R, 'hello')
|
||||
})
|
||||
})
|
||||
@@ -5,7 +5,7 @@ import './bigint'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './constructor'
|
||||
import './custom'
|
||||
import './kind'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './function'
|
||||
|
||||
39
test/runtime/value/convert/kind.ts
Normal file
39
test/runtime/value/convert/kind.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { TypeRegistry, Kind, TSchema } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/convert/Kind', () => {
|
||||
// ---------------------------------------------------------
|
||||
// Fixtures
|
||||
// ---------------------------------------------------------
|
||||
beforeEach(() => TypeRegistry.Set('Kind', () => true))
|
||||
afterEach(() => TypeRegistry.Delete('Kind'))
|
||||
// ---------------------------------------------------------
|
||||
// Test
|
||||
// ---------------------------------------------------------
|
||||
it('Should not convert value 1', () => {
|
||||
const T = { [Kind]: 'Kind' } as TSchema
|
||||
const R = Value.Convert(T, true)
|
||||
Assert.IsEqual(R, true)
|
||||
})
|
||||
it('Should not convert value 2', () => {
|
||||
const T = { [Kind]: 'Kind' } as TSchema
|
||||
const R = Value.Convert(T, 42)
|
||||
Assert.IsEqual(R, 42)
|
||||
})
|
||||
it('Should not convert value 3', () => {
|
||||
const T = { [Kind]: 'Kind' } as TSchema
|
||||
const R = Value.Convert(T, 'hello')
|
||||
Assert.IsEqual(R, 'hello')
|
||||
})
|
||||
it('Should not convert value 4', () => {
|
||||
const T = { [Kind]: 'Kind' } as TSchema
|
||||
const R = Value.Convert(T, { x: 1 })
|
||||
Assert.IsEqual(R, { x: 1 })
|
||||
})
|
||||
it('Should not convert value 5', () => {
|
||||
const T = { [Kind]: 'Kind' } as TSchema
|
||||
const R = Value.Convert(T, [0, 1])
|
||||
Assert.IsEqual(R, [0, 1])
|
||||
})
|
||||
})
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type, Kind, TypeRegistry } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/create/Custom', () => {
|
||||
it('Should create custom value with default', () => {
|
||||
TypeRegistry.Set('CustomCreate1', () => true)
|
||||
const T = Type.Unsafe({ [Kind]: 'CustomCreate1', default: 'hello' })
|
||||
Assert.IsEqual(Value.Create(T), 'hello')
|
||||
})
|
||||
|
||||
it('Should throw when no default value is specified', () => {
|
||||
TypeRegistry.Set('CustomCreate2', () => true)
|
||||
const T = Type.Unsafe({ [Kind]: 'CustomCreate2' })
|
||||
Assert.Throws(() => Value.Create(T))
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,6 @@ import './async-iterator'
|
||||
import './bigint'
|
||||
import './boolean'
|
||||
import './composite'
|
||||
import './custom'
|
||||
import './constructor'
|
||||
import './enum'
|
||||
import './function'
|
||||
@@ -12,6 +11,7 @@ import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './never'
|
||||
import './not'
|
||||
|
||||
23
test/runtime/value/create/kind.ts
Normal file
23
test/runtime/value/create/kind.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type, Kind, TypeRegistry } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/create/Kind', () => {
|
||||
// ---------------------------------------------------------
|
||||
// Fixtures
|
||||
// ---------------------------------------------------------
|
||||
beforeEach(() => TypeRegistry.Set('Kind', () => true))
|
||||
afterEach(() => TypeRegistry.Delete('Kind'))
|
||||
// ---------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------
|
||||
it('Should create custom value with default', () => {
|
||||
const T = Type.Unsafe({ [Kind]: 'Kind', default: 'hello' })
|
||||
Assert.IsEqual(Value.Create(T), 'hello')
|
||||
})
|
||||
it('Should throw when no default value is specified', () => {
|
||||
TypeRegistry.Set('Kind', () => true)
|
||||
const T = Type.Unsafe({ [Kind]: 'Kind' })
|
||||
Assert.Throws(() => Value.Create(T))
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user