ata-validator 1.41.0 → 1.43.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/index.js +124 -2
- package/lib/branch-collapse.js +32 -17
- package/lib/enrich-error.js +46 -30
- package/lib/enrich-site.js +58 -0
- package/lib/interpreter.js +6 -0
- package/lib/js-compiler.js +1391 -171
- package/lib/plan-compiler.js +7 -2
- package/lib/rejections.js +20 -3
- package/lib/scan-compiler.js +210 -25
- package/lib/scan-runtime.js +13 -1
- package/lib/validator-core.js +122 -14
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/index.js
CHANGED
|
@@ -35,6 +35,65 @@ class TextRejection {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Errors built enriched where they fail by the rich combined function (see
|
|
39
|
+
// makeRich in lib/enrich-site.js), for a validator whose errors are being read
|
|
40
|
+
// with richErrors on. Presenting them is the ordering and the `related` links
|
|
41
|
+
// presentErrors applies after enriching. Where the rich function does not
|
|
42
|
+
// reject (the data changed under the read), the plain fallback is presented
|
|
43
|
+
// the usual way.
|
|
44
|
+
const { needsOrdering: _needsOrdering, sortErrorsBySchemaOrder: _sortErrors, attachRelated: _attachRelated } = require('./lib/rejections');
|
|
45
|
+
let _enrichFnIdx = null;
|
|
46
|
+
function _enrichOne(e, data) {
|
|
47
|
+
if (_enrichFnIdx === null) _enrichFnIdx = require('./lib/enrich-error').enrich;
|
|
48
|
+
return _enrichFnIdx(e, data !== undefined ? { data } : null);
|
|
49
|
+
}
|
|
50
|
+
function richBuilder(combRich, root, presentFallback, fallback, plain, cleanPlain) {
|
|
51
|
+
if (plain) {
|
|
52
|
+
// The runtime-shape combined function writes each error fresh and in the
|
|
53
|
+
// plain shape: no shared literal, no ordinal, no code or docUrl, no
|
|
54
|
+
// collapsed branches (lib/js-compiler.js, compileToJSCombined with
|
|
55
|
+
// runtimeShape). So without enrichment the list is the caller's as it is,
|
|
56
|
+
// once in schema order. Checking each error for those fields instead cost
|
|
57
|
+
// a megamorphic load per field per error across many schemas.
|
|
58
|
+
// tests/test_error_read_paths.js holds every error handed out to being
|
|
59
|
+
// the caller's own.
|
|
60
|
+
return (data) => {
|
|
61
|
+
const r = combRich(data);
|
|
62
|
+
if (r.valid || !r.errors || !r.errors.length) return presentFallback(fallback, data);
|
|
63
|
+
if (cleanPlain) return _needsOrdering(r.errors) ? _sortErrors(root, r.errors) : r.errors;
|
|
64
|
+
return presentFallback(r.errors, data);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return (data) => {
|
|
68
|
+
const r = combRich(data);
|
|
69
|
+
if (r.valid || !r.errors || !r.errors.length) return presentFallback(fallback, data);
|
|
70
|
+
const out = _needsOrdering(r.errors) ? _sortErrors(root, r.errors) : r.errors;
|
|
71
|
+
// A collapsed oneOf or anyOf comes out plain (no docUrl) and is enriched
|
|
72
|
+
// here, its branch errors with it, as presentErrors would.
|
|
73
|
+
for (let i = 0; i < out.length; i++) {
|
|
74
|
+
if (out[i].docUrl === undefined) out[i] = _enrichOne(out[i], data);
|
|
75
|
+
}
|
|
76
|
+
if (out.length > 1) _attachRelated(out);
|
|
77
|
+
return out;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// The rejection validate() returns once it is the one-pass combined function
|
|
82
|
+
// (resultShape in compileToJSCombined): the errors are already built, plain,
|
|
83
|
+
// fresh and in schema order. Serialises as the lazy rejection does.
|
|
84
|
+
class EagerRejection {
|
|
85
|
+
constructor(errors) {
|
|
86
|
+
this.valid = false;
|
|
87
|
+
this.errors = errors;
|
|
88
|
+
}
|
|
89
|
+
toJSON() {
|
|
90
|
+
return { valid: false, errors: this.errors };
|
|
91
|
+
}
|
|
92
|
+
_ataRaw() {
|
|
93
|
+
return this.errors;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
38
97
|
function installCodegenPaths (ctx) {
|
|
39
98
|
const { ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindVerdict, _jsonSyntaxRejection, _mustReject, _verboseWrap, getNative, isV1Dialect, resolveSchemaByPath } = core._internals;
|
|
40
99
|
const { jsFn, _isCodegen, preprocess, fusedRemove, options, schemaObj, useSimdjsonForLarge, _buildCombined, _buildErr } = ctx;
|
|
@@ -131,6 +190,36 @@ function installCodegenPaths (ctx) {
|
|
|
131
190
|
return _safeCombined;
|
|
132
191
|
};
|
|
133
192
|
|
|
193
|
+
// The error function on its own, probed the way errOnly probes it, or null.
|
|
194
|
+
const errFnIfSafe = () => {
|
|
195
|
+
_buildErr();
|
|
196
|
+
const efn = ctx.err();
|
|
197
|
+
if (!efn) return null;
|
|
198
|
+
try { efn({}, true); } catch { return null; }
|
|
199
|
+
return (x) => efn(x, true);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// The rich combined function, built and probed the way the plain one is,
|
|
203
|
+
// and only where the plain one passed its probe.
|
|
204
|
+
let _richProbed = false;
|
|
205
|
+
let _safeRich = null;
|
|
206
|
+
const richIfSafe = () => {
|
|
207
|
+
if (_richProbed) return _safeRich;
|
|
208
|
+
_richProbed = true;
|
|
209
|
+
if (!combinedIfSafe() || !ctx.buildRich) return null;
|
|
210
|
+
const fn = ctx.buildRich();
|
|
211
|
+
if (fn) {
|
|
212
|
+
try {
|
|
213
|
+
const probe = {};
|
|
214
|
+
if (schemaObj && schemaObj.properties) for (const k of Object.keys(schemaObj.properties)) probe[k] = "";
|
|
215
|
+
if (schemaObj && schemaObj.if && schemaObj.if.properties) for (const k of Object.keys(schemaObj.if.properties)) probe[k] = "";
|
|
216
|
+
fn(probe); fn({}); fn(null); fn(0);
|
|
217
|
+
_safeRich = fn;
|
|
218
|
+
} catch {}
|
|
219
|
+
}
|
|
220
|
+
return _safeRich;
|
|
221
|
+
};
|
|
222
|
+
|
|
134
223
|
// What the hybrid path hands to its error slot: the combined function
|
|
135
224
|
// when it is usable, since it validates and collects in one pass, and
|
|
136
225
|
// the error generator otherwise. Same order the eager code chose, just
|
|
@@ -227,6 +316,38 @@ function installCodegenPaths (ctx) {
|
|
|
227
316
|
// and used to call validate() to get the errors, which ran the verdict a
|
|
228
317
|
// second time and built a second rejection around it.
|
|
229
318
|
if (!preprocess) ctx.rejectBase = onReject;
|
|
319
|
+
// The rich combined function, for a validator whose errors are being read
|
|
320
|
+
// with richErrors on. See _ensureCompiled.
|
|
321
|
+
if (!preprocess) {
|
|
322
|
+
ctx.richBuilder = richBuilder;
|
|
323
|
+
// The plain combined function stands in where no rich one is built
|
|
324
|
+
// (richErrors off, or a schema source to frame errors from): its errors
|
|
325
|
+
// are presented, and enriched when asked, the usual way, without the
|
|
326
|
+
// rejection layers between.
|
|
327
|
+
// Where the plain combined function declines (oneOf, anyOf, an
|
|
328
|
+
// additionalProperties schema), the error function stands in, called
|
|
329
|
+
// directly rather than through the rejection layers. Its errors can be
|
|
330
|
+
// collapsed branches that still need presenting; only the combined
|
|
331
|
+
// function's are already in their final plain shape (isCleanShape).
|
|
332
|
+
ctx.oneShotRich = ctx.buildRich ? richIfSafe : () => combinedIfSafe() || errFnIfSafe();
|
|
333
|
+
ctx.oneShotPlain = !ctx.buildRich;
|
|
334
|
+
ctx.isCleanShape = (fn) => fn !== null && fn === _safeCombined;
|
|
335
|
+
ctx.isCombined = (fn) => fn !== null && fn === _safeCombined;
|
|
336
|
+
// validate() as the combined function in its result shape: built and
|
|
337
|
+
// probed here, null when it declines or throws on the probe.
|
|
338
|
+
ctx.onePassOf = (empty, fallback) => {
|
|
339
|
+
// A collapsed branch error is built in its final plain shape here
|
|
340
|
+
// (no ordering key in the runtime shape); its branch errors are put
|
|
341
|
+
// in order with the rest.
|
|
342
|
+
const sort = (errs) => (_needsOrdering(errs) ? _sortErrors(schemaObj, errs) : errs);
|
|
343
|
+
try {
|
|
344
|
+
const fn = jsCompiler.compileToJSCombined(schemaObj, VALID_RESULT, this._schemaMap.size > 0 ? this._schemaMap : null, this._userFormats, { runtimeShape: true, resultShape: { Rejection: EagerRejection, empty, sort, fallback, verdict: jsFn } });
|
|
345
|
+
if (!fn) return null;
|
|
346
|
+
fn({}); fn(null); fn(0);
|
|
347
|
+
return fn;
|
|
348
|
+
} catch { return null; }
|
|
349
|
+
};
|
|
350
|
+
}
|
|
230
351
|
} else {
|
|
231
352
|
// No hybrid factory, so the assembly needs the function itself rather
|
|
232
353
|
// than a reference it can call later: build it now.
|
|
@@ -256,6 +377,7 @@ function installCodegenPaths (ctx) {
|
|
|
256
377
|
// The verbose fields are added here, so a path around this layer would
|
|
257
378
|
// miss them.
|
|
258
379
|
ctx.rejectBase = null;
|
|
380
|
+
ctx.oneShotRich = null;
|
|
259
381
|
this.validate = _verboseWrap(this.validate, this._schemaObj);
|
|
260
382
|
}
|
|
261
383
|
// The verdict methods answer validate()'s question without building the
|
|
@@ -381,7 +503,7 @@ function installCodegenPaths (ctx) {
|
|
|
381
503
|
// The verdict function alone, for isValidObject() before a full compile: the
|
|
382
504
|
// error and combined generators are left for the first rejection.
|
|
383
505
|
installCodegenPaths.compileVerdict = function compileVerdict () {
|
|
384
|
-
const { compileCacheKey, _compileCache, _bindVerdict, _rememberInstance } = core._internals;
|
|
506
|
+
const { compileCacheKey, _compileCache, _compileCacheSet, _bindVerdict, _rememberInstance } = core._internals;
|
|
385
507
|
if (!this._schemaStr) this._schemaStr = JSON.stringify(this._schemaObj);
|
|
386
508
|
const sm = this._schemaMap.size > 0 ? this._schemaMap : null;
|
|
387
509
|
const mapKey = compileCacheKey(this._schemaStr, this._schemaMap);
|
|
@@ -410,7 +532,7 @@ installCodegenPaths.compileVerdict = function compileVerdict () {
|
|
|
410
532
|
// validator that later reuses this entry reports the same engine it
|
|
411
533
|
// would have compiled to.
|
|
412
534
|
if (!uf && !this._keywords) {
|
|
413
|
-
if (!cached)
|
|
535
|
+
if (!cached) _compileCacheSet(mapKey, { jsFn, combined: undefined, errFn: undefined, isCodegen: !!_cg, full: false });
|
|
414
536
|
else cached.jsFn = jsFn;
|
|
415
537
|
}
|
|
416
538
|
}
|
package/lib/branch-collapse.js
CHANGED
|
@@ -40,25 +40,35 @@ function __ataScore (errs) {
|
|
|
40
40
|
return errs.length * 100 + s; // primary: count, secondary: severity
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// `br` is one entry per branch in declaration order: {valid, errors
|
|
43
|
+
// `br` is one entry per branch in declaration order: {valid, errors} and a
|
|
44
|
+
// `title`, or the titles in `titles` when given (the generated code passes
|
|
45
|
+
// each branch's result as it is rather than copying it to add one).
|
|
44
46
|
// Returns the single error to report, or null when the keyword is satisfied.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
// oneOf passed by more than one branch; generated code builds it from a count.
|
|
48
|
+
function __ataMulti (passIdx, total, pp, sp, o) {
|
|
49
|
+
const multi = {
|
|
50
|
+
code: 'ATA4002',
|
|
51
|
+
keyword: 'oneOf',
|
|
52
|
+
instancePath: pp || '',
|
|
53
|
+
path: pp || '',
|
|
54
|
+
schemaPath: sp,
|
|
55
|
+
message: 'value matched ' + passIdx.length + ' of ' + total + ' oneOf variants, expected exactly one',
|
|
56
|
+
params: { passingSchemas: passIdx },
|
|
57
|
+
};
|
|
58
|
+
if (o !== null) Object.defineProperty(multi, '_o', { value: o });
|
|
59
|
+
return multi;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function __ataCollapse (kw, br, pp, sp, o, titles) {
|
|
63
|
+
let passN = 0;
|
|
64
|
+
for (let i = 0; i < br.length; i++) if (br[i].valid) passN++;
|
|
65
|
+
const pass = { length: passN };
|
|
47
66
|
if (kw === 'oneOf') {
|
|
48
67
|
if (pass.length === 1) return null;
|
|
49
68
|
if (pass.length > 1) {
|
|
50
69
|
const passIdx = [];
|
|
51
70
|
for (let i = 0; i < br.length; i++) if (br[i].valid) passIdx.push(i);
|
|
52
|
-
return
|
|
53
|
-
code: 'ATA4002',
|
|
54
|
-
keyword: 'oneOf',
|
|
55
|
-
instancePath: pp || '',
|
|
56
|
-
path: pp || '',
|
|
57
|
-
schemaPath: sp,
|
|
58
|
-
_o: o,
|
|
59
|
-
message: 'value matched ' + pass.length + ' of ' + br.length + ' oneOf variants, expected exactly one',
|
|
60
|
-
params: { passingSchemas: passIdx },
|
|
61
|
-
};
|
|
71
|
+
return __ataMulti(passIdx, br.length, pp, sp, o);
|
|
62
72
|
}
|
|
63
73
|
} else if (pass.length >= 1) {
|
|
64
74
|
return null;
|
|
@@ -70,17 +80,21 @@ function __ataCollapse (kw, br, pp, sp, o) {
|
|
|
70
80
|
if (s < bs) { bs = s; bi = i; }
|
|
71
81
|
}
|
|
72
82
|
const best = br[bi];
|
|
73
|
-
|
|
83
|
+
// The ordering key, when there is one, is not enumerable: the error is
|
|
84
|
+
// otherwise in the shape it is handed out in, and a caller that reads it
|
|
85
|
+
// as it comes (the combined function's one-pass result) does not copy it.
|
|
86
|
+
const none = {
|
|
74
87
|
code: kw === 'oneOf' ? 'ATA4001' : 'ATA4003',
|
|
75
88
|
keyword: kw,
|
|
76
89
|
instancePath: pp || '',
|
|
77
90
|
path: pp || '',
|
|
78
91
|
schemaPath: sp,
|
|
79
|
-
_o: o,
|
|
80
92
|
message: 'value matched 0 of ' + br.length + ' ' + kw + ' variants',
|
|
81
|
-
params: { variants: br.length, closest: bi, closestName: best.title || ('variant ' + (bi + 1)) },
|
|
93
|
+
params: { variants: br.length, closest: bi, closestName: (titles !== undefined ? titles[bi] : best.title) || ('variant ' + (bi + 1)) },
|
|
82
94
|
branchErrors: best.errors,
|
|
83
95
|
};
|
|
96
|
+
if (o !== null) Object.defineProperty(none, '_o', { value: o });
|
|
97
|
+
return none;
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
// The named-argument form the interpreter and the tests call.
|
|
@@ -88,12 +102,13 @@ function collapseBranches ({ keyword, branchResults, parentPath, parentSchemaPat
|
|
|
88
102
|
return __ataCollapse(keyword, branchResults, parentPath, parentSchemaPath, ordinal === undefined ? null : ordinal);
|
|
89
103
|
}
|
|
90
104
|
|
|
91
|
-
// The
|
|
105
|
+
// The four declarations as source, for the generated code to carry. Derived
|
|
92
106
|
// from the functions above rather than written out again, so a fix to one
|
|
93
107
|
// cannot miss the other. `tests/test_branch_collapse.js` runs both.
|
|
94
108
|
function embedSource () {
|
|
95
109
|
return 'const __ATA_SEVERITY=' + JSON.stringify(__ATA_SEVERITY) + ';' +
|
|
96
110
|
'const __ataScore=' + __ataScore.toString() + ';' +
|
|
111
|
+
'const __ataMulti=' + __ataMulti.toString() + ';' +
|
|
97
112
|
'const __ataCollapse=' + __ataCollapse.toString() + ';';
|
|
98
113
|
}
|
|
99
114
|
|
package/lib/enrich-error.js
CHANGED
|
@@ -185,40 +185,56 @@ function rankFor (keyword) {
|
|
|
185
185
|
function enrich (rawErr, opts) {
|
|
186
186
|
const data = opts && opts.data;
|
|
187
187
|
const positions = opts && opts.positions;
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
'
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
188
|
+
// A literal the generated code shares between calls carries a slot for the
|
|
189
|
+
// fields that depend on nothing but the literal: worked out on the first
|
|
190
|
+
// read, taken from the slot after that.
|
|
191
|
+
let st = rawErr._t;
|
|
192
|
+
if (st === undefined || st.code === undefined) {
|
|
193
|
+
const format = rawErr.params && rawErr.params.format;
|
|
194
|
+
// The native engine reports its own enum as a number. Translate it to the
|
|
195
|
+
// keyword and public code the JavaScript engines use, so an error means the
|
|
196
|
+
// same thing whichever engine produced it.
|
|
197
|
+
const fromAddon = typeof rawErr.code === 'number' ? fromNative(rawErr.code, format) : null;
|
|
198
|
+
const keyword = rawErr.keyword || (fromAddon && fromAddon.keyword);
|
|
199
|
+
// Prefer a code the codegen already attached (e.g. branch-collapse emits
|
|
200
|
+
// ATA4001/4002/4003 distinguishing zero/multi/anyOf failure modes). The
|
|
201
|
+
// keyword-derived lookup only finds the first match for `keyword: 'oneOf'`.
|
|
202
|
+
// A property that dependentRequired asks for is reported with the keyword
|
|
203
|
+
// `required`, and its documented code is ATA7005, not required's ATA7001.
|
|
204
|
+
// The error generator stamped 7005 and the other engines left it to the
|
|
205
|
+
// keyword here, so the same failure carried either code depending on which
|
|
206
|
+
// generator answered.
|
|
207
|
+
const dependent = keyword === 'required' && typeof rawErr.schemaPath === 'string' && rawErr.schemaPath.endsWith('/dependentRequired');
|
|
208
|
+
const code = (fromAddon && fromAddon.code) ||
|
|
209
|
+
(typeof rawErr.code === 'string' && rawErr.code) ||
|
|
210
|
+
(dependent && 'ATA7005') ||
|
|
211
|
+
codeFor(keyword, format) ||
|
|
212
|
+
'ATA9001';
|
|
213
|
+
const meta = CODES[code];
|
|
214
|
+
const fixed = {
|
|
215
|
+
code,
|
|
216
|
+
message: rawErr.message || (meta && meta.headline) || 'validation failed',
|
|
217
|
+
keyword,
|
|
218
|
+
path: rawErr.instancePath != null ? rawErr.instancePath : (rawErr.path || ''),
|
|
219
|
+
expected: expectedFor(rawErr),
|
|
220
|
+
docUrl: DOC_BASE + code,
|
|
221
|
+
rank: rankFor(keyword),
|
|
222
|
+
};
|
|
223
|
+
if (st !== undefined && !fromAddon) Object.assign(st, fixed);
|
|
224
|
+
st = fixed;
|
|
225
|
+
}
|
|
226
|
+
const path = st.path;
|
|
211
227
|
const at = data !== undefined ? resolveAt(rawErr, data) : MISSING;
|
|
212
228
|
|
|
213
229
|
const out = {
|
|
214
|
-
code,
|
|
215
|
-
message:
|
|
216
|
-
keyword,
|
|
230
|
+
code: st.code,
|
|
231
|
+
message: st.message,
|
|
232
|
+
keyword: st.keyword,
|
|
217
233
|
path,
|
|
218
|
-
expected:
|
|
234
|
+
expected: st.expected,
|
|
219
235
|
received: at === MISSING ? undefined : reprValue(at),
|
|
220
236
|
schemaPath: rawErr.schemaPath,
|
|
221
|
-
docUrl:
|
|
237
|
+
docUrl: st.docUrl,
|
|
222
238
|
// Back-compat aliases (additive, present in both rich and legacy paths)
|
|
223
239
|
instancePath: path,
|
|
224
240
|
dataPath: path,
|
|
@@ -264,7 +280,7 @@ function enrich (rawErr, opts) {
|
|
|
264
280
|
|
|
265
281
|
const detail = detailFor(rawErr, out);
|
|
266
282
|
if (detail !== undefined) out.detail = detail;
|
|
267
|
-
out.rank =
|
|
283
|
+
out.rank = st.rank;
|
|
268
284
|
|
|
269
285
|
// Token-level anchor. `dataFrame` already carries the value span; `anchor`
|
|
270
286
|
// adds the key span so a caret can sit on the property that is wrong rather
|
|
@@ -287,4 +303,4 @@ function enrich (rawErr, opts) {
|
|
|
287
303
|
return out;
|
|
288
304
|
}
|
|
289
305
|
|
|
290
|
-
module.exports = { enrich, reprValue, expectedFor };
|
|
306
|
+
module.exports = { enrich, reprValue, expectedFor, detailFor, rankFor, MISSING, DOC_BASE };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { CODES, codeFor } = require('./error-codes');
|
|
4
|
+
const { suggestFor } = require('./suggestions');
|
|
5
|
+
const { reprValue, expectedFor, detailFor, rankFor, MISSING, DOC_BASE } = require('./enrich-error');
|
|
6
|
+
|
|
7
|
+
// Enrichment done where the error happens. The combined generator, once a
|
|
8
|
+
// validator's errors are being read, calls `makeRich` at each failure with a
|
|
9
|
+
// site record built at compile time by `errorSite` and the value it was
|
|
10
|
+
// checking, and gets the same object `enrich` would build from the raw error:
|
|
11
|
+
// same keys, same order, same values. What depends only on the site (code,
|
|
12
|
+
// docUrl, rank, and `expected` when the params are fixed) is worked out once,
|
|
13
|
+
// and the value comes from the generated code instead of a pointer walk. It is
|
|
14
|
+
// for validate(data) without positions or a schema source; those reads keep
|
|
15
|
+
// `enrich`. tests/test_rich_errors_generated.js holds the two to the same
|
|
16
|
+
// output. A module of its own because only the code generator reaches it, and
|
|
17
|
+
// ata-validator/lite, which enriches through `enrich`, should not carry it.
|
|
18
|
+
const NO_EXPECTED = {};
|
|
19
|
+
function errorSite (keyword, schemaPath, params, format) {
|
|
20
|
+
const dependent = keyword === 'required' && typeof schemaPath === 'string' && schemaPath.endsWith('/dependentRequired');
|
|
21
|
+
const code = (dependent && 'ATA7005') || codeFor(keyword, format) || 'ATA9001';
|
|
22
|
+
return {
|
|
23
|
+
code,
|
|
24
|
+
keyword,
|
|
25
|
+
schemaPath,
|
|
26
|
+
docUrl: DOC_BASE + code,
|
|
27
|
+
rank: rankFor(keyword),
|
|
28
|
+
headline: (CODES[code] && CODES[code].headline) || 'validation failed',
|
|
29
|
+
expected: params === undefined ? NO_EXPECTED : expectedFor({ keyword, params }),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function makeRich (site, path, params, message, value, root, spPrefix) {
|
|
34
|
+
// As resolveAt: a root that is null, undefined, '' or NaN resolves nothing.
|
|
35
|
+
const at = (!root && root !== 0 && root !== false) ? MISSING : value;
|
|
36
|
+
const out = {
|
|
37
|
+
code: site.code,
|
|
38
|
+
message: message || site.headline,
|
|
39
|
+
keyword: site.keyword,
|
|
40
|
+
path,
|
|
41
|
+
expected: site.expected === NO_EXPECTED ? expectedFor({ keyword: site.keyword, params }) : site.expected,
|
|
42
|
+
received: at === MISSING ? undefined : reprValue(at),
|
|
43
|
+
schemaPath: spPrefix === undefined ? site.schemaPath : spPrefix + site.schemaPath,
|
|
44
|
+
docUrl: site.docUrl,
|
|
45
|
+
instancePath: path,
|
|
46
|
+
dataPath: path,
|
|
47
|
+
params,
|
|
48
|
+
parentSchema: undefined,
|
|
49
|
+
};
|
|
50
|
+
const sugg = suggestFor(out, root, at === MISSING ? undefined : at);
|
|
51
|
+
if (sugg) out.suggestion = sugg;
|
|
52
|
+
const detail = detailFor({ keyword: site.keyword, params }, out);
|
|
53
|
+
if (detail !== undefined) out.detail = detail;
|
|
54
|
+
out.rank = site.rank;
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { errorSite, makeRich };
|
package/lib/interpreter.js
CHANGED
|
@@ -1255,6 +1255,12 @@ function createInterpreter(schema, options) {
|
|
|
1255
1255
|
}
|
|
1256
1256
|
|
|
1257
1257
|
module.exports = { createInterpreter };
|
|
1258
|
+
// The resolver, for the code generator: it resolves references at compile
|
|
1259
|
+
// time the way this engine does at run time (normalizeRefs in js-compiler).
|
|
1260
|
+
Object.defineProperty(module.exports, '_refInternals', {
|
|
1261
|
+
value: { indexSchemas, resolveRef, FALLBACK_BASE },
|
|
1262
|
+
enumerable: false,
|
|
1263
|
+
});
|
|
1258
1264
|
// For lib/plan-source.js, which emits source from the same plans and must call
|
|
1259
1265
|
// the same helpers so the two cannot answer differently.
|
|
1260
1266
|
Object.defineProperty(module.exports, '_planInternals', {
|