@sourcemeta/blaze 16.1.0 → 16.2.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.
Files changed (3) hide show
  1. package/describe.mjs +24 -7
  2. package/index.mjs +62 -23
  3. package/package.json +1 -1
package/describe.mjs CHANGED
@@ -135,6 +135,12 @@ function resolveTarget(instance, instanceLocation) {
135
135
  return current;
136
136
  }
137
137
 
138
+ function instanceParent(instance, instanceLocation) {
139
+ if (instanceLocation === '') return undefined;
140
+ const lastSlash = instanceLocation.lastIndexOf('/');
141
+ return resolveTarget(instance, instanceLocation.slice(0, lastSlash));
142
+ }
143
+
138
144
  function extractKeyword(evaluatePath) {
139
145
  if (evaluatePath === '') return '';
140
146
  const lastSlash = evaluatePath.lastIndexOf('/');
@@ -430,13 +436,20 @@ export function describe(valid, instruction, evaluatePath,
430
436
  if (keyword === 'contains') {
431
437
  return 'The constraints declared for this keyword were not satisfiable';
432
438
  }
433
- if (keyword === 'additionalProperties' ||
434
- keyword === 'unevaluatedProperties') {
439
+ // A `false` subschema declares no keyword of its own, so a property named
440
+ // after one of these must not be described as if it were that keyword.
441
+ // The instance location agrees only when it sits under an object for the
442
+ // property cases, or under an array for the item case
443
+ const parent = instanceParent(instance, instanceLocation);
444
+ if ((keyword === 'additionalProperties' ||
445
+ keyword === 'unevaluatedProperties') &&
446
+ parent !== undefined && parent !== null && typeof parent === 'object' &&
447
+ !Array.isArray(parent)) {
435
448
  const property = lastInstanceToken(instanceLocation);
436
449
  return 'The object value was not expected to define the property ' +
437
450
  escapeString(property);
438
451
  }
439
- if (keyword === 'unevaluatedItems') {
452
+ if (keyword === 'unevaluatedItems' && Array.isArray(parent)) {
440
453
  const tokenValue = lastInstanceToken(instanceLocation);
441
454
  return 'The array value was not expected to define the item at index ' +
442
455
  tokenValue;
@@ -560,7 +573,11 @@ export function describe(valid, instruction, evaluatePath,
560
573
  'positional subschemas';
561
574
  }
562
575
 
563
- if (keyword === 'title' || keyword === 'description') {
576
+ // Annotation values come from the schema as written, so they need not have
577
+ // the type their keyword expects. When they do not, the generic fallthrough
578
+ // below describes them instead
579
+ if ((keyword === 'title' || keyword === 'description') &&
580
+ typeof annotation === 'string') {
564
581
  let message = 'The ' + keyword + ' of the';
565
582
  if (instanceLocation === '') {
566
583
  message += ' instance';
@@ -621,7 +638,7 @@ export function describe(valid, instruction, evaluatePath,
621
638
  return message;
622
639
  }
623
640
 
624
- if (keyword === 'examples') {
641
+ if (keyword === 'examples' && Array.isArray(annotation)) {
625
642
  let message = '';
626
643
  if (instanceLocation === '') {
627
644
  message += 'Examples of the instance';
@@ -640,7 +657,7 @@ export function describe(valid, instruction, evaluatePath,
640
657
  return message;
641
658
  }
642
659
 
643
- if (keyword === 'contentEncoding') {
660
+ if (keyword === 'contentEncoding' && typeof annotation === 'string') {
644
661
  let message = 'The content encoding of the';
645
662
  if (instanceLocation === '') {
646
663
  message += ' instance';
@@ -651,7 +668,7 @@ export function describe(valid, instruction, evaluatePath,
651
668
  return message;
652
669
  }
653
670
 
654
- if (keyword === 'contentMediaType') {
671
+ if (keyword === 'contentMediaType' && typeof annotation === 'string') {
655
672
  let message = 'The content media type of the';
656
673
  if (instanceLocation === '') {
657
674
  message += ' instance';
package/index.mjs CHANGED
@@ -647,6 +647,14 @@ class Blaze {
647
647
  if (match) entry.skip = true;
648
648
  }
649
649
  }
650
+
651
+ checkpoint() {
652
+ return this.evaluated.length;
653
+ }
654
+
655
+ rewind(checkpoint) {
656
+ this.evaluated.length = checkpoint;
657
+ }
650
658
  }
651
659
 
652
660
  function evaluateInstructionFast(instruction, instance, depth, template, evaluator) {
@@ -1646,16 +1654,21 @@ function LogicalOr(instruction, instance, depth, template, evaluator) {
1646
1654
  let result = false;
1647
1655
  if (exhaustive) {
1648
1656
  for (let index = 0; index < children.length; index++) {
1657
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
1649
1658
  if (evaluateInstruction(children[index], target, depth + 1, template, evaluator)) {
1650
1659
  result = true;
1660
+ } else if (evaluator.trackMode) {
1661
+ evaluator.rewind(checkpoint);
1651
1662
  }
1652
1663
  }
1653
1664
  } else {
1654
1665
  for (let index = 0; index < children.length; index++) {
1666
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
1655
1667
  if (evaluateInstruction(children[index], target, depth + 1, template, evaluator)) {
1656
1668
  result = true;
1657
1669
  break;
1658
1670
  }
1671
+ if (evaluator.trackMode) evaluator.rewind(checkpoint);
1659
1672
  }
1660
1673
  }
1661
1674
  if (evaluator.callbackMode) evaluator.callbackPop(instruction, result);
@@ -1687,6 +1700,7 @@ function LogicalXor(instruction, instance, depth, template, evaluator) {
1687
1700
  let hasMatched = false;
1688
1701
  if (children) {
1689
1702
  for (let index = 0; index < children.length; index++) {
1703
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
1690
1704
  if (evaluateInstruction(children[index], target, depth + 1, template, evaluator)) {
1691
1705
  if (hasMatched) {
1692
1706
  result = false;
@@ -1694,6 +1708,8 @@ function LogicalXor(instruction, instance, depth, template, evaluator) {
1694
1708
  } else {
1695
1709
  hasMatched = true;
1696
1710
  }
1711
+ } else if (evaluator.trackMode) {
1712
+ evaluator.rewind(checkpoint);
1697
1713
  }
1698
1714
  }
1699
1715
  }
@@ -1710,24 +1726,29 @@ function LogicalCondition(instruction, instance, depth, template, evaluator) {
1710
1726
  const children = instruction[6];
1711
1727
  const childrenSize = children ? children.length : 0;
1712
1728
 
1713
- let conditionEnd = childrenSize;
1714
- if (thenStart > 0) conditionEnd = thenStart;
1715
- else if (elseStart > 0) conditionEnd = elseStart;
1716
-
1717
1729
  const target = resolveInstance(instance, instruction[2]);
1718
1730
 
1731
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
1719
1732
  let conditionResult = true;
1720
- for (let cursor = 0; cursor < conditionEnd; cursor++) {
1733
+ for (let cursor = 0; cursor < thenStart; cursor++) {
1721
1734
  if (!evaluateInstruction(children[cursor], target, depth + 1, template, evaluator)) {
1722
1735
  conditionResult = false;
1723
1736
  break;
1724
1737
  }
1725
1738
  }
1739
+ if (!conditionResult && evaluator.trackMode) evaluator.rewind(checkpoint);
1726
1740
 
1727
- const consequenceStart = conditionResult ? thenStart : elseStart;
1728
- const consequenceEnd = (conditionResult && elseStart > 0) ? elseStart : childrenSize;
1741
+ let consequenceStart;
1742
+ let consequenceEnd;
1743
+ if (conditionResult) {
1744
+ consequenceStart = thenStart;
1745
+ consequenceEnd = elseStart > 0 ? elseStart : childrenSize;
1746
+ } else {
1747
+ consequenceStart = elseStart;
1748
+ consequenceEnd = elseStart > 0 ? childrenSize : elseStart;
1749
+ }
1729
1750
 
1730
- if (consequenceStart > 0) {
1751
+ if (consequenceStart < consequenceEnd) {
1731
1752
  if (evaluator.trackMode || evaluator.callbackMode) {
1732
1753
  evaluator.popPath(instruction[1].length);
1733
1754
  }
@@ -2147,15 +2168,16 @@ function LoopPropertiesExactlyTypeStrict(instruction, instance, depth, template,
2147
2168
  return false;
2148
2169
  }
2149
2170
  const value = instruction[5];
2171
+ const names = new Set(value[1]);
2150
2172
  let count = 0;
2151
2173
  for (const key in target) {
2152
2174
  count++;
2153
- if (effectiveTypeStrictReal(target[key]) !== value[0]) {
2175
+ if (effectiveTypeStrictReal(target[key]) !== value[0] || !names.has(key)) {
2154
2176
  if (evaluator.callbackMode) evaluator.callbackPop(instruction, false);
2155
2177
  return false;
2156
2178
  }
2157
2179
  }
2158
- const __result = count === value[1].length;
2180
+ const __result = count === names.size;
2159
2181
  if (evaluator.callbackMode) evaluator.callbackPop(instruction, __result);
2160
2182
  return __result;
2161
2183
  };
@@ -2465,9 +2487,11 @@ function LoopItemsIntegerBoundedSized(instruction, instance, depth, template, ev
2465
2487
  const minimum = value[0][0];
2466
2488
  const maximum = value[0][1];
2467
2489
  const minimumSize = value[1][0];
2490
+ const maximumSize = value[1][1];
2468
2491
  const target = resolveInstance(instance, instruction[2]);
2469
2492
  if (evaluator.callbackMode) evaluator.callbackPush(instruction);
2470
- if (!Array.isArray(target) || target.length < minimumSize) {
2493
+ if (!Array.isArray(target) || target.length < minimumSize ||
2494
+ (maximumSize !== null && maximumSize !== undefined && target.length > maximumSize)) {
2471
2495
  if (evaluator.callbackMode) evaluator.callbackPop(instruction, false);
2472
2496
  return false;
2473
2497
  }
@@ -2494,11 +2518,10 @@ function LoopContains(instruction, instance, depth, template, evaluator) {
2494
2518
  const minimum = range[0];
2495
2519
  const maximum = range[1];
2496
2520
  const isExhaustive = range[2];
2497
- if (minimum === 0 && target.length === 0) return true;
2498
2521
  if (evaluator.callbackMode) evaluator.callbackPush(instruction);
2499
2522
 
2500
2523
  const children = instruction[6];
2501
- let result = false;
2524
+ let result = minimum === 0;
2502
2525
  let matchCount = 0;
2503
2526
  for (let index = 0; index < target.length; index++) {
2504
2527
  if (evaluator.callbackMode) evaluator.pushInstanceToken(index);
@@ -2813,11 +2836,15 @@ function LogicalOr_fast(instruction, instance, depth, template, evaluator) {
2813
2836
  let result = false;
2814
2837
  if (exhaustive) {
2815
2838
  for (let index = 0; index < children.length; index++) {
2839
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
2816
2840
  if (evaluateInstruction(children[index], target, depth + 1, template, evaluator)) result = true;
2841
+ else if (evaluator.trackMode) evaluator.rewind(checkpoint);
2817
2842
  }
2818
2843
  } else {
2819
2844
  for (let index = 0; index < children.length; index++) {
2845
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
2820
2846
  if (evaluateInstruction(children[index], target, depth + 1, template, evaluator)) return true;
2847
+ if (evaluator.trackMode) evaluator.rewind(checkpoint);
2821
2848
  }
2822
2849
  }
2823
2850
  return result;
@@ -2862,6 +2889,7 @@ function LogicalXor_fast(instruction, instance, depth, template, evaluator) {
2862
2889
  let hasMatched = false;
2863
2890
  if (children) {
2864
2891
  for (let index = 0; index < children.length; index++) {
2892
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
2865
2893
  if (evaluateInstruction(children[index], target, depth + 1, template, evaluator)) {
2866
2894
  if (hasMatched) {
2867
2895
  result = false;
@@ -2869,6 +2897,8 @@ function LogicalXor_fast(instruction, instance, depth, template, evaluator) {
2869
2897
  } else {
2870
2898
  hasMatched = true;
2871
2899
  }
2900
+ } else if (evaluator.trackMode) {
2901
+ evaluator.rewind(checkpoint);
2872
2902
  }
2873
2903
  }
2874
2904
  }
@@ -2965,21 +2995,27 @@ function LogicalCondition_fast(instruction, instance, depth, template, evaluator
2965
2995
  const elseStart = value[1];
2966
2996
  const children = instruction[6];
2967
2997
  const childrenSize = children ? children.length : 0;
2968
- let conditionEnd = childrenSize;
2969
- if (thenStart > 0) conditionEnd = thenStart;
2970
- else if (elseStart > 0) conditionEnd = elseStart;
2971
2998
  const relInstance = instruction[2];
2972
2999
  const target = relInstance.length === 0 ? instance : resolveInstance(instance, relInstance);
3000
+ const checkpoint = evaluator.trackMode ? evaluator.checkpoint() : 0;
2973
3001
  let conditionResult = true;
2974
- for (let cursor = 0; cursor < conditionEnd; cursor++) {
3002
+ for (let cursor = 0; cursor < thenStart; cursor++) {
2975
3003
  if (!evaluateInstruction(children[cursor], target, depth + 1, template, evaluator)) {
2976
3004
  conditionResult = false;
2977
3005
  break;
2978
3006
  }
2979
3007
  }
2980
- const consequenceStart = conditionResult ? thenStart : elseStart;
2981
- const consequenceEnd = (conditionResult && elseStart > 0) ? elseStart : childrenSize;
2982
- if (consequenceStart > 0) {
3008
+ if (!conditionResult && evaluator.trackMode) evaluator.rewind(checkpoint);
3009
+ let consequenceStart;
3010
+ let consequenceEnd;
3011
+ if (conditionResult) {
3012
+ consequenceStart = thenStart;
3013
+ consequenceEnd = elseStart > 0 ? elseStart : childrenSize;
3014
+ } else {
3015
+ consequenceStart = elseStart;
3016
+ consequenceEnd = elseStart > 0 ? childrenSize : elseStart;
3017
+ }
3018
+ if (consequenceStart < consequenceEnd) {
2983
3019
  if (evaluator.trackMode) {
2984
3020
  evaluator.popPath(instruction[1].length);
2985
3021
  }
@@ -3700,12 +3736,13 @@ function LoopPropertiesExactlyTypeStrict_fast(instruction, instance, depth, temp
3700
3736
  const target = resolveInstance(instance, instruction[2]);
3701
3737
  if (!isObject(target)) return false;
3702
3738
  const value = instruction[5];
3739
+ const names = new Set(value[1]);
3703
3740
  let count = 0;
3704
3741
  for (const key in target) {
3705
3742
  count++;
3706
- if (effectiveTypeStrictReal(target[key]) !== value[0]) return false;
3743
+ if (effectiveTypeStrictReal(target[key]) !== value[0] || !names.has(key)) return false;
3707
3744
  }
3708
- return count === value[1].length;
3745
+ return count === names.size;
3709
3746
  }
3710
3747
 
3711
3748
  function LoopPropertiesExactlyTypeStrictHash_fast(instruction, instance, depth, template, evaluator) {
@@ -3881,8 +3918,10 @@ function LoopItemsIntegerBoundedSized_fast(instruction, instance, depth, templat
3881
3918
  const minimum = value[0][0];
3882
3919
  const maximum = value[0][1];
3883
3920
  const minimumSize = value[1][0];
3921
+ const maximumSize = value[1][1];
3884
3922
  const target = resolveInstance(instance, instruction[2]);
3885
- if (!Array.isArray(target) || target.length < minimumSize) return false;
3923
+ if (!Array.isArray(target) || target.length < minimumSize ||
3924
+ (maximumSize !== null && maximumSize !== undefined && target.length > maximumSize)) return false;
3886
3925
  for (let index = 0; index < target.length; index++) {
3887
3926
  const element = target[index];
3888
3927
  const elementType = typeof element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sourcemeta/blaze",
3
- "version": "16.1.0",
3
+ "version": "16.2.1",
4
4
  "description": "A pure JavaScript port of the evaluator from Blaze, a high-performance C++ JSON Schema validator. Zero dependencies. Supports Draft 4, Draft 6, Draft 7, 2019-09, and 2020-12 with schema-specific code generation for near-native speed",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",