@arrirpc/schema 0.70.1 → 0.71.1
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 +217 -81
- package/dist/index.cjs +35 -28
- package/dist/index.d.cts +17 -10
- package/dist/index.d.mts +17 -10
- package/dist/index.d.ts +17 -10
- package/dist/index.mjs +35 -30
- package/dist/shared/{schema.TlNJgMz1.cjs → schema.COiEWm_0.cjs} +389 -256
- package/dist/shared/{schema.C-0ZU_OQ.d.cts → schema.DLX0nTwB.d.cts} +4 -3
- package/dist/shared/{schema.C-0ZU_OQ.d.mts → schema.DLX0nTwB.d.mts} +4 -3
- package/dist/shared/{schema.C-0ZU_OQ.d.ts → schema.DLX0nTwB.d.ts} +4 -3
- package/dist/shared/{schema.lowJcmic.mjs → schema.PNAcAdz3.mjs} +388 -257
- package/dist/testSuites.cjs +10 -10
- package/dist/testSuites.d.cts +6 -5
- package/dist/testSuites.d.mts +6 -5
- package/dist/testSuites.d.ts +6 -5
- package/dist/testSuites.mjs +10 -10
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Arri Schema
|
|
2
2
|
|
|
3
|
-
A
|
|
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
|
+
|
|
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.
|
|
4
6
|
|
|
5
7
|
## Project Philosophy
|
|
6
8
|
|
|
@@ -24,7 +26,8 @@ Originally this library was created as a way for building schemas for [Json Type
|
|
|
24
26
|
|
|
25
27
|
- [Installation](#installation)
|
|
26
28
|
- [Basic Example](#basic-example)
|
|
27
|
-
- [Usage
|
|
29
|
+
- [Usage with @arrirpc/server](#usage-with-arrirpcserver)
|
|
30
|
+
- [Compiling to other languages](#compiling-to-other-languages)
|
|
28
31
|
- [Supported Types](#supported-types)
|
|
29
32
|
- [Primitives](#primitives)
|
|
30
33
|
- [Enums](#enums)
|
|
@@ -48,8 +51,8 @@ Originally this library was created as a way for building schemas for [Json Type
|
|
|
48
51
|
- [Safe Coerce](#safe-coerce)
|
|
49
52
|
- [Serialize](#serialize)
|
|
50
53
|
- [Errors](#errors)
|
|
51
|
-
- [Compiled Validators](#compiled-validators)
|
|
52
54
|
- [Metadata](#metadata)
|
|
55
|
+
- [Compiled Validators](#compiled-validators)
|
|
53
56
|
- [Benchmarks](#benchmarks)
|
|
54
57
|
- [Development](#development)
|
|
55
58
|
|
|
@@ -66,7 +69,7 @@ pnpm install @arrirpc/schema
|
|
|
66
69
|
## Basic Example
|
|
67
70
|
|
|
68
71
|
```ts
|
|
69
|
-
import { a } from
|
|
72
|
+
import { a } from '@arrirpc/schema';
|
|
70
73
|
|
|
71
74
|
const User = a.object({
|
|
72
75
|
id: a.string(),
|
|
@@ -81,12 +84,18 @@ a.parse(User, `{"id": "1", "name": "John Doe"}`);
|
|
|
81
84
|
a.parse(User, `{"id": "1", "name": null}`);
|
|
82
85
|
|
|
83
86
|
// returns true
|
|
84
|
-
a.validate(User, { id:
|
|
87
|
+
a.validate(User, { id: '1', name: 'John Doe' });
|
|
85
88
|
// returns false
|
|
86
|
-
a.validate(User, { id:
|
|
89
|
+
a.validate(User, { id: '1', name: null });
|
|
87
90
|
|
|
88
91
|
// outputs valid json
|
|
89
|
-
a.serialize(User, { id:
|
|
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' });
|
|
90
99
|
```
|
|
91
100
|
|
|
92
101
|
## Usage With @arrirpc/server
|
|
@@ -94,8 +103,8 @@ a.serialize(User, { id: "1", name: "John Doe" });
|
|
|
94
103
|
See [here](/languages/ts/ts-server/README.md) for full details.
|
|
95
104
|
|
|
96
105
|
```ts
|
|
97
|
-
import { a } from
|
|
98
|
-
import { defineRpc } from
|
|
106
|
+
import { a } from '@arrirpc/schema';
|
|
107
|
+
import { defineRpc } from '@arrirpc/server';
|
|
99
108
|
|
|
100
109
|
export default defineRpc({
|
|
101
110
|
params: a.object({
|
|
@@ -113,6 +122,135 @@ export default defineRpc({
|
|
|
113
122
|
});
|
|
114
123
|
```
|
|
115
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
|
+
|
|
116
254
|
## Supported Types
|
|
117
255
|
|
|
118
256
|
### Primitives
|
|
@@ -141,14 +279,14 @@ Enum schemas allow you to specify a predefine list of accepted strings
|
|
|
141
279
|
**Usage**
|
|
142
280
|
|
|
143
281
|
```ts
|
|
144
|
-
const Status = a.enumerator([
|
|
282
|
+
const Status = a.enumerator(['ACTIVE', 'INACTIVE', 'UNKNOWN']);
|
|
145
283
|
type Status = a.infer<typeof Status>; // "ACTIVE" | "INACTIVE" | "UNKNOWN";
|
|
146
284
|
|
|
147
|
-
a.validate(Status,
|
|
148
|
-
a.validate(Status,
|
|
285
|
+
a.validate(Status, 'BLAH'); // false
|
|
286
|
+
a.validate(Status, 'ACTIVE'); // true
|
|
149
287
|
```
|
|
150
288
|
|
|
151
|
-
**Outputted
|
|
289
|
+
**Outputted ATD**
|
|
152
290
|
|
|
153
291
|
```json
|
|
154
292
|
{
|
|
@@ -165,10 +303,10 @@ const MyList = a.array(a.string());
|
|
|
165
303
|
type MyList = a.infer<typeof MyList>; // string[];
|
|
166
304
|
|
|
167
305
|
a.validate(MyList, [1, 2]); // false
|
|
168
|
-
a.validate(MyList, [
|
|
306
|
+
a.validate(MyList, ['hello', 'world']); // true
|
|
169
307
|
```
|
|
170
308
|
|
|
171
|
-
**Outputted
|
|
309
|
+
**Outputted ATD**
|
|
172
310
|
|
|
173
311
|
```json
|
|
174
312
|
{
|
|
@@ -191,18 +329,18 @@ const User = a.object({
|
|
|
191
329
|
type User = a.infer<typeof User>; // { id: string; email: string; created: Date; }
|
|
192
330
|
|
|
193
331
|
a.validate({
|
|
194
|
-
id:
|
|
195
|
-
email:
|
|
332
|
+
id: '1',
|
|
333
|
+
email: 'johndoe@example.com',
|
|
196
334
|
created: new Date(),
|
|
197
335
|
}); // true
|
|
198
336
|
a.validate({
|
|
199
|
-
id:
|
|
337
|
+
id: '1',
|
|
200
338
|
email: null,
|
|
201
339
|
created: new Date(),
|
|
202
340
|
}); // false
|
|
203
341
|
```
|
|
204
342
|
|
|
205
|
-
**Outputted
|
|
343
|
+
**Outputted ATD**
|
|
206
344
|
|
|
207
345
|
```json
|
|
208
346
|
{
|
|
@@ -237,14 +375,14 @@ const UserStrict = a.object(
|
|
|
237
375
|
);
|
|
238
376
|
|
|
239
377
|
a.parse(UserStrict, {
|
|
240
|
-
id:
|
|
241
|
-
name:
|
|
378
|
+
id: '1',
|
|
379
|
+
name: 'johndoe',
|
|
242
380
|
created: new Date(),
|
|
243
|
-
bio:
|
|
381
|
+
bio: 'my name is joe',
|
|
244
382
|
}); // fails parsing because of the additional field "bio"
|
|
245
383
|
```
|
|
246
384
|
|
|
247
|
-
**Outputted
|
|
385
|
+
**Outputted ATD**
|
|
248
386
|
|
|
249
387
|
```json
|
|
250
388
|
{
|
|
@@ -276,11 +414,11 @@ a.validate(R, {
|
|
|
276
414
|
world: false,
|
|
277
415
|
}); // true;
|
|
278
416
|
a.validate(R, {
|
|
279
|
-
hello:
|
|
417
|
+
hello: 'world',
|
|
280
418
|
}); // false;
|
|
281
419
|
```
|
|
282
420
|
|
|
283
|
-
**Outputted
|
|
421
|
+
**Outputted ATD**
|
|
284
422
|
|
|
285
423
|
```json
|
|
286
424
|
{
|
|
@@ -295,7 +433,7 @@ a.validate(R, {
|
|
|
295
433
|
**Usage**
|
|
296
434
|
|
|
297
435
|
```ts
|
|
298
|
-
const Shape = a.discriminator(
|
|
436
|
+
const Shape = a.discriminator('type', {
|
|
299
437
|
RECTANGLE: a.object({
|
|
300
438
|
width: a.float32(),
|
|
301
439
|
height: a.float32(),
|
|
@@ -307,26 +445,26 @@ const Shape = a.discriminator("type", {
|
|
|
307
445
|
type Shape = a.infer<typeof Shape>; // { type: "RECTANGLE"; width: number; height: number; } | { type: "CIRCLE"; radius: number; }
|
|
308
446
|
|
|
309
447
|
// Infer specific sub types of the union
|
|
310
|
-
type ShapeTypeRectangle = a.inferSubType<Shape,
|
|
311
|
-
type ShapeTypeCircle = a.inferSubType<Shape,
|
|
448
|
+
type ShapeTypeRectangle = a.inferSubType<Shape, 'type', 'RECTANGLE'>; // { type "RECTANGLE"; width: number; height: number; };
|
|
449
|
+
type ShapeTypeCircle = a.inferSubType<Shape, 'type', 'CIRCLE'>; // { type "CIRCLE"; radius: number; }
|
|
312
450
|
|
|
313
451
|
a.validate(Shape, {
|
|
314
|
-
type:
|
|
452
|
+
type: 'RECTANGLE',
|
|
315
453
|
width: 1,
|
|
316
454
|
height: 1.5,
|
|
317
455
|
}); // true
|
|
318
456
|
a.validate(Shape, {
|
|
319
|
-
type:
|
|
457
|
+
type: 'CIRCLE',
|
|
320
458
|
radius: 5,
|
|
321
459
|
}); // true
|
|
322
460
|
a.validate(Shape, {
|
|
323
|
-
type:
|
|
461
|
+
type: 'CIRCLE',
|
|
324
462
|
width: 1,
|
|
325
463
|
height: 1.5,
|
|
326
464
|
}); // false
|
|
327
465
|
```
|
|
328
466
|
|
|
329
|
-
**Outputted
|
|
467
|
+
**Outputted ATD**
|
|
330
468
|
|
|
331
469
|
```json
|
|
332
470
|
{
|
|
@@ -385,7 +523,7 @@ const BinaryTree = a.recursive<BinaryTree>(
|
|
|
385
523
|
right: a.nullable(self),
|
|
386
524
|
}),
|
|
387
525
|
{
|
|
388
|
-
id:
|
|
526
|
+
id: 'BinaryTree',
|
|
389
527
|
},
|
|
390
528
|
);
|
|
391
529
|
|
|
@@ -411,7 +549,7 @@ a.validate(BinaryTree, {
|
|
|
411
549
|
}); // false
|
|
412
550
|
```
|
|
413
551
|
|
|
414
|
-
**Outputted
|
|
552
|
+
**Outputted ATD**
|
|
415
553
|
|
|
416
554
|
```json
|
|
417
555
|
{
|
|
@@ -454,7 +592,7 @@ const User = a.object({
|
|
|
454
592
|
*/
|
|
455
593
|
```
|
|
456
594
|
|
|
457
|
-
**Outputted
|
|
595
|
+
**Outputted ATD**
|
|
458
596
|
|
|
459
597
|
```json
|
|
460
598
|
{
|
|
@@ -487,7 +625,7 @@ const name = a.nullable(a.string());
|
|
|
487
625
|
*/
|
|
488
626
|
```
|
|
489
627
|
|
|
490
|
-
**Outputted
|
|
628
|
+
**Outputted ATD**
|
|
491
629
|
|
|
492
630
|
```json
|
|
493
631
|
{
|
|
@@ -506,7 +644,7 @@ const A = a.object(
|
|
|
506
644
|
a: a.string(),
|
|
507
645
|
b: a.float32(),
|
|
508
646
|
},
|
|
509
|
-
{ id:
|
|
647
|
+
{ id: 'A' },
|
|
510
648
|
);
|
|
511
649
|
console.log(A.metadata.id); // "A"
|
|
512
650
|
|
|
@@ -545,7 +683,7 @@ const A = a.object({
|
|
|
545
683
|
});
|
|
546
684
|
// { a: string; b: number; }
|
|
547
685
|
|
|
548
|
-
const B = a.omit(A, [
|
|
686
|
+
const B = a.omit(A, ['a']);
|
|
549
687
|
// { b: number; }
|
|
550
688
|
```
|
|
551
689
|
|
|
@@ -561,7 +699,7 @@ const A = a.object({
|
|
|
561
699
|
});
|
|
562
700
|
// { a: string; b: number; c: Date; }
|
|
563
701
|
|
|
564
|
-
const B = a.pick(A, [
|
|
702
|
+
const B = a.pick(A, ['a', 'c']);
|
|
565
703
|
// { a: string; c: Date; }
|
|
566
704
|
```
|
|
567
705
|
|
|
@@ -593,7 +731,7 @@ const User = a.object({
|
|
|
593
731
|
name: a.string(),
|
|
594
732
|
});
|
|
595
733
|
a.validate(User, true); // false
|
|
596
|
-
a.validate(User, { id:
|
|
734
|
+
a.validate(User, { id: '1', name: 'john doe' }); // true
|
|
597
735
|
|
|
598
736
|
if (a.validate(User, someInput)) {
|
|
599
737
|
console.log(someInput.id); // intellisense works here
|
|
@@ -644,9 +782,9 @@ const A = a.object({
|
|
|
644
782
|
});
|
|
645
783
|
|
|
646
784
|
a.coerce(A, {
|
|
647
|
-
a:
|
|
648
|
-
b:
|
|
649
|
-
c:
|
|
785
|
+
a: '1',
|
|
786
|
+
b: 'true',
|
|
787
|
+
c: '500.24',
|
|
650
788
|
});
|
|
651
789
|
// { a: "1", b: true, c: 500.24 };
|
|
652
790
|
```
|
|
@@ -681,7 +819,7 @@ const User = a.object({
|
|
|
681
819
|
name: a.string(),
|
|
682
820
|
});
|
|
683
821
|
|
|
684
|
-
a.serialize(User, { id:
|
|
822
|
+
a.serialize(User, { id: '1', name: 'john doe' });
|
|
685
823
|
// {"id":"1","name":"john doe"}
|
|
686
824
|
```
|
|
687
825
|
|
|
@@ -697,7 +835,7 @@ const User = a.object({
|
|
|
697
835
|
date: a.timestamp(),
|
|
698
836
|
});
|
|
699
837
|
|
|
700
|
-
a.errors(User, { id: 1, date:
|
|
838
|
+
a.errors(User, { id: 1, date: 'hello world' });
|
|
701
839
|
/**
|
|
702
840
|
* [
|
|
703
841
|
* {
|
|
@@ -715,34 +853,6 @@ a.errors(User, { id: 1, date: "hello world" });
|
|
|
715
853
|
*/
|
|
716
854
|
```
|
|
717
855
|
|
|
718
|
-
## Compiled Validators
|
|
719
|
-
|
|
720
|
-
`@arrirpc/schema` comes with a high performance JIT compiler that transforms Arri Schemas into highly optimized validation, parsing, serialization functions.
|
|
721
|
-
|
|
722
|
-
```ts
|
|
723
|
-
const User = a.object({
|
|
724
|
-
id: a.string(),
|
|
725
|
-
email: a.nullable(a.string()),
|
|
726
|
-
created: a.timestamp(),
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
const $$User = a.compile(User);
|
|
730
|
-
|
|
731
|
-
$$User.validate(someInput);
|
|
732
|
-
$$User.parse(someJson);
|
|
733
|
-
$$User.serialize({ id: "1", email: null, created: new Date() });
|
|
734
|
-
```
|
|
735
|
-
|
|
736
|
-
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.
|
|
737
|
-
|
|
738
|
-
You can also use `a.compile` for code generation. The compiler result gives you access to the generated function bodies.
|
|
739
|
-
|
|
740
|
-
```ts
|
|
741
|
-
$$User.compiledCode.validate; // the generated validation code
|
|
742
|
-
$$User.compiledCode.parse; // the generated parsing code
|
|
743
|
-
$$User.compiledCode.serialize; // the generated serialization code
|
|
744
|
-
```
|
|
745
|
-
|
|
746
856
|
## Metadata
|
|
747
857
|
|
|
748
858
|
Metadata is used during cross-language code generation. Arri schemas allow you to specify the following metadata fields:
|
|
@@ -764,8 +874,8 @@ const BookSchema = a.object(
|
|
|
764
874
|
publishDate: a.timestamp(),
|
|
765
875
|
},
|
|
766
876
|
{
|
|
767
|
-
id:
|
|
768
|
-
description:
|
|
877
|
+
id: 'Book',
|
|
878
|
+
description: 'This is a book',
|
|
769
879
|
},
|
|
770
880
|
);
|
|
771
881
|
```
|
|
@@ -825,20 +935,20 @@ data class Book(
|
|
|
825
935
|
)
|
|
826
936
|
```
|
|
827
937
|
|
|
828
|
-
### ID Shorthand
|
|
938
|
+
### ID Shorthand
|
|
829
939
|
|
|
830
|
-
Because IDs are really important for producing concise type names. Arri validate also provides
|
|
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.
|
|
831
941
|
|
|
832
942
|
```ts
|
|
833
943
|
// ID will be set to "Book"
|
|
834
|
-
const BookSchema = a.object(
|
|
944
|
+
const BookSchema = a.object('Book', {
|
|
835
945
|
title: a.string(),
|
|
836
946
|
author: a.string(),
|
|
837
947
|
publishDate: a.timestamp(),
|
|
838
948
|
});
|
|
839
949
|
|
|
840
950
|
// ID will be set to "Message"
|
|
841
|
-
const MessageSchema = a.discriminator(
|
|
951
|
+
const MessageSchema = a.discriminator('Message', 'type', {
|
|
842
952
|
TEXT: a.object({
|
|
843
953
|
userId: a.string(),
|
|
844
954
|
content: a.string(),
|
|
@@ -850,7 +960,7 @@ const MessageSchema = a.discriminator("Message", "type", {
|
|
|
850
960
|
});
|
|
851
961
|
|
|
852
962
|
// ID will be set to "BTree"
|
|
853
|
-
const BinaryTreeSchema = a.recursive(
|
|
963
|
+
const BinaryTreeSchema = a.recursive('BTree', (self) =>
|
|
854
964
|
a.object({
|
|
855
965
|
left: a.nullable(self),
|
|
856
966
|
right: a.nullable(self),
|
|
@@ -858,7 +968,33 @@ const BinaryTreeSchema = a.recursive("BTree", (self) =>
|
|
|
858
968
|
);
|
|
859
969
|
```
|
|
860
970
|
|
|
861
|
-
|
|
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
|
+
```
|
|
862
998
|
|
|
863
999
|
## Benchmarks
|
|
864
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.
|
|
5
|
+
const timestamp = require('./shared/schema.COiEWm_0.cjs');
|
|
6
6
|
|
|
7
7
|
function createParsingTemplate(input, schema) {
|
|
8
8
|
const fallbackTemplate = `
|
|
@@ -1367,33 +1367,31 @@ function compile(schema) {
|
|
|
1367
1367
|
const parseFn = parser.fn;
|
|
1368
1368
|
const serializer = getCompiledSerializer(schema);
|
|
1369
1369
|
const serializeFn = serializer.fn;
|
|
1370
|
-
const validate = new Function(
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
} catch (err) {
|
|
1380
|
-
const errors = timestamp.errors(schema, input);
|
|
1381
|
-
let errorMessage = err instanceof Error ? err.message : "";
|
|
1382
|
-
if (errors.length) {
|
|
1383
|
-
errorMessage = errors[0].message ?? `Parsing error at ${errors[0].instancePath}`;
|
|
1384
|
-
}
|
|
1385
|
-
throw new timestamp.ValidationError({
|
|
1386
|
-
message: errorMessage,
|
|
1387
|
-
errors
|
|
1388
|
-
});
|
|
1370
|
+
const validate = new Function("input", validateCode);
|
|
1371
|
+
const parse = (input) => {
|
|
1372
|
+
try {
|
|
1373
|
+
return parseFn(input);
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
const errors = timestamp.errors(schema, input);
|
|
1376
|
+
let errorMessage = err instanceof Error ? err.message : "";
|
|
1377
|
+
if (errors.length) {
|
|
1378
|
+
errorMessage = errors[0].message ?? `Parsing error at ${errors[0].instancePath}`;
|
|
1389
1379
|
}
|
|
1390
|
-
|
|
1380
|
+
throw new timestamp.ValidationError({
|
|
1381
|
+
message: errorMessage,
|
|
1382
|
+
errors
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
const result = {
|
|
1387
|
+
validate,
|
|
1388
|
+
parse,
|
|
1391
1389
|
safeParse(input) {
|
|
1392
1390
|
try {
|
|
1393
|
-
const
|
|
1391
|
+
const result2 = parseFn(input);
|
|
1394
1392
|
return {
|
|
1395
1393
|
success: true,
|
|
1396
|
-
value:
|
|
1394
|
+
value: result2
|
|
1397
1395
|
};
|
|
1398
1396
|
} catch (err) {
|
|
1399
1397
|
const errors = timestamp.errors(schema, input);
|
|
@@ -1426,8 +1424,10 @@ function compile(schema) {
|
|
|
1426
1424
|
validate: validateCode,
|
|
1427
1425
|
parse: parser.code,
|
|
1428
1426
|
serialize: serializer.code
|
|
1429
|
-
}
|
|
1427
|
+
},
|
|
1428
|
+
"~standard": timestamp.createStandardSchemaProperty(validate, parse)
|
|
1430
1429
|
};
|
|
1430
|
+
return result;
|
|
1431
1431
|
}
|
|
1432
1432
|
function getCompiledParser(input, schema) {
|
|
1433
1433
|
const code = createParsingTemplate(input, schema);
|
|
@@ -2134,7 +2134,7 @@ function isAdaptedSchema(input) {
|
|
|
2134
2134
|
return input.metadata[timestamp.SCHEMA_METADATA]._isAdapted === true;
|
|
2135
2135
|
}
|
|
2136
2136
|
function validatorFromAdaptedSchema(schema) {
|
|
2137
|
-
|
|
2137
|
+
const result = {
|
|
2138
2138
|
compiledCode: {
|
|
2139
2139
|
parse: "",
|
|
2140
2140
|
serialize: "",
|
|
@@ -2156,13 +2156,13 @@ function validatorFromAdaptedSchema(schema) {
|
|
|
2156
2156
|
errors: []
|
|
2157
2157
|
};
|
|
2158
2158
|
try {
|
|
2159
|
-
const
|
|
2159
|
+
const result2 = schema.metadata[timestamp.SCHEMA_METADATA].parse(
|
|
2160
2160
|
input,
|
|
2161
2161
|
context
|
|
2162
2162
|
);
|
|
2163
2163
|
return {
|
|
2164
2164
|
success: true,
|
|
2165
|
-
value:
|
|
2165
|
+
value: result2
|
|
2166
2166
|
};
|
|
2167
2167
|
} catch (err) {
|
|
2168
2168
|
if (err instanceof timestamp.ValidationError) {
|
|
@@ -2187,8 +2187,13 @@ function validatorFromAdaptedSchema(schema) {
|
|
|
2187
2187
|
errors: []
|
|
2188
2188
|
};
|
|
2189
2189
|
return schema.metadata[timestamp.SCHEMA_METADATA].serialize(input, context);
|
|
2190
|
-
}
|
|
2190
|
+
},
|
|
2191
|
+
"~standard": timestamp.createStandardSchemaProperty(
|
|
2192
|
+
schema.metadata[timestamp.SCHEMA_METADATA].validate,
|
|
2193
|
+
schema.metadata[timestamp.SCHEMA_METADATA].parse
|
|
2194
|
+
)
|
|
2191
2195
|
};
|
|
2196
|
+
return result;
|
|
2192
2197
|
}
|
|
2193
2198
|
|
|
2194
2199
|
exports.NumberTypeValues = timestamp.NumberTypeValues;
|
|
@@ -2200,12 +2205,14 @@ exports.array = timestamp.array;
|
|
|
2200
2205
|
exports.boolean = timestamp.boolean;
|
|
2201
2206
|
exports.clone = timestamp.clone;
|
|
2202
2207
|
exports.coerce = timestamp.coerce;
|
|
2208
|
+
exports.createStandardSchemaProperty = timestamp.createStandardSchemaProperty;
|
|
2203
2209
|
exports.discriminator = timestamp.discriminator;
|
|
2204
2210
|
exports.enumerator = timestamp.enumerator;
|
|
2205
2211
|
exports.errors = timestamp.errors;
|
|
2206
2212
|
exports.extend = timestamp.extend;
|
|
2207
2213
|
exports.float32 = timestamp.float32;
|
|
2208
2214
|
exports.float64 = timestamp.float64;
|
|
2215
|
+
exports.hideInvalidProperties = timestamp.hideInvalidProperties;
|
|
2209
2216
|
exports.int16 = timestamp.int16;
|
|
2210
2217
|
exports.int16Max = timestamp.int16Max;
|
|
2211
2218
|
exports.int16Min = timestamp.int16Min;
|