@arrirpc/schema 0.69.2 → 0.71.0

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/README.md CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  A type builder and validation library for [Arri Type Definitions](/specifications/arri_type_definition.md). A lot of inspiration was taken from both [Typebox](https://github.com/sinclairzx81/typebox) and [Zod](https://github.com/colinhacks/zod) when designing this library.
4
4
 
5
+ This library also supports [standard-schema](https://github.com/standard-schema/standard-schema) meaning it can be used with any third-party library that accepts standard schema.
6
+
5
7
  ## Project Philosophy
6
8
 
7
9
  The goals of this project are as follows:
8
10
 
9
- - Portable type definitions
10
- - High performance validation, parsing, and serialization
11
- - Consistent error reporting for parsing and serialization errors
11
+ - Portable type definitions
12
+ - High performance validation, parsing, and serialization
13
+ - Consistent error reporting for parsing and serialization errors
12
14
 
13
15
  I am not looking to support every feature of Typescript's type system or even every possible representation of JSON. The goal is that the data models defined through this library can be used as a source of truth across multiple programming languages. Both JSON and Typescript have to be limited to accomplish this.
14
16
 
@@ -16,42 +18,42 @@ I am not looking to support every feature of Typescript's type system or even ev
16
18
 
17
19
  Originally this library was created as a way for building schemas for [Json Type Definition](https://jsontypedef.com/). However over time parts of the internal schema were modified to better suite the goals of Arri RPC. Some of these modifications include:
18
20
 
19
- - Adding support for 64-bit integers
20
- - Replacing the `additionalProperties` field with `strict` to allow for additional properties by default.
21
- - Restrict `ref` to only be used for recursive references.
21
+ - Adding support for 64-bit integers
22
+ - Replacing the `additionalProperties` field with `strict` to allow for additional properties by default.
23
+ - Restrict `ref` to only be used for recursive references.
22
24
 
23
25
  ## Table of Contents
24
26
 
25
- - [Installation](#installation)
26
- - [Basic Example](#basic-example)
27
- - [Usage With @arrirpc/server](#usage-with-arrirpcserver)
28
- - [Supported Types](#supported-types)
29
- - [Primitives](#primitives)
30
- - [Enums](#enums)
31
- - [Arrays / Lists](#arrays--lists)
32
- - [Objects](#objects)
33
- - [Records / Maps](#records--maps)
34
- - [Discriminated Unions](#discriminated-unions)
35
- - [Recursive Types](#recursive-types)
36
- - [Modifiers](#modifiers)
37
- - [Optional](#optional)
38
- - [Nullable](#nullable)
39
- - [Extend](#extend)
40
- - [Omit](#omit)
41
- - [Pick](#pick)
42
- - [Partial](#partial)
43
- - [Utilities](#utilities)
44
- - [Validate](#validate)
45
- - [Parse](#parse)
46
- - [Safe Parse](#safe-parse)
47
- - [Coerce](#coerce)
48
- - [Safe Coerce](#safe-coerce)
49
- - [Serialize](#serialize)
50
- - [Errors](#errors)
51
- - [Compiled Validators](#compiled-validators)
52
- - [Metadata](#metadata)
53
- - [Benchmarks](#benchmarks)
54
- - [Development](#development)
27
+ - [Installation](#installation)
28
+ - [Basic Example](#basic-example)
29
+ - [Usage With @arrirpc/server](#usage-with-arrirpcserver)
30
+ - [Supported Types](#supported-types)
31
+ - [Primitives](#primitives)
32
+ - [Enums](#enums)
33
+ - [Arrays / Lists](#arrays--lists)
34
+ - [Objects](#objects)
35
+ - [Records / Maps](#records--maps)
36
+ - [Discriminated Unions](#discriminated-unions)
37
+ - [Recursive Types](#recursive-types)
38
+ - [Modifiers](#modifiers)
39
+ - [Optional](#optional)
40
+ - [Nullable](#nullable)
41
+ - [Extend](#extend)
42
+ - [Omit](#omit)
43
+ - [Pick](#pick)
44
+ - [Partial](#partial)
45
+ - [Utilities](#utilities)
46
+ - [Validate](#validate)
47
+ - [Parse](#parse)
48
+ - [Safe Parse](#safe-parse)
49
+ - [Coerce](#coerce)
50
+ - [Safe Coerce](#safe-coerce)
51
+ - [Serialize](#serialize)
52
+ - [Errors](#errors)
53
+ - [Compiled Validators](#compiled-validators)
54
+ - [Metadata](#metadata)
55
+ - [Benchmarks](#benchmarks)
56
+ - [Development](#development)
55
57
 
56
58
  ## Installation
57
59
 
@@ -66,7 +68,7 @@ pnpm install @arrirpc/schema
66
68
  ## Basic Example
67
69
 
68
70
  ```ts
69
- import { a } from "@arrirpc/schema";
71
+ import { a } from '@arrirpc/schema';
70
72
 
71
73
  const User = a.object({
72
74
  id: a.string(),
@@ -81,12 +83,12 @@ a.parse(User, `{"id": "1", "name": "John Doe"}`);
81
83
  a.parse(User, `{"id": "1", "name": null}`);
82
84
 
83
85
  // returns true
84
- a.validate(User, { id: "1", name: "John Doe" });
86
+ a.validate(User, { id: '1', name: 'John Doe' });
85
87
  // returns false
86
- a.validate(User, { id: "1", name: null });
88
+ a.validate(User, { id: '1', name: null });
87
89
 
88
90
  // outputs valid json
89
- a.serialize(User, { id: "1", name: "John Doe" });
91
+ a.serialize(User, { id: '1', name: 'John Doe' });
90
92
  ```
91
93
 
92
94
  ## Usage With @arrirpc/server
@@ -94,8 +96,8 @@ a.serialize(User, { id: "1", name: "John Doe" });
94
96
  See [here](/languages/ts/ts-server/README.md) for full details.
95
97
 
96
98
  ```ts
97
- import { a } from "@arrirpc/schema";
98
- import { defineRpc } from "@arrirpc/server";
99
+ import { a } from '@arrirpc/schema';
100
+ import { defineRpc } from '@arrirpc/server';
99
101
 
100
102
  export default defineRpc({
101
103
  params: a.object({
@@ -141,11 +143,11 @@ Enum schemas allow you to specify a predefine list of accepted strings
141
143
  **Usage**
142
144
 
143
145
  ```ts
144
- const Status = a.enumerator(["ACTIVE", "INACTIVE", "UNKNOWN"]);
146
+ const Status = a.enumerator(['ACTIVE', 'INACTIVE', 'UNKNOWN']);
145
147
  type Status = a.infer<typeof Status>; // "ACTIVE" | "INACTIVE" | "UNKNOWN";
146
148
 
147
- a.validate(Status, "BLAH"); // false
148
- a.validate(Status, "ACTIVE"); // true
149
+ a.validate(Status, 'BLAH'); // false
150
+ a.validate(Status, 'ACTIVE'); // true
149
151
  ```
150
152
 
151
153
  **Outputted JTD**
@@ -165,7 +167,7 @@ const MyList = a.array(a.string());
165
167
  type MyList = a.infer<typeof MyList>; // string[];
166
168
 
167
169
  a.validate(MyList, [1, 2]); // false
168
- a.validate(MyList, ["hello", "world"]); // true
170
+ a.validate(MyList, ['hello', 'world']); // true
169
171
  ```
170
172
 
171
173
  **Outputted JTD**
@@ -191,12 +193,12 @@ const User = a.object({
191
193
  type User = a.infer<typeof User>; // { id: string; email: string; created: Date; }
192
194
 
193
195
  a.validate({
194
- id: "1",
195
- email: "johndoe@example.com",
196
+ id: '1',
197
+ email: 'johndoe@example.com',
196
198
  created: new Date(),
197
199
  }); // true
198
200
  a.validate({
199
- id: "1",
201
+ id: '1',
200
202
  email: null,
201
203
  created: new Date(),
202
204
  }); // false
@@ -237,10 +239,10 @@ const UserStrict = a.object(
237
239
  );
238
240
 
239
241
  a.parse(UserStrict, {
240
- id: "1",
241
- name: "johndoe",
242
+ id: '1',
243
+ name: 'johndoe',
242
244
  created: new Date(),
243
- bio: "my name is joe",
245
+ bio: 'my name is joe',
244
246
  }); // fails parsing because of the additional field "bio"
245
247
  ```
246
248
 
@@ -276,7 +278,7 @@ a.validate(R, {
276
278
  world: false,
277
279
  }); // true;
278
280
  a.validate(R, {
279
- hello: "world",
281
+ hello: 'world',
280
282
  }); // false;
281
283
  ```
282
284
 
@@ -295,7 +297,7 @@ a.validate(R, {
295
297
  **Usage**
296
298
 
297
299
  ```ts
298
- const Shape = a.discriminator("type", {
300
+ const Shape = a.discriminator('type', {
299
301
  RECTANGLE: a.object({
300
302
  width: a.float32(),
301
303
  height: a.float32(),
@@ -307,20 +309,20 @@ const Shape = a.discriminator("type", {
307
309
  type Shape = a.infer<typeof Shape>; // { type: "RECTANGLE"; width: number; height: number; } | { type: "CIRCLE"; radius: number; }
308
310
 
309
311
  // Infer specific sub types of the union
310
- type ShapeTypeRectangle = a.inferSubType<Shape, "type", "RECTANGLE">; // { type "RECTANGLE"; width: number; height: number; };
311
- type ShapeTypeCircle = a.inferSubType<Shape, "type", "CIRCLE">; // { type "CIRCLE"; radius: number; }
312
+ type ShapeTypeRectangle = a.inferSubType<Shape, 'type', 'RECTANGLE'>; // { type "RECTANGLE"; width: number; height: number; };
313
+ type ShapeTypeCircle = a.inferSubType<Shape, 'type', 'CIRCLE'>; // { type "CIRCLE"; radius: number; }
312
314
 
313
315
  a.validate(Shape, {
314
- type: "RECTANGLE",
316
+ type: 'RECTANGLE',
315
317
  width: 1,
316
318
  height: 1.5,
317
319
  }); // true
318
320
  a.validate(Shape, {
319
- type: "CIRCLE",
321
+ type: 'CIRCLE',
320
322
  radius: 5,
321
323
  }); // true
322
324
  a.validate(Shape, {
323
- type: "CIRCLE",
325
+ type: 'CIRCLE',
324
326
  width: 1,
325
327
  height: 1.5,
326
328
  }); // false
@@ -385,7 +387,7 @@ const BinaryTree = a.recursive<BinaryTree>(
385
387
  right: a.nullable(self),
386
388
  }),
387
389
  {
388
- id: "BinaryTree",
390
+ id: 'BinaryTree',
389
391
  },
390
392
  );
391
393
 
@@ -506,7 +508,7 @@ const A = a.object(
506
508
  a: a.string(),
507
509
  b: a.float32(),
508
510
  },
509
- { id: "A" },
511
+ { id: 'A' },
510
512
  );
511
513
  console.log(A.metadata.id); // "A"
512
514
 
@@ -545,7 +547,7 @@ const A = a.object({
545
547
  });
546
548
  // { a: string; b: number; }
547
549
 
548
- const B = a.omit(A, ["a"]);
550
+ const B = a.omit(A, ['a']);
549
551
  // { b: number; }
550
552
  ```
551
553
 
@@ -561,7 +563,7 @@ const A = a.object({
561
563
  });
562
564
  // { a: string; b: number; c: Date; }
563
565
 
564
- const B = a.pick(A, ["a", "c"]);
566
+ const B = a.pick(A, ['a', 'c']);
565
567
  // { a: string; c: Date; }
566
568
  ```
567
569
 
@@ -593,7 +595,7 @@ const User = a.object({
593
595
  name: a.string(),
594
596
  });
595
597
  a.validate(User, true); // false
596
- a.validate(User, { id: "1", name: "john doe" }); // true
598
+ a.validate(User, { id: '1', name: 'john doe' }); // true
597
599
 
598
600
  if (a.validate(User, someInput)) {
599
601
  console.log(someInput.id); // intellisense works here
@@ -644,9 +646,9 @@ const A = a.object({
644
646
  });
645
647
 
646
648
  a.coerce(A, {
647
- a: "1",
648
- b: "true",
649
- c: "500.24",
649
+ a: '1',
650
+ b: 'true',
651
+ c: '500.24',
650
652
  });
651
653
  // { a: "1", b: true, c: 500.24 };
652
654
  ```
@@ -681,7 +683,7 @@ const User = a.object({
681
683
  name: a.string(),
682
684
  });
683
685
 
684
- a.serialize(User, { id: "1", name: "john doe" });
686
+ a.serialize(User, { id: '1', name: 'john doe' });
685
687
  // {"id":"1","name":"john doe"}
686
688
  ```
687
689
 
@@ -697,7 +699,7 @@ const User = a.object({
697
699
  date: a.timestamp(),
698
700
  });
699
701
 
700
- a.errors(User, { id: 1, date: "hello world" });
702
+ a.errors(User, { id: 1, date: 'hello world' });
701
703
  /**
702
704
  * [
703
705
  * {
@@ -730,7 +732,7 @@ const $$User = a.compile(User);
730
732
 
731
733
  $$User.validate(someInput);
732
734
  $$User.parse(someJson);
733
- $$User.serialize({ id: "1", email: null, created: new Date() });
735
+ $$User.serialize({ id: '1', email: null, created: new Date() });
734
736
  ```
735
737
 
736
738
  In most cases, the compiled validators will be much faster than the standard utilities. However there is some overhead with compiling the schemas so ideally each validator would be compiled once. Additionally the resulting methods make use of eval so they can only be used in an environment that you control such as a backend server. They WILL NOT work in a browser environment.
@@ -747,9 +749,9 @@ $$User.compiledCode.serialize; // the generated serialization code
747
749
 
748
750
  Metadata is used during cross-language code generation. Arri schemas allow you to specify the following metadata fields:
749
751
 
750
- - id - Will be used as the type name in any arri client generators
751
- - description - Will be added as a description comment above any generated types
752
- - isDeprecated - Will mark any generated code with the deprecation annotation of target language
752
+ - id - Will be used as the type name in any arri client generators
753
+ - description - Will be added as a description comment above any generated types
754
+ - isDeprecated - Will mark any generated code with the deprecation annotation of target language
753
755
 
754
756
  ### Examples
755
757
 
@@ -764,8 +766,8 @@ const BookSchema = a.object(
764
766
  publishDate: a.timestamp(),
765
767
  },
766
768
  {
767
- id: "Book",
768
- description: "This is a book",
769
+ id: 'Book',
770
+ description: 'This is a book',
769
771
  },
770
772
  );
771
773
  ```
@@ -831,14 +833,14 @@ Because IDs are really important for producing concise type names. Arri validate
831
833
 
832
834
  ```ts
833
835
  // ID will be set to "Book"
834
- const BookSchema = a.object("Book", {
836
+ const BookSchema = a.object('Book', {
835
837
  title: a.string(),
836
838
  author: a.string(),
837
839
  publishDate: a.timestamp(),
838
840
  });
839
841
 
840
842
  // ID will be set to "Message"
841
- const MessageSchema = a.discriminator("Message", "type", {
843
+ const MessageSchema = a.discriminator('Message', 'type', {
842
844
  TEXT: a.object({
843
845
  userId: a.string(),
844
846
  content: a.string(),
@@ -850,7 +852,7 @@ const MessageSchema = a.discriminator("Message", "type", {
850
852
  });
851
853
 
852
854
  // ID will be set to "BTree"
853
- const BinaryTreeSchema = a.recursive("BTree", (self) =>
855
+ const BinaryTreeSchema = a.recursive('BTree', (self) =>
854
856
  a.object({
855
857
  left: a.nullable(self),
856
858
  right: a.nullable(self),
@@ -862,7 +864,7 @@ const BinaryTreeSchema = a.recursive("BTree", (self) =>
862
864
 
863
865
  ## Benchmarks
864
866
 
865
- _Last Updated: 2024-03-19_
867
+ _Last Updated: 2024-12-27_
866
868
 
867
869
  All benchmarks were run on my personal desktop. You can view the methodology used in [./benchmarks/src](./benchmark/src).
868
870
 
@@ -907,44 +909,44 @@ The following data was used in these benchmarks. Relevant schemas were created i
907
909
 
908
910
  #### Validation
909
911
 
910
- | Library | op/s |
911
- | ---------------------------- | ----------- |
912
- | **Arri (Compiled)** | 122,753,978 |
913
- | Typebox (Compiled) | 63,131,651 |
914
- | Ajv -JTD (Compiled) | 37,814,896 |
915
- | Ajv - JTD | 29,402,413 |
916
- | Ajv - JSON Schema (Compiled) | 12,003,432 |
917
- | Ajv -JSON Schema | 10,057,501 |
918
- | **Arri** | 2,131,823 |
919
- | Typebox | 93,5599 |
920
- | Zod | 52,1357 |
912
+ | Library | op/s |
913
+ | ---------------------------- | -------------- |
914
+ | **Arri (Compiled)** | **51,149,033** |
915
+ | Typebox (Compiled) | 47,826,755 |
916
+ | Ajv -JTD (Compiled) | 32,001,140 |
917
+ | Ajv - JTD | 12,731,224 |
918
+ | Ajv - JSON Schema (Compiled) | 12,371,095 |
919
+ | Ajv -JSON Schema | 8,811,605 |
920
+ | **Arri** | **2,151,961** |
921
+ | Typebox | 1,024,386 |
922
+ | Zod | 471,700 |
921
923
 
922
924
  #### Parsing
923
925
 
924
- | Library | op/s |
925
- | ------------------- | ------- |
926
- | JSON.parse | 749,534 |
927
- | **Arri (Compiled)** | 728,382 |
928
- | **Arri** | 378,175 |
929
- | Ajv -JTD (Compiled) | 241,107 |
926
+ | Library | op/s |
927
+ | ------------------- | ----------- |
928
+ | JSON.parse | 785,364 |
929
+ | **Arri (Compiled)** | **736,957** |
930
+ | **Arri** | **378,841** |
931
+ | Ajv -JTD (Compiled) | 230,124 |
930
932
 
931
933
  #### Serialization
932
934
 
933
- | Library | op/s |
934
- | ------------------------------------------ | --------- |
935
- | **Arri (Compiled)** | 4,272,430 |
936
- | **Arri (Compiled) Validate and Serialize** | 3,846,453 |
937
- | Ajv - JTD (Compiled) | 2,012,894 |
938
- | JSON.stringify | 938,289 |
939
- | Arri | 481,985 |
935
+ | Library | op/s |
936
+ | ------------------------------------------ | ------------- |
937
+ | **Arri (Compiled)** | **4,131,382** |
938
+ | **Arri (Compiled) Validate and Serialize** | **3,710,794** |
939
+ | Ajv - JTD (Compiled) | 2,066,041 |
940
+ | JSON.stringify | 1,599,758 |
941
+ | Arri | 467,417 |
940
942
 
941
943
  #### Coercion
942
944
 
943
- | Library | op/s |
944
- | -------- | ------- |
945
- | **Arri** | 818,963 |
946
- | Zod | 466,092 |
947
- | Typebox | 209,363 |
945
+ | Library | op/s |
946
+ | -------- | ----------- |
947
+ | **Arri** | **820,103** |
948
+ | Zod | 465,466 |
949
+ | Typebox | 405,292 |
948
950
 
949
951
  ### Integers
950
952
 
@@ -952,44 +954,44 @@ The following benchmarks measure how quickly each library operates on a single i
952
954
 
953
955
  #### Validation
954
956
 
955
- | Library | op/s |
956
- | ---------------------------- | ----------- |
957
- | Ajv - JSON Schema (Compiled) | 329,332,736 |
958
- | **Arri (Compiled)** | 201,644,167 |
959
- | **Arri** | 186,634,732 |
960
- | Ajv - JTD (Compiled) | 151,044,902 |
961
- | Typebox (Compiled) | 110,692,029 |
962
- | Ajv - JTD | 48,200,004 |
963
- | Ajv - JSON Schema | 47,840,571 |
964
- | Typebox | 42,363,980 |
965
- | Zod | 1,266,268 |
957
+ | Library | op/s |
958
+ | ---------------------------- | --------------- |
959
+ | Typebox (Compiled) | 196,718,452 |
960
+ | **Arri (Compiled)** | **190,853,038** |
961
+ | Ajv - JSON Schema (Compiled) | 189,905,832 |
962
+ | Ajv - JTD (Compiled) | 143,619,126 |
963
+ | **Arri** | **89,428,888** |
964
+ | Typebox | 48,408,435 |
965
+ | Ajv - JSON Schema | 36,560,467 |
966
+ | Ajv - JTD | 35,639,616 |
967
+ | Zod | 1,286,707 |
966
968
 
967
969
  #### Parsing
968
970
 
969
- | Library | op/s |
970
- | -------------------- | ----------- |
971
- | **Arri (Compiled)** | 138,189,123 |
972
- | **Arri** | 136,995,619 |
973
- | JSON.parse() | 19,911,721 |
974
- | Ajv - JTD (Compiled) | 8,996,081 |
971
+ | Library | op/s |
972
+ | -------------------- | --------------- |
973
+ | **Arri (Compiled)** | **131,214,018** |
974
+ | **Arri** | **56,134,552** |
975
+ | JSON.parse() | 21,001,320 |
976
+ | Ajv - JTD (Compiled) | 9,441,285 |
975
977
 
976
978
  #### Serialization
977
979
 
978
- | Library | op/s |
979
- | -------------------- | ----------- |
980
- | Ajv - JTD (Compiled) | 198,980,679 |
981
- | **Arri (Compiled)** | 190,386,426 |
982
- | **Arri** | 114,799,692 |
983
- | JSON.stringify | 21,433,854 |
980
+ | Library | op/s |
981
+ | -------------------- | --------------- |
982
+ | Ajv - JTD (Compiled) | 199,802,584 |
983
+ | **Arri (Compiled)** | **194,399,849** |
984
+ | **Arri** | **65,097,829** |
985
+ | JSON.stringify | 16,853,237 |
984
986
 
985
987
  #### Coercion
986
988
 
987
- | Library | op/s |
988
- | ----------------- | ----------- |
989
- | Arri | 117,854,219 |
990
- | TypeBox | 34,633,126 |
991
- | Ajv - JSON Schema | 28,016,735 |
992
- | Zod | 1,586,546 |
989
+ | Library | op/s |
990
+ | ----------------- | -------------- |
991
+ | **Arri** | **55,840,221** |
992
+ | TypeBox | 34,403,424 |
993
+ | Ajv - JSON Schema | 22,190,607 |
994
+ | Zod | 1,195,111 |
993
995
 
994
996
  ## Development
995
997