ata-validator 1.46.1 → 1.48.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/README.md +33 -14
- package/bin/ata.js +9 -2
- package/build.d.ts +15 -2
- package/index.js +3 -2
- package/lib/aot-build.js +3 -1
- package/lib/aot-impl.js +178 -8
- package/lib/combined-runtime.js +1 -1
- package/lib/compile-fn.js +16 -0
- package/lib/enrich-error.js +46 -1
- package/lib/env.js +13 -0
- package/lib/interpreter.js +3 -1
- package/lib/js-compiler-errors.js +3576 -0
- package/lib/js-compiler.js +431 -3709
- package/lib/native-load.js +10 -1
- package/lib/rejections.js +27 -11
- package/lib/scan-compiler.js +16 -7
- package/lib/validator-core.js +10 -4
- package/lib/version.js +1 -1
- package/lite.d.ts +12 -0
- package/lite.js +20 -0
- package/lite.mjs +3 -0
- package/package.json +17 -8
package/lib/native-load.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const { env } = require('./env');
|
|
2
3
|
|
|
3
4
|
// Locate the optional native engine. Resolution order:
|
|
4
5
|
// 1. ATA_NO_NATIVE set -> null (explicit pure-JS mode)
|
|
@@ -36,6 +37,12 @@ function detectMusl() {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
// The require of the platform package is of a computed name on purpose. A
|
|
41
|
+
// literal require of each of the seven packages was tried: at most one of
|
|
42
|
+
// them is installed on any machine, and a bundler that bundles the Node
|
|
43
|
+
// entry (esbuild, bun, webpack) fails to resolve the other six and stops.
|
|
44
|
+
// A supply-chain scanner reports a computed require as dynamic loading;
|
|
45
|
+
// this is what it loads, and nothing else is ever required by name here.
|
|
39
46
|
let warned = false;
|
|
40
47
|
function checkVersion(binding, source) {
|
|
41
48
|
if (!binding) return null;
|
|
@@ -58,7 +65,7 @@ function checkVersion(binding, source) {
|
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
module.exports = function loadNative() {
|
|
61
|
-
if (
|
|
68
|
+
if (env('ATA_NO_NATIVE')) return null;
|
|
62
69
|
|
|
63
70
|
const isMusl = process.platform === 'linux' ? detectMusl() : false;
|
|
64
71
|
const candidates = [];
|
|
@@ -80,6 +87,8 @@ module.exports = function loadNative() {
|
|
|
80
87
|
|
|
81
88
|
// Contributors running from a source checkout.
|
|
82
89
|
try {
|
|
90
|
+
// Not a literal path for the same reason: the file is absent from the
|
|
91
|
+
// published package and a bundler must not try to resolve it.
|
|
83
92
|
const path = require('path');
|
|
84
93
|
const binding = checkVersion(
|
|
85
94
|
require(path.join(__dirname, '..', 'build', 'Release', 'ata.node')),
|
package/lib/rejections.js
CHANGED
|
@@ -84,17 +84,16 @@ class RichRejection {
|
|
|
84
84
|
// legacy key set, or enriched. Shared by both rejection classes below.
|
|
85
85
|
function presentErrors(raw, data, rawInput, self, root, enrich) {
|
|
86
86
|
if (needsOrdering(raw)) raw = sortErrorsBySchemaOrder(root, raw);
|
|
87
|
-
// The position map, resolved
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
if (positions) self._posCache.reset();
|
|
92
|
-
}
|
|
87
|
+
// The position map, resolved on the first read of an error's frame (see
|
|
88
|
+
// defineLazyFrames in lib/enrich-error.js): a walk of the document that a
|
|
89
|
+
// caller reading messages and paths never needs.
|
|
90
|
+
const lazyPositions = enrich && raw.length && rawInput != null ? lazyPositionsFor(self, rawInput, raw) : null;
|
|
93
91
|
// One options object for the whole list, not one per error.
|
|
94
92
|
const opts = enrich && raw.length
|
|
95
93
|
? {
|
|
96
94
|
data,
|
|
97
|
-
positions,
|
|
95
|
+
positions: null,
|
|
96
|
+
lazyPositions,
|
|
98
97
|
schemaPositions: self._schemaPositions,
|
|
99
98
|
schemaFile: self._source ? self._source.path : undefined,
|
|
100
99
|
}
|
|
@@ -180,13 +179,16 @@ Object.defineProperty(LazyJsonRejection.prototype, 'errors', {
|
|
|
180
179
|
// on raw errors; taking it as the signal left those errors without
|
|
181
180
|
// `received` or a suggestion on the text path while validate() had both.
|
|
182
181
|
if (!raw[0] || !('received' in raw[0])) {
|
|
183
|
-
const positions = self._pos().targeted(jsonStr, wantedPointersFor(raw));
|
|
184
182
|
// Declaration order, as validate() applies it; the text path used to
|
|
185
183
|
// enrich in emission order.
|
|
186
184
|
const ordered = needsOrdering(raw) ? sortErrorsBySchemaOrder(self._schemaObj, raw) : raw;
|
|
185
|
+
// The position map waits for the first read of a frame (lib/enrich-error
|
|
186
|
+
// defineLazyFrames); until then reading the list costs the enrichment alone.
|
|
187
|
+
const lazyPositions = lazyPositionsFor(self, jsonStr, ordered);
|
|
187
188
|
const enrichOpts = {
|
|
188
189
|
data: parsedData,
|
|
189
|
-
positions,
|
|
190
|
+
positions: null,
|
|
191
|
+
lazyPositions,
|
|
190
192
|
schemaPositions: self._schemaPositions,
|
|
191
193
|
schemaFile: self._source ? self._source.path : undefined,
|
|
192
194
|
};
|
|
@@ -195,11 +197,11 @@ Object.defineProperty(LazyJsonRejection.prototype, 'errors', {
|
|
|
195
197
|
attachDiagnosticSource(enriched, {
|
|
196
198
|
data: parsedData,
|
|
197
199
|
text: jsonStr,
|
|
198
|
-
|
|
200
|
+
// Read by the diagnostic renderer only, which resolves the same map.
|
|
201
|
+
get positions () { return lazyPositions(); },
|
|
199
202
|
schema: self._schemaObj,
|
|
200
203
|
mutatesInput: self._mutatesInput === true,
|
|
201
204
|
});
|
|
202
|
-
if (positions) self._posCache.reset();
|
|
203
205
|
this._cached = enriched;
|
|
204
206
|
return enriched;
|
|
205
207
|
}
|
|
@@ -232,6 +234,20 @@ Object.defineProperty(LazyJsonRejection.prototype, 'errors', {
|
|
|
232
234
|
},
|
|
233
235
|
});
|
|
234
236
|
|
|
237
|
+
// The targeted position map for a list, built on the first call and shared
|
|
238
|
+
// by every error's frame accessor (lib/enrich-error defineLazyFrames).
|
|
239
|
+
function lazyPositionsFor (self, text, errors) {
|
|
240
|
+
let done = false;
|
|
241
|
+
let positions = null;
|
|
242
|
+
return () => {
|
|
243
|
+
if (done) return positions;
|
|
244
|
+
done = true;
|
|
245
|
+
positions = self._pos().targeted(text, wantedPointersFor(errors));
|
|
246
|
+
if (positions) self._posCache.reset();
|
|
247
|
+
return positions;
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
235
251
|
// The pointers a set of errors will ask the position map about. lib/enrich-error
|
|
236
252
|
// looks up the error's own path, and for an additional or unevaluated property
|
|
237
253
|
// the child pointer named in `params`, unescaped, which is the form it asks for.
|
package/lib/scan-compiler.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const { compileFunction } = require('./compile-fn');
|
|
2
3
|
|
|
3
4
|
// A schema-directed scanner: it answers the verdict from the JSON text without
|
|
4
5
|
// building the document. `JSON.parse` is three quarters of the cost of a real
|
|
@@ -436,7 +437,7 @@ function finish(built, opts) {
|
|
|
436
437
|
let fn;
|
|
437
438
|
try {
|
|
438
439
|
// eslint-disable-next-line no-new-func
|
|
439
|
-
fn =
|
|
440
|
+
fn = compileFunction('_h', '_r', ctx.subSource.join('\n') + '\nreturn function scan(s){\n' + body + '\n}')(ctx.helpers, require('./scan-runtime'));
|
|
440
441
|
} catch {
|
|
441
442
|
return null; // a runtime without code generation has no scanner, by design
|
|
442
443
|
}
|
|
@@ -891,7 +892,7 @@ function emitArray(schema, ctx, out, depth) {
|
|
|
891
892
|
// Every `return 0` inside the member becomes that record, which is also how
|
|
892
893
|
// the rule reaches nested objects: an inner object's own verdict is a value
|
|
893
894
|
// failure at the level above it.
|
|
894
|
-
function emitMember(sub, ctx, out, depth, objId) {
|
|
895
|
+
function emitMember(sub, ctx, out, depth, objId, kn) {
|
|
895
896
|
let lines = [];
|
|
896
897
|
emitValue(sub, ctx, lines, depth + 1);
|
|
897
898
|
// A large member value becomes a function of its own, so no one function
|
|
@@ -920,9 +921,17 @@ function emitMember(sub, ctx, out, depth, objId) {
|
|
|
920
921
|
out.push(label + ':{');
|
|
921
922
|
out.push(body.split('return 0;').join('{_f' + objId + '=2;break ' + label + ';}'));
|
|
922
923
|
out.push('}');
|
|
923
|
-
//
|
|
924
|
-
//
|
|
925
|
-
|
|
924
|
+
// A failed member is the verdict unless this object repeats the key later,
|
|
925
|
+
// since the parser keeps the last value. Whether it can is answered by two
|
|
926
|
+
// native searches over the rest of the text, for the key as written and
|
|
927
|
+
// for any backslash, which is the only way to spell that key differently:
|
|
928
|
+
// when neither is there the scan ends here, and a 69 KB body with a bad
|
|
929
|
+
// first item is refused in the time of an indexOf rather than a full pass.
|
|
930
|
+
// Otherwise resync from where the value started, since a scan that stopped
|
|
931
|
+
// partway through one cannot say where it ends and the skipper can, and
|
|
932
|
+
// go on, so a repeat still bails to the parser.
|
|
933
|
+
out.push('if(_f' + objId + '===2){if(s.indexOf(s.slice(_p' + kn + '-1,_e' + kn + '+1),i)<0&&s.indexOf("\\\\",i)<0)return 0;');
|
|
934
|
+
out.push('_f' + objId + '=1;i=_r.skipValue(s,_vs' + label + ');if(i<0)return 0;c=s.charCodeAt(i);}');
|
|
926
935
|
}
|
|
927
936
|
|
|
928
937
|
function emitObject(schema, ctx, out, depth) {
|
|
@@ -1110,7 +1119,7 @@ function emitObject(schema, ctx, out, depth) {
|
|
|
1110
1119
|
const cond = gi === bounds.length - 1 ? '' : 'if(_kid' + kn + '<' + g.end + ')';
|
|
1111
1120
|
out.push((gi === 0 ? '' : 'else ') + cond + '{');
|
|
1112
1121
|
if (g.sub === undefined) out.push(addH);
|
|
1113
|
-
else emitMember(g.sub, ctx, out, depth, n);
|
|
1122
|
+
else emitMember(g.sub, ctx, out, depth, n, kn);
|
|
1114
1123
|
out.push('}');
|
|
1115
1124
|
prev = g.end;
|
|
1116
1125
|
});
|
|
@@ -1159,7 +1168,7 @@ function emitObject(schema, ctx, out, depth) {
|
|
|
1159
1168
|
out.push('_s' + n + '_' + slot.w + '|=' + slot.b + ';');
|
|
1160
1169
|
const sub = props && Object.prototype.hasOwnProperty.call(props, name) ? props[name] : undefined;
|
|
1161
1170
|
if (sub === undefined) out.push(additional.join('\n'));
|
|
1162
|
-
else emitMember(sub, ctx, out, depth, n);
|
|
1171
|
+
else emitMember(sub, ctx, out, depth, n, kn);
|
|
1163
1172
|
out.push('}');
|
|
1164
1173
|
}
|
|
1165
1174
|
out.push('else{' + additional.join('\n') + '}');
|
package/lib/validator-core.js
CHANGED
|
@@ -10,6 +10,8 @@ function getNative() {
|
|
|
10
10
|
if (_nativeMod === undefined) _nativeMod = require("./native-load")();
|
|
11
11
|
return _nativeMod;
|
|
12
12
|
}
|
|
13
|
+
const { env } = require('./env');
|
|
14
|
+
const { compileFunction } = require('./compile-fn');
|
|
13
15
|
const { normalizeKeywords, schemaUsesKeywords } = require('./keywords');
|
|
14
16
|
// The code generator (lib/js-compiler and what builds on it: the scanner, the
|
|
15
17
|
// parse() copy, the ahead-of-time emitters) is registered by the entry that
|
|
@@ -207,7 +209,7 @@ function codegenAvailable() {
|
|
|
207
209
|
if (_codegen === null) return false;
|
|
208
210
|
if (_codegenAvailable === null) {
|
|
209
211
|
try {
|
|
210
|
-
_codegenAvailable =
|
|
212
|
+
_codegenAvailable = compileFunction('return 1')() === 1;
|
|
211
213
|
} catch {
|
|
212
214
|
_codegenAvailable = false;
|
|
213
215
|
}
|
|
@@ -880,7 +882,7 @@ class Validator {
|
|
|
880
882
|
// Check cache first -- reuse compiled functions for same schema
|
|
881
883
|
const sm = this._schemaMap.size > 0 ? this._schemaMap : null;
|
|
882
884
|
const mapKey = compileCacheKey(this._schemaStr, this._schemaMap);
|
|
883
|
-
var _forceNapi = this._interpretOnly || (
|
|
885
|
+
var _forceNapi = this._interpretOnly || env('ATA_FORCE_NAPI');
|
|
884
886
|
// Custom formats are JS functions: bypass the compile cache since they can
|
|
885
887
|
// differ between validators that share the same schema string. An
|
|
886
888
|
// interpreter-only validator never touches it either way: a function a
|
|
@@ -1584,7 +1586,7 @@ class Validator {
|
|
|
1584
1586
|
return;
|
|
1585
1587
|
}
|
|
1586
1588
|
this._ensureVocabularies();
|
|
1587
|
-
if (this._interpretOnly || _codegen === null || (
|
|
1589
|
+
if (this._interpretOnly || _codegen === null || env('ATA_FORCE_NAPI')) return;
|
|
1588
1590
|
_codegen.compileVerdict.call(this);
|
|
1589
1591
|
}
|
|
1590
1592
|
|
|
@@ -1800,8 +1802,12 @@ Validator.loadBundle = function (mods, schemas, opts) {
|
|
|
1800
1802
|
// simdjson when the addon is there, JSON.parse otherwise; the addon loads on
|
|
1801
1803
|
// the first call rather than when the package is required.
|
|
1802
1804
|
function parseJSON(input) {
|
|
1805
|
+
// The addon has not carried parseJSON since the per-platform packages
|
|
1806
|
+
// (1.1.0); the call threw a TypeError whenever the engine was loaded and
|
|
1807
|
+
// returned JSON.parse's answer otherwise. A Buffer is decoded first.
|
|
1803
1808
|
const native = getNative();
|
|
1804
|
-
|
|
1809
|
+
if (native && typeof native.parseJSON === 'function') return native.parseJSON(input);
|
|
1810
|
+
return JSON.parse(typeof input === 'string' ? input : String(input));
|
|
1805
1811
|
}
|
|
1806
1812
|
|
|
1807
1813
|
// Ultra-fast compile: returns validate function directly, no Validator wrapper
|
package/lib/version.js
CHANGED
package/lite.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// ata-validator/lite: the same Validator and one-shot functions as
|
|
2
|
+
// ata-validator, on the interpreted engine and without the code generator.
|
|
3
|
+
// Validator#parse and the static bundle methods throw a TypeError here.
|
|
4
|
+
export {
|
|
5
|
+
Validator,
|
|
6
|
+
validate,
|
|
7
|
+
validateAsync,
|
|
8
|
+
parseAsync,
|
|
9
|
+
defineSchema,
|
|
10
|
+
version,
|
|
11
|
+
} from './index.js';
|
|
12
|
+
export type * from './index.js';
|
package/lite.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ata-validator/lite: the Validator without the code generator. Every schema
|
|
4
|
+
// runs on the interpreted engine, which passes the same official test suite as
|
|
5
|
+
// the compiled path and never calls `new Function`, so it also runs under a
|
|
6
|
+
// strict Content-Security-Policy. The results are the ones `ata-validator`
|
|
7
|
+
// gives: the same verdicts, the same errors. What it leaves out is what needs
|
|
8
|
+
// the code generator (parse(), the ahead-of-time bundle methods) and the tools
|
|
9
|
+
// no Validator calls (TypeScript generation, the renderers). Made for pages
|
|
10
|
+
// where the size of what ships matters more than nanoseconds per call.
|
|
11
|
+
const core = require('./lib/validator-core.js');
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
Validator: core.Validator,
|
|
15
|
+
validate: core.validate,
|
|
16
|
+
validateAsync: core.validateAsync,
|
|
17
|
+
parseAsync: core.parseAsync,
|
|
18
|
+
defineSchema: core.defineSchema,
|
|
19
|
+
version: core.version,
|
|
20
|
+
};
|
package/lite.mjs
ADDED
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0",
|
|
4
4
|
"description": "JSON Schema validator that compiles to plain JavaScript, still runs where code generation is blocked, and can be compiled away at build time. Passes the full official test suite.",
|
|
5
|
+
"type": "commonjs",
|
|
5
6
|
"main": "index.js",
|
|
6
7
|
"module": "index.mjs",
|
|
7
8
|
"types": "index.d.ts",
|
|
@@ -48,6 +49,11 @@
|
|
|
48
49
|
"import": "./compiled-verdict.mjs",
|
|
49
50
|
"require": "./compiled-verdict.js"
|
|
50
51
|
},
|
|
52
|
+
"./lite": {
|
|
53
|
+
"types": "./lite.d.ts",
|
|
54
|
+
"import": "./lite.mjs",
|
|
55
|
+
"require": "./lite.js"
|
|
56
|
+
},
|
|
51
57
|
"./package.json": "./package.json"
|
|
52
58
|
},
|
|
53
59
|
"sideEffects": false,
|
|
@@ -138,18 +144,21 @@
|
|
|
138
144
|
"compiled-verdict.js",
|
|
139
145
|
"compiled-verdict.mjs",
|
|
140
146
|
"compiled-verdict.d.ts",
|
|
147
|
+
"lite.js",
|
|
148
|
+
"lite.mjs",
|
|
149
|
+
"lite.d.ts",
|
|
141
150
|
"bin/",
|
|
142
151
|
"README.md",
|
|
143
152
|
"LICENSE"
|
|
144
153
|
],
|
|
145
154
|
"optionalDependencies": {
|
|
146
|
-
"@ata-validator/native-darwin-arm64": "1.
|
|
147
|
-
"@ata-validator/native-darwin-x64": "1.
|
|
148
|
-
"@ata-validator/native-linux-arm64-gnu": "1.
|
|
149
|
-
"@ata-validator/native-linux-arm64-musl": "1.
|
|
150
|
-
"@ata-validator/native-linux-x64-gnu": "1.
|
|
151
|
-
"@ata-validator/native-linux-x64-musl": "1.
|
|
152
|
-
"@ata-validator/native-win32-x64": "1.
|
|
155
|
+
"@ata-validator/native-darwin-arm64": "1.48.0",
|
|
156
|
+
"@ata-validator/native-darwin-x64": "1.48.0",
|
|
157
|
+
"@ata-validator/native-linux-arm64-gnu": "1.48.0",
|
|
158
|
+
"@ata-validator/native-linux-arm64-musl": "1.48.0",
|
|
159
|
+
"@ata-validator/native-linux-x64-gnu": "1.48.0",
|
|
160
|
+
"@ata-validator/native-linux-x64-musl": "1.48.0",
|
|
161
|
+
"@ata-validator/native-win32-x64": "1.48.0"
|
|
153
162
|
},
|
|
154
163
|
"peerDependencies": {
|
|
155
164
|
"yaml": "^2.0.0"
|