@sourcemeta/blaze 15.1.0 → 15.2.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/describe.mjs ADDED
@@ -0,0 +1,1551 @@
1
+ import {
2
+ ASSERTION_FAIL, ASSERTION_DEFINES, ASSERTION_DEFINES_STRICT,
3
+ ASSERTION_DEFINES_ALL, ASSERTION_DEFINES_ALL_STRICT,
4
+ ASSERTION_DEFINES_EXACTLY, ASSERTION_DEFINES_EXACTLY_STRICT,
5
+ ASSERTION_DEFINES_EXACTLY_STRICT_HASH3, ASSERTION_PROPERTY_DEPENDENCIES,
6
+ ASSERTION_TYPE, ASSERTION_TYPE_ANY, ASSERTION_TYPE_STRICT,
7
+ ASSERTION_TYPE_STRICT_ANY, ASSERTION_TYPE_STRING_BOUNDED,
8
+ ASSERTION_TYPE_STRING_UPPER, ASSERTION_TYPE_ARRAY_BOUNDED,
9
+ ASSERTION_TYPE_ARRAY_UPPER, ASSERTION_TYPE_OBJECT_BOUNDED,
10
+ ASSERTION_TYPE_OBJECT_UPPER, ASSERTION_REGEX,
11
+ ASSERTION_STRING_SIZE_LESS, ASSERTION_STRING_SIZE_GREATER,
12
+ ASSERTION_ARRAY_SIZE_LESS, ASSERTION_ARRAY_SIZE_GREATER,
13
+ ASSERTION_OBJECT_SIZE_LESS, ASSERTION_OBJECT_SIZE_GREATER,
14
+ ASSERTION_EQUAL, ASSERTION_EQUALS_ANY, ASSERTION_EQUALS_ANY_STRING_HASH,
15
+ ASSERTION_GREATER_EQUAL, ASSERTION_LESS_EQUAL,
16
+ ASSERTION_GREATER, ASSERTION_LESS,
17
+ ASSERTION_UNIQUE, ASSERTION_DIVISIBLE,
18
+ ASSERTION_TYPE_INTEGER_BOUNDED, ASSERTION_TYPE_INTEGER_BOUNDED_STRICT,
19
+ ASSERTION_TYPE_INTEGER_LOWER_BOUND, ASSERTION_TYPE_INTEGER_LOWER_BOUND_STRICT,
20
+ ASSERTION_STRING_TYPE,
21
+ ASSERTION_PROPERTY_TYPE, ASSERTION_PROPERTY_TYPE_EVALUATE,
22
+ ASSERTION_PROPERTY_TYPE_STRICT, ASSERTION_PROPERTY_TYPE_STRICT_EVALUATE,
23
+ ASSERTION_PROPERTY_TYPE_STRICT_ANY, ASSERTION_PROPERTY_TYPE_STRICT_ANY_EVALUATE,
24
+ ASSERTION_ARRAY_PREFIX, ASSERTION_ARRAY_PREFIX_EVALUATE,
25
+ ASSERTION_OBJECT_PROPERTIES_SIMPLE,
26
+ ANNOTATION_EMIT, ANNOTATION_TO_PARENT, ANNOTATION_BASENAME_TO_PARENT,
27
+ EVALUATE,
28
+ LOGICAL_NOT, LOGICAL_NOT_EVALUATE,
29
+ LOGICAL_OR, LOGICAL_AND, LOGICAL_XOR, LOGICAL_CONDITION,
30
+ LOGICAL_WHEN_TYPE, LOGICAL_WHEN_DEFINES, LOGICAL_WHEN_ARRAY_SIZE_GREATER,
31
+ LOOP_PROPERTIES_UNEVALUATED, LOOP_PROPERTIES_UNEVALUATED_EXCEPT,
32
+ LOOP_PROPERTIES_MATCH, LOOP_PROPERTIES_MATCH_CLOSED,
33
+ LOOP_PROPERTIES, LOOP_PROPERTIES_EVALUATE,
34
+ LOOP_PROPERTIES_REGEX, LOOP_PROPERTIES_REGEX_CLOSED,
35
+ LOOP_PROPERTIES_STARTS_WITH, LOOP_PROPERTIES_EXCEPT,
36
+ LOOP_PROPERTIES_TYPE, LOOP_PROPERTIES_TYPE_EVALUATE,
37
+ LOOP_PROPERTIES_EXACTLY_TYPE_STRICT, LOOP_PROPERTIES_EXACTLY_TYPE_STRICT_HASH,
38
+ LOOP_PROPERTIES_TYPE_STRICT, LOOP_PROPERTIES_TYPE_STRICT_EVALUATE,
39
+ LOOP_PROPERTIES_TYPE_STRICT_ANY, LOOP_PROPERTIES_TYPE_STRICT_ANY_EVALUATE,
40
+ LOOP_KEYS, LOOP_ITEMS, LOOP_ITEMS_FROM, LOOP_ITEMS_UNEVALUATED,
41
+ LOOP_ITEMS_TYPE, LOOP_ITEMS_TYPE_STRICT, LOOP_ITEMS_TYPE_STRICT_ANY,
42
+ LOOP_ITEMS_PROPERTIES_EXACTLY_TYPE_STRICT_HASH,
43
+ LOOP_ITEMS_PROPERTIES_EXACTLY_TYPE_STRICT_HASH3,
44
+ LOOP_ITEMS_INTEGER_BOUNDED, LOOP_ITEMS_INTEGER_BOUNDED_SIZED,
45
+ LOOP_CONTAINS,
46
+ CONTROL_DYNAMIC_ANCHOR_JUMP, CONTROL_JUMP
47
+ } from './opcodes.mjs';
48
+
49
+ const TYPE_INTEGER = 2;
50
+ const TYPE_REAL = 3;
51
+ const TYPE_OBJECT = 6;
52
+
53
+ const TYPE_NAMES = [ 'null', 'boolean', 'integer', 'number',
54
+ 'string', 'array', 'object', 'number' ];
55
+
56
+ function typeName(typeIndex) {
57
+ return TYPE_NAMES[typeIndex];
58
+ }
59
+
60
+ function jsonTypeOf(value) {
61
+ if (value === null) return 0;
62
+ switch (typeof value) {
63
+ case 'boolean': return 1;
64
+ case 'number': return Number.isInteger(value) ? 2 : 3;
65
+ case 'bigint': return 2;
66
+ case 'string': return 4;
67
+ case 'object': return Array.isArray(value) ? 5 : 6;
68
+ default: return 0;
69
+ }
70
+ }
71
+
72
+ function valueTypeName(value) {
73
+ if (typeof value === 'bigint') return 'integer';
74
+ if (typeof value === 'number') {
75
+ return Number.isInteger(value) ? 'integer' : 'number';
76
+ }
77
+ return typeName(jsonTypeOf(value));
78
+ }
79
+
80
+ function escapeString(input) {
81
+ return '"' + String(input).replaceAll('"', '\\"') + '"';
82
+ }
83
+
84
+ function stringifyValue(value) {
85
+ if (typeof value === 'bigint') return String(value);
86
+ return JSON.stringify(value, (key, current) =>
87
+ typeof current === 'bigint' ? JSON.rawJSON(String(current)) : current);
88
+ }
89
+
90
+ function resolveTarget(instance, instanceLocation) {
91
+ if (instanceLocation === '') return instance;
92
+ const tokens = instanceLocation.slice(1).split('/');
93
+ let current = instance;
94
+ for (const raw of tokens) {
95
+ const token = raw.replaceAll('~1', '/').replaceAll('~0', '~');
96
+ if (Array.isArray(current)) {
97
+ current = current[Number(token)];
98
+ } else {
99
+ current = current[token];
100
+ }
101
+ }
102
+ return current;
103
+ }
104
+
105
+ function extractKeyword(evaluatePath) {
106
+ if (evaluatePath === '') return '';
107
+ const lastSlash = evaluatePath.lastIndexOf('/');
108
+ if (lastSlash === -1) return '';
109
+ const token = evaluatePath.slice(lastSlash + 1)
110
+ .replaceAll('~1', '/').replaceAll('~0', '~');
111
+ if (/^[0-9]+$/.test(token)) return '';
112
+ return token;
113
+ }
114
+
115
+ function isWithinKeyword(evaluatePath, keyword) {
116
+ const segments = evaluatePath.split('/');
117
+ for (let index = 1; index < segments.length; index++) {
118
+ if (segments[index].replaceAll('~1', '/').replaceAll('~0', '~') === keyword) {
119
+ return true;
120
+ }
121
+ }
122
+ return false;
123
+ }
124
+
125
+ function lastInstanceToken(instanceLocation) {
126
+ if (instanceLocation === '') return null;
127
+ const lastSlash = instanceLocation.lastIndexOf('/');
128
+ return instanceLocation.slice(lastSlash + 1)
129
+ .replaceAll('~1', '/').replaceAll('~0', '~');
130
+ }
131
+
132
+ function objectSize(value) {
133
+ let count = 0;
134
+ for (const _key in value) count++;
135
+ return count;
136
+ }
137
+
138
+ function objectKeys(value) {
139
+ const keys = [];
140
+ for (const key in value) keys.push(key);
141
+ return keys;
142
+ }
143
+
144
+ function unicodeLength(string) {
145
+ let count = 0;
146
+ for (let index = 0; index < string.length; index++) {
147
+ count++;
148
+ const code = string.charCodeAt(index);
149
+ if (code >= 0xD800 && code <= 0xDBFF) index++;
150
+ }
151
+ return count;
152
+ }
153
+
154
+ function isObject(value) {
155
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
156
+ }
157
+
158
+ function jsonEqual(left, right) {
159
+ if (left === right) return true;
160
+ if (left === null || right === null) return left === right;
161
+ if (typeof left !== typeof right) return false;
162
+ if (typeof left !== 'object') return left === right;
163
+ if (Array.isArray(left) !== Array.isArray(right)) return false;
164
+ if (Array.isArray(left)) {
165
+ if (left.length !== right.length) return false;
166
+ for (let index = 0; index < left.length; index++) {
167
+ if (!jsonEqual(left[index], right[index])) return false;
168
+ }
169
+ return true;
170
+ }
171
+ const leftKeys = Object.keys(left).sort();
172
+ const rightKeys = Object.keys(right).sort();
173
+ if (leftKeys.length !== rightKeys.length) return false;
174
+ for (let index = 0; index < leftKeys.length; index++) {
175
+ if (leftKeys[index] !== rightKeys[index]) return false;
176
+ if (!jsonEqual(left[leftKeys[index]], right[rightKeys[index]])) return false;
177
+ }
178
+ return true;
179
+ }
180
+
181
+ function jsonCompare(left, right) {
182
+ const leftType = jsonTypeOf(left);
183
+ const rightType = jsonTypeOf(right);
184
+ if (leftType !== rightType) return leftType - rightType;
185
+ if (left === null) return 0;
186
+ if (typeof left === 'boolean') return (left ? 1 : 0) - (right ? 1 : 0);
187
+ if (typeof left === 'number' || typeof left === 'bigint') {
188
+ if (left < right) return -1;
189
+ if (left > right) return 1;
190
+ return 0;
191
+ }
192
+ if (typeof left === 'string') return left < right ? -1 : left > right ? 1 : 0;
193
+ const leftStr = JSON.stringify(left);
194
+ const rightStr = JSON.stringify(right);
195
+ return leftStr < rightStr ? -1 : leftStr > rightStr ? 1 : 0;
196
+ }
197
+
198
+ function normalizeTypes(bitmask) {
199
+ let types = bitmask;
200
+ const hasReal = (types & (1 << TYPE_REAL)) !== 0;
201
+ const hasInteger = (types & (1 << TYPE_INTEGER)) !== 0;
202
+ const hasDecimal = (types & (1 << 7)) !== 0;
203
+ if (hasReal && hasInteger) types &= ~(1 << TYPE_INTEGER);
204
+ if (hasReal && hasDecimal) types &= ~(1 << 7);
205
+ if (hasInteger && hasDecimal) types &= ~(1 << 7);
206
+ return types;
207
+ }
208
+
209
+ function describeTypeCheck(valid, currentType, expectedType) {
210
+ let message = 'The value was expected to be of type ' + typeName(expectedType);
211
+ if (!valid) {
212
+ message += ' but it was of type ' + typeName(currentType);
213
+ }
214
+ return message;
215
+ }
216
+
217
+ function describeTypesCheck(valid, currentType, bitmask) {
218
+ let types = normalizeTypes(bitmask);
219
+ const hasReal = (bitmask & (1 << TYPE_REAL)) !== 0;
220
+ const hasInteger = (bitmask & (1 << TYPE_INTEGER)) !== 0;
221
+
222
+ let popcount = 0;
223
+ for (let bit = 0; bit < 8; bit++) {
224
+ if ((types & (1 << bit)) !== 0) popcount++;
225
+ }
226
+
227
+ if (popcount === 1) {
228
+ let typeIndex = 0;
229
+ for (let bit = 0; bit < 8; bit++) {
230
+ if ((types & (1 << bit)) !== 0) { typeIndex = bit; break; }
231
+ }
232
+ return describeTypeCheck(valid, currentType, typeIndex);
233
+ }
234
+
235
+ let message = 'The value was expected to be of type ';
236
+ let first = true;
237
+ let lastBit = 0;
238
+ for (let bit = 0; bit < 8; bit++) {
239
+ if ((types & (1 << bit)) !== 0) lastBit = bit;
240
+ }
241
+ for (let bit = 0; bit < 8; bit++) {
242
+ if ((types & (1 << bit)) !== 0) {
243
+ if (!first) message += ', ';
244
+ if (bit === lastBit) message += 'or ';
245
+ message += typeName(bit);
246
+ first = false;
247
+ }
248
+ }
249
+
250
+ if (valid) {
251
+ message += ' and it was of type ';
252
+ } else {
253
+ message += ' but it was of type ';
254
+ }
255
+
256
+ if (valid && currentType === TYPE_INTEGER && hasReal) {
257
+ message += 'number';
258
+ } else if ((valid && currentType === TYPE_INTEGER && hasReal) ||
259
+ currentType === TYPE_REAL) {
260
+ message += 'number';
261
+ } else {
262
+ message += typeName(currentType);
263
+ }
264
+
265
+ return message;
266
+ }
267
+
268
+ function describeReference(target) {
269
+ return 'The ' + typeName(jsonTypeOf(target)) +
270
+ ' value was expected to validate against the referenced schema';
271
+ }
272
+
273
+ function describeTypeList(bitmask) {
274
+ let types = normalizeTypes(bitmask);
275
+
276
+ let popcount = 0;
277
+ for (let bit = 0; bit < 8; bit++) {
278
+ if ((types & (1 << bit)) !== 0) popcount++;
279
+ }
280
+
281
+ if (popcount === 1) {
282
+ for (let bit = 0; bit < 8; bit++) {
283
+ if ((types & (1 << bit)) !== 0) return typeName(bit);
284
+ }
285
+ }
286
+
287
+ let result = '';
288
+ let first = true;
289
+ let lastBit = 0;
290
+ for (let bit = 0; bit < 8; bit++) {
291
+ if ((types & (1 << bit)) !== 0) lastBit = bit;
292
+ }
293
+ for (let bit = 0; bit < 8; bit++) {
294
+ if ((types & (1 << bit)) !== 0) {
295
+ if (!first) result += ', ';
296
+ if (bit === lastBit) result += 'or ';
297
+ result += typeName(bit);
298
+ first = false;
299
+ }
300
+ }
301
+ return result;
302
+ }
303
+
304
+ function formatList(items, conjunction) {
305
+ let result = '';
306
+ for (let index = 0; index < items.length; index++) {
307
+ if (index === items.length - 1) {
308
+ result += conjunction + ' ' + items[index];
309
+ } else {
310
+ result += items[index] + ', ';
311
+ }
312
+ }
313
+ return result;
314
+ }
315
+
316
+ export function describe(valid, instruction, evaluatePath,
317
+ instanceLocation, instance, annotation) {
318
+ const opcode = instruction[0];
319
+ const value = instruction[5];
320
+ const children = instruction[6];
321
+ const keyword = extractKeyword(evaluatePath);
322
+ const target = resolveTarget(instance, instanceLocation);
323
+ const targetType = jsonTypeOf(target);
324
+
325
+ if (opcode === ASSERTION_FAIL) {
326
+ if (keyword === 'enum') {
327
+ return 'The ' + typeName(targetType) +
328
+ ' value was not expected to validate against the empty enumeration';
329
+ }
330
+ if (keyword === 'contains') {
331
+ return 'The constraints declared for this keyword were not satisfiable';
332
+ }
333
+ if (keyword === 'additionalProperties' ||
334
+ keyword === 'unevaluatedProperties') {
335
+ const property = lastInstanceToken(instanceLocation);
336
+ return 'The object value was not expected to define the property ' +
337
+ escapeString(property);
338
+ }
339
+ if (keyword === 'unevaluatedItems') {
340
+ const tokenValue = lastInstanceToken(instanceLocation);
341
+ return 'The array value was not expected to define the item at index ' +
342
+ tokenValue;
343
+ }
344
+ return 'No instance is expected to succeed against the false schema';
345
+ }
346
+
347
+ if (opcode === LOGICAL_OR) {
348
+ const childCount = children ? children.length : 0;
349
+ let message = 'The ' + typeName(targetType) +
350
+ ' value was expected to validate against ';
351
+ if (childCount > 1) {
352
+ message += 'at least one of the ' + childCount + ' given subschemas';
353
+ } else {
354
+ message += 'the given subschema';
355
+ }
356
+ return message;
357
+ }
358
+
359
+ if (opcode === LOGICAL_AND) {
360
+ if (keyword === 'allOf') {
361
+ const childCount = children ? children.length : 0;
362
+ let message = 'The ' + typeName(targetType) +
363
+ ' value was expected to validate against the ';
364
+ if (childCount > 1) {
365
+ message += childCount + ' given subschemas';
366
+ } else {
367
+ message += 'given subschema';
368
+ }
369
+ return message;
370
+ }
371
+ if (keyword === '$ref') {
372
+ return describeReference(target);
373
+ }
374
+ return '<unknown>';
375
+ }
376
+
377
+ if (opcode === LOGICAL_XOR) {
378
+ const childCount = children ? children.length : 0;
379
+ let message = '';
380
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
381
+ instanceLocation !== '' && lastInstanceToken(instanceLocation) !== null) {
382
+ const propertyName = lastInstanceToken(instanceLocation);
383
+ message += 'The property name ' + escapeString(propertyName);
384
+ } else {
385
+ message += 'The ' + typeName(targetType) + ' value';
386
+ }
387
+ message += ' was expected to validate against ';
388
+ if (childCount > 1) {
389
+ message += 'one and only one of the ' + childCount + ' given subschemas';
390
+ } else {
391
+ message += 'the given subschema';
392
+ }
393
+ return message;
394
+ }
395
+
396
+ if (opcode === LOGICAL_CONDITION) {
397
+ return 'The ' + typeName(targetType) +
398
+ ' value was expected to validate against the given conditional';
399
+ }
400
+
401
+ if (opcode === LOGICAL_NOT) {
402
+ let message = 'The ' + typeName(targetType) +
403
+ ' value was expected to not validate against the given subschema';
404
+ if (!valid) message += ', but it did';
405
+ return message;
406
+ }
407
+
408
+ if (opcode === LOGICAL_NOT_EVALUATE) {
409
+ let message = 'The ' + typeName(targetType) +
410
+ ' value was expected to not validate against the given subschema';
411
+ if (!valid) message += ', but it did';
412
+ return message;
413
+ }
414
+
415
+ if (opcode === EVALUATE) {
416
+ return 'The instance location was marked as evaluated';
417
+ }
418
+
419
+ if (opcode === CONTROL_DYNAMIC_ANCHOR_JUMP) {
420
+ if (keyword === '$dynamicRef') {
421
+ return 'The ' + typeName(targetType) +
422
+ ' value was expected to validate against the first subschema ' +
423
+ 'in scope that declared the dynamic anchor ' + escapeString(value);
424
+ }
425
+ return 'The ' + typeName(targetType) +
426
+ ' value was expected to validate against the first subschema ' +
427
+ 'in scope that declared a recursive anchor';
428
+ }
429
+
430
+ if (opcode === ANNOTATION_EMIT) {
431
+ if (keyword === 'properties') {
432
+ return 'The object property ' + escapeString(annotation) +
433
+ ' successfully validated against its property subschema';
434
+ }
435
+
436
+ if ((keyword === 'items' || keyword === 'additionalItems') &&
437
+ annotation === true) {
438
+ return 'Every item in the array value was successfully validated';
439
+ }
440
+
441
+ if ((keyword === 'prefixItems' || keyword === 'items') &&
442
+ typeof annotation === 'number') {
443
+ if (annotation === 0) {
444
+ return 'The first item of the array value successfully validated ' +
445
+ 'against the first positional subschema';
446
+ }
447
+ return 'The first ' + (annotation + 1) +
448
+ ' items of the array value successfully validated against the given ' +
449
+ 'positional subschemas';
450
+ }
451
+
452
+ if (keyword === 'prefixItems' && annotation === true) {
453
+ return 'Every item of the array value validated against the given ' +
454
+ 'positional subschemas';
455
+ }
456
+
457
+ if (keyword === 'title' || keyword === 'description') {
458
+ let message = 'The ' + keyword + ' of the';
459
+ if (instanceLocation === '') {
460
+ message += ' instance';
461
+ } else {
462
+ message += ' instance location "' + instanceLocation + '"';
463
+ }
464
+ message += ' was ' + escapeString(annotation);
465
+ return message;
466
+ }
467
+
468
+ if (keyword === 'default') {
469
+ let message = 'The default value of the';
470
+ if (instanceLocation === '') {
471
+ message += ' instance';
472
+ } else {
473
+ message += ' instance location "' + instanceLocation + '"';
474
+ }
475
+ message += ' was ' + stringifyValue(annotation);
476
+ return message;
477
+ }
478
+
479
+ if (keyword === 'deprecated' && typeof annotation === 'boolean') {
480
+ let message = '';
481
+ if (instanceLocation === '') {
482
+ message += 'The instance';
483
+ } else {
484
+ message += 'The instance location "' + instanceLocation + '"';
485
+ }
486
+ message += annotation
487
+ ? ' was considered deprecated'
488
+ : ' was not considered deprecated';
489
+ return message;
490
+ }
491
+
492
+ if (keyword === 'readOnly' && typeof annotation === 'boolean') {
493
+ let message = '';
494
+ if (instanceLocation === '') {
495
+ message += 'The instance';
496
+ } else {
497
+ message += 'The instance location "' + instanceLocation + '"';
498
+ }
499
+ message += annotation
500
+ ? ' was considered read-only'
501
+ : ' was not considered read-only';
502
+ return message;
503
+ }
504
+
505
+ if (keyword === 'writeOnly' && typeof annotation === 'boolean') {
506
+ let message = '';
507
+ if (instanceLocation === '') {
508
+ message += 'The instance';
509
+ } else {
510
+ message += 'The instance location "' + instanceLocation + '"';
511
+ }
512
+ message += annotation
513
+ ? ' was considered write-only'
514
+ : ' was not considered write-only';
515
+ return message;
516
+ }
517
+
518
+ if (keyword === 'examples') {
519
+ let message = '';
520
+ if (instanceLocation === '') {
521
+ message += 'Examples of the instance';
522
+ } else {
523
+ message += 'Examples of the instance location "' +
524
+ instanceLocation + '"';
525
+ }
526
+ message += ' were ';
527
+ for (let index = 0; index < annotation.length; index++) {
528
+ if (index === annotation.length - 1) {
529
+ message += 'and ' + stringifyValue(annotation[index]);
530
+ } else {
531
+ message += stringifyValue(annotation[index]) + ', ';
532
+ }
533
+ }
534
+ return message;
535
+ }
536
+
537
+ if (keyword === 'contentEncoding') {
538
+ let message = 'The content encoding of the';
539
+ if (instanceLocation === '') {
540
+ message += ' instance';
541
+ } else {
542
+ message += ' instance location "' + instanceLocation + '"';
543
+ }
544
+ message += ' was ' + escapeString(annotation);
545
+ return message;
546
+ }
547
+
548
+ if (keyword === 'contentMediaType') {
549
+ let message = 'The content media type of the';
550
+ if (instanceLocation === '') {
551
+ message += ' instance';
552
+ } else {
553
+ message += ' instance location "' + instanceLocation + '"';
554
+ }
555
+ message += ' was ' + escapeString(annotation);
556
+ return message;
557
+ }
558
+
559
+ if (keyword === 'contentSchema') {
560
+ let message = 'When decoded, the';
561
+ if (instanceLocation === '') {
562
+ message += ' instance';
563
+ } else {
564
+ message += ' instance location "' + instanceLocation + '"';
565
+ }
566
+ message += ' was expected to validate against the schema ' +
567
+ stringifyValue(annotation);
568
+ return message;
569
+ }
570
+
571
+ if (keyword === 'format') {
572
+ let message = 'The logical type of the';
573
+ if (instanceLocation === '') {
574
+ message += ' instance';
575
+ } else {
576
+ message += ' instance location "' + instanceLocation + '"';
577
+ }
578
+ message += ' was expected to be ' + stringifyValue(annotation);
579
+ return message;
580
+ }
581
+
582
+ return 'The unrecognized keyword ' + escapeString(keyword) +
583
+ ' was collected as the annotation ' + stringifyValue(annotation);
584
+ }
585
+
586
+ if (opcode === ANNOTATION_TO_PARENT) {
587
+ if (keyword === 'unevaluatedItems' && annotation === true) {
588
+ return 'At least one item of the array value successfully validated ' +
589
+ 'against the subschema for unevaluated items';
590
+ }
591
+ return '<unknown>';
592
+ }
593
+
594
+ if (opcode === ANNOTATION_BASENAME_TO_PARENT) {
595
+ if (keyword === 'patternProperties') {
596
+ return 'The object property ' + escapeString(String(annotation)) +
597
+ ' successfully validated against its pattern property subschema';
598
+ }
599
+ if (keyword === 'additionalProperties') {
600
+ return 'The object property ' + escapeString(String(annotation)) +
601
+ ' successfully validated against the additional properties subschema';
602
+ }
603
+ if (keyword === 'unevaluatedProperties') {
604
+ return 'The object property ' + escapeString(String(annotation)) +
605
+ ' successfully validated against the subschema for ' +
606
+ 'unevaluated properties';
607
+ }
608
+ if (keyword === 'contains' && typeof annotation === 'number') {
609
+ return 'The item at index ' + annotation +
610
+ ' of the array value successfully validated against the ' +
611
+ 'containment check subschema';
612
+ }
613
+ return '<unknown>';
614
+ }
615
+
616
+ if (opcode === LOOP_PROPERTIES) {
617
+ const childCount = children ? children.length : 0;
618
+ if (childCount > 0 && children[0][0] === ASSERTION_FAIL) {
619
+ if (keyword === 'unevaluatedProperties') {
620
+ return 'The object value was not expected to define unevaluated properties';
621
+ }
622
+ return 'The object value was not expected to define additional properties';
623
+ }
624
+ if (keyword === 'unevaluatedProperties') {
625
+ return 'The object properties not covered by other object ' +
626
+ 'keywords were expected to validate against this subschema';
627
+ }
628
+ return 'The object properties not covered by other adjacent object ' +
629
+ 'keywords were expected to validate against this subschema';
630
+ }
631
+
632
+ if (opcode === LOOP_PROPERTIES_EVALUATE) {
633
+ const childCount = children ? children.length : 0;
634
+ if (childCount === 1 && children[0][0] === ASSERTION_FAIL) {
635
+ return 'The object value was not expected to define additional properties';
636
+ }
637
+ return 'The object properties not covered by other adjacent object ' +
638
+ 'keywords were expected to validate against this subschema';
639
+ }
640
+
641
+ if (opcode === LOOP_PROPERTIES_UNEVALUATED) {
642
+ if (keyword === 'unevaluatedProperties') {
643
+ const childCount = children ? children.length : 0;
644
+ if (childCount > 0 && children[0][0] === ASSERTION_FAIL) {
645
+ return 'The object value was not expected to define unevaluated properties';
646
+ }
647
+ return 'The object properties not covered by other object ' +
648
+ 'keywords were expected to validate against this subschema';
649
+ }
650
+ return '<unknown>';
651
+ }
652
+
653
+ if (opcode === LOOP_PROPERTIES_UNEVALUATED_EXCEPT) {
654
+ if (keyword === 'unevaluatedProperties') {
655
+ const childCount = children ? children.length : 0;
656
+ if (childCount > 0 && children[0][0] === ASSERTION_FAIL) {
657
+ return 'The object value was not expected to define unevaluated properties';
658
+ }
659
+ return 'The object properties not covered by other object ' +
660
+ 'keywords were expected to validate against this subschema';
661
+ }
662
+ return '<unknown>';
663
+ }
664
+
665
+ if (opcode === LOOP_PROPERTIES_EXCEPT) {
666
+ const childCount = children ? children.length : 0;
667
+ if (childCount > 0 && children[0][0] === ASSERTION_FAIL) {
668
+ if (keyword === 'unevaluatedProperties') {
669
+ return 'The object value was not expected to define unevaluated properties';
670
+ }
671
+ return 'The object value was not expected to define additional properties';
672
+ }
673
+ if (keyword === 'unevaluatedProperties') {
674
+ return 'The object properties not covered by other object ' +
675
+ 'keywords were expected to validate against this subschema';
676
+ }
677
+ return 'The object properties not covered by other adjacent object ' +
678
+ 'keywords were expected to validate against this subschema';
679
+ }
680
+
681
+ if (opcode === LOOP_PROPERTIES_EXACTLY_TYPE_STRICT) {
682
+ return 'The required object properties were expected to be of type ' +
683
+ typeName(value[0]);
684
+ }
685
+
686
+ if (opcode === LOOP_PROPERTIES_EXACTLY_TYPE_STRICT_HASH) {
687
+ return 'The required object properties were expected to be of type ' +
688
+ typeName(value[0]);
689
+ }
690
+
691
+ if (opcode === LOOP_ITEMS_PROPERTIES_EXACTLY_TYPE_STRICT_HASH ||
692
+ opcode === LOOP_ITEMS_PROPERTIES_EXACTLY_TYPE_STRICT_HASH3) {
693
+ return 'Every item in the array was expected to be an object whose ' +
694
+ 'required properties were of type ' + typeName(value[0]);
695
+ }
696
+
697
+ if (opcode === LOOP_ITEMS_INTEGER_BOUNDED ||
698
+ opcode === LOOP_ITEMS_INTEGER_BOUNDED_SIZED) {
699
+ return 'Every item in the array was expected to be a number within the given range';
700
+ }
701
+
702
+ if (opcode === ASSERTION_TYPE_INTEGER_BOUNDED ||
703
+ opcode === ASSERTION_TYPE_INTEGER_BOUNDED_STRICT) {
704
+ return 'The value was expected to be an integer within the given range';
705
+ }
706
+
707
+ if (opcode === ASSERTION_TYPE_INTEGER_LOWER_BOUND ||
708
+ opcode === ASSERTION_TYPE_INTEGER_LOWER_BOUND_STRICT) {
709
+ return 'The value was expected to be an integer above the given minimum';
710
+ }
711
+
712
+ if (opcode === ASSERTION_OBJECT_PROPERTIES_SIMPLE) {
713
+ return 'The object value was expected to validate against the defined property subschemas';
714
+ }
715
+
716
+ if (opcode === LOOP_PROPERTIES_TYPE ||
717
+ opcode === LOOP_PROPERTIES_TYPE_EVALUATE ||
718
+ opcode === LOOP_PROPERTIES_TYPE_STRICT ||
719
+ opcode === LOOP_PROPERTIES_TYPE_STRICT_EVALUATE) {
720
+ return 'The object properties were expected to be of type ' + typeName(value);
721
+ }
722
+
723
+ if (opcode === LOOP_PROPERTIES_TYPE_STRICT_ANY) {
724
+ return 'The object properties were expected to be of type ' +
725
+ describeTypeList(value);
726
+ }
727
+
728
+ if (opcode === LOOP_PROPERTIES_TYPE_STRICT_ANY_EVALUATE) {
729
+ return 'The object properties were expected to be of type ' +
730
+ describeTypeList(value);
731
+ }
732
+
733
+ if (opcode === LOOP_KEYS) {
734
+ const size = objectSize(target);
735
+ const keys = objectKeys(target);
736
+ if (size === 0) {
737
+ return 'The object is empty and no properties were expected to ' +
738
+ 'validate against the given subschema';
739
+ }
740
+ if (size === 1) {
741
+ return 'The object property ' + escapeString(keys[0]) +
742
+ ' was expected to validate against the given subschema';
743
+ }
744
+ let message = 'The object properties ';
745
+ for (let index = 0; index < keys.length; index++) {
746
+ if (index === keys.length - 1) {
747
+ message += 'and ' + escapeString(keys[index]);
748
+ } else {
749
+ message += escapeString(keys[index]) + ', ';
750
+ }
751
+ }
752
+ message += ' were expected to validate against the given subschema';
753
+ return message;
754
+ }
755
+
756
+ if (opcode === LOOP_ITEMS) {
757
+ return 'Every item in the array value was expected to validate against the given subschema';
758
+ }
759
+
760
+ if (opcode === LOOP_ITEMS_FROM) {
761
+ let message = 'Every item in the array value';
762
+ if (value === 1) {
763
+ message += ' except for the first one';
764
+ } else if (value > 0) {
765
+ message += ' except for the first ' + value;
766
+ }
767
+ message += ' was expected to validate against the given subschema';
768
+ return message;
769
+ }
770
+
771
+ if (opcode === LOOP_ITEMS_UNEVALUATED) {
772
+ return 'The array items not covered by other array keywords, if any, ' +
773
+ 'were expected to validate against this subschema';
774
+ }
775
+
776
+ if (opcode === LOOP_ITEMS_TYPE || opcode === LOOP_ITEMS_TYPE_STRICT) {
777
+ return 'The array items were expected to be of type ' + typeName(value);
778
+ }
779
+
780
+ if (opcode === LOOP_ITEMS_TYPE_STRICT_ANY) {
781
+ return 'The array items were expected to be of type ' + describeTypeList(value);
782
+ }
783
+
784
+ if (opcode === LOOP_CONTAINS) {
785
+ const minimum = value[0];
786
+ const maximum = value[1];
787
+ let plural = true;
788
+ let message = 'The array value was expected to contain ';
789
+ if (maximum !== null) {
790
+ if (minimum === maximum && minimum === 0) {
791
+ message += 'any number of';
792
+ } else if (minimum === maximum) {
793
+ message += 'exactly ' + minimum;
794
+ if (minimum === 1) plural = false;
795
+ } else if (minimum === 0) {
796
+ message += 'up to ' + maximum;
797
+ if (maximum === 1) plural = false;
798
+ } else {
799
+ message += minimum + ' to ' + maximum;
800
+ if (maximum === 1) plural = false;
801
+ }
802
+ } else {
803
+ message += 'at least ' + minimum;
804
+ if (minimum === 1) plural = false;
805
+ }
806
+ message += plural
807
+ ? ' items that validate against the given subschema'
808
+ : ' item that validates against the given subschema';
809
+ return message;
810
+ }
811
+
812
+ if (opcode === ASSERTION_DEFINES) {
813
+ return 'The object value was expected to define the property ' +
814
+ escapeString(value);
815
+ }
816
+
817
+ if (opcode === ASSERTION_DEFINES_STRICT) {
818
+ return 'The value was expected to be an object that defines the property ' +
819
+ escapeString(value);
820
+ }
821
+
822
+ if (opcode === ASSERTION_DEFINES_ALL) {
823
+ let message = 'The object value was expected to define properties ';
824
+ for (let index = 0; index < value.length; index++) {
825
+ if (index === value.length - 1) {
826
+ message += 'and ' + escapeString(value[index]);
827
+ } else {
828
+ message += escapeString(value[index]) + ', ';
829
+ }
830
+ }
831
+ if (valid) return message;
832
+
833
+ const missing = [];
834
+ for (let index = 0; index < value.length; index++) {
835
+ if (!Object.hasOwn(target, value[index])) {
836
+ missing.push(value[index]);
837
+ }
838
+ }
839
+ missing.sort();
840
+
841
+ if (missing.length === 1) {
842
+ message += ' but did not define the property ' + escapeString(missing[0]);
843
+ } else {
844
+ message += ' but did not define properties ';
845
+ for (let index = 0; index < missing.length; index++) {
846
+ if (index === missing.length - 1) {
847
+ message += 'and ' + escapeString(missing[index]);
848
+ } else {
849
+ message += escapeString(missing[index]) + ', ';
850
+ }
851
+ }
852
+ }
853
+ return message;
854
+ }
855
+
856
+ if (opcode === ASSERTION_DEFINES_ALL_STRICT) {
857
+ let message = 'The value was expected to be an object that defines properties ';
858
+ for (let index = 0; index < value.length; index++) {
859
+ if (index === value.length - 1) {
860
+ message += 'and ' + escapeString(value[index]);
861
+ } else {
862
+ message += escapeString(value[index]) + ', ';
863
+ }
864
+ }
865
+ return message;
866
+ }
867
+
868
+ if (opcode === ASSERTION_DEFINES_EXACTLY) {
869
+ const sorted = [...value].sort();
870
+ let message = 'The object value was expected to only define properties ';
871
+ for (let index = 0; index < sorted.length; index++) {
872
+ if (index === sorted.length - 1) {
873
+ message += 'and ' + escapeString(sorted[index]);
874
+ } else {
875
+ message += escapeString(sorted[index]) + ', ';
876
+ }
877
+ }
878
+ return message;
879
+ }
880
+
881
+ if (opcode === ASSERTION_DEFINES_EXACTLY_STRICT) {
882
+ const sorted = [...value].sort();
883
+ let message =
884
+ 'The value was expected to be an object that only defines properties ';
885
+ for (let index = 0; index < sorted.length; index++) {
886
+ if (index === sorted.length - 1) {
887
+ message += 'and ' + escapeString(sorted[index]);
888
+ } else {
889
+ message += escapeString(sorted[index]) + ', ';
890
+ }
891
+ }
892
+ return message;
893
+ }
894
+
895
+ if (opcode === ASSERTION_DEFINES_EXACTLY_STRICT_HASH3) {
896
+ const entries = value[0];
897
+ return 'The value was expected to be an object that only defines the ' +
898
+ entries.length + ' given properties';
899
+ }
900
+
901
+ if (opcode === ASSERTION_TYPE || opcode === ASSERTION_TYPE_STRICT) {
902
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
903
+ instanceLocation !== '') {
904
+ const propertyName = lastInstanceToken(instanceLocation);
905
+ return 'The property name ' + escapeString(propertyName) +
906
+ ' was expected to be of type ' + typeName(value);
907
+ }
908
+ return describeTypeCheck(valid, targetType, value);
909
+ }
910
+
911
+ if (opcode === ASSERTION_TYPE_ANY) {
912
+ return describeTypesCheck(valid, targetType, value);
913
+ }
914
+
915
+ if (opcode === ASSERTION_TYPE_STRICT_ANY) {
916
+ return describeTypesCheck(valid, targetType, value);
917
+ }
918
+
919
+ if (opcode === ASSERTION_TYPE_STRING_BOUNDED) {
920
+ const minimum = value[0];
921
+ const maximum = value[1];
922
+ if (minimum === 0 && maximum !== null) {
923
+ return 'The value was expected to consist of a string of at most ' +
924
+ maximum + (maximum === 1 ? ' character' : ' characters');
925
+ }
926
+ if (maximum !== null) {
927
+ return 'The value was expected to consist of a string of ' + minimum +
928
+ ' to ' + maximum + (maximum === 1 ? ' character' : ' characters');
929
+ }
930
+ return 'The value was expected to consist of a string of at least ' +
931
+ minimum + (minimum === 1 ? ' character' : ' characters');
932
+ }
933
+
934
+ if (opcode === ASSERTION_TYPE_STRING_UPPER) {
935
+ return 'The value was expected to consist of a string of at most ' +
936
+ value + (value === 1 ? ' character' : ' characters');
937
+ }
938
+
939
+ if (opcode === ASSERTION_TYPE_ARRAY_BOUNDED) {
940
+ const minimum = value[0];
941
+ const maximum = value[1];
942
+ if (minimum === 0 && maximum !== null) {
943
+ return 'The value was expected to consist of an array of at most ' +
944
+ maximum + (maximum === 1 ? ' item' : ' items');
945
+ }
946
+ if (maximum !== null) {
947
+ return 'The value was expected to consist of an array of ' + minimum +
948
+ ' to ' + maximum + (maximum === 1 ? ' item' : ' items');
949
+ }
950
+ return 'The value was expected to consist of an array of at least ' +
951
+ minimum + (minimum === 1 ? ' item' : ' items');
952
+ }
953
+
954
+ if (opcode === ASSERTION_TYPE_ARRAY_UPPER) {
955
+ return 'The value was expected to consist of an array of at most ' +
956
+ value + (value === 1 ? ' item' : ' items');
957
+ }
958
+
959
+ if (opcode === ASSERTION_TYPE_OBJECT_BOUNDED) {
960
+ const minimum = value[0];
961
+ const maximum = value[1];
962
+ if (minimum === 0 && maximum !== null) {
963
+ return 'The value was expected to consist of an object of at most ' +
964
+ maximum + (maximum === 1 ? ' property' : ' properties');
965
+ }
966
+ if (maximum !== null) {
967
+ return 'The value was expected to consist of an object of ' + minimum +
968
+ ' to ' + maximum + (maximum === 1 ? ' property' : ' properties');
969
+ }
970
+ return 'The value was expected to consist of an object of at least ' +
971
+ minimum + (minimum === 1 ? ' property' : ' properties');
972
+ }
973
+
974
+ if (opcode === ASSERTION_TYPE_OBJECT_UPPER) {
975
+ return 'The value was expected to consist of an object of at most ' +
976
+ value + (value === 1 ? ' property' : ' properties');
977
+ }
978
+
979
+ if (opcode === ASSERTION_REGEX) {
980
+ const pattern = value.source;
981
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
982
+ instanceLocation !== '') {
983
+ const propertyName = lastInstanceToken(instanceLocation);
984
+ return 'The property name ' + escapeString(propertyName) +
985
+ ' was expected to match the regular expression ' +
986
+ escapeString(pattern);
987
+ }
988
+ return 'The string value ' + escapeString(target) +
989
+ ' was expected to match the regular expression ' +
990
+ escapeString(pattern);
991
+ }
992
+
993
+ if (opcode === ASSERTION_STRING_SIZE_LESS) {
994
+ if (keyword === 'maxLength') {
995
+ const maximum = value - 1;
996
+ let message = '';
997
+ if (isWithinKeyword(evaluatePath, 'propertyNames')) {
998
+ const propertyName = lastInstanceToken(instanceLocation);
999
+ message += 'The object property name ' + escapeString(propertyName);
1000
+ message += ' was expected to consist of at most ' + maximum +
1001
+ (maximum === 1 ? ' character' : ' characters');
1002
+ message += valid ? ' and' : ' but';
1003
+ message += ' it consisted of ';
1004
+ const length = unicodeLength(propertyName);
1005
+ message += length + (length === 1 ? ' character' : ' characters');
1006
+ } else {
1007
+ message += 'The string value ' + stringifyValue(target);
1008
+ message += ' was expected to consist of at most ' + maximum +
1009
+ (maximum === 1 ? ' character' : ' characters');
1010
+ message += valid ? ' and' : ' but';
1011
+ message += ' it consisted of ';
1012
+ const length = unicodeLength(target);
1013
+ message += length + (length === 1 ? ' character' : ' characters');
1014
+ }
1015
+ return message;
1016
+ }
1017
+ return '<unknown>';
1018
+ }
1019
+
1020
+ if (opcode === ASSERTION_STRING_SIZE_GREATER) {
1021
+ if (keyword === 'minLength') {
1022
+ const minimum = value + 1;
1023
+ let message = '';
1024
+ if (isWithinKeyword(evaluatePath, 'propertyNames')) {
1025
+ const propertyName = lastInstanceToken(instanceLocation);
1026
+ message += 'The object property name ' + escapeString(propertyName);
1027
+ message += ' was expected to consist of at least ' + minimum +
1028
+ (minimum === 1 ? ' character' : ' characters');
1029
+ message += valid ? ' and' : ' but';
1030
+ message += ' it consisted of ';
1031
+ const length = unicodeLength(propertyName);
1032
+ message += length + (length === 1 ? ' character' : ' characters');
1033
+ } else {
1034
+ message += 'The string value ' + stringifyValue(target);
1035
+ message += ' was expected to consist of at least ' + minimum +
1036
+ (minimum === 1 ? ' character' : ' characters');
1037
+ message += valid ? ' and' : ' but';
1038
+ message += ' it consisted of ';
1039
+ const length = unicodeLength(target);
1040
+ message += length + (length === 1 ? ' character' : ' characters');
1041
+ }
1042
+ return message;
1043
+ }
1044
+ return '<unknown>';
1045
+ }
1046
+
1047
+ if (opcode === ASSERTION_ARRAY_SIZE_LESS) {
1048
+ if (keyword === 'maxItems') {
1049
+ const maximum = value - 1;
1050
+ let message = 'The array value was expected to contain at most ' +
1051
+ maximum + (maximum === 1 ? ' item' : ' items');
1052
+ message += valid ? ' and' : ' but';
1053
+ message += ' it contained ' + target.length +
1054
+ (target.length === 1 ? ' item' : ' items');
1055
+ return message;
1056
+ }
1057
+ return '<unknown>';
1058
+ }
1059
+
1060
+ if (opcode === ASSERTION_ARRAY_SIZE_GREATER) {
1061
+ const minimum = value + 1;
1062
+ let message = 'The array value was expected to contain at least ' +
1063
+ minimum + (minimum === 1 ? ' item' : ' items');
1064
+ message += valid ? ' and' : ' but';
1065
+ message += ' it contained ' + target.length +
1066
+ (target.length === 1 ? ' item' : ' items');
1067
+ return message;
1068
+ }
1069
+
1070
+ if (opcode === ASSERTION_OBJECT_SIZE_LESS) {
1071
+ if (keyword === 'additionalProperties') {
1072
+ return 'The object value was not expected to define additional properties';
1073
+ }
1074
+ if (keyword === 'maxProperties') {
1075
+ const maximum = value - 1;
1076
+ const size = objectSize(target);
1077
+ let message = 'The object value was expected to contain at most ' +
1078
+ maximum + (maximum === 1 ? ' property' : ' properties');
1079
+ message += valid ? ' and' : ' but';
1080
+ message += ' it contained ' + size;
1081
+ if (size === 0) {
1082
+ message += ' properties';
1083
+ } else if (size === 1) {
1084
+ message += ' property: ' + escapeString(objectKeys(target)[0]);
1085
+ } else {
1086
+ message += ' properties: ';
1087
+ const properties = objectKeys(target).sort();
1088
+ message += formatList(properties.map(escapeString), 'and');
1089
+ }
1090
+ return message;
1091
+ }
1092
+ return '<unknown>';
1093
+ }
1094
+
1095
+ if (opcode === ASSERTION_OBJECT_SIZE_GREATER) {
1096
+ if (keyword === 'minProperties') {
1097
+ const minimum = value + 1;
1098
+ const size = objectSize(target);
1099
+ let message = 'The object value was expected to contain at least ' +
1100
+ minimum + (minimum === 1 ? ' property' : ' properties');
1101
+ message += valid ? ' and' : ' but';
1102
+ message += ' it contained ' + size;
1103
+ if (size === 1) {
1104
+ message += ' property: ' + escapeString(objectKeys(target)[0]);
1105
+ } else {
1106
+ message += ' properties: ';
1107
+ const properties = objectKeys(target).sort();
1108
+ message += formatList(properties.map(escapeString), 'and');
1109
+ }
1110
+ return message;
1111
+ }
1112
+ return '<unknown>';
1113
+ }
1114
+
1115
+ if (opcode === ASSERTION_EQUAL) {
1116
+ let message = '';
1117
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
1118
+ instanceLocation !== '') {
1119
+ const propertyName = lastInstanceToken(instanceLocation);
1120
+ message += 'The property name ' + escapeString(propertyName);
1121
+ } else {
1122
+ message += 'The ' + typeName(targetType) + ' value ';
1123
+ message += stringifyValue(target);
1124
+ }
1125
+ message += ' was expected to equal the ' + typeName(jsonTypeOf(value)) +
1126
+ ' constant ' + stringifyValue(value);
1127
+ return message;
1128
+ }
1129
+
1130
+ if (opcode === ASSERTION_EQUALS_ANY) {
1131
+ let message = '';
1132
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
1133
+ instanceLocation !== '') {
1134
+ const propertyName = lastInstanceToken(instanceLocation);
1135
+ message += 'The property name ' + escapeString(propertyName);
1136
+ } else {
1137
+ message += 'The ' + typeName(targetType) + ' value ';
1138
+ message += stringifyValue(target);
1139
+ }
1140
+
1141
+ const values = Array.isArray(value) ? value : (value.values || []);
1142
+ if (values.length === 1) {
1143
+ message += ' was expected to equal the ' +
1144
+ typeName(jsonTypeOf(values[0])) + ' constant ' +
1145
+ stringifyValue(values[0]);
1146
+ } else {
1147
+ if (valid) {
1148
+ message += ' was expected to equal one of the ' +
1149
+ values.length + ' declared values';
1150
+ } else {
1151
+ message += ' was expected to equal one of the following values: ';
1152
+ const sorted = [...values].sort(jsonCompare);
1153
+ for (let index = 0; index < sorted.length; index++) {
1154
+ if (index === sorted.length - 1) {
1155
+ message += 'and ' + stringifyValue(sorted[index]);
1156
+ } else {
1157
+ message += stringifyValue(sorted[index]) + ', ';
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+ return message;
1163
+ }
1164
+
1165
+ if (opcode === ASSERTION_EQUALS_ANY_STRING_HASH) {
1166
+ let message = '';
1167
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
1168
+ instanceLocation !== '') {
1169
+ const propertyName = lastInstanceToken(instanceLocation);
1170
+ message += 'The property name ' + escapeString(propertyName);
1171
+ } else {
1172
+ message += 'The ' + typeName(targetType) + ' value ';
1173
+ message += stringifyValue(target);
1174
+ }
1175
+ const entries = value[0];
1176
+ if (entries.length === 1) {
1177
+ message += ' was expected to equal the given constant';
1178
+ } else {
1179
+ message += ' was expected to equal one of the given declared values';
1180
+ }
1181
+ return message;
1182
+ }
1183
+
1184
+ if (opcode === ASSERTION_GREATER_EQUAL) {
1185
+ return 'The ' + valueTypeName(target) + ' value ' +
1186
+ stringifyValue(target) +
1187
+ ' was expected to be greater than or equal to the ' +
1188
+ valueTypeName(value) + ' ' + stringifyValue(value);
1189
+ }
1190
+
1191
+ if (opcode === ASSERTION_LESS_EQUAL) {
1192
+ return 'The ' + valueTypeName(target) + ' value ' +
1193
+ stringifyValue(target) +
1194
+ ' was expected to be less than or equal to the ' +
1195
+ valueTypeName(value) + ' ' + stringifyValue(value);
1196
+ }
1197
+
1198
+ if (opcode === ASSERTION_GREATER) {
1199
+ let message = 'The ' + valueTypeName(target) + ' value ' +
1200
+ stringifyValue(target) +
1201
+ ' was expected to be greater than the ' +
1202
+ valueTypeName(value) + ' ' + stringifyValue(value);
1203
+ if (!valid && jsonEqual(value, target)) {
1204
+ message += ', but they were equal';
1205
+ }
1206
+ return message;
1207
+ }
1208
+
1209
+ if (opcode === ASSERTION_LESS) {
1210
+ let message = 'The ' + valueTypeName(target) + ' value ' +
1211
+ stringifyValue(target) +
1212
+ ' was expected to be less than the ' +
1213
+ valueTypeName(value) + ' ' + stringifyValue(value);
1214
+ if (!valid && jsonEqual(value, target)) {
1215
+ message += ', but they were equal';
1216
+ }
1217
+ return message;
1218
+ }
1219
+
1220
+ if (opcode === ASSERTION_UNIQUE) {
1221
+ if (valid) {
1222
+ return 'The array value was expected to not contain duplicate items';
1223
+ }
1224
+ const duplicateStrs = new Set();
1225
+ for (let index = 0; index < target.length; index++) {
1226
+ for (let other = index + 1; other < target.length; other++) {
1227
+ if (jsonEqual(target[index], target[other])) {
1228
+ duplicateStrs.add(stringifyValue(target[index]));
1229
+ }
1230
+ }
1231
+ }
1232
+ const sorted = [...duplicateStrs].sort();
1233
+ if (sorted.length === 1) {
1234
+ return 'The array value contained the following duplicate item: ' + sorted[0];
1235
+ }
1236
+ let message = 'The array value contained the following duplicate items: ';
1237
+ for (let index = 0; index < sorted.length; index++) {
1238
+ if (index === sorted.length - 1) {
1239
+ message += 'and ' + sorted[index];
1240
+ } else {
1241
+ message += sorted[index] + ', ';
1242
+ }
1243
+ }
1244
+ return message;
1245
+ }
1246
+
1247
+ if (opcode === ASSERTION_DIVISIBLE) {
1248
+ return 'The ' + valueTypeName(target) + ' value ' +
1249
+ stringifyValue(target) +
1250
+ ' was expected to be divisible by the ' +
1251
+ valueTypeName(value) + ' ' + stringifyValue(value);
1252
+ }
1253
+
1254
+ if (opcode === ASSERTION_STRING_TYPE) {
1255
+ return 'The string value ' + escapeString(target) +
1256
+ ' was expected to represent a valid URI';
1257
+ }
1258
+
1259
+ if (opcode === ASSERTION_PROPERTY_TYPE ||
1260
+ opcode === ASSERTION_PROPERTY_TYPE_EVALUATE ||
1261
+ opcode === ASSERTION_PROPERTY_TYPE_STRICT ||
1262
+ opcode === ASSERTION_PROPERTY_TYPE_STRICT_EVALUATE) {
1263
+ return describeTypeCheck(valid, targetType, value);
1264
+ }
1265
+
1266
+ if (opcode === ASSERTION_PROPERTY_TYPE_STRICT_ANY ||
1267
+ opcode === ASSERTION_PROPERTY_TYPE_STRICT_ANY_EVALUATE) {
1268
+ return describeTypesCheck(valid, targetType, value);
1269
+ }
1270
+
1271
+ if (opcode === ASSERTION_ARRAY_PREFIX ||
1272
+ opcode === ASSERTION_ARRAY_PREFIX_EVALUATE) {
1273
+ const childCount = children ? children.length : 0;
1274
+ let message = 'The first ';
1275
+ if (childCount <= 2) {
1276
+ message += 'item of the array value was';
1277
+ } else {
1278
+ message += (childCount - 1) + ' items of the array value were';
1279
+ }
1280
+ message += ' expected to validate against the corresponding subschemas';
1281
+ return message;
1282
+ }
1283
+
1284
+ if (opcode === LOOP_PROPERTIES_MATCH) {
1285
+ const childCount = children ? children.length : 0;
1286
+ let message = 'The object value was expected to validate against the ';
1287
+ if (childCount === 1) {
1288
+ message += 'single defined property subschema';
1289
+ } else {
1290
+ message += childCount + ' defined properties subschemas';
1291
+ }
1292
+ return message;
1293
+ }
1294
+
1295
+ if (opcode === LOOP_PROPERTIES_MATCH_CLOSED) {
1296
+ const childCount = children ? children.length : 0;
1297
+ if (childCount === 1) {
1298
+ return 'The object value was expected to validate against the ' +
1299
+ 'single defined property subschema';
1300
+ }
1301
+ return 'Every object value was expected to validate against one of the ' +
1302
+ childCount + ' defined properties subschemas';
1303
+ }
1304
+
1305
+ if (opcode === LOGICAL_WHEN_DEFINES) {
1306
+ return 'The object value defined the property "' + value + '"';
1307
+ }
1308
+
1309
+ if (opcode === LOOP_PROPERTIES_REGEX) {
1310
+ return 'The object properties that match the regular expression "' +
1311
+ value.source + '" were expected to validate against the defined ' +
1312
+ 'pattern property subschema';
1313
+ }
1314
+
1315
+ if (opcode === LOOP_PROPERTIES_REGEX_CLOSED) {
1316
+ return 'The object properties were expected to match the regular ' +
1317
+ 'expression "' + value.source + '" and validate against the ' +
1318
+ 'defined pattern property subschema';
1319
+ }
1320
+
1321
+ if (opcode === LOOP_PROPERTIES_STARTS_WITH) {
1322
+ return 'The object properties that start with the string "' + value +
1323
+ '" were expected to validate against the defined pattern property subschema';
1324
+ }
1325
+
1326
+ if (opcode === LOGICAL_WHEN_TYPE) {
1327
+ if (keyword === 'items') {
1328
+ return describeTypeCheck(valid, targetType, value);
1329
+ }
1330
+
1331
+ if (keyword === 'properties') {
1332
+ const childCount = children ? children.length : 0;
1333
+ if (targetType !== TYPE_OBJECT) {
1334
+ return describeTypeCheck(valid, targetType, TYPE_OBJECT);
1335
+ }
1336
+ let message = 'The object value was expected to validate against the ';
1337
+ if (childCount === 1) {
1338
+ message += 'single defined property subschema';
1339
+ } else {
1340
+ message += 'defined properties subschemas';
1341
+ }
1342
+ return message;
1343
+ }
1344
+
1345
+ if (keyword === 'dependencies') {
1346
+ const childCount = children ? children.length : 0;
1347
+ const present = new Set();
1348
+ const presentWithSchemas = new Set();
1349
+ const presentWithProperties = new Set();
1350
+ const allDependencies = new Set();
1351
+ const requiredProperties = new Set();
1352
+
1353
+ for (let index = 0; index < childCount; index++) {
1354
+ const child = children[index];
1355
+ if (child[0] === LOGICAL_WHEN_DEFINES) {
1356
+ const property = child[5];
1357
+ allDependencies.add(property);
1358
+ if (!Object.hasOwn(target, property)) continue;
1359
+ present.add(property);
1360
+ presentWithSchemas.add(property);
1361
+ } else if (child[0] === ASSERTION_PROPERTY_DEPENDENCIES) {
1362
+ const entries = child[5];
1363
+ for (const property in entries) {
1364
+ allDependencies.add(property);
1365
+ if (Object.hasOwn(target, property)) {
1366
+ present.add(property);
1367
+ presentWithProperties.add(property);
1368
+ const dependencies = entries[property];
1369
+ for (let depIndex = 0; depIndex < dependencies.length; depIndex++) {
1370
+ if (valid || !Object.hasOwn(target, dependencies[depIndex])) {
1371
+ requiredProperties.add(dependencies[depIndex]);
1372
+ }
1373
+ }
1374
+ }
1375
+ }
1376
+ }
1377
+ }
1378
+
1379
+ const allDepsArr = [...allDependencies].sort();
1380
+ const presentArr = [...present].sort();
1381
+ const presentSchemasArr = [...presentWithSchemas].sort();
1382
+ const requiredArr = [...requiredProperties].sort();
1383
+
1384
+ if (presentWithSchemas.size === 0 && presentWithProperties.size === 0) {
1385
+ let message = 'The object value did not define the';
1386
+ if (allDepsArr.length === 1) {
1387
+ message += ' property ' + escapeString(allDepsArr[0]);
1388
+ } else {
1389
+ message += ' properties ';
1390
+ message += formatList(allDepsArr.map(escapeString), 'or');
1391
+ }
1392
+ return message;
1393
+ }
1394
+
1395
+ let message = '';
1396
+ if (presentArr.length === 1) {
1397
+ message += 'Because the object value defined the';
1398
+ message += ' property ' + escapeString(presentArr[0]);
1399
+ } else {
1400
+ message += 'Because the object value defined the';
1401
+ message += ' properties ';
1402
+ message += formatList(presentArr.map(escapeString), 'and');
1403
+ }
1404
+
1405
+ if (requiredArr.length > 0) {
1406
+ message += ', it was also expected to define the';
1407
+ if (requiredArr.length === 1) {
1408
+ message += ' property ' + escapeString(requiredArr[0]);
1409
+ } else {
1410
+ message += ' properties ';
1411
+ message += formatList(requiredArr.map(escapeString), 'and');
1412
+ }
1413
+ }
1414
+
1415
+ if (presentSchemasArr.length > 0) {
1416
+ message += ', ';
1417
+ if (requiredArr.length > 0) message += 'and ';
1418
+ message += 'it was also expected to successfully validate against ' +
1419
+ 'the corresponding ';
1420
+ if (presentSchemasArr.length === 1) {
1421
+ message += escapeString(presentSchemasArr[0]) + ' subschema';
1422
+ } else {
1423
+ message += formatList(presentSchemasArr.map(escapeString), 'and');
1424
+ message += ' subschemas';
1425
+ }
1426
+ }
1427
+
1428
+ return message;
1429
+ }
1430
+
1431
+ if (keyword === 'dependentSchemas') {
1432
+ const childCount = children ? children.length : 0;
1433
+ const present = new Set();
1434
+ const allDependencies = new Set();
1435
+
1436
+ for (let index = 0; index < childCount; index++) {
1437
+ const child = children[index];
1438
+ const property = child[5];
1439
+ allDependencies.add(property);
1440
+ if (Object.hasOwn(target, property)) {
1441
+ present.add(property);
1442
+ }
1443
+ }
1444
+
1445
+ const allDepsArr = [...allDependencies].sort();
1446
+ const presentArr = [...present].sort();
1447
+
1448
+ if (present.size === 0) {
1449
+ let message = 'The object value did not define the';
1450
+ if (allDepsArr.length === 1) {
1451
+ message += ' property ' + escapeString(allDepsArr[0]);
1452
+ } else {
1453
+ message += ' properties ';
1454
+ message += formatList(allDepsArr.map(escapeString), 'or');
1455
+ }
1456
+ return message;
1457
+ }
1458
+
1459
+ if (present.size === 1) {
1460
+ return 'Because the object value defined the property ' +
1461
+ escapeString(presentArr[0]) +
1462
+ ', it was also expected to validate against the corresponding subschema';
1463
+ }
1464
+
1465
+ let message = 'Because the object value defined the properties ';
1466
+ message += formatList(presentArr.map(escapeString), 'and');
1467
+ message += ', it was also expected to validate against the ' +
1468
+ 'corresponding subschemas';
1469
+ return message;
1470
+ }
1471
+
1472
+ return '<unknown>';
1473
+ }
1474
+
1475
+ if (opcode === ASSERTION_PROPERTY_DEPENDENCIES) {
1476
+ const present = [];
1477
+ const allDependencies = [];
1478
+ const required = new Set();
1479
+
1480
+ for (const property in value) {
1481
+ allDependencies.push(property);
1482
+ if (Object.hasOwn(target, property)) {
1483
+ present.push(property);
1484
+ const dependencies = value[property];
1485
+ for (let index = 0; index < dependencies.length; index++) {
1486
+ if (valid || !Object.hasOwn(target, dependencies[index])) {
1487
+ required.add(dependencies[index]);
1488
+ }
1489
+ }
1490
+ }
1491
+ }
1492
+
1493
+ allDependencies.sort();
1494
+ present.sort();
1495
+ const requiredArr = [...required].sort();
1496
+
1497
+ if (present.length === 0) {
1498
+ let message = 'The object value did not define the';
1499
+ if (allDependencies.length === 1) {
1500
+ message += ' property ' + escapeString(allDependencies[0]);
1501
+ } else {
1502
+ message += ' properties ';
1503
+ message += formatList(allDependencies.map(escapeString), 'or');
1504
+ }
1505
+ return message;
1506
+ }
1507
+
1508
+ let message = '';
1509
+ if (present.length === 1) {
1510
+ message += 'Because the object value defined the';
1511
+ message += ' property ' + escapeString(present[0]);
1512
+ } else {
1513
+ message += 'Because the object value defined the';
1514
+ message += ' properties ';
1515
+ message += formatList(present.map(escapeString), 'and');
1516
+ }
1517
+
1518
+ message += ', it was also expected to define the';
1519
+ if (requiredArr.length === 1) {
1520
+ message += ' property ' + escapeString(requiredArr[0]);
1521
+ } else {
1522
+ message += ' properties ';
1523
+ message += formatList(requiredArr.map(escapeString), 'and');
1524
+ }
1525
+ return message;
1526
+ }
1527
+
1528
+ if (opcode === LOGICAL_WHEN_ARRAY_SIZE_GREATER) {
1529
+ if (keyword === 'additionalItems' || keyword === 'items') {
1530
+ if (target.length > value) {
1531
+ const rest = target.length - value;
1532
+ return 'The array value contains ' + rest + ' additional' +
1533
+ (rest === 1 ? ' item' : ' items') +
1534
+ ' not described by related keywords';
1535
+ }
1536
+ return 'The array value does not contain additional items not ' +
1537
+ 'described by related keywords';
1538
+ }
1539
+ return '<unknown>';
1540
+ }
1541
+
1542
+ if (opcode === CONTROL_JUMP) {
1543
+ if (isWithinKeyword(evaluatePath, 'propertyNames') &&
1544
+ instanceLocation !== '') {
1545
+ return 'The string value was expected to validate against the referenced schema';
1546
+ }
1547
+ return describeReference(target);
1548
+ }
1549
+
1550
+ return '<unknown>';
1551
+ }