@sinclair/typebox 0.24.9 → 0.24.12

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.
@@ -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 additional;
6
+ private readonly references;
7
7
  private readonly checkFunc;
8
8
  private readonly code;
9
- constructor(schema: T, additional: Types.TSchema[], checkFunc: CheckFunction, code: string);
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. */
@@ -35,12 +35,12 @@ const Types = require("../typebox");
35
35
  // -------------------------------------------------------------------
36
36
  class TypeCheck {
37
37
  schema;
38
- additional;
38
+ references;
39
39
  checkFunc;
40
40
  code;
41
- constructor(schema, additional, checkFunc, code) {
41
+ constructor(schema, references, checkFunc, code) {
42
42
  this.schema = schema;
43
- this.additional = additional;
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.additional, value);
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) {
@@ -71,8 +71,14 @@ var TypeCompiler;
71
71
  yield '(true)';
72
72
  }
73
73
  function* Array(schema, value) {
74
- const expr = [...Visit(schema.items, `value`)].map((condition) => condition).join(' && ');
75
- yield `(Array.isArray(${value}) && ${value}.every(value => ${expr}))`;
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}))`;
76
82
  }
77
83
  function* Boolean(schema, value) {
78
84
  yield `(typeof ${value} === 'boolean')`;
@@ -85,15 +91,15 @@ var TypeCompiler;
85
91
  }
86
92
  function* Integer(schema, value) {
87
93
  yield `(typeof ${value} === 'number' && Number.isInteger(${value}))`;
88
- if (schema.multipleOf)
94
+ if (schema.multipleOf !== undefined)
89
95
  yield `(${value} % ${schema.multipleOf} === 0)`;
90
- if (schema.exclusiveMinimum)
96
+ if (schema.exclusiveMinimum !== undefined)
91
97
  yield `(${value} > ${schema.exclusiveMinimum})`;
92
- if (schema.exclusiveMaximum)
98
+ if (schema.exclusiveMaximum !== undefined)
93
99
  yield `(${value} < ${schema.exclusiveMaximum})`;
94
- if (schema.minimum)
100
+ if (schema.minimum !== undefined)
95
101
  yield `(${value} >= ${schema.minimum})`;
96
- if (schema.maximum)
102
+ if (schema.maximum !== undefined)
97
103
  yield `(${value} <= ${schema.maximum})`;
98
104
  }
99
105
  function* Literal(schema, value) {
@@ -109,15 +115,15 @@ var TypeCompiler;
109
115
  }
110
116
  function* Number(schema, value) {
111
117
  yield `(typeof ${value} === 'number')`;
112
- if (schema.multipleOf)
118
+ if (schema.multipleOf !== undefined)
113
119
  yield `(${value} % ${schema.multipleOf} === 0)`;
114
- if (schema.exclusiveMinimum)
120
+ if (schema.exclusiveMinimum !== undefined)
115
121
  yield `(${value} > ${schema.exclusiveMinimum})`;
116
- if (schema.exclusiveMaximum)
122
+ if (schema.exclusiveMaximum !== undefined)
117
123
  yield `(${value} < ${schema.exclusiveMaximum})`;
118
- if (schema.minimum)
124
+ if (schema.minimum !== undefined)
119
125
  yield `(${value} >= ${schema.minimum})`;
120
- if (schema.maximum)
126
+ if (schema.maximum !== undefined)
121
127
  yield `(${value} <= ${schema.maximum})`;
122
128
  }
123
129
  function* Object(schema, value) {
@@ -183,6 +189,12 @@ var TypeCompiler;
183
189
  }
184
190
  function* String(schema, value) {
185
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
+ }
186
198
  if (schema.pattern !== undefined) {
187
199
  const local = PushLocal(`const local = new RegExp('${schema.pattern}');`);
188
200
  yield `(${local}.test(${value}))`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinclair/typebox",
3
- "version": "0.24.9",
3
+ "version": "0.24.12",
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
  },
@@ -29,7 +30,7 @@
29
30
  "@types/chai": "^4.3.0",
30
31
  "@types/mocha": "^9.1.0",
31
32
  "@types/node": "^17.0.12",
32
- "ajv": "^8.9.0",
33
+ "ajv": "^8.11.0",
33
34
  "ajv-formats": "^2.1.1",
34
35
  "chai": "^4.3.5",
35
36
  "mocha": "^9.2.0",
package/readme.md CHANGED
@@ -9,7 +9,9 @@
9
9
  <br />
10
10
  <br />
11
11
 
12
- [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox) [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](https://github.com/sinclairzx81/typebox/actions)
12
+ [![npm version](https://badge.fury.io/js/%40sinclair%2Ftypebox.svg)](https://badge.fury.io/js/%40sinclair%2Ftypebox)
13
+ [![Downloads](https://img.shields.io/npm/dm/%40sinclair%2Ftypebox.svg)](https://www.npmjs.com/package/%40sinclair%2Ftypebox)
14
+ [![GitHub CI](https://github.com/sinclairzx81/typebox/workflows/GitHub%20CI/badge.svg)](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
- ### Modifiers
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
- ### Options
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
- ### Extended Types
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
- ### Reference Types
469
+ ## Reference Types
468
470
 
469
- Types can be referenced with `Type.Ref(...)`. To reference a type, the target type must specify an `$id`.
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
- ### Recursive Types
486
+ ## Recursive Types
485
487
 
486
- Recursive types can be created with the `Type.Recursive(...)` function.
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: Type.String(), // $id: "Node",
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 visit(node: Node) {
515
- for(const inner of node.nodes) {
516
- visit(inner)
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
- ### Generic Types
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
- ### Unsafe Types
558
+ ## Unsafe Types
556
559
 
557
- In some scenarios, you may need specific schemas not provided by TypeBox. In these cases, it's common to want to define a custom schema with custom static inference rules. The `Type.Unsafe(...)` function provides this functionality. This function enables one to specify both schema representation and a static type to infer. Consider the following which defines a `number` schema but infers as `string`.
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 combined with function generics to create user defined schemas for validators that need specific schema representations. An example of this might be the OpenAPI `nullable` and `string-enum` schema representations which are not provided by TypeBox. The following demonstrates creating these schemas using the `Type.Unsafe(...)` function.
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
- ### Values
611
+ ## Values
609
612
 
610
- TypeBox can create values from types. It creates reasonable defaults for each value which can overrided by specifying a `default` value.
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
- TypeBox also allows values to be upgraded to match the schematics of a given type. The `Value.Cast(...)` function can be used to upgrade a value into a target type while retaining as much information of the original value as possible. Casts are immutable operations.
627
-
629
+ Use `Value.Cast(...)` to cast a value into a given type.
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
- ### Guards
648
+ ## Guards
647
649
 
648
- If reflecting on TypeBox types, it can be helpful to test if a value matches a TypeBox schematic. This can be achieved using the TypeGuard namespace. The TypeGuard namespace offers exhaustive checks for each known TypeBox type.
650
+ Use a `TypeGuard` to test if a value meets a TypeBox type specification. Guards can be helpful 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: any = {} // T is any
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
- const { type } = T // safe: type is 'string'
660
+ // T is TString
661
661
  }
662
662
 
663
663
  ```
664
664
 
665
665
  <a name="Strict"></a>
666
666
 
667
- ### Strict
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
- ### Validation
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
- }, { additionalProperties: false })
748
+ })
749
749
 
750
750
  //--------------------------------------------------------------------------------------------
751
751
  //
@@ -753,73 +753,146 @@ const T = Type.Object({
753
753
  //
754
754
  //--------------------------------------------------------------------------------------------
755
755
 
756
- const OK = ajv.validate(T, {
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
- ### Compiler
763
+ ## Compiler
768
764
 
769
- TypeBox includes a specialized `TypeCompiler` that can be used as a runtime type checker in lieu of a JSON Schema validator. This compiler is optimized for high throughput Web Socket messaging and can perform better than AJV for some structural checks. Please note that this compiler is not fully JSON Schema compliant and is limited to known TypeBox types only. The `TypeCompiler` contains a `Compile(T)` function that returns a `TypeCheck<T>` object that can be used to test the validity of a value as well as obtain errors.
765
+ TypeBox provides an optional high performance runtime type checker that can be used in applications that require extremely fast validation. This type checker is optimized for TypeBox types only whose schematics are known in advance. If defining custom schemas with `Type.Unsafe<T>` please consider AJV.
766
+
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 T = Type.Object({
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 OK = C.Check({
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
- Errors can be obtained by calling the `Errors(...)` function. This function returns an iterator that may contain zero or more errors for the given value. For performance, you should only call `Errors(V)` if the `Check(V)` function returns `false`.
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
+ })) // }>>
790
+
791
+ const value = { }
792
+
793
+ const errors = [...C.Errors(value)] // const errors = [{
794
+ // schema: { type: 'number' },
795
+ // path: '/x',
796
+ // value: undefined,
797
+ // message: 'Expected number'
798
+ // }, {
799
+ // schema: { type: 'number' },
800
+ // path: '/y',
801
+ // value: undefined,
802
+ // message: 'Expected number'
803
+ // }, {
804
+ // schema: { type: 'number' },
805
+ // path: '/z',
806
+ // value: undefined,
807
+ // message: 'Expected number'
808
+ // }]
809
+ ```
796
810
 
797
- const V = { } // invalid
811
+ Compiled routines can be inspected with the `.Code()` function.
798
812
 
799
- if(!C.Check(V)) {
800
- for(const error of C.Errors(V)) {
801
- console.log(error)
802
- }
803
- }
813
+ ```typescript
814
+ const C = TypeCompiler.Compile(Type.String()) // const C: TypeCheck<TString>
815
+
816
+ console.log(C.Code()) // return function check(value) {
817
+ // return (
818
+ // (typeof value === 'string')
819
+ // )
820
+ // }
804
821
  ```
805
- The TypeCompiler generates JavaScript validation routines types that are evaluated at runtime. You can inspect the generated code by calling the `Code()` function of the `TypeCheck<T>` object.
822
+
823
+ ### Benchmarks
824
+
825
+ This project maintains benchmarks that measure TypeBox and AJV compile and validate performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. Results show for AJV version 8.11.0.
826
+
827
+ #### Validate
828
+
829
+ This benchmark measures overall validate performance. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/check.ts).
806
830
 
807
831
  ```typescript
808
- const C = TypeCompiler.Compile(Type.String())
832
+ ┌──────────────────┬────────────┬────────────┬────────────┬────────────────────────┐
833
+ │ (index) │ Iterations │ Ajv │ TypeBox │ Measured │
834
+ ├──────────────────┼────────────┼────────────┼────────────┼────────────────────────┤
835
+ │ Number │ 16000000 │ '74ms ' │ '66ms ' │ '1.12 x faster ' │
836
+ │ String │ 16000000 │ '277ms ' │ '154ms ' │ '1.80 x faster ' │
837
+ │ Boolean │ 16000000 │ '263ms ' │ '152ms ' │ '1.73 x faster ' │
838
+ │ Null │ 16000000 │ '275ms ' │ '153ms ' │ '1.80 x faster ' │
839
+ │ RegEx │ 16000000 │ '658ms ' │ '548ms ' │ '1.20 x faster ' │
840
+ │ ObjectA │ 16000000 │ '453ms ' │ '319ms ' │ '1.42 x faster ' │
841
+ │ ObjectB │ 16000000 │ '676ms ' │ '514ms ' │ '1.32 x faster ' │
842
+ │ Tuple │ 16000000 │ '320ms ' │ '193ms ' │ '1.66 x faster ' │
843
+ │ Union │ 16000000 │ '331ms ' │ '211ms ' │ '1.57 x faster ' │
844
+ │ Recursive │ 16000000 │ '6010ms ' │ '2368ms ' │ '2.54 x faster ' │
845
+ │ Vector4 │ 16000000 │ '313ms ' │ '168ms ' │ '1.86 x faster ' │
846
+ │ Matrix4 │ 16000000 │ '604ms ' │ '419ms ' │ '1.44 x faster ' │
847
+ │ Literal_String │ 16000000 │ '281ms ' │ '154ms ' │ '1.82 x faster ' │
848
+ │ Literal_Number │ 16000000 │ '268ms ' │ '150ms ' │ '1.79 x faster ' │
849
+ │ Literal_Boolean │ 16000000 │ '273ms ' │ '149ms ' │ '1.83 x faster ' │
850
+ │ Array_Number │ 16000000 │ '470ms ' │ '235ms ' │ '2.00 x faster ' │
851
+ │ Array_String │ 16000000 │ '462ms ' │ '303ms ' │ '1.52 x faster ' │
852
+ │ Array_Boolean │ 16000000 │ '527ms ' │ '352ms ' │ '1.50 x faster ' │
853
+ │ Array_ObjectA │ 16000000 │ '43852ms ' │ '28042ms ' │ '1.56 x faster ' │
854
+ │ Array_ObjectB │ 16000000 │ '48025ms ' │ '33205ms ' │ '1.45 x faster ' │
855
+ │ Array_Tuple │ 16000000 │ '1424ms ' │ '1095ms ' │ '1.30 x faster ' │
856
+ │ Array_Vector4 │ 16000000 │ '1495ms ' │ '808ms ' │ '1.85 x faster ' │
857
+ │ Array_Matrix4 │ 16000000 │ '6129ms ' │ '4969ms ' │ '1.23 x faster ' │
858
+ └──────────────────┴────────────┴────────────┴────────────┴────────────────────────┘
859
+ ```
809
860
 
810
- console.log(C.Code())
811
- //
812
- // outputs:
813
- //
814
- // return function check(value) {
815
- // return (
816
- // (typeof value === 'string')
817
- // )
818
- // }
861
+ #### Compile
862
+
863
+ This benchmark measures schema compilation time. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/compile.ts).
864
+
865
+ ```typescript
866
+ ┌──────────────────┬────────────┬────────────┬────────────┬────────────────────────┐
867
+ (index) │ Iterations │ Ajv │ TypeBox │ Measured │
868
+ ├──────────────────┼────────────┼────────────┼────────────┼────────────────────────┤
869
+ │ Number │ 2000 │ '400ms ' │ '8ms ' │ '50.00 x faster ' │
870
+ │ String │ 2000 │ '324ms ' │ '7ms ' │ '46.29 x faster ' │
871
+ │ Boolean │ 2000 │ '325ms ' │ '10ms ' │ '32.50 x faster ' │
872
+ │ Null │ 2000 │ '271ms ' │ '4ms ' │ '67.75 x faster ' │
873
+ │ RegEx │ 2000 │ '493ms ' │ '11ms ' │ '44.82 x faster ' │
874
+ │ ObjectA │ 2000 │ '2998ms ' │ '27ms ' │ '111.04 x faster ' │
875
+ │ ObjectB │ 2000 │ '3058ms ' │ '28ms ' │ '109.21 x faster ' │
876
+ │ Tuple │ 2000 │ '1306ms ' │ '19ms ' │ '68.74 x faster ' │
877
+ │ Union │ 2000 │ '1450ms ' │ '18ms ' │ '80.56 x faster ' │
878
+ │ Vector4 │ 2000 │ '1633ms ' │ '12ms ' │ '136.08 x faster ' │
879
+ │ Matrix4 │ 2000 │ '984ms ' │ '10ms ' │ '98.40 x faster ' │
880
+ │ Literal_String │ 2000 │ '376ms ' │ '5ms ' │ '75.20 x faster ' │
881
+ │ Literal_Number │ 2000 │ '396ms ' │ '7ms ' │ '56.57 x faster ' │
882
+ │ Literal_Boolean │ 2000 │ '383ms ' │ '3ms ' │ '127.67 x faster ' │
883
+ │ Array_Number │ 2000 │ '748ms ' │ '6ms ' │ '124.67 x faster ' │
884
+ │ Array_String │ 2000 │ '774ms ' │ '5ms ' │ '154.80 x faster ' │
885
+ │ Array_Boolean │ 2000 │ '814ms ' │ '8ms ' │ '101.75 x faster ' │
886
+ │ Array_ObjectA │ 2000 │ '3675ms ' │ '24ms ' │ '153.13 x faster ' │
887
+ │ Array_ObjectB │ 2000 │ '4364ms ' │ '30ms ' │ '145.47 x faster ' │
888
+ │ Array_Tuple │ 2000 │ '2236ms ' │ '13ms ' │ '172.00 x faster ' │
889
+ │ Array_Vector4 │ 2000 │ '1772ms ' │ '15ms ' │ '118.13 x faster ' │
890
+ │ Array_Matrix4 │ 2000 │ '1612ms ' │ '10ms ' │ '161.20 x faster ' │
891
+ └──────────────────┴────────────┴────────────┴────────────┴────────────────────────┘
819
892
  ```
820
893
 
821
894
  <a name="Contribute"></a>
822
895
 
823
- ### Contribute
896
+ ## Contribute
824
897
 
825
898
  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/value/cast.js CHANGED
@@ -238,7 +238,7 @@ var ValueCast;
238
238
  case 'Void':
239
239
  return Void(anySchema, anyReferences, value);
240
240
  default:
241
- throw Error(`Unknown schema kind '${schema[Types.Kind]}'`);
241
+ throw Error(`ValueCast.Visit: Unknown schema kind '${schema[Types.Kind]}'`);
242
242
  }
243
243
  }
244
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))
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 !== undefined) {
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) {
@@ -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;
@@ -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 new Error('ValueCreate: Cannot create Union with zero variants');
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 new Error(`ValueCreate: 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
  }
@@ -61,19 +70,19 @@ var ValueErrors;
61
70
  yield { schema, path, value, message: `Expected integer` };
62
71
  }
63
72
  if (schema.multipleOf && !(value % schema.multipleOf === 0)) {
64
- yield { schema, path, value, message: `Expected number to be a multiple of ${schema.multipleOf}` };
73
+ yield { schema, path, value, message: `Expected integer to be a multiple of ${schema.multipleOf}` };
65
74
  }
66
75
  if (schema.exclusiveMinimum && !(value > schema.exclusiveMinimum)) {
67
- yield { schema, path, value, message: `Expected number to be greater than ${schema.exclusiveMinimum}` };
76
+ yield { schema, path, value, message: `Expected integer to be greater than ${schema.exclusiveMinimum}` };
68
77
  }
69
78
  if (schema.exclusiveMaximum && !(value < schema.exclusiveMaximum)) {
70
- yield { schema, path, value, message: `Expected number to be less than ${schema.exclusiveMaximum}` };
79
+ yield { schema, path, value, message: `Expected integer to be less than ${schema.exclusiveMaximum}` };
71
80
  }
72
81
  if (schema.minimum && !(value >= schema.minimum)) {
73
- yield { schema, path, value, message: `Expected number to be greater or equal to ${schema.minimum}` };
82
+ yield { schema, path, value, message: `Expected integer to be greater or equal to ${schema.minimum}` };
74
83
  }
75
84
  if (schema.maximum && !(value <= schema.maximum)) {
76
- yield { schema, path, value, message: `Expected number to be less or equal to ${schema.maximum}` };
85
+ yield { schema, path, value, message: `Expected integer to be less or equal to ${schema.maximum}` };
77
86
  }
78
87
  }
79
88
  function* Literal(schema, references, path, value) {
@@ -172,6 +181,12 @@ var ValueErrors;
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)) {