z-schema 12.0.4 → 12.1.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.
Files changed (176) hide show
  1. package/bin/z-schema +7 -2
  2. package/cjs/index.d.ts +541 -530
  3. package/cjs/index.js +3963 -7740
  4. package/dist/errors.d.mts +64 -0
  5. package/dist/errors.mjs +67 -0
  6. package/dist/format-validators.d.mts +14 -0
  7. package/dist/format-validators.mjs +261 -0
  8. package/dist/index.d.mts +10 -0
  9. package/dist/index.mjs +7 -0
  10. package/dist/json-schema-versions.d.mts +137 -0
  11. package/dist/json-schema-versions.mjs +11 -0
  12. package/dist/json-schema.d.mts +61 -0
  13. package/dist/json-schema.mjs +53 -0
  14. package/dist/json-validation.mjs +441 -0
  15. package/dist/report.d.mts +97 -0
  16. package/dist/report.mjs +194 -0
  17. package/dist/schema-cache.d.mts +23 -0
  18. package/dist/schema-cache.mjs +144 -0
  19. package/dist/schema-compiler.d.mts +23 -0
  20. package/dist/schema-compiler.mjs +265 -0
  21. package/dist/schema-validator.d.mts +15 -0
  22. package/dist/schema-validator.mjs +446 -0
  23. package/dist/schemas/draft-04-schema.mjs +125 -0
  24. package/dist/schemas/draft-06-schema.mjs +126 -0
  25. package/dist/schemas/draft-07-schema.mjs +141 -0
  26. package/dist/schemas/draft-2019-09-meta-applicator.mjs +46 -0
  27. package/dist/schemas/draft-2019-09-meta-content.mjs +15 -0
  28. package/dist/schemas/draft-2019-09-meta-core.mjs +52 -0
  29. package/dist/schemas/draft-2019-09-meta-format.mjs +11 -0
  30. package/dist/schemas/draft-2019-09-meta-meta-data.mjs +31 -0
  31. package/dist/schemas/draft-2019-09-meta-validation.mjs +80 -0
  32. package/dist/schemas/draft-2019-09-schema.mjs +39 -0
  33. package/dist/schemas/draft-2020-12-meta-applicator.mjs +45 -0
  34. package/dist/schemas/draft-2020-12-meta-content.mjs +15 -0
  35. package/dist/schemas/draft-2020-12-meta-core.mjs +46 -0
  36. package/dist/schemas/draft-2020-12-meta-format-annotation.mjs +11 -0
  37. package/dist/schemas/draft-2020-12-meta-format-assertion.mjs +11 -0
  38. package/dist/schemas/draft-2020-12-meta-meta-data.mjs +31 -0
  39. package/dist/schemas/draft-2020-12-meta-unevaluated.mjs +14 -0
  40. package/dist/schemas/draft-2020-12-meta-validation.mjs +80 -0
  41. package/dist/schemas/draft-2020-12-schema.mjs +55 -0
  42. package/dist/utils/array.mjs +54 -0
  43. package/dist/utils/base64.mjs +21 -0
  44. package/dist/utils/clone.mjs +42 -0
  45. package/dist/utils/constants.mjs +16 -0
  46. package/dist/utils/date.mjs +19 -0
  47. package/dist/utils/hostname.mjs +73 -0
  48. package/dist/utils/json.mjs +34 -0
  49. package/dist/utils/properties.mjs +11 -0
  50. package/dist/utils/schema-regex.mjs +49 -0
  51. package/dist/utils/symbols.mjs +5 -0
  52. package/dist/utils/time.mjs +42 -0
  53. package/dist/utils/unicode.mjs +12 -0
  54. package/dist/utils/uri.mjs +12 -0
  55. package/dist/utils/what-is.mjs +23 -0
  56. package/dist/validation/array.mjs +57 -0
  57. package/dist/validation/combinators.mjs +62 -0
  58. package/dist/validation/numeric.mjs +38 -0
  59. package/dist/validation/object.mjs +109 -0
  60. package/dist/validation/ref.mjs +49 -0
  61. package/dist/validation/shared.mjs +97 -0
  62. package/dist/validation/string.mjs +88 -0
  63. package/dist/validation/type.mjs +32 -0
  64. package/dist/z-schema-base.d.mts +85 -0
  65. package/dist/z-schema-base.mjs +230 -0
  66. package/dist/z-schema-options.d.mts +35 -0
  67. package/dist/z-schema-options.mjs +55 -0
  68. package/dist/z-schema-reader.d.mts +6 -0
  69. package/dist/z-schema-reader.mjs +10 -0
  70. package/dist/z-schema-versions.mjs +67 -0
  71. package/dist/z-schema.d.mts +193 -0
  72. package/dist/z-schema.mjs +300 -0
  73. package/package.json +49 -64
  74. package/src/index.ts +3 -3
  75. package/src/schema-compiler.ts +30 -10
  76. package/src/schema-validator.ts +1 -1
  77. package/src/utils/schema-regex.ts +13 -2
  78. package/src/z-schema-base.ts +1 -1
  79. package/src/z-schema-versions.ts +1 -2
  80. package/src/z-schema.ts +0 -1
  81. package/umd/ZSchema.js +5934 -8110
  82. package/umd/ZSchema.min.js +2 -1
  83. package/dist/errors.js +0 -72
  84. package/dist/format-validators.js +0 -392
  85. package/dist/index.js +0 -5
  86. package/dist/json-schema-versions.js +0 -8
  87. package/dist/json-schema.js +0 -84
  88. package/dist/json-validation.js +0 -728
  89. package/dist/package.json +0 -3
  90. package/dist/report.js +0 -265
  91. package/dist/schema-cache.js +0 -211
  92. package/dist/schema-compiler.js +0 -415
  93. package/dist/schema-validator.js +0 -721
  94. package/dist/schemas/draft-04-schema.json +0 -149
  95. package/dist/schemas/draft-06-schema.json +0 -155
  96. package/dist/schemas/draft-07-schema.json +0 -172
  97. package/dist/schemas/draft-2019-09-meta-applicator.json +0 -52
  98. package/dist/schemas/draft-2019-09-meta-content.json +0 -12
  99. package/dist/schemas/draft-2019-09-meta-core.json +0 -53
  100. package/dist/schemas/draft-2019-09-meta-format.json +0 -10
  101. package/dist/schemas/draft-2019-09-meta-meta-data.json +0 -32
  102. package/dist/schemas/draft-2019-09-meta-validation.json +0 -94
  103. package/dist/schemas/draft-2019-09-schema.json +0 -41
  104. package/dist/schemas/draft-2020-12-meta-applicator.json +0 -44
  105. package/dist/schemas/draft-2020-12-meta-content.json +0 -12
  106. package/dist/schemas/draft-2020-12-meta-core.json +0 -47
  107. package/dist/schemas/draft-2020-12-meta-format-annotation.json +0 -10
  108. package/dist/schemas/draft-2020-12-meta-format-assertion.json +0 -10
  109. package/dist/schemas/draft-2020-12-meta-meta-data.json +0 -32
  110. package/dist/schemas/draft-2020-12-meta-unevaluated.json +0 -11
  111. package/dist/schemas/draft-2020-12-meta-validation.json +0 -94
  112. package/dist/schemas/draft-2020-12-schema.json +0 -57
  113. package/dist/types/errors.d.ts +0 -64
  114. package/dist/types/format-validators.d.ts +0 -11
  115. package/dist/types/index.d.ts +0 -14
  116. package/dist/types/json-schema-versions.d.ts +0 -142
  117. package/dist/types/json-schema.d.ts +0 -66
  118. package/dist/types/json-validation.d.ts +0 -6
  119. package/dist/types/report.d.ts +0 -94
  120. package/dist/types/schema-cache.d.ts +0 -27
  121. package/dist/types/schema-compiler.d.ts +0 -31
  122. package/dist/types/schema-validator.d.ts +0 -10
  123. package/dist/types/utils/array.d.ts +0 -9
  124. package/dist/types/utils/base64.d.ts +0 -2
  125. package/dist/types/utils/clone.d.ts +0 -2
  126. package/dist/types/utils/constants.d.ts +0 -19
  127. package/dist/types/utils/date.d.ts +0 -1
  128. package/dist/types/utils/hostname.d.ts +0 -2
  129. package/dist/types/utils/json.d.ts +0 -9
  130. package/dist/types/utils/properties.d.ts +0 -1
  131. package/dist/types/utils/schema-regex.d.ts +0 -10
  132. package/dist/types/utils/symbols.d.ts +0 -2
  133. package/dist/types/utils/time.d.ts +0 -12
  134. package/dist/types/utils/unicode.d.ts +0 -5
  135. package/dist/types/utils/uri.d.ts +0 -4
  136. package/dist/types/utils/what-is.d.ts +0 -4
  137. package/dist/types/validation/array.d.ts +0 -12
  138. package/dist/types/validation/combinators.d.ts +0 -10
  139. package/dist/types/validation/numeric.d.ts +0 -8
  140. package/dist/types/validation/object.d.ts +0 -13
  141. package/dist/types/validation/ref.d.ts +0 -11
  142. package/dist/types/validation/shared.d.ts +0 -26
  143. package/dist/types/validation/string.d.ts +0 -9
  144. package/dist/types/validation/type.d.ts +0 -6
  145. package/dist/types/z-schema-base.d.ts +0 -87
  146. package/dist/types/z-schema-options.d.ts +0 -35
  147. package/dist/types/z-schema-reader.d.ts +0 -4
  148. package/dist/types/z-schema-versions.d.ts +0 -1
  149. package/dist/types/z-schema.d.ts +0 -191
  150. package/dist/utils/array.js +0 -69
  151. package/dist/utils/base64.js +0 -29
  152. package/dist/utils/clone.js +0 -59
  153. package/dist/utils/constants.js +0 -19
  154. package/dist/utils/date.js +0 -21
  155. package/dist/utils/hostname.js +0 -146
  156. package/dist/utils/json.js +0 -70
  157. package/dist/utils/properties.js +0 -10
  158. package/dist/utils/schema-regex.js +0 -52
  159. package/dist/utils/symbols.js +0 -2
  160. package/dist/utils/time.js +0 -50
  161. package/dist/utils/unicode.js +0 -12
  162. package/dist/utils/uri.js +0 -15
  163. package/dist/utils/what-is.js +0 -32
  164. package/dist/validation/array.js +0 -127
  165. package/dist/validation/combinators.js +0 -106
  166. package/dist/validation/numeric.js +0 -97
  167. package/dist/validation/object.js +0 -237
  168. package/dist/validation/ref.js +0 -70
  169. package/dist/validation/shared.js +0 -136
  170. package/dist/validation/string.js +0 -170
  171. package/dist/validation/type.js +0 -55
  172. package/dist/z-schema-base.js +0 -286
  173. package/dist/z-schema-options.js +0 -99
  174. package/dist/z-schema-reader.js +0 -7
  175. package/dist/z-schema-versions.js +0 -110
  176. package/dist/z-schema.js +0 -287
@@ -1,728 +0,0 @@
1
- import { getId } from './json-schema.js';
2
- import { Report } from './report.js';
3
- import { compileSchemaRegex } from './utils/schema-regex.js';
4
- import { isObject, whatIs } from './utils/what-is.js';
5
- import { additionalItemsValidator, containsValidator, itemsValidator, maxContainsValidator, maxItemsValidator, minContainsValidator, minItemsValidator, prefixItemsValidator, uniqueItemsValidator, } from './validation/array.js';
6
- import { allOfValidator, anyOfValidator, elseValidator, ifValidator, notValidator, oneOfValidator, thenValidator, } from './validation/combinators.js';
7
- import { exclusiveMaximumValidator, exclusiveMinimumValidator, maximumValidator, minimumValidator, multipleOfValidator, } from './validation/numeric.js';
8
- import { additionalPropertiesValidator, dependenciesValidator, dependentRequiredValidator, dependentSchemasValidator, maxPropertiesValidator, minPropertiesValidator, patternPropertiesValidator, propertiesValidator, propertyNamesValidator, requiredValidator, } from './validation/object.js';
9
- import { resolveDynamicRef, resolveRecursiveRef } from './validation/ref.js';
10
- import { getCachedValidationResult, isValidationVocabularyEnabled, VALIDATION_VOCAB_KEYWORDS, } from './validation/shared.js';
11
- import { contentEncodingValidator, contentMediaTypeValidator, formatValidator, maxLengthValidator, minLengthValidator, patternValidator, } from './validation/string.js';
12
- import { constValidator, enumValidator, typeValidator } from './validation/type.js';
13
- function collectEvaluated(args) {
14
- const { report, currentSchema, json, mode, depth } = args;
15
- if (!currentSchema || typeof currentSchema === 'boolean') {
16
- return new Set();
17
- }
18
- if (depth > (this.options.maxRecursionDepth ?? 100)) {
19
- report.addError('COLLECT_EVALUATED_DEPTH_EXCEEDED', [depth]);
20
- return new Set();
21
- }
22
- const evaluated = new Set();
23
- const merge = (other) => {
24
- if (other === 'all')
25
- return true;
26
- for (const v of other) {
27
- evaluated.add(v);
28
- }
29
- return false;
30
- };
31
- const recurse = (subSchema) => {
32
- if (mode === 'items') {
33
- return collectEvaluated.call(this, {
34
- report,
35
- currentSchema: subSchema,
36
- json,
37
- mode: 'items',
38
- jsonArr: args.jsonArr,
39
- depth: depth + 1,
40
- });
41
- }
42
- return collectEvaluated.call(this, {
43
- report,
44
- currentSchema: subSchema,
45
- json,
46
- mode: 'properties',
47
- jsonData: args.jsonData,
48
- depth: depth + 1,
49
- });
50
- };
51
- // --- Mode-specific leaf collection ---
52
- if (mode === 'items') {
53
- const jsonArr = args.jsonArr;
54
- // prefixItems (2020-12 tuple)
55
- if (Array.isArray(currentSchema.prefixItems)) {
56
- const len = Math.min(currentSchema.prefixItems.length, jsonArr.length);
57
- for (let i = 0; i < len; i++) {
58
- evaluated.add(i);
59
- }
60
- }
61
- // items - can be array (2019-09 tuple) or schema (evaluates all)
62
- if (currentSchema.items !== undefined) {
63
- if (Array.isArray(currentSchema.items)) {
64
- const len = Math.min(currentSchema.items.length, jsonArr.length);
65
- for (let i = 0; i < len; i++) {
66
- evaluated.add(i);
67
- }
68
- }
69
- else if (currentSchema.items !== false) {
70
- return 'all';
71
- }
72
- }
73
- // additionalItems (2019-09) - when items is array form and additionalItems is present and not false
74
- if (currentSchema.additionalItems !== undefined &&
75
- currentSchema.additionalItems !== false &&
76
- Array.isArray(currentSchema.items)) {
77
- return 'all';
78
- }
79
- // contains - evaluates specific indices that match the schema
80
- if (currentSchema.contains !== undefined) {
81
- for (let i = 0; i < jsonArr.length; i++) {
82
- let passed = getCachedValidationResult(report, currentSchema.contains, jsonArr[i]);
83
- if (passed === undefined) {
84
- const subReport = new Report(report);
85
- validate.call(this, subReport, currentSchema.contains, jsonArr[i]);
86
- passed = subReport.errors.length === 0;
87
- }
88
- if (passed) {
89
- evaluated.add(i);
90
- }
91
- }
92
- }
93
- // unevaluatedItems: true means all items evaluated
94
- if (currentSchema.unevaluatedItems === true) {
95
- return 'all';
96
- }
97
- }
98
- else {
99
- // mode === 'properties'
100
- const jsonData = args.jsonData;
101
- // properties
102
- if (isObject(currentSchema.properties)) {
103
- for (const key of Object.keys(currentSchema.properties)) {
104
- if (Object.hasOwn(jsonData, key)) {
105
- evaluated.add(key);
106
- }
107
- }
108
- }
109
- // patternProperties
110
- if (isObject(currentSchema.patternProperties)) {
111
- for (const pattern of Object.keys(currentSchema.patternProperties)) {
112
- const result = compileSchemaRegex(pattern);
113
- if (result.ok) {
114
- for (const key of Object.keys(jsonData)) {
115
- if (result.value.test(key)) {
116
- evaluated.add(key);
117
- }
118
- }
119
- }
120
- }
121
- }
122
- // additionalProperties - evaluates all non-properties/non-patternProperties keys
123
- if (currentSchema.additionalProperties !== undefined) {
124
- const propKeys = isObject(currentSchema.properties) ? Object.keys(currentSchema.properties) : [];
125
- const patternRegexes = [];
126
- if (isObject(currentSchema.patternProperties)) {
127
- for (const pattern of Object.keys(currentSchema.patternProperties)) {
128
- const result = compileSchemaRegex(pattern);
129
- if (result.ok) {
130
- patternRegexes.push(result.value);
131
- }
132
- }
133
- }
134
- for (const key of Object.keys(jsonData)) {
135
- if (propKeys.includes(key))
136
- continue;
137
- if (patternRegexes.some((re) => re.test(key)))
138
- continue;
139
- evaluated.add(key);
140
- }
141
- }
142
- // dependentSchemas - only applies when the dependency key is present in the data
143
- if (isObject(currentSchema.dependentSchemas)) {
144
- for (const [depKey, depSchema] of Object.entries(currentSchema.dependentSchemas)) {
145
- if (Object.hasOwn(jsonData, depKey)) {
146
- if (merge(recurse(depSchema))) {
147
- return 'all';
148
- }
149
- }
150
- }
151
- }
152
- // unevaluatedProperties: true in a sub-schema means all props are evaluated
153
- if (currentSchema.unevaluatedProperties === true) {
154
- return 'all';
155
- }
156
- }
157
- // --- Shared combinator traversal ---
158
- // allOf
159
- if (Array.isArray(currentSchema.allOf)) {
160
- for (const subSchema of currentSchema.allOf) {
161
- if (merge(recurse(subSchema))) {
162
- return 'all';
163
- }
164
- }
165
- }
166
- // anyOf - only matching branches contribute
167
- if (Array.isArray(currentSchema.anyOf)) {
168
- for (const subSchema of currentSchema.anyOf) {
169
- let passed = getCachedValidationResult(report, subSchema, json);
170
- if (passed === undefined) {
171
- const subReport = new Report(report);
172
- validate.call(this, subReport, subSchema, json);
173
- passed = subReport.errors.length === 0;
174
- }
175
- if (passed) {
176
- if (merge(recurse(subSchema))) {
177
- return 'all';
178
- }
179
- }
180
- }
181
- }
182
- // oneOf - only matching branches contribute
183
- if (Array.isArray(currentSchema.oneOf)) {
184
- for (const subSchema of currentSchema.oneOf) {
185
- let passed = getCachedValidationResult(report, subSchema, json);
186
- if (passed === undefined) {
187
- const subReport = new Report(report);
188
- validate.call(this, subReport, subSchema, json);
189
- passed = subReport.errors.length === 0;
190
- }
191
- if (passed) {
192
- if (merge(recurse(subSchema))) {
193
- return 'all';
194
- }
195
- }
196
- }
197
- }
198
- // if/then/else
199
- if (currentSchema.if !== undefined) {
200
- let condPassed = getCachedValidationResult(report, currentSchema.if, json);
201
- if (condPassed === undefined) {
202
- const condReport = new Report(report);
203
- validate.call(this, condReport, currentSchema.if, json);
204
- condPassed = condReport.errors.length === 0;
205
- }
206
- if (condPassed) {
207
- if (merge(recurse(currentSchema.if))) {
208
- return 'all';
209
- }
210
- if (currentSchema.then !== undefined) {
211
- if (merge(recurse(currentSchema.then))) {
212
- return 'all';
213
- }
214
- }
215
- }
216
- else {
217
- if (currentSchema.else !== undefined) {
218
- if (merge(recurse(currentSchema.else))) {
219
- return 'all';
220
- }
221
- }
222
- }
223
- }
224
- // $ref resolved
225
- if (currentSchema.__$refResolved && currentSchema.__$refResolved !== currentSchema) {
226
- if (merge(recurse(currentSchema.__$refResolved))) {
227
- return 'all';
228
- }
229
- }
230
- // $recursiveRef
231
- const recursiveTarget = resolveRecursiveRef(currentSchema, report.__$recursiveAnchorStack);
232
- if (recursiveTarget && recursiveTarget !== currentSchema) {
233
- if (merge(recurse(recursiveTarget))) {
234
- return 'all';
235
- }
236
- }
237
- // $dynamicRef
238
- const dynamicTarget = resolveDynamicRef(currentSchema, report.__$dynamicScopeStack);
239
- if (dynamicTarget && dynamicTarget !== currentSchema) {
240
- if (merge(recurse(dynamicTarget))) {
241
- return 'all';
242
- }
243
- }
244
- return evaluated;
245
- }
246
- // ---------------------------------------------------------------------------
247
- // unevaluatedItems
248
- // ---------------------------------------------------------------------------
249
- function unevaluatedItemsValidator(report, schema, json) {
250
- if (!Array.isArray(json)) {
251
- return;
252
- }
253
- // unevaluatedItems: true means all items are valid
254
- if (schema.unevaluatedItems === true) {
255
- return;
256
- }
257
- const unevalSchema = schema.unevaluatedItems;
258
- if (unevalSchema === undefined) {
259
- return;
260
- }
261
- if (json.length === 0) {
262
- return;
263
- }
264
- const evaluatedItems = collectEvaluated.call(this, {
265
- report,
266
- currentSchema: schema,
267
- json,
268
- mode: 'items',
269
- jsonArr: json,
270
- depth: 0,
271
- });
272
- if (evaluatedItems === 'all') {
273
- return;
274
- }
275
- const unevaluatedIndices = [];
276
- for (let i = 0; i < json.length; i++) {
277
- if (!evaluatedItems.has(i)) {
278
- unevaluatedIndices.push(i);
279
- }
280
- }
281
- if (unevaluatedIndices.length === 0) {
282
- return;
283
- }
284
- if (unevalSchema === false) {
285
- report.addError('ARRAY_UNEVALUATED_ITEMS', undefined, undefined, schema, 'unevaluatedItems');
286
- }
287
- else {
288
- // unevaluatedItems as a schema — validate each unevaluated item against it
289
- for (const idx of unevaluatedIndices) {
290
- const subReport = new Report(report);
291
- validate.call(this, subReport, unevalSchema, json[idx]);
292
- if (subReport.errors.length > 0) {
293
- report.addError('ARRAY_UNEVALUATED_ITEMS', undefined, undefined, schema, 'unevaluatedItems');
294
- break;
295
- }
296
- }
297
- }
298
- }
299
- // ---------------------------------------------------------------------------
300
- // unevaluatedProperties
301
- // ---------------------------------------------------------------------------
302
- function unevaluatedPropertiesValidator(report, schema, json) {
303
- if (!isObject(json)) {
304
- return;
305
- }
306
- // unevaluatedProperties: true means all properties are valid
307
- if (schema.unevaluatedProperties === true) {
308
- return;
309
- }
310
- // unevaluatedProperties: false or unevaluatedProperties: {schema} both need evaluation
311
- const unevalSchema = schema.unevaluatedProperties;
312
- if (unevalSchema === undefined) {
313
- return;
314
- }
315
- const allKeys = Object.keys(json);
316
- if (allKeys.length === 0) {
317
- return;
318
- }
319
- const evaluatedProperties = collectEvaluated.call(this, {
320
- report,
321
- currentSchema: schema,
322
- json,
323
- mode: 'properties',
324
- jsonData: json,
325
- depth: 0,
326
- });
327
- if (evaluatedProperties === 'all') {
328
- return;
329
- }
330
- const unevaluatedKeys = allKeys.filter((key) => !evaluatedProperties.has(key));
331
- if (unevaluatedKeys.length === 0) {
332
- return;
333
- }
334
- if (unevalSchema === false) {
335
- report.addError('OBJECT_UNEVALUATED_PROPERTIES', [unevaluatedKeys.join(', ')], undefined, schema, 'unevaluatedProperties');
336
- }
337
- else {
338
- // unevaluatedProperties as a schema — validate each unevaluated key against it
339
- for (const key of unevaluatedKeys) {
340
- const subReport = new Report(report);
341
- validate.call(this, subReport, unevalSchema, json[key]);
342
- if (subReport.errors.length > 0) {
343
- report.addError('OBJECT_UNEVALUATED_PROPERTIES', [key], undefined, schema, 'unevaluatedProperties');
344
- }
345
- }
346
- }
347
- }
348
- // ---------------------------------------------------------------------------
349
- // definitions (no-op)
350
- // ---------------------------------------------------------------------------
351
- function definitionsValidator() {
352
- /*report: Report, schema: JsonSchemaInternal, json: unknown*/
353
- // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.7.2
354
- // nothing to do here
355
- }
356
- // ---------------------------------------------------------------------------
357
- // JsonValidators — keyword dispatch table
358
- // ---------------------------------------------------------------------------
359
- export const JsonValidators = {
360
- // no-op validators (metadata / handled elsewhere)
361
- id: () => { },
362
- $id: () => { },
363
- $ref: () => { },
364
- $schema: () => { },
365
- $dynamicAnchor: () => { },
366
- $dynamicRef: () => { },
367
- $anchor: () => { },
368
- $defs: () => { },
369
- $vocabulary: () => { },
370
- $recursiveAnchor: () => { },
371
- $recursiveRef: () => { },
372
- examples: () => { },
373
- title: () => { },
374
- description: () => { },
375
- default: () => { },
376
- // type validators
377
- type: typeValidator,
378
- enum: enumValidator,
379
- const: constValidator,
380
- // numeric validators
381
- multipleOf: multipleOfValidator,
382
- maximum: maximumValidator,
383
- exclusiveMaximum: exclusiveMaximumValidator,
384
- minimum: minimumValidator,
385
- exclusiveMinimum: exclusiveMinimumValidator,
386
- // string validators
387
- maxLength: maxLengthValidator,
388
- minLength: minLengthValidator,
389
- pattern: patternValidator,
390
- format: formatValidator,
391
- contentEncoding: contentEncodingValidator,
392
- contentMediaType: contentMediaTypeValidator,
393
- // array validators
394
- additionalItems: additionalItemsValidator,
395
- items: itemsValidator,
396
- prefixItems: prefixItemsValidator,
397
- maxItems: maxItemsValidator,
398
- minItems: minItemsValidator,
399
- uniqueItems: uniqueItemsValidator,
400
- contains: containsValidator,
401
- maxContains: maxContainsValidator,
402
- minContains: minContainsValidator,
403
- unevaluatedItems: unevaluatedItemsValidator,
404
- // object validators
405
- maxProperties: maxPropertiesValidator,
406
- minProperties: minPropertiesValidator,
407
- required: requiredValidator,
408
- additionalProperties: additionalPropertiesValidator,
409
- patternProperties: patternPropertiesValidator,
410
- properties: propertiesValidator,
411
- dependencies: dependenciesValidator,
412
- dependentSchemas: dependentSchemasValidator,
413
- dependentRequired: dependentRequiredValidator,
414
- propertyNames: propertyNamesValidator,
415
- unevaluatedProperties: unevaluatedPropertiesValidator,
416
- // combinator validators
417
- allOf: allOfValidator,
418
- anyOf: anyOfValidator,
419
- oneOf: oneOfValidator,
420
- not: notValidator,
421
- if: ifValidator,
422
- then: thenValidator,
423
- else: elseValidator,
424
- // misc
425
- definitions: definitionsValidator,
426
- };
427
- // ---------------------------------------------------------------------------
428
- // recurseArray
429
- // ---------------------------------------------------------------------------
430
- const recurseArray = function (report, schema, json) {
431
- // http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.2
432
- const schemaUri = typeof schema.$schema === 'string' ? schema.$schema : undefined;
433
- const isDraft202012Schema = schemaUri === 'https://json-schema.org/draft/2020-12/schema' ||
434
- (!schemaUri && this.options.version === 'draft2020-12');
435
- const prefixItems = isDraft202012Schema && Array.isArray(schema.prefixItems) ? schema.prefixItems : undefined;
436
- if (prefixItems) {
437
- for (let idx = 0; idx < json.length; idx++) {
438
- if (idx < prefixItems.length) {
439
- report.path.push(idx);
440
- validate.call(this, report, prefixItems[idx], json[idx]);
441
- report.path.pop();
442
- }
443
- else if (schema.items !== undefined && !Array.isArray(schema.items)) {
444
- report.path.push(idx);
445
- report.schemaPath.push('items');
446
- validate.call(this, report, schema.items, json[idx]);
447
- report.schemaPath.pop();
448
- report.path.pop();
449
- }
450
- }
451
- return;
452
- }
453
- // If "items" is an array, this situation, the schema depends on the index:
454
- // if the index is less than, or equal to, the size of "items",
455
- // the child instance must be valid against the corresponding schema in the "items" array;
456
- // otherwise, it must be valid against the schema defined by "additionalItems".
457
- if (Array.isArray(schema.items)) {
458
- for (let idx = 0; idx < json.length; idx++) {
459
- // equal to doesn't make sense here
460
- if (idx < schema.items.length) {
461
- report.path.push(idx);
462
- validate.call(this, report, schema.items[idx], json[idx]);
463
- report.path.pop();
464
- }
465
- else {
466
- // might be boolean, so check that it's an object
467
- if (typeof schema.additionalItems === 'object') {
468
- report.path.push(idx);
469
- validate.call(this, report, schema.additionalItems, json[idx]);
470
- report.path.pop();
471
- }
472
- }
473
- }
474
- }
475
- else if (typeof schema.items === 'object' || typeof schema.items === 'boolean') {
476
- // If items is a schema, then the child instance must be valid against this schema,
477
- // regardless of its index, and regardless of the value of "additionalItems".
478
- for (let idx = 0; idx < json.length; idx++) {
479
- report.path.push(idx);
480
- // Track schema path for array items validation
481
- report.schemaPath.push('items');
482
- validate.call(this, report, schema.items, json[idx]);
483
- report.schemaPath.pop();
484
- report.path.pop();
485
- }
486
- }
487
- };
488
- // ---------------------------------------------------------------------------
489
- // recurseObject
490
- // ---------------------------------------------------------------------------
491
- const recurseObject = function (report, schema, json) {
492
- // http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.3
493
- // If "additionalProperties" is absent, it is considered present with an empty schema as a value.
494
- // In addition, boolean value true is considered equivalent to an empty schema.
495
- let additionalProperties = schema.additionalProperties;
496
- if (additionalProperties === true || additionalProperties === undefined) {
497
- additionalProperties = {};
498
- }
499
- // p - The property set from "properties".
500
- const p = schema.properties ? Object.keys(schema.properties) : [];
501
- // pp - The property set from "patternProperties". Elements of this set will be called regexes for convenience.
502
- const pp = schema.patternProperties ? Object.keys(schema.patternProperties) : [];
503
- // m - The property name of the child.
504
- const keys = Object.keys(json);
505
- for (const m of keys) {
506
- const propertyValue = json[m];
507
- // s - The set of schemas for the child instance.
508
- const s = [];
509
- // 1. If set "p" contains value "m", then the corresponding schema in "properties" is added to "s".
510
- if (p.includes(m)) {
511
- s.push(schema.properties[m]);
512
- }
513
- // 2. For each regex in "pp", if it matches "m" successfully, the corresponding schema in "patternProperties" is added to "s".
514
- for (const regexString of pp) {
515
- const result = compileSchemaRegex(regexString);
516
- if (result.ok && result.value.test(m) === true) {
517
- s.push(schema.patternProperties[regexString]);
518
- }
519
- }
520
- // 3. The schema defined by "additionalProperties" is added to "s" if and only if, at this stage, "s" is empty.
521
- if (s.length === 0 && additionalProperties !== false) {
522
- s.push(additionalProperties);
523
- }
524
- // we are passing tests even without this assert because this is covered by properties check
525
- // if s is empty in this stage, no additionalProperties are allowed
526
- // report.expect(s.length !== 0, 'E001', m);
527
- // Instance property value must pass all schemas from s
528
- for (const schema_s of s) {
529
- report.path.push(m);
530
- // Track schema path for properties validation
531
- if (p.includes(m)) {
532
- // This is a defined property
533
- report.schemaPath.push('properties');
534
- report.schemaPath.push(m);
535
- }
536
- else {
537
- // This is additionalProperties or patternProperties
538
- report.schemaPath.push('additionalProperties');
539
- }
540
- validate.call(this, report, schema_s, propertyValue);
541
- report.path.pop();
542
- report.schemaPath.pop();
543
- if (p.includes(m)) {
544
- report.schemaPath.pop(); // pop the property name for defined properties
545
- }
546
- }
547
- }
548
- };
549
- // ---------------------------------------------------------------------------
550
- // validate — main entry point
551
- // ---------------------------------------------------------------------------
552
- export function validate(report, schema, json) {
553
- report.commonErrorMessage = 'JSON_OBJECT_VALIDATION_FAILED';
554
- if (schema === true) {
555
- return true;
556
- }
557
- if (schema === false) {
558
- report.addError('SCHEMA_IS_FALSE', [], undefined, schema);
559
- return false;
560
- }
561
- // check if schema is an object
562
- if (!isObject(schema)) {
563
- report.addError('SCHEMA_NOT_AN_OBJECT', [whatIs(schema)], undefined, schema);
564
- return false;
565
- }
566
- // check if schema is empty, everything is valid against empty schema
567
- let keys = Object.keys(schema);
568
- if (keys.length === 0) {
569
- return true;
570
- }
571
- // this method can be called recursively, so we need to remember our root
572
- let isRoot = false;
573
- if (!report.rootSchema) {
574
- report.rootSchema = schema;
575
- isRoot = true;
576
- }
577
- const recursiveAnchorStack = report.__$recursiveAnchorStack;
578
- const dynamicScopeStack = report.__$dynamicScopeStack;
579
- let pushedRecursiveAnchor = false;
580
- let pushedDynamicScope = false;
581
- const schemaId = getId(schema);
582
- const schemaResourceRoot = schema.__$resourceRoot;
583
- const dynamicScopeEntry = schemaResourceRoot || (isRoot || typeof schemaId === 'string' ? schema : undefined);
584
- if (dynamicScopeEntry && dynamicScopeStack[dynamicScopeStack.length - 1] !== dynamicScopeEntry) {
585
- dynamicScopeStack.push(dynamicScopeEntry);
586
- pushedDynamicScope = true;
587
- }
588
- if (schema.$recursiveAnchor === true) {
589
- recursiveAnchorStack.push(schema);
590
- pushedRecursiveAnchor = true;
591
- }
592
- // follow schema.$ref keys
593
- if (schema.$ref !== undefined) {
594
- const applySiblingKeywordsWithRef = this.options.version === 'draft2019-09' || this.options.version === 'draft2020-12';
595
- if (applySiblingKeywordsWithRef) {
596
- if (!schema.__$refResolved) {
597
- report.addError('REF_UNRESOLVED', [schema.$ref], undefined, schema);
598
- }
599
- else {
600
- validate.call(this, report, schema.__$refResolved, json);
601
- }
602
- keys = keys.filter((key) => key !== '$ref');
603
- }
604
- else {
605
- // avoid infinite loop with maxRefs
606
- let maxRefs = 99;
607
- while (schema.$ref && maxRefs > 0) {
608
- if (!schema.__$refResolved) {
609
- report.addError('REF_UNRESOLVED', [schema.$ref], undefined, schema);
610
- break;
611
- }
612
- else if (schema.__$refResolved === schema) {
613
- break;
614
- }
615
- else {
616
- schema = schema.__$refResolved;
617
- keys = Object.keys(schema);
618
- }
619
- maxRefs--;
620
- }
621
- if (maxRefs === 0) {
622
- throw new Error('Circular dependency by $ref references!');
623
- }
624
- // Reset schema path for referenced schema - paths are relative to the referenced schema
625
- report.schemaPath = [];
626
- }
627
- }
628
- // follow schema.$recursiveRef keys
629
- if (schema.$recursiveRef !== undefined) {
630
- const applySiblingKeywordsWithRecursiveRef = this.options.version === 'draft2019-09' || this.options.version === 'draft2020-12';
631
- if (applySiblingKeywordsWithRecursiveRef) {
632
- const recursiveRefTarget = resolveRecursiveRef(schema, recursiveAnchorStack);
633
- if (!recursiveRefTarget) {
634
- report.addError('REF_UNRESOLVED', [schema.$recursiveRef], undefined, schema);
635
- }
636
- else {
637
- validate.call(this, report, recursiveRefTarget, json);
638
- }
639
- keys = keys.filter((key) => key !== '$recursiveRef');
640
- }
641
- }
642
- // follow schema.$dynamicRef keys
643
- if (schema.$dynamicRef !== undefined) {
644
- const applySiblingKeywordsWithDynamicRef = this.options.version === 'draft2020-12';
645
- if (applySiblingKeywordsWithDynamicRef) {
646
- const dynamicRefTarget = resolveDynamicRef(schema, dynamicScopeStack);
647
- if (typeof dynamicRefTarget === 'undefined') {
648
- report.addError('REF_UNRESOLVED', [schema.$dynamicRef], undefined, schema);
649
- }
650
- else {
651
- validate.call(this, report, dynamicRefTarget, json);
652
- }
653
- keys = keys.filter((key) => key !== '$dynamicRef');
654
- }
655
- }
656
- const validationVocabularyEnabled = isValidationVocabularyEnabled(schema, report, this.options.version);
657
- if (!validationVocabularyEnabled) {
658
- keys = keys.filter((key) => !VALIDATION_VOCAB_KEYWORDS.has(key));
659
- }
660
- // type checking first
661
- if (validationVocabularyEnabled && schema.type) {
662
- keys.splice(keys.indexOf('type'), 1);
663
- report.schemaPath.push('type');
664
- JsonValidators.type.call(this, report, schema, json);
665
- report.schemaPath.pop();
666
- if (report.errors.length && this.options.breakOnFirstError) {
667
- if (pushedRecursiveAnchor) {
668
- recursiveAnchorStack.pop();
669
- }
670
- if (pushedDynamicScope) {
671
- dynamicScopeStack.pop();
672
- }
673
- return false;
674
- }
675
- }
676
- // now iterate all the keys in schema and execute validation methods
677
- // Defer unevaluatedItems/unevaluatedProperties to run after other validators,
678
- // so combinator validation results are cached and available for annotation collection
679
- const deferredUnevaluatedKeys = [];
680
- for (const key of keys) {
681
- if (key === 'unevaluatedItems' || key === 'unevaluatedProperties') {
682
- deferredUnevaluatedKeys.push(key);
683
- continue;
684
- }
685
- const validator = JsonValidators[key];
686
- if (validator) {
687
- validator.call(this, report, schema, json);
688
- if (report.errors.length && this.options.breakOnFirstError) {
689
- break;
690
- }
691
- }
692
- }
693
- // Run unevaluated* validators after all others have cached their combinator results
694
- if (deferredUnevaluatedKeys.length > 0 && !(report.errors.length > 0 && this.options.breakOnFirstError)) {
695
- for (const key of deferredUnevaluatedKeys) {
696
- const validator = JsonValidators[key];
697
- if (validator) {
698
- validator.call(this, report, schema, json);
699
- if (report.errors.length && this.options.breakOnFirstError) {
700
- break;
701
- }
702
- }
703
- }
704
- }
705
- if (report.errors.length === 0 || this.options.breakOnFirstError === false) {
706
- if (Array.isArray(json)) {
707
- recurseArray.call(this, report, schema, json);
708
- }
709
- else if (isObject(json)) {
710
- recurseObject.call(this, report, schema, json);
711
- }
712
- }
713
- if (typeof this.options.customValidator === 'function') {
714
- this.options.customValidator.call(this, report, schema, json);
715
- }
716
- if (pushedRecursiveAnchor) {
717
- recursiveAnchorStack.pop();
718
- }
719
- if (pushedDynamicScope) {
720
- dynamicScopeStack.pop();
721
- }
722
- // we don't need the root pointer anymore
723
- if (isRoot) {
724
- report.rootSchema = undefined;
725
- }
726
- // return valid just to be able to break at some code points
727
- return report.errors.length === 0;
728
- }