@arrirpc/schema 0.71.0 → 0.72.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
@@ -1,8 +1,8 @@
1
1
  # Arri Schema
2
2
 
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.
3
+ A Typescript validator and schema builder that can be compiled to other languages. 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. 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.
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.
5
+ Under the hood this library constructs [Arri Type Definitions (ATD)](/specifications/arri_type_definition.md). These definitions can be passed to the [Arri CLI](/tooling/cli//README.md) to generate code for any of the client languages that Arri supports. Lastly, this library also comes with a [JIT compiler](#compiled-validators) which produces precompiled validators that are more than 100x faster than Zod.
6
6
 
7
7
  ## Project Philosophy
8
8
 
@@ -26,7 +26,8 @@ Originally this library was created as a way for building schemas for [Json Type
26
26
 
27
27
  - [Installation](#installation)
28
28
  - [Basic Example](#basic-example)
29
- - [Usage With @arrirpc/server](#usage-with-arrirpcserver)
29
+ - [Usage with @arrirpc/server](#usage-with-arrirpcserver)
30
+ - [Compiling to other languages](#compiling-to-other-languages)
30
31
  - [Supported Types](#supported-types)
31
32
  - [Primitives](#primitives)
32
33
  - [Enums](#enums)
@@ -50,8 +51,8 @@ Originally this library was created as a way for building schemas for [Json Type
50
51
  - [Safe Coerce](#safe-coerce)
51
52
  - [Serialize](#serialize)
52
53
  - [Errors](#errors)
53
- - [Compiled Validators](#compiled-validators)
54
54
  - [Metadata](#metadata)
55
+ - [Compiled Validators](#compiled-validators)
55
56
  - [Benchmarks](#benchmarks)
56
57
  - [Development](#development)
57
58
 
@@ -89,6 +90,12 @@ a.validate(User, { id: '1', name: null });
89
90
 
90
91
  // outputs valid json
91
92
  a.serialize(User, { id: '1', name: 'John Doe' });
93
+
94
+ // JIT compiled validator (faster but server-side only)
95
+ const $$User = a.compile(User);
96
+ $$User.validate({ id: '1', name: 'John Doe' });
97
+ $$User.parse(`{"id": "1", "name": "John Doe"}`);
98
+ $$User.serialize({ id: '1', name: 'John Doe' });
92
99
  ```
93
100
 
94
101
  ## Usage With @arrirpc/server
@@ -115,6 +122,135 @@ export default defineRpc({
115
122
  });
116
123
  ```
117
124
 
125
+ ## Compiling To Other Languages
126
+
127
+ All schemas defined with this library can be compiled to other languages using the [Arri CLI](/tooling/cli/README.md).
128
+
129
+ ### Install the Arri ClI
130
+
131
+ ```bash
132
+ # npm
133
+ npm i --save-dev arri
134
+
135
+ # pnpm
136
+ pnpm i --save-dev arri
137
+ ```
138
+
139
+ ### Create You Arri Config
140
+
141
+ ```ts
142
+ import { defineConfig, generators } from 'arri';
143
+
144
+ export default defineConfig({
145
+ generators: [
146
+ // add your generators here
147
+ generators.rustClient({
148
+ // options
149
+ }),
150
+ generators.dartClient({
151
+ // options
152
+ }),
153
+ ],
154
+ });
155
+ ```
156
+
157
+ ### Create And Export Your Schemas
158
+
159
+ Use the `createAppDefinition` helper to export your schemas for the Arri CLI.
160
+
161
+ ```ts
162
+ // definitions.ts
163
+ import { createAppDefinition } from 'arri';
164
+ import { a } from '@arrirpc/schema';
165
+
166
+ const User = a.object('User', {
167
+ id: a.string(),
168
+ name: a.optional(string()),
169
+ email: a.nullable(a.string()),
170
+ createdAt: a.timestamp({ description: 'When the user was created' }),
171
+ updatedAt: a.timestamp(),
172
+ });
173
+
174
+ export default createAppDefinition({
175
+ definitions: {
176
+ User,
177
+ },
178
+ });
179
+ ```
180
+
181
+ ### Run the Code Generator
182
+
183
+ ```bash
184
+ # npm
185
+ npx arri codegen ./definitions.ts
186
+
187
+ # pnpm
188
+ pnpm arri codegen ./definitions.ts
189
+ ```
190
+
191
+ And your done. Now you can rerun this command whenever any of your schemas get updated.
192
+
193
+ ### Example Output
194
+
195
+ ```dart
196
+ // dart output
197
+
198
+ class User {
199
+ final String id;
200
+ final String? name;
201
+ final String? email;
202
+ /// when the user was created
203
+ final DateTime createdAt;
204
+ final DateTime updatedAt;
205
+ const User({
206
+ required this.id,
207
+ this.name,
208
+ required this.email,
209
+ required this.createdAt,
210
+ required this.updatedAt,
211
+ });
212
+
213
+ // implementation details
214
+ }
215
+ ```
216
+
217
+ ```rust
218
+ // rust output
219
+
220
+ pub struct User {
221
+ id: String,
222
+ name: String,
223
+ name: Option<String>,
224
+ email: Option<String>,
225
+ // when the user was created
226
+ created_at: DateTime<FixedOffset>,
227
+ updated_at: DateTime<FixedOffset>,
228
+ }
229
+
230
+ impl ArriModel for User {
231
+ // implementation details
232
+ }
233
+ ```
234
+
235
+ ```kotlin
236
+ // kotlin output
237
+
238
+ data class User(
239
+ val id: String,
240
+ val name: String?,
241
+ val email: String? = null,
242
+ /**
243
+ * When the user was created
244
+ */
245
+ val createdAt: Instant,
246
+ val updatedAt: Instance,
247
+ ) {
248
+ // implementation details
249
+ }
250
+ ```
251
+
252
+ See [here](/README.md#client-generators) for a list of all officially supported language generators.
253
+
118
254
  ## Supported Types
119
255
 
120
256
  ### Primitives
@@ -150,7 +286,7 @@ a.validate(Status, 'BLAH'); // false
150
286
  a.validate(Status, 'ACTIVE'); // true
151
287
  ```
152
288
 
153
- **Outputted JTD**
289
+ **Outputted ATD**
154
290
 
155
291
  ```json
156
292
  {
@@ -170,7 +306,7 @@ a.validate(MyList, [1, 2]); // false
170
306
  a.validate(MyList, ['hello', 'world']); // true
171
307
  ```
172
308
 
173
- **Outputted JTD**
309
+ **Outputted ATD**
174
310
 
175
311
  ```json
176
312
  {
@@ -204,7 +340,7 @@ a.validate({
204
340
  }); // false
205
341
  ```
206
342
 
207
- **Outputted JTD**
343
+ **Outputted ATD**
208
344
 
209
345
  ```json
210
346
  {
@@ -246,7 +382,7 @@ a.parse(UserStrict, {
246
382
  }); // fails parsing because of the additional field "bio"
247
383
  ```
248
384
 
249
- **Outputted JTD**
385
+ **Outputted ATD**
250
386
 
251
387
  ```json
252
388
  {
@@ -282,7 +418,7 @@ a.validate(R, {
282
418
  }); // false;
283
419
  ```
284
420
 
285
- **Outputted JTD**
421
+ **Outputted ATD**
286
422
 
287
423
  ```json
288
424
  {
@@ -328,7 +464,7 @@ a.validate(Shape, {
328
464
  }); // false
329
465
  ```
330
466
 
331
- **Outputted JTD**
467
+ **Outputted ATD**
332
468
 
333
469
  ```json
334
470
  {
@@ -413,7 +549,7 @@ a.validate(BinaryTree, {
413
549
  }); // false
414
550
  ```
415
551
 
416
- **Outputted JTD**
552
+ **Outputted ATD**
417
553
 
418
554
  ```json
419
555
  {
@@ -456,7 +592,7 @@ const User = a.object({
456
592
  */
457
593
  ```
458
594
 
459
- **Outputted JTD**
595
+ **Outputted ATD**
460
596
 
461
597
  ```json
462
598
  {
@@ -489,7 +625,7 @@ const name = a.nullable(a.string());
489
625
  */
490
626
  ```
491
627
 
492
- **Outputted JTD**
628
+ **Outputted ATD**
493
629
 
494
630
  ```json
495
631
  {
@@ -717,34 +853,6 @@ a.errors(User, { id: 1, date: 'hello world' });
717
853
  */
718
854
  ```
719
855
 
720
- ## Compiled Validators
721
-
722
- `@arrirpc/schema` comes with a high performance JIT compiler that transforms Arri Schemas into highly optimized validation, parsing, serialization functions.
723
-
724
- ```ts
725
- const User = a.object({
726
- id: a.string(),
727
- email: a.nullable(a.string()),
728
- created: a.timestamp(),
729
- });
730
-
731
- const $$User = a.compile(User);
732
-
733
- $$User.validate(someInput);
734
- $$User.parse(someJson);
735
- $$User.serialize({ id: '1', email: null, created: new Date() });
736
- ```
737
-
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.
739
-
740
- You can also use `a.compile` for code generation. The compiler result gives you access to the generated function bodies.
741
-
742
- ```ts
743
- $$User.compiledCode.validate; // the generated validation code
744
- $$User.compiledCode.parse; // the generated parsing code
745
- $$User.compiledCode.serialize; // the generated serialization code
746
- ```
747
-
748
856
  ## Metadata
749
857
 
750
858
  Metadata is used during cross-language code generation. Arri schemas allow you to specify the following metadata fields:
@@ -827,9 +935,9 @@ data class Book(
827
935
  )
828
936
  ```
829
937
 
830
- ### ID Shorthand (Experimental)
938
+ ### ID Shorthand
831
939
 
832
- Because IDs are really important for producing concise type names. Arri validate also provides an _experimental\*_ shorthand for defining IDs of objects, discriminators, and recursive types.
940
+ Because IDs are really important for producing concise type names. Arri validate also provides shorthand for defining IDs of objects, discriminators, and recursive types.
833
941
 
834
942
  ```ts
835
943
  // ID will be set to "Book"
@@ -860,7 +968,33 @@ const BinaryTreeSchema = a.recursive('BTree', (self) =>
860
968
  );
861
969
  ```
862
970
 
863
- \* Because this is experimental it may be removed in the future. The main thing I'm testings is whether added convenience is worth the overhead of maintaining 2 versions of each of these functions. The shorthand could also introduce unintended confusion for users of this library as it creates two places to look for an id.
971
+ ## Compiled Validators
972
+
973
+ `@arrirpc/schema` comes with a high performance JIT compiler that transforms Arri Schemas into highly optimized validation, parsing, serialization functions. The result of the compilation also implements the [standard-schema](https://github.com/standard-schema/standard-schema) interface, meaning it can be passed into any library that accepts standard-schema.
974
+
975
+ ```ts
976
+ const User = a.object({
977
+ id: a.string(),
978
+ email: a.nullable(a.string()),
979
+ created: a.timestamp(),
980
+ });
981
+
982
+ const $$User = a.compile(User);
983
+
984
+ $$User.validate(someInput);
985
+ $$User.parse(someJson);
986
+ $$User.serialize({ id: '1', email: null, created: new Date() });
987
+ ```
988
+
989
+ 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 are created using [`new Function()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) 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.
990
+
991
+ You can also use `a.compile` for code generation. The compiler result gives you access to the generated function bodies.
992
+
993
+ ```ts
994
+ $$User.compiledCode.validate; // the generated validation code
995
+ $$User.compiledCode.parse; // the generated parsing code
996
+ $$User.compiledCode.serialize; // the generated serialization code
997
+ ```
864
998
 
865
999
  ## Benchmarks
866
1000
 
package/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const typeDefs = require('@arrirpc/type-defs');
4
4
  const scule = require('scule');
5
- const timestamp = require('./shared/schema.BcsXcrqv.cjs');
5
+ const timestamp = require('./shared/schema.COiEWm_0.cjs');
6
6
 
7
7
  function createParsingTemplate(input, schema) {
8
8
  const fallbackTemplate = `
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isSchemaFormProperties, isSchemaFormValues, isSchemaFormElements, isSchemaFormDiscriminator, isSchemaFormEmpty, isSchemaFormType, isSchemaFormEnum, isSchemaFormRef } from '@arrirpc/type-defs';
2
2
  import { camelCase } from 'scule';
3
- import { u as uint8Max, a as uint8Min, b as uint16Max, c as uint16Min, d as uint32Max, e as uint32Min, i as int8Max, f as int8Min, g as int16Max, h as int16Min, j as int32Max, k as int32Min, l as isAScalarSchema, m as isAObjectSchema, n as isAStringEnumSchema, o as isAAraySchema, p as isARecordSchema, q as isADiscriminatorSchema, r as isARefSchema, s as errors, V as ValidationError, t as createStandardSchemaProperty, v as any, w as array, x as boolean, y as clone, z as coerce, A as discriminator, B as enumerator, C as extend, D as float32, E as float64, F as int16, G as int32, H as int64, I as int8, J as nullable, K as number, L as object, M as omit, N as optional, O as parse, P as partial, Q as pick, R as record, S as recursive, T as safeCoerce, U as safeParse, W as serialize, X as string, Y as stringEnum, Z as timestamp, _ as uint16, $ as uint32, a0 as uint64, a1 as uint8, a2 as validate, a3 as SCHEMA_METADATA } from './shared/schema.O5uzRO_D.mjs';
4
- export { ah as NumberTypeValues, ac as STR_ESCAPE, aj as hideInvalidProperties, a5 as int64Max, a4 as int64Min, ag as isASchema, ai as isObject, af as isValidationError, a8 as parseObjectSchema, ae as sanitizeJson, aa as serializeObject, ad as serializeSmallString, ab as serializeString, a7 as uint64Max, a6 as uint64Min, a9 as validateObjectSchema } from './shared/schema.O5uzRO_D.mjs';
3
+ import { u as uint8Max, a as uint8Min, b as uint16Max, c as uint16Min, d as uint32Max, e as uint32Min, i as int8Max, f as int8Min, g as int16Max, h as int16Min, j as int32Max, k as int32Min, l as isAScalarSchema, m as isAObjectSchema, n as isAStringEnumSchema, o as isAAraySchema, p as isARecordSchema, q as isADiscriminatorSchema, r as isARefSchema, s as errors, V as ValidationError, t as createStandardSchemaProperty, v as any, w as array, x as boolean, y as clone, z as coerce, A as discriminator, B as enumerator, C as extend, D as float32, E as float64, F as int16, G as int32, H as int64, I as int8, J as nullable, K as number, L as object, M as omit, N as optional, O as parse, P as partial, Q as pick, R as record, S as recursive, T as safeCoerce, U as safeParse, W as serialize, X as string, Y as stringEnum, Z as timestamp, _ as uint16, $ as uint32, a0 as uint64, a1 as uint8, a2 as validate, a3 as SCHEMA_METADATA } from './shared/schema.PNAcAdz3.mjs';
4
+ export { ah as NumberTypeValues, ac as STR_ESCAPE, aj as hideInvalidProperties, a5 as int64Max, a4 as int64Min, ag as isASchema, ai as isObject, af as isValidationError, a8 as parseObjectSchema, ae as sanitizeJson, aa as serializeObject, ad as serializeSmallString, ab as serializeString, a7 as uint64Max, a6 as uint64Min, a9 as validateObjectSchema } from './shared/schema.PNAcAdz3.mjs';
5
5
 
6
6
  function createParsingTemplate(input, schema) {
7
7
  const fallbackTemplate = `
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const typeDefs = require('@arrirpc/type-defs');
4
+ require('scule');
4
5
 
5
6
  const int8Min = -128;
6
7
  const int8Max = 127;
@@ -86,7 +87,7 @@ function createStandardSchemaProperty(validate, parse) {
86
87
  };
87
88
  try {
88
89
  const result = parse(input, ctx);
89
- if (!result || ctx.errors.length) {
90
+ if (ctx.errors.length) {
90
91
  return {
91
92
  issues: ctx.errors.map((err) => ({
92
93
  message: err.message ?? "Unknown error",
@@ -98,6 +99,14 @@ function createStandardSchemaProperty(validate, parse) {
98
99
  value: result
99
100
  };
100
101
  } catch (err) {
102
+ if (err instanceof ValidationError) {
103
+ return {
104
+ issues: err.errors.map((err2) => ({
105
+ message: err2.message ?? "Unknown error",
106
+ path: err2.instancePath.split("/").filter((item) => item.length > 0)
107
+ }))
108
+ };
109
+ }
101
110
  return {
102
111
  issues: [
103
112
  {
@@ -1,4 +1,5 @@
1
1
  import { TypeValues, isSchemaFormElements, isSchemaFormEnum, isSchemaFormDiscriminator, isSchemaFormValues, isSchemaFormProperties, isSchemaFormRef } from '@arrirpc/type-defs';
2
+ import 'scule';
2
3
 
3
4
  const int8Min = -128;
4
5
  const int8Max = 127;
@@ -84,7 +85,7 @@ function createStandardSchemaProperty(validate, parse) {
84
85
  };
85
86
  try {
86
87
  const result = parse(input, ctx);
87
- if (!result || ctx.errors.length) {
88
+ if (ctx.errors.length) {
88
89
  return {
89
90
  issues: ctx.errors.map((err) => ({
90
91
  message: err.message ?? "Unknown error",
@@ -96,6 +97,14 @@ function createStandardSchemaProperty(validate, parse) {
96
97
  value: result
97
98
  };
98
99
  } catch (err) {
100
+ if (err instanceof ValidationError) {
101
+ return {
102
+ issues: err.errors.map((err2) => ({
103
+ message: err2.message ?? "Unknown error",
104
+ path: err2.instancePath.split("/").filter((item) => item.length > 0)
105
+ }))
106
+ };
107
+ }
99
108
  return {
100
109
  issues: [
101
110
  {
@@ -2,7 +2,7 @@
2
2
 
3
3
  require('@arrirpc/type-defs');
4
4
  require('scule');
5
- const timestamp = require('./shared/schema.BcsXcrqv.cjs');
5
+ const timestamp = require('./shared/schema.COiEWm_0.cjs');
6
6
 
7
7
  const User = timestamp.object({
8
8
  id: timestamp.string(),
@@ -1,6 +1,6 @@
1
1
  import '@arrirpc/type-defs';
2
2
  import 'scule';
3
- import { L as object, X as string, N as optional, J as nullable, G as int32, A as discriminator, x as boolean, Y as stringEnum, w as array, Z as timestamp, $ as uint32, v as any, K as number, R as record, S as recursive, E as float64, D as float32, H as int64, F as int16, _ as uint16, I as int8, a1 as uint8, P as partial, a0 as uint64, Q as pick, B as enumerator } from './shared/schema.O5uzRO_D.mjs';
3
+ import { L as object, X as string, N as optional, J as nullable, G as int32, A as discriminator, x as boolean, Y as stringEnum, w as array, Z as timestamp, $ as uint32, v as any, K as number, R as record, S as recursive, E as float64, D as float32, H as int64, F as int16, _ as uint16, I as int8, a1 as uint8, P as partial, a0 as uint64, Q as pick, B as enumerator } from './shared/schema.PNAcAdz3.mjs';
4
4
 
5
5
  const User = object({
6
6
  id: string(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrirpc/schema",
3
- "version": "0.71.0",
3
+ "version": "0.72.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -25,7 +25,7 @@
25
25
  "scule": "^1.3.0",
26
26
  "uncrypto": "^0.1.3",
27
27
  "@standard-schema/spec": "1.0.0-rc.0",
28
- "@arrirpc/type-defs": "0.71.0"
28
+ "@arrirpc/type-defs": "0.72.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "ajv": "^8.17.1",