ata-validator 1.46.1 → 1.48.0

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