@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.
@@ -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. */
@@ -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>;
@@ -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) {
@@ -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 expr = [...Visit(schema.items, `value`)].map((condition) => condition).join(' && ');
74
- 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}))`;
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. Failed for '${JSON.stringify(schema)}'`);
308
+ throw new Error(`TypeCompiler: Referenced schemas must specify an $id.`);
296
309
  if (referenceMap.has(schema.$id))
297
- throw Error(`Duplicate schema $id detected for '${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
  }
@@ -1,2 +1,2 @@
1
+ export type { ValueError } from '../value/errors';
1
2
  export * from './compiler';
2
- export * from '../value/errors';
package/compiler/index.js CHANGED
@@ -38,4 +38,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
38
38
  };
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  __exportStar(require("./compiler"), exports);
41
- __exportStar(require("../value/errors"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinclair/typebox",
3
- "version": "0.24.8",
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
- [![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 cases, you may need schema definitions that are not provided by TypeBox. In these scenarios, it's common to want to define your own schema and static type inference rules. The `Type.Unsafe(...)` function provides this functionality, allowing you to specify both schema representation and a static type to infer. Consider the following which defines a `number` schema, but will infer as a `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 used with function generics to create custom schema representations for validators requiring specific schema representations. An example of which would be OpenAPI's `nullable` and `string-enum` representations which are not provided by TypeBox by default. 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. 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
- ### 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 `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: 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,101 @@ 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
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
- 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.
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
+ })) // }>>
796
790
 
797
- const V = { } // invalid
791
+ const value = { ... }
798
792
 
799
- if(!C.Check(V)) {
800
- for(const error of C.Errors(V)) {
801
- console.log(error)
793
+ if(!C.Check(value)) {
794
+ for(const { message } of C.Errors(value)) {
795
+ console.log(message)
802
796
  }
803
797
  }
804
798
  ```
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.
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
- // outputs:
813
- //
814
- // return function check(value) {
815
- // return (
816
- // (typeof value === 'string')
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
- ### Contribute
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('Cannot create reference schema as target schema as has no $id');
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 patch recursive schemas');
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 !== 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) {
@@ -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 facade = this;
74
- facade[key] = val;
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('Rec types require a default value');
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 generate Union with empty set');
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(`CheckValue.Ref: Cannot find schema with $id '${schema.$ref}'.`);
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(`CheckValue.Ref: Cannot find schema with $id '${schema.$ref}'.`);
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
@@ -1 +1,2 @@
1
+ export type { ValueError } from './errors';
1
2
  export * from './value';