ata-validator 1.60.0 → 1.61.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -6
- package/bin/ata.js +84 -0
- package/build.d.ts +29 -0
- package/build.mjs +2 -0
- package/compiled-bare-rich.d.ts +6 -0
- package/compiled-bare.d.ts +7 -0
- package/compiled-bare.js +5 -0
- package/compiled-bare.mjs +35 -0
- package/compiled-object.d.ts +11 -0
- package/compiled-object.js +6 -0
- package/compiled-object.mjs +3 -0
- package/compiled-plain.d.ts +16 -0
- package/compiled-plain.js +5 -0
- package/compiled-plain.mjs +3 -0
- package/compiled.d.ts +3 -1
- package/index.browser.mjs +2 -2
- package/index.bundle.mjs +2 -2
- package/index.d.ts +9 -0
- package/index.edge.mjs +2 -2
- package/index.js +48 -0
- package/index.mjs +1 -1
- package/index.node.mjs +1 -1
- package/lib/aot-build.js +81 -6
- package/lib/aot-impl.js +24 -21
- package/lib/bun-target.js +612 -0
- package/lib/codegen-engine.js +9 -3
- package/lib/combined-runtime.js +1 -1
- package/lib/compiled-bare-rich.js +54 -0
- package/lib/compiled-bare.js +44 -0
- package/lib/compiled-common.js +17 -2
- package/lib/compiled-defaults.browser.js +9 -0
- package/lib/compiled-defaults.js +8 -0
- package/lib/compiled-object.js +123 -0
- package/lib/compiled-plain.js +57 -0
- package/lib/compiled.js +2 -1
- package/lib/email-literal.js +29 -0
- package/lib/enrich-error.js +5 -1
- package/lib/error-codes.js +6 -76
- package/lib/formats-source.js +20 -3
- package/lib/formats.js +1 -1
- package/lib/js-compiler-errors.js +49 -10
- package/lib/js-compiler.js +92 -13
- package/lib/json-rejections.js +159 -0
- package/lib/native-codes.js +67 -0
- package/lib/native-message.js +8 -0
- package/lib/native.browser.js +11 -0
- package/lib/public-api.js +1 -0
- package/lib/rejections.js +13 -152
- package/lib/standard-schema.js +1 -1
- package/lib/tier0.js +1 -1
- package/lib/validator-core.js +46 -4
- package/lib/verify.js +386 -0
- package/lib/version.js +1 -1
- package/lite.js +1 -0
- package/lite.mjs +1 -1
- package/package.json +44 -9
|
@@ -9,7 +9,7 @@ const { compileFunction } = require('./compile-fn')
|
|
|
9
9
|
// the first call that compiles errors. Everything here was written as part
|
|
10
10
|
// of js-compiler.js and reads its helpers from that module's _internal.
|
|
11
11
|
const core = require('./js-compiler')
|
|
12
|
-
const { ownHead, hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, enumOperand, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun, keyLoopEligible, SAME_KEYS_HELPER, seqTag } = core._internal
|
|
12
|
+
const { ownHead, plainFlag, hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, enumOperand, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun, keyLoopEligible, SAME_KEYS_HELPER, seqTag } = core._internal
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
function emitPtrEsc(ctx) {
|
|
@@ -1017,7 +1017,7 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1017
1017
|
const bodyLines = []
|
|
1018
1018
|
const outerStack = ctx.refStack
|
|
1019
1019
|
ctx.refStack = new Set()
|
|
1020
|
-
try { genCodeE(ctx.rootE, 'd', '_p', bodyLines, ctx, SP_PLACEHOLDER) } finally { ctx.refStack = outerStack }
|
|
1020
|
+
try { isolatedE(ctx, () => genCodeE(ctx.rootE, 'd', '_p', bodyLines, ctx, SP_PLACEHOLDER)) } finally { ctx.refStack = outerStack }
|
|
1021
1021
|
let body = bodyLines.join('\n ')
|
|
1022
1022
|
body = body.split(`'${SP_PLACEHOLDER}`).join(`_sp+'`).split(`"${SP_PLACEHOLDER}`).join(`_sp+"`)
|
|
1023
1023
|
if (body.includes(SP_PLACEHOLDER)) throw DECLINE
|
|
@@ -1056,7 +1056,7 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1056
1056
|
// string it starts. One left anywhere else declines, rather than
|
|
1057
1057
|
// emitting a path the placeholder stands in for.
|
|
1058
1058
|
const bodyLines = []
|
|
1059
|
-
genCodeE(ctx.rootDefs[defName], 'd', '_p', bodyLines, ctx, SP_PLACEHOLDER)
|
|
1059
|
+
isolatedE(ctx, () => genCodeE(ctx.rootDefs[defName], 'd', '_p', bodyLines, ctx, SP_PLACEHOLDER))
|
|
1060
1060
|
let body = bodyLines.join('\n ')
|
|
1061
1061
|
body = body.split(`'${SP_PLACEHOLDER}`).join(`_sp+'`).split(`"${SP_PLACEHOLDER}`).join(`_sp+"`)
|
|
1062
1062
|
if (body.includes(SP_PLACEHOLDER)) throw DECLINE
|
|
@@ -1321,12 +1321,26 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1321
1321
|
// This function walks every node of a document that failed anywhere, so on a
|
|
1322
1322
|
// large array the clean items were each paying for a key array.
|
|
1323
1323
|
const gateChecks = patChecks.map((c) => c.split(`_k${ci}[_i]`).join(`_f${ci}`))
|
|
1324
|
-
|
|
1324
|
+
// The gate names the declared keys outright when there are few of them: a
|
|
1325
|
+
// Set lookup per key of every object of a document that failed somewhere
|
|
1326
|
+
// was a third of the error path on a ten-item order (31% of the samples
|
|
1327
|
+
// on that one line); three string compares are not.
|
|
1328
|
+
const declaredNames = Object.keys(schema.properties || {})
|
|
1329
|
+
const notDeclared = declaredNames.length > 0 && declaredNames.length <= 8
|
|
1330
|
+
? declaredNames.map((k) => `_f${ci}!==${JSON.stringify(k)}`).join('&&')
|
|
1331
|
+
: `!_a${ci}.has(_f${ci})`
|
|
1332
|
+
const gateTest = gateChecks.length ? `${notDeclared}&&!(${gateChecks.join('||')})` : notDeclared
|
|
1325
1333
|
const detail = `const _k${ci}=Object.keys(${v});for(let _i=0;_i<_k${ci}.length;_i++){if(${isAdditional}){_e.push({code:'${apLit.codeStr}',keyword:'additionalProperties',instancePath:${pathExpr||'""'},schemaPath:'${apSp}'${ordinalField(ctx, `${apSp}`)},params:{additionalProperty:_k${ci}[_i]},message:'must NOT have additional properties',docUrl:'${apLit.docUrl}'${apLit.frame}});if(!_all)return{valid:false,errors:_e}}}`
|
|
1326
1334
|
// The name set is built once per call of the error function, not at every
|
|
1327
1335
|
// object it checks: on a list of 1723 tokens it was 1723 Sets.
|
|
1328
1336
|
ctx.helperCode.push(`const _a${ci}=new Set([${allowed}])`)
|
|
1329
|
-
|
|
1337
|
+
// The walk reads the node's plain-object flag where one is in scope
|
|
1338
|
+
// (withPlain declared it for this value) instead of reading the prototype
|
|
1339
|
+
// again for every object: that read was a tenth of the error path.
|
|
1340
|
+
const stdHead = ownHead(ctx, v, `_f${ci}`, 'const')
|
|
1341
|
+
const pk = plainFlag(ctx, v)
|
|
1342
|
+
const head = pk ? `for(const _f${ci} in ${v})if(!${pk}&&!_hop.call(${v},_f${ci}))continue;else ` : stdHead
|
|
1343
|
+
const inner = `let _x${ci}=0;${head}{if(${gateTest}){_x${ci}=1;break}}if(_x${ci}){${detail}}`
|
|
1330
1344
|
lines.push(isObj ? `{${inner}}` : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${inner}}`)
|
|
1331
1345
|
}
|
|
1332
1346
|
|
|
@@ -1368,12 +1382,18 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
1368
1382
|
const sp = schemaPrefix+'/properties/'+ptrSeg(key)
|
|
1369
1383
|
const from = lines.length
|
|
1370
1384
|
// A value that is undefined is not checked (see the verdict generator).
|
|
1371
|
-
|
|
1385
|
+
// The value is read once into a local and the subschema checks that:
|
|
1386
|
+
// read as `${v}[key]` at every keyword below, a nested property was
|
|
1387
|
+
// loaded again for each check (a getter may answer differently, so V8
|
|
1388
|
+
// cannot merge the loads), and a rejected ten-item order cost 378 ns on
|
|
1389
|
+
// this collector against 105 on the one-pass function.
|
|
1390
|
+
const child = `_c${ctx.varCounter++}`
|
|
1391
|
+
lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){const ${child}=${v}[${JSON.stringify(key)}];if(${child}!==undefined&&${ownKeyExpr(ctx, v, key)}){`)
|
|
1372
1392
|
// A large subschema as a function of its own (emitPropFnE); false means inline.
|
|
1373
|
-
if (!propFnCandidateE(prop, ctx, sp) || !emitPropFnE(prop,
|
|
1374
|
-
genCodeE(prop,
|
|
1393
|
+
if (!propFnCandidateE(prop, ctx, sp) || !emitPropFnE(prop, child, childPath, lines, ctx, sp)) {
|
|
1394
|
+
genCodeE(prop, child, childPath, lines, ctx, sp)
|
|
1375
1395
|
}
|
|
1376
|
-
lines.push(`}`)
|
|
1396
|
+
lines.push(`}}`)
|
|
1377
1397
|
countInlineE(lines, from, ctx)
|
|
1378
1398
|
}
|
|
1379
1399
|
}
|
|
@@ -2008,6 +2028,10 @@ const NOT_VALIDATING = new Set(['$schema', '$id', '$anchor', '$comment', '$defs'
|
|
|
2008
2028
|
function typeMismatchRest(schema, types) {
|
|
2009
2029
|
const drop = new Set(['type', 'enum', 'const', 'anyOf', 'oneOf'])
|
|
2010
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')
|
|
2011
2035
|
// A non-integer number reaches the type block already (numberToo).
|
|
2012
2036
|
let rest = null
|
|
2013
2037
|
for (const k of Object.keys(schema)) {
|
|
@@ -2080,11 +2104,26 @@ function propFnCandidateE (prop, ctx, schemaPrefix) {
|
|
|
2080
2104
|
try { size = JSON.stringify(prop).length } catch { return false }
|
|
2081
2105
|
return size >= SPLIT_C_SCHEMA || ((ctx.fnBytesE || 0) > SPLIT_C_BUDGET && size >= SPLIT_C_SMALL)
|
|
2082
2106
|
}
|
|
2107
|
+
// A body that becomes a function of its own (a hoisted property, a
|
|
2108
|
+
// definition, the root through `$ref: "#"`) takes the parameter name `d` as
|
|
2109
|
+
// the node being written does; the ordinary-object flags that node declared
|
|
2110
|
+
// for its own `d` (withPlain) are out of scope in the helper and must not be
|
|
2111
|
+
// read there, as js-compiler's genIsolated keeps them out of the verdict's
|
|
2112
|
+
// helpers. Found by the SchemaStore differential: 24 schemas threw
|
|
2113
|
+
// `ReferenceError: _pk0 is not defined` from validate().
|
|
2114
|
+
function isolatedE (ctx, run) {
|
|
2115
|
+
const outerPlain = ctx.plainOf
|
|
2116
|
+
const outerReuse = ctx.plainReuse
|
|
2117
|
+
ctx.plainOf = undefined
|
|
2118
|
+
ctx.plainReuse = undefined
|
|
2119
|
+
try { return run() } finally { ctx.plainOf = outerPlain; ctx.plainReuse = outerReuse }
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2083
2122
|
function emitPropFnE (prop, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
2084
2123
|
const outerBytes = ctx.fnBytesE
|
|
2085
2124
|
ctx.fnBytesE = 0
|
|
2086
2125
|
const bodyLines = []
|
|
2087
|
-
try { genCodeE(prop, 'd', '_p', bodyLines, ctx, schemaPrefix) } finally { ctx.fnBytesE = outerBytes }
|
|
2126
|
+
try { isolatedE(ctx, () => genCodeE(prop, 'd', '_p', bodyLines, ctx, schemaPrefix)) } finally { ctx.fnBytesE = outerBytes }
|
|
2088
2127
|
if (bodyLines.length === 0) return false
|
|
2089
2128
|
const body = bodyLines.join('\n ')
|
|
2090
2129
|
if (body.includes(SP_PLACEHOLDER)) return false
|
package/lib/js-compiler.js
CHANGED
|
@@ -105,8 +105,8 @@ function apMembershipCheck (ctx, names, v) {
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
// With nothing declared every key is additional; `if()` would not parse.
|
|
108
|
-
if (names.length === 0) return
|
|
109
|
-
return
|
|
108
|
+
if (names.length === 0) return closedKeyLoop(ctx, v, `if(${ownFail(ctx, v)})return false`)
|
|
109
|
+
return closedKeyLoop(ctx, v, `if(${names.map((k) => `_k!==${JSON.stringify(k)}`).join('&&')}&&${ownFail(ctx, v)})return false`)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function emitDeq(ctx) {
|
|
@@ -946,12 +946,26 @@ const PROTO_NAMES = new Set([...Object.getOwnPropertyNames(Object.prototype), '_
|
|
|
946
946
|
// every module that asked about presence at all, about a sixth of a small
|
|
947
947
|
// schema's compiled module.
|
|
948
948
|
const OWN_HELPER_CODE = {
|
|
949
|
-
|
|
949
|
+
// Whether the plain-object test reads `__proto__` (V8: the accessor is
|
|
950
|
+
// the cheaper read) or Object.getPrototypeOf (JavaScriptCore, and Bun's
|
|
951
|
+
// ahead-of-time compiler, where the accessor is a slow path: 212 against
|
|
952
|
+
// 127 ns on a ten-item order). Decided once, where the module loads.
|
|
953
|
+
// The plain-object test reads `__proto__` on V8, where the accessor is the
|
|
954
|
+
// cheaper read, and Object.getPrototypeOf on JavaScriptCore, where the
|
|
955
|
+
// accessor is a slow path under Bun's compiler (212 against 127 ns on a
|
|
956
|
+
// ten-item order). Decided per target engine when the code is generated;
|
|
957
|
+
// the V8 form still tests the accessor at load, since Deno removes it.
|
|
958
|
+
get _PN () { return TARGET_ENGINE !== 'v8' ? 'const _PN=false;const _OP=Object.prototype;const _gp=Object.getPrototypeOf;' : 'const _PN=({}).__proto__===Object.prototype;const _OP=Object.prototype;const _gp=Object.getPrototypeOf;' },
|
|
950
959
|
_hop: 'const _hop=Object.prototype.hasOwnProperty;',
|
|
951
960
|
_h: 'function _h(o,k){return _hop.call(o,k)}',
|
|
952
961
|
_ok: 'function _ok(o,k){return (_PN?o.__proto__:_gp(o))===_OP||_hop.call(o,k)}',
|
|
953
962
|
_hall: 'function _hall(o,ks){for(let i=0;i<ks.length;i++)if(!_hop.call(o,ks[i]))return false;return true}',
|
|
954
963
|
_nk: 'function _nk(o,n){return (_PN?o.__proto__:_gp(o))===_OP||Object.keys(o).length!==n}',
|
|
964
|
+
// The own-key count of a closed object, and the walk over its keys, take
|
|
965
|
+
// the form of the engine the code is generated for (targetEngine below):
|
|
966
|
+
// for...in on V8, where it is the cheaper form, Object.keys on
|
|
967
|
+
// JavaScriptCore, where for...in is a slow path. One form is written, so
|
|
968
|
+
// neither engine pays for a branch per object.
|
|
955
969
|
}
|
|
956
970
|
const OWN_HELPER_DEPS = { _h: ['_hop'], _ok: ['_PN', '_hop'], _hall: ['_hop'], _nk: ['_PN'] }
|
|
957
971
|
function useOwnHelper (ctx, name) {
|
|
@@ -1032,7 +1046,7 @@ function withPlain (schema, v, lines, ctx, run) {
|
|
|
1032
1046
|
}
|
|
1033
1047
|
// The same test at run time, for the closure compiler.
|
|
1034
1048
|
const _hop = Object.prototype.hasOwnProperty
|
|
1035
|
-
const _PN = ({}).__proto__ === Object.prototype
|
|
1049
|
+
const _PN = typeof Bun === 'undefined' && ({}).__proto__ === Object.prototype // JavaScriptCore reads the prototype through Object.getPrototypeOf, see the _PN declaration
|
|
1036
1050
|
// One hasOwnProperty call. From 1.31.2 to 1.33.0 this first tested the name
|
|
1037
1051
|
// against Object.prototype's names, then `in`, then read the prototype through
|
|
1038
1052
|
// the __proto__ accessor, on every property of every object the eval-free
|
|
@@ -3644,7 +3658,9 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3644
3658
|
lines.push(`if(${reqChecks.join('||')})return false`)
|
|
3645
3659
|
}
|
|
3646
3660
|
// On an object with another prototype the typed ones may be inherited.
|
|
3647
|
-
|
|
3661
|
+
// Where the node's key set is proved exactly (exactKeysNode), the typed
|
|
3662
|
+
// names are own by that proof and the list is not needed.
|
|
3663
|
+
if (typed.length > 0 && !exactKeysNode(schema, ctx)) {
|
|
3648
3664
|
useOwnHelper(ctx, '_hall')
|
|
3649
3665
|
// Read directly: the object type is already established here, and a
|
|
3650
3666
|
// flag declared for this alone costs more than it saves. The list is
|
|
@@ -3677,7 +3693,7 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3677
3693
|
if (allRequired && propCount > 0) {
|
|
3678
3694
|
// Adaptive: for-in for <=15 keys (V8 fast path), Object.keys for >15
|
|
3679
3695
|
if (propCount <= 15) {
|
|
3680
|
-
lines.push(
|
|
3696
|
+
lines.push(exactKeys(ctx, v, knownKeys))
|
|
3681
3697
|
} else {
|
|
3682
3698
|
lines.push(`if(Object.keys(${v}).length!==${propCount})return false`)
|
|
3683
3699
|
}
|
|
@@ -3817,7 +3833,7 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
3817
3833
|
const allRequired = !!schema.required && declaredKeys.every((k) => req.has(k))
|
|
3818
3834
|
const inner = allRequired
|
|
3819
3835
|
? (propCount <= 15
|
|
3820
|
-
?
|
|
3836
|
+
? exactKeys(ctx, v, declaredKeys)
|
|
3821
3837
|
: `if(Object.keys(${v}).length!==${propCount})return false`)
|
|
3822
3838
|
: apMembershipCheck(ctx, Object.keys(schema.properties || {}), v)
|
|
3823
3839
|
_deferOrInline(ctx, lines, v, isObj ? inner : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${inner}}`)
|
|
@@ -4833,7 +4849,7 @@ function genUnevaluatedPropertiesNode(schema, v, lines, ctx, knownType, isObj, h
|
|
|
4833
4849
|
if (!earlyKeyCount) {
|
|
4834
4850
|
// Adaptive: for-in for <=15 keys, Object.keys for >15
|
|
4835
4851
|
inner = propCount <= 15
|
|
4836
|
-
?
|
|
4852
|
+
? exactKeys(ctx, v, knownKeys)
|
|
4837
4853
|
: `if(Object.keys(${v}).length!==${propCount})return false`
|
|
4838
4854
|
_deferOrInline(ctx, lines, v, isObj ? inner : `if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${inner}}`)
|
|
4839
4855
|
}
|
|
@@ -5647,6 +5663,69 @@ function ownFail (ctx, v, k = '_k') {
|
|
|
5647
5663
|
useOwnHelper(ctx, '_ok')
|
|
5648
5664
|
return `_ok(${v},${k})`
|
|
5649
5665
|
}
|
|
5666
|
+
// The engine the generated code is written for. The runtime compiles for the
|
|
5667
|
+
// engine it runs on; `ata build --target bun` and the plugin's Bun entry write
|
|
5668
|
+
// modules for JavaScriptCore (setTargetEngine). The two engines want opposite
|
|
5669
|
+
// forms for walking a closed object's keys: for...in is the cheaper form on
|
|
5670
|
+
// V8 and a slow path on JavaScriptCore (a ten-item order under Bun's
|
|
5671
|
+
// ahead-of-time compiler: 227 against 108 ns), and Object.keys the reverse. A
|
|
5672
|
+
// branch per object on a load-time flag cost V8 5 to 7% on small schemas, so
|
|
5673
|
+
// one form is written, chosen here.
|
|
5674
|
+
// 'jsc-aot' is JavaScriptCore under Bun's ahead-of-time compiler, where the
|
|
5675
|
+
// cheapest proof that a closed object holds exactly its declared keys is to
|
|
5676
|
+
// read Object.keys once and compare it with the declared list in order (the
|
|
5677
|
+
// order JSON.parse gives), falling back to a membership walk when the order
|
|
5678
|
+
// differs. On Bun's JIT that form lost 5 to 15 ns a ten-item order to the
|
|
5679
|
+
// plain count, and under the compiler it won 4 to 10, which brought the
|
|
5680
|
+
// module level with Bun's own validate<T>; so it is its own target, the one
|
|
5681
|
+
// `ata build --target bun` writes for.
|
|
5682
|
+
let TARGET_ENGINE = typeof Bun !== 'undefined' ? 'jsc' : 'v8'
|
|
5683
|
+
const ENGINES = ['v8', 'jsc', 'jsc-aot']
|
|
5684
|
+
function setTargetEngine (engine) {
|
|
5685
|
+
const prev = TARGET_ENGINE
|
|
5686
|
+
if (ENGINES.includes(engine)) TARGET_ENGINE = engine
|
|
5687
|
+
else if (engine === null || engine === undefined) TARGET_ENGINE = typeof Bun !== 'undefined' ? 'jsc' : 'v8'
|
|
5688
|
+
else throw new Error(`unknown target engine ${JSON.stringify(engine)}; expected "v8", "jsc" or "jsc-aot"`)
|
|
5689
|
+
return prev
|
|
5690
|
+
}
|
|
5691
|
+
function targetEngine () { return TARGET_ENGINE }
|
|
5692
|
+
// `var _n=<own key count of v>;`
|
|
5693
|
+
function keyCount (v) {
|
|
5694
|
+
return TARGET_ENGINE !== 'v8' ? `var _n=Object.keys(${v}).length;` : `var _n=0;for(var _k in ${v})_n++;`
|
|
5695
|
+
}
|
|
5696
|
+
// Whether this node's check under the compiler is the exact key list: a
|
|
5697
|
+
// closed object, no pattern properties, every declared key required, at
|
|
5698
|
+
// most fifteen of them, not one the removing verdict rewrites. The proof then
|
|
5699
|
+
// covers presence and ownership of the typed keys too.
|
|
5700
|
+
function exactKeysNode (schema, ctx) {
|
|
5701
|
+
if (TARGET_ENGINE !== 'jsc-aot') return false
|
|
5702
|
+
if (schema.additionalProperties !== false || schema.patternProperties || !schema.properties || !schema.required) return false
|
|
5703
|
+
if (ctx.removeNodes && ctx.removeNodes.has(schema)) return false
|
|
5704
|
+
const keys = Object.keys(schema.properties)
|
|
5705
|
+
if (keys.length === 0 || keys.length > 15) return false
|
|
5706
|
+
const req = new Set(schema.required)
|
|
5707
|
+
return keys.every((k) => req.has(k))
|
|
5708
|
+
}
|
|
5709
|
+
// The check that a closed object whose declared keys are all required holds
|
|
5710
|
+
// exactly those keys: the count, compared against the own-key count when it
|
|
5711
|
+
// is off (ownCountOff), or under the compiler the key list itself.
|
|
5712
|
+
function exactKeys (ctx, v, keys) {
|
|
5713
|
+
const n = keys.length
|
|
5714
|
+
// The plain-object flag is asked for only on the counting form: asking marks
|
|
5715
|
+
// it used, and the exact key list needs no prototype read at all.
|
|
5716
|
+
if (TARGET_ENGINE !== 'jsc-aot' || n === 0) return `${keyCount(v)}if(_n!==${n}&&${ownCountOff(ctx, v, n, plainFlag(ctx, v))})return false`
|
|
5717
|
+
const i = ctx.varCounter++
|
|
5718
|
+
const names = keys.map((k) => JSON.stringify(k))
|
|
5719
|
+
const inOrder = names.map((k, j) => `_ks${i}[${j}]===${k}`).join('&&')
|
|
5720
|
+
const member = names.map((k) => `_k!==${k}`).join('&&')
|
|
5721
|
+
return `{const _ks${i}=Object.keys(${v});if(_ks${i}.length!==${n})return false;if(!(${inOrder}))for(let _i=0;_i<${n};_i++){const _k=_ks${i}[_i];if(${member})return false}}`
|
|
5722
|
+
}
|
|
5723
|
+
// A loop over the keys of a closed object, `body` reading each as `_k`.
|
|
5724
|
+
function closedKeyLoop (ctx, v, body) {
|
|
5725
|
+
if (TARGET_ENGINE === 'v8') return `for(var _k in ${v}){${body}}`
|
|
5726
|
+
const i = ctx.varCounter++
|
|
5727
|
+
return `{var _ks${i}=Object.keys(${v});for(var _ki${i}=0;_ki${i}<_ks${i}.length;_ki${i}++){var _k=_ks${i}[_ki${i}];${body}}}`
|
|
5728
|
+
}
|
|
5650
5729
|
// For a key count that came out wrong: whether it is wrong counting own keys
|
|
5651
5730
|
// only. An ordinary object's count stands; any other object is counted again.
|
|
5652
5731
|
// A oneOf of closed objects fails its other branches here on every value, so
|
|
@@ -5677,10 +5756,10 @@ function ownHead (ctx, v, k = '_k', kind = 'var') {
|
|
|
5677
5756
|
// Generate a charCodeAt(0)-based switch tree for fast key validation.
|
|
5678
5757
|
// V8 compiles switch to jump tables — O(1) dispatch vs O(n) chain.
|
|
5679
5758
|
function genCharCodeSwitch(keys, v, ctx) {
|
|
5680
|
-
if (keys.length === 0) return
|
|
5759
|
+
if (keys.length === 0) return closedKeyLoop(ctx, v, `if(${ownFail(ctx, v)})return false`)
|
|
5681
5760
|
if (keys.length <= 3) {
|
|
5682
5761
|
// Small set: simple chain is faster than switch overhead
|
|
5683
|
-
return
|
|
5762
|
+
return closedKeyLoop(ctx, v, `if(${keys.map(k => `_k!==${JSON.stringify(k)}`).join('&&')}&&${ownFail(ctx, v)})return false`)
|
|
5684
5763
|
}
|
|
5685
5764
|
|
|
5686
5765
|
// Group keys by first charCode
|
|
@@ -5697,7 +5776,7 @@ function genCharCodeSwitch(keys, v, ctx) {
|
|
|
5697
5776
|
cases += `case ${cc}:if(${cond})continue;break;`
|
|
5698
5777
|
}
|
|
5699
5778
|
|
|
5700
|
-
return
|
|
5779
|
+
return closedKeyLoop(ctx, v, `switch(_k.charCodeAt(0)){${cases}default:break}if(${ownFail(ctx, v)})return false`)
|
|
5701
5780
|
}
|
|
5702
5781
|
|
|
5703
5782
|
// The verdict form of genUnevaluatedDynamicC: the same set, built the same
|
|
@@ -6036,10 +6115,10 @@ function compileToJSCodegenWithErrors (schema, schemaMap, userFormats, sourceOpt
|
|
|
6036
6115
|
function compileToJSCombined (schema, VALID_RESULT, schemaMap, userFormats, opts) { return errorsModule().compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats, opts) }
|
|
6037
6116
|
|
|
6038
6117
|
|
|
6039
|
-
module.exports = { compileToJS, compileToJSCodegen, compileToJSCodegenWithErrors, compileToJSCombined, collectEvaluated, unevalContributions, expandedChars, AJV_MESSAGES, _setCodeBudget, _helperSources: { DEQ_HELPER, PTR_ESC_HELPER, OWN_HELPER_CODE } }
|
|
6118
|
+
module.exports = { compileToJS, compileToJSCodegen, compileToJSCodegenWithErrors, compileToJSCombined, setTargetEngine, targetEngine, collectEvaluated, unevalContributions, expandedChars, AJV_MESSAGES, _setCodeBudget, _helperSources: { DEQ_HELPER, PTR_ESC_HELPER, OWN_HELPER_CODE } }
|
|
6040
6119
|
// For tests: the dynamic-scope unrolling, to hold its output to the interpreter.
|
|
6041
6120
|
Object.defineProperty(module.exports, '_expandDynamicScopes', { value: expandDynamicScopes, enumerable: false })
|
|
6042
6121
|
// For tests/test_custom_keyword_codegen.js, which holds these copies to the interpreter's.
|
|
6043
6122
|
Object.defineProperty(module.exports, '_kwTypeBits', { value: { kwTypeBit, kwDataBits, KW_T_ANY }, enumerable: false })
|
|
6044
6123
|
|
|
6045
|
-
module.exports._internal = { ownHead, hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, enumOperand, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun, keyLoopEligible, SAME_KEYS_HELPER, seqTag }
|
|
6124
|
+
module.exports._internal = { ownHead, plainFlag, hasConstraints, assertEmitted, prepareForCodegen, sharedCodegenGate, codegenSafe, isSimplePN, hasUnresolvableRef, needsBaseTracking, _cpLen, sharedRt, DECLINE, bindRt, compileSafe, emitGuardState, withPlain, REF_NEUTRAL_SIBLINGS, resolveCrossSchemaRef, enumCondition, enumOperand, emitDeq, emitConstant, ownGuard, ownKeyExpr, multipleOfBad, compilePatternInline, useSafeEngine, reFlags, FORMAT_CODEGEN, PROTO_NAMES, setOwn, nestedGenCode, compileToJSCodegen, SKIP_VALUE_KEYS, skipString, CYCLE_DEPTH, cyclicDefNames, sharedDefNames, reFlagArg, hoistOnce, PTR_ESC_HELPER, unevalContributions, emitUq, uqSources, safeReClosure, fastPrefixCheck, replaceTopLevel, refTargetFor, genCode, addLocalAnnotations, addTopApplicatorAnnotations, protoIs, ANNOT_APPLICATORS, SIZE_SKIPS, emitRootGuard, emitGuardedRun, keyLoopEligible, SAME_KEYS_HELPER, seqTag }
|
|
@@ -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;
|