ata-validator 1.31.3 → 1.32.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.d.ts +10 -0
- package/index.js +53 -0
- package/lib/aot-impl.js +1 -271
- package/lib/clone-emit.js +302 -0
- package/lib/interpreter.js +4 -4
- package/lib/js-compiler.js +12 -12
- package/lib/plan-compiler.js +1 -1
- package/lib/plan-source.js +3 -3
- package/lib/tier0.js +2 -2
- package/lib/version.js +1 -1
- package/package.json +9 -9
package/index.d.ts
CHANGED
|
@@ -497,6 +497,16 @@ export interface Validator<T = unknown> {
|
|
|
497
497
|
/** Parse JSON and validate against the schema. Returns the parsed value and the validation result. Works without the native addon. */
|
|
498
498
|
validateAndParse(jsonString: string | Buffer): ValidateAndParseResult<T>;
|
|
499
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Validate, then return a copy holding only the properties the schema declares, with defaults
|
|
502
|
+
* filled. The input is not modified. Throws an error named `AtaValidationError`, with `errors`,
|
|
503
|
+
* when the value is invalid, and a `TypeError` where the kept key set cannot be proven from the
|
|
504
|
+
* schema (a $ref it cannot inline, patternProperties, an object that allows unknown keys) or the
|
|
505
|
+
* validator rewrites input (`coerceTypes`, custom keywords). Same result as the `parse` export of
|
|
506
|
+
* a module built with `ata build`.
|
|
507
|
+
*/
|
|
508
|
+
parse(data: unknown): T;
|
|
509
|
+
|
|
500
510
|
/** Ultra-fast buffer validation via native addon */
|
|
501
511
|
isValid(input: Buffer | Uint8Array | string): boolean;
|
|
502
512
|
|
package/index.js
CHANGED
|
@@ -2895,6 +2895,59 @@ Validator.prototype._extendValidate = function (resolve) {
|
|
|
2895
2895
|
// closure, where wrapping the five entry points cost a closure per entry point,
|
|
2896
2896
|
// the wrappers themselves and an accessor, most of what building a wrapped
|
|
2897
2897
|
// validator took.
|
|
2898
|
+
// parse(data): validate, then return a copy holding only what the schema
|
|
2899
|
+
// declares, the way the parse() export of an ahead-of-time module does, with
|
|
2900
|
+
// the same emitter behind both. Building the copy from the schema's own key
|
|
2901
|
+
// list costs less than finding and deleting unknown keys, and leaves the
|
|
2902
|
+
// caller's object alone. Where the key set cannot be proven (a $ref it cannot
|
|
2903
|
+
// inline, patternProperties, an open object) the method declines with an
|
|
2904
|
+
// error instead of guessing, as the module ships no parse() there; so it does
|
|
2905
|
+
// under options that rewrite input, whose answers a copy would not reproduce.
|
|
2906
|
+
Validator.prototype.parse = function (data) {
|
|
2907
|
+
let fn = this._parseFn;
|
|
2908
|
+
if (fn === undefined) {
|
|
2909
|
+
fn = _buildParse(this);
|
|
2910
|
+
Object.defineProperty(this, '_parseFn', { value: fn, writable: true, configurable: true, enumerable: false });
|
|
2911
|
+
}
|
|
2912
|
+
return fn(data);
|
|
2913
|
+
};
|
|
2914
|
+
|
|
2915
|
+
function _buildParse(self) {
|
|
2916
|
+
const decline = (why) => () => {
|
|
2917
|
+
throw new TypeError(`parse() is not available for this validator: ${why}. Use validate() with removeAdditional instead.`);
|
|
2918
|
+
};
|
|
2919
|
+
const o = self._options;
|
|
2920
|
+
if (o.coerceTypes || o.removeAdditional === 'all' || self._usesKeywords || self._verdictTail !== null || self._validateTail !== null) {
|
|
2921
|
+
return decline('its options or keywords rewrite or extend what the schema checks');
|
|
2922
|
+
}
|
|
2923
|
+
const { cloneExprFor } = require('./lib/clone-emit');
|
|
2924
|
+
const expr = cloneExprFor(self._schemaObj);
|
|
2925
|
+
if (!expr) return decline('the set of keys to keep cannot be proven from the schema');
|
|
2926
|
+
let copy;
|
|
2927
|
+
try {
|
|
2928
|
+
// eslint-disable-next-line no-new-func
|
|
2929
|
+
copy = new Function('data', 'return ' + expr);
|
|
2930
|
+
} catch {
|
|
2931
|
+
return decline('code generation is not allowed here');
|
|
2932
|
+
}
|
|
2933
|
+
self._ensureCompiled();
|
|
2934
|
+
const verdict = self._jsFn;
|
|
2935
|
+
if (typeof verdict !== 'function') return decline('the schema has no generated verdict function');
|
|
2936
|
+
return (data) => {
|
|
2937
|
+
if (!verdict(data)) {
|
|
2938
|
+
const e = new Error('validation failed');
|
|
2939
|
+
e.name = 'AtaValidationError';
|
|
2940
|
+
let target = data;
|
|
2941
|
+
if (self._mutatesInput) {
|
|
2942
|
+
try { target = structuredClone(data); } catch { target = data; }
|
|
2943
|
+
}
|
|
2944
|
+
e.errors = self.validate(target).errors;
|
|
2945
|
+
throw e;
|
|
2946
|
+
}
|
|
2947
|
+
return copy(data);
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2898
2951
|
Validator.prototype._extendChecks = function (resolve) {
|
|
2899
2952
|
if (typeof resolve !== 'function') throw new TypeError('_extendChecks expects a function');
|
|
2900
2953
|
this._extendValidate(resolve);
|
package/lib/aot-impl.js
CHANGED
|
@@ -189,277 +189,7 @@ function emitFormatDecls(closures, mode, declKW) {
|
|
|
189
189
|
return { decls: out, exportsSetFormats: false };
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
|
|
193
|
-
// declares, dropping everything else. This is the sanitising counterpart to
|
|
194
|
-
// removeAdditional: instead of enumerating the input and deleting the keys
|
|
195
|
-
// that do not belong, it reads only the keys that do. That is O(schema)
|
|
196
|
-
// rather than O(input), it leaves the caller's object untouched, and it keeps
|
|
197
|
-
// one hidden class instead of walking the object through a transition per
|
|
198
|
-
// delete.
|
|
199
|
-
//
|
|
200
|
-
// Returns null when the clone cannot be proven exact, and the module then
|
|
201
|
-
// simply has no parse(). Declining is recoverable; a sanitiser that quietly
|
|
202
|
-
// drops a property the schema allows is not.
|
|
203
|
-
// Resolve a local JSON pointer ('#', '#/$defs/x', draft-7 '#/definitions/x',
|
|
204
|
-
// any '#/...' path) against the schema root. Returns null for anything else:
|
|
205
|
-
// external documents, anchors, URNs. Those stay with the runtime engines.
|
|
206
|
-
function resolveLocalPointer(root, ref) {
|
|
207
|
-
if (ref === '#') return root;
|
|
208
|
-
if (typeof ref !== 'string' || !ref.startsWith('#/')) return null;
|
|
209
|
-
let cur = root;
|
|
210
|
-
for (const raw of ref.slice(2).split('/')) {
|
|
211
|
-
let seg = raw.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
212
|
-
if (cur === null || typeof cur !== 'object') return null;
|
|
213
|
-
if (!Object.prototype.hasOwnProperty.call(cur, seg)) {
|
|
214
|
-
try { seg = decodeURIComponent(seg); } catch (_) { return null; }
|
|
215
|
-
if (!Object.prototype.hasOwnProperty.call(cur, seg)) return null;
|
|
216
|
-
}
|
|
217
|
-
cur = cur[seg];
|
|
218
|
-
}
|
|
219
|
-
return cur;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Inline local, acyclic $refs before the clone proof runs, so a generated
|
|
223
|
-
// schema ($defs + $ref, the shape every schema generator emits) can still get
|
|
224
|
-
// a parse(). Only a node that is exactly a $ref plus annotations is replaced;
|
|
225
|
-
// a $ref with a constraining sibling, a cycle, an unresolvable target or an
|
|
226
|
-
// external reference is left in place, and emitClone declines it as before.
|
|
227
|
-
// The walk never mutates its input and never descends into data positions
|
|
228
|
-
// (default, const, enum, examples), where an object holding a "$ref" key is
|
|
229
|
-
// a value, not a schema.
|
|
230
|
-
const _INLINE_ANNOTATIONS = new Set(['$ref', 'title', 'description', '$comment', 'examples', 'deprecated', 'readOnly', 'writeOnly', 'default']);
|
|
231
|
-
const _INLINE_DATA_KEYS = new Set(['default', 'const', 'enum', 'examples']);
|
|
232
|
-
function inlineRefsForClone(root) {
|
|
233
|
-
const state = { budget: 512 };
|
|
234
|
-
// `inRef` marks content brought in from behind a reference. The runtime's
|
|
235
|
-
// useDefaults fills a default written next to the $ref, and does not fill
|
|
236
|
-
// defaults written inside the referenced definition, so the inlined copy
|
|
237
|
-
// keeps the first and drops the second. parse() must stay exactly as
|
|
238
|
-
// generous as validate(); a parse that fills more is a disagreement, not a
|
|
239
|
-
// feature.
|
|
240
|
-
const walk = (node, active, inRef) => {
|
|
241
|
-
if (!node || typeof node !== 'object') return node;
|
|
242
|
-
if (Array.isArray(node)) return node.map((n) => walk(n, active, inRef));
|
|
243
|
-
let cur = node;
|
|
244
|
-
let entered = false;
|
|
245
|
-
let siblingDefault;
|
|
246
|
-
while (cur && typeof cur === 'object' && !Array.isArray(cur) && typeof cur.$ref === 'string') {
|
|
247
|
-
for (const k of Object.keys(cur)) if (!_INLINE_ANNOTATIONS.has(k)) return node;
|
|
248
|
-
if (active.has(cur.$ref) || state.budget-- <= 0) return node;
|
|
249
|
-
const target = resolveLocalPointer(root, cur.$ref);
|
|
250
|
-
if (!target || typeof target !== 'object' || Array.isArray(target)) return node;
|
|
251
|
-
if (!entered && !inRef && cur.default !== undefined) siblingDefault = cur.default;
|
|
252
|
-
active = new Set(active);
|
|
253
|
-
active.add(cur.$ref);
|
|
254
|
-
cur = target;
|
|
255
|
-
entered = true;
|
|
256
|
-
}
|
|
257
|
-
const strip = inRef || entered;
|
|
258
|
-
const out = {};
|
|
259
|
-
for (const k of Object.keys(cur)) {
|
|
260
|
-
if (strip && k === 'default') continue;
|
|
261
|
-
const v = cur[k];
|
|
262
|
-
if (_INLINE_DATA_KEYS.has(k) || k === '$defs' || k === 'definitions') { out[k] = v; continue; }
|
|
263
|
-
out[k] = walk(v, active, strip);
|
|
264
|
-
}
|
|
265
|
-
if (siblingDefault !== undefined) out.default = siblingDefault;
|
|
266
|
-
return out;
|
|
267
|
-
};
|
|
268
|
-
return walk(root, new Set(), false);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function emitClone(node, access, depth) {
|
|
272
|
-
if (!node || typeof node !== 'object') return null;
|
|
273
|
-
if (depth > 12) return null;
|
|
274
|
-
// Only plain object nodes are cloneable, and only when the schema names
|
|
275
|
-
// every property it keeps. A $ref means the set of allowed keys is not
|
|
276
|
-
// this node's to decide. In-place applicators (allOf, anyOf, oneOf,
|
|
277
|
-
// if/then/else) are admitted under the same proof the unevaluated* error
|
|
278
|
-
// generators use: every property name they contribute must already be
|
|
279
|
-
// declared in this node's own `properties`, so they constrain values but
|
|
280
|
-
// never widen the key set. `not` never widens it. `unevaluatedProperties`
|
|
281
|
-
// is admitted only as `false` under that proof, where it is exactly
|
|
282
|
-
// `additionalProperties: false`.
|
|
283
|
-
if (node.$ref || node.patternProperties || node.additionalProperties === true) {
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
// additionalProperties as a schema is the record shape (z.record and every
|
|
287
|
-
// generator's map type): the key set is open by declaration, so keeping
|
|
288
|
-
// every key is exact, and every undeclared value is rebuilt against that
|
|
289
|
-
// one schema. That is a proof, not a guess, so it is admitted. What stays
|
|
290
|
-
// out: applicators next to a record (a branch could constrain some keys'
|
|
291
|
-
// values beyond the record schema, and the rebuild would ignore it), and
|
|
292
|
-
// `additionalProperties: true`, where the value is unconstrained and this
|
|
293
|
-
// pass only copies what it can name. An open object with no
|
|
294
|
-
// `additionalProperties` at all also stays out, for the original reason:
|
|
295
|
-
// validate() accepts its unknown keys, so a parse() that strips them would
|
|
296
|
-
// silently drop allowed data, and one that keeps them raw would not be a
|
|
297
|
-
// sanitiser.
|
|
298
|
-
const apSchema = (node.additionalProperties && typeof node.additionalProperties === 'object')
|
|
299
|
-
? node.additionalProperties
|
|
300
|
-
: null;
|
|
301
|
-
if (apSchema) {
|
|
302
|
-
if (node.unevaluatedProperties !== undefined) return null;
|
|
303
|
-
if (node.allOf || node.anyOf || node.oneOf || node.if || node.then || node.else ||
|
|
304
|
-
node.dependentSchemas !== undefined || node.dependencies !== undefined ||
|
|
305
|
-
node.$dynamicRef !== undefined || node.$recursiveRef !== undefined) return null;
|
|
306
|
-
}
|
|
307
|
-
if (node.unevaluatedProperties !== undefined && node.unevaluatedProperties !== false) return null;
|
|
308
|
-
if (node.type !== 'object') return null;
|
|
309
|
-
if (!node.properties && !apSchema) return null;
|
|
310
|
-
const keys = node.properties ? Object.keys(node.properties) : [];
|
|
311
|
-
if (keys.length === 0 && !apSchema) return null;
|
|
312
|
-
if (node.allOf || node.anyOf || node.oneOf || node.if || node.then || node.else ||
|
|
313
|
-
node.unevaluatedProperties === false) {
|
|
314
|
-
if (node.$dynamicRef !== undefined || node.$recursiveRef !== undefined ||
|
|
315
|
-
node.dependentSchemas !== undefined || node.dependencies !== undefined) return null;
|
|
316
|
-
const names = new Set();
|
|
317
|
-
const state = { prefix: 0 };
|
|
318
|
-
for (const k of ['allOf', 'anyOf', 'oneOf']) {
|
|
319
|
-
if (node[k] !== undefined) {
|
|
320
|
-
if (!Array.isArray(node[k])) return null;
|
|
321
|
-
for (const b of node[k]) if (!unevalContributions(b, 'props', names, state, 0)) return null;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
for (const k of ['if', 'then', 'else']) {
|
|
325
|
-
if (node[k] !== undefined && !unevalContributions(node[k], 'props', names, state, 0)) return null;
|
|
326
|
-
}
|
|
327
|
-
for (const n of names) if (!Object.prototype.hasOwnProperty.call(node.properties, n)) return null;
|
|
328
|
-
}
|
|
329
|
-
const required = new Set(Array.isArray(node.required) ? node.required : []);
|
|
330
|
-
|
|
331
|
-
const fixed = [];
|
|
332
|
-
const conditional = [];
|
|
333
|
-
for (const key of keys) {
|
|
334
|
-
const prop = node.properties[key];
|
|
335
|
-
if (!prop || typeof prop !== 'object') return null;
|
|
336
|
-
if (prop.$ref) return null;
|
|
337
|
-
// A property that is required and carries a default is the one place the
|
|
338
|
-
// runtime and a raw-input validation disagree: the runtime fills the
|
|
339
|
-
// default before checking `required`, so `{}` validates there and would
|
|
340
|
-
// throw here. Declining keeps parse() exactly as strict as validate().
|
|
341
|
-
if (required.has(key) && prop.default !== undefined) return null;
|
|
342
|
-
// A default the property's own schema rejects would make parse() hand
|
|
343
|
-
// back a document validate() refuses: the runtime fills defaults before
|
|
344
|
-
// validating, so it catches the bad default, and a raw-input check here
|
|
345
|
-
// never sees it. Probe the default at emit time and decline when it does
|
|
346
|
-
// not hold, or when the subschema cannot be compiled to check it.
|
|
347
|
-
if (prop.default !== undefined) {
|
|
348
|
-
let probe = null;
|
|
349
|
-
try { probe = compileToJSCodegen(prop, null, null); } catch (_) { probe = null; }
|
|
350
|
-
if (!probe || !probe(prop.default)) return null;
|
|
351
|
-
}
|
|
352
|
-
const read = `${access}[${JSON.stringify(key)}]`;
|
|
353
|
-
const value = emitValueExpr(prop, read, depth);
|
|
354
|
-
if (value === null) return null;
|
|
355
|
-
// Computed key syntax: a plain `"__proto__":` in an object literal is
|
|
356
|
-
// the prototype slot, not a property; `["__proto__"]:` is a property.
|
|
357
|
-
if (required.has(key)) fixed.push(`[${JSON.stringify(key)}]: ${value}`);
|
|
358
|
-
else conditional.push({ key, value, dflt: prop.default });
|
|
359
|
-
}
|
|
360
|
-
if (fixed.length === 0 && conditional.length === 0 && !apSchema) return null;
|
|
361
|
-
|
|
362
|
-
const tmp = `_c${depth}`;
|
|
363
|
-
// The record loop: every key of the input that is not a declared property
|
|
364
|
-
// is kept, its value rebuilt against the additionalProperties schema. The
|
|
365
|
-
// input is already validated, so every such value satisfies that schema;
|
|
366
|
-
// the rebuild only strips inside it. Defaults under a record value are
|
|
367
|
-
// dropped before emission because the runtime's useDefaults does not fill
|
|
368
|
-
// them there, and parse() must equal validate().data, not improve on it.
|
|
369
|
-
let recordLoop = '';
|
|
370
|
-
if (apSchema) {
|
|
371
|
-
const kVar = `_k${depth}`;
|
|
372
|
-
const vVar = `_v${depth}`;
|
|
373
|
-
const valueExpr = emitValueExpr(stripDefaultsDeep(apSchema), vVar, depth);
|
|
374
|
-
if (valueExpr === null) return null;
|
|
375
|
-
let skip = '';
|
|
376
|
-
let declSet = '';
|
|
377
|
-
if (keys.length > 8) {
|
|
378
|
-
declSet = `const _d${depth} = new Set(${JSON.stringify(keys)}); `;
|
|
379
|
-
skip = `if (_d${depth}.has(${kVar})) continue; `;
|
|
380
|
-
} else if (keys.length > 0) {
|
|
381
|
-
skip = `if (${keys.map((k) => `${kVar} === ${JSON.stringify(k)}`).join(' || ')}) continue; `;
|
|
382
|
-
}
|
|
383
|
-
// The key comes from the input, so it can be the string "__proto__" (an
|
|
384
|
-
// own key only JSON.parse writes); assignment would hit the prototype
|
|
385
|
-
// setter, so that one key goes through defineProperty.
|
|
386
|
-
recordLoop = ` ${declSet}for (const ${kVar} of Object.keys(${access})) { ${skip}const ${vVar} = ${access}[${kVar}]; if (${kVar} === "__proto__") Object.defineProperty(${tmp}, ${kVar}, { value: ${valueExpr}, writable: true, enumerable: true, configurable: true }); else ${tmp}[${kVar}] = ${valueExpr}; }`;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const literal = `{ ${fixed.join(', ')} }`;
|
|
390
|
-
if (conditional.length === 0 && !recordLoop) return literal;
|
|
391
|
-
// Optional properties are added only when present, so the result never
|
|
392
|
-
// gains a key the input did not have.
|
|
393
|
-
const adds = conditional
|
|
394
|
-
.map(({ key, value, dflt }) => {
|
|
395
|
-
// Object.hasOwn, not `in`: `in` also answers for what the instance
|
|
396
|
-
// inherits, so a property named `constructor` would copy the inherited
|
|
397
|
-
// function into the output that validate().data does not carry. And a
|
|
398
|
-
// key named `__proto__` cannot be written by assignment, which hits
|
|
399
|
-
// the prototype setter; defineProperty writes an own key.
|
|
400
|
-
const assign = key === '__proto__'
|
|
401
|
-
? `Object.defineProperty(${tmp}, ${JSON.stringify(key)}, { value: ${value}, writable: true, enumerable: true, configurable: true });`
|
|
402
|
-
: `${tmp}[${JSON.stringify(key)}] = ${value};`;
|
|
403
|
-
const set = `if (Object.hasOwn(${access}, ${JSON.stringify(key)})) ${assign}`;
|
|
404
|
-
// The default is emitted as a literal inside the call, so an object or
|
|
405
|
-
// array default is a fresh value on every parse, never shared state.
|
|
406
|
-
if (dflt === undefined) return set;
|
|
407
|
-
const setDflt = key === '__proto__'
|
|
408
|
-
? `Object.defineProperty(${tmp}, ${JSON.stringify(key)}, { value: ${JSON.stringify(dflt)}, writable: true, enumerable: true, configurable: true });`
|
|
409
|
-
: `${tmp}[${JSON.stringify(key)}] = ${JSON.stringify(dflt)};`;
|
|
410
|
-
return `${set} else ${setDflt}`;
|
|
411
|
-
})
|
|
412
|
-
.join(' ');
|
|
413
|
-
return `(function(){ const ${tmp} = ${literal}; ${adds}${recordLoop} return ${tmp}; })()`;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// The value dispatch shared by declared properties and the record loop. A
|
|
417
|
-
// value is copyable only when the emitter can name everything that may live
|
|
418
|
-
// under it: a primitive holds nothing, an object with declared properties or
|
|
419
|
-
// a record schema is rebuilt the same way, an array of primitives has
|
|
420
|
-
// nowhere to hide a key. A $ref, a composition or an untyped node could all
|
|
421
|
-
// carry keys this code cannot see, and copying the reference would smuggle
|
|
422
|
-
// them into a value that claims to be sanitised, so the caller declines the
|
|
423
|
-
// whole clone instead.
|
|
424
|
-
// Drop every schema-position `default` in a subtree, leaving data positions
|
|
425
|
-
// (const, enum, examples) untouched. Used on a record's value schema, where
|
|
426
|
-
// the runtime's useDefaults fills nothing.
|
|
427
|
-
function stripDefaultsDeep(node) {
|
|
428
|
-
if (!node || typeof node !== 'object') return node;
|
|
429
|
-
if (Array.isArray(node)) return node.map(stripDefaultsDeep);
|
|
430
|
-
const out = {};
|
|
431
|
-
for (const k of Object.keys(node)) {
|
|
432
|
-
if (k === 'default') continue;
|
|
433
|
-
out[k] = _INLINE_DATA_KEYS.has(k) ? node[k] : stripDefaultsDeep(node[k]);
|
|
434
|
-
}
|
|
435
|
-
return out;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const _CLONE_PRIMITIVE = new Set(['string', 'number', 'integer', 'boolean', 'null']);
|
|
439
|
-
function emitValueExpr(prop, read, depth) {
|
|
440
|
-
if (!prop || typeof prop !== 'object') return null;
|
|
441
|
-
if (prop.$ref) return null;
|
|
442
|
-
const types = Array.isArray(prop.type) ? prop.type : [prop.type];
|
|
443
|
-
if (types.every((t) => _CLONE_PRIMITIVE.has(t))) return read;
|
|
444
|
-
const isRecordable = (s) => s.properties ||
|
|
445
|
-
(s.additionalProperties && typeof s.additionalProperties === 'object');
|
|
446
|
-
if (prop.type === 'object' && isRecordable(prop)) {
|
|
447
|
-
return emitClone(prop, read, depth + 1);
|
|
448
|
-
}
|
|
449
|
-
if (prop.type === 'array' && prop.items && typeof prop.items === 'object' &&
|
|
450
|
-
!Array.isArray(prop.items)) {
|
|
451
|
-
const it = prop.items;
|
|
452
|
-
const itemTypes = Array.isArray(it.type) ? it.type : [it.type];
|
|
453
|
-
if (itemTypes.every((t) => _CLONE_PRIMITIVE.has(t))) return read;
|
|
454
|
-
if (it.type === 'object' && isRecordable(it)) {
|
|
455
|
-
const el = '_e' + depth;
|
|
456
|
-
const inner = emitClone(it, el, depth + 1);
|
|
457
|
-
if (!inner) return null;
|
|
458
|
-
return `${read}.map((${el}) => (${inner}))`;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
return null;
|
|
462
|
-
}
|
|
192
|
+
const { emitClone, inlineRefsForClone } = require('./clone-emit');
|
|
463
193
|
|
|
464
194
|
function toStandaloneModule(validator, opts) {
|
|
465
195
|
assertEmittable(validator, 'toStandaloneModule');
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// The copy behind parse(): shared by the parse() export of an ahead-of-time
|
|
4
|
+
// module (lib/aot-impl.js) and the runtime's Validator#parse (index.js). It
|
|
5
|
+
// lives apart from the module emitter so the runtime can use it without
|
|
6
|
+
// pulling the emitter, and the safe-regex source it embeds, into a browser
|
|
7
|
+
// bundle.
|
|
8
|
+
|
|
9
|
+
const { compileToJSCodegen, unevalContributions } = require('./js-compiler');
|
|
10
|
+
|
|
11
|
+
// Emit an expression that rebuilds `access` from the properties the schema
|
|
12
|
+
// declares, dropping everything else. This is the sanitising counterpart to
|
|
13
|
+
// removeAdditional: instead of enumerating the input and deleting the keys
|
|
14
|
+
// that do not belong, it reads only the keys that do. That is O(schema)
|
|
15
|
+
// rather than O(input), it leaves the caller's object untouched, and it keeps
|
|
16
|
+
// one hidden class instead of walking the object through a transition per
|
|
17
|
+
// delete.
|
|
18
|
+
//
|
|
19
|
+
// Returns null when the clone cannot be proven exact, and the module then
|
|
20
|
+
// simply has no parse(). Declining is recoverable; a sanitiser that quietly
|
|
21
|
+
// drops a property the schema allows is not.
|
|
22
|
+
// Resolve a local JSON pointer ('#', '#/$defs/x', draft-7 '#/definitions/x',
|
|
23
|
+
// any '#/...' path) against the schema root. Returns null for anything else:
|
|
24
|
+
// external documents, anchors, URNs. Those stay with the runtime engines.
|
|
25
|
+
function resolveLocalPointer(root, ref) {
|
|
26
|
+
if (ref === '#') return root;
|
|
27
|
+
if (typeof ref !== 'string' || !ref.startsWith('#/')) return null;
|
|
28
|
+
let cur = root;
|
|
29
|
+
for (const raw of ref.slice(2).split('/')) {
|
|
30
|
+
let seg = raw.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
31
|
+
if (cur === null || typeof cur !== 'object') return null;
|
|
32
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg)) {
|
|
33
|
+
try { seg = decodeURIComponent(seg); } catch (_) { return null; }
|
|
34
|
+
if (!Object.prototype.hasOwnProperty.call(cur, seg)) return null;
|
|
35
|
+
}
|
|
36
|
+
cur = cur[seg];
|
|
37
|
+
}
|
|
38
|
+
return cur;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Inline local, acyclic $refs before the clone proof runs, so a generated
|
|
42
|
+
// schema ($defs + $ref, the shape every schema generator emits) can still get
|
|
43
|
+
// a parse(). Only a node that is exactly a $ref plus annotations is replaced;
|
|
44
|
+
// a $ref with a constraining sibling, a cycle, an unresolvable target or an
|
|
45
|
+
// external reference is left in place, and emitClone declines it as before.
|
|
46
|
+
// The walk never mutates its input and never descends into data positions
|
|
47
|
+
// (default, const, enum, examples), where an object holding a "$ref" key is
|
|
48
|
+
// a value, not a schema.
|
|
49
|
+
const _INLINE_ANNOTATIONS = new Set(['$ref', 'title', 'description', '$comment', 'examples', 'deprecated', 'readOnly', 'writeOnly', 'default']);
|
|
50
|
+
const _INLINE_DATA_KEYS = new Set(['default', 'const', 'enum', 'examples']);
|
|
51
|
+
function inlineRefsForClone(root) {
|
|
52
|
+
const state = { budget: 512 };
|
|
53
|
+
// `inRef` marks content brought in from behind a reference. The runtime's
|
|
54
|
+
// useDefaults fills a default written next to the $ref, and does not fill
|
|
55
|
+
// defaults written inside the referenced definition, so the inlined copy
|
|
56
|
+
// keeps the first and drops the second. parse() must stay exactly as
|
|
57
|
+
// generous as validate(); a parse that fills more is a disagreement, not a
|
|
58
|
+
// feature.
|
|
59
|
+
const walk = (node, active, inRef) => {
|
|
60
|
+
if (!node || typeof node !== 'object') return node;
|
|
61
|
+
if (Array.isArray(node)) return node.map((n) => walk(n, active, inRef));
|
|
62
|
+
let cur = node;
|
|
63
|
+
let entered = false;
|
|
64
|
+
let siblingDefault;
|
|
65
|
+
while (cur && typeof cur === 'object' && !Array.isArray(cur) && typeof cur.$ref === 'string') {
|
|
66
|
+
for (const k of Object.keys(cur)) if (!_INLINE_ANNOTATIONS.has(k)) return node;
|
|
67
|
+
if (active.has(cur.$ref) || state.budget-- <= 0) return node;
|
|
68
|
+
const target = resolveLocalPointer(root, cur.$ref);
|
|
69
|
+
if (!target || typeof target !== 'object' || Array.isArray(target)) return node;
|
|
70
|
+
if (!entered && !inRef && cur.default !== undefined) siblingDefault = cur.default;
|
|
71
|
+
active = new Set(active);
|
|
72
|
+
active.add(cur.$ref);
|
|
73
|
+
cur = target;
|
|
74
|
+
entered = true;
|
|
75
|
+
}
|
|
76
|
+
const strip = inRef || entered;
|
|
77
|
+
const out = {};
|
|
78
|
+
for (const k of Object.keys(cur)) {
|
|
79
|
+
if (strip && k === 'default') continue;
|
|
80
|
+
const v = cur[k];
|
|
81
|
+
if (_INLINE_DATA_KEYS.has(k) || k === '$defs' || k === 'definitions') { out[k] = v; continue; }
|
|
82
|
+
out[k] = walk(v, active, strip);
|
|
83
|
+
}
|
|
84
|
+
if (siblingDefault !== undefined) out.default = siblingDefault;
|
|
85
|
+
return out;
|
|
86
|
+
};
|
|
87
|
+
return walk(root, new Set(), false);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const PROTO_KEY_NAMES = new Set([...Object.getOwnPropertyNames(Object.prototype), '__proto__']);
|
|
91
|
+
|
|
92
|
+
function emitClone(node, access, depth) {
|
|
93
|
+
if (!node || typeof node !== 'object') return null;
|
|
94
|
+
if (depth > 12) return null;
|
|
95
|
+
// Only plain object nodes are cloneable, and only when the schema names
|
|
96
|
+
// every property it keeps. A $ref means the set of allowed keys is not
|
|
97
|
+
// this node's to decide. In-place applicators (allOf, anyOf, oneOf,
|
|
98
|
+
// if/then/else) are admitted under the same proof the unevaluated* error
|
|
99
|
+
// generators use: every property name they contribute must already be
|
|
100
|
+
// declared in this node's own `properties`, so they constrain values but
|
|
101
|
+
// never widen the key set. `not` never widens it. `unevaluatedProperties`
|
|
102
|
+
// is admitted only as `false` under that proof, where it is exactly
|
|
103
|
+
// `additionalProperties: false`.
|
|
104
|
+
if (node.$ref || node.patternProperties || node.additionalProperties === true) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
// additionalProperties as a schema is the record shape (z.record and every
|
|
108
|
+
// generator's map type): the key set is open by declaration, so keeping
|
|
109
|
+
// every key is exact, and every undeclared value is rebuilt against that
|
|
110
|
+
// one schema. That is a proof, not a guess, so it is admitted. What stays
|
|
111
|
+
// out: applicators next to a record (a branch could constrain some keys'
|
|
112
|
+
// values beyond the record schema, and the rebuild would ignore it), and
|
|
113
|
+
// `additionalProperties: true`, where the value is unconstrained and this
|
|
114
|
+
// pass only copies what it can name. An open object with no
|
|
115
|
+
// `additionalProperties` at all also stays out, for the original reason:
|
|
116
|
+
// validate() accepts its unknown keys, so a parse() that strips them would
|
|
117
|
+
// silently drop allowed data, and one that keeps them raw would not be a
|
|
118
|
+
// sanitiser.
|
|
119
|
+
const apSchema = (node.additionalProperties && typeof node.additionalProperties === 'object')
|
|
120
|
+
? node.additionalProperties
|
|
121
|
+
: null;
|
|
122
|
+
if (apSchema) {
|
|
123
|
+
if (node.unevaluatedProperties !== undefined) return null;
|
|
124
|
+
if (node.allOf || node.anyOf || node.oneOf || node.if || node.then || node.else ||
|
|
125
|
+
node.dependentSchemas !== undefined || node.dependencies !== undefined ||
|
|
126
|
+
node.$dynamicRef !== undefined || node.$recursiveRef !== undefined) return null;
|
|
127
|
+
}
|
|
128
|
+
if (node.unevaluatedProperties !== undefined && node.unevaluatedProperties !== false) return null;
|
|
129
|
+
if (node.type !== 'object') return null;
|
|
130
|
+
if (!node.properties && !apSchema) return null;
|
|
131
|
+
const keys = node.properties ? Object.keys(node.properties) : [];
|
|
132
|
+
if (keys.length === 0 && !apSchema) return null;
|
|
133
|
+
if (node.allOf || node.anyOf || node.oneOf || node.if || node.then || node.else ||
|
|
134
|
+
node.unevaluatedProperties === false) {
|
|
135
|
+
if (node.$dynamicRef !== undefined || node.$recursiveRef !== undefined ||
|
|
136
|
+
node.dependentSchemas !== undefined || node.dependencies !== undefined) return null;
|
|
137
|
+
const names = new Set();
|
|
138
|
+
const state = { prefix: 0 };
|
|
139
|
+
for (const k of ['allOf', 'anyOf', 'oneOf']) {
|
|
140
|
+
if (node[k] !== undefined) {
|
|
141
|
+
if (!Array.isArray(node[k])) return null;
|
|
142
|
+
for (const b of node[k]) if (!unevalContributions(b, 'props', names, state, 0)) return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
for (const k of ['if', 'then', 'else']) {
|
|
146
|
+
if (node[k] !== undefined && !unevalContributions(node[k], 'props', names, state, 0)) return null;
|
|
147
|
+
}
|
|
148
|
+
for (const n of names) if (!Object.prototype.hasOwnProperty.call(node.properties, n)) return null;
|
|
149
|
+
}
|
|
150
|
+
const required = new Set(Array.isArray(node.required) ? node.required : []);
|
|
151
|
+
|
|
152
|
+
const fixed = [];
|
|
153
|
+
const conditional = [];
|
|
154
|
+
for (const key of keys) {
|
|
155
|
+
const prop = node.properties[key];
|
|
156
|
+
if (!prop || typeof prop !== 'object') return null;
|
|
157
|
+
if (prop.$ref) return null;
|
|
158
|
+
// A property that is required and carries a default is the one place the
|
|
159
|
+
// runtime and a raw-input validation disagree: the runtime fills the
|
|
160
|
+
// default before checking `required`, so `{}` validates there and would
|
|
161
|
+
// throw here. Declining keeps parse() exactly as strict as validate().
|
|
162
|
+
if (required.has(key) && prop.default !== undefined) return null;
|
|
163
|
+
// A default the property's own schema rejects would make parse() hand
|
|
164
|
+
// back a document validate() refuses: the runtime fills defaults before
|
|
165
|
+
// validating, so it catches the bad default, and a raw-input check here
|
|
166
|
+
// never sees it. Probe the default at emit time and decline when it does
|
|
167
|
+
// not hold, or when the subschema cannot be compiled to check it.
|
|
168
|
+
if (prop.default !== undefined) {
|
|
169
|
+
let probe = null;
|
|
170
|
+
try { probe = compileToJSCodegen(prop, null, null); } catch (_) { probe = null; }
|
|
171
|
+
if (!probe || !probe(prop.default)) return null;
|
|
172
|
+
}
|
|
173
|
+
const read = `${access}[${JSON.stringify(key)}]`;
|
|
174
|
+
const value = emitValueExpr(prop, read, depth);
|
|
175
|
+
if (value === null) return null;
|
|
176
|
+
// A plain `"__proto__":` in an object literal is the prototype slot, not
|
|
177
|
+
// a property; `["__proto__"]:` is a property. Only that key is written as
|
|
178
|
+
// a computed one: a literal with computed keys cannot use the engine's
|
|
179
|
+
// literal boilerplate and is built key by key, which made the copy cost
|
|
180
|
+
// about as much as the validation in front of it.
|
|
181
|
+
if (required.has(key)) fixed.push(key === '__proto__' ? `[${JSON.stringify(key)}]: ${value}` : `${JSON.stringify(key)}: ${value}`);
|
|
182
|
+
else conditional.push({ key, value, dflt: prop.default });
|
|
183
|
+
}
|
|
184
|
+
if (fixed.length === 0 && conditional.length === 0 && !apSchema) return null;
|
|
185
|
+
|
|
186
|
+
const tmp = `_c${depth}`;
|
|
187
|
+
// The record loop: every key of the input that is not a declared property
|
|
188
|
+
// is kept, its value rebuilt against the additionalProperties schema. The
|
|
189
|
+
// input is already validated, so every such value satisfies that schema;
|
|
190
|
+
// the rebuild only strips inside it. Defaults under a record value are
|
|
191
|
+
// dropped before emission because the runtime's useDefaults does not fill
|
|
192
|
+
// them there, and parse() must equal validate().data, not improve on it.
|
|
193
|
+
let recordLoop = '';
|
|
194
|
+
if (apSchema) {
|
|
195
|
+
const kVar = `_k${depth}`;
|
|
196
|
+
const vVar = `_v${depth}`;
|
|
197
|
+
const valueExpr = emitValueExpr(stripDefaultsDeep(apSchema), vVar, depth);
|
|
198
|
+
if (valueExpr === null) return null;
|
|
199
|
+
let skip = '';
|
|
200
|
+
let declSet = '';
|
|
201
|
+
if (keys.length > 8) {
|
|
202
|
+
declSet = `const _d${depth} = new Set(${JSON.stringify(keys)}); `;
|
|
203
|
+
skip = `if (_d${depth}.has(${kVar})) continue; `;
|
|
204
|
+
} else if (keys.length > 0) {
|
|
205
|
+
skip = `if (${keys.map((k) => `${kVar} === ${JSON.stringify(k)}`).join(' || ')}) continue; `;
|
|
206
|
+
}
|
|
207
|
+
// The key comes from the input, so it can be the string "__proto__" (an
|
|
208
|
+
// own key only JSON.parse writes); assignment would hit the prototype
|
|
209
|
+
// setter, so that one key goes through defineProperty.
|
|
210
|
+
recordLoop = ` ${declSet}for (const ${kVar} of Object.keys(${access})) { ${skip}const ${vVar} = ${access}[${kVar}]; if (${kVar} === "__proto__") Object.defineProperty(${tmp}, ${kVar}, { value: ${valueExpr}, writable: true, enumerable: true, configurable: true }); else ${tmp}[${kVar}] = ${valueExpr}; }`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const literal = `{ ${fixed.join(', ')} }`;
|
|
214
|
+
if (conditional.length === 0 && !recordLoop) return literal;
|
|
215
|
+
// Optional properties are added only when present, so the result never
|
|
216
|
+
// gains a key the input did not have.
|
|
217
|
+
const adds = conditional
|
|
218
|
+
.map(({ key, value, dflt }) => {
|
|
219
|
+
// Object.hasOwn, not `in`: `in` also answers for what the instance
|
|
220
|
+
// inherits, so a property named `constructor` would copy the inherited
|
|
221
|
+
// function into the output that validate().data does not carry. And a
|
|
222
|
+
// key named `__proto__` cannot be written by assignment, which hits
|
|
223
|
+
// the prototype setter; defineProperty writes an own key.
|
|
224
|
+
const assign = key === '__proto__'
|
|
225
|
+
? `Object.defineProperty(${tmp}, ${JSON.stringify(key)}, { value: ${value}, writable: true, enumerable: true, configurable: true });`
|
|
226
|
+
: `${tmp}[${JSON.stringify(key)}] = ${value};`;
|
|
227
|
+
// The same own-property test the generators emit, since Object.hasOwn
|
|
228
|
+
// is not inlined: `in`, then the prototype, then hasOwnProperty only
|
|
229
|
+
// for an object that is not an ordinary one.
|
|
230
|
+
const k = JSON.stringify(key);
|
|
231
|
+
const own = PROTO_KEY_NAMES.has(key)
|
|
232
|
+
? `Object.prototype.hasOwnProperty.call(${access}, ${k})`
|
|
233
|
+
: `(${k} in ${access} && (${access}.__proto__ === Object.prototype || Object.prototype.hasOwnProperty.call(${access}, ${k})))`;
|
|
234
|
+
const set = `if (${own}) ${assign}`;
|
|
235
|
+
// The default is emitted as a literal inside the call, so an object or
|
|
236
|
+
// array default is a fresh value on every parse, never shared state.
|
|
237
|
+
if (dflt === undefined) return set;
|
|
238
|
+
const setDflt = key === '__proto__'
|
|
239
|
+
? `Object.defineProperty(${tmp}, ${JSON.stringify(key)}, { value: ${JSON.stringify(dflt)}, writable: true, enumerable: true, configurable: true });`
|
|
240
|
+
: `${tmp}[${JSON.stringify(key)}] = ${JSON.stringify(dflt)};`;
|
|
241
|
+
return `${set} else ${setDflt}`;
|
|
242
|
+
})
|
|
243
|
+
.join(' ');
|
|
244
|
+
return `(function(){ const ${tmp} = ${literal}; ${adds}${recordLoop} return ${tmp}; })()`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// The value dispatch shared by declared properties and the record loop. A
|
|
248
|
+
// value is copyable only when the emitter can name everything that may live
|
|
249
|
+
// under it: a primitive holds nothing, an object with declared properties or
|
|
250
|
+
// a record schema is rebuilt the same way, an array of primitives has
|
|
251
|
+
// nowhere to hide a key. A $ref, a composition or an untyped node could all
|
|
252
|
+
// carry keys this code cannot see, and copying the reference would smuggle
|
|
253
|
+
// them into a value that claims to be sanitised, so the caller declines the
|
|
254
|
+
// whole clone instead.
|
|
255
|
+
// Drop every schema-position `default` in a subtree, leaving data positions
|
|
256
|
+
// (const, enum, examples) untouched. Used on a record's value schema, where
|
|
257
|
+
// the runtime's useDefaults fills nothing.
|
|
258
|
+
function stripDefaultsDeep(node) {
|
|
259
|
+
if (!node || typeof node !== 'object') return node;
|
|
260
|
+
if (Array.isArray(node)) return node.map(stripDefaultsDeep);
|
|
261
|
+
const out = {};
|
|
262
|
+
for (const k of Object.keys(node)) {
|
|
263
|
+
if (k === 'default') continue;
|
|
264
|
+
out[k] = _INLINE_DATA_KEYS.has(k) ? node[k] : stripDefaultsDeep(node[k]);
|
|
265
|
+
}
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const _CLONE_PRIMITIVE = new Set(['string', 'number', 'integer', 'boolean', 'null']);
|
|
270
|
+
function emitValueExpr(prop, read, depth) {
|
|
271
|
+
if (!prop || typeof prop !== 'object') return null;
|
|
272
|
+
if (prop.$ref) return null;
|
|
273
|
+
const types = Array.isArray(prop.type) ? prop.type : [prop.type];
|
|
274
|
+
if (types.every((t) => _CLONE_PRIMITIVE.has(t))) return read;
|
|
275
|
+
const isRecordable = (s) => s.properties ||
|
|
276
|
+
(s.additionalProperties && typeof s.additionalProperties === 'object');
|
|
277
|
+
if (prop.type === 'object' && isRecordable(prop)) {
|
|
278
|
+
return emitClone(prop, read, depth + 1);
|
|
279
|
+
}
|
|
280
|
+
if (prop.type === 'array' && prop.items && typeof prop.items === 'object' &&
|
|
281
|
+
!Array.isArray(prop.items)) {
|
|
282
|
+
const it = prop.items;
|
|
283
|
+
const itemTypes = Array.isArray(it.type) ? it.type : [it.type];
|
|
284
|
+
if (itemTypes.every((t) => _CLONE_PRIMITIVE.has(t))) return read;
|
|
285
|
+
if (it.type === 'object' && isRecordable(it)) {
|
|
286
|
+
const el = '_e' + depth;
|
|
287
|
+
const inner = emitClone(it, el, depth + 1);
|
|
288
|
+
if (!inner) return null;
|
|
289
|
+
return `${read}.map((${el}) => (${inner}))`;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// The copy as an expression over `data`, or null where the kept key set is
|
|
296
|
+
// not provable.
|
|
297
|
+
function cloneExprFor(schema) {
|
|
298
|
+
if (!schema || typeof schema !== 'object') return null;
|
|
299
|
+
return emitClone(inlineRefsForClone(schema), 'data', 0);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
module.exports = { cloneExprFor, emitClone, inlineRefsForClone, stripDefaultsDeep, resolveLocalPointer };
|
package/lib/interpreter.js
CHANGED
|
@@ -230,7 +230,7 @@ function cpAtMost(s, max) {
|
|
|
230
230
|
function typeMatches(type, d) {
|
|
231
231
|
switch (type) {
|
|
232
232
|
case 'string': return typeof d === 'string';
|
|
233
|
-
case 'number': return
|
|
233
|
+
case 'number': return Number.isFinite(d);
|
|
234
234
|
case 'integer': return typeof d === 'number' && Number.isInteger(d);
|
|
235
235
|
case 'boolean': return typeof d === 'boolean';
|
|
236
236
|
case 'null': return d === null;
|
|
@@ -529,7 +529,7 @@ function evalLeafV(P, data) {
|
|
|
529
529
|
if (!found) return false;
|
|
530
530
|
}
|
|
531
531
|
if (P.hasConst && !deepEqual(P.const, data)) return false;
|
|
532
|
-
if (P.hasNumber &&
|
|
532
|
+
if (P.hasNumber && Number.isFinite(data)) {
|
|
533
533
|
if (P.minimum !== undefined && !(data >= P.minimum)) return false;
|
|
534
534
|
if (P.maximum !== undefined && !(data <= P.maximum)) return false;
|
|
535
535
|
if (P.exclusiveMinimum !== undefined && !(data > P.exclusiveMinimum)) return false;
|
|
@@ -646,7 +646,7 @@ function evalLeaf(P, data, errors, instancePath, schemaPath) {
|
|
|
646
646
|
if (errors !== NOERRORS) errors.push(err('const', 'const', instancePath, schemaPath + '/const', { allowedValue: P.const }, 'must be equal to constant'));
|
|
647
647
|
valid = false;
|
|
648
648
|
}
|
|
649
|
-
if (P.hasNumber &&
|
|
649
|
+
if (P.hasNumber && Number.isFinite(data)) {
|
|
650
650
|
if (P.minimum !== undefined && !(data >= P.minimum)) { errors === NOERRORS || errors.push(err('minimum', 'minimum', instancePath, schemaPath + '/minimum', { comparison: '>=', limit: P.minimum }, `must be >= ${P.minimum}`)); valid = false; }
|
|
651
651
|
if (P.maximum !== undefined && !(data <= P.maximum)) { errors === NOERRORS || errors.push(err('maximum', 'maximum', instancePath, schemaPath + '/maximum', { comparison: '<=', limit: P.maximum }, `must be <= ${P.maximum}`)); valid = false; }
|
|
652
652
|
if (P.exclusiveMinimum !== undefined && !(data > P.exclusiveMinimum)) { errors === NOERRORS || errors.push(err('exclusiveMinimum', 'exclusiveMinimum', instancePath, schemaPath + '/exclusiveMinimum', { comparison: '>', limit: P.exclusiveMinimum }, `must be > ${P.exclusiveMinimum}`)); valid = false; }
|
|
@@ -937,7 +937,7 @@ class Interpreter {
|
|
|
937
937
|
}
|
|
938
938
|
|
|
939
939
|
// numbers
|
|
940
|
-
if (P.hasNumber &&
|
|
940
|
+
if (P.hasNumber && Number.isFinite(data)) {
|
|
941
941
|
if (P.minimum !== undefined && !(data >= P.minimum)) { errors === NOERRORS || errors.push(err('minimum', 'minimum', instancePath, schemaPath + '/minimum', { comparison: '>=', limit: P.minimum }, `must be >= ${P.minimum}`)); valid = false; }
|
|
942
942
|
if (P.maximum !== undefined && !(data <= P.maximum)) { errors === NOERRORS || errors.push(err('maximum', 'maximum', instancePath, schemaPath + '/maximum', { comparison: '<=', limit: P.maximum }, `must be <= ${P.maximum}`)); valid = false; }
|
|
943
943
|
if (P.exclusiveMinimum !== undefined && !(data > P.exclusiveMinimum)) { errors === NOERRORS || errors.push(err('exclusiveMinimum', 'exclusiveMinimum', instancePath, schemaPath + '/exclusiveMinimum', { comparison: '>', limit: P.exclusiveMinimum }, `must be > ${P.exclusiveMinimum}`)); valid = false; }
|
package/lib/js-compiler.js
CHANGED
|
@@ -489,24 +489,24 @@ function compileToJS(schema, defs, schemaMap) {
|
|
|
489
489
|
// answer differently for it.
|
|
490
490
|
if (schema.minimum !== undefined) {
|
|
491
491
|
const min = schema.minimum
|
|
492
|
-
checks.push((d) =>
|
|
492
|
+
checks.push((d) => !Number.isFinite(d) || d >= min)
|
|
493
493
|
}
|
|
494
494
|
if (schema.maximum !== undefined) {
|
|
495
495
|
const max = schema.maximum
|
|
496
|
-
checks.push((d) =>
|
|
496
|
+
checks.push((d) => !Number.isFinite(d) || d <= max)
|
|
497
497
|
}
|
|
498
498
|
if (schema.exclusiveMinimum !== undefined) {
|
|
499
499
|
const min = schema.exclusiveMinimum
|
|
500
|
-
checks.push((d) =>
|
|
500
|
+
checks.push((d) => !Number.isFinite(d) || d > min)
|
|
501
501
|
}
|
|
502
502
|
if (schema.exclusiveMaximum !== undefined) {
|
|
503
503
|
const max = schema.exclusiveMaximum
|
|
504
|
-
checks.push((d) =>
|
|
504
|
+
checks.push((d) => !Number.isFinite(d) || d < max)
|
|
505
505
|
}
|
|
506
506
|
if (schema.multipleOf !== undefined) {
|
|
507
507
|
const div = schema.multipleOf
|
|
508
508
|
checks.push((d) => {
|
|
509
|
-
if (
|
|
509
|
+
if (!Number.isFinite(d)) return true
|
|
510
510
|
if (div === 0) return false
|
|
511
511
|
const q = d / div
|
|
512
512
|
return Number.isInteger(q) || Math.abs(q - Math.round(q)) < 1e-9
|
|
@@ -850,7 +850,7 @@ function multipleOfBad(v, m) {
|
|
|
850
850
|
|
|
851
851
|
const TYPE_CHECKS = {
|
|
852
852
|
string: (d) => typeof d === 'string',
|
|
853
|
-
number: (d) =>
|
|
853
|
+
number: (d) => Number.isFinite(d),
|
|
854
854
|
integer: (d) => Number.isInteger(d),
|
|
855
855
|
boolean: (d) => typeof d === 'boolean',
|
|
856
856
|
null: (d) => d === null,
|
|
@@ -2005,7 +2005,7 @@ function tryGenCombined(schema, access, ctx) {
|
|
|
2005
2005
|
}
|
|
2006
2006
|
|
|
2007
2007
|
if (t === 'number') {
|
|
2008
|
-
const conds = [
|
|
2008
|
+
const conds = [`!Number.isFinite(_v)`]
|
|
2009
2009
|
if (schema.minimum !== undefined) conds.push(`_v<${schema.minimum}`)
|
|
2010
2010
|
if (schema.maximum !== undefined) conds.push(`_v>${schema.maximum}`)
|
|
2011
2011
|
if (schema.exclusiveMinimum !== undefined) conds.push(`_v<=${schema.exclusiveMinimum}`)
|
|
@@ -2190,7 +2190,7 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
2190
2190
|
case 'object': lines.push(`if(typeof ${v}!=='object'||${v}===null||Array.isArray(${v}))return false`); break
|
|
2191
2191
|
case 'array': lines.push(`if(!Array.isArray(${v}))return false`); break
|
|
2192
2192
|
case 'string': lines.push(`if(typeof ${v}!=='string')return false`); break
|
|
2193
|
-
case 'number': lines.push(`if(
|
|
2193
|
+
case 'number': lines.push(`if(!Number.isFinite(${v}))return false`); break
|
|
2194
2194
|
case 'integer': lines.push(`if(!Number.isInteger(${v}))return false`); break
|
|
2195
2195
|
case 'boolean': lines.push(`if(typeof ${v}!=='boolean')return false`); break
|
|
2196
2196
|
case 'null': lines.push(`if(${v}!==null)return false`); break
|
|
@@ -2201,7 +2201,7 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
2201
2201
|
case 'object': return `(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))`
|
|
2202
2202
|
case 'array': return `Array.isArray(${v})`
|
|
2203
2203
|
case 'string': return `typeof ${v}==='string'`
|
|
2204
|
-
case 'number': return `
|
|
2204
|
+
case 'number': return `Number.isFinite(${v})`
|
|
2205
2205
|
case 'integer': return `Number.isInteger(${v})`
|
|
2206
2206
|
case 'boolean': return `typeof ${v}==='boolean'`
|
|
2207
2207
|
case 'null': return `${v}===null`
|
|
@@ -2308,7 +2308,7 @@ function genCodeNode(schema, v, lines, ctx, knownType) {
|
|
|
2308
2308
|
// non-finite value through: it carries no type, an empty schema accepts it,
|
|
2309
2309
|
// and the interpreted engine reads it the same way. With `type: number` the
|
|
2310
2310
|
// type check has already turned it away, so the guard is only needed here.
|
|
2311
|
-
const numGuard = isNum ? '' : `
|
|
2311
|
+
const numGuard = isNum ? '' : `Number.isFinite(${v})&&`
|
|
2312
2312
|
if (schema.minimum !== undefined) lines.push(`if(${numGuard}${v}<${schema.minimum})return false`)
|
|
2313
2313
|
if (schema.maximum !== undefined) lines.push(`if(${numGuard}${v}>${schema.maximum})return false`)
|
|
2314
2314
|
if (schema.exclusiveMinimum !== undefined) lines.push(`if(${numGuard}${v}<=${schema.exclusiveMinimum})return false`)
|
|
@@ -3973,7 +3973,7 @@ function genCodeENode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3973
3973
|
case 'object': return `(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))`
|
|
3974
3974
|
case 'array': return `Array.isArray(${v})`
|
|
3975
3975
|
case 'string': return `typeof ${v}==='string'`
|
|
3976
|
-
case 'number': return `
|
|
3976
|
+
case 'number': return `Number.isFinite(${v})`
|
|
3977
3977
|
case 'integer': return `Number.isInteger(${v})`
|
|
3978
3978
|
case 'boolean': return `typeof ${v}==='boolean'`
|
|
3979
3979
|
case 'null': return `${v}===null`
|
|
@@ -4710,7 +4710,7 @@ function genCodeCNode(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
4710
4710
|
case 'object': return `(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v}))`
|
|
4711
4711
|
case 'array': return `Array.isArray(${v})`
|
|
4712
4712
|
case 'string': return `typeof ${v}==='string'`
|
|
4713
|
-
case 'number': return `
|
|
4713
|
+
case 'number': return `Number.isFinite(${v})`
|
|
4714
4714
|
case 'integer': return `Number.isInteger(${v})`
|
|
4715
4715
|
case 'boolean': return `typeof ${v}==='boolean'`
|
|
4716
4716
|
case 'null': return `${v}===null`
|
package/lib/plan-compiler.js
CHANGED
|
@@ -182,7 +182,7 @@ function install(deps) {
|
|
|
182
182
|
// no type bit, and an empty schema accepts it. So the numeric keywords
|
|
183
183
|
// must not apply to it either, or `{ minimum: 3 }` rejects a value that
|
|
184
184
|
// `{}` accepts and the compiled engine admits.
|
|
185
|
-
if (run !== null) checks.push((data) =>
|
|
185
|
+
if (run !== null) checks.push((data) => !Number.isFinite(data) || run(data));
|
|
186
186
|
}
|
|
187
187
|
if (P.hasString) {
|
|
188
188
|
const strs = [];
|
package/lib/plan-source.js
CHANGED
|
@@ -28,7 +28,7 @@ const HELPERS = 'const _hop=Object.prototype.hasOwnProperty;';
|
|
|
28
28
|
const PROTO_NAMES = new Set([...Object.getOwnPropertyNames(Object.prototype), '__proto__']);
|
|
29
29
|
|
|
30
30
|
function isPrimitive(x) {
|
|
31
|
-
return x === null || typeof x === 'string' || typeof x === 'boolean' ||
|
|
31
|
+
return x === null || typeof x === 'string' || typeof x === 'boolean' || Number.isFinite(x);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function lit(x) {
|
|
@@ -58,7 +58,7 @@ function local(ctx) {
|
|
|
58
58
|
function typeTest(mask, v) {
|
|
59
59
|
const parts = [];
|
|
60
60
|
if (mask & T_STRING) parts.push(`typeof ${v}==='string'`);
|
|
61
|
-
if (mask & T_NUMBER) parts.push(`
|
|
61
|
+
if (mask & T_NUMBER) parts.push(`Number.isFinite(${v})`);
|
|
62
62
|
else if (mask & T_INTEGER) parts.push(`Number.isInteger(${v})`);
|
|
63
63
|
if (mask & T_BOOLEAN) parts.push(`typeof ${v}==='boolean'`);
|
|
64
64
|
if (mask & T_NULL) parts.push(`${v}===null`);
|
|
@@ -114,7 +114,7 @@ function emitLeaf(ctx, P, v, known) {
|
|
|
114
114
|
if (P.exclusiveMinimum !== undefined) n += `if(${v}<=${lit(P.exclusiveMinimum)})return false;`;
|
|
115
115
|
if (P.exclusiveMaximum !== undefined) n += `if(${v}>=${lit(P.exclusiveMaximum)})return false;`;
|
|
116
116
|
if (P.multipleOf !== undefined) n += `if(!${arg(ctx, multipleOfOk)}(${v},${lit(P.multipleOf)}))return false;`;
|
|
117
|
-
out += guarded(known, K_NUM, `
|
|
117
|
+
out += guarded(known, K_NUM, `Number.isFinite(${v})`, n);
|
|
118
118
|
}
|
|
119
119
|
if (P.hasString) {
|
|
120
120
|
let s = '';
|
package/lib/tier0.js
CHANGED
|
@@ -137,7 +137,7 @@ function checkPrimitive(c, v) {
|
|
|
137
137
|
if ((f & F_MULT) && !_multipleOk(v, c.multipleOf)) return false;
|
|
138
138
|
}
|
|
139
139
|
} else if (m === T_NUMBER) {
|
|
140
|
-
if (
|
|
140
|
+
if (!Number.isFinite(v)) return false;
|
|
141
141
|
const f = c.numFlags;
|
|
142
142
|
if (f !== 0) {
|
|
143
143
|
if ((f & F_MIN) && v < c.min) return false;
|
|
@@ -194,7 +194,7 @@ function tier0ValidateObject(plan, data) {
|
|
|
194
194
|
if ((f & F_MULT) && !_multipleOk(v, c.multipleOf)) return false;
|
|
195
195
|
}
|
|
196
196
|
} else if (m === T_NUMBER) {
|
|
197
|
-
if (
|
|
197
|
+
if (!Number.isFinite(v)) return false;
|
|
198
198
|
const f = c.numFlags;
|
|
199
199
|
if (f !== 0) {
|
|
200
200
|
if ((f & F_MIN) && v < c.min) return false;
|
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.32.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_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_nested_recursion_decline.js && node tests/test_generator_error_content.js && node tests/test_inherited_key_presence.js && node tests/test_plan_source.js && node tests/test_fused_remove_additional.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",
|
|
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_nested_recursion_decline.js && node tests/test_generator_error_content.js && node tests/test_inherited_key_presence.js && node tests/test_plan_source.js && node tests/test_fused_remove_additional.js && node tests/test_runtime_parse.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.32.0",
|
|
125
|
+
"@ata-validator/native-darwin-x64": "1.32.0",
|
|
126
|
+
"@ata-validator/native-linux-arm64-gnu": "1.32.0",
|
|
127
|
+
"@ata-validator/native-linux-arm64-musl": "1.32.0",
|
|
128
|
+
"@ata-validator/native-linux-x64-gnu": "1.32.0",
|
|
129
|
+
"@ata-validator/native-linux-x64-musl": "1.32.0",
|
|
130
|
+
"@ata-validator/native-win32-x64": "1.32.0"
|
|
131
131
|
},
|
|
132
132
|
"peerDependencies": {
|
|
133
133
|
"yaml": "^2.0.0"
|