ata-validator 1.30.2 → 1.31.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 +379 -66
- package/lib/formats.js +36 -4
- package/lib/js-compiler.js +61 -7
- package/lib/t.js +38 -18
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/index.js
CHANGED
|
@@ -694,17 +694,31 @@ function sortErrorsBySchemaOrder(rootSchema, errors) {
|
|
|
694
694
|
|
|
695
695
|
// Paths repeat across rejections of the same shape, so the parsed segment
|
|
696
696
|
// list is cached per path string. Entries are frozen: the same array is
|
|
697
|
-
// handed to every issue that names the path.
|
|
698
|
-
//
|
|
699
|
-
|
|
700
|
-
|
|
697
|
+
// handed to every issue that names the path.
|
|
698
|
+
//
|
|
699
|
+
// The cache is bucketed by length and searched with ===, not keyed in a Map.
|
|
700
|
+
// A path with an array index in it is concatenated afresh by every rejection,
|
|
701
|
+
// and a Map has to hash each such string from scratch before it can look
|
|
702
|
+
// anything up; that hashing was 30% of a Standard Schema rejection with
|
|
703
|
+
// sixteen issues. Comparing against the few paths of the same length costs
|
|
704
|
+
// less. Both levels are bounded so a stream of array indexes cannot grow it
|
|
705
|
+
// without limit: a full bucket stops caching, too many lengths clear it.
|
|
706
|
+
const _pathBuckets = new Map();
|
|
707
|
+
const PATH_BUCKET_MAX = 32;
|
|
708
|
+
const PATH_LENGTHS_MAX = 256;
|
|
701
709
|
function parsePointerPath(path) {
|
|
702
710
|
if (!path) return EMPTY_PATH;
|
|
703
|
-
const
|
|
704
|
-
|
|
711
|
+
const n = path.length;
|
|
712
|
+
let bucket = _pathBuckets.get(n);
|
|
713
|
+
if (bucket !== undefined) {
|
|
714
|
+
for (let i = 0; i < bucket.length; i += 2) if (bucket[i] === path) return bucket[i + 1];
|
|
715
|
+
} else {
|
|
716
|
+
if (_pathBuckets.size >= PATH_LENGTHS_MAX) _pathBuckets.clear();
|
|
717
|
+
bucket = [];
|
|
718
|
+
_pathBuckets.set(n, bucket);
|
|
719
|
+
}
|
|
705
720
|
const segs = Object.freeze(parsePointerPathUncached(path));
|
|
706
|
-
if (
|
|
707
|
-
_pathCache.set(path, segs);
|
|
721
|
+
if (bucket.length < PATH_BUCKET_MAX * 2) bucket.push(path, segs);
|
|
708
722
|
return segs;
|
|
709
723
|
}
|
|
710
724
|
const EMPTY_PATH = Object.freeze([]);
|
|
@@ -1109,6 +1123,11 @@ class Validator {
|
|
|
1109
1123
|
this._lastRawInput = null;
|
|
1110
1124
|
// undefined: not built yet. null: this schema has no scanner.
|
|
1111
1125
|
this._scanner = undefined;
|
|
1126
|
+
// Checks a wrapper registered through _extendChecks. Declared here so
|
|
1127
|
+
// registering one does not change the instance's shape.
|
|
1128
|
+
this._verdictTail = null;
|
|
1129
|
+
this._validateTail = null;
|
|
1130
|
+
this._entryExt = null;
|
|
1112
1131
|
|
|
1113
1132
|
// Public methods start as memoized accessors on the prototype; nothing is
|
|
1114
1133
|
// allocated per instance until one is first read. See _defineLazyMethod
|
|
@@ -1117,10 +1136,12 @@ class Validator {
|
|
|
1117
1136
|
// "~standard" (Standard Schema V1) is a lazy prototype accessor too;
|
|
1118
1137
|
// see below the class. Consumers only pay for it if they read it.
|
|
1119
1138
|
|
|
1120
|
-
//
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1139
|
+
// The identity cache, which lets a later `new Validator(sameSchema)` return
|
|
1140
|
+
// this instance, is filled on the first compile rather than here. A WeakMap
|
|
1141
|
+
// entry is an ephemeron the collector has to trace separately, and setting
|
|
1142
|
+
// one cost about 780 ns against 150 for the rest of this constructor, five
|
|
1143
|
+
// times over for an instance that may never validate anything. Once an
|
|
1144
|
+
// instance has compiled, the shortcut behaves as before.
|
|
1124
1145
|
}
|
|
1125
1146
|
|
|
1126
1147
|
// `$vocabulary` says which keywords the dialect has, and answering needs the
|
|
@@ -1556,9 +1577,9 @@ class Validator {
|
|
|
1556
1577
|
// The verdict methods answer validate()'s question without building the
|
|
1557
1578
|
// error list, so they run the same preprocess pass. Skipping it made the
|
|
1558
1579
|
// two disagree on input that coercion or a default would have fixed.
|
|
1559
|
-
this
|
|
1580
|
+
_bindVerdict(this, preprocess
|
|
1560
1581
|
? (data) => { preprocess(data); return jsFn(data) }
|
|
1561
|
-
: jsFn;
|
|
1582
|
+
: jsFn);
|
|
1562
1583
|
// Same preference as the object path: the combined function first, since
|
|
1563
1584
|
// it validates and collects in one pass, and the error generator behind
|
|
1564
1585
|
// it. `errPreferCombined` is that order, resolved on the first rejection
|
|
@@ -1672,37 +1693,59 @@ class Validator {
|
|
|
1672
1693
|
this.isValid = (buf) => {
|
|
1673
1694
|
self._ensureNative();
|
|
1674
1695
|
const slot = self._fastSlot;
|
|
1675
|
-
self.isValid =
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1696
|
+
self.isValid = slot < 0
|
|
1697
|
+
? (b) => self._slowBufferValid(b, 'isValid')
|
|
1698
|
+
: (b) => {
|
|
1699
|
+
if (typeof b === 'string') b = Buffer.from(b);
|
|
1700
|
+
else if (!(b instanceof Uint8Array)) throw new TypeError('isValid() requires a Buffer, Uint8Array, or string. For parsed objects, use isValidObject().');
|
|
1701
|
+
return native.rawFastValidate(slot, b);
|
|
1702
|
+
};
|
|
1680
1703
|
return self.isValid(buf);
|
|
1681
1704
|
};
|
|
1682
1705
|
this.countValid = (ndjsonBuf) => {
|
|
1683
1706
|
self._ensureNative();
|
|
1684
1707
|
const slot = self._fastSlot;
|
|
1685
|
-
self.countValid =
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1708
|
+
self.countValid = slot < 0
|
|
1709
|
+
? (b) => {
|
|
1710
|
+
if (typeof b !== 'string' && !(b instanceof Uint8Array)) throw new TypeError('countValid() requires a Buffer, Uint8Array, or string');
|
|
1711
|
+
const text = typeof b === 'string' ? b : Buffer.from(b.buffer, b.byteOffset, b.byteLength).toString('utf8');
|
|
1712
|
+
let c = 0;
|
|
1713
|
+
for (const line of text.split('\n')) {
|
|
1714
|
+
if (line.trim() === '') continue;
|
|
1715
|
+
if (self._slowBufferValid(line, 'countValid')) c++;
|
|
1716
|
+
}
|
|
1717
|
+
return c;
|
|
1718
|
+
}
|
|
1719
|
+
: (b) => {
|
|
1720
|
+
if (typeof b === 'string') b = Buffer.from(b);
|
|
1721
|
+
else if (!(b instanceof Uint8Array)) throw new TypeError('countValid() requires a Buffer, Uint8Array, or string');
|
|
1722
|
+
const r = native.rawNDJSONValidate(slot, b);
|
|
1723
|
+
let c = 0;
|
|
1724
|
+
for (let i = 0; i < r.length; i++) if (r[i]) c++;
|
|
1725
|
+
return c;
|
|
1726
|
+
};
|
|
1693
1727
|
return self.countValid(ndjsonBuf);
|
|
1694
1728
|
};
|
|
1695
1729
|
this.batchIsValid = (buffers) => {
|
|
1696
1730
|
self._ensureNative();
|
|
1697
1731
|
const slot = self._fastSlot;
|
|
1698
|
-
self.batchIsValid =
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1732
|
+
self.batchIsValid = slot < 0
|
|
1733
|
+
? (bufs) => {
|
|
1734
|
+
let v = 0;
|
|
1735
|
+
for (const b of bufs) {
|
|
1736
|
+
if (!(b instanceof Uint8Array)) throw new TypeError('batchIsValid() requires Buffer or Uint8Array elements');
|
|
1737
|
+
if (self._slowBufferValid(b, 'batchIsValid')) v++;
|
|
1738
|
+
}
|
|
1739
|
+
return v;
|
|
1703
1740
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1741
|
+
: (bufs) => {
|
|
1742
|
+
let v = 0;
|
|
1743
|
+
for (const b of bufs) {
|
|
1744
|
+
if (!(b instanceof Uint8Array)) throw new TypeError('batchIsValid() requires Buffer or Uint8Array elements');
|
|
1745
|
+
if (native.rawFastValidate(slot, b)) v++;
|
|
1746
|
+
}
|
|
1747
|
+
return v;
|
|
1748
|
+
};
|
|
1706
1749
|
return self.batchIsValid(buffers);
|
|
1707
1750
|
};
|
|
1708
1751
|
}
|
|
@@ -1749,9 +1792,9 @@ class Validator {
|
|
|
1749
1792
|
return _validate(data);
|
|
1750
1793
|
}
|
|
1751
1794
|
: _validate;
|
|
1752
|
-
this
|
|
1795
|
+
_bindVerdict(this, this._fastVerdict
|
|
1753
1796
|
? this._fastVerdict
|
|
1754
|
-
: (data) => _validate(data).valid;
|
|
1797
|
+
: (data) => _validate(data).valid);
|
|
1755
1798
|
this.validateAndParse = (jsonStr) => this._compiled.validateAndParse(jsonStr);
|
|
1756
1799
|
{
|
|
1757
1800
|
const slot = this._fastSlot;
|
|
@@ -1809,9 +1852,9 @@ class Validator {
|
|
|
1809
1852
|
? (data) => { preprocess(data); return interp.validate(data); }
|
|
1810
1853
|
: (data) => interp.validate(data));
|
|
1811
1854
|
this.validate = run;
|
|
1812
|
-
this
|
|
1855
|
+
_bindVerdict(this, this._fastVerdict
|
|
1813
1856
|
? this._fastVerdict
|
|
1814
|
-
: (data) => run(data).valid;
|
|
1857
|
+
: (data) => run(data).valid);
|
|
1815
1858
|
this.validateJSON = (jsonStr) => {
|
|
1816
1859
|
try {
|
|
1817
1860
|
return run(JSON.parse(jsonStr));
|
|
@@ -1940,30 +1983,54 @@ class Validator {
|
|
|
1940
1983
|
// schema coerces or defaults (preprocess mutates before the verdict),
|
|
1941
1984
|
// under abortEarly (already a frozen stub), and for $dynamicRef (the
|
|
1942
1985
|
// boolean engine is not the authority there).
|
|
1986
|
+
// A check registered through _extendValidate joins here when it can: the
|
|
1987
|
+
// verdict comes from the generated function with the check compiled in,
|
|
1988
|
+
// and the check's own errors are appended when somebody reads them. A
|
|
1989
|
+
// rejection then costs what it costs without the check. Where this layer
|
|
1990
|
+
// is not installed, the end of this method wraps validate() instead.
|
|
1991
|
+
const _vx = this._validateTail !== null ? this._validateTail() : null;
|
|
1992
|
+
let _vxApplied = false;
|
|
1943
1993
|
if (this._fastVerdict && !preprocess && !options.abortEarly && this.validate) {
|
|
1944
1994
|
const _full = this.validate;
|
|
1945
|
-
const _fast = this._fastVerdict;
|
|
1995
|
+
const _fast = _vx ? _fuseTail(this._fastVerdict, _vx.check) : this._fastVerdict;
|
|
1996
|
+
const _extra = _vx ? _vx.errors : null;
|
|
1997
|
+
_vxApplied = true;
|
|
1946
1998
|
const EMPTY_ERRORS = Object.freeze([]);
|
|
1947
1999
|
const _verdictFallback = [{ keyword: 'validation', instancePath: '', schemaPath: '#', params: {}, message: 'schema validation failed' }];
|
|
2000
|
+
const _withExtra = (own, data) => {
|
|
2001
|
+
const more = _extra === null ? null : _extra(data);
|
|
2002
|
+
if (more && more.length) return own ? own.concat(more) : more;
|
|
2003
|
+
// The data changed between the verdict and this read; keep the
|
|
2004
|
+
// verdict and say so rather than inventing a specific error.
|
|
2005
|
+
return own || _verdictFallback;
|
|
2006
|
+
};
|
|
1948
2007
|
const _buildErrors = (data) => {
|
|
1949
2008
|
const r = _full(data);
|
|
1950
|
-
return (r && r.valid === false && r.errors && r.errors.length)
|
|
1951
|
-
? r.errors
|
|
1952
|
-
// The data changed between the verdict and this read; keep the
|
|
1953
|
-
// verdict and say so rather than inventing a specific error.
|
|
1954
|
-
: _verdictFallback;
|
|
2009
|
+
return _withExtra((r && r.valid === false && r.errors && r.errors.length) ? r.errors : null, data);
|
|
1955
2010
|
};
|
|
1956
2011
|
const _buildRawErrors = (data) => {
|
|
1957
2012
|
const r = _full(data);
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2013
|
+
let raw = null;
|
|
2014
|
+
if (r && r.valid === false) {
|
|
2015
|
+
raw = typeof r._ataRaw === 'function' ? r._ataRaw() : r.errors;
|
|
2016
|
+
if (!raw || !raw.length) raw = null;
|
|
2017
|
+
}
|
|
2018
|
+
return _withExtra(raw, data);
|
|
1961
2019
|
};
|
|
1962
2020
|
this.validate = (data) => {
|
|
1963
2021
|
if (_fast(data)) return { valid: true, data, errors: EMPTY_ERRORS };
|
|
1964
2022
|
return new LazyRejection(_buildErrors, data, _buildRawErrors);
|
|
1965
2023
|
};
|
|
1966
2024
|
}
|
|
2025
|
+
if (_vx && !_vxApplied && this.validate) {
|
|
2026
|
+
const inner = this.validate;
|
|
2027
|
+
const { check, errors } = _vx;
|
|
2028
|
+
this.validate = (data) => {
|
|
2029
|
+
const r = inner(data);
|
|
2030
|
+
if (r.valid && check(data)) return r;
|
|
2031
|
+
return new ExtendedRejection(r, data, errors);
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
1967
2034
|
|
|
1968
2035
|
// The buffer APIs answer from the native walker, which disagrees with
|
|
1969
2036
|
// validate() on shapes listed in lib/buffer-gate.js. For those schemas
|
|
@@ -2011,8 +2078,8 @@ class Validator {
|
|
|
2011
2078
|
this.isValidJSON = (jsonStr) => {
|
|
2012
2079
|
const scan = self._ensureScanner();
|
|
2013
2080
|
if (scan === undefined) return byParsing(jsonStr);
|
|
2014
|
-
if (scan === null) { self
|
|
2015
|
-
self
|
|
2081
|
+
if (scan === null) { _bindEntry(self, 'isValidJSON', byParsing); return byParsing(jsonStr); }
|
|
2082
|
+
_bindEntry(self, 'isValidJSON', memoizable
|
|
2016
2083
|
? (text) => {
|
|
2017
2084
|
if (typeof text !== 'string') return byParsing(text);
|
|
2018
2085
|
if (text === _memoText) return _memoVerdict;
|
|
@@ -2027,7 +2094,7 @@ class Validator {
|
|
|
2027
2094
|
const r = scan(text);
|
|
2028
2095
|
if (r === -1) return byParsing(text);
|
|
2029
2096
|
return r === 1;
|
|
2030
|
-
};
|
|
2097
|
+
});
|
|
2031
2098
|
return self.isValidJSON(jsonStr);
|
|
2032
2099
|
};
|
|
2033
2100
|
// validateJSON gets the same short-circuit isValidJSON has. The verdict is
|
|
@@ -2050,8 +2117,8 @@ class Validator {
|
|
|
2050
2117
|
this.validateJSON = (jsonStr) => {
|
|
2051
2118
|
const scan = self._ensureScanner();
|
|
2052
2119
|
if (scan === undefined) return validateByParsing(jsonStr);
|
|
2053
|
-
if (scan === null) { self
|
|
2054
|
-
self
|
|
2120
|
+
if (scan === null) { _bindEntry(self, 'validateJSON', validateByParsing); return validateByParsing(jsonStr); }
|
|
2121
|
+
_bindEntry(self, 'validateJSON', (text) => {
|
|
2055
2122
|
if (typeof text === 'string') {
|
|
2056
2123
|
const r = scan(text);
|
|
2057
2124
|
if (r === 1) return VALID_RESULT;
|
|
@@ -2066,21 +2133,25 @@ class Validator {
|
|
|
2066
2133
|
}
|
|
2067
2134
|
}
|
|
2068
2135
|
return validateByParsing(text);
|
|
2069
|
-
};
|
|
2136
|
+
});
|
|
2070
2137
|
return self.validateJSON(jsonStr);
|
|
2071
2138
|
};
|
|
2072
2139
|
}
|
|
2073
2140
|
}
|
|
2074
2141
|
|
|
2075
|
-
|
|
2076
|
-
//
|
|
2077
|
-
//
|
|
2078
|
-
//
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2142
|
+
// An extension registered through _extendValidate covers the JSON entry
|
|
2143
|
+
// points too. They are final here except for the scanner stubs above,
|
|
2144
|
+
// which rebind themselves on first use through _bindEntry, so the
|
|
2145
|
+
// extension is applied to whatever each one is now and again on rebind.
|
|
2146
|
+
if (_vx) {
|
|
2147
|
+
this._entryExt = _jsonEntryWrappers(_vx);
|
|
2148
|
+
for (const name of ['validateJSON', 'isValidJSON', 'validateAndParse']) {
|
|
2149
|
+
if (Object.prototype.hasOwnProperty.call(this, name) && typeof this[name] === 'function') _bindEntry(this, name, this[name]);
|
|
2150
|
+
}
|
|
2083
2151
|
}
|
|
2152
|
+
|
|
2153
|
+
|
|
2154
|
+
_rememberInstance(this);
|
|
2084
2155
|
}
|
|
2085
2156
|
|
|
2086
2157
|
// Which engine answers validate() for this schema: 'codegen' (generated
|
|
@@ -2106,7 +2177,37 @@ class Validator {
|
|
|
2106
2177
|
nativeSchemaStr = JSON.stringify(merged);
|
|
2107
2178
|
}
|
|
2108
2179
|
this._compiled = new native.CompiledSchema(nativeSchemaStr);
|
|
2109
|
-
|
|
2180
|
+
// The fast registry is a fixed array of slots in the addon, and registering
|
|
2181
|
+
// a distinct schema past the last one throws. It is an accelerator for the
|
|
2182
|
+
// buffer path, not a requirement, so a full registry leaves the slot at -1
|
|
2183
|
+
// and the buffer methods answer from the JS engine instead. Letting this
|
|
2184
|
+
// throw made every validator built after the 4096th unusable on that path.
|
|
2185
|
+
try {
|
|
2186
|
+
this._fastSlot = native.fastRegister(nativeSchemaStr);
|
|
2187
|
+
} catch {
|
|
2188
|
+
this._fastSlot = -1;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
// The buffer path without a fast slot: decode, parse, and hand the value to
|
|
2193
|
+
// the engine that does not need one. Slower than the zero-copy walk, and the
|
|
2194
|
+
// same answer. A negative slot must never reach rawFastValidate, whose bounds
|
|
2195
|
+
// check would report a valid document as invalid.
|
|
2196
|
+
_slowBufferValid(input, who) {
|
|
2197
|
+
let text;
|
|
2198
|
+
if (typeof input === 'string') text = input;
|
|
2199
|
+
else if (input instanceof Uint8Array) {
|
|
2200
|
+
text = Buffer.from(input.buffer, input.byteOffset, input.byteLength).toString('utf8');
|
|
2201
|
+
} else {
|
|
2202
|
+
throw new TypeError(who + '() requires a Buffer, Uint8Array, or string');
|
|
2203
|
+
}
|
|
2204
|
+
let parsed;
|
|
2205
|
+
try {
|
|
2206
|
+
parsed = JSON.parse(text);
|
|
2207
|
+
} catch {
|
|
2208
|
+
return false;
|
|
2209
|
+
}
|
|
2210
|
+
return this.isValidObject(parsed);
|
|
2110
2211
|
}
|
|
2111
2212
|
|
|
2112
2213
|
addSchema(schema) {
|
|
@@ -2153,7 +2254,8 @@ class Validator {
|
|
|
2153
2254
|
const cached = (this._userFormats || this._usesKeywords) ? null : _compileCache.get(mapKey);
|
|
2154
2255
|
if (cached && cached.jsFn) {
|
|
2155
2256
|
this._jsFn = cached.jsFn;
|
|
2156
|
-
this
|
|
2257
|
+
_bindVerdict(this, cached.jsFn);
|
|
2258
|
+
_rememberInstance(this);
|
|
2157
2259
|
return;
|
|
2158
2260
|
}
|
|
2159
2261
|
const uf = this._userFormats;
|
|
@@ -2161,7 +2263,8 @@ class Validator {
|
|
|
2161
2263
|
const jsFn = _cg || compileToJS(this._schemaObj, null, sm);
|
|
2162
2264
|
this._jsFn = jsFn;
|
|
2163
2265
|
if (jsFn) {
|
|
2164
|
-
this
|
|
2266
|
+
_bindVerdict(this, jsFn);
|
|
2267
|
+
_rememberInstance(this);
|
|
2165
2268
|
// A partial entry: the verdict function is real, the other two are not
|
|
2166
2269
|
// built yet rather than declined. `undefined` is the not-built marker
|
|
2167
2270
|
// the full compile's _buildDeferred looks for; `null` would read as
|
|
@@ -2563,6 +2666,216 @@ Object.defineProperty(Validator.prototype, "~standard", {
|
|
|
2563
2666
|
},
|
|
2564
2667
|
});
|
|
2565
2668
|
|
|
2669
|
+
// Install the verdict method. Every place that binds isValidObject comes
|
|
2670
|
+
// through here, so a check registered with _extendVerdict survives the method
|
|
2671
|
+
// being replaced as the validator compiles further, which it does more than
|
|
2672
|
+
// once over its life.
|
|
2673
|
+
function _bindVerdict(self, fn) {
|
|
2674
|
+
const resolve = self._verdictTail;
|
|
2675
|
+
if (resolve !== null && typeof fn === 'function') {
|
|
2676
|
+
const tail = resolve();
|
|
2677
|
+
if (typeof tail === 'function') fn = _fuseTail(fn, tail);
|
|
2678
|
+
}
|
|
2679
|
+
self.isValidObject = fn;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// Bind one of the JSON entry points, through the extension wrapper when there is one.
|
|
2683
|
+
function _bindEntry(self, name, fn) {
|
|
2684
|
+
const ext = self._entryExt;
|
|
2685
|
+
self[name] = ext !== null && ext[name] ? ext[name](fn) : fn;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
// The JSON entry points under an extension: the schema answers first, and only
|
|
2689
|
+
// text it accepts is parsed for the check, so a rejection costs nothing extra.
|
|
2690
|
+
function _jsonEntryWrappers({ check, errors }) {
|
|
2691
|
+
const parse = (text) => JSON.parse(typeof text === 'string' ? text : new TextDecoder().decode(text));
|
|
2692
|
+
return {
|
|
2693
|
+
validateJSON: (inner) => (text) => {
|
|
2694
|
+
const res = inner(text);
|
|
2695
|
+
if (!res.valid) return res;
|
|
2696
|
+
let data;
|
|
2697
|
+
try { data = parse(text); } catch { return res; }
|
|
2698
|
+
if (check(data)) return res;
|
|
2699
|
+
const e = errors(data);
|
|
2700
|
+
return e && e.length ? { valid: false, errors: e } : { valid: false, errors: [_EXT_FALLBACK] };
|
|
2701
|
+
},
|
|
2702
|
+
isValidJSON: (inner) => (text) => {
|
|
2703
|
+
if (!inner(text)) return false;
|
|
2704
|
+
let data;
|
|
2705
|
+
try { data = parse(text); } catch { return true; }
|
|
2706
|
+
return check(data);
|
|
2707
|
+
},
|
|
2708
|
+
validateAndParse: (inner) => (text) => {
|
|
2709
|
+
const res = inner(text);
|
|
2710
|
+
if (!res.valid) return res;
|
|
2711
|
+
if (check(res.value)) return res;
|
|
2712
|
+
const e = errors(res.value);
|
|
2713
|
+
return { valid: false, value: res.value, errors: e && e.length ? e : [_EXT_FALLBACK] };
|
|
2714
|
+
},
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
const _EXT_FALLBACK = Object.freeze({ keyword: 'validation', instancePath: '', schemaPath: '#', params: {}, message: 'schema validation failed' });
|
|
2718
|
+
|
|
2719
|
+
// A verdict function that also runs `tail` on what it accepts. The generated
|
|
2720
|
+
// function can take the check in place of its final `return true`, one call
|
|
2721
|
+
// per document; anything else is composed.
|
|
2722
|
+
function _fuseTail(fn, tail) {
|
|
2723
|
+
const fused = typeof fn._withTail === 'function' ? fn._withTail(tail) : null;
|
|
2724
|
+
return fused || ((d) => fn(d) && tail(d));
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
// The rejection validate() returns on the paths where an extension check could
|
|
2728
|
+
// not join the lazy layer: the inner result, plus the check's errors appended
|
|
2729
|
+
// on first read. `inner` may itself be valid, when only the check failed.
|
|
2730
|
+
class ExtendedRejection {
|
|
2731
|
+
constructor(inner, data, collect) {
|
|
2732
|
+
this.valid = false;
|
|
2733
|
+
this._inner = inner;
|
|
2734
|
+
this._data = data;
|
|
2735
|
+
this._collect = collect;
|
|
2736
|
+
this._errors = null;
|
|
2737
|
+
}
|
|
2738
|
+
toJSON() {
|
|
2739
|
+
return { valid: false, errors: this.errors };
|
|
2740
|
+
}
|
|
2741
|
+
_ataRaw() {
|
|
2742
|
+
const inner = this._inner;
|
|
2743
|
+
const more = this._collect(this._data) || [];
|
|
2744
|
+
const raw = inner.valid ? more : (typeof inner._ataRaw === 'function' ? inner._ataRaw() : inner.errors).concat(more);
|
|
2745
|
+
return raw.length ? raw : [_EXT_FALLBACK];
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
Object.defineProperty(ExtendedRejection.prototype, 'errors', {
|
|
2749
|
+
enumerable: true,
|
|
2750
|
+
configurable: true,
|
|
2751
|
+
get() {
|
|
2752
|
+
if (this._errors === null) {
|
|
2753
|
+
const inner = this._inner;
|
|
2754
|
+
const more = this._collect(this._data) || [];
|
|
2755
|
+
const all = inner.valid ? more : inner.errors.concat(more);
|
|
2756
|
+
this._errors = all.length ? all : [_EXT_FALLBACK];
|
|
2757
|
+
}
|
|
2758
|
+
return this._errors;
|
|
2759
|
+
},
|
|
2760
|
+
});
|
|
2761
|
+
|
|
2762
|
+
// For wrappers that enforce a check the schema does not carry, such as the
|
|
2763
|
+
// `instanceof` keyword of @ata-project/keywords. `resolve` is called whenever
|
|
2764
|
+
// the verdict method is bound, which is after the schema has been normalized,
|
|
2765
|
+
// and returns the check, a function of the document that answers true or
|
|
2766
|
+
// false, or null when there is nothing to add. Only isValidObject takes it;
|
|
2767
|
+
// the other entry points, which report errors, stay the wrapper's to handle.
|
|
2768
|
+
//
|
|
2769
|
+
// Before this, such a wrapper had to hold isValidObject behind an accessor so
|
|
2770
|
+
// that the validator's own rebinding could not drop its check, and every call
|
|
2771
|
+
// paid for the accessor and two more calls: 10.1 ns against 4.2 on a document
|
|
2772
|
+
// the schema rejects at its third property.
|
|
2773
|
+
Validator.prototype._verdictTail = null;
|
|
2774
|
+
Validator.prototype._validateTail = null;
|
|
2775
|
+
Validator.prototype._entryExt = null;
|
|
2776
|
+
|
|
2777
|
+
// Let `new Validator(schema)` with the same schema object return this instance.
|
|
2778
|
+
// Only an instance built without options may answer that call: one built with
|
|
2779
|
+
// options (richErrors: false, coerceTypes, formats, ...) would hand its options
|
|
2780
|
+
// to a caller that asked for none, and an extended one would enforce checks the
|
|
2781
|
+
// caller never registered. Both the caller's object and the normalized one are
|
|
2782
|
+
// keys, since a later caller passes the former.
|
|
2783
|
+
function _rememberInstance(self) {
|
|
2784
|
+
if (!self._noOpts || self._verdictTail !== null || self._validateTail !== null) return;
|
|
2785
|
+
const raw = self._rawSchema;
|
|
2786
|
+
if (raw && typeof raw === 'object' && !_identityCache.has(raw)) _identityCache.set(raw, self);
|
|
2787
|
+
const obj = self._schemaObj;
|
|
2788
|
+
if (obj !== raw && obj && typeof obj === 'object' && !_identityCache.has(obj)) _identityCache.set(obj, self);
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
// An extended validator answers differently from a plain one for the same
|
|
2792
|
+
// schema, so it must not be the instance `new Validator(sameSchema)` hands out.
|
|
2793
|
+
function _leaveIdentityCache(self) {
|
|
2794
|
+
self._noOpts = false;
|
|
2795
|
+
// Nothing is registered before the first compile, so there is nothing to
|
|
2796
|
+
// take back.
|
|
2797
|
+
if (!self._initialized && self._jsFn === null) return;
|
|
2798
|
+
const raw = self._rawSchema;
|
|
2799
|
+
if (raw && typeof raw === 'object' && _identityCache.get(raw) === self) _identityCache.delete(raw);
|
|
2800
|
+
// The compiled form is cached too, at the end of the first compile. Read it
|
|
2801
|
+
// only if it is already materialized: reading it otherwise builds it.
|
|
2802
|
+
if (Object.prototype.hasOwnProperty.call(self, '_schemaObj')) {
|
|
2803
|
+
const obj = self._schemaObj;
|
|
2804
|
+
if (obj && typeof obj === 'object' && _identityCache.get(obj) === self) _identityCache.delete(obj);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2808
|
+
// The same kind of extension for validate(): `resolve` returns { check, errors }
|
|
2809
|
+
// or null, where `errors(data)` lists the check's own errors, or returns null
|
|
2810
|
+
// when there are none. The check's errors come after the schema's, and a value
|
|
2811
|
+
// that fails only the check is rejected with the check's errors alone. Must be
|
|
2812
|
+
// called before validate() is first used, which is when it is compiled; later
|
|
2813
|
+
// calls throw rather than being silently ignored.
|
|
2814
|
+
Validator.prototype._extendValidate = function (resolve) {
|
|
2815
|
+
if (typeof resolve !== 'function') throw new TypeError('_extendValidate expects a function');
|
|
2816
|
+
if (this._initialized) throw new Error('_extendValidate must be called before the validator compiles');
|
|
2817
|
+
_leaveIdentityCache(this);
|
|
2818
|
+
const prev = this._validateTail;
|
|
2819
|
+
this._validateTail = prev === null ? resolve : () => {
|
|
2820
|
+
const a = prev(), b = resolve();
|
|
2821
|
+
if (!a) return b;
|
|
2822
|
+
if (!b) return a;
|
|
2823
|
+
return {
|
|
2824
|
+
check: (d) => a.check(d) && b.check(d),
|
|
2825
|
+
errors: (d) => {
|
|
2826
|
+
const x = a.errors(d), y = b.errors(d);
|
|
2827
|
+
if (!x) return y;
|
|
2828
|
+
if (!y) return x;
|
|
2829
|
+
return x.concat(y);
|
|
2830
|
+
},
|
|
2831
|
+
};
|
|
2832
|
+
};
|
|
2833
|
+
return this;
|
|
2834
|
+
};
|
|
2835
|
+
|
|
2836
|
+
// Both extensions in one call, from one resolver that returns { check, errors }
|
|
2837
|
+
// or null. This is the form @ata-project/keywords uses: registering costs one
|
|
2838
|
+
// closure, where wrapping the five entry points cost a closure per entry point,
|
|
2839
|
+
// the wrappers themselves and an accessor, most of what building a wrapped
|
|
2840
|
+
// validator took.
|
|
2841
|
+
Validator.prototype._extendChecks = function (resolve) {
|
|
2842
|
+
if (typeof resolve !== 'function') throw new TypeError('_extendChecks expects a function');
|
|
2843
|
+
this._extendValidate(resolve);
|
|
2844
|
+
return this._extendVerdict(() => {
|
|
2845
|
+
const x = resolve();
|
|
2846
|
+
return x ? x.check : null;
|
|
2847
|
+
});
|
|
2848
|
+
};
|
|
2849
|
+
|
|
2850
|
+
// Whether this instance enforces a check its schema does not carry. The
|
|
2851
|
+
// ahead-of-time emitters build a module from the schema alone, so they refuse
|
|
2852
|
+
// an instance that says yes rather than emit one that accepts too much.
|
|
2853
|
+
// Reading it resolves the registered checks; only an emitter reads it.
|
|
2854
|
+
Object.defineProperty(Validator.prototype, '_externalChecks', {
|
|
2855
|
+
configurable: true,
|
|
2856
|
+
get() {
|
|
2857
|
+
if (this._validateTail !== null && this._validateTail()) return true;
|
|
2858
|
+
if (this._verdictTail !== null && typeof this._verdictTail() === 'function') return true;
|
|
2859
|
+
return false;
|
|
2860
|
+
},
|
|
2861
|
+
});
|
|
2862
|
+
|
|
2863
|
+
Validator.prototype._extendVerdict = function (resolve) {
|
|
2864
|
+
if (typeof resolve !== 'function') throw new TypeError('_extendVerdict expects a function');
|
|
2865
|
+
_leaveIdentityCache(this);
|
|
2866
|
+
const prev = this._verdictTail;
|
|
2867
|
+
this._verdictTail = prev === null ? resolve : () => {
|
|
2868
|
+
const a = prev(), b = resolve();
|
|
2869
|
+
if (typeof a !== 'function') return b;
|
|
2870
|
+
if (typeof b !== 'function') return a;
|
|
2871
|
+
return (d) => a(d) && b(d);
|
|
2872
|
+
};
|
|
2873
|
+
// A method bound before this call was bound without the check. Rebind it.
|
|
2874
|
+
// An unbound one binds through _bindVerdict on its first call.
|
|
2875
|
+
if (Object.prototype.hasOwnProperty.call(this, 'isValidObject')) _bindVerdict(this, this.isValidObject);
|
|
2876
|
+
return this;
|
|
2877
|
+
};
|
|
2878
|
+
|
|
2566
2879
|
function _defineLazyMethod(name, maker) {
|
|
2567
2880
|
Object.defineProperty(Validator.prototype, name, {
|
|
2568
2881
|
configurable: true,
|
|
@@ -2608,13 +2921,13 @@ _defineLazyMethod('isValidObject', (self) => (data) => {
|
|
|
2608
2921
|
if (_tier.tier === 0) {
|
|
2609
2922
|
const _plan = buildTier0Plan(self._schemaObj);
|
|
2610
2923
|
let _n = 0;
|
|
2611
|
-
self
|
|
2924
|
+
_bindVerdict(self, (d) => {
|
|
2612
2925
|
const r = tier0Validate(_plan, d);
|
|
2613
2926
|
if (++_n === 2) {
|
|
2614
2927
|
try { self._ensureCodegen(); } catch {}
|
|
2615
2928
|
}
|
|
2616
2929
|
return r;
|
|
2617
|
-
};
|
|
2930
|
+
});
|
|
2618
2931
|
} else {
|
|
2619
2932
|
// `new Function` is a property of the realm, not of the schema: under a
|
|
2620
2933
|
// strict CSP or `--disallow-code-generation-from-strings` this throws
|
package/lib/formats.js
CHANGED
|
@@ -117,8 +117,16 @@ function dateTime (s) {
|
|
|
117
117
|
// Also used for the dotted tail of an IPv4-mapped IPv6 address, which is why
|
|
118
118
|
// the work is done over a range rather than a whole string: taking a slice for
|
|
119
119
|
// it would allocate on a path that exists to avoid allocating.
|
|
120
|
+
// Four dotted decimal octets, 0 to 255, no leading zeros: the same language
|
|
121
|
+
// ipv4Range walks, as one regular expression. The engine answers a dotted quad
|
|
122
|
+
// in about 12 ns where the walk takes 17, for the reason `uri` above found:
|
|
123
|
+
// its character-class loop beats a charCodeAt loop. ipv4Range stays for the
|
|
124
|
+
// embedded form inside an IPv6 address, where the octets are a slice.
|
|
125
|
+
// tests/test_ipv4_regex.js holds the two to the same answers.
|
|
126
|
+
const IPV4 = /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
|
|
127
|
+
|
|
120
128
|
function ipv4 (s) {
|
|
121
|
-
return
|
|
129
|
+
return IPV4.test(s);
|
|
122
130
|
}
|
|
123
131
|
|
|
124
132
|
function ipv4Range (s, from, to) {
|
|
@@ -147,8 +155,17 @@ function ipv4Range (s, from, to) {
|
|
|
147
155
|
// allocated twice and accepted things like "12345::1"; they also disagreed
|
|
148
156
|
// with each other about an IPv4 tail. Answers match Node's own `net.isIPv6`,
|
|
149
157
|
// which the test uses as its oracle.
|
|
158
|
+
// Eight groups of one to four hex digits and nothing else: the uncompressed
|
|
159
|
+
// form, which is always a valid address. The walk below takes 64 ns on one;
|
|
160
|
+
// this answers in about 30, and anything it does not match, compressed forms
|
|
161
|
+
// and embedded IPv4 included, goes to the walk as before. It is only tried from
|
|
162
|
+
// 15 characters, the shortest string it can match, so a short rejection does
|
|
163
|
+
// not pay for it. tests/test_ipv6_fast_path.js holds the subset relation.
|
|
164
|
+
const IPV6_FULL = /^(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}$/;
|
|
165
|
+
|
|
150
166
|
function ipv6 (s) {
|
|
151
167
|
const n = s.length;
|
|
168
|
+
if (n >= 15 && IPV6_FULL.test(s)) return true;
|
|
152
169
|
if (n < 2 || n > 45) return false;
|
|
153
170
|
|
|
154
171
|
let end = n;
|
|
@@ -256,7 +273,22 @@ SCHEME_CHAR[43] = 1; SCHEME_CHAR[45] = 1; SCHEME_CHAR[46] = 1;
|
|
|
256
273
|
// string or scanned again. Reading it out with `slice` and asking the copy for
|
|
257
274
|
// `lastIndexOf` and two regular expressions allocated a string per URI, and a
|
|
258
275
|
// document carrying a handful of URLs paid that per field.
|
|
276
|
+
// The shape almost every URI in a document has, as one regular expression: a
|
|
277
|
+
// scheme, then either "//" and a host with no userinfo, port, brackets or
|
|
278
|
+
// percent escapes, or no authority at all, then a tail of characters RFC 3986
|
|
279
|
+
// allows, again without "%". Everything it accepts the walk below accepts too,
|
|
280
|
+
// and it answers the common case in about 25 ns where the walk takes 43 on a
|
|
281
|
+
// 34-character URL, because the engine scans a character class faster than a
|
|
282
|
+
// charCodeAt loop does. Anything it declines, the walk decides as before.
|
|
283
|
+
//
|
|
284
|
+
// The two branches after the colon are exclusive on purpose. With the "//"
|
|
285
|
+
// group merely optional, "http://[::1" could skip it and match "//[::1" as a
|
|
286
|
+
// path, accepting a string the walk rejects for its unclosed bracket.
|
|
287
|
+
// tests/test_uri_fast_path.js holds the subset relation.
|
|
288
|
+
const URI_FAST = /^[A-Za-z][A-Za-z0-9+.\-]*:(?:\/\/[!$&-.0-9;=A-Z_a-z~]*(?:[\/?#][!#$&-;=?-\[\]_a-z~]*)?|(?!\/\/)[!#$&-;=?-\[\]_a-z~]*)$/;
|
|
289
|
+
|
|
259
290
|
function uri (s) {
|
|
291
|
+
if (URI_FAST.test(s)) return true;
|
|
260
292
|
const n = s.length;
|
|
261
293
|
if (n === 0) return false;
|
|
262
294
|
let c = s.charCodeAt(0);
|
|
@@ -762,8 +794,8 @@ const URI_HELPER_TABLES = 'const _uct=new Uint8Array(128);for(let _i=33;_i<127;_
|
|
|
762
794
|
'for(let _i=97;_i<103;_i++)_uch[_i]=1;for(let _i=65;_i<71;_i++)_uch[_i]=1;' +
|
|
763
795
|
'const _ucs=new Uint8Array(128);for(let _i=48;_i<58;_i++)_ucs[_i]=1;' +
|
|
764
796
|
'for(let _i=97;_i<123;_i++)_ucs[_i]=1;for(let _i=65;_i<91;_i++)_ucs[_i]=1;' +
|
|
765
|
-
'_ucs[43]=1;_ucs[45]=1;_ucs[46]=1';
|
|
766
|
-
const URI_HELPER_BODY = 'const _n=_s.length;if(_n===0)return false;let _c=_s.charCodeAt(0);if(_c>127||_ucs[_c]===0||(_c>=48&&_c<=57)||_c===43||_c===45||_c===46)return false;let _co=-1;for(let _i=1;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c===58){_co=_i;break}if(_c>127||_ucs[_c]===0)return false}if(_co===-1)return false;let _i=_co+1;let _ae=_n;if(_s.charCodeAt(_i)===47&&_s.charCodeAt(_i+1)===47){const _as=_i+2;let _at=-1,_fc=-1,_lc=-1,_br=0,_ba=0,_hs=_as;_ae=-1;for(_i=_as;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c>127||_uct[_c]===0)return false;if(_c===47||_c===63||_c===35){_ae=_i;break}if(_c===37){const _h1=_s.charCodeAt(_i+1),_h2=_s.charCodeAt(_i+2);if(!(_h1<=127)||!(_h2<=127)||_uch[_h1]===0||_uch[_h2]===0)return false;_i+=2;continue}if(_c===64){_ba=_br;_at=_i;_fc=-1;_lc=-1;_hs=_i+1}else if(_c===58){if(_fc===-1)_fc=_i;_lc=_i}else if(_c===91||_c===93){_br=1}}if(_ae===-1)_ae=_n;if(_at!==-1&&_ba)return false;if(_s.charCodeAt(_hs)===91){let _cl=-1;for(let _j=_hs+1;_j<_ae;_j++){if(_s.charCodeAt(_j)===93){_cl=_j;break}}if(_cl===-1)return false;if(_cl+1!==_ae){if(_s.charCodeAt(_cl+1)!==58)return false;for(let _j=_cl+2;_j<_ae;_j++){const _d=_s.charCodeAt(_j);if(_d<48||_d>57)return false}}}else if(_lc!==-1){if(_fc!==_lc)return false;for(let _j=_lc+1;_j<_ae;_j++){const _d=_s.charCodeAt(_j);if(_d<48||_d>57)return false}}_i=_ae}for(;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c>127||_uct[_c]===0)return false;if(_c===37){const _h1=_s.charCodeAt(_i+1),_h2=_s.charCodeAt(_i+2);if(!(_h1<=127)||!(_h2<=127)||_uch[_h1]===0||_uch[_h2]===0)return false;_i+=2}}return true;';
|
|
797
|
+
'_ucs[43]=1;_ucs[45]=1;_ucs[46]=1;const _ufa=/' + URI_FAST.source + '/';
|
|
798
|
+
const URI_HELPER_BODY = 'if(_ufa.test(_s))return true;const _n=_s.length;if(_n===0)return false;let _c=_s.charCodeAt(0);if(_c>127||_ucs[_c]===0||(_c>=48&&_c<=57)||_c===43||_c===45||_c===46)return false;let _co=-1;for(let _i=1;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c===58){_co=_i;break}if(_c>127||_ucs[_c]===0)return false}if(_co===-1)return false;let _i=_co+1;let _ae=_n;if(_s.charCodeAt(_i)===47&&_s.charCodeAt(_i+1)===47){const _as=_i+2;let _at=-1,_fc=-1,_lc=-1,_br=0,_ba=0,_hs=_as;_ae=-1;for(_i=_as;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c>127||_uct[_c]===0)return false;if(_c===47||_c===63||_c===35){_ae=_i;break}if(_c===37){const _h1=_s.charCodeAt(_i+1),_h2=_s.charCodeAt(_i+2);if(!(_h1<=127)||!(_h2<=127)||_uch[_h1]===0||_uch[_h2]===0)return false;_i+=2;continue}if(_c===64){_ba=_br;_at=_i;_fc=-1;_lc=-1;_hs=_i+1}else if(_c===58){if(_fc===-1)_fc=_i;_lc=_i}else if(_c===91||_c===93){_br=1}}if(_ae===-1)_ae=_n;if(_at!==-1&&_ba)return false;if(_s.charCodeAt(_hs)===91){let _cl=-1;for(let _j=_hs+1;_j<_ae;_j++){if(_s.charCodeAt(_j)===93){_cl=_j;break}}if(_cl===-1)return false;if(_cl+1!==_ae){if(_s.charCodeAt(_cl+1)!==58)return false;for(let _j=_cl+2;_j<_ae;_j++){const _d=_s.charCodeAt(_j);if(_d<48||_d>57)return false}}}else if(_lc!==-1){if(_fc!==_lc)return false;for(let _j=_lc+1;_j<_ae;_j++){const _d=_s.charCodeAt(_j);if(_d<48||_d>57)return false}}_i=_ae}for(;_i<_n;_i++){_c=_s.charCodeAt(_i);if(_c>127||_uct[_c]===0)return false;if(_c===37){const _h1=_s.charCodeAt(_i+1),_h2=_s.charCodeAt(_i+2);if(!(_h1<=127)||!(_h2<=127)||_uch[_h1]===0||_uch[_h2]===0)return false;_i+=2}}return true;';
|
|
767
799
|
const uriHelperSource = (name) =>
|
|
768
800
|
URI_HELPER_TABLES + ';function ' + name + '(_s){' + URI_HELPER_BODY + '}';
|
|
769
801
|
|
|
@@ -1010,4 +1042,4 @@ function durationSource (v, isStr) {
|
|
|
1010
1042
|
return isStr ? inner : `if(typeof ${v}==='string'&&${inner.slice(3)}`;
|
|
1011
1043
|
}
|
|
1012
1044
|
|
|
1013
|
-
module.exports = { uriHelperSource, date, ipv4, dateTime, ipv6, hostname, uri, uriReference, uuid, time, noReserved, jsonPointer, relativeJsonPointer, uriTemplate, email, duration, uriChars, uriAuthority, uriCharsSource, uriAuthoritySource, iri, iriReference, idnEmail, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uriSource, uuidSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };
|
|
1045
|
+
module.exports = { IPV4, IPV6_FULL, ipv4Range, URI_FAST, uriHelperSource, date, ipv4, dateTime, ipv6, hostname, uri, uriReference, uuid, time, noReserved, jsonPointer, relativeJsonPointer, uriTemplate, email, duration, uriChars, uriAuthority, uriCharsSource, uriAuthoritySource, iri, iriReference, idnEmail, iriSource, iriReferenceSource, idnEmailSource, dateSource, ipv4Source, dateTimeSource, ipv6Source, hostnameSource, uriSource, uuidSource, timeSource, noReservedSource, jsonPointerSource, relativeJsonPointerSource, uriTemplateSource, emailSource, durationSource };
|
package/lib/js-compiler.js
CHANGED
|
@@ -1567,7 +1567,7 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
1567
1567
|
const defSets = ctx.defFns ? Array.from(ctx.defFns.values()) : []
|
|
1568
1568
|
const needsGuard = ctx.usesRecursion || defSets.length > 0
|
|
1569
1569
|
|
|
1570
|
-
let body, hybridBody
|
|
1570
|
+
let body, hybridBody, tailBody
|
|
1571
1571
|
if (ctx.usesRecursion) {
|
|
1572
1572
|
// Self-recursive: a named body reached through the cycle guard, so data
|
|
1573
1573
|
// that points back at itself settles instead of exhausting the stack.
|
|
@@ -1577,15 +1577,21 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
1577
1577
|
body = `${decl}function _run(d){\n ${run}\n }\n return _run(d)`
|
|
1578
1578
|
// Hybrid: keep the boolean answer, wrap only the outer call
|
|
1579
1579
|
hybridBody = `${decl}function _run(d){\n ${run}\n }\n return _run(d)?R:E(d)`
|
|
1580
|
+
tailBody = `${decl}function _run(d){\n ${run}\n }\n return _run(d)&&__ataTail(d)`
|
|
1580
1581
|
} else if (defSets.length > 0) {
|
|
1581
1582
|
// No self-reference at the root, but a def on a cycle can still be
|
|
1582
1583
|
// re-entered with the same value; the guarded functions need their state.
|
|
1583
1584
|
const run = emitGuardedRun(`return _body(d)`, '', defSets)
|
|
1584
1585
|
body = `function _body(d){\n ${checkStr}\n return true\n }\n function _run(d){\n ${run}\n }\n return _run(d)`
|
|
1585
1586
|
hybridBody = `function _body(d){\n ${checkStr}\n return true\n }\n function _run(d){\n ${run}\n }\n return _run(d)?R:E(d)`
|
|
1587
|
+
tailBody = `function _body(d){\n ${checkStr}\n return true\n }\n function _run(d){\n ${run}\n }\n return _run(d)&&__ataTail(d)`
|
|
1586
1588
|
} else {
|
|
1587
1589
|
body = checkStr + '\n return true'
|
|
1588
1590
|
hybridBody = replaceTopLevel(checkStr + '\n return R')
|
|
1591
|
+
// An early accept inside the checks has to reach the tail too, so every
|
|
1592
|
+
// top-level `return true` becomes the tail call, by the same rewrite the
|
|
1593
|
+
// hybrid above relies on and test_hybrid_agreement holds to the suite.
|
|
1594
|
+
tailBody = replaceTopLevel(checkStr + '\n return true', 'return __ataTail(d)', 'return false')
|
|
1589
1595
|
}
|
|
1590
1596
|
|
|
1591
1597
|
const guardStr = needsGuard ? emitGuardState() : ''
|
|
@@ -1610,6 +1616,26 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
1610
1616
|
boolFn._hybridFactory = (R, E) => hybridFactory(...closureValues, R, E)
|
|
1611
1617
|
} catch {}
|
|
1612
1618
|
|
|
1619
|
+
// The same function with one more check where it would return true, for a
|
|
1620
|
+
// wrapper that enforces something the schema does not carry (see
|
|
1621
|
+
// Validator#_extendVerdict). Calling the check from here rather than from a
|
|
1622
|
+
// function around this one is the point: this function is too large for
|
|
1623
|
+
// the engine to inline, so a wrapper is a second call on every document,
|
|
1624
|
+
// and a rejection never reaches the check at all. Built on first request,
|
|
1625
|
+
// since almost no validator is extended. Null when code generation is
|
|
1626
|
+
// refused, and the caller composes instead.
|
|
1627
|
+
let tailFactory
|
|
1628
|
+
boolFn._withTail = (tail) => {
|
|
1629
|
+
if (tailFactory === undefined) {
|
|
1630
|
+
try {
|
|
1631
|
+
tailFactory = new Function(...closureNames, '__ataTail', `${preambleStr}return function(d){${tailBody}}`)
|
|
1632
|
+
} catch {
|
|
1633
|
+
tailFactory = null
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
return tailFactory === null ? null : tailFactory(...closureValues, tail)
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1613
1639
|
// Store source for standalone compilation. Regex declarations are NOT
|
|
1614
1640
|
// inlined into the body: they are carried by _closures and the emitters
|
|
1615
1641
|
// declare them once at load scope. Inlining them here put a
|
|
@@ -1663,7 +1689,7 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
1663
1689
|
|
|
1664
1690
|
// Replace top-level `return false` → `return E(d)` and `return true` → `return R`.
|
|
1665
1691
|
// Tracks function nesting depth to preserve nested function internals.
|
|
1666
|
-
function replaceTopLevel(code) {
|
|
1692
|
+
function replaceTopLevel(code, onTrue = 'return R', onFalse = 'return E(d)') {
|
|
1667
1693
|
let fnDepth = 0, result = '', i = 0
|
|
1668
1694
|
while (i < code.length) {
|
|
1669
1695
|
// A nested closure keeps its own returns. Both spellings count: a
|
|
@@ -1690,10 +1716,10 @@ function replaceTopLevel(code) {
|
|
|
1690
1716
|
i++
|
|
1691
1717
|
}
|
|
1692
1718
|
} else if (code.startsWith('return false', i)) {
|
|
1693
|
-
result +=
|
|
1719
|
+
result += onFalse
|
|
1694
1720
|
i += 12
|
|
1695
1721
|
} else if (code.startsWith('return true', i) && (i + 11 >= code.length || !/[a-zA-Z_$]/.test(code[i + 11]))) {
|
|
1696
|
-
result +=
|
|
1722
|
+
result += onTrue
|
|
1697
1723
|
i += 11
|
|
1698
1724
|
} else {
|
|
1699
1725
|
result += code[i]
|
|
@@ -3147,8 +3173,20 @@ const FORMAT_CODEGEN = {
|
|
|
3147
3173
|
'uri-reference': (v, isStr) => isStr
|
|
3148
3174
|
? `{${_formats.uriCharsSource(v, '0')}}`
|
|
3149
3175
|
: `if(typeof ${v}==='string'){${_formats.uriCharsSource(v, '0')}}`,
|
|
3150
|
-
ipv4:
|
|
3151
|
-
|
|
3176
|
+
ipv4: (v, isStr, ctx) => {
|
|
3177
|
+
if (!ctx) return _formats.ipv4Source(v, isStr)
|
|
3178
|
+
// Hoisted: a regex literal in the body would build a new RegExp per call.
|
|
3179
|
+
hoistOnce(ctx, '_ip4Hoisted', 'const _ip4=/' + _formats.IPV4.source + '/')
|
|
3180
|
+
return isStr ? `if(!_ip4.test(${v}))return false` : `if(typeof ${v}==='string'&&!_ip4.test(${v}))return false`
|
|
3181
|
+
},
|
|
3182
|
+
ipv6: (v, isStr, ctx) => {
|
|
3183
|
+
if (!ctx) return _formats.ipv6Source(v, isStr)
|
|
3184
|
+
hoistOnce(ctx, '_ip6Hoisted', 'const _ip6f=/' + _formats.IPV6_FULL.source + '/')
|
|
3185
|
+
// The fast accept jumps past the walk; the walk's `return false`s are what
|
|
3186
|
+
// the error generators rewrite, so they stay as they are.
|
|
3187
|
+
const body = `_ip6:{if(${v}.length>=15&&_ip6f.test(${v}))break _ip6;${_formats.ipv6Source(v, true).replace(/^\{|\}$/g, '')}}`
|
|
3188
|
+
return isStr ? body : `if(typeof ${v}==='string'){${body}}`
|
|
3189
|
+
},
|
|
3152
3190
|
hostname: _formats.hostnameSource,
|
|
3153
3191
|
}
|
|
3154
3192
|
|
|
@@ -3512,7 +3550,23 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
3512
3550
|
// first invalid document threw a ReferenceError instead of reporting.
|
|
3513
3551
|
const cvars = ctx.closureVars
|
|
3514
3552
|
const cvals = ctx.closureVals
|
|
3515
|
-
|
|
3553
|
+
// The helpers are constants: the uri tables, the compiled patterns, the
|
|
3554
|
+
// branch-collapse functions, the name sets. In `body` they sit inside the
|
|
3555
|
+
// function and were rebuilt on every call, which on a product schema with
|
|
3556
|
+
// six URLs and one anyOf cost about 450 ns before a single check ran, more
|
|
3557
|
+
// than every check and every error together. Here they run once, in a
|
|
3558
|
+
// factory, as the verdict function's preamble does. Only for the plain
|
|
3559
|
+
// shape: with $defs or recursion the helpers include functions that read
|
|
3560
|
+
// the per-call guard state declared in the body, so those keep `body` as
|
|
3561
|
+
// it is. `_errSource` below stays `body` either way, for the emitters.
|
|
3562
|
+
const hoistable = !ctx.usesRecursion && defSetsE.length === 0 && helpersE !== ''
|
|
3563
|
+
if (hoistable) {
|
|
3564
|
+
const factoryBody = helpersE + 'return function(d,_all){const _e=[];\n ' + checkStr +
|
|
3565
|
+
'\n return{valid:_e.length===0,errors:_e}}'
|
|
3566
|
+
const params = ctx.usesSafeRe ? [...cvars, '__ataSafeRe'] : [...cvars]
|
|
3567
|
+
const args = ctx.usesSafeRe ? [...cvals, compileSafe] : [...cvals]
|
|
3568
|
+
fn = new Function(...params, factoryBody)(...args)
|
|
3569
|
+
} else if (ctx.usesSafeRe) {
|
|
3516
3570
|
// Inlined helperCode references __ataSafeRe; bind the safe-regex factory.
|
|
3517
3571
|
// Standalone keeps the inline _errSource — the embed defines __ataSafeRe.
|
|
3518
3572
|
const built = new Function(...cvars, '__ataSafeRe', 'd', '_all', body)
|
package/lib/t.js
CHANGED
|
@@ -15,15 +15,15 @@ const OPTIONAL = Symbol.for('ata.t.optional');
|
|
|
15
15
|
const { attach: attachRefine } = require('./refine');
|
|
16
16
|
|
|
17
17
|
function string(opts) {
|
|
18
|
-
return
|
|
18
|
+
return opts === undefined ? { type: 'string' } : { type: 'string', ...opts };
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function number(opts) {
|
|
22
|
-
return
|
|
22
|
+
return opts === undefined ? { type: 'number' } : { type: 'number', ...opts };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
function integer(opts) {
|
|
26
|
-
return
|
|
26
|
+
return opts === undefined ? { type: 'integer' } : { type: 'integer', ...opts };
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function boolean() {
|
|
@@ -47,7 +47,7 @@ function enumOf(values) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function array(items, opts) {
|
|
50
|
-
return
|
|
50
|
+
return opts === undefined ? { type: 'array', items } : { type: 'array', items, ...opts };
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function tuple(items, opts) {
|
|
@@ -63,26 +63,46 @@ function tuple(items, opts) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function record(values, opts) {
|
|
66
|
-
return
|
|
66
|
+
return opts === undefined ? { type: 'object', additionalProperties: values } : { type: 'object', additionalProperties: values, ...opts };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function object(properties, opts) {
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
// The common case has no optional member: then `properties` is copied as it
|
|
71
|
+
// is and the fresh key list is `required`. The values come from one
|
|
72
|
+
// Object.values call because reading them back key by key was the larger
|
|
73
|
+
// part of this function's cost. A member marked optional sends the whole
|
|
74
|
+
// object down the slower path that rewrites it.
|
|
72
75
|
const keys = Object.keys(properties);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const values = Object.values(properties);
|
|
77
|
+
let anyOptional = false;
|
|
78
|
+
for (let i = 0; i < values.length; i++) {
|
|
79
|
+
const schema = values[i];
|
|
80
|
+
if (schema && schema[OPTIONAL] === true) { anyOptional = true; break; }
|
|
81
|
+
}
|
|
82
|
+
let out;
|
|
83
|
+
if (!anyOptional) {
|
|
84
|
+
out = keys.length
|
|
85
|
+
? { type: 'object', properties: { ...properties }, required: keys }
|
|
86
|
+
: { type: 'object', properties: { ...properties } };
|
|
87
|
+
} else {
|
|
88
|
+
const props = {};
|
|
89
|
+
const required = [];
|
|
90
|
+
for (let i = 0; i < keys.length; i++) {
|
|
91
|
+
const key = keys[i];
|
|
92
|
+
const schema = values[i];
|
|
93
|
+
if (schema && schema[OPTIONAL] === true) {
|
|
94
|
+
const { [OPTIONAL]: _drop, ...rest } = schema;
|
|
95
|
+
props[key] = rest;
|
|
96
|
+
} else {
|
|
97
|
+
props[key] = schema;
|
|
98
|
+
required.push(key);
|
|
99
|
+
}
|
|
81
100
|
}
|
|
101
|
+
out = required.length
|
|
102
|
+
? { type: 'object', properties: props, required }
|
|
103
|
+
: { type: 'object', properties: props };
|
|
82
104
|
}
|
|
83
|
-
|
|
84
|
-
if (required.length) out.required = required;
|
|
85
|
-
return Object.assign(out, opts);
|
|
105
|
+
return opts === undefined ? out : Object.assign(out, opts);
|
|
86
106
|
}
|
|
87
107
|
|
|
88
108
|
function optional(schema) {
|
package/lib/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"description": "JSON Schema validation that compiles for speed and still runs where code generation is blocked. Compiled and interpreted engines answer identically at 100% of the official suite. TypeScript inference, Standard Schema V1, and a build step that emits dependency-free modules.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"release:check": "node scripts/regen-safe-regex-source.js && node tests/test_pack_purity.js && node scripts/check-doc-coverage.js && node tests/test_error_codes_lock.js && node tests/test_safe_regex_source_sync.js && node tests/test_version_sync.js",
|
|
52
52
|
"build": "cmake-js build --target ata",
|
|
53
53
|
"rebuild": "cmake-js rebuild --target ata",
|
|
54
|
-
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_scanner_differential.js && node tests/test_validate_json_scanner_cost.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_exclusive_bounds.js && node tests/test_engine_differential.js && node tests/test_error_shape_differential.js && node tests/test_output_format.js && node tests/test_retry_message.js && node tests/test_describe_schema.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_option.js && node tests/test_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_error_accumulator_collision.js && node tests/test_uri_helper_parity.js && node tests/test_formats_single_pass.js && node tests/test_email_format_cost.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_deferred_def_scope.js && node tests/test_node_scoped_ctx_flags.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_strict_schema.js && node tests/test_aot_external_checks.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_unevaluated_error_path.js && node tests/test_unevaluated_error_shape.js && node tests/test_pattern_properties_errors.js && node tests/test_additional_properties_scaling.js && node tests/test_additional_props_combined_cost.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_error_order_ordinal.js && node tests/test_rejection_shape.js && node tests/test_lazy_normalization.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_single_pass_errors.js && node tests/test_lazy_json_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_defaults_own_keys.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_malformed_json_termination.js && node tests/test_data_positions.js && node tests/test_targeted_positions.js && node tests/test_position_map_cost.js && node tests/test_targeted_positions_cost.js && node tests/test_aot_staleness.js && node tests/test_file_positions.js && node tests/test_aot_positions_cost.js && node tests/test_schema_hash.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
|
|
54
|
+
"test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_no_eval.js && node tests/test_property_dependencies.js && node tests/test_v1_dialect.js && node tests/test_buffer_path_parity.js && node tests/test_buffer_gate.js && node tests/test_scanner_differential.js && node tests/test_validate_json_scanner_cost.js && node tests/test_buffer_reject_cost.js && node tests/test_nan_verdict.js && node tests/test_remove_additional_nested.js && node tests/test_exclusive_bounds.js && node tests/test_engine_differential.js && node tests/test_error_shape_differential.js && node tests/test_output_format.js && node tests/test_retry_message.js && node tests/test_describe_schema.js && node tests/test_aot_parse.js && node tests/test_draft7_semantics.js && node tests/test_metaschema_ref.js && node tests/test_pure_js_unsupported.js && node tests/test_native_load_order.js && node tests/test_pack_purity.js && node tests/test_make_native_package.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.js && node tests/test_native_loaded.js && node tests/test_safe_regex_source_sync.js && node tests/test_t_builder.js && node tests/test_async_refine.js && node tests/test_safe_regex.js && node tests/test_safe_regex_integration.js && node tests/test_aot_build.js && node tests/test_aot_differential.js && node tests/test_aot_cli_build.js && node tests/test_aot_cli_smoke.js && node tests/test_bundle_standalone.js && node tests/test_standalone_anyof.js && node tests/test_standalone_formats.js && node tests/test_aot_format_mode.js && node tests/test_aot_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_engine_routing.js && node tests/test_engine_option.js && node tests/test_compile_cache_order.js && node tests/test_engine_diagnostic.js && node tests/test_format_engine_parity.js && node tests/test_error_accumulator_collision.js && node tests/test_uri_helper_parity.js && node tests/test_uri_fast_path.js && node tests/test_extend_verdict.js && node tests/test_ipv4_regex.js && node tests/test_ipv6_fast_path.js && node tests/test_formats_single_pass.js && node tests/test_email_format_cost.js && node tests/test_format_single_error.js && node tests/test_defs_pointer_alias.js && node tests/test_cross_doc_root_ref.js && node tests/test_codegen_entrypoint_agreement.js && node tests/test_hybrid_agreement.js && node tests/test_codegen_edge_shapes.js && node tests/test_deferred_def_scope.js && node tests/test_node_scoped_ctx_flags.js && node tests/test_fast_slot_exhaustion.js && node tests/test_ajv_errors.js && node tests/test_custom_keywords.js && node tests/test_strict_schema.js && node tests/test_aot_external_checks.js && node tests/test_ajv_parity.js && node tests/test_user_format_error_path.js && node tests/test_unevaluated_error_path.js && node tests/test_unevaluated_error_shape.js && node tests/test_pattern_properties_errors.js && node tests/test_additional_properties_scaling.js && node tests/test_additional_props_combined_cost.js && node tests/test_no_input_mutation.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.js && node tests/test_error_code_lookup.js && node tests/test_error_order.js && node tests/test_error_order_ordinal.js && node tests/test_rejection_shape.js && node tests/test_lazy_normalization.js && node tests/test_value_equality.js && node tests/test_vocabulary.js && node tests/test_schema_scan.js && node tests/test_lazy_errors.js && node tests/test_single_pass_errors.js && node tests/test_lazy_json_errors.js && node tests/test_lazy_instance.js && node tests/test_cyclic_input.js && node tests/test_native_error_codes.js && node tests/test_verdict_preprocess.js && node tests/test_defaults_own_keys.js && node tests/test_plan_compiler.js && node tests/test_nullable.js && node tests/test_validate_and_parse.js && node tests/test_validate_data.js && node tests/test_enrich_error.js && node tests/test_enrich_received.js && node tests/test_rich_errors_optout.js && node tests/test_error_messages.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_malformed_json_termination.js && node tests/test_data_positions.js && node tests/test_targeted_positions.js && node tests/test_position_map_cost.js && node tests/test_targeted_positions_cost.js && node tests/test_aot_staleness.js && node tests/test_file_positions.js && node tests/test_aot_positions_cost.js && node tests/test_schema_hash.js && node tests/test_render_shared.js && node tests/test_renderers.js && node tests/test_additive_fields.js && node tests/test_diagnostic_source.js && node tests/test_diagnose.js && node tests/test_correlate.js && node tests/test_diagnostics_score.js && node tests/test_runtime_error_dx.js && node tests/test_aot_error_dx.js && node tests/test_abort_early.js && node tests/test_branch_collapse.js && node tests/test_suggestions.js && node tests/test_cli_validate.js && node tests/test_cli_version.js && node benchmark/bench_aot_size.mjs",
|
|
55
55
|
"bench:size": "node benchmark/bench_aot_size.mjs",
|
|
56
56
|
"test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
|
|
57
57
|
"test:compat": "node tests/test_compat.js",
|
|
@@ -121,13 +121,13 @@
|
|
|
121
121
|
"LICENSE"
|
|
122
122
|
],
|
|
123
123
|
"optionalDependencies": {
|
|
124
|
-
"@ata-validator/native-darwin-arm64": "1.
|
|
125
|
-
"@ata-validator/native-darwin-x64": "1.
|
|
126
|
-
"@ata-validator/native-linux-arm64-gnu": "1.
|
|
127
|
-
"@ata-validator/native-linux-arm64-musl": "1.
|
|
128
|
-
"@ata-validator/native-linux-x64-gnu": "1.
|
|
129
|
-
"@ata-validator/native-linux-x64-musl": "1.
|
|
130
|
-
"@ata-validator/native-win32-x64": "1.
|
|
124
|
+
"@ata-validator/native-darwin-arm64": "1.31.0",
|
|
125
|
+
"@ata-validator/native-darwin-x64": "1.31.0",
|
|
126
|
+
"@ata-validator/native-linux-arm64-gnu": "1.31.0",
|
|
127
|
+
"@ata-validator/native-linux-arm64-musl": "1.31.0",
|
|
128
|
+
"@ata-validator/native-linux-x64-gnu": "1.31.0",
|
|
129
|
+
"@ata-validator/native-linux-x64-musl": "1.31.0",
|
|
130
|
+
"@ata-validator/native-win32-x64": "1.31.0"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
133
|
"yaml": "^2.0.0"
|