ata-validator 1.61.0 → 1.61.2

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 (43) hide show
  1. package/README.md +34 -7
  2. package/compiled-bare-rich.d.ts +6 -0
  3. package/compiled-bare.d.ts +7 -0
  4. package/compiled-bare.js +5 -0
  5. package/compiled-bare.mjs +35 -0
  6. package/compiled-object.d.ts +11 -0
  7. package/compiled-object.js +6 -0
  8. package/compiled-object.mjs +3 -0
  9. package/compiled-plain.d.ts +16 -0
  10. package/compiled-plain.js +5 -0
  11. package/compiled-plain.mjs +3 -0
  12. package/index.aot.js +8 -0
  13. package/index.aot.mjs +5 -0
  14. package/lib/aot-build.js +9 -1
  15. package/lib/aot-impl.js +10 -2
  16. package/lib/aot-mark.js +7 -0
  17. package/lib/compiled-bare-rich.js +54 -0
  18. package/lib/compiled-bare.js +44 -0
  19. package/lib/compiled-common.js +17 -2
  20. package/lib/compiled-defaults.browser.js +9 -0
  21. package/lib/compiled-defaults.js +8 -0
  22. package/lib/compiled-object.js +123 -0
  23. package/lib/compiled-plain.js +57 -0
  24. package/lib/compiled.js +2 -1
  25. package/lib/email-literal.js +29 -0
  26. package/lib/enrich-error.js +5 -1
  27. package/lib/error-codes.js +6 -76
  28. package/lib/formats-source.js +20 -3
  29. package/lib/formats.js +28 -1
  30. package/lib/interpreter-core.js +4 -28
  31. package/lib/js-compiler-errors.js +4 -0
  32. package/lib/json-rejections.js +159 -0
  33. package/lib/native-codes.js +67 -0
  34. package/lib/native-message.js +8 -0
  35. package/lib/native.browser.js +11 -0
  36. package/lib/rejections.js +13 -152
  37. package/lib/shape-classifier.js +6 -1
  38. package/lib/standard-schema.js +1 -1
  39. package/lib/tier0.js +17 -4
  40. package/lib/validator-core.js +50 -19
  41. package/lib/verify.js +68 -3
  42. package/lib/version.js +1 -1
  43. package/package.json +54 -10
@@ -1,6 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { CODES, codeFor, fromNative } = require('./error-codes');
3
+ const { CODES, codeFor } = require('./error-codes');
4
+ // Only the native engine writes a numeric code, so its table loads with the
5
+ // first such error (lib/native-codes.js), not with every process.
6
+ let _fromNative = null;
7
+ const fromNative = (ordinal, format) => (_fromNative || (_fromNative = require('./native-codes').fromNative))(ordinal, format);
4
8
  const { suggestFor } = require('./suggestions');
5
9
  const { resolvePointer } = require('./pointer');
6
10
 
@@ -26,16 +26,10 @@ const CODES = Object.freeze({
26
26
  ATA2013: { keyword: 'pattern', category: 'constraint', headline: 'string does not match pattern' },
27
27
 
28
28
  // ATA3xxx — format
29
- ATA3001: { keyword: 'format', format: 'email', category: 'format', headline: 'value does not match format "email"' },
30
- ATA3002: { keyword: 'format', format: 'date', category: 'format', headline: 'value does not match format "date"' },
31
- ATA3003: { keyword: 'format', format: 'date-time', category: 'format', headline: 'value does not match format "date-time"' },
32
- ATA3004: { keyword: 'format', format: 'time', category: 'format', headline: 'value does not match format "time"' },
33
- ATA3005: { keyword: 'format', format: 'uri', category: 'format', headline: 'value does not match format "uri"' },
34
- ATA3006: { keyword: 'format', format: 'uri-reference', category: 'format', headline: 'value does not match format "uri-reference"' },
35
- ATA3007: { keyword: 'format', format: 'ipv4', category: 'format', headline: 'value does not match format "ipv4"' },
36
- ATA3008: { keyword: 'format', format: 'ipv6', category: 'format', headline: 'value does not match format "ipv6"' },
37
- ATA3009: { keyword: 'format', format: 'uuid', category: 'format', headline: 'value does not match format "uuid"' },
38
- ATA3010: { keyword: 'format', format: 'hostname', category: 'format', headline: 'value does not match format "hostname"' },
29
+ // One entry per built-in format the engines name, in this order: the same
30
+ // object for each, so it is written once.
31
+ ...Object.fromEntries(['email', 'date', 'date-time', 'time', 'uri', 'uri-reference', 'ipv4', 'ipv6', 'uuid', 'hostname']
32
+ .map((format, i) => [`ATA30${String(i + 1).padStart(2, '0')}`, { keyword: 'format', format, category: 'format', headline: `value does not match format "${format}"` }])),
39
33
  ATA3099: { keyword: 'format', category: 'format', headline: 'value does not match user-defined format' },
40
34
 
41
35
  // ATA4xxx — composition
@@ -69,14 +63,6 @@ const CODES = Object.freeze({
69
63
  ATA9002: { keyword: '__compile__', category: 'system', headline: 'schema failed to compile' },
70
64
  });
71
65
 
72
- function get (code) {
73
- return CODES[code];
74
- }
75
-
76
- function all () {
77
- return Object.keys(CODES).sort();
78
- }
79
-
80
66
  // Reverse lookups, built once. codeFor runs per error on every failing
81
67
  // validation, so walking the table there showed up as most of the cost of
82
68
  // enriching a rejected payload. Insertion follows sorted code order, so the
@@ -101,62 +87,6 @@ function codeFor (keyword, format) {
101
87
  }
102
88
 
103
89
 
104
- // The native engine reports its own `error_code` enum from include/ata.h as an
105
- // ordinal. Nothing outside the addon should ever see one: an error that came
106
- // from there carries the same public code and keyword as the same failure from
107
- // the JavaScript engines. Ordinals are positional, so this table follows the
108
- // enum's declaration order and `tests/test_native_error_codes.js` fails if the
109
- // two drift apart.
110
- const NATIVE_KEYWORDS = [
111
- null, // 0 ok
112
- '__parse__', // 1 invalid_json
113
- '__compile__', // 2 invalid_schema
114
- 'type', // 3 type_mismatch
115
- 'required', // 4 required_property_missing
116
- 'additionalProperties', // 5 additional_property_not_allowed
117
- 'enum', // 6 enum_mismatch
118
- 'const', // 7 const_mismatch
119
- 'minimum', // 8 minimum_violation
120
- 'maximum', // 9 maximum_violation
121
- 'exclusiveMinimum', // 10 exclusive_minimum_violation
122
- 'exclusiveMaximum', // 11 exclusive_maximum_violation
123
- 'minLength', // 12 min_length_violation
124
- 'maxLength', // 13 max_length_violation
125
- 'pattern', // 14 pattern_mismatch
126
- 'format', // 15 format_mismatch
127
- 'minItems', // 16 min_items_violation
128
- 'maxItems', // 17 max_items_violation
129
- 'uniqueItems', // 18 unique_items_violation
130
- 'minProperties', // 19 min_properties_violation
131
- 'maxProperties', // 20 max_properties_violation
132
- 'multipleOf', // 21 multiple_of_violation
133
- 'allOf', // 22 all_of_failed
134
- 'anyOf', // 23 any_of_failed
135
- 'oneOf', // 24 one_of_failed
136
- 'not', // 25 not_failed
137
- '$ref', // 26 ref_not_found
138
- 'if', // 27 if_then_else_failed
139
- '__depth__', // 28 depth_exceeded
140
- ]
141
-
142
- // Keyword and public code for a native ordinal, or null when the ordinal is
143
- // outside the enum, which would mean the addon is newer than this table.
144
- function fromNative (ordinal, format) {
145
- if (typeof ordinal !== 'number' || !Number.isInteger(ordinal)) return null
146
- const keyword = NATIVE_KEYWORDS[ordinal]
147
- if (!keyword) return null
148
- // codeFor('format') answers with the first format code, which names a
149
- // specific format. Without knowing which format failed, the generic one is
150
- // the honest answer.
151
- // The walker stopped at its depth limit and rejected the document. No
152
- // public code names that: the APIs that ask the walker route schemas deep or
153
- // recursive enough to reach it to validate() first (lib/buffer-gate.js), so
154
- // it is the generic failure.
155
- if (keyword === '__depth__') return { keyword, code: 'ATA9000' }
156
- const code = keyword === 'format'
157
- ? (format ? codeFor('format', format) || 'ATA3099' : 'ATA3099')
158
- : codeFor(keyword) || 'ATA9001'
159
- return { keyword, code }
160
- }
90
+ // The native engine's ordinals are translated in lib/native-codes.js.
161
91
 
162
- module.exports = { CODES, get, all, codeFor, fromNative, NATIVE_KEYWORDS };
92
+ module.exports = { CODES, codeFor };
@@ -263,6 +263,21 @@ const EMAIL_LOCAL_SRC = (v) => `(${v}>32&&${v}<127&&${v}!==34&&${v}!==40&&${v}!=
263
263
  // functions by their source text, so anything reaching a free variable would
264
264
  // not survive the trip. The domain half reuses hostnameSource, which keeps
265
265
  // one hostname implementation for both halves of this file.
266
+ // The check of an email address literal's inside (`_in`, between the
267
+ // brackets). Where code is written for a module, by expression
268
+ // (lib/email-literal.js), which is a fraction of the bytes of the parsers. A
269
+ // browser build maps that module to nothing: its generator writes code for the
270
+ // page alone, so it uses the parsers it carries anyway and the expression's
271
+ // builder stays out of the page. tests/test_ipv6_expression.js holds both forms
272
+ // to the same answers.
273
+ const _literalByParsers = () =>
274
+ `if((_in.charCodeAt(0)|32)===105&&(_in.charCodeAt(1)|32)===112&&(_in.charCodeAt(2)|32)===118&&_in.charCodeAt(3)===54&&_in.charCodeAt(4)===58){const _i6=_in.slice(5);${ipv6Source('_i6', true)}}` +
275
+ `else{${ipv4Source('_in', true)}}`;
276
+ const _emailLiteral = () => {
277
+ const byExpression = require('./email-literal');
278
+ return typeof byExpression === 'function' ? byExpression() : _literalByParsers();
279
+ };
280
+
266
281
  function emailSource (v, isStr) {
267
282
  const inner =
268
283
  // One forward pass, and neither half sliced out. '@' is not a local-part
@@ -294,8 +309,10 @@ function emailSource (v, isStr) {
294
309
  `const _dm=${v}.slice(_at+1);` +
295
310
  `if(_dm.charCodeAt(_dm.length-1)!==93||_dm.length<=2)return false;` +
296
311
  `const _in=_dm.slice(1,-1);` +
297
- `if((_in.charCodeAt(0)|32)===105&&(_in.charCodeAt(1)|32)===112&&(_in.charCodeAt(2)|32)===118&&_in.charCodeAt(3)===54&&_in.charCodeAt(4)===58){const _i6=_in.slice(5);${ipv6Source('_i6', true)}}` +
298
- `else{${ipv4Source('_in', true)}}` +
312
+ // The literal is rare, and checked by expression rather than by the
313
+ // IPv6 and IPv4 parsers, which made it 72% of this check's source:
314
+ // the same answers (tests/test_ipv6_expression.js), a fraction of the bytes.
315
+ _emailLiteral() +
299
316
  `}` +
300
317
  `else{${hostnameRangeSource(v, '_at+1', `${v}.length`)}}`;
301
318
  return isStr ? `{${inner}}` : `if(typeof ${v}==='string'){${inner}}`;
@@ -304,4 +321,4 @@ function durationSource (v, isStr) {
304
321
  const inner = `if(!${_formats().DURATION_RE.toString()}.test(${v}))return false`;
305
322
  return isStr ? inner : `if(typeof ${v}==='string'&&${inner.slice(3)}`;
306
323
  }
307
- module.exports = { uriHelperSource, uriHelperCompactSource, uriSource, uriReferenceSource, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uuidSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };
324
+ module.exports = { _literalByParsers, uriHelperSource, uriHelperCompactSource, uriSource, uriReferenceSource, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uuidSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };
package/lib/formats.js CHANGED
@@ -538,4 +538,31 @@ function email(s) {
538
538
  const DURATION_RE = /^P(?:\d+W|(?:\d+Y(?:\d+M(?:\d+D)?)?|\d+M(?:\d+D)?|\d+D)(?:T(?:\d+H(?:\d+M(?:\d+S)?)?|\d+M(?:\d+S)?|\d+S))?|T(?:\d+H(?:\d+M(?:\d+S)?)?|\d+M(?:\d+S)?|\d+S))$/;
539
539
  function duration (s) { return DURATION_RE.test(s); }
540
540
 
541
- module.exports = { IPV4, DURATION_RE, IPV6_FULL, ipv4Range, URI_FAST, date, ipv4, dateTime, ipv6, hostname, uri, uriReference, uriWalk: _uriWalk, uuid, time, noReserved, jsonPointer, relativeJsonPointer, uriTemplate, email, duration, iri, iriReference, idnEmail, DURATION_RE, URI_TEMPLATE_EXPR };
541
+ // The built-in format checks by name, built on first use. The interpreted
542
+ // engine and the tier-0 verdict (lib/tier0.js) read the same table, so the two
543
+ // cannot answer differently for a format.
544
+ let _checks = null;
545
+ function formatChecks () {
546
+ return _checks || (_checks = {
547
+ email,
548
+ date,
549
+ 'date-time': dateTime,
550
+ time,
551
+ duration,
552
+ uuid,
553
+ uri,
554
+ 'uri-reference': uriReference,
555
+ ipv4,
556
+ ipv6,
557
+ hostname,
558
+ regex: (s) => { try { new RegExp(s, 'u'); return true; } catch { return false; } },
559
+ 'json-pointer': jsonPointer,
560
+ 'relative-json-pointer': relativeJsonPointer,
561
+ 'uri-template': uriTemplate,
562
+ iri,
563
+ 'iri-reference': iriReference,
564
+ 'idn-email': idnEmail,
565
+ });
566
+ }
567
+
568
+ module.exports = { formatChecks, IPV4, DURATION_RE, IPV6_FULL, ipv4Range, URI_FAST, date, ipv4, dateTime, ipv6, hostname, uri, uriReference, uriWalk: _uriWalk, uuid, time, noReserved, jsonPointer, relativeJsonPointer, uriTemplate, email, duration, iri, iriReference, idnEmail, DURATION_RE, URI_TEMPLATE_EXPR };
@@ -288,33 +288,9 @@ function multipleOfOk(d, m) {
288
288
  return Math.abs(q - Math.round(q)) < 1e-9;
289
289
  }
290
290
 
291
- // The built-in format checks, built on the first schema that names a format:
292
- // the engine loads without lib/formats.js, which schemas without `format`
293
- // never need.
294
- let _formatChecks = null;
295
- function buildFormatChecks() {
296
- const _formats = require('./formats');
297
- return {
298
- email: _formats.email,
299
- date: _formats.date,
300
- 'date-time': _formats.dateTime,
301
- time: _formats.time,
302
- duration: _formats.duration,
303
- uuid: _formats.uuid,
304
- uri: _formats.uri,
305
- 'uri-reference': _formats.uriReference,
306
- ipv4: _formats.ipv4,
307
- ipv6: _formats.ipv6,
308
- hostname: _formats.hostname,
309
- regex: (s) => { try { new RegExp(s, 'u'); return true; } catch { return false; } },
310
- 'json-pointer': _formats.jsonPointer,
311
- 'relative-json-pointer': _formats.relativeJsonPointer,
312
- 'uri-template': _formats.uriTemplate,
313
- iri: _formats.iri,
314
- 'iri-reference': _formats.iriReference,
315
- 'idn-email': _formats.idnEmail,
316
- };
317
- }
291
+ // The built-in format checks come from lib/formats.js (formatChecks), loaded
292
+ // on the first schema that names a format: the engine loads without it, which
293
+ // schemas without `format` never need.
318
294
 
319
295
  // ---------- evaluation ----------
320
296
 
@@ -454,7 +430,7 @@ class Plan {
454
430
  this.formatFn = null;
455
431
  if (schema.format !== undefined) {
456
432
  const uf = interp.userFormats;
457
- const fc = (uf && typeof uf[schema.format] === 'function') ? uf[schema.format] : (_formatChecks || (_formatChecks = buildFormatChecks()))[schema.format];
433
+ const fc = (uf && typeof uf[schema.format] === 'function') ? uf[schema.format] : require('./formats').formatChecks()[schema.format];
458
434
  this.formatFn = fc || null;
459
435
  }
460
436
  this.hasString = this.minLength !== undefined || this.maxLength !== undefined || this.pattern !== null || this.formatFn !== null;
@@ -2028,6 +2028,10 @@ const NOT_VALIDATING = new Set(['$schema', '$id', '$anchor', '$comment', '$defs'
2028
2028
  function typeMismatchRest(schema, types) {
2029
2029
  const drop = new Set(['type', 'enum', 'const', 'anyOf', 'oneOf'])
2030
2030
  for (const t of types) for (const k of (TYPE_KEYWORDS[t] || [])) drop.add(k)
2031
+ // A built-in format is written for strings only (`typeof v==='string'&&`
2032
+ // off the string path), so for a value that failed `type: string` it can
2033
+ // never fire: carried here it was dead code in every module that checks one.
2034
+ if (types.includes('string') && typeof schema.format === 'string' && FORMAT_CODEGEN[schema.format]) drop.add('format')
2031
2035
  // A non-integer number reaches the type block already (numberToo).
2032
2036
  let rest = null
2033
2037
  for (const k of Object.keys(schema)) {
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ // The rejection validateJSON() returns, and the position map behind an error's
4
+ // frame in the JSON text. Apart from lib/rejections.js so that code with no
5
+ // JSON entry point (validate() through a compiled wrapper) does not bundle it.
6
+
7
+ const { needsOrdering, sortErrorsBySchemaOrder, attachRelated, _setPositionsFor } = require('./rejections');
8
+ const { setDiagnosticSource: attachDiagnosticSource } = require('./diagnostic-source');
9
+
10
+ // The rejection validateJSON returns. Everything the text path adds over
11
+ // validate(data), the value tree enrichment needs, the position map, the
12
+ // diagnostic payload, happens on first access to `.errors`. Reading `.valid`
13
+ // touches none of it. The JSON text is held here rather than read back off the
14
+ // validator, so the result does not depend on what the instance does next.
15
+ class LazyJsonRejection {
16
+ // `parsed`, when given, is the document the inner path parsed from
17
+ // `jsonStr` and validated, after defaults, coercion and removal: reading the
18
+ // errors uses it instead of parsing the text again, which on a 5 MB
19
+ // document was a third of what reading them cost.
20
+ constructor(result, jsonStr, self, enrich, parsed) {
21
+ this.valid = false;
22
+ this._result = result;
23
+ this._jsonStr = jsonStr;
24
+ this._self = self;
25
+ this._enrich = enrich;
26
+ this._cached = null;
27
+ this._parsed = parsed;
28
+ }
29
+ toJSON() {
30
+ return { valid: false, errors: this.errors };
31
+ }
32
+ }
33
+ Object.defineProperty(LazyJsonRejection.prototype, 'errors', {
34
+ enumerable: true,
35
+ configurable: true,
36
+ get() {
37
+ if (this._cached !== null) return this._cached;
38
+ const self = this._self;
39
+ const enrich = this._enrich;
40
+ const jsonStr = this._jsonStr;
41
+ // Reading the inner errors realizes the inner lazy layer, if there was one.
42
+ const raw = this._result.errors || [];
43
+ if (!raw.length) { this._cached = raw; return raw; }
44
+
45
+ // The enrich pass plucks `received` from the value tree and the suggestion
46
+ // engine (required-typo, format hints, coercion nudges) walks it too.
47
+ let parsedData = this._parsed;
48
+ if (parsedData === undefined) {
49
+ try { parsedData = JSON.parse(jsonStr); } catch { parsedData = undefined; }
50
+ // What was checked is the document after defaults, coercion and removal,
51
+ // so `received` has to come from that, as it does for validate(). A
52
+ // default that failed its own schema has no value in the text at all.
53
+ if (parsedData !== undefined && self._preprocess) self._preprocess(parsedData);
54
+ }
55
+
56
+ // Errors the inner path already enriched carry a `received` key: enrich()
57
+ // always sets one, even when there is no value to show. `code` is not a
58
+ // safe signal, because branch-collapse attaches codes to raw errors, and
59
+ // `docUrl` is not either, because the generated error functions stamp it
60
+ // on raw errors; taking it as the signal left those errors without
61
+ // `received` or a suggestion on the text path while validate() had both.
62
+ if (!raw[0] || !('received' in raw[0])) {
63
+ // Declaration order, as validate() applies it; the text path used to
64
+ // enrich in emission order.
65
+ const ordered = needsOrdering(raw) ? sortErrorsBySchemaOrder(self._schemaObj, raw) : raw;
66
+ // The position map waits for the first read of a frame (lib/enrich-error
67
+ // defineLazyFrames); until then reading the list costs the enrichment alone.
68
+ const lazyPositions = lazyPositionsFor(self, jsonStr, ordered);
69
+ const enrichOpts = {
70
+ data: parsedData,
71
+ positions: null,
72
+ lazyPositions,
73
+ schemaPositions: self._schemaPositions,
74
+ schemaFile: self._source ? self._source.path : undefined,
75
+ };
76
+ const enriched = ordered.map((e) => enrich(e, enrichOpts));
77
+ if (enriched.length > 1) attachRelated(enriched);
78
+ attachDiagnosticSource(enriched, {
79
+ data: parsedData,
80
+ text: jsonStr,
81
+ // Read by the diagnostic renderer only, which resolves the same map.
82
+ get positions () { return lazyPositions(); },
83
+ schema: self._schemaObj,
84
+ mutatesInput: self._mutatesInput === true,
85
+ });
86
+ this._cached = enriched;
87
+ return enriched;
88
+ }
89
+
90
+ // Already enriched, so the frames are usually attached too. Only a gap in
91
+ // them is worth another walk of the document: resolving the map to discover
92
+ // there was nothing to fill cost a second full walk on every rejection.
93
+ if (raw.some((e) => e && !e.dataFrame)) {
94
+ const positions = self._pos().targeted(jsonStr, wantedPointersFor(raw.filter((e) => e && !e.dataFrame)));
95
+ if (positions) {
96
+ for (const e of raw) {
97
+ if (e && !e.dataFrame) {
98
+ const path = e.path != null ? e.path : (e.instancePath || '');
99
+ const p = positions[path];
100
+ if (p) e.dataFrame = { byteOffset: p.byteOffset, length: p.length, line: p.line, col: p.col, text: p.text };
101
+ }
102
+ }
103
+ self._posCache.reset();
104
+ }
105
+ }
106
+ if (raw.length > 1) attachRelated(raw);
107
+ attachDiagnosticSource(raw, {
108
+ data: parsedData,
109
+ text: jsonStr,
110
+ schema: self._schemaObj,
111
+ mutatesInput: self._mutatesInput === true,
112
+ });
113
+ this._cached = raw;
114
+ return raw;
115
+ },
116
+ });
117
+
118
+ // The targeted position map for a list, built on the first call and shared
119
+ // by every error's frame accessor (lib/enrich-error defineLazyFrames).
120
+ function lazyPositionsFor (self, text, errors) {
121
+ let done = false;
122
+ let positions = null;
123
+ return () => {
124
+ if (done) return positions;
125
+ done = true;
126
+ positions = self._pos().targeted(text, wantedPointersFor(errors));
127
+ if (positions) self._posCache.reset();
128
+ return positions;
129
+ };
130
+ }
131
+
132
+ // The pointers a set of errors will ask the position map about. lib/enrich-error
133
+ // looks up the error's own path, and for an additional or unevaluated property
134
+ // the child pointer named in `params`, unescaped, which is the form it asks for.
135
+ // The escaped form goes in too: including a pointer that is never read costs
136
+ // nothing, and the cache answers anything outside this set from the full map
137
+ // rather than reporting no position.
138
+ function wantedPointersFor (errors) {
139
+ const wanted = new Set();
140
+ for (const e of errors) {
141
+ if (!e) continue;
142
+ const path = e.path != null ? e.path : (e.instancePath || '');
143
+ wanted.add(path);
144
+ if (e.instancePath != null && e.instancePath !== path) wanted.add(e.instancePath);
145
+ const params = e.params;
146
+ const named = params && (params.additionalProperty || params.unevaluatedProperty);
147
+ if (typeof named === 'string') {
148
+ wanted.add(path + '/' + named);
149
+ if (named.indexOf('~') !== -1 || named.indexOf('/') !== -1) {
150
+ wanted.add(path + '/' + named.replace(/~/g, '~0').replace(/\//g, '~1'));
151
+ }
152
+ }
153
+ }
154
+ return wanted;
155
+ }
156
+
157
+ _setPositionsFor(lazyPositionsFor);
158
+
159
+ module.exports = { LazyJsonRejection };
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ // A module of its own so the browser build, which has no native engine
4
+ // (lib/native-load.browser.js), maps it to lib/native-codes.browser.js and
5
+ // carries no table for ordinals that never reach it.
6
+
7
+ const { codeFor } = require('./error-codes');
8
+
9
+ // The native engine reports its own `error_code` enum from include/ata.h as an
10
+ // ordinal. Nothing outside the addon should ever see one: an error that came
11
+ // from there carries the same public code and keyword as the same failure from
12
+ // the JavaScript engines. Ordinals are positional, so this table follows the
13
+ // enum's declaration order and `tests/test_native_error_codes.js` fails if the
14
+ // two drift apart.
15
+ const NATIVE_KEYWORDS = [
16
+ null, // 0 ok
17
+ '__parse__', // 1 invalid_json
18
+ '__compile__', // 2 invalid_schema
19
+ 'type', // 3 type_mismatch
20
+ 'required', // 4 required_property_missing
21
+ 'additionalProperties', // 5 additional_property_not_allowed
22
+ 'enum', // 6 enum_mismatch
23
+ 'const', // 7 const_mismatch
24
+ 'minimum', // 8 minimum_violation
25
+ 'maximum', // 9 maximum_violation
26
+ 'exclusiveMinimum', // 10 exclusive_minimum_violation
27
+ 'exclusiveMaximum', // 11 exclusive_maximum_violation
28
+ 'minLength', // 12 min_length_violation
29
+ 'maxLength', // 13 max_length_violation
30
+ 'pattern', // 14 pattern_mismatch
31
+ 'format', // 15 format_mismatch
32
+ 'minItems', // 16 min_items_violation
33
+ 'maxItems', // 17 max_items_violation
34
+ 'uniqueItems', // 18 unique_items_violation
35
+ 'minProperties', // 19 min_properties_violation
36
+ 'maxProperties', // 20 max_properties_violation
37
+ 'multipleOf', // 21 multiple_of_violation
38
+ 'allOf', // 22 all_of_failed
39
+ 'anyOf', // 23 any_of_failed
40
+ 'oneOf', // 24 one_of_failed
41
+ 'not', // 25 not_failed
42
+ '$ref', // 26 ref_not_found
43
+ 'if', // 27 if_then_else_failed
44
+ '__depth__', // 28 depth_exceeded
45
+ ]
46
+
47
+ // Keyword and public code for a native ordinal, or null when the ordinal is
48
+ // outside the enum, which would mean the addon is newer than this table.
49
+ function fromNative (ordinal, format) {
50
+ if (typeof ordinal !== 'number' || !Number.isInteger(ordinal)) return null
51
+ const keyword = NATIVE_KEYWORDS[ordinal]
52
+ if (!keyword) return null
53
+ // codeFor('format') answers with the first format code, which names a
54
+ // specific format. Without knowing which format failed, the generic one is
55
+ // the honest answer.
56
+ // The walker stopped at its depth limit and rejected the document. No
57
+ // public code names that: the APIs that ask the walker route schemas deep or
58
+ // recursive enough to reach it to validate() first (lib/buffer-gate.js), so
59
+ // it is the generic failure.
60
+ if (keyword === '__depth__') return { keyword, code: 'ATA9000' }
61
+ const code = keyword === 'format'
62
+ ? (format ? codeFor('format', format) || 'ATA3099' : 'ATA3099')
63
+ : codeFor(keyword) || 'ATA9001'
64
+ return { keyword, code }
65
+ }
66
+
67
+ module.exports = { fromNative, NATIVE_KEYWORDS };
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // The words for an error the native engine reported without a message (it
4
+ // reports numeric codes): the enrich pass knows them. A module of its own so
5
+ // the browser build, which has no native engine (lib/native-load.browser.js),
6
+ // maps it to lib/native-message.browser.js and leaves the enrichment out of
7
+ // bundles that only reach it from here, such as ata-validator/compiled-plain.
8
+ module.exports = (err) => require('./enrich-error').enrich(err, {}).message;
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ // Browser build of lib/native-message.js, lib/native-codes.js and
4
+ // lib/native-positions.js, one file for all three so a page carries one module
5
+ // for them: there is no native engine in a browser (lib/native-load.browser.js
6
+ // returns null), so no error arrives without a message or with a native
7
+ // ordinal, and no position is found by the addon. Kept total all the same.
8
+ const message = (err) => (err && err.message) || 'validation failed';
9
+ message.fromNative = message.nativeTargeted = () => null;
10
+ message.NATIVE_KEYWORDS = [];
11
+ module.exports = message;