@jetio/validator 1.0.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/LICENSE +21 -0
- package/README.md +1362 -0
- package/dist/cli.js +219 -0
- package/dist/compileSchema.d.ts +148 -0
- package/dist/compileSchema.js +2199 -0
- package/dist/compileSchema.js.map +1 -0
- package/dist/formats.d.ts +41 -0
- package/dist/formats.js +166 -0
- package/dist/formats.js.map +1 -0
- package/dist/index.cjs.js +6167 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.esm.js +6148 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/jet-validator.d.ts +88 -0
- package/dist/jet-validator.js +983 -0
- package/dist/jet-validator.js.map +1 -0
- package/dist/resolver.d.ts +348 -0
- package/dist/resolver.js +2459 -0
- package/dist/resolver.js.map +1 -0
- package/dist/scripts/load-metaschemas.d.ts +1 -0
- package/dist/scripts/metaschema-loader.d.ts +2 -0
- package/dist/src/compileSchema.d.ts +148 -0
- package/dist/src/formats.d.ts +41 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/jet-validator.d.ts +88 -0
- package/dist/src/resolver.d.ts +348 -0
- package/dist/src/types/format.d.ts +7 -0
- package/dist/src/types/keywords.d.ts +78 -0
- package/dist/src/types/schema.d.ts +123 -0
- package/dist/src/types/standalone.d.ts +4 -0
- package/dist/src/types/validation.d.ts +49 -0
- package/dist/src/utilities/index.d.ts +11 -0
- package/dist/src/utilities/schema.d.ts +10 -0
- package/dist/types/format.d.ts +7 -0
- package/dist/types/format.js +3 -0
- package/dist/types/format.js.map +1 -0
- package/dist/types/keywords.d.ts +78 -0
- package/dist/types/keywords.js +4 -0
- package/dist/types/keywords.js.map +1 -0
- package/dist/types/schema.d.ts +123 -0
- package/dist/types/schema.js +3 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/standalone.d.ts +4 -0
- package/dist/types/standalone.js +3 -0
- package/dist/types/standalone.js.map +1 -0
- package/dist/types/validation.d.ts +49 -0
- package/dist/types/validation.js +3 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utilities/index.d.ts +11 -0
- package/dist/utilities/index.js +146 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/utilities/schema.d.ts +10 -0
- package/dist/utilities/schema.js +232 -0
- package/dist/utilities/schema.js.map +1 -0
- package/dist/validator.umd.js +6196 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,1362 @@
|
|
|
1
|
+
# jet-validator
|
|
2
|
+
|
|
3
|
+
**The Fastest JSON Schema Validator in JavaScript**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@jetio/validator) [](https://opensource.org/licenses/MIT) [](https://github.com/jetio/validator/actions) [](https://bundlephobia.com/package/@jetio/validator)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## π Why jet-validator?
|
|
10
|
+
|
|
11
|
+
**5-44x faster compilation** | **<100KB** | **Zero dependencies** | **JSON Schema Draft 06-2020-12**
|
|
12
|
+
|
|
13
|
+
jet-validator compiles JSON Schemas into highly optimized validation functions in **sub-millisecond time**. Unlike traditional validators that interpret schemas at runtime, jet-validator generates specialized code tailored to your exact schema structure.
|
|
14
|
+
|
|
15
|
+
Built with simplicity in mind.
|
|
16
|
+
|
|
17
|
+
Just one import for all supported drafts, check full documentation for how to specify draft for $ref keyword for schema 07 and earlier.
|
|
18
|
+
|
|
19
|
+
### Key Features
|
|
20
|
+
|
|
21
|
+
- β‘ **Lightning Fast** - 10-27x faster compilation than AJV (sub-millisecond compilation)
|
|
22
|
+
- β
**Highly Compliant** - 99.5%+ compliance on JSON Schema Test Suite across all supported drafts
|
|
23
|
+
- π¦ **Smaller Bundle** - <100KB with built-in format validators (no external packages needed)
|
|
24
|
+
- π― **Zero Dependencies** - Pure TypeScript implementation
|
|
25
|
+
- πͺ **TypeScript-First** - Full type safety out of the box
|
|
26
|
+
- π§ **Enhanced Features** - `elseIf` conditionals, advanced `$data` references, custom keywords
|
|
27
|
+
- π **Partial AJV Compatibility** - Similar API, minimal code changes needed
|
|
28
|
+
- π **Multiple Error Modes** - Fail-fast or collect all errors
|
|
29
|
+
- π **Async Schema Loading** - Load schemas from HTTP, databases, or file systems
|
|
30
|
+
- π« **No Infinite Recursion** - Smart resolution process prevents stack overflow errors
|
|
31
|
+
|
|
32
|
+
## π οΈ Schema Builder
|
|
33
|
+
|
|
34
|
+
Want a fluent, type-safe API for building schemas? Check out [@jetio/schema-builder](https://github.com/jetio/schema-builder):
|
|
35
|
+
```typescript
|
|
36
|
+
import { SchemaBuilder, JetValidator } from "@jetio/schema-builder";
|
|
37
|
+
|
|
38
|
+
const schema = new SchemaBuilder()
|
|
39
|
+
.object()
|
|
40
|
+
.properties({
|
|
41
|
+
name: s => s.string().minLength(2),
|
|
42
|
+
email: s => s.string().format("email"),
|
|
43
|
+
age: s => s.integer().minimum(18)
|
|
44
|
+
})
|
|
45
|
+
.required(["name", "email"])
|
|
46
|
+
.build();
|
|
47
|
+
|
|
48
|
+
const validator = new JetValidator();
|
|
49
|
+
const validate = validator.compile(schema);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The schema-builder package includes this validator, so you get both in one install.
|
|
53
|
+
|
|
54
|
+
### What Fast Compilation Enables
|
|
55
|
+
|
|
56
|
+
Because compilation is fast (1-5ms for complex schemas), you can:
|
|
57
|
+
|
|
58
|
+
β
**Compile schemas on-the-fly** - No caching required if performance isn't critical
|
|
59
|
+
β
**Hot-reload validation rules** - Update schemas without restarting
|
|
60
|
+
β
**Dynamic schema generation** - Build schemas based on user input or config
|
|
61
|
+
β
**Per-request validators** - Create custom validators for each request context
|
|
62
|
+
β
**Faster Startup** - Faster cold starts when compiling large numbers of schemas in serverless environments
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## π¦ Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install @jetio/validator
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm add @jetio/validator
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
yarn add @jetio/validator
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## π Quick Start
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { JetValidator } from "@jetio/validator";
|
|
86
|
+
|
|
87
|
+
const jetValidator = new JetValidator();
|
|
88
|
+
|
|
89
|
+
const schema = {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
name: { type: "string", minLength: 2 },
|
|
93
|
+
age: { type: "number", minimum: 0, maximum: 120 },
|
|
94
|
+
email: { type: "string", format: "email" },
|
|
95
|
+
},
|
|
96
|
+
required: ["name", "age"],
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const validate = jetValidator.compile(schema);
|
|
100
|
+
|
|
101
|
+
// Valid data
|
|
102
|
+
console.log(
|
|
103
|
+
validate({
|
|
104
|
+
name: "Alice",
|
|
105
|
+
age: 25,
|
|
106
|
+
email: "alice@example.com",
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
// Output: true
|
|
110
|
+
|
|
111
|
+
// Invalid data
|
|
112
|
+
console.log(
|
|
113
|
+
validate({
|
|
114
|
+
name: "A",
|
|
115
|
+
age: 150,
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
// Output: false
|
|
119
|
+
|
|
120
|
+
console.log(validate.errors);
|
|
121
|
+
// Output:
|
|
122
|
+
// [{
|
|
123
|
+
// dataPath: '/name',
|
|
124
|
+
// schemaPath: '/properties/name',
|
|
125
|
+
// keyword: 'minLength',
|
|
126
|
+
// message: 'must NOT have fewer than 2 characters'
|
|
127
|
+
// },
|
|
128
|
+
// {
|
|
129
|
+
// dataPath: '/age',
|
|
130
|
+
// schemaPath: '/properties/age',
|
|
131
|
+
// keyword: 'maximum',
|
|
132
|
+
// message: 'must be <= 120'
|
|
133
|
+
// }]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**β [See Getting Started Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#-installation)**
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## π JSON Schema Compliance
|
|
141
|
+
|
|
142
|
+
jet-validator supports **JSON Schema Draft 06 through 2020-12** with exceptional compliance rates across all drafts.
|
|
143
|
+
|
|
144
|
+
### Compliance Results
|
|
145
|
+
|
|
146
|
+
All results are from the official [JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite). Optional tests were skipped. You can view the test code in the [test folder](https://github.com/jetio/validator/tree/main/tests) of our repository.
|
|
147
|
+
|
|
148
|
+
#### Draft 2020-12
|
|
149
|
+
|
|
150
|
+
**jet-validator:**
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
Total tests: 1261
|
|
154
|
+
Passed: 1251 (99.2%)
|
|
155
|
+
Failed: 10 (0.8%)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**AJV (for comparison):**
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
Total tests: 1261
|
|
162
|
+
Passed: 1208 (95.8%)
|
|
163
|
+
Failed: 53 (4.2%)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
<details>
|
|
167
|
+
<summary>π View jet-validator failures (10 tests)</summary>
|
|
168
|
+
|
|
169
|
+
**dynamicRef.json (4 failures):**
|
|
170
|
+
|
|
171
|
+
- Multiple dynamic paths edge cases
|
|
172
|
+
- Dynamic scope leaving edge cases
|
|
173
|
+
|
|
174
|
+
**properties.json (1 failure):**
|
|
175
|
+
|
|
176
|
+
- `__proto__` property edge case
|
|
177
|
+
|
|
178
|
+
**required.json (4 failures)** - JavaScript object property names `__proto__` property edge case
|
|
179
|
+
|
|
180
|
+
**vocabulary.json (1 failure):**
|
|
181
|
+
|
|
182
|
+
- Custom metaschema with no validation vocabulary
|
|
183
|
+
|
|
184
|
+
</details>
|
|
185
|
+
|
|
186
|
+
<details>
|
|
187
|
+
<summary>π View AJV failures (53 tests)</summary>
|
|
188
|
+
|
|
189
|
+
**dynamicRef.json (23 failures)** - Dynamic reference resolution issues
|
|
190
|
+
**ref.json (8 failures)** - **Stack overflow errors with relative URIs** (RangeError: Maximum call stack size exceeded)
|
|
191
|
+
**required.json (4 failures)** - JavaScript object property names `__proto__` property edge case
|
|
192
|
+
**unevaluatedItems.json (12 failures)** - Nested items evaluation
|
|
193
|
+
**unevaluatedProperties.json (4 failures)** - Dynamic reference with unevaluated properties
|
|
194
|
+
**properties.json (1 failure)** - JavaScript object property names
|
|
195
|
+
**vocabulary.json (1 failure)** - Custom metaschema validation
|
|
196
|
+
|
|
197
|
+
</details>
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
#### Draft 2019-09
|
|
202
|
+
|
|
203
|
+
**jet-validator:**
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
Total tests: 1227
|
|
207
|
+
Passed: 1206 (98.3%)
|
|
208
|
+
Failed: 21 (1.7%)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**AJV (for comparison):**
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
Total tests: 1227
|
|
215
|
+
Passed: 1206 (98.3%)
|
|
216
|
+
Failed: 23 (1.7%)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
<details>
|
|
220
|
+
<summary>π View jet-validator failures (23 tests)</summary>
|
|
221
|
+
|
|
222
|
+
**recursiveRef.json (13 failures):**
|
|
223
|
+
|
|
224
|
+
- `$recursiveRef` and `$recursiveAnchor` are **intentionally not supported**
|
|
225
|
+
- These keywords are confusing and serve little practical purpose
|
|
226
|
+
- Users should upgrade to Draft 2020-12 and use `$dynamicRef`/`$dynamicAnchor` instead (much better design)
|
|
227
|
+
|
|
228
|
+
**Other failures:**
|
|
229
|
+
|
|
230
|
+
- `defs.json` (1) - Metaschema validation edge case recursiveAnchor
|
|
231
|
+
- `properties.json` (1) - `__proto__` property edge case
|
|
232
|
+
**required.json (4 failures)** - JavaScript object property names `__proto__` property edge case
|
|
233
|
+
- `ref.json` (1) - Recursive anchor interaction
|
|
234
|
+
- `unevaluatedItems.json` (1) - Recursive reference evaluation
|
|
235
|
+
- `unevaluatedProperties.json` (1) - Recursive reference evaluation
|
|
236
|
+
- `vocabulary.json` (1) - Custom metaschema (not supported)
|
|
237
|
+
|
|
238
|
+
</details>
|
|
239
|
+
|
|
240
|
+
<details>
|
|
241
|
+
<summary>π View AJV failures (21 tests)</summary>
|
|
242
|
+
|
|
243
|
+
**ref.json (8 failures)** - **Stack overflow errors** (RangeError: Maximum call stack size exceeded)
|
|
244
|
+
**required.json (4 failures)** - JavaScript object property names
|
|
245
|
+
**unevaluatedItems.json (3 failures)** - Nested items and conditional evaluation
|
|
246
|
+
**recursiveRef.json (2 failures)** - Recursive reference resolution
|
|
247
|
+
**unevaluatedProperties.json (2 failures)** - Conditional evaluation
|
|
248
|
+
**properties.json (1 failure)** - JavaScript object property names
|
|
249
|
+
**vocabulary.json (1 failure)** - Custom metaschema validation
|
|
250
|
+
|
|
251
|
+
</details>
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
#### Draft 07
|
|
256
|
+
|
|
257
|
+
**jet-validator:**
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
Total tests: 913
|
|
261
|
+
Passed: 908 (99.5%)
|
|
262
|
+
Failed: 5 (0.5%)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**AJV (for comparison):**
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
Total tests: 913
|
|
269
|
+
Passed: 905 (99.1%)
|
|
270
|
+
Failed: 8 (0.9%)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
<details>
|
|
274
|
+
<summary>π View jet-validator failures (5 test)</summary>
|
|
275
|
+
|
|
276
|
+
**properties.json (5 failure):**
|
|
277
|
+
|
|
278
|
+
- `__proto__` property edge case
|
|
279
|
+
**required.json (4 failures)** - JavaScript object property names `__proto__` property edge case
|
|
280
|
+
|
|
281
|
+
</details>
|
|
282
|
+
|
|
283
|
+
<details>
|
|
284
|
+
<summary>π View AJV failures (8 tests)</summary>
|
|
285
|
+
|
|
286
|
+
**required.json (4 failures)** - JavaScript object property names `__proto__` property edge case
|
|
287
|
+
**ref.json (3 failures)** - Reference resolution and sibling keywords
|
|
288
|
+
**properties.json (1 failure)** - JavaScript object property names
|
|
289
|
+
|
|
290
|
+
</details>
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
#### Draft 06
|
|
295
|
+
|
|
296
|
+
**jet-validator:**
|
|
297
|
+
|
|
298
|
+
```
|
|
299
|
+
Total tests: 829
|
|
300
|
+
Passed: 824 (99.4%)
|
|
301
|
+
Failed: 5 (0.6%)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**AJV:** _Specific version does not support Draft 06_
|
|
305
|
+
|
|
306
|
+
<details>
|
|
307
|
+
<summary>π View jet-validator failures (5 test)</summary>
|
|
308
|
+
|
|
309
|
+
**properties.json (1 failure):**
|
|
310
|
+
**required.json (4 failures)** - JavaScript object property names `__proto__` property edge case
|
|
311
|
+
|
|
312
|
+
- `__proto__` property edge case
|
|
313
|
+
|
|
314
|
+
</details>
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### π« No Infinite Recursion
|
|
319
|
+
|
|
320
|
+
Unlike other validators that struggle with certain schema patterns, **jet-validator never encounters infinite recursion problems**.
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
Due to our advanced resolution process, jet-validator never has infinite recursion problems.
|
|
324
|
+
Unlike other validators which exceed maximum call stack size on certain schemas,
|
|
325
|
+
jet-validator's recursion only goes as deep as the data being validated.
|
|
326
|
+
|
|
327
|
+
THERE IS NO INFINITE RECURSION IN jet-validator.
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
While AJV encounters stack overflow errors (`RangeError: Maximum call stack size exceeded`) on schemas with complex relative URI references, jet-validator handles these schemas without issue. Our three-phase resolution process (Collection β Assignment β Resolution) eliminates circular reference problems at compile time.
|
|
331
|
+
|
|
332
|
+
**β [Learn more about our Resolution Process](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#schema-resolution-process)**
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### π Unsupported Keywords
|
|
337
|
+
|
|
338
|
+
For transparency, here are the keywords jet-validator intentionally does not support:
|
|
339
|
+
|
|
340
|
+
**Draft 2019-09:**
|
|
341
|
+
|
|
342
|
+
- `$recursiveRef` / `$recursiveAnchor` - Confusing design, replaced by better `$dynamicRef`/`$dynamicAnchor` in Draft 2020-12
|
|
343
|
+
- `$vocabulary` - Custom vocabulary system (edge case feature)
|
|
344
|
+
|
|
345
|
+
**Draft 2020-12:**
|
|
346
|
+
|
|
347
|
+
- `$vocabulary` - Custom vocabulary system (edge case feature)
|
|
348
|
+
|
|
349
|
+
**Why not support these?**
|
|
350
|
+
|
|
351
|
+
- `$recursiveRef`/`$recursiveAnchor` serve little practical purpose and are confusing. The Draft 2020-12 `$dynamicRef`/`$dynamicAnchor` keywords are much better designed.
|
|
352
|
+
- `$vocabulary` is an edge case feature that adds complexity without significant benefit for most users.
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## β‘ Performance Benchmarks
|
|
357
|
+
|
|
358
|
+
**Environment:** Ubuntu 1.6GHz laptop under realistic load (browser, IDE, system services)
|
|
359
|
+
|
|
360
|
+
**Why under load?** Production servers are never idle. These results reflect real-world conditions.
|
|
361
|
+
|
|
362
|
+
### Summary
|
|
363
|
+
|
|
364
|
+
- **Compilation:** 10-27x faster than AJV (1.33ms vs 13.20ms average)
|
|
365
|
+
- **Valid data validation:** 7% faster overall (2.44M vs 2.28M ops/sec)
|
|
366
|
+
- **Invalid data validation:** 24% faster overall (4.15M vs 3.36M ops/sec)
|
|
367
|
+
- **Win rate:** 49.2% on valid data, 66.2% on invalid data
|
|
368
|
+
- **Overall winner:** 71.8% of all benchmarks
|
|
369
|
+
|
|
370
|
+
**Key Wins:**
|
|
371
|
+
|
|
372
|
+
- Composition operations: 240-284% faster
|
|
373
|
+
- Pattern matching: Up to 88% faster
|
|
374
|
+
- Integer validation: 48-120% faster
|
|
375
|
+
- Object validation at scale: 28-51% faster
|
|
376
|
+
|
|
377
|
+
**Where AJV leads:**
|
|
378
|
+
|
|
379
|
+
- Deeply chained $refs: 88% faster
|
|
380
|
+
- Deep allOf chains: 68% faster
|
|
381
|
+
- Complex conditionals: 36% faster
|
|
382
|
+
|
|
383
|
+
[π Full Benchmark Report](https://github.com/jetio/validator/blob/main/benchmark/results/COMPARISON.md) | [π Detailed Results](https://github.com/jetio/validator/tree/main/benchmark/results/)
|
|
384
|
+
|
|
385
|
+
_Tested against AJV v8.17.1 using official benchmarks with 65 schemas, 1000 warmups, 10000 iterations, 5 runs per test_
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### Why This Matters
|
|
390
|
+
|
|
391
|
+
Caching becomes **optional** instead of mandatory:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// β Other validators: Must cache to avoid compilation overhead
|
|
395
|
+
const validateUser = ajv.compile(userSchema); // Cache this!
|
|
396
|
+
|
|
397
|
+
// β
jet-validator: Fast enough to compile on-demand
|
|
398
|
+
app.post("/validate", (req, res) => {
|
|
399
|
+
const schema = req.body.schema;
|
|
400
|
+
const validate = jetValidator.compile(schema); // < 2ms
|
|
401
|
+
res.json(validate(req.body.data));
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## π― Core Features
|
|
408
|
+
|
|
409
|
+
### Basic Validation
|
|
410
|
+
|
|
411
|
+
Simple validation with comprehensive error reporting:
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
const jetValidator = new JetValidator();
|
|
415
|
+
|
|
416
|
+
const schema = {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
username: { type: "string", minLength: 3, maxLength: 20 },
|
|
420
|
+
email: { type: "string", format: "email" },
|
|
421
|
+
age: { type: "number", minimum: 18 },
|
|
422
|
+
},
|
|
423
|
+
required: ["username", "email"],
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const validate = jetValidator.compile(schema);
|
|
427
|
+
const result = validate({ username: "jo", email: "invalid" });
|
|
428
|
+
|
|
429
|
+
console.log(result); // false
|
|
430
|
+
console.log(validate.errors); // Detailed error information
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**β [See Basic Validation Examples](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#-basic-validation-examples)**
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
### Schema Compilation & Management
|
|
438
|
+
|
|
439
|
+
Compile once, validate many times with blazing speed:
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
// Synchronous compilation
|
|
443
|
+
const validate = jetValidator.compile(schema);
|
|
444
|
+
|
|
445
|
+
// Async compilation (for remote schemas)
|
|
446
|
+
const validate = await jetValidator.compileAsync(schema);
|
|
447
|
+
|
|
448
|
+
// Add schemas to registry for reuse
|
|
449
|
+
jetValidator.addSchema(schema, "user-schema");
|
|
450
|
+
const validate = jetValidator.getSchema("user-schema");
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**β [See Schema Management & Compilation](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#schema-management--compilation)**
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
### Configuration Options
|
|
458
|
+
|
|
459
|
+
Customize validation behavior to match your needs:
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
const jetValidator = new JetValidator({
|
|
463
|
+
// Error handling
|
|
464
|
+
allErrors: true, // Collect all errors, not just first
|
|
465
|
+
errorMessage: true, // Enable custom error messages
|
|
466
|
+
|
|
467
|
+
// Validation strictness
|
|
468
|
+
strict: true, // Strict mode for schema validation
|
|
469
|
+
strictNumbers: true, // Reject NaN and Infinity
|
|
470
|
+
strictRequired: true, // Fail on undefined required properties
|
|
471
|
+
|
|
472
|
+
// Data modification
|
|
473
|
+
coerceTypes: true, // Auto-convert types (string β number)
|
|
474
|
+
useDefaults: true, // Apply default values
|
|
475
|
+
removeAdditional: true, // Remove extra properties
|
|
476
|
+
|
|
477
|
+
// Format validation
|
|
478
|
+
validateFormats: true, // Enable format validation
|
|
479
|
+
|
|
480
|
+
// Performance
|
|
481
|
+
async: true, // Enable async validation
|
|
482
|
+
});
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
**β [See All Configuration Options](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#οΈ-configuration-options)**
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
### Error Handling
|
|
490
|
+
|
|
491
|
+
Rich, detailed error information with customizable messages:
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
const jetValidator = new JetValidator({
|
|
495
|
+
allErrors: true,
|
|
496
|
+
errorMessage: true,
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const schema = {
|
|
500
|
+
type: "object",
|
|
501
|
+
properties: {
|
|
502
|
+
password: {
|
|
503
|
+
type: "string",
|
|
504
|
+
minLength: 8,
|
|
505
|
+
errorMessage: "Password must be at least 8 characters long",
|
|
506
|
+
},
|
|
507
|
+
confirmPassword: {
|
|
508
|
+
type: "string",
|
|
509
|
+
const: { $data: "1/password" },
|
|
510
|
+
errorMessage: "Passwords must match",
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const validate = jetValidator.compile(schema);
|
|
516
|
+
const result = validate({
|
|
517
|
+
password: "short",
|
|
518
|
+
confirmPassword: "different",
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
console.log(validate.errors); // Custom error messages included
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**Error Object Structure:**
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
{
|
|
528
|
+
dataPath: string; // Path to invalid data: "/properties/user"
|
|
529
|
+
schemaPath: string; // Path to schema location: "/properties/user"
|
|
530
|
+
keyword: string; // Keyword that failed: "minLength"
|
|
531
|
+
fullSchemaPath: string; // Complete path: "/properties/user/minLength"
|
|
532
|
+
message: string; // Error message
|
|
533
|
+
params?: object; // Additional context
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
**β [See Error Handling Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#error-handling)**
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
### Advanced Error Handling
|
|
542
|
+
|
|
543
|
+
jet-validator provides **production-grade error handling** out of the box:
|
|
544
|
+
|
|
545
|
+
#### Error Utility Methods
|
|
546
|
+
|
|
547
|
+
**Built-in utilities for working with errors:**
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
// Pretty-print errors with hierarchy
|
|
551
|
+
jetValidator.logErrors(validate.errors);
|
|
552
|
+
|
|
553
|
+
// Group errors by field (perfect for forms)
|
|
554
|
+
const fieldErrors = jetValidator.getFieldErrors(validate.errors);
|
|
555
|
+
// { '/email': ['Invalid format', 'Too short'], '/age': ['Must be positive'] }
|
|
556
|
+
|
|
557
|
+
// Format as readable string
|
|
558
|
+
jetValidator.errorsText(validate.errors, { separator: "\n" });
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
#### Custom Error Messages
|
|
562
|
+
|
|
563
|
+
**Schema-level or parent-level customization:**
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
const schema = {
|
|
567
|
+
type: "object",
|
|
568
|
+
properties: {
|
|
569
|
+
email: {
|
|
570
|
+
type: "string",
|
|
571
|
+
format: "email",
|
|
572
|
+
minLength: 5,
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
errorMessage: {
|
|
576
|
+
properties: {
|
|
577
|
+
email: {
|
|
578
|
+
type: "Email must be text",
|
|
579
|
+
format: "Invalid email format",
|
|
580
|
+
minLength: "Email too short",
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
**β [See Complete Error Handling Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#error-handling)**
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
### Schema References & Composition
|
|
592
|
+
|
|
593
|
+
Build modular, reusable schemas with `$ref`, `$dynamicRef`, and schema composition:
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
const schema = {
|
|
597
|
+
$id: "https://example.com/schemas/user.json",
|
|
598
|
+
type: "object",
|
|
599
|
+
properties: {
|
|
600
|
+
profile: { $ref: "#/$defs/profile" },
|
|
601
|
+
address: { $ref: "https://example.com/schemas/address.json" },
|
|
602
|
+
},
|
|
603
|
+
$defs: {
|
|
604
|
+
profile: {
|
|
605
|
+
type: "object",
|
|
606
|
+
properties: {
|
|
607
|
+
name: { type: "string" },
|
|
608
|
+
bio: { type: "string" },
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
// Load remote schemas automatically
|
|
615
|
+
const jetValidator = new JetValidator({
|
|
616
|
+
loadSchema: async (uri) => {
|
|
617
|
+
const response = await fetch(uri);
|
|
618
|
+
return response.json();
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
const validate = await jetValidator.compileAsync(schema);
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
**β [See Schema References & Composition](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#schema-references--composition)**
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
### Meta-Schema System
|
|
630
|
+
|
|
631
|
+
Validate your schemas before using them (supports JSON Schema Draft 06-2020-12):
|
|
632
|
+
|
|
633
|
+
```typescript
|
|
634
|
+
const jetValidator = new JetValidator({
|
|
635
|
+
validateSchema: true,
|
|
636
|
+
meta: true,
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// Your schema is automatically validated against the meta-schema
|
|
640
|
+
const validate = jetValidator.compile(schema);
|
|
641
|
+
// Throws error if schema is invalid
|
|
642
|
+
|
|
643
|
+
// Or validate explicitly
|
|
644
|
+
const isValid = jetValidator.validateSchema(schema);
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
**β [See Meta-Schema System](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#meta-schema-system)**
|
|
648
|
+
|
|
649
|
+
---
|
|
650
|
+
|
|
651
|
+
## π₯ Advanced Features
|
|
652
|
+
|
|
653
|
+
### Format Validation
|
|
654
|
+
|
|
655
|
+
Built-in formats plus easy custom format registration:
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
// Built-in formats (no extra packages needed!)
|
|
659
|
+
const schema = {
|
|
660
|
+
type: "object",
|
|
661
|
+
properties: {
|
|
662
|
+
email: { type: "string", format: "email" },
|
|
663
|
+
url: { type: "string", format: "uri" },
|
|
664
|
+
date: { type: "string", format: "date-time" },
|
|
665
|
+
ipv4: { type: "string", format: "ipv4" },
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// Add custom formats
|
|
670
|
+
jetValidator.addFormat("phone", /^\+?[1-9]\d{1,14}$/);
|
|
671
|
+
|
|
672
|
+
// Or with validation function
|
|
673
|
+
jetValidator.addFormat("even-number", {
|
|
674
|
+
type: "number",
|
|
675
|
+
validate: (value) => value % 2 === 0,
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// Async format validation
|
|
679
|
+
jetValidator.addFormat("unique-email", {
|
|
680
|
+
type: "string",
|
|
681
|
+
async: true,
|
|
682
|
+
validate: async (email) => {
|
|
683
|
+
return !(await database.emailExists(email));
|
|
684
|
+
},
|
|
685
|
+
});
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**β [See Format Validation Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#format-validation)**
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
692
|
+
### $data References
|
|
693
|
+
|
|
694
|
+
Compare and validate against other values in your data:
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
const schema = {
|
|
698
|
+
type: "object",
|
|
699
|
+
properties: {
|
|
700
|
+
startDate: { type: "string", format: "date" },
|
|
701
|
+
endDate: {
|
|
702
|
+
type: "string",
|
|
703
|
+
format: "date",
|
|
704
|
+
// endDate must be after startDate
|
|
705
|
+
formatMinimum: { $data: "1/startDate" },
|
|
706
|
+
},
|
|
707
|
+
minPrice: { type: "number" },
|
|
708
|
+
maxPrice: { type: "number" },
|
|
709
|
+
currentPrice: {
|
|
710
|
+
type: "number",
|
|
711
|
+
minimum: { $data: "1/minPrice" },
|
|
712
|
+
maximum: { $data: "1/maxPrice" },
|
|
713
|
+
},
|
|
714
|
+
},
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
const jetValidator = new JetValidator({ $data: true });
|
|
718
|
+
const validate = jetValidator.compile(schema);
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
**β [See $data References Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#data)**
|
|
722
|
+
|
|
723
|
+
---
|
|
724
|
+
|
|
725
|
+
### elseIf Conditionals
|
|
726
|
+
|
|
727
|
+
Enhanced conditional validation without deep nesting:
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
// Standard JSON Schema (deeply nested)
|
|
731
|
+
{
|
|
732
|
+
if: { properties: { type: { const: 'A' } } },
|
|
733
|
+
then: { /* ... */ },
|
|
734
|
+
else: {
|
|
735
|
+
if: { properties: { type: { const: 'B' } } },
|
|
736
|
+
then: { /* ... */ },
|
|
737
|
+
else: { /* ... */ }
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// jet-validator elseIf (clean and readable)
|
|
742
|
+
{
|
|
743
|
+
if: { properties: { type: { const: 'A' } } },
|
|
744
|
+
then: { /* ... */ },
|
|
745
|
+
elseIf: [
|
|
746
|
+
{
|
|
747
|
+
if: { properties: { type: { const: 'B' } } },
|
|
748
|
+
then: { /* ... */ }
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
if: { properties: { type: { const: 'C' } } },
|
|
752
|
+
then: { /* ... */ }
|
|
753
|
+
}
|
|
754
|
+
],
|
|
755
|
+
else: { /* default case */ }
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
**β [See elseIf Keyword Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#elseif-keyword)**
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
### Custom Keywords
|
|
764
|
+
|
|
765
|
+
Extend jet-validator with your own validation logic:
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
// Add custom keyword
|
|
769
|
+
jetValidator.addKeyword({
|
|
770
|
+
keyword: "isEven",
|
|
771
|
+
type: "number",
|
|
772
|
+
validate: (schema, data) => {
|
|
773
|
+
return data % 2 === 0;
|
|
774
|
+
},
|
|
775
|
+
error: {
|
|
776
|
+
message: "Number must be even",
|
|
777
|
+
},
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
const schema = {
|
|
781
|
+
type: "number",
|
|
782
|
+
isEven: true,
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
const validate = jetValidator.compile(schema);
|
|
786
|
+
console.log(validate(4)); // true
|
|
787
|
+
console.log(validate(5)); // false
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
**β [See Custom Keywords Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#custom-keywords)**
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
## π οΈ Utility Functions
|
|
795
|
+
|
|
796
|
+
jet-validator exports utility functions that can be used for schema manipulation and debugging:
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
import {
|
|
800
|
+
getSchemaAtPath,
|
|
801
|
+
getJSONType,
|
|
802
|
+
deepEqual,
|
|
803
|
+
canonicalStringify,
|
|
804
|
+
len_of,
|
|
805
|
+
} from "@jetio/validator/utilities";
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
### `getSchemaAtPath(schema, path)`
|
|
809
|
+
|
|
810
|
+
Retrieve a sub-schema at a specific JSON Pointer path.
|
|
811
|
+
|
|
812
|
+
```typescript
|
|
813
|
+
const schema = {
|
|
814
|
+
properties: {
|
|
815
|
+
user: {
|
|
816
|
+
type: "object",
|
|
817
|
+
properties: {
|
|
818
|
+
name: { type: "string", minLength: 2 },
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
},
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
const subSchema = getSchemaAtPath(schema, "#/properties/user/properties/name");
|
|
825
|
+
// Returns: { type: 'string', minLength: 2 }
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**Use case:** Debugging validation errors - retrieve the exact schema that caused an error.
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
### `getJSONType(value)`
|
|
833
|
+
|
|
834
|
+
Get the JSON Schema type of any value (matches JSON Schema type semantics).
|
|
835
|
+
|
|
836
|
+
```typescript
|
|
837
|
+
getJSONType(42); // 'integer'
|
|
838
|
+
getJSONType(3.14); // 'number'
|
|
839
|
+
getJSONType([1, 2, 3]); // 'array'
|
|
840
|
+
getJSONType(null); // 'null'
|
|
841
|
+
getJSONType({}); // 'object'
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
**Use case:** Type checking in custom keywords or validation logic.
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
### `deepEqual(a, b)`
|
|
849
|
+
|
|
850
|
+
Deep equality comparison (used internally for `const` keyword).
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
deepEqual({ a: 1, b: [2, 3] }, { a: 1, b: [2, 3] }); // true
|
|
854
|
+
deepEqual({ a: 1 }, { a: 1, b: 2 }); // false
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
**Use case:** Comparing complex data structures in tests or custom validators.
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
### `canonicalStringify(obj)`
|
|
862
|
+
|
|
863
|
+
Deterministic JSON stringification (sorted keys for consistent hashing).
|
|
864
|
+
|
|
865
|
+
```typescript
|
|
866
|
+
const obj1 = { b: 2, a: 1 };
|
|
867
|
+
const obj2 = { a: 1, b: 2 };
|
|
868
|
+
|
|
869
|
+
canonicalStringify(obj1) === canonicalStringify(obj2); // true
|
|
870
|
+
JSON.stringify(obj1) === JSON.stringify(obj2); // false
|
|
871
|
+
```
|
|
872
|
+
|
|
873
|
+
**Use case:** Caching, deduplication, or comparing schemas.
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
### `len_of(str)`
|
|
878
|
+
|
|
879
|
+
Get the true Unicode character count (handles surrogate pairs correctly).
|
|
880
|
+
|
|
881
|
+
```typescript
|
|
882
|
+
len_of("hello"); // 5
|
|
883
|
+
len_of("ππ½"); // 2 (not 4)
|
|
884
|
+
len_of("cafΓ©"); // 4
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
**Use case:** Validating `minLength`/`maxLength` for strings with emoji or special Unicode characters.
|
|
888
|
+
|
|
889
|
+
**β [See Full Utilities API Reference](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#utilities-api)**
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
## π Migration from AJV
|
|
894
|
+
|
|
895
|
+
jet-validator has a very **similar API** to AJV:
|
|
896
|
+
|
|
897
|
+
```typescript
|
|
898
|
+
// Before (AJV)
|
|
899
|
+
import Ajv from "ajv";
|
|
900
|
+
const ajv = new Ajv();
|
|
901
|
+
const validate = ajv.compile(schema);
|
|
902
|
+
|
|
903
|
+
// After (jet-validator)
|
|
904
|
+
import { JetValidator } from "@jetio/validator";
|
|
905
|
+
const jetValidator = new JetValidator();
|
|
906
|
+
const validate = jetValidator.compile(schema);
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
**Benefits of switching:**
|
|
910
|
+
|
|
911
|
+
- β‘ 10-27x faster compilation
|
|
912
|
+
- π¦ Smaller bundle size
|
|
913
|
+
- π― Built-in formats (no separate packages)
|
|
914
|
+
- β¨ Enhanced features (`elseIf`, better `$data`)
|
|
915
|
+
|
|
916
|
+
**What's different:**
|
|
917
|
+
|
|
918
|
+
- Error object structure (but more detailed)
|
|
919
|
+
- Custom keywords use different API (more powerful)
|
|
920
|
+
- Meta-schema setup is easier
|
|
921
|
+
|
|
922
|
+
**β [See Migration Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#migration-from-ajv)**
|
|
923
|
+
|
|
924
|
+
---
|
|
925
|
+
|
|
926
|
+
## π When to Use jet-validator
|
|
927
|
+
|
|
928
|
+
### Perfect For:
|
|
929
|
+
|
|
930
|
+
β
**High-throughput APIs** - Validate thousands of requests per second
|
|
931
|
+
β
**Dynamic schemas** - Generate and compile schemas on-the-fly
|
|
932
|
+
β
**Serverless functions** - Fast cold starts with quick compilation
|
|
933
|
+
β
**Real-time validation** - Hot-reload schemas without restart
|
|
934
|
+
β
**Complex validation logic** - Advanced `$data` and conditional validation
|
|
935
|
+
β
**Bundle size matters** - <100KB with all features included
|
|
936
|
+
|
|
937
|
+
### Consider Alternatives If:
|
|
938
|
+
|
|
939
|
+
β οΈ You need 100% JSON Schema spec compliance (we're at 99.5%)
|
|
940
|
+
β οΈ You're already heavily invested in AJV ecosystem with custom plugins (we offer the same custom keywords but contexts are simpler and different, although much more easy to use and better)
|
|
941
|
+
β οΈ You need streaming validation for extremely large documents
|
|
942
|
+
|
|
943
|
+
---
|
|
944
|
+
|
|
945
|
+
## π What Makes jet-validator Different?
|
|
946
|
+
|
|
947
|
+
### 1. Three-Phase Resolution Process
|
|
948
|
+
|
|
949
|
+
jet-validator eliminates infinite recursion through a unique resolution approach. Unlike other validators that resolve references during traversal (causing stack overflow), jet-validator:
|
|
950
|
+
|
|
951
|
+
1. **Collects** all references and identifiers
|
|
952
|
+
2. **Assigns** unique function names to each location
|
|
953
|
+
3. **Resolves** references by replacing them with function calls
|
|
954
|
+
|
|
955
|
+
This architecture enables both lightning-fast compilation and bulletproof circular reference handling.
|
|
956
|
+
|
|
957
|
+
**β [Learn more about the resolution process](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#schema-resolution-process)**
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
### 2. Sub-Millisecond Compilation
|
|
962
|
+
|
|
963
|
+
Traditional validators:
|
|
964
|
+
|
|
965
|
+
```typescript
|
|
966
|
+
// Slow: 20ms per compilation
|
|
967
|
+
const validate = ajv.compile(schema); // Must cache!
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
jet-validator:
|
|
971
|
+
|
|
972
|
+
```typescript
|
|
973
|
+
// Fast: 1-2ms per compilation
|
|
974
|
+
const validate = jetValidator.compile(schema); // Can recompile!
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
---
|
|
978
|
+
|
|
979
|
+
### 3. Enhanced Conditionals with `elseIf`
|
|
980
|
+
|
|
981
|
+
Avoid deeply nested if/else chains with clean, readable `elseIf` syntax.
|
|
982
|
+
|
|
983
|
+
---
|
|
984
|
+
|
|
985
|
+
### 4. Advanced `$data` Support
|
|
986
|
+
|
|
987
|
+
Reference and compare values within your data during validation.
|
|
988
|
+
|
|
989
|
+
---
|
|
990
|
+
|
|
991
|
+
### 5. Built-in Everything
|
|
992
|
+
|
|
993
|
+
No need for separate packages - formats, error messages, and advanced features are all included.
|
|
994
|
+
|
|
995
|
+
---
|
|
996
|
+
|
|
997
|
+
## π‘ Common Use Cases
|
|
998
|
+
|
|
999
|
+
### API Request Validation
|
|
1000
|
+
|
|
1001
|
+
```typescript
|
|
1002
|
+
app.post("/api/users", (req, res) => {
|
|
1003
|
+
const validate = jetValidator.compile(userSchema);
|
|
1004
|
+
const result = validate(req.body);
|
|
1005
|
+
|
|
1006
|
+
if (!result) {
|
|
1007
|
+
return res.status(400).json({ errors: validate.errors });
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Process valid data
|
|
1011
|
+
});
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
---
|
|
1015
|
+
|
|
1016
|
+
### Form Validation with Type Coercion
|
|
1017
|
+
|
|
1018
|
+
```typescript
|
|
1019
|
+
const jetValidator = new JetValidator({ coerceTypes: true });
|
|
1020
|
+
|
|
1021
|
+
const formSchema = {
|
|
1022
|
+
type: "object",
|
|
1023
|
+
properties: {
|
|
1024
|
+
age: { type: "number", minimum: 18 },
|
|
1025
|
+
agree: { type: "boolean" },
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
const validate = jetValidator.compile(formSchema);
|
|
1030
|
+
|
|
1031
|
+
// Automatically converts strings to correct types
|
|
1032
|
+
const data = { age: "25", agree: "true" };
|
|
1033
|
+
validate(data);
|
|
1034
|
+
console.log(data); // { age: '25', agree: 'true' }
|
|
1035
|
+
// Note: JetValidator does not modify original data objects
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
---
|
|
1039
|
+
|
|
1040
|
+
### Config File Validation
|
|
1041
|
+
|
|
1042
|
+
```typescript
|
|
1043
|
+
const configSchema = {
|
|
1044
|
+
type: "object",
|
|
1045
|
+
properties: {
|
|
1046
|
+
port: { type: "number", default: 3000 },
|
|
1047
|
+
host: { type: "string", default: "localhost" },
|
|
1048
|
+
ssl: { type: "boolean", default: false },
|
|
1049
|
+
},
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
const jetValidator = new JetValidator({ useDefaults: true });
|
|
1053
|
+
const validate = jetValidator.compile(configSchema);
|
|
1054
|
+
|
|
1055
|
+
const config = {};
|
|
1056
|
+
validate(config);
|
|
1057
|
+
console.log(config); // {}
|
|
1058
|
+
// Note: JetValidator does not modify original data objects
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
---
|
|
1062
|
+
|
|
1063
|
+
### Dynamic Schema Generation
|
|
1064
|
+
|
|
1065
|
+
```typescript
|
|
1066
|
+
function createValidatorForUser(userType) {
|
|
1067
|
+
const schema = {
|
|
1068
|
+
type: "object",
|
|
1069
|
+
properties: {
|
|
1070
|
+
name: { type: "string" },
|
|
1071
|
+
role: { const: userType },
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
|
|
1075
|
+
// Fast enough to compile per request
|
|
1076
|
+
return jetValidator.compile(schema);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
const validateAdmin = createValidatorForUser("admin");
|
|
1080
|
+
const validateUser = createValidatorForUser("user");
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
## π Documentation
|
|
1086
|
+
|
|
1087
|
+
### Quick Navigation
|
|
1088
|
+
|
|
1089
|
+
**Getting Started:**
|
|
1090
|
+
|
|
1091
|
+
- [Installation & Setup](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#-installation)
|
|
1092
|
+
- [Quick Start Guide](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#-quick-start)
|
|
1093
|
+
- [Choosing Schema Language](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#-choosing-schema-language)
|
|
1094
|
+
|
|
1095
|
+
**Core Concepts:**
|
|
1096
|
+
|
|
1097
|
+
- [Configuration Options](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#οΈ-configuration-options)
|
|
1098
|
+
- [Schema Compilation](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#compiling-schemas)
|
|
1099
|
+
- [Validation Methods](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#validation)
|
|
1100
|
+
- [Schema Management](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#schema-management)
|
|
1101
|
+
- [Error Handling](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#error-handling)
|
|
1102
|
+
|
|
1103
|
+
**Advanced Features:**
|
|
1104
|
+
|
|
1105
|
+
- [Schema References & Composition](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#schema-references--composition)
|
|
1106
|
+
- [Meta-Schema System](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#meta-schema-system)
|
|
1107
|
+
- [$data References](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#data)
|
|
1108
|
+
- [elseIf Conditionals](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#elseif-keyword)
|
|
1109
|
+
- [Format Validation](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#format-validation)
|
|
1110
|
+
- [Custom Keywords](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#custom-keywords)
|
|
1111
|
+
- [Utilities API](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md#utilities-api)
|
|
1112
|
+
|
|
1113
|
+
**Complete Documentation:**
|
|
1114
|
+
|
|
1115
|
+
- [π Full Documentation (20k+ lines)](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md) - Everything in one searchable file
|
|
1116
|
+
|
|
1117
|
+
---
|
|
1118
|
+
|
|
1119
|
+
## Acknowledgments
|
|
1120
|
+
|
|
1121
|
+
- Format validation patterns follow RFC and ISO specifications
|
|
1122
|
+
- Inspired by the performance goals of AJV
|
|
1123
|
+
|
|
1124
|
+
## π€ Contributing
|
|
1125
|
+
|
|
1126
|
+
We welcome contributions! jet-validator is a community project and we appreciate all help.
|
|
1127
|
+
|
|
1128
|
+
### Ways to Contribute
|
|
1129
|
+
|
|
1130
|
+
- π **Report bugs** - Found an issue? [Open a bug report](https://github.com/jetio/validator/issues/new?labels=bug)
|
|
1131
|
+
- π‘ **Suggest features** - Have an idea? [Open a feature request](https://github.com/jetio/validator/issues/new?labels=enhancement)
|
|
1132
|
+
- π **Improve docs** - Fix typos, add examples, clarify explanations
|
|
1133
|
+
- π§ͺ **Add test cases** - Contribute to JSON Schema compliance
|
|
1134
|
+
- β‘ **Performance improvements** - Make it even faster
|
|
1135
|
+
- π¨ **Code contributions** - Fix bugs or implement features
|
|
1136
|
+
|
|
1137
|
+
### Development Setup
|
|
1138
|
+
|
|
1139
|
+
```bash
|
|
1140
|
+
# Clone the repo
|
|
1141
|
+
git clone https://github.com/jetio/validator
|
|
1142
|
+
cd jet-validator
|
|
1143
|
+
|
|
1144
|
+
# Install dependencies
|
|
1145
|
+
npm install
|
|
1146
|
+
|
|
1147
|
+
# Build the project
|
|
1148
|
+
npm run build
|
|
1149
|
+
|
|
1150
|
+
# Run JSON Schema Test Suite
|
|
1151
|
+
npm run test:draft2020-12
|
|
1152
|
+
npm run test:draft2019-09
|
|
1153
|
+
npm run test:draft7
|
|
1154
|
+
npm run test:draft6
|
|
1155
|
+
|
|
1156
|
+
# Run all tests
|
|
1157
|
+
npm run test
|
|
1158
|
+
|
|
1159
|
+
# Run benchmarks
|
|
1160
|
+
npm run benchmark
|
|
1161
|
+
npm run benchmark:compare
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
### Testing Philosophy
|
|
1165
|
+
|
|
1166
|
+
jet-validator uses the official [JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) as its primary test suite. This ensures real-world compliance rather than contrived unit tests.
|
|
1167
|
+
|
|
1168
|
+
**To test your changes:**
|
|
1169
|
+
|
|
1170
|
+
1. Make your changes to the codebase
|
|
1171
|
+
2. Build: `npm run build`
|
|
1172
|
+
3. Run the official test suite: `npm run test:draft2020-12` (or appropriate draft)
|
|
1173
|
+
4. Check compliance rate - aim to maintain or improve current 99.5%+ compliance
|
|
1174
|
+
|
|
1175
|
+
**The test runner** (`tests/test-runner.ts`) validates against:
|
|
1176
|
+
|
|
1177
|
+
- 1,261+ tests for Draft 2020-12
|
|
1178
|
+
- 1,227+ tests for Draft 2019-09
|
|
1179
|
+
- 913+ tests for Draft 07
|
|
1180
|
+
- 829+ tests for Draft 06
|
|
1181
|
+
|
|
1182
|
+
### Code Contributions
|
|
1183
|
+
|
|
1184
|
+
**Before submitting a PR:**
|
|
1185
|
+
|
|
1186
|
+
1. β
Run the test suite and ensure no regressions
|
|
1187
|
+
2. β
Run `npm run build` successfully
|
|
1188
|
+
3. β
Update documentation if you're adding features
|
|
1189
|
+
4. β
Add benchmark tests if you're optimizing performance
|
|
1190
|
+
5. β
Explain the "why" in your PR description
|
|
1191
|
+
|
|
1192
|
+
**Areas that need help:**
|
|
1193
|
+
|
|
1194
|
+
- Further optimization of common validation patterns
|
|
1195
|
+
- Additional format validators
|
|
1196
|
+
- Better error messages
|
|
1197
|
+
- Documentation improvements
|
|
1198
|
+
- Edge case handling from the test suite
|
|
1199
|
+
|
|
1200
|
+
### Understanding the Codebase
|
|
1201
|
+
|
|
1202
|
+
The resolver (`src/compiler/resolver.ts`) is the heart of jet-validator. It handles:
|
|
1203
|
+
|
|
1204
|
+
- Schema resolution and reference tracking
|
|
1205
|
+
- `$ref`, `$dynamicRef`, `$anchor` resolution
|
|
1206
|
+
- External schema loading
|
|
1207
|
+
- Circular reference prevention
|
|
1208
|
+
- Three-phase resolution process
|
|
1209
|
+
|
|
1210
|
+
If you're working on the resolver, please be extra careful - it's complex but battle-tested against 3,000+ official test cases.
|
|
1211
|
+
|
|
1212
|
+
### Questions?
|
|
1213
|
+
|
|
1214
|
+
- π¬ **Discussions:** [GitHub Discussions](https://github.com/jetio/validator/discussions)
|
|
1215
|
+
- π **Issues:** [Issue Tracker](https://github.com/jetio/validator/issues)
|
|
1216
|
+
|
|
1217
|
+
---
|
|
1218
|
+
|
|
1219
|
+
**All contributions are appreciated!** Even small improvements help make jet-validator better for everyone.
|
|
1220
|
+
|
|
1221
|
+
---
|
|
1222
|
+
|
|
1223
|
+
## π Found an Issue?
|
|
1224
|
+
|
|
1225
|
+
We track bugs and feature requests using GitHub Issues.
|
|
1226
|
+
|
|
1227
|
+
### Before Filing an Issue
|
|
1228
|
+
|
|
1229
|
+
1. **Search existing issues** - Your issue might already be reported
|
|
1230
|
+
2. **Check the documentation** - The answer might be in the [20k+ line docs](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md)
|
|
1231
|
+
3. **Try the latest version** - `npm install @jetio/validator@latest`
|
|
1232
|
+
|
|
1233
|
+
### Types of Issues
|
|
1234
|
+
|
|
1235
|
+
- π **Bug Report:** Something isn't working correctly
|
|
1236
|
+
|
|
1237
|
+
- Include: Schema, data, expected vs actual behavior, error messages
|
|
1238
|
+
- [File a bug report β](https://github.com/jetio/validator/issues/new?labels=bug)
|
|
1239
|
+
|
|
1240
|
+
- π‘ **Feature Request:** Suggest a new feature or improvement
|
|
1241
|
+
|
|
1242
|
+
- Include: Use case, examples, why it's needed
|
|
1243
|
+
- [Request a feature β](https://github.com/jetio/validator/issues/new?labels=enhancement)
|
|
1244
|
+
|
|
1245
|
+
- β **Question:** Need help or clarification
|
|
1246
|
+
|
|
1247
|
+
- Use [GitHub Discussions](https://github.com/jetio/validator/discussions) for questions
|
|
1248
|
+
- Issues are for bugs and features only
|
|
1249
|
+
|
|
1250
|
+
- π **Documentation:** Docs are unclear or missing information
|
|
1251
|
+
- [Improve docs β](https://github.com/jetio/validator/issues/new?labels=documentation)
|
|
1252
|
+
|
|
1253
|
+
### Good Bug Report Template
|
|
1254
|
+
|
|
1255
|
+
```markdown
|
|
1256
|
+
**Description:** Brief description of the issue
|
|
1257
|
+
|
|
1258
|
+
**Schema:**
|
|
1259
|
+
\`\`\`json
|
|
1260
|
+
{
|
|
1261
|
+
"type": "object",
|
|
1262
|
+
// your schema here
|
|
1263
|
+
}
|
|
1264
|
+
\`\`\`
|
|
1265
|
+
|
|
1266
|
+
**Data:**
|
|
1267
|
+
\`\`\`json
|
|
1268
|
+
{
|
|
1269
|
+
// your data here
|
|
1270
|
+
}
|
|
1271
|
+
\`\`\`
|
|
1272
|
+
|
|
1273
|
+
**Expected:** What should happen
|
|
1274
|
+
|
|
1275
|
+
**Actual:** What actually happens
|
|
1276
|
+
|
|
1277
|
+
**Environment:**
|
|
1278
|
+
|
|
1279
|
+
- jet-validator version:
|
|
1280
|
+
- Node.js version:
|
|
1281
|
+
- OS:
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
---
|
|
1285
|
+
|
|
1286
|
+
**π Quick Links:**
|
|
1287
|
+
|
|
1288
|
+
- [Report a Bug](https://github.com/jetio/validator/issues/new?labels=bug)
|
|
1289
|
+
- [Request a Feature](https://github.com/jetio/validator/issues/new?labels=enhancement)
|
|
1290
|
+
- [Ask a Question](https://github.com/jetio/validator/discussions)
|
|
1291
|
+
|
|
1292
|
+
---
|
|
1293
|
+
|
|
1294
|
+
## β οΈ Benchmark Methodology & Hardware Notes
|
|
1295
|
+
|
|
1296
|
+
The benchmarks took hours to run due to laptop hardware and the billions of operations performed. The `array100KItems` benchmark aloneβvalidating 100K array items with 1,000 warmups, 10,000 iterations, and 5 runsβwas particularly intensive.
|
|
1297
|
+
|
|
1298
|
+
**Performance on Better Hardware:**
|
|
1299
|
+
|
|
1300
|
+
Results will vary significantly on better hardware. While absolute numbers will improve across the board, jet-validator is expected to maintain its performance advantages, especially in compilation speed.
|
|
1301
|
+
|
|
1302
|
+
**Implementation Differences:**
|
|
1303
|
+
|
|
1304
|
+
Unlike AJV, jet-validator doesn't inline `$ref`s by default. This design choice:
|
|
1305
|
+
|
|
1306
|
+
- Reduces code bloat
|
|
1307
|
+
- Enables proper tracing for `unevaluatedItems`/`unevaluatedProperties`
|
|
1308
|
+
- Maintains cleaner stack traces for debugging
|
|
1309
|
+
|
|
1310
|
+
Optional inlining is a feature we plan to add in the future for users who prefer maximum runtime performance over bundle size.
|
|
1311
|
+
|
|
1312
|
+
---
|
|
1313
|
+
|
|
1314
|
+
## π Conclusion
|
|
1315
|
+
|
|
1316
|
+
jet-validator has proven itself as a strong contender for JSON Schema validation in the Node.js ecosystem, offering:
|
|
1317
|
+
|
|
1318
|
+
β
**99.5% JSON Schema compliance** - More spec-compliant than AJV in many areas
|
|
1319
|
+
β
**10-27x faster compilation** - Game-changer for serverless
|
|
1320
|
+
β
**7% faster valid data** - Better average throughput
|
|
1321
|
+
β
**24% faster invalid data** - Better security posture
|
|
1322
|
+
β
**49% valid win rate** - More common-case wins
|
|
1323
|
+
β
**66% invalid win rate** - Dominant error detection
|
|
1324
|
+
|
|
1325
|
+
**jet-validator is the clear choice for:**
|
|
1326
|
+
|
|
1327
|
+
- Serverless applications (cold start critical)
|
|
1328
|
+
- High-volume validation workloads
|
|
1329
|
+
- Real-world schema validation
|
|
1330
|
+
- Applications requiring fast compilation
|
|
1331
|
+
- Security-focused applications (invalid data detection)
|
|
1332
|
+
|
|
1333
|
+
**Consider AJV for:**
|
|
1334
|
+
|
|
1335
|
+
- Extremely deep composition chains (>10 levels)
|
|
1336
|
+
- Heavy reliance on complex $ref graphs
|
|
1337
|
+
- Schemas with complex conditional logic
|
|
1338
|
+
|
|
1339
|
+
---
|
|
1340
|
+
|
|
1341
|
+
Whether jet-validator is the "new king" depends on your use case, but the numbers speak for themselves.
|
|
1342
|
+
|
|
1343
|
+
---
|
|
1344
|
+
|
|
1345
|
+
## π License
|
|
1346
|
+
|
|
1347
|
+
MIT Β© [Great Venerable](https://github.com/greatvenerable)
|
|
1348
|
+
|
|
1349
|
+
---
|
|
1350
|
+
|
|
1351
|
+
## π Links
|
|
1352
|
+
|
|
1353
|
+
- **[npm Package](https://www.npmjs.com/package/@jetio/validator)**
|
|
1354
|
+
- **[GitHub Repository](https://github.com/greatvenerable/jet-validator)**
|
|
1355
|
+
- **[Complete Documentation (20k+ lines)](https://github.com/jetio/validator/blob/main/DOCUMENTATION.md)**
|
|
1356
|
+
- **[Benchmark Results](https://github.com/jetio/validator/blob/main/benchmark/results/COMPARISON.md)**
|
|
1357
|
+
- **[Issue Tracker](https://github.com/jetio/validator/issues)**
|
|
1358
|
+
- **[GitHub Discussions](https://github.com/jetio/validator/discussions)**
|
|
1359
|
+
|
|
1360
|
+
---
|
|
1361
|
+
|
|
1362
|
+
**Built with β€οΈ by [The Venerable Supreme](https://github.com/greatvenerable)**
|