@jetio/validator 1.0.2 → 1.0.4
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 +25 -59
- package/dist/compileSchema.d.ts +0 -1
- package/dist/compileSchema.js +129 -67
- package/dist/compileSchema.js.map +1 -1
- package/dist/index.cjs.js +165 -84
- package/dist/index.esm.js +165 -84
- package/dist/jet-validator.js +36 -16
- package/dist/jet-validator.js.map +1 -1
- package/dist/resolver.js +1 -1
- package/dist/resolver.js.map +1 -1
- package/dist/src/compileSchema.d.ts +0 -1
- package/dist/validator.umd.js +165 -88
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -363,28 +363,14 @@ For transparency, here are the keywords jet-validator intentionally does not sup
|
|
|
363
363
|
|
|
364
364
|
### Summary
|
|
365
365
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
- **Overall winner:** 71.8% of all benchmarks
|
|
366
|
+
🚀 **Compilation:** 19x faster (1.47ms vs 28.29ms)
|
|
367
|
+
✅ **Valid Data:** 58% win rate (36/62)
|
|
368
|
+
🛡️ **Invalid Data:** 73% win rate (45/62)
|
|
369
|
+
🏆 **Overall:** 72% win rate (89/124)
|
|
371
370
|
|
|
372
|
-
|
|
371
|
+
[📊 Full Benchmark Report](https://github.com/official-jetio/validator/blob/main/benchmarks/results/COMPARISON.md) | [📈 Detailed Results](https://github.com/official-jetio/validator/tree/main/benchmarks/results/)
|
|
373
372
|
|
|
374
|
-
|
|
375
|
-
- Pattern matching: Up to 88% faster
|
|
376
|
-
- Integer validation: 48-120% faster
|
|
377
|
-
- Object validation at scale: 28-51% faster
|
|
378
|
-
|
|
379
|
-
**Where AJV leads:**
|
|
380
|
-
|
|
381
|
-
- Deeply chained $refs: 88% faster
|
|
382
|
-
- Deep allOf chains: 68% faster
|
|
383
|
-
- Complex conditionals: 36% faster
|
|
384
|
-
|
|
385
|
-
[📊 Full Benchmark Report](https://github.com/official-jetio/validator/blob/main/benchmark/results/COMPARISON.md) | [📈 Detailed Results](https://github.com/official-jetio/validator/tree/main/benchmark/results/)
|
|
386
|
-
|
|
387
|
-
_Tested against AJV v8.17.1 using official benchmarks with 65 schemas, 1000 warmups, 10000 iterations, 5 runs per test_
|
|
373
|
+
_Tested against AJV v8.17.1 using official benchmarks with 65 schemas, 1000 warmups, 10000 iterations, 30 runs per test_
|
|
388
374
|
|
|
389
375
|
---
|
|
390
376
|
|
|
@@ -888,7 +874,6 @@ len_of("café"); // 4
|
|
|
888
874
|
|
|
889
875
|
**Use case:** Validating `minLength`/`maxLength` for strings with emoji or special Unicode characters.
|
|
890
876
|
|
|
891
|
-
**→ [See Full Utilities API Reference](https://github.com/official-jetio/validator/blob/main/DOCUMENTATION.md#utilities-api)**
|
|
892
877
|
|
|
893
878
|
---
|
|
894
879
|
|
|
@@ -910,18 +895,16 @@ const validate = jetValidator.compile(schema);
|
|
|
910
895
|
|
|
911
896
|
**Benefits of switching:**
|
|
912
897
|
|
|
913
|
-
- ⚡
|
|
898
|
+
- ⚡ 19x faster compilation
|
|
914
899
|
- 📦 Smaller bundle size
|
|
915
|
-
- 🎯 Built-in formats (no separate packages)
|
|
900
|
+
- 🎯 Built-in formats and error messages (no separate packages)
|
|
916
901
|
- ✨ Enhanced features (`elseIf`, better `$data`)
|
|
917
902
|
|
|
918
903
|
**What's different:**
|
|
919
904
|
|
|
920
|
-
- Error object structure
|
|
921
|
-
- Custom keywords use different API
|
|
922
|
-
- Meta-schema setup
|
|
923
|
-
|
|
924
|
-
**→ [See Migration Guide](https://github.com/official-jetio/validator/blob/main/DOCUMENTATION.md#migration-from-ajv)**
|
|
905
|
+
- Error object structure
|
|
906
|
+
- Custom keywords use different API
|
|
907
|
+
- Meta-schema setup with cli
|
|
925
908
|
|
|
926
909
|
---
|
|
927
910
|
|
|
@@ -939,7 +922,7 @@ const validate = jetValidator.compile(schema);
|
|
|
939
922
|
### Consider Alternatives If:
|
|
940
923
|
|
|
941
924
|
⚠️ You need 100% JSON Schema spec compliance (we're at 99.5%)
|
|
942
|
-
⚠️ 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
|
|
925
|
+
⚠️ 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)
|
|
943
926
|
⚠️ You need streaming validation for extremely large documents
|
|
944
927
|
|
|
945
928
|
---
|
|
@@ -951,7 +934,7 @@ const validate = jetValidator.compile(schema);
|
|
|
951
934
|
jet-validator eliminates infinite recursion through a unique resolution approach. Unlike other validators that resolve references during traversal (causing stack overflow), jet-validator:
|
|
952
935
|
|
|
953
936
|
1. **Collects** all references and identifiers
|
|
954
|
-
2. **Assigns** unique function names to each location
|
|
937
|
+
2. **Assigns** unique function names to each location when inlining is impossible
|
|
955
938
|
3. **Resolves** references by replacing them with function calls
|
|
956
939
|
|
|
957
940
|
This architecture enables both lightning-fast compilation and bulletproof circular reference handling.
|
|
@@ -965,14 +948,14 @@ This architecture enables both lightning-fast compilation and bulletproof circul
|
|
|
965
948
|
Traditional validators:
|
|
966
949
|
|
|
967
950
|
```typescript
|
|
968
|
-
// Slow: 20ms per compilation
|
|
951
|
+
// Slow: 10-20ms per compilation
|
|
969
952
|
const validate = ajv.compile(schema); // Must cache!
|
|
970
953
|
```
|
|
971
954
|
|
|
972
955
|
jet-validator:
|
|
973
956
|
|
|
974
957
|
```typescript
|
|
975
|
-
// Fast:
|
|
958
|
+
// Fast: 0.7ms per compilation
|
|
976
959
|
const validate = jetValidator.compile(schema); // Can recompile!
|
|
977
960
|
```
|
|
978
961
|
|
|
@@ -1141,7 +1124,7 @@ We welcome contributions! jet-validator is a community project and we appreciate
|
|
|
1141
1124
|
```bash
|
|
1142
1125
|
# Clone the repo
|
|
1143
1126
|
git clone https://github.com/official-jetio/validator
|
|
1144
|
-
cd
|
|
1127
|
+
cd validator
|
|
1145
1128
|
|
|
1146
1129
|
# Install dependencies
|
|
1147
1130
|
npm install
|
|
@@ -1159,8 +1142,7 @@ npm run test:draft6
|
|
|
1159
1142
|
npm run test
|
|
1160
1143
|
|
|
1161
1144
|
# Run benchmarks
|
|
1162
|
-
|
|
1163
|
-
npm run benchmark:compare
|
|
1145
|
+
node --expose-gc --max-old-space-size=4096 --max-semi-space-size=64 benchmarks/jet-validator-benchmark.js
|
|
1164
1146
|
```
|
|
1165
1147
|
|
|
1166
1148
|
### Testing Philosophy
|
|
@@ -1174,7 +1156,7 @@ jet-validator uses the official [JSON Schema Test Suite](https://github.com/json
|
|
|
1174
1156
|
3. Run the official test suite: `npm run test:draft2020-12` (or appropriate draft)
|
|
1175
1157
|
4. Check compliance rate - aim to maintain or improve current 99.5%+ compliance
|
|
1176
1158
|
|
|
1177
|
-
**The test runner** (`tests/test
|
|
1159
|
+
**The test runner** (`tests/test.ts`) validates against:
|
|
1178
1160
|
|
|
1179
1161
|
- 1,261+ tests for Draft 2020-12
|
|
1180
1162
|
- 1,227+ tests for Draft 2019-09
|
|
@@ -1294,23 +1276,10 @@ We track bugs and feature requests using GitHub Issues.
|
|
|
1294
1276
|
---
|
|
1295
1277
|
|
|
1296
1278
|
## ⚠️ Benchmark Methodology & Hardware Notes
|
|
1297
|
-
|
|
1298
|
-
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.
|
|
1299
|
-
|
|
1300
1279
|
**Performance on Better Hardware:**
|
|
1301
1280
|
|
|
1302
1281
|
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.
|
|
1303
1282
|
|
|
1304
|
-
**Implementation Differences:**
|
|
1305
|
-
|
|
1306
|
-
Unlike AJV, jet-validator doesn't inline `$ref`s by default. This design choice:
|
|
1307
|
-
|
|
1308
|
-
- Reduces code bloat
|
|
1309
|
-
- Enables proper tracing for `unevaluatedItems`/`unevaluatedProperties`
|
|
1310
|
-
- Maintains cleaner stack traces for debugging
|
|
1311
|
-
|
|
1312
|
-
Optional inlining is a feature we plan to add in the future for users who prefer maximum runtime performance over bundle size.
|
|
1313
|
-
|
|
1314
1283
|
---
|
|
1315
1284
|
|
|
1316
1285
|
## 🏆 Conclusion
|
|
@@ -1318,11 +1287,10 @@ Optional inlining is a feature we plan to add in the future for users who prefer
|
|
|
1318
1287
|
jet-validator has proven itself as a strong contender for JSON Schema validation in the Node.js ecosystem, offering:
|
|
1319
1288
|
|
|
1320
1289
|
✅ **99.5% JSON Schema compliance** - More spec-compliant than AJV in many areas
|
|
1321
|
-
|
|
1322
|
-
✅ **
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
✅ **66% invalid win rate** - Dominant error detection
|
|
1290
|
+
🚀 **Compilation:** 19x faster (1.47ms vs 28.29ms) on average - Game-changer for serverless
|
|
1291
|
+
✅ **Valid Data:** 58% win rate (36/62)
|
|
1292
|
+
🛡️ **Invalid Data:** 73% win rate (45/62)
|
|
1293
|
+
🏆 **Overall:** 72% win rate (89/124)
|
|
1326
1294
|
|
|
1327
1295
|
**jet-validator is the clear choice for:**
|
|
1328
1296
|
|
|
@@ -1331,12 +1299,10 @@ jet-validator has proven itself as a strong contender for JSON Schema validation
|
|
|
1331
1299
|
- Real-world schema validation
|
|
1332
1300
|
- Applications requiring fast compilation
|
|
1333
1301
|
- Security-focused applications (invalid data detection)
|
|
1334
|
-
|
|
1335
|
-
**Consider AJV for:**
|
|
1336
|
-
|
|
1337
|
-
- Extremely deep composition chains (>10 levels)
|
|
1338
1302
|
- Heavy reliance on complex $ref graphs
|
|
1339
1303
|
- Schemas with complex conditional logic
|
|
1304
|
+
- Extremely deep composition chains (>10 levels)
|
|
1305
|
+
|
|
1340
1306
|
|
|
1341
1307
|
---
|
|
1342
1308
|
|
|
@@ -1355,7 +1321,7 @@ MIT © [Great Venerable](https://github.com/greatvenerable)
|
|
|
1355
1321
|
- **[npm Package](https://www.npmjs.com/package/@jetio/validator)**
|
|
1356
1322
|
- **[GitHub Repository](https://github.com/@jetio/validator)**
|
|
1357
1323
|
- **[Complete Documentation (20k+ lines)](https://github.com/official-jetio/validator/blob/main/DOCUMENTATION.md)**
|
|
1358
|
-
- **[Benchmark Results](https://github.com/official-jetio/validator/blob/main/
|
|
1324
|
+
- **[Benchmark Results](https://github.com/official-jetio/validator/blob/main/benchmarks/results/COMPARISON.md)**
|
|
1359
1325
|
- **[Issue Tracker](https://github.com/official-jetio/validator/issues)**
|
|
1360
1326
|
- **[GitHub Discussions](https://github.com/official-jetio/validator/discussions)**
|
|
1361
1327
|
|
package/dist/compileSchema.d.ts
CHANGED
package/dist/compileSchema.js
CHANGED
|
@@ -113,7 +113,6 @@ class Compiler {
|
|
|
113
113
|
this.validateKeywords = new Set();
|
|
114
114
|
this.notLogic = false;
|
|
115
115
|
this.noreturn = false;
|
|
116
|
-
this.neutralError = false;
|
|
117
116
|
this.standAlone = false;
|
|
118
117
|
this.hasCompileKeyword = false;
|
|
119
118
|
this.needslen_of = false;
|
|
@@ -306,11 +305,10 @@ class Compiler {
|
|
|
306
305
|
return src.join("");
|
|
307
306
|
}
|
|
308
307
|
const schema = rootSchema;
|
|
309
|
-
const varName =
|
|
310
|
-
!this.options.
|
|
311
|
-
!this.options.coerceTypes
|
|
308
|
+
const varName = accessPattern === "rootData" ||
|
|
309
|
+
(inlined && (schema.default === undefined || !this.options.useDefaults))
|
|
312
310
|
? accessPattern
|
|
313
|
-
: "
|
|
311
|
+
: "jv" + counter++;
|
|
314
312
|
this.initializeDefault(src, schema, varName, accessPattern, inlined);
|
|
315
313
|
let shouldTrackProps;
|
|
316
314
|
let shouldTrackItems;
|
|
@@ -354,11 +352,16 @@ class Compiler {
|
|
|
354
352
|
return src.join("");
|
|
355
353
|
}
|
|
356
354
|
initializeDefault(src, schema, varName, accessPattern, inlined) {
|
|
357
|
-
if (
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
355
|
+
if (accessPattern !== "rootData") {
|
|
356
|
+
if (schema.default !== undefined && this.options.useDefaults) {
|
|
357
|
+
src.push(`let ${varName} = ${accessPattern};`, `if (${varName} === undefined || ${varName} === null) {`, `${varName} = ${JSON.stringify(schema.default)};`, "}");
|
|
358
|
+
}
|
|
359
|
+
else if (!inlined) {
|
|
360
|
+
const keyword = this.options.removeAdditional || this.options.coerceTypes
|
|
361
|
+
? "let"
|
|
362
|
+
: "const";
|
|
363
|
+
src.push(`${keyword} ${varName} = ${accessPattern};`);
|
|
364
|
+
}
|
|
362
365
|
}
|
|
363
366
|
if (this.options.coerceTypes) {
|
|
364
367
|
if (schema.type === "number" || schema.type === "integer") {
|
|
@@ -474,10 +477,20 @@ class Compiler {
|
|
|
474
477
|
src.push(`if(${extra.before} true){`);
|
|
475
478
|
src.push(`const ${funcValidator}Result = ${awaitPrefix}${schema.__functionName}${callArgs};`);
|
|
476
479
|
if (this.options.allErrors) {
|
|
477
|
-
src.push(`if (!${funcValidator}Result)
|
|
480
|
+
src.push(`if (!${funcValidator}Result){${extra.after}${this.notLogic ? "" : `${this.errorVariable} = ${this.errorVariable}.concat(${schema.__functionName}.errors);`}}`);
|
|
478
481
|
}
|
|
479
482
|
else {
|
|
480
|
-
|
|
483
|
+
if (this.noreturn) {
|
|
484
|
+
src.push(`if (!${funcValidator}Result){${extra.after}${this.notLogic ? "" : `${schema.__functionName}.errors.length > 1 ? (${this.errorVariable} = ${this.errorVariable}.concat(${schema.__functionName}.errors)) : ${this.errorVariable}.push(${schema.__functionName}.errors[0]);`}}`);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
if (this.mainFunctionName === schema.__functionName) {
|
|
488
|
+
src.push(`if (!${funcValidator}Result){${this.notLogic ? extra.after : `return false;`}}`);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
src.push(`if (!${funcValidator}Result){${this.notLogic ? extra.after : `${this.mainFunctionName}.errors = ${schema.__functionName}.errors;return false;`}}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
481
494
|
}
|
|
482
495
|
if (extra.before != "")
|
|
483
496
|
src.push(`}`);
|
|
@@ -503,10 +516,20 @@ class Compiler {
|
|
|
503
516
|
src.push(`if(${extra.before} true){`);
|
|
504
517
|
src.push(`const ${refValidator}Result = ${awaitPrefix}${functionName}${callArgs};`);
|
|
505
518
|
if (this.options.allErrors) {
|
|
506
|
-
src.push(`if (!${refValidator}Result)
|
|
519
|
+
src.push(`if (!${refValidator}Result){${extra.after}${this.notLogic ? "" : `${this.errorVariable} = ${this.errorVariable}.concat(${functionName}.errors);`}}`);
|
|
507
520
|
}
|
|
508
521
|
else {
|
|
509
|
-
|
|
522
|
+
if (this.noreturn) {
|
|
523
|
+
src.push(`if (!${refValidator}Result){${extra.after}${this.notLogic ? "" : `${functionName}.errors.length > 1 ? (${this.errorVariable} = ${this.errorVariable}.concat(${functionName}.errors)) : ${this.errorVariable}.push(${functionName}.errors[0]);`}}`);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
if (this.mainFunctionName === functionName) {
|
|
527
|
+
src.push(`if (!${refValidator}Result){${this.notLogic ? extra.after : `return false;`}}`);
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
src.push(`if (!${refValidator}Result){${this.notLogic ? extra.after : `${this.mainFunctionName}.errors = ${functionName}.errors;return false;`}}`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
510
533
|
}
|
|
511
534
|
if (extra.before != "")
|
|
512
535
|
src.push(`}`);
|
|
@@ -525,12 +548,12 @@ class Compiler {
|
|
|
525
548
|
return `(${args.join(", ")})`;
|
|
526
549
|
}
|
|
527
550
|
handleLogicalOperators(src, schema, varName, pathContext, trackingState, extra) {
|
|
551
|
+
if (schema.allOf)
|
|
552
|
+
this.handleAllOfOperator(src, schema, varName, pathContext, trackingState, extra);
|
|
528
553
|
if (schema.not)
|
|
529
554
|
this.handleNotOperator(src, schema, varName, pathContext, extra);
|
|
530
555
|
if (schema.anyOf)
|
|
531
556
|
this.handleAnyOfOperator(src, schema, varName, pathContext, trackingState, extra);
|
|
532
|
-
if (schema.allOf)
|
|
533
|
-
this.handleAllOfOperator(src, schema, varName, pathContext, trackingState, extra);
|
|
534
557
|
if (schema.oneOf)
|
|
535
558
|
this.handleOneOfOperator(src, schema, varName, pathContext, trackingState, extra);
|
|
536
559
|
}
|
|
@@ -564,9 +587,12 @@ class Compiler {
|
|
|
564
587
|
let firstLength = "";
|
|
565
588
|
const anyOfValid = "anyOfValid" + counter++;
|
|
566
589
|
src.push(`let ${anyOfValid} = false;`);
|
|
567
|
-
const anyOfError =
|
|
568
|
-
|
|
590
|
+
const anyOfError = this.noreturn
|
|
591
|
+
? this.errorVariable
|
|
592
|
+
: "anyOfErr" + counter++;
|
|
593
|
+
if (!this.options.allErrors && !this.noreturn)
|
|
569
594
|
src.push(`let ${anyOfError} = [];`);
|
|
595
|
+
const error = this.errorVariable;
|
|
570
596
|
schema.anyOf.forEach((subSchema, index) => {
|
|
571
597
|
const branch = `branch${counter++}Valid`;
|
|
572
598
|
if (!this.options.allErrors)
|
|
@@ -577,24 +603,27 @@ class Compiler {
|
|
|
577
603
|
const configs = this.createSubschemaOptions(trackingState, pathContext, `anyOf/${index}`, schema);
|
|
578
604
|
configs.pathContext.alt = `${pathContext.schema}/anyOf/${index}`;
|
|
579
605
|
configs.pathContext.alt2 = `${pathContext.schema}/anyOf`;
|
|
606
|
+
let errorCountVar = "anyErrCnt" + counter++;
|
|
580
607
|
if (this.options.allErrors) {
|
|
581
608
|
validatorFn = this.compileSchema(subSchema, configs.pathContext, configs.trackingState, varName, extra, true);
|
|
582
609
|
}
|
|
583
610
|
else {
|
|
611
|
+
this.errorVariable = anyOfError;
|
|
584
612
|
validatorFn = this.compileSchema(subSchema, configs.pathContext, configs.trackingState, varName, {
|
|
585
613
|
before: `${branch} && `,
|
|
586
|
-
after: `${branch} = false
|
|
587
|
-
refAfter: `${branch} = false;${this.notLogic ? "" : `${anyOfError} = ${anyOfError}.concat(${this.mainFunctionName}.errors);`}`,
|
|
614
|
+
after: `${branch} = false;`,
|
|
588
615
|
}, true);
|
|
589
616
|
}
|
|
590
|
-
this.noreturn = noreturn;
|
|
591
|
-
let errorCountVar;
|
|
592
617
|
if (this.options.allErrors) {
|
|
593
|
-
errorCountVar = "anyErrCnt" + counter++;
|
|
594
618
|
src.push(`const ${errorCountVar} = ${this.errorVariable}.length;`);
|
|
595
619
|
if (index === 0)
|
|
596
620
|
firstLength = errorCountVar;
|
|
597
621
|
}
|
|
622
|
+
else if (index === 0) {
|
|
623
|
+
src.push(`const ${errorCountVar} = ${this.errorVariable}.length;`);
|
|
624
|
+
firstLength = errorCountVar;
|
|
625
|
+
}
|
|
626
|
+
this.noreturn = noreturn;
|
|
598
627
|
if (index > 0 &&
|
|
599
628
|
!trackingState.shouldTrackEvaluatedProperties &&
|
|
600
629
|
!trackingState.shouldTrackEvaluatedItems) {
|
|
@@ -629,27 +658,36 @@ class Compiler {
|
|
|
629
658
|
}
|
|
630
659
|
});
|
|
631
660
|
if (this.options.allErrors) {
|
|
632
|
-
src.push(`if (${anyOfValid}) {${this.errorVariable}.length = ${firstLength};}${extra.after != "" ? `else{${extra.after}}` : ""}`);
|
|
661
|
+
src.push(`if (${anyOfValid} && ${this.errorVariable}.length != ${firstLength}) {${this.errorVariable}.length = ${firstLength};}${extra.after != "" ? `else if(!${anyOfValid}){${extra.after}}` : ""}`);
|
|
633
662
|
}
|
|
634
663
|
else {
|
|
635
|
-
|
|
664
|
+
if (this.noreturn) {
|
|
665
|
+
src.push(`if (${anyOfValid} && ${this.errorVariable}.length != ${firstLength}){${this.errorVariable}.length = ${firstLength};}else if(!${anyOfValid}){${extra.after}}`);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
src.push(`if (${anyOfValid} && ${this.errorVariable}.length != ${firstLength}){${this.errorVariable}.length = ${firstLength};}else if(!${anyOfValid}){${this.notLogic ? extra.after : `${this.mainFunctionName}.errors = ${this.errorVariable};return false;`}}`);
|
|
669
|
+
}
|
|
670
|
+
this.errorVariable = error;
|
|
636
671
|
}
|
|
637
672
|
}
|
|
638
673
|
handleOneOfOperator(src, schema, varName, pathContext, trackingState, extra) {
|
|
639
674
|
if (!schema.oneOf)
|
|
640
675
|
return;
|
|
641
676
|
let firstLength = "";
|
|
642
|
-
const oneOfErrors =
|
|
677
|
+
const oneOfErrors = this.noreturn
|
|
678
|
+
? this.errorVariable
|
|
679
|
+
: `oneOfErrors${counter++}`;
|
|
643
680
|
const validSchemaCount = "validSchemaCount" + counter++;
|
|
644
681
|
src.push(`let ${validSchemaCount} = 0;`);
|
|
645
|
-
if (!this.options.allErrors)
|
|
682
|
+
if (!this.options.allErrors && !this.noreturn)
|
|
646
683
|
src.push(`let ${oneOfErrors} = [];`);
|
|
684
|
+
const noreturn = this.noreturn;
|
|
685
|
+
const error = this.errorVariable;
|
|
647
686
|
schema.oneOf.forEach((subSchema, index) => {
|
|
648
687
|
const branch = `branch${counter++}Valid`;
|
|
649
|
-
if (!this.options.allErrors
|
|
688
|
+
if (!this.options.allErrors)
|
|
650
689
|
src.push(`let ${branch} = true;`);
|
|
651
690
|
let validatorFn;
|
|
652
|
-
const noreturn = this.noreturn;
|
|
653
691
|
this.noreturn = true;
|
|
654
692
|
const configs = this.createSubschemaOptions(trackingState, pathContext, `oneOf/${index}`, schema);
|
|
655
693
|
configs.pathContext.alt = `${pathContext.schema}/oneOf/${index}`;
|
|
@@ -658,20 +696,22 @@ class Compiler {
|
|
|
658
696
|
validatorFn = this.compileSchema(subSchema, configs.pathContext, configs.trackingState, varName, extra, true);
|
|
659
697
|
}
|
|
660
698
|
else {
|
|
699
|
+
this.errorVariable = oneOfErrors;
|
|
661
700
|
validatorFn = this.compileSchema(subSchema, configs.pathContext, configs.trackingState, varName, {
|
|
662
701
|
before: `${branch} && `,
|
|
663
|
-
after: `${branch} = false
|
|
664
|
-
refAfter: `${branch} = false;${this.notLogic ? "" : `${oneOfErrors} = ${oneOfErrors}.concat(${this.mainFunctionName}.errors);`}`,
|
|
702
|
+
after: `${branch} = false;`,
|
|
665
703
|
}, true);
|
|
666
704
|
}
|
|
667
|
-
|
|
668
|
-
let errorCountVar;
|
|
705
|
+
let errorCountVar = "oneErrCnt" + counter++;
|
|
669
706
|
if (this.options.allErrors) {
|
|
670
|
-
errorCountVar = "oneErrCnt" + counter;
|
|
671
707
|
src.push(`const ${errorCountVar} = ${this.errorVariable}.length;`);
|
|
672
708
|
if (index === 0)
|
|
673
709
|
firstLength = errorCountVar;
|
|
674
710
|
}
|
|
711
|
+
else if (index === 0) {
|
|
712
|
+
src.push(`const ${errorCountVar} = ${this.errorVariable}.length;`);
|
|
713
|
+
firstLength = errorCountVar;
|
|
714
|
+
}
|
|
675
715
|
if (index > 0 &&
|
|
676
716
|
!trackingState.shouldTrackEvaluatedProperties &&
|
|
677
717
|
!trackingState.shouldTrackEvaluatedItems) {
|
|
@@ -706,7 +746,7 @@ class Compiler {
|
|
|
706
746
|
}
|
|
707
747
|
});
|
|
708
748
|
if (this.options.allErrors) {
|
|
709
|
-
src.push(`if (${validSchemaCount} == 1) {${this.errorVariable}.length = ${firstLength};}
|
|
749
|
+
src.push(`if (${validSchemaCount} == 1 && ${this.errorVariable}.length != ${firstLength}) {${this.errorVariable}.length = ${firstLength};}else if(${validSchemaCount} != 1){${this.buildErrorReturn(pathContext, {
|
|
710
750
|
keyword: "oneOf",
|
|
711
751
|
value: varName,
|
|
712
752
|
message: `"Data must validate against exactly one schema, but matched "+ ${validSchemaCount}`,
|
|
@@ -714,14 +754,23 @@ class Compiler {
|
|
|
714
754
|
})}${extra.after}}`);
|
|
715
755
|
}
|
|
716
756
|
else {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}
|
|
757
|
+
if (noreturn) {
|
|
758
|
+
src.push(`if (${validSchemaCount} == 1 && ${this.errorVariable}.length != ${firstLength}){${this.errorVariable}.length = ${firstLength};}else if(${validSchemaCount} != 1){${this.buildErrorReturn(pathContext, {
|
|
759
|
+
keyword: "oneOf",
|
|
760
|
+
value: varName,
|
|
761
|
+
message: `"Data must validate against exactly one schema, but matched "+ ${validSchemaCount}`,
|
|
762
|
+
expected: '"exactly one schema"',
|
|
763
|
+
})}${extra.after}}`);
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
src.push(`if (${validSchemaCount} == 1 && ${this.errorVariable}.length != ${firstLength}){${this.errorVariable}.length = ${firstLength};}else if(${validSchemaCount} != 1){${this.buildErrorReturn(pathContext, {
|
|
767
|
+
keyword: "oneOf",
|
|
768
|
+
value: varName,
|
|
769
|
+
message: `"Data must validate against exactly one schema, but matched "+ ${validSchemaCount}`,
|
|
770
|
+
expected: '"exactly one schema"',
|
|
771
|
+
})}${this.notLogic ? extra.after : `${this.mainFunctionName}.errors = ${this.errorVariable};return false;`}}`);
|
|
772
|
+
}
|
|
773
|
+
this.errorVariable = error;
|
|
725
774
|
this.noreturn = noreturn;
|
|
726
775
|
}
|
|
727
776
|
}
|
|
@@ -1309,7 +1358,7 @@ class Compiler {
|
|
|
1309
1358
|
else {
|
|
1310
1359
|
const data = this.jetValidator.getFormat(schema.format);
|
|
1311
1360
|
if (!data) {
|
|
1312
|
-
|
|
1361
|
+
console.warn(`Format '${schema.format}' not found will be ignored`);
|
|
1313
1362
|
}
|
|
1314
1363
|
const format = typeof data === "object" && "validate" in data ? data.validate : data;
|
|
1315
1364
|
const formatType = typeof data === "object" &&
|
|
@@ -1431,24 +1480,39 @@ class Compiler {
|
|
|
1431
1480
|
if (Array.isArray(schema.required)) {
|
|
1432
1481
|
if (this.options.allErrors ||
|
|
1433
1482
|
schema.required.length > this.options.loopRequired) {
|
|
1434
|
-
const arr = JSON.stringify(schema.required);
|
|
1435
|
-
const arrVar = `arr${src.length}${counter++}`;
|
|
1436
|
-
const iVar = `i${src.length}${counter++}`;
|
|
1437
|
-
src.push(`const ${arrVar} = ${arr};`);
|
|
1438
1483
|
if (extra.before != "")
|
|
1439
1484
|
src.push(`if(${extra.before} true){`);
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1485
|
+
if (schema.required.length > this.options.loopRequired) {
|
|
1486
|
+
const arr = JSON.stringify(schema.required);
|
|
1487
|
+
const arrVar = `arr${src.length}${counter++}`;
|
|
1488
|
+
const iVar = `i${src.length}${counter++}`;
|
|
1489
|
+
src.push(`const ${arrVar} = ${arr};`);
|
|
1490
|
+
src.push(`for (let ${iVar} = 0; ${iVar} < ${arrVar}.length; ${iVar}++) {`);
|
|
1491
|
+
const prop = "prop" + counter++;
|
|
1492
|
+
src.push(`const ${prop} = ${arrVar}[${iVar}];`);
|
|
1493
|
+
addEvaluatedProperty(src, prop, trackingState);
|
|
1494
|
+
src.push(`if (${extra.before}${varName}[${prop}] === undefined) {${this.buildErrorReturn(pathContext, {
|
|
1495
|
+
keyword: "required",
|
|
1496
|
+
value: varName,
|
|
1497
|
+
message: `"Missing required field: " + ${prop} + " in data."`,
|
|
1498
|
+
expected: `${prop}`,
|
|
1499
|
+
schemaPath: `${pathContext.schema}`,
|
|
1500
|
+
})}${extra.after}}`);
|
|
1501
|
+
src.push(`}`);
|
|
1502
|
+
}
|
|
1503
|
+
else {
|
|
1504
|
+
for (const prop of schema.required) {
|
|
1505
|
+
const errorMessage = JSON.stringify(`Missing required field: ${prop} in data.`);
|
|
1506
|
+
const pstring = JSON.stringify(prop);
|
|
1507
|
+
src.push(`if (${extra.before}${varName}[${pstring}] === undefined) {${this.buildErrorReturn(pathContext, {
|
|
1508
|
+
keyword: "required",
|
|
1509
|
+
value: varName,
|
|
1510
|
+
message: errorMessage,
|
|
1511
|
+
expected: `${pstring}`,
|
|
1512
|
+
schemaPath: `${pathContext.schema}`,
|
|
1513
|
+
})}${extra.after}}`);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1452
1516
|
if (extra.before != "")
|
|
1453
1517
|
src.push(`}`);
|
|
1454
1518
|
}
|
|
@@ -2116,9 +2180,7 @@ class Compiler {
|
|
|
2116
2180
|
buildErrorReturn(pathContext, error, spreads) {
|
|
2117
2181
|
if (this.notLogic)
|
|
2118
2182
|
return "";
|
|
2119
|
-
|
|
2120
|
-
return "return false;";
|
|
2121
|
-
let result = this.options.allErrors
|
|
2183
|
+
let result = this.options.allErrors || this.noreturn
|
|
2122
2184
|
? `${this.errorVariable}.push({`
|
|
2123
2185
|
: `${this.mainFunctionName}.errors = [{`;
|
|
2124
2186
|
const escapedDataPath = (0, utilities_1.escapeTemplateString)(error.dataPath || pathContext.data || "/");
|
|
@@ -2126,11 +2188,11 @@ class Compiler {
|
|
|
2126
2188
|
result += `dataPath: \`${escapedDataPath}\`,`;
|
|
2127
2189
|
result += `schemaPath: \`${escapedSchemaPath}\`,`;
|
|
2128
2190
|
result += `keyword: "${error.keyword}",`;
|
|
2191
|
+
if (error.expected) {
|
|
2192
|
+
result += `expected: ${error.expected},`;
|
|
2193
|
+
}
|
|
2129
2194
|
if (this.options.verbose) {
|
|
2130
2195
|
result += `value: ${error.value},`;
|
|
2131
|
-
if (error.expected) {
|
|
2132
|
-
result += `expected: ${error.expected},`;
|
|
2133
|
-
}
|
|
2134
2196
|
}
|
|
2135
2197
|
let errorMessage;
|
|
2136
2198
|
if (this.options.errorMessage && typeof this.schema !== "boolean") {
|
|
@@ -2199,7 +2261,7 @@ class Compiler {
|
|
|
2199
2261
|
result += `,${spreads}`;
|
|
2200
2262
|
}
|
|
2201
2263
|
result += "}";
|
|
2202
|
-
if (this.options.allErrors) {
|
|
2264
|
+
if (this.options.allErrors || this.noreturn) {
|
|
2203
2265
|
result += ");";
|
|
2204
2266
|
}
|
|
2205
2267
|
else {
|