@sinclair/typebox 0.24.8 → 0.24.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/compiler.d.ts +3 -2
- package/compiler/compiler.js +32 -19
- package/compiler/index.d.ts +1 -1
- package/compiler/index.js +0 -1
- package/package.json +2 -1
- package/readme.md +100 -72
- package/typebox.js +1 -1
- package/value/cast.js +2 -4
- package/value/check.js +23 -6
- package/value/create.js +17 -9
- package/value/errors.js +18 -3
- package/value/index.d.ts +1 -0
package/compiler/compiler.d.ts
CHANGED
|
@@ -3,10 +3,10 @@ import * as Types from '../typebox';
|
|
|
3
3
|
export declare type CheckFunction = (value: unknown) => boolean;
|
|
4
4
|
export declare class TypeCheck<T extends Types.TSchema> {
|
|
5
5
|
private readonly schema;
|
|
6
|
-
private readonly
|
|
6
|
+
private readonly references;
|
|
7
7
|
private readonly checkFunc;
|
|
8
8
|
private readonly code;
|
|
9
|
-
constructor(schema: T,
|
|
9
|
+
constructor(schema: T, references: Types.TSchema[], checkFunc: CheckFunction, code: string);
|
|
10
10
|
/** Returns the generated validation code used to validate this type. */
|
|
11
11
|
Code(): string;
|
|
12
12
|
/** Returns an iterator for each error in this value. */
|
|
@@ -14,6 +14,7 @@ export declare class TypeCheck<T extends Types.TSchema> {
|
|
|
14
14
|
/** Returns true if the value matches the given type. */
|
|
15
15
|
Check(value: unknown): value is Types.Static<T>;
|
|
16
16
|
}
|
|
17
|
+
/** Compiles TypeBox Types for Runtime Type Checking */
|
|
17
18
|
export declare namespace TypeCompiler {
|
|
18
19
|
/** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */
|
|
19
20
|
function Compile<T extends Types.TSchema>(schema: T, references?: Types.TSchema[]): TypeCheck<T>;
|
package/compiler/compiler.js
CHANGED
|
@@ -35,12 +35,12 @@ const Types = require("../typebox");
|
|
|
35
35
|
// -------------------------------------------------------------------
|
|
36
36
|
class TypeCheck {
|
|
37
37
|
schema;
|
|
38
|
-
|
|
38
|
+
references;
|
|
39
39
|
checkFunc;
|
|
40
40
|
code;
|
|
41
|
-
constructor(schema,
|
|
41
|
+
constructor(schema, references, checkFunc, code) {
|
|
42
42
|
this.schema = schema;
|
|
43
|
-
this.
|
|
43
|
+
this.references = references;
|
|
44
44
|
this.checkFunc = checkFunc;
|
|
45
45
|
this.code = code;
|
|
46
46
|
}
|
|
@@ -50,7 +50,7 @@ class TypeCheck {
|
|
|
50
50
|
}
|
|
51
51
|
/** Returns an iterator for each error in this value. */
|
|
52
52
|
Errors(value) {
|
|
53
|
-
return errors_1.ValueErrors.Errors(this.schema, this.
|
|
53
|
+
return errors_1.ValueErrors.Errors(this.schema, this.references, value);
|
|
54
54
|
}
|
|
55
55
|
/** Returns true if the value matches the given type. */
|
|
56
56
|
Check(value) {
|
|
@@ -61,6 +61,7 @@ exports.TypeCheck = TypeCheck;
|
|
|
61
61
|
// -------------------------------------------------------------------
|
|
62
62
|
// TypeCompiler
|
|
63
63
|
// -------------------------------------------------------------------
|
|
64
|
+
/** Compiles TypeBox Types for Runtime Type Checking */
|
|
64
65
|
var TypeCompiler;
|
|
65
66
|
(function (TypeCompiler) {
|
|
66
67
|
// -------------------------------------------------------------------
|
|
@@ -70,8 +71,14 @@ var TypeCompiler;
|
|
|
70
71
|
yield '(true)';
|
|
71
72
|
}
|
|
72
73
|
function* Array(schema, value) {
|
|
73
|
-
const
|
|
74
|
-
|
|
74
|
+
const expression = [...Visit(schema.items, `value`)].map((condition) => condition).join(' && ');
|
|
75
|
+
if (schema.minItems !== undefined)
|
|
76
|
+
yield `(${value}.length >= ${schema.minItems})`;
|
|
77
|
+
if (schema.maxItems !== undefined)
|
|
78
|
+
yield `(${value}.length <= ${schema.maxItems})`;
|
|
79
|
+
if (schema.uniqueItems !== undefined)
|
|
80
|
+
yield `(new Set(${value}).size === ${value}.length)`;
|
|
81
|
+
yield `(Array.isArray(${value}) && ${value}.every(value => ${expression}))`;
|
|
75
82
|
}
|
|
76
83
|
function* Boolean(schema, value) {
|
|
77
84
|
yield `(typeof ${value} === 'boolean')`;
|
|
@@ -84,15 +91,15 @@ var TypeCompiler;
|
|
|
84
91
|
}
|
|
85
92
|
function* Integer(schema, value) {
|
|
86
93
|
yield `(typeof ${value} === 'number' && Number.isInteger(${value}))`;
|
|
87
|
-
if (schema.multipleOf)
|
|
94
|
+
if (schema.multipleOf !== undefined)
|
|
88
95
|
yield `(${value} % ${schema.multipleOf} === 0)`;
|
|
89
|
-
if (schema.exclusiveMinimum)
|
|
96
|
+
if (schema.exclusiveMinimum !== undefined)
|
|
90
97
|
yield `(${value} > ${schema.exclusiveMinimum})`;
|
|
91
|
-
if (schema.exclusiveMaximum)
|
|
98
|
+
if (schema.exclusiveMaximum !== undefined)
|
|
92
99
|
yield `(${value} < ${schema.exclusiveMaximum})`;
|
|
93
|
-
if (schema.minimum)
|
|
100
|
+
if (schema.minimum !== undefined)
|
|
94
101
|
yield `(${value} >= ${schema.minimum})`;
|
|
95
|
-
if (schema.maximum)
|
|
102
|
+
if (schema.maximum !== undefined)
|
|
96
103
|
yield `(${value} <= ${schema.maximum})`;
|
|
97
104
|
}
|
|
98
105
|
function* Literal(schema, value) {
|
|
@@ -108,15 +115,15 @@ var TypeCompiler;
|
|
|
108
115
|
}
|
|
109
116
|
function* Number(schema, value) {
|
|
110
117
|
yield `(typeof ${value} === 'number')`;
|
|
111
|
-
if (schema.multipleOf)
|
|
118
|
+
if (schema.multipleOf !== undefined)
|
|
112
119
|
yield `(${value} % ${schema.multipleOf} === 0)`;
|
|
113
|
-
if (schema.exclusiveMinimum)
|
|
120
|
+
if (schema.exclusiveMinimum !== undefined)
|
|
114
121
|
yield `(${value} > ${schema.exclusiveMinimum})`;
|
|
115
|
-
if (schema.exclusiveMaximum)
|
|
122
|
+
if (schema.exclusiveMaximum !== undefined)
|
|
116
123
|
yield `(${value} < ${schema.exclusiveMaximum})`;
|
|
117
|
-
if (schema.minimum)
|
|
124
|
+
if (schema.minimum !== undefined)
|
|
118
125
|
yield `(${value} >= ${schema.minimum})`;
|
|
119
|
-
if (schema.maximum)
|
|
126
|
+
if (schema.maximum !== undefined)
|
|
120
127
|
yield `(${value} <= ${schema.maximum})`;
|
|
121
128
|
}
|
|
122
129
|
function* Object(schema, value) {
|
|
@@ -182,6 +189,12 @@ var TypeCompiler;
|
|
|
182
189
|
}
|
|
183
190
|
function* String(schema, value) {
|
|
184
191
|
yield `(typeof ${value} === 'string')`;
|
|
192
|
+
if (schema.minLength !== undefined) {
|
|
193
|
+
yield `(${value}.length >= ${schema.minLength})`;
|
|
194
|
+
}
|
|
195
|
+
if (schema.maxLength !== undefined) {
|
|
196
|
+
yield `(${value}.length <= ${schema.maxLength})`;
|
|
197
|
+
}
|
|
185
198
|
if (schema.pattern !== undefined) {
|
|
186
199
|
const local = PushLocal(`const local = new RegExp('${schema.pattern}');`);
|
|
187
200
|
yield `(${local}.test(${value}))`;
|
|
@@ -275,7 +288,7 @@ var TypeCompiler;
|
|
|
275
288
|
case 'Void':
|
|
276
289
|
return yield* Void(anySchema, value);
|
|
277
290
|
default:
|
|
278
|
-
throw Error(`Unknown schema kind '${schema[Types.Kind]}'`);
|
|
291
|
+
throw new Error(`TypeCompiler: Unknown schema kind '${schema[Types.Kind]}'`);
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
294
|
// -------------------------------------------------------------------
|
|
@@ -292,9 +305,9 @@ var TypeCompiler;
|
|
|
292
305
|
function PushReferences(schemas = []) {
|
|
293
306
|
for (const schema of schemas) {
|
|
294
307
|
if (!schema.$id)
|
|
295
|
-
throw Error(`Referenced schemas must specify an $id
|
|
308
|
+
throw new Error(`TypeCompiler: Referenced schemas must specify an $id.`);
|
|
296
309
|
if (referenceMap.has(schema.$id))
|
|
297
|
-
throw Error(`Duplicate schema $id
|
|
310
|
+
throw new Error(`TypeCompiler: Duplicate schema $id found for '${schema.$id}'`);
|
|
298
311
|
referenceMap.set(schema.$id, schema);
|
|
299
312
|
}
|
|
300
313
|
}
|
package/compiler/index.d.ts
CHANGED
package/compiler/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sinclair/typebox",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.11",
|
|
4
4
|
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"json-schema",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"format": "hammer task format",
|
|
22
22
|
"start": "hammer task start",
|
|
23
23
|
"test": "hammer task test",
|
|
24
|
+
"benchmark": "hammer task benchmark",
|
|
24
25
|
"build": "hammer task build",
|
|
25
26
|
"publish": "hammer task publish"
|
|
26
27
|
},
|
package/readme.md
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
<br />
|
|
10
10
|
<br />
|
|
11
11
|
|
|
12
|
-
[](https://badge.fury.io/js/%40sinclair%2Ftypebox)
|
|
12
|
+
[](https://badge.fury.io/js/%40sinclair%2Ftypebox)
|
|
13
|
+
[](https://www.npmjs.com/package/%40sinclair%2Ftypebox)
|
|
14
|
+
[](https://github.com/sinclairzx81/typebox/actions)
|
|
13
15
|
|
|
14
16
|
</div>
|
|
15
17
|
|
|
@@ -339,7 +341,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
|
|
|
339
341
|
```
|
|
340
342
|
<a name="Modifiers"></a>
|
|
341
343
|
|
|
342
|
-
|
|
344
|
+
## Modifiers
|
|
343
345
|
|
|
344
346
|
TypeBox provides modifiers that can be applied to an objects properties. This allows for `optional` and `readonly` to be applied to that property. The following table illustates how they map between TypeScript and JSON Schema.
|
|
345
347
|
|
|
@@ -383,7 +385,7 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
|
|
|
383
385
|
|
|
384
386
|
<a name="Options"></a>
|
|
385
387
|
|
|
386
|
-
|
|
388
|
+
## Options
|
|
387
389
|
|
|
388
390
|
You can pass additional JSON schema options on the last argument of any given type. The following are some examples.
|
|
389
391
|
|
|
@@ -400,7 +402,7 @@ const T = Type.Array(Type.Integer(), { minItems: 5 })
|
|
|
400
402
|
|
|
401
403
|
<a name="Extended-Types"></a>
|
|
402
404
|
|
|
403
|
-
|
|
405
|
+
## Extended Types
|
|
404
406
|
|
|
405
407
|
In addition to JSON schema types, TypeBox provides several extended types that allow for `function` and `constructor` types to be composed. These additional types are not valid JSON Schema and will not validate using typical JSON Schema validation. However, these types can be used to frame JSON schema and describe callable interfaces that may receive JSON validated data. These types are as follows.
|
|
406
408
|
|
|
@@ -464,9 +466,9 @@ In addition to JSON schema types, TypeBox provides several extended types that a
|
|
|
464
466
|
|
|
465
467
|
<a name="Reference-Types"></a>
|
|
466
468
|
|
|
467
|
-
|
|
469
|
+
## Reference Types
|
|
468
470
|
|
|
469
|
-
|
|
471
|
+
Use `Type.Ref(...)` to create referenced types. The target type must specify an `$id`.
|
|
470
472
|
|
|
471
473
|
```typescript
|
|
472
474
|
const T = Type.String({ $id: 'T' }) // const T = {
|
|
@@ -481,13 +483,13 @@ const R = Type.Ref(T) // const R = {
|
|
|
481
483
|
|
|
482
484
|
<a name="Recursive-Types"></a>
|
|
483
485
|
|
|
484
|
-
|
|
486
|
+
## Recursive Types
|
|
485
487
|
|
|
486
|
-
|
|
488
|
+
Use `Type.Recursive(...)` to create recursive types.
|
|
487
489
|
|
|
488
490
|
```typescript
|
|
489
491
|
const Node = Type.Recursive(Node => Type.Object({ // const Node = {
|
|
490
|
-
id:
|
|
492
|
+
id: Type.String(), // $id: "Node",
|
|
491
493
|
nodes: Type.Array(Node), // type: "object",
|
|
492
494
|
}), { $id: 'Node' }) // properties: {
|
|
493
495
|
// id: {
|
|
@@ -508,19 +510,20 @@ const Node = Type.Recursive(Node => Type.Object({ // const Node = {
|
|
|
508
510
|
|
|
509
511
|
type Node = Static<typeof Node> // type Node = {
|
|
510
512
|
// id: string
|
|
511
|
-
// nodes: ...
|
|
513
|
+
// nodes: ...[]
|
|
512
514
|
// }
|
|
513
515
|
|
|
514
|
-
function
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
516
|
+
function test(node: Node) {
|
|
517
|
+
const id = node.nodes[0].nodes[0].nodes[0] // id is string
|
|
518
|
+
.nodes[0].nodes[0].nodes[0]
|
|
519
|
+
.nodes[0].nodes[0].nodes[0]
|
|
520
|
+
.id
|
|
518
521
|
}
|
|
519
522
|
```
|
|
520
523
|
|
|
521
524
|
<a name="Generic-Types"></a>
|
|
522
525
|
|
|
523
|
-
|
|
526
|
+
## Generic Types
|
|
524
527
|
|
|
525
528
|
Generic types can be created using functions. The following creates a generic `Nullable<T>` type.
|
|
526
529
|
|
|
@@ -552,9 +555,9 @@ type U = Static<typeof U> // type U = number | null
|
|
|
552
555
|
|
|
553
556
|
<a name="Unsafe-Types"></a>
|
|
554
557
|
|
|
555
|
-
|
|
558
|
+
## Unsafe Types
|
|
556
559
|
|
|
557
|
-
|
|
560
|
+
Use `Type.Unsafe(...)` to create custom schemas with user defined inference rules.
|
|
558
561
|
|
|
559
562
|
```typescript
|
|
560
563
|
const T = Type.Unsafe<string>({ type: 'number' }) // const T = {
|
|
@@ -564,7 +567,7 @@ const T = Type.Unsafe<string>({ type: 'number' }) // const T = {
|
|
|
564
567
|
type T = Static<typeof T> // type T = string
|
|
565
568
|
```
|
|
566
569
|
|
|
567
|
-
The `Type.Unsafe(...)` function can be used
|
|
570
|
+
The `Type.Unsafe(...)` function can be used to create schemas for validators that require specific schema representations. An example of this would be OpenAPI's `nullable` and `enum` schemas which are not provided by TypeBox. The following demonstrates using `Type.Unsafe(...)` to create these types.
|
|
568
571
|
|
|
569
572
|
```typescript
|
|
570
573
|
import { Type, Static, TSchema } from '@sinclair/typebox'
|
|
@@ -605,9 +608,9 @@ type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
|
|
|
605
608
|
|
|
606
609
|
<a name="Values"></a>
|
|
607
610
|
|
|
608
|
-
|
|
611
|
+
## Values
|
|
609
612
|
|
|
610
|
-
|
|
613
|
+
Use `Value.Create(...)` to generate values from types.
|
|
611
614
|
|
|
612
615
|
```typescript
|
|
613
616
|
import { Value } from '@sinclair/typebox/value'
|
|
@@ -623,8 +626,7 @@ const V = Value.Create(T) // const V = {
|
|
|
623
626
|
// y: 0,
|
|
624
627
|
// }
|
|
625
628
|
```
|
|
626
|
-
|
|
627
|
-
|
|
629
|
+
Use `Value.Cast(...)` to cast a value into a given type. This functionality can be helpful in data migrations.
|
|
628
630
|
```typescript
|
|
629
631
|
import { Value } from '@sinclair/typebox/value'
|
|
630
632
|
import { Type } from '@sinclair/typebox'
|
|
@@ -643,28 +645,26 @@ const C = Value.Cast(T, { x: 1, y: 2, z: 3 }) // const C = { x: 1, y: 2
|
|
|
643
645
|
|
|
644
646
|
<a name="Guards"></a>
|
|
645
647
|
|
|
646
|
-
|
|
648
|
+
## Guards
|
|
647
649
|
|
|
648
|
-
|
|
650
|
+
Use `TypeGuard.TSchema(...)` to check if a type is a valid TypeBox type. This functionality can be useful when reflecting types.
|
|
649
651
|
|
|
650
652
|
```typescript
|
|
651
653
|
import { TypeGuard } from '@sinclair/typebox/guard'
|
|
652
654
|
import { Type } from '@sinclair/typebox'
|
|
653
655
|
|
|
654
|
-
const T
|
|
655
|
-
|
|
656
|
-
const { type } = T // unsafe: type is any
|
|
656
|
+
const T = Type.String()
|
|
657
657
|
|
|
658
658
|
if(TypeGuard.TString(T)) {
|
|
659
659
|
|
|
660
|
-
|
|
660
|
+
// T is TString
|
|
661
661
|
}
|
|
662
662
|
|
|
663
663
|
```
|
|
664
664
|
|
|
665
665
|
<a name="Strict"></a>
|
|
666
666
|
|
|
667
|
-
|
|
667
|
+
## Strict
|
|
668
668
|
|
|
669
669
|
TypeBox schemas contain the `Kind` and `Modifier` symbol properties. These properties are provided to enable runtime type reflection on schemas, as well as helping TypeBox internally compose types. These properties are not strictly valid JSON schema; so in some cases it may be desirable to omit them. TypeBox provides a `Type.Strict()` function that will omit these properties if necessary.
|
|
670
670
|
|
|
@@ -693,7 +693,7 @@ const U = Type.Strict(T) // const U = {
|
|
|
693
693
|
|
|
694
694
|
<a name="Validation"></a>
|
|
695
695
|
|
|
696
|
-
|
|
696
|
+
## Validation
|
|
697
697
|
|
|
698
698
|
TypeBox schemas target JSON Schema draft 6 so any validator capable of draft 6 should be fine. A good library to use for validation in JavaScript environments is [AJV](https://www.npmjs.com/package/ajv). The following example shows setting up AJV 7 to work with TypeBox.
|
|
699
699
|
|
|
@@ -745,7 +745,7 @@ const T = Type.Object({
|
|
|
745
745
|
x: Type.Number(),
|
|
746
746
|
y: Type.Number(),
|
|
747
747
|
z: Type.Number(),
|
|
748
|
-
}
|
|
748
|
+
})
|
|
749
749
|
|
|
750
750
|
//--------------------------------------------------------------------------------------------
|
|
751
751
|
//
|
|
@@ -753,73 +753,101 @@ const T = Type.Object({
|
|
|
753
753
|
//
|
|
754
754
|
//--------------------------------------------------------------------------------------------
|
|
755
755
|
|
|
756
|
-
const
|
|
757
|
-
x: 1,
|
|
758
|
-
y: 2,
|
|
759
|
-
z: 3
|
|
760
|
-
}) // -> true
|
|
756
|
+
const R = ajv.validate(T, { x: 1, y: 2, z: 3 }) // const R = true
|
|
761
757
|
```
|
|
762
758
|
|
|
763
759
|
Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV.
|
|
764
760
|
|
|
765
761
|
<a name="Compiler"></a>
|
|
766
762
|
|
|
767
|
-
|
|
763
|
+
## Compiler
|
|
764
|
+
|
|
765
|
+
TypeBox provides a high performance runtime type checker that can be used in applications that need extremely fast message validation. This type checker is optimized for TypeBox types whose schematics are known in advance. If defining custom schemas with `Type.Unsafe<T>`; please consider AJV.
|
|
768
766
|
|
|
769
|
-
|
|
767
|
+
The following demonstrates its use.
|
|
770
768
|
|
|
771
769
|
```typescript
|
|
772
770
|
import { TypeCompiler } from '@sinclair/typebox/compiler'
|
|
773
771
|
import { Type } from '@sinclair/typebox'
|
|
774
772
|
|
|
775
|
-
const
|
|
776
|
-
x: Type.Number(),
|
|
777
|
-
y: Type.Number(),
|
|
778
|
-
z: Type.Number()
|
|
779
|
-
})
|
|
780
|
-
|
|
781
|
-
const C = TypeCompiler.Compile(T)
|
|
773
|
+
const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck<TObject<{
|
|
774
|
+
x: Type.Number(), // x: TNumber;
|
|
775
|
+
y: Type.Number(), // y: TNumber;
|
|
776
|
+
z: Type.Number() // z: TNumber;
|
|
777
|
+
})) // }>>
|
|
782
778
|
|
|
783
|
-
const
|
|
784
|
-
x: 1,
|
|
785
|
-
y: 2,
|
|
786
|
-
z: 3
|
|
787
|
-
}) // -> true
|
|
779
|
+
const R = C.Check({ x: 1, y: 2, z: 3 }) // const R = true
|
|
788
780
|
```
|
|
789
|
-
|
|
781
|
+
|
|
782
|
+
Validation errors can be read with the `Errors(...)` function.
|
|
783
|
+
|
|
790
784
|
```typescript
|
|
791
|
-
const C = TypeCompiler.Compile(Type.Object({
|
|
792
|
-
x: Type.Number(),
|
|
793
|
-
y: Type.Number(),
|
|
794
|
-
z: Type.Number()
|
|
795
|
-
}))
|
|
785
|
+
const C = TypeCompiler.Compile(Type.Object({ // const C: TypeCheck<TObject<{
|
|
786
|
+
x: Type.Number(), // x: TNumber;
|
|
787
|
+
y: Type.Number(), // y: TNumber;
|
|
788
|
+
z: Type.Number() // z: TNumber;
|
|
789
|
+
})) // }>>
|
|
796
790
|
|
|
797
|
-
const
|
|
791
|
+
const value = { ... }
|
|
798
792
|
|
|
799
|
-
if(!C.Check(
|
|
800
|
-
for(const
|
|
801
|
-
console.log(
|
|
793
|
+
if(!C.Check(value)) {
|
|
794
|
+
for(const { message } of C.Errors(value)) {
|
|
795
|
+
console.log(message)
|
|
802
796
|
}
|
|
803
797
|
}
|
|
804
798
|
```
|
|
805
|
-
|
|
799
|
+
|
|
800
|
+
Compiled routines can be inspected with the `.Code()` function.
|
|
806
801
|
|
|
807
802
|
```typescript
|
|
808
|
-
const C = TypeCompiler.Compile(Type.String())
|
|
803
|
+
const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck<TString>
|
|
809
804
|
|
|
810
|
-
console.log(C.Code())
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
//
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
805
|
+
console.log(C.Code()) // return function check(value) {
|
|
806
|
+
// return (
|
|
807
|
+
// (typeof value === 'string')
|
|
808
|
+
// )
|
|
809
|
+
// }
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### Benchmarks
|
|
813
|
+
|
|
814
|
+
The following table shows comparative benchmarks between TypeBox and Ajv. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`.
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
┌──────────────────┬────────────┬──────────┬──────────┬─────────────┐
|
|
818
|
+
│ (index) │ Iterations │ Ajv │ TypeBox │ Performance │
|
|
819
|
+
├──────────────────┼────────────┼──────────┼──────────┼─────────────┤
|
|
820
|
+
│ RegEx │ 16000000 │ '681ms' │ '510ms' │ '+33%' │
|
|
821
|
+
│ ObjectA │ 16000000 │ '427ms' │ '300ms' │ '+42%' │
|
|
822
|
+
│ ObjectB │ 16000000 │ '680ms' │ '458ms' │ '+48%' │
|
|
823
|
+
│ Tuple │ 16000000 │ '286ms' │ '190ms' │ '+50%' │
|
|
824
|
+
│ Union │ 16000000 │ '300ms' │ '207ms' │ '+44%' │
|
|
825
|
+
│ Recursive │ 16000000 │ '5330ms' │ '1994ms' │ '+167%' │
|
|
826
|
+
│ Vector4 │ 16000000 │ '292ms' │ '168ms' │ '+73%' │
|
|
827
|
+
│ Matrix4 │ 16000000 │ '586ms' │ '382ms' │ '+53%' │
|
|
828
|
+
│ Literal<String> │ 16000000 │ '251ms' │ '152ms' │ '+65%' │
|
|
829
|
+
│ Literal<Number> │ 16000000 │ '241ms' │ '147ms' │ '+63%' │
|
|
830
|
+
│ Literal<Boolean> │ 16000000 │ '239ms' │ '152ms' │ '+57%' │
|
|
831
|
+
│ Array<Number> │ 16000000 │ '432ms' │ '235ms' │ '+83%' │
|
|
832
|
+
│ Array<String> │ 16000000 │ '430ms' │ '300ms' │ '+43%' │
|
|
833
|
+
│ Array<Boolean> │ 16000000 │ '490ms' │ '354ms' │ '+38%' │
|
|
834
|
+
│ Array<ObjectA> │ 16000000 │ '3537ms' │ '2107ms' │ '+67%' │
|
|
835
|
+
│ Array<ObjectB> │ 16000000 │ '6366ms' │ '4556ms' │ '+39%' │
|
|
836
|
+
│ Array<Tuple> │ 16000000 │ '1382ms' │ '1038ms' │ '+33%' │
|
|
837
|
+
│ Array<Vector4> │ 16000000 │ '1429ms' │ '767ms' │ '+86%' │
|
|
838
|
+
│ Array<Matrix4> │ 16000000 │ '6029ms' │ '4119ms' │ '+46%' │
|
|
839
|
+
│ Any │ 16000000 │ '232ms' │ '149ms' │ '+55%' │
|
|
840
|
+
│ Boolean │ 16000000 │ '233ms' │ '149ms' │ '+56%' │
|
|
841
|
+
│ Integer │ 16000000 │ '244ms' │ '154ms' │ '+58%' │
|
|
842
|
+
│ Null │ 16000000 │ '230ms' │ '149ms' │ '+54%' │
|
|
843
|
+
│ Number │ 16000000 │ '227ms' │ '145ms' │ '+56%' │
|
|
844
|
+
│ String │ 16000000 │ '230ms' │ '150ms' │ '+53%' │
|
|
845
|
+
│ Unknown │ 16000000 │ '231ms' │ '147ms' │ '+57%' │
|
|
846
|
+
└──────────────────┴────────────┴──────────┴──────────┴─────────────┘
|
|
819
847
|
```
|
|
820
848
|
|
|
821
849
|
<a name="Contribute"></a>
|
|
822
850
|
|
|
823
|
-
|
|
851
|
+
## Contribute
|
|
824
852
|
|
|
825
853
|
TypeBox is open to community contribution. Please ensure you submit an open issue before submitting your pull request. The TypeBox project preferences open community discussion prior to accepting new features.
|
package/typebox.js
CHANGED
|
@@ -227,7 +227,7 @@ class TypeBuilder {
|
|
|
227
227
|
/** Creates a reference schema */
|
|
228
228
|
Ref(schema, options = {}) {
|
|
229
229
|
if (schema.$id === undefined)
|
|
230
|
-
throw Error('
|
|
230
|
+
throw Error('Type.Ref: Referenced schema must specify an $id');
|
|
231
231
|
return this.Create({ ...options, [exports.Kind]: 'Ref', $ref: schema.$id });
|
|
232
232
|
}
|
|
233
233
|
/** Creates a string type from a regular expression */
|
package/value/cast.js
CHANGED
|
@@ -72,7 +72,6 @@ var UnionValueCast;
|
|
|
72
72
|
})(UnionValueCast || (UnionValueCast = {}));
|
|
73
73
|
var ValueCast;
|
|
74
74
|
(function (ValueCast) {
|
|
75
|
-
const ids = new Map();
|
|
76
75
|
function Any(schema, references, value) {
|
|
77
76
|
return check_1.ValueCheck.Check(schema, references, value) ? value : create_1.ValueCreate.Create(schema, references);
|
|
78
77
|
}
|
|
@@ -121,7 +120,6 @@ var ValueCast;
|
|
|
121
120
|
return value;
|
|
122
121
|
if (value === null || typeof value !== 'object')
|
|
123
122
|
return create_1.ValueCreate.Create(schema, references);
|
|
124
|
-
ids.set(schema.$id, schema);
|
|
125
123
|
const required = new Set(schema.required || []);
|
|
126
124
|
const result = {};
|
|
127
125
|
for (const [key, property] of globalThis.Object.entries(schema.properties)) {
|
|
@@ -148,7 +146,7 @@ var ValueCast;
|
|
|
148
146
|
return result;
|
|
149
147
|
}
|
|
150
148
|
function Recursive(schema, references, value) {
|
|
151
|
-
throw Error('Cannot
|
|
149
|
+
throw new Error('CastValue.Recursive: Cannot cast recursive schemas');
|
|
152
150
|
}
|
|
153
151
|
function Ref(schema, references, value) {
|
|
154
152
|
const reference = references.find((reference) => reference.$id === schema.$ref);
|
|
@@ -240,7 +238,7 @@ var ValueCast;
|
|
|
240
238
|
case 'Void':
|
|
241
239
|
return Void(anySchema, anyReferences, value);
|
|
242
240
|
default:
|
|
243
|
-
throw Error(`Unknown schema kind '${schema[Types.Kind]}'`);
|
|
241
|
+
throw Error(`ValueCast.Visit: Unknown schema kind '${schema[Types.Kind]}'`);
|
|
244
242
|
}
|
|
245
243
|
}
|
|
246
244
|
ValueCast.Visit = Visit;
|
package/value/check.js
CHANGED
|
@@ -38,6 +38,15 @@ var ValueCheck;
|
|
|
38
38
|
if (!globalThis.Array.isArray(value)) {
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
|
+
if (schema.minItems !== undefined && !(value.length >= schema.minItems)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (schema.maxItems !== undefined && !(value.length <= schema.maxItems)) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (schema.uniqueItems === true && !(new Set(value).size === value.length)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
41
50
|
return value.every((val) => Visit(schema.items, references, val));
|
|
42
51
|
}
|
|
43
52
|
function Boolean(schema, references, value) {
|
|
@@ -56,19 +65,19 @@ var ValueCheck;
|
|
|
56
65
|
if (!globalThis.Number.isInteger(value)) {
|
|
57
66
|
return false;
|
|
58
67
|
}
|
|
59
|
-
if (schema.multipleOf && !(value % schema.multipleOf === 0)) {
|
|
68
|
+
if (schema.multipleOf !== undefined && !(value % schema.multipleOf === 0)) {
|
|
60
69
|
return false;
|
|
61
70
|
}
|
|
62
|
-
if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) {
|
|
71
|
+
if (schema.exclusiveMinimum !== undefined && !(value > schema.exclusiveMinimum)) {
|
|
63
72
|
return false;
|
|
64
73
|
}
|
|
65
|
-
if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) {
|
|
74
|
+
if (schema.exclusiveMaximum !== undefined && !(value < schema.exclusiveMaximum)) {
|
|
66
75
|
return false;
|
|
67
76
|
}
|
|
68
|
-
if (schema.minimum && !(value >= schema.minimum)) {
|
|
77
|
+
if (schema.minimum !== undefined && !(value >= schema.minimum)) {
|
|
69
78
|
return false;
|
|
70
79
|
}
|
|
71
|
-
if (schema.maximum && !(value <= schema.maximum)) {
|
|
80
|
+
if (schema.maximum !== undefined && !(value <= schema.maximum)) {
|
|
72
81
|
return false;
|
|
73
82
|
}
|
|
74
83
|
return true;
|
|
@@ -176,6 +185,14 @@ var ValueCheck;
|
|
|
176
185
|
if (!(typeof value === 'string')) {
|
|
177
186
|
return false;
|
|
178
187
|
}
|
|
188
|
+
if (schema.minLength !== undefined) {
|
|
189
|
+
if (!(value.length >= schema.minLength))
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (schema.maxLength !== undefined) {
|
|
193
|
+
if (!(value.length <= schema.maxLength))
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
179
196
|
if (schema.pattern !== undefined) {
|
|
180
197
|
const regex = new RegExp(schema.pattern);
|
|
181
198
|
if (!regex.test(value))
|
|
@@ -273,7 +290,7 @@ var ValueCheck;
|
|
|
273
290
|
case 'Void':
|
|
274
291
|
return Void(anySchema, anyReferences, value);
|
|
275
292
|
default:
|
|
276
|
-
throw Error(`Unknown schema kind '${schema[Types.Kind]}'`);
|
|
293
|
+
throw new Error(`CheckValue: Unknown schema kind '${schema[Types.Kind]}'`);
|
|
277
294
|
}
|
|
278
295
|
}
|
|
279
296
|
// -------------------------------------------------------------------------
|
package/value/create.js
CHANGED
|
@@ -40,7 +40,10 @@ var ValueCreate;
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
function Array(schema, references) {
|
|
43
|
-
if (schema.default
|
|
43
|
+
if (schema.uniqueItems === true && schema.default === undefined) {
|
|
44
|
+
throw new Error('ValueCreate.Array: Arrays with uniqueItems require a default value');
|
|
45
|
+
}
|
|
46
|
+
else if (schema.default !== undefined) {
|
|
44
47
|
return schema.default;
|
|
45
48
|
}
|
|
46
49
|
else if (schema.minItems !== undefined) {
|
|
@@ -70,8 +73,8 @@ var ValueCreate;
|
|
|
70
73
|
return class {
|
|
71
74
|
constructor() {
|
|
72
75
|
for (const [key, val] of globalThis.Object.entries(value)) {
|
|
73
|
-
const
|
|
74
|
-
|
|
76
|
+
const self = this;
|
|
77
|
+
self[key] = val;
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
};
|
|
@@ -87,7 +90,7 @@ var ValueCreate;
|
|
|
87
90
|
return schema.default;
|
|
88
91
|
}
|
|
89
92
|
else if (schema.anyOf.length === 0) {
|
|
90
|
-
throw new Error('Cannot create default enum value as this enum has no items');
|
|
93
|
+
throw new Error('ValueCreate.Enum: Cannot create default enum value as this enum has no items');
|
|
91
94
|
}
|
|
92
95
|
else {
|
|
93
96
|
return schema.anyOf[0].const;
|
|
@@ -169,7 +172,7 @@ var ValueCreate;
|
|
|
169
172
|
return schema.default;
|
|
170
173
|
}
|
|
171
174
|
else {
|
|
172
|
-
throw new Error('
|
|
175
|
+
throw new Error('ValueCreate.Recursive: Recursive types require a default value');
|
|
173
176
|
}
|
|
174
177
|
}
|
|
175
178
|
function Ref(schema, references) {
|
|
@@ -197,7 +200,7 @@ var ValueCreate;
|
|
|
197
200
|
function String(schema, references) {
|
|
198
201
|
if (schema.pattern !== undefined) {
|
|
199
202
|
if (schema.default === undefined) {
|
|
200
|
-
throw Error('String types with patterns must specify a default value');
|
|
203
|
+
throw new Error('ValueCreate.String: String types with patterns must specify a default value');
|
|
201
204
|
}
|
|
202
205
|
else {
|
|
203
206
|
return schema.default;
|
|
@@ -207,6 +210,11 @@ var ValueCreate;
|
|
|
207
210
|
if (schema.default !== undefined) {
|
|
208
211
|
return schema.default;
|
|
209
212
|
}
|
|
213
|
+
else if (schema.minLength !== undefined) {
|
|
214
|
+
return globalThis.Array.from({ length: schema.minLength })
|
|
215
|
+
.map(() => '.')
|
|
216
|
+
.join('');
|
|
217
|
+
}
|
|
210
218
|
else {
|
|
211
219
|
return '';
|
|
212
220
|
}
|
|
@@ -231,7 +239,7 @@ var ValueCreate;
|
|
|
231
239
|
return schema.default;
|
|
232
240
|
}
|
|
233
241
|
else if (schema.anyOf.length === 0) {
|
|
234
|
-
throw Error('Cannot
|
|
242
|
+
throw new Error('ValueCreate.Union: Cannot create Union with zero variants');
|
|
235
243
|
}
|
|
236
244
|
else {
|
|
237
245
|
return ValueCreate.Create(schema.anyOf[0], references);
|
|
@@ -241,7 +249,7 @@ var ValueCreate;
|
|
|
241
249
|
if (schema.default !== undefined) {
|
|
242
250
|
return schema.default;
|
|
243
251
|
}
|
|
244
|
-
else if (schema.minByteLength) {
|
|
252
|
+
else if (schema.minByteLength !== undefined) {
|
|
245
253
|
return new globalThis.Uint8Array(schema.minByteLength);
|
|
246
254
|
}
|
|
247
255
|
else {
|
|
@@ -311,7 +319,7 @@ var ValueCreate;
|
|
|
311
319
|
case 'Void':
|
|
312
320
|
return Void(anySchema, anyReferences);
|
|
313
321
|
default:
|
|
314
|
-
throw Error(`Unknown schema kind '${schema[Types.Kind]}'`);
|
|
322
|
+
throw new Error(`ValueCreate.Visit: Unknown schema kind '${schema[Types.Kind]}'`);
|
|
315
323
|
}
|
|
316
324
|
}
|
|
317
325
|
ValueCreate.Visit = Visit;
|
package/value/errors.js
CHANGED
|
@@ -36,6 +36,15 @@ var ValueErrors;
|
|
|
36
36
|
if (!globalThis.Array.isArray(value)) {
|
|
37
37
|
return yield { schema, path, value, message: `Expected array` };
|
|
38
38
|
}
|
|
39
|
+
if (schema.minItems !== undefined && !(value.length >= schema.minItems)) {
|
|
40
|
+
yield { schema, path, value, message: `Expected array length to be greater or equal to ${schema.minItems}` };
|
|
41
|
+
}
|
|
42
|
+
if (schema.maxItems !== undefined && !(value.length <= schema.maxItems)) {
|
|
43
|
+
yield { schema, path, value, message: `Expected array length to be less or equal to ${schema.maxItems}` };
|
|
44
|
+
}
|
|
45
|
+
if (schema.uniqueItems === true && !(new Set(value).size === value.length)) {
|
|
46
|
+
yield { schema, path, value, message: `Expected array elements to be unique` };
|
|
47
|
+
}
|
|
39
48
|
for (let i = 0; i < value.length; i++) {
|
|
40
49
|
yield* Visit(schema.items, references, `${path}/${i}`, value[i]);
|
|
41
50
|
}
|
|
@@ -159,19 +168,25 @@ var ValueErrors;
|
|
|
159
168
|
function* Ref(schema, references, path, value) {
|
|
160
169
|
const reference = references.find((reference) => reference.$id === schema.$ref);
|
|
161
170
|
if (reference === undefined)
|
|
162
|
-
throw new Error(`
|
|
171
|
+
throw new Error(`ValueErrors.Ref: Cannot find schema with $id '${schema.$ref}'.`);
|
|
163
172
|
yield* Visit(reference, references, path, value);
|
|
164
173
|
}
|
|
165
174
|
function* Self(schema, references, path, value) {
|
|
166
175
|
const reference = references.find((reference) => reference.$id === schema.$ref);
|
|
167
176
|
if (reference === undefined)
|
|
168
|
-
throw new Error(`
|
|
177
|
+
throw new Error(`ValueErrors.Self: Cannot find schema with $id '${schema.$ref}'.`);
|
|
169
178
|
yield* Visit(reference, references, path, value);
|
|
170
179
|
}
|
|
171
180
|
function* String(schema, references, path, value) {
|
|
172
181
|
if (!(typeof value === 'string')) {
|
|
173
182
|
return yield { schema, path, value, message: 'Expected string' };
|
|
174
183
|
}
|
|
184
|
+
if (schema.minLength !== undefined && !(value.length >= schema.minLength)) {
|
|
185
|
+
yield { schema, path, value, message: `Expected string length greater or equal to ${schema.minLength}` };
|
|
186
|
+
}
|
|
187
|
+
if (schema.maxLength !== undefined && !(value.length <= schema.maxLength)) {
|
|
188
|
+
yield { schema, path, value, message: `Expected string length less or equal to ${schema.maxLength}` };
|
|
189
|
+
}
|
|
175
190
|
if (schema.pattern !== undefined) {
|
|
176
191
|
const regex = new RegExp(schema.pattern);
|
|
177
192
|
if (!regex.test(value)) {
|
|
@@ -280,7 +295,7 @@ var ValueErrors;
|
|
|
280
295
|
case 'Void':
|
|
281
296
|
return yield* Void(anySchema, anyReferences, path, value);
|
|
282
297
|
default:
|
|
283
|
-
throw Error(`Unknown schema kind '${schema[Types.Kind]}'`);
|
|
298
|
+
throw new Error(`ValueErrors: Unknown schema kind '${schema[Types.Kind]}'`);
|
|
284
299
|
}
|
|
285
300
|
}
|
|
286
301
|
function* Errors(schema, references, value) {
|
package/value/index.d.ts
CHANGED