ata-validator 1.48.0 → 1.50.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.
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ // A copy of a schema without the keywords that only describe it (title,
4
+ // description, examples and the like), for a compact module that embeds its
5
+ // schema so the shared error runtime can walk it. Validation never reads
6
+ // these, and in a typical published schema they are most of the bytes.
7
+ //
8
+ // Only schema positions are touched. An `enum`, a `const` or any keyword this
9
+ // file does not know is copied as it is, so a value that happens to hold a
10
+ // "description" key keeps it. If a local `$ref` points through a keyword that
11
+ // would be removed, nothing is removed: the reference has to land where it
12
+ // did.
13
+
14
+ const DROP = new Set([
15
+ 'title', 'description', '$comment', 'examples', 'default', 'deprecated',
16
+ 'readOnly', 'writeOnly', 'markdownDescription', 'deprecationMessage',
17
+ 'enumDescriptions', 'markdownEnumDescriptions', 'defaultSnippets',
18
+ ]);
19
+
20
+ const SINGLE = new Set([
21
+ 'additionalProperties', 'contains', 'propertyNames', 'if', 'then', 'else', 'not',
22
+ 'items', 'unevaluatedItems', 'unevaluatedProperties', 'additionalItems', 'contentSchema',
23
+ ]);
24
+ const MAPS = new Set(['$defs', 'definitions', 'properties', 'patternProperties', 'dependentSchemas']);
25
+ const LISTS = new Set(['allOf', 'anyOf', 'oneOf', 'prefixItems']);
26
+
27
+ function dropped(key) {
28
+ return DROP.has(key) || key.startsWith('x-');
29
+ }
30
+
31
+ function setOwn(o, k, v) {
32
+ // A key named __proto__ has to stay an own property.
33
+ if (k === '__proto__') Object.defineProperty(o, k, { value: v, enumerable: true, writable: true, configurable: true });
34
+ else o[k] = v;
35
+ }
36
+
37
+ function unescape(seg) {
38
+ return seg.replace(/~1/g, '/').replace(/~0/g, '~');
39
+ }
40
+
41
+ // True when some local reference walks through a key that would be dropped.
42
+ function refsIntoDropped(node, seen) {
43
+ if (node === null || typeof node !== 'object') return false;
44
+ if (seen.has(node)) return false;
45
+ seen.add(node);
46
+ if (Array.isArray(node)) {
47
+ for (const x of node) if (refsIntoDropped(x, seen)) return true;
48
+ return false;
49
+ }
50
+ for (const k of ['$ref', '$dynamicRef', '$recursiveRef']) {
51
+ const r = node[k];
52
+ if (typeof r !== 'string') continue;
53
+ const hash = r.indexOf('#');
54
+ if (hash < 0) continue;
55
+ let frag;
56
+ try { frag = decodeURIComponent(r.slice(hash + 1)); } catch { return true; }
57
+ if (!frag.startsWith('/')) continue;
58
+ for (const seg of frag.slice(1).split('/')) if (dropped(unescape(seg))) return true;
59
+ }
60
+ for (const k of Object.keys(node)) if (refsIntoDropped(node[k], seen)) return true;
61
+ return false;
62
+ }
63
+
64
+ // keepTitle: the node is a branch of anyOf or oneOf, whose title names the
65
+ // branch in the collapsed error (params.closestName).
66
+ function stripSchema(node, keepTitle) {
67
+ if (node === null || typeof node !== 'object' || Array.isArray(node)) return node;
68
+ const out = {};
69
+ for (const k of Object.keys(node)) {
70
+ if (dropped(k) && !(keepTitle === true && k === 'title')) continue;
71
+ const v = node[k];
72
+ if (SINGLE.has(k)) {
73
+ setOwn(out, k, Array.isArray(v) ? v.map((x) => stripSchema(x, false)) : stripSchema(v, false));
74
+ } else if (MAPS.has(k) && v !== null && typeof v === 'object' && !Array.isArray(v)) {
75
+ const m = {};
76
+ for (const name of Object.keys(v)) setOwn(m, name, stripSchema(v[name], false));
77
+ setOwn(out, k, m);
78
+ } else if (LISTS.has(k) && Array.isArray(v)) {
79
+ const branches = k === 'anyOf' || k === 'oneOf';
80
+ setOwn(out, k, v.map((x) => stripSchema(x, branches)));
81
+ } else if (k === 'dependencies' && v !== null && typeof v === 'object' && !Array.isArray(v)) {
82
+ const m = {};
83
+ for (const name of Object.keys(v)) setOwn(m, name, Array.isArray(v[name]) ? v[name] : stripSchema(v[name], false));
84
+ setOwn(out, k, m);
85
+ } else if (k === 'propertyDependencies' && v !== null && typeof v === 'object' && !Array.isArray(v)) {
86
+ const m = {};
87
+ for (const name of Object.keys(v)) {
88
+ const inner = v[name];
89
+ if (inner === null || typeof inner !== 'object' || Array.isArray(inner)) { setOwn(m, name, inner); continue; }
90
+ const mi = {};
91
+ for (const val of Object.keys(inner)) setOwn(mi, val, stripSchema(inner[val], false));
92
+ setOwn(m, name, mi);
93
+ }
94
+ setOwn(out, k, m);
95
+ } else {
96
+ setOwn(out, k, v);
97
+ }
98
+ }
99
+ return out;
100
+ }
101
+
102
+ // A compact or schema module strips its schema three times on the way out
103
+ // (the reference check, the module, the flat-index check): once per schema
104
+ // object. A validator's schema object does not change after it compiles.
105
+ const _stripped = new WeakMap();
106
+ function stripAnnotations(schema) {
107
+ if (schema === null || typeof schema !== 'object') return schema;
108
+ const hit = _stripped.get(schema);
109
+ if (hit !== undefined) return hit;
110
+ const out = refsIntoDropped(schema, new Set()) ? schema : stripSchema(schema, false);
111
+ _stripped.set(schema, out);
112
+ return out;
113
+ }
114
+
115
+ module.exports = { stripAnnotations };
package/lib/tools.mjs ADDED
@@ -0,0 +1,21 @@
1
+ // The package's tools as ES modules, one import each, for the entries a
2
+ // bundler reads (index.browser.mjs, index.edge.mjs): an application that
3
+ // imports only Validator leaves TypeScript generation, the renderers, the
4
+ // output format, the retry message and the schema description out of its
5
+ // bundle. Through index.js they load on first call instead, which a bundler
6
+ // cannot see, so it kept all of them.
7
+ import tsGen from './ts-gen.js';
8
+ import renderPrettyMod from './render-pretty.js';
9
+ import renderCompactMod from './render-compact.js';
10
+ import outputFormat from './output-format.js';
11
+ import retryMessage from './retry-message.js';
12
+ import describe from './describe-schema.js';
13
+ import renderJsonMod from './render-json.js';
14
+
15
+ export function toTypeScript (...a) { return tsGen.toTypeScript(...a); }
16
+ export function renderPretty (...a) { return renderPrettyMod.renderPretty(...a); }
17
+ export function renderCompact (...a) { return renderCompactMod.renderCompact(...a); }
18
+ export function toOutput (...a) { return outputFormat.toOutput(...a); }
19
+ export function toRetryMessage (...a) { return retryMessage.toRetryMessage(...a); }
20
+ export function describeSchema (...a) { return describe.describeSchema(...a); }
21
+ export function renderJSON (...a) { return renderJsonMod.renderJSON(...a); }
@@ -25,7 +25,15 @@ function _registerCodegen(engine) {
25
25
  _codegen = engine;
26
26
  ({ compileToJS, compileToJSCodegen, compileToJSCodegenWithErrors, compileToJSCombined } = engine.jsCompiler);
27
27
  }
28
- const { normalizeDraft7, normalizeNullable, normalizeExclusiveBounds, stripFormatAssertions } = require("./draft7");
28
+ // The ahead-of-time loaders without the code generator: the edge entry
29
+ // (index.edge.js) registers lib/aot.browser.js here, so modules written by
30
+ // `ata build` or Validator.bundle() load where generated code cannot run.
31
+ // The emitters stay out, as they do in a browser bundle of the full package.
32
+ let _aotLoader = null;
33
+ function _registerAot(load) {
34
+ _aotLoader = load;
35
+ }
36
+ const { draftCode, normalizeDraft2019, normalizeDraft7, normalizeNullable, normalizeExclusiveBounds, stripFormatAssertions } = require("./draft7");
29
37
  const { enabledKeywords, stripDisabledKeywords } = require("./vocabularies");
30
38
  const { needsNormalization } = require("./schema-scan");
31
39
  const { isV1Dialect } = require("./dialect");
@@ -426,21 +434,26 @@ function _deepCloneWithSymbols(v) {
426
434
  // Normalize a caller-provided schema without mutating the original.
427
435
  // Clones only when normalization would change the object (draft-07 keys
428
436
  // present or nullable fields present). Internal-only — not exported.
437
+ // `inheritDraft7` is the root's draft for a document that declares none of
438
+ // its own: 0 or absent for none, 1 for draft-07, 2 for draft-06, 3 for 2019-09.
429
439
  function _normalizeCallerSchema(s, inheritDraft7) {
430
- const declares = s && typeof s === 'object' && s.$schema !== undefined
431
- const needsDraft7 = declares
432
- ? (s.$schema === 'http://json-schema.org/draft-07/schema#' || s.$schema === 'http://json-schema.org/draft-07/schema')
433
- : !!inheritDraft7
440
+ const declared = s && typeof s === 'object' ? s.$schema : undefined
441
+ const code = declared !== undefined ? draftCode(declared) : (inheritDraft7 || 0)
442
+ const needsDraft7 = code === 1 || code === 2
443
+ const draft6 = code === 2
444
+ const draft2019 = code === 3
434
445
  // One walk answers whether there is anything to do. Almost always there is
435
446
  // not, and then the serialize, clone, normalize, serialize, compare below is
436
447
  // work spent to find that out. The walk over-reports rather than under, so a
437
448
  // schema it clears is one no normalizer would have touched;
438
449
  // `tests/test_schema_scan.js` holds that direction against the whole suite.
439
- if (!needsNormalization(s, needsDraft7)) return s
450
+ // The scan does not look for `if`/`then`/`else`, which draft-06 drops.
451
+ if (!draft6 && !draft2019 && !needsNormalization(s, needsDraft7)) return s
440
452
 
441
453
  const str = JSON.stringify(s)
442
454
  const copy = _deepCloneWithSymbols(s)
443
- if (needsDraft7) normalizeDraft7(copy, true)
455
+ if (needsDraft7) normalizeDraft7(copy, true, draft6)
456
+ if (draft2019) normalizeDraft2019(copy, true)
444
457
  normalizeNullable(copy)
445
458
  normalizeExclusiveBounds(copy)
446
459
  // Return original when normalization produced no change, copy otherwise.
@@ -467,7 +480,7 @@ function declaredId(original, normalized) {
467
480
  return undefined
468
481
  }
469
482
 
470
- // `inheritDraft7` is true when the root schema is draft-07: a retrieved
483
+ // `inheritDraft7` is non-zero when the root schema is draft-07 or draft-06: a retrieved
471
484
  // document that declares no dialect is read under the root's draft.
472
485
  // The map is derived entirely from what the caller passed, so the same
473
486
  // `schemas` gives the same map. A server building one validator per route over
@@ -485,12 +498,12 @@ function buildSchemaMap(schemas, inheritDraft7) {
485
498
  if (!schemas) return null
486
499
  const byDraft = _schemaMapCache.get(schemas)
487
500
  if (byDraft) {
488
- const hit = byDraft[inheritDraft7 ? 1 : 0]
501
+ const hit = byDraft[inheritDraft7 || 0]
489
502
  if (hit) return hit
490
503
  }
491
504
  const map = _buildSchemaMap(schemas, inheritDraft7)
492
- const slot = byDraft || [null, null]
493
- slot[inheritDraft7 ? 1 : 0] = map
505
+ const slot = byDraft || [null, null, null, null]
506
+ slot[inheritDraft7 || 0] = map
494
507
  if (!byDraft) _schemaMapCache.set(schemas, slot)
495
508
  return map
496
509
  }
@@ -676,8 +689,7 @@ class Validator {
676
689
  // uses, and a benchmark timing construction measures construction.
677
690
  // When schema is a string, JSON.parse already produces a fresh object.
678
691
  const raw = typeof schema === "string" ? JSON.parse(schema) : schema;
679
- const rootIsDraft7 = !!(raw && typeof raw === 'object' && typeof raw.$schema === 'string' &&
680
- (raw.$schema === 'http://json-schema.org/draft-07/schema#' || raw.$schema === 'http://json-schema.org/draft-07/schema'));
692
+ const rootIsDraft7 = raw && typeof raw === 'object' ? draftCode(raw.$schema) : 0;
681
693
 
682
694
  // Built here rather than below because `$vocabulary` is resolved against
683
695
  // it, and that resolution waits until compilation so a meta-schema
@@ -1561,8 +1573,7 @@ class Validator {
1561
1573
  // Normalize a copy so the caller's object is never mutated. A document
1562
1574
  // without a dialect of its own is read under the root's draft.
1563
1575
  const root = this._schemaObj
1564
- const rootIsDraft7 = !!(root && typeof root === 'object' && typeof root.$schema === 'string' &&
1565
- (root.$schema === 'http://json-schema.org/draft-07/schema#' || root.$schema === 'http://json-schema.org/draft-07/schema'))
1576
+ const rootIsDraft7 = root && typeof root === 'object' ? draftCode(root.$schema) : 0
1566
1577
  const normalized = _normalizeCallerSchema(schema, rootIsDraft7)
1567
1578
  this._ownSchemaMap()
1568
1579
  this._schemaMap.set(normalized.$id, normalized)
@@ -1596,7 +1607,7 @@ class Validator {
1596
1607
  static fromStandalone(mod, schema, opts) {
1597
1608
  // It loads what Validator.bundleStandalone() writes, and compiles an error
1598
1609
  // function when the module carries none, so it goes with those methods.
1599
- if (_codegen === null) throw new TypeError('Validator.fromStandalone is not part of ata-validator/lite, like the bundle methods it loads for. Import it from ata-validator, or import a module from `ata build` directly.');
1610
+ if (_codegen === null && _aotLoader === null) throw new TypeError('Validator.fromStandalone is not part of ata-validator/lite, like the bundle methods it loads for. Import it from ata-validator, or import a module from `ata build` directly.');
1600
1611
  const options = opts || {};
1601
1612
  const schemaObj = typeof schema === "string" ? JSON.parse(schema) : schema;
1602
1613
 
@@ -1632,7 +1643,9 @@ class Validator {
1632
1643
  });
1633
1644
  if (mod.errFn) {
1634
1645
  errFn = (d) => mod.errFn(d, true);
1635
- } else {
1646
+ } else if (compileToJSCodegenWithErrors !== undefined) {
1647
+ // Without the code generator (the edge entry) a module that carries no
1648
+ // error function reports the stub above.
1636
1649
  const jsErrFn = compileToJSCodegenWithErrors(schemaObj);
1637
1650
  if (jsErrFn) {
1638
1651
  try {
@@ -1780,8 +1793,9 @@ function version() {
1780
1793
  // generated module shapes; browser bundles get `lib/aot.browser.js` (a stub
1781
1794
  // that throws) via the package.json `browser` field.
1782
1795
  function _aot() {
1783
- if (_codegen === null) throw new TypeError('The ahead-of-time bundle methods are not part of ata-validator/lite. Import them from ata-validator.');
1784
- return _codegen.aot();
1796
+ if (_codegen !== null) return _codegen.aot();
1797
+ if (_aotLoader !== null) return _aotLoader();
1798
+ throw new TypeError('The ahead-of-time bundle methods are not part of ata-validator/lite. Import them from ata-validator.');
1785
1799
  }
1786
1800
  Validator.bundle = function (schemas, opts) {
1787
1801
  return _aot().bundle(Validator, schemas, opts);
@@ -2192,7 +2206,7 @@ function _buildParse(self) {
2192
2206
  // isValidObject, which runs them, and a property they check with instanceof
2193
2207
  // is carried over as it is (see clone-emit).
2194
2208
  const extended = self._verdictTail !== null || self._validateTail !== null;
2195
- if (_codegen === null) return decline('ata-validator/lite has no code generator to build the copy with', 'Use parse() from ata-validator, or validate(); a valid result carries the input as it is.');
2209
+ if (_codegen === null) return decline('this entry has no code generator to build the copy with', 'Use validate(); a valid result carries the input as it is.');
2196
2210
  return _codegen.buildParse(self, decline, extended);
2197
2211
  }
2198
2212
 
@@ -2366,5 +2380,6 @@ module.exports = {
2366
2380
  // How an entry registers the code generator. Not enumerable, so the exported
2367
2381
  // surface of `require('ata-validator')` is what it was.
2368
2382
  Object.defineProperty(module.exports, '_registerCodegen', { value: _registerCodegen, enumerable: false });
2383
+ Object.defineProperty(module.exports, '_registerAot', { value: _registerAot, enumerable: false });
2369
2384
  // What the compiled paths in index.js need from this module.
2370
2385
  Object.defineProperty(module.exports, '_internals', { value: { _jsonSyntaxRejection, ABORT_EARLY_RESULT, HYBRID_TIER_CALLS, SIMDJSON_THRESHOLD, VALID_RESULT, _bindEntry, _bindVerdict, _compileCache, _compileCacheSet, _mustReject, _rememberInstance, _verboseWrap, compileCacheKey, getNative, isV1Dialect, resolveSchemaByPath }, enumerable: false });
package/lib/version.js CHANGED
@@ -7,4 +7,4 @@
7
7
  //
8
8
  // Kept in lockstep with package.json by `tests/test_version_sync.js`.
9
9
 
10
- module.exports = '1.48.0';
10
+ module.exports = '1.50.0';
@@ -50,6 +50,18 @@ function keywordsByVocabulary() {
50
50
  new Set(Object.keys(meta.properties)),
51
51
  )
52
52
  }
53
+ // Draft 2019-09's vocabularies, from its own vendored meta-schemas. A
54
+ // 2019-09 dialect names these URIs in `$vocabulary`.
55
+ for (const name of ['core', 'applicator', 'validation', 'meta-data', 'format', 'content']) {
56
+ const meta = require('./metaschemas').METASCHEMAS.get(
57
+ `https://json-schema.org/draft/2019-09/meta/${name}`,
58
+ )
59
+ if (!meta || !meta.properties) continue
60
+ byVocabulary.set(
61
+ `https://json-schema.org/draft/2019-09/vocab/${name}`,
62
+ new Set(Object.keys(meta.properties)),
63
+ )
64
+ }
53
65
  // The one vocabulary with no document to read. `format-assertion` is an
54
66
  // alternative to `format-annotation` rather than part of the standard
55
67
  // dialect, so its meta-schema is not among the vendored ones. It defines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.48.0",
3
+ "version": "1.50.0",
4
4
  "description": "JSON Schema validator that compiles to plain JavaScript, still runs where code generation is blocked, and can be compiled away at build time. Passes the full official test suite.",
5
5
  "type": "commonjs",
6
6
  "main": "index.js",
@@ -8,6 +8,16 @@
8
8
  "types": "index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
+ "workerd": {
12
+ "types": "./index.d.ts",
13
+ "import": "./index.edge.mjs",
14
+ "require": "./index.edge.js"
15
+ },
16
+ "edge-light": {
17
+ "types": "./index.d.ts",
18
+ "import": "./index.edge.mjs",
19
+ "require": "./index.edge.js"
20
+ },
11
21
  "types": "./index.d.ts",
12
22
  "browser": {
13
23
  "import": "./index.browser.mjs",
@@ -54,6 +64,11 @@
54
64
  "import": "./lite.mjs",
55
65
  "require": "./lite.js"
56
66
  },
67
+ "./error-runtime": {
68
+ "types": "./error-runtime.d.ts",
69
+ "import": "./error-runtime.mjs",
70
+ "require": "./error-runtime.js"
71
+ },
57
72
  "./package.json": "./package.json"
58
73
  },
59
74
  "sideEffects": false,
@@ -72,7 +87,7 @@
72
87
  "rebuild": "cmake-js rebuild --target ata",
73
88
  "test": "node tests/run.js",
74
89
  "bench:size": "node benchmark/bench_aot_size.mjs",
75
- "test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1",
90
+ "test:suite": "node tests/run_suite.js && node tests/run_suite.js draft7 && node tests/run_suite.js v1 && node tests/run_suite.js draft6 && node tests/run_suite.js draft2019-09",
76
91
  "test:compat": "node tests/test_compat.js",
77
92
  "test:standard-schema": "node tests/test_standard_schema.js",
78
93
  "test:browser": "node tests/test_browser.js",
@@ -123,6 +138,8 @@
123
138
  "index.mjs",
124
139
  "index.node.mjs",
125
140
  "index.browser.mjs",
141
+ "index.edge.js",
142
+ "index.edge.mjs",
126
143
  "index.d.ts",
127
144
  "aot.js",
128
145
  "aot.mjs",
@@ -147,18 +164,21 @@
147
164
  "lite.js",
148
165
  "lite.mjs",
149
166
  "lite.d.ts",
167
+ "error-runtime.js",
168
+ "error-runtime.mjs",
169
+ "error-runtime.d.ts",
150
170
  "bin/",
151
171
  "README.md",
152
172
  "LICENSE"
153
173
  ],
154
174
  "optionalDependencies": {
155
- "@ata-validator/native-darwin-arm64": "1.48.0",
156
- "@ata-validator/native-darwin-x64": "1.48.0",
157
- "@ata-validator/native-linux-arm64-gnu": "1.48.0",
158
- "@ata-validator/native-linux-arm64-musl": "1.48.0",
159
- "@ata-validator/native-linux-x64-gnu": "1.48.0",
160
- "@ata-validator/native-linux-x64-musl": "1.48.0",
161
- "@ata-validator/native-win32-x64": "1.48.0"
175
+ "@ata-validator/native-darwin-arm64": "1.50.0",
176
+ "@ata-validator/native-darwin-x64": "1.50.0",
177
+ "@ata-validator/native-linux-arm64-gnu": "1.50.0",
178
+ "@ata-validator/native-linux-arm64-musl": "1.50.0",
179
+ "@ata-validator/native-linux-x64-gnu": "1.50.0",
180
+ "@ata-validator/native-linux-x64-musl": "1.50.0",
181
+ "@ata-validator/native-win32-x64": "1.50.0"
162
182
  },
163
183
  "peerDependencies": {
164
184
  "yaml": "^2.0.0"