ata-validator 0.18.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Native addon: optional. Core validate() uses JS codegen and works without it.
2
2
  // Buffer APIs (isValid, countValid, isValidParallel) require native.
3
- let native;
4
- try { native = require("pkg-prebuilds")(__dirname, require("./binding-options")); } catch {}
3
+ // Loading is delegated so this file stays free of `pkg-prebuilds`/`__dirname`/
4
+ // `path` (the browser entry must not pull those in via the bundler).
5
+ const native = require("./lib/native-load")();
5
6
  const {
6
7
  compileToJS,
7
8
  compileToJSCodegen,
@@ -287,45 +288,11 @@ const ABORT_EARLY_RESULT = Object.freeze({
287
288
  })]),
288
289
  });
289
290
 
290
- // Embedded verbatim in standalone modules so the output file has no runtime
291
- // dependency on ata-validator. ASCII fast-path plus surrogate-aware slow path.
292
- const _CP_LEN_SOURCE = `function _cpLen(s) {
293
- const len = s.length;
294
- for (let i = 0; i < len; i++) {
295
- if (s.charCodeAt(i) >= 0xD800 && s.charCodeAt(i) <= 0xDBFF) {
296
- let n = 0; for (const _ of s) n++; return n;
297
- }
298
- }
299
- return len;
300
- }`;
301
-
302
- // The linear-time regex engine, inlined verbatim into standalone output so a
303
- // compiled module that uses safe `pattern` matchers has no runtime dependency on
304
- // ata-validator. Read from the source file (one copy, no drift) with the strict
305
- // directive and CommonJS exports stripped, plus the `__ataSafeRe` alias the
306
- // emitted code calls. The engine has no eval/new Function, so the embed is
307
- // CSP-safe.
308
- // Lazy so merely importing ata never touches the filesystem. Bundlers that stub
309
- // `fs`/`path` for the browser would otherwise crash at module load; the read is
310
- // deferred to the first standalone compile that actually embeds the engine.
311
- let _safeRegexEmbed = null;
312
- function getSafeRegexEmbed() {
313
- if (_safeRegexEmbed === null) {
314
- const raw = require("fs").readFileSync(require("path").join(__dirname, "lib", "safe-regex.js"), "utf8");
315
- const body = raw
316
- .replace(/^'use strict'\s*\n/, "")
317
- .replace(/\nmodule\.exports[^\n]*\n?/, "\n");
318
- _safeRegexEmbed = body.trimEnd() + "\nconst __ataSafeRe = compileSafe;";
319
- }
320
- return _safeRegexEmbed;
321
- }
322
-
323
- // Returns the engine embed when any supplied compiled function references the
324
- // safe matcher (jsFn._usesSafeRe), else an empty string so non-pattern modules
325
- // pay zero bytes.
326
- function safeRePrelude(...fns) {
327
- return fns.some((f) => f && f._usesSafeRe) ? getSafeRegexEmbed() + "\n" : "";
328
- }
291
+ // `_CP_LEN_SOURCE`, the safe-regex embed, and the AOT helpers that consume them
292
+ // now live in `lib/aot.js` — keeping this file free of `fs`/`path`/`__dirname`
293
+ // references so a default import never touches disk. The instance and static
294
+ // AOT methods further down lazily require `./lib/aot`, so they pay nothing
295
+ // until a user calls `toStandaloneModule`/`bundleStandalone`/etc.
329
296
 
330
297
  // Above this size, simdjson On Demand (selective field access) beats JSON.parse
331
298
  // (which must materialize the full JS object tree). Buffer.from + NAPI ~2x faster.
@@ -1097,6 +1064,44 @@ class Validator {
1097
1064
  };
1098
1065
  }
1099
1066
 
1067
+ // Custom error messages: if any subschema declares an `errorMessage`
1068
+ // keyword, install an outermost decorator that overrides the `message`
1069
+ // field of the errors it owns. Gated on a one-time scan so schemas without
1070
+ // errorMessage keep the validate hot path untouched. Layered after rich
1071
+ // enrichment so `code`/`keyword`/`path` are already final and only the
1072
+ // human-facing message changes.
1073
+ {
1074
+ const emLib = require('./lib/error-messages');
1075
+ const schemaStr = this._schemaStr || (this._schemaObj ? JSON.stringify(this._schemaObj) : '');
1076
+ if (emLib.schemaHasErrorMessages(schemaStr)) {
1077
+ const root = this._schemaObj;
1078
+ const wrap = (inner) => (arg) => {
1079
+ const result = inner(arg);
1080
+ if (result && result.valid === false && result.errors && result.errors.length && result !== ABORT_EARLY_RESULT) {
1081
+ const overridden = emLib.applyErrorMessages(result.errors, root);
1082
+ if (overridden !== result.errors) return { valid: false, errors: overridden };
1083
+ }
1084
+ return result;
1085
+ };
1086
+ if (this.validate) this.validate = wrap(this.validate);
1087
+ if (this.validateJSON) this.validateJSON = wrap(this.validateJSON);
1088
+ // validateAndParse routes through self.validate on the codegen path, but
1089
+ // the native-only path returns directly from the addon — wrap it so both
1090
+ // paths get overrides. The result shape carries `value`, preserved here.
1091
+ if (this.validateAndParse) {
1092
+ const innerVP = this.validateAndParse;
1093
+ this.validateAndParse = (arg) => {
1094
+ const result = innerVP(arg);
1095
+ if (result && result.valid === false && result.errors && result.errors.length) {
1096
+ const overridden = emLib.applyErrorMessages(result.errors, root);
1097
+ if (overridden !== result.errors) return { valid: false, value: result.value, errors: overridden };
1098
+ }
1099
+ return result;
1100
+ };
1101
+ }
1102
+ }
1103
+ }
1104
+
1100
1105
  // Save to identity cache for ultra-fast reuse with same schema object
1101
1106
  if (this._schemaObj && typeof this._schemaObj === 'object') {
1102
1107
  _identityCache.set(this._schemaObj, this);
@@ -1160,157 +1165,18 @@ class Validator {
1160
1165
  }
1161
1166
  }
1162
1167
 
1163
- // --- Standalone pre-compilation ---
1164
- // Generate a JS module string that can be written to a file.
1165
- // On next startup, load with Validator.fromStandalone() -- zero compile time.
1168
+ // --- AOT pre-compilation ---
1169
+ // The bodies of `toStandalone` and `toStandaloneModule` live in `lib/aot.js`
1170
+ // so this entry stays free of `fs`/`path`/`__dirname`. The lazy require runs
1171
+ // only on first call, never during a plain `import 'ata-validator'`. Browser
1172
+ // bundlers swap `lib/aot.js` with `lib/aot.browser.js`, which throws a
1173
+ // pointed error instead of attempting to read source from disk.
1166
1174
  toStandalone() {
1167
- this._ensureCompiled();
1168
- const jsFn = this._jsFn;
1169
- if (!jsFn || !jsFn._source) return null;
1170
- const src = jsFn._source;
1171
- const hybridSrc = jsFn._hybridSource || "";
1172
- const preambleSrc = jsFn._preambleSource || "";
1173
-
1174
- // Also capture error function source for zero-compile standalone load
1175
- const jsErrFn = compileToJSCodegenWithErrors(
1176
- typeof this._schemaObj === "object" ? this._schemaObj : {},
1177
- );
1178
- const errSrc = jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : "";
1179
-
1180
- return `// Auto-generated by ata-validator — do not edit
1181
- 'use strict';
1182
- ${_CP_LEN_SOURCE}
1183
- ${safeRePrelude(jsFn, jsErrFn)}${preambleSrc}
1184
- const boolFn = function(d) {
1185
- ${src}
1186
- };
1187
- const hybridFactory = function(R, E) {
1188
- return function(d) {
1189
- ${hybridSrc}
1190
- };
1191
- };
1192
- ${errSrc ? `const errFn = function(d, _all) {\n ${errSrc}\n};` : "const errFn = null;"}
1193
- module.exports = { boolFn, hybridFactory, errFn };
1194
- `;
1175
+ return require('./lib/aot').toStandalone(this);
1195
1176
  }
1196
1177
 
1197
- // --- Fully standalone module ---
1198
- // Generates a self-contained module that can be imported directly without
1199
- // pulling in ata-validator at runtime. Browser bundle gets only the
1200
- // generated validator (~2 KB typical) instead of the 165 KB compiler.
1201
- //
1202
- // import { validate, isValid } from './user-validator.mjs'
1203
- // if (isValid(data)) { ... }
1204
- //
1205
- // format: 'esm' | 'cjs'. Default 'esm'.
1206
- // abortEarly: if true, invalid result is a shared stub; smaller output.
1207
1178
  toStandaloneModule(opts) {
1208
- this._ensureCompiled();
1209
- const jsFn = this._jsFn;
1210
- if (!jsFn || !jsFn._source) return null;
1211
- const format = (opts && opts.format) || 'esm';
1212
- const abortEarly = !!(opts && opts.abortEarly);
1213
- const source = !!(opts && opts.source);
1214
- const sourceMap = opts && opts.sourceMap ? opts.sourceMap : null;
1215
- const schemaFile = opts && opts.schemaFile ? opts.schemaFile : null;
1216
- const src = jsFn._source;
1217
-
1218
- let errCore = '';
1219
- let jsErrFn = null;
1220
- if (!abortEarly) {
1221
- jsErrFn = compileToJSCodegenWithErrors(
1222
- typeof this._schemaObj === 'object' ? this._schemaObj : {},
1223
- null,
1224
- this._userFormats,
1225
- (source && sourceMap && schemaFile) ? { sourceMap, schemaFile } : null,
1226
- );
1227
- const errSrc = jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : '';
1228
- if (errSrc) {
1229
- errCore = `const errFn = function(d, _all) {\n ${errSrc}\n};\n`;
1230
- }
1231
- }
1232
-
1233
- // Schema-source frames are baked as literals inside each emitted error so
1234
- // consumers don't need a runtime lookup. We still expose the schema file
1235
- // as a sentinel constant when --source is on — handy for introspection
1236
- // and visible in source graphs. With --no-source, the constant is omitted
1237
- // entirely so size budgets and grep-based "is this source-mapped?" checks
1238
- // both work.
1239
- const schemaSourceConst = (source && schemaFile)
1240
- ? `const __ATA_SCHEMA_SOURCE__ = ${JSON.stringify({ file: schemaFile })};\n`
1241
- : '';
1242
-
1243
- // Serialize closure vars referenced in _fn body: regex, sub-validators, sets.
1244
- let closureDecls = '';
1245
- if (jsFn._closures && jsFn._closures.length > 0) {
1246
- const lines = [];
1247
- for (const { name, val } of jsFn._closures) {
1248
- if (Array.isArray(val)) {
1249
- lines.push(`const ${name} = ${JSON.stringify(val)};`);
1250
- continue;
1251
- }
1252
- if (val && val.__ataSafe) {
1253
- lines.push(`const ${name} = __ataSafeRe(${JSON.stringify(val.source)});`);
1254
- } else if (val instanceof RegExp) {
1255
- const flags = val.flags;
1256
- lines.push(`const ${name} = new RegExp(${JSON.stringify(val.source)}${flags ? ', ' + JSON.stringify(flags) : ''});`);
1257
- } else if (val instanceof Set) {
1258
- lines.push(`const ${name} = new Set(${JSON.stringify([...val])});`);
1259
- } else if (typeof val === 'function') {
1260
- // new Function('_ppv', body) — extract body from toString()
1261
- const str = val.toString();
1262
- // Matches: "function anonymous(_ppv\n) {\nbody\n}" or "function(_ppv){body}"
1263
- const m = str.match(/^function[^(]*\([^)]*\)\s*\{([\s\S]*)\}$/)
1264
- const body = m ? m[1].trim() : str;
1265
- lines.push(`const ${name} = function(_ppv) { ${body} };`);
1266
- }
1267
- }
1268
- if (lines.length) closureDecls = lines.join('\n') + '\n';
1269
- }
1270
-
1271
- // Hoisted oneOf/anyOf branch checks live in the boolean fn's preamble (the
1272
- // runtime emits them before the function). The standalone module must declare
1273
- // them at module scope too, or _fn references undefined names (e.g. _af1_b0).
1274
- const preambleDecls = jsFn._preambleSource ? jsFn._preambleSource + '\n' : '';
1275
-
1276
- // User-supplied format functions are referenced as _uf_<name> by both the
1277
- // boolean (_fn) and error (errFn) bodies. Embed them via Function#toString
1278
- // so the standalone module stays self-contained.
1279
- let formatDecls = '';
1280
- if (jsFn._formatClosures && jsFn._formatClosures.length > 0) {
1281
- formatDecls = jsFn._formatClosures
1282
- .map(({ name, fn }) => `const ${name} = ${fn.toString()};`)
1283
- .join('\n') + '\n';
1284
- }
1285
-
1286
- const validBody = errCore
1287
- ? 'return _fn(data) ? VALID : { valid: false, errors: errFn(data, true).errors }'
1288
- : 'return _fn(data) ? VALID : ABORT';
1289
-
1290
- const exports = format === 'esm'
1291
- ? `export { validate, isValid };\nexport default { validate, isValid };\n`
1292
- : `module.exports = { validate, isValid };\nmodule.exports.default = module.exports;\n`;
1293
-
1294
- return `// Auto-generated by ata-validator — do not edit.
1295
- // Schema is embedded; runtime has zero dependency on ata-validator.
1296
- 'use strict';
1297
- ${_CP_LEN_SOURCE}
1298
- ${safeRePrelude(jsFn, jsErrFn)}${schemaSourceConst}const VALID = Object.freeze({ valid: true, errors: Object.freeze([]) });
1299
- const ABORT = Object.freeze({
1300
- valid: false,
1301
- errors: Object.freeze([Object.freeze({
1302
- code: 'ATA9000',
1303
- message: 'validation failed',
1304
- keyword: '__abort_early__',
1305
- path: '',
1306
- })]),
1307
- });
1308
- ${closureDecls}${preambleDecls}${formatDecls}const _fn = function(d) {
1309
- ${src}
1310
- };
1311
- ${errCore}function isValid(data) { return _fn(data); }
1312
- function validate(data) { ${validBody}; }
1313
- ${exports}`;
1179
+ return require('./lib/aot').toStandaloneModule(this, opts);
1314
1180
  }
1315
1181
 
1316
1182
  // Load a pre-compiled standalone module. Zero schema compilation.
@@ -1494,202 +1360,65 @@ function validate(schema, data) {
1494
1360
  return v.validate(data);
1495
1361
  }
1496
1362
 
1363
+ // Async validation for schemas built with `t.refine(...)`. Structural
1364
+ // validation runs synchronously first; refinements are awaited only when the
1365
+ // value is structurally valid (a refinement body may assume the right shape).
1366
+ // Accepts a schema literal or an existing Validator instance plus its schema.
1367
+ // Returns a Promise<ValidationResult>.
1368
+ async function validateAsync(schemaOrValidator, data) {
1369
+ const refineLib = require('./lib/refine');
1370
+ let validator, schema;
1371
+ if (schemaOrValidator instanceof Validator) {
1372
+ validator = schemaOrValidator;
1373
+ schema = validator._schemaObj;
1374
+ } else {
1375
+ schema = schemaOrValidator;
1376
+ validator = new Validator(schema);
1377
+ }
1378
+ const structural = validator.validate(data);
1379
+ if (!structural.valid) return structural;
1380
+ const refinements = refineLib.getRefinements(schema);
1381
+ if (!refinements) return structural;
1382
+ const issues = await refineLib.runRefinements(refinements, structural.data !== undefined ? structural.data : data);
1383
+ if (issues.length) return { valid: false, errors: issues };
1384
+ return structural;
1385
+ }
1386
+
1387
+ // parseAsync resolves to the validated data, or rejects with an Error whose
1388
+ // `.errors` carries the ValidationError list. Mirrors the parse/validate split
1389
+ // used by Zod-style callers.
1390
+ async function parseAsync(schemaOrValidator, data) {
1391
+ const result = await validateAsync(schemaOrValidator, data);
1392
+ if (result.valid) return result.data !== undefined ? result.data : data;
1393
+ const err = new Error('ata: async validation failed');
1394
+ err.errors = result.errors;
1395
+ throw err;
1396
+ }
1397
+
1497
1398
  function version() {
1498
1399
  if (native) return native.version();
1499
- try { return require("./package.json").version; } catch { return "unknown"; }
1400
+ try { return require("./lib/version"); } catch { return "unknown"; }
1500
1401
  }
1501
1402
 
1502
- // Bundle multiple validators into a single JS file for fast startup.
1503
- // Usage:
1504
- // const bundle = Validator.bundle([schema1, schema2, ...]);
1505
- // fs.writeFileSync('validators.js', bundle);
1506
- // // On startup:
1507
- // const validators = Validator.loadBundle(require('./validators.js'), [schema1, schema2, ...]);
1403
+ // Static AOT entry points are thin lazy-loaders into `lib/aot.js`. The
1404
+ // implementation files (and the `fs`/`path` reads they perform) only enter
1405
+ // the process when one of these is actually called. See `lib/aot.js` for the
1406
+ // generated module shapes; browser bundles get `lib/aot.browser.js` (a stub
1407
+ // that throws) via the package.json `browser` field.
1508
1408
  Validator.bundle = function (schemas, opts) {
1509
- const parts = schemas.map((schema) => {
1510
- const v = new Validator(schema, opts);
1511
- const standalone = v.toStandalone();
1512
- if (!standalone) return "null";
1513
- return (
1514
- "(function(){" +
1515
- standalone
1516
- .replace("'use strict';", "")
1517
- .replace("module.exports = ", "return ") +
1518
- "})()"
1519
- );
1520
- });
1521
- return "'use strict';\nmodule.exports = [\n" + parts.join(",\n") + "\n];\n";
1409
+ return require('./lib/aot').bundle(Validator, schemas, opts);
1522
1410
  };
1523
1411
 
1524
- // Zero-dependency self-contained bundle — no require('ata-validator') needed at runtime.
1525
- // opts.format: 'cjs' (default) or 'esm'.
1526
- // opts.formats: { name: fn } — embedded in the output via Function#toString.
1527
1412
  Validator.bundleStandalone = function (schemas, opts) {
1528
- // Cross-schema $ref resolution: only meaningful when at least one schema has
1529
- // an $id. Skip the schemas-as-map plumbing when none of them do.
1530
- const haveIds = schemas.some((s) => s && typeof s === 'object' && s.$id);
1531
- const bundleOpts = haveIds ? { ...(opts || {}), schemas } : (opts || {});
1532
- const format = (opts && opts.format) || 'cjs';
1533
- const R = "Object.freeze({valid:true,errors:Object.freeze([])})";
1534
- let bundleUsesSafeRe = false;
1535
- const fns = schemas.map((schema) => {
1536
- const v = new Validator(schema, bundleOpts);
1537
- v._ensureCompiled();
1538
- const jsFn = v._jsFn;
1539
- if (!jsFn || !jsFn._hybridSource) return "null";
1540
- const jsErrFn = compileToJSCodegenWithErrors(
1541
- typeof schema === "string" ? JSON.parse(schema) : schema,
1542
- v._schemaMap,
1543
- v._userFormats,
1544
- );
1545
- if (jsFn._usesSafeRe || (jsErrFn && jsErrFn._usesSafeRe)) bundleUsesSafeRe = true;
1546
- const errBody =
1547
- jsErrFn && jsErrFn._errSource
1548
- ? jsErrFn._errSource
1549
- : "return{valid:false,errors:[{code:'error',path:'',message:'validation failed'}]}";
1550
- // Serialize custom format closures so the bundle has no runtime dep on ata.
1551
- let preamble = '';
1552
- if (jsFn._formatClosures) {
1553
- preamble = jsFn._formatClosures
1554
- .map(({ name, fn }) => `var ${name}=${fn.toString()};`)
1555
- .join('\n');
1556
- }
1557
- // Include hoisted anyOf/oneOf branch helpers (e.g. `_af1_b0`) so the
1558
- // bundle output is self-contained. `toStandalone` emits this same source
1559
- // for single-schema standalone output.
1560
- if (jsFn._preambleSource) {
1561
- preamble = preamble ? `${preamble}\n${jsFn._preambleSource}` : jsFn._preambleSource;
1562
- }
1563
- if (opts && opts.verbose) {
1564
- // Embed the schema and a small resolver so errors carry parentSchema.
1565
- const schemaLit = JSON.stringify(typeof schema === 'string' ? JSON.parse(schema) : schema);
1566
- return `(function(R){${preamble}var _S=${schemaLit};function _PS(p){if(!p||p[0]!=='#')return undefined;var s=p.slice(1);if(!s)return _S;var ps=s.split('/').filter(Boolean).map(function(x){return x.replace(/~1/g,'/').replace(/~0/g,'~')});var t=_S;for(var i=0;i<ps.length-1;i++){if(t==null||typeof t!=='object')return undefined;t=t[ps[i]]}return t}var E=function(d){var _all=true;${errBody}};var _v=function(d){${jsFn._hybridSource}};return function(d){var r=_v(d);if(r&&r.valid===false&&r.errors){var es=[];for(var i=0;i<r.errors.length;i++){var e=r.errors[i];es.push(Object.assign({},e,{parentSchema:_PS(e.schemaPath)}))}return{valid:false,errors:es}}return r}})(R)`;
1567
- }
1568
- return `(function(R){${preamble}var E=function(d){var _all=true;${errBody}};return function(d){${jsFn._hybridSource}}})(R)`;
1569
- });
1570
- const arr = `[${fns.join(",")}]`;
1571
- const safeEmbed = bundleUsesSafeRe ? getSafeRegexEmbed() + "\n" : "";
1572
- if (format === 'esm') {
1573
- return `// Auto-generated by ata-validator — do not edit\n${safeEmbed}const R=${R};\nconst validators=${arr};\nexport default validators;\nexport { validators };\n`;
1574
- }
1575
- return `'use strict';\n${safeEmbed}var R=${R};\nmodule.exports=[${fns.join(",")}];\n`;
1413
+ return require('./lib/aot').bundleStandalone(Validator, schemas, opts);
1576
1414
  };
1577
1415
 
1578
- // Compact bundle: deduplicated code. Shared template functions + per-schema params.
1579
- // Much smaller file → faster V8 parse → faster startup.
1580
- // opts.format: 'cjs' (default) or 'esm'.
1581
1416
  Validator.bundleCompact = function (schemas, opts) {
1582
- const haveIds = schemas.some((s) => s && typeof s === 'object' && s.$id);
1583
- const bundleOpts = haveIds ? { ...(opts || {}), schemas } : (opts || {});
1584
- const format = (opts && opts.format) || 'cjs';
1585
- let bundleUsesSafeRe = false;
1586
- // Analyze schemas and group by structure
1587
- const entries = schemas.map((schema) => {
1588
- const v = new Validator(schema, bundleOpts);
1589
- v._ensureCompiled();
1590
- const jsFn = v._jsFn;
1591
- if (!jsFn || !jsFn._hybridSource) return null;
1592
- const jsErrFn = compileToJSCodegenWithErrors(
1593
- typeof schema === "string" ? JSON.parse(schema) : schema,
1594
- v._schemaMap,
1595
- v._userFormats,
1596
- );
1597
- if (jsFn._usesSafeRe || (jsErrFn && jsErrFn._usesSafeRe)) bundleUsesSafeRe = true;
1598
- // Hoisted anyOf/oneOf branch helpers (e.g. `_af1_b0`) must travel with the
1599
- // hybrid body or it references undefined names. Prepending keeps dedup honest:
1600
- // schemas with different branch sets no longer collide on body alone.
1601
- const hybrid = jsFn._preambleSource
1602
- ? `${jsFn._preambleSource}\n${jsFn._hybridSource}`
1603
- : jsFn._hybridSource;
1604
- return {
1605
- hybrid,
1606
- err: jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : null,
1607
- fmt: jsFn._formatClosures || null,
1608
- };
1609
- });
1610
-
1611
- // Deduplicate function bodies — many schemas produce identical or near-identical code
1612
- const bodyMap = new Map(); // body → index
1613
- const bodies = [];
1614
- const errMap = new Map();
1615
- const errBodies = [];
1616
-
1617
- const indices = entries.map((e) => {
1618
- if (!e) return [-1, -1];
1619
- let hi = bodyMap.get(e.hybrid);
1620
- if (hi === undefined) {
1621
- hi = bodies.length;
1622
- bodies.push(e.hybrid);
1623
- bodyMap.set(e.hybrid, hi);
1624
- }
1625
- let ei = -1;
1626
- if (e.err) {
1627
- ei = errMap.get(e.err);
1628
- if (ei === undefined) {
1629
- ei = errBodies.length;
1630
- errBodies.push(e.err);
1631
- errMap.set(e.err, ei);
1632
- }
1633
- }
1634
- return [hi, ei];
1635
- });
1636
-
1637
- // Generate compact bundle
1638
- const isEsm = format === 'esm';
1639
- let out = isEsm
1640
- ? "// Auto-generated by ata-validator — do not edit\n"
1641
- : "'use strict';\n";
1642
- if (bundleUsesSafeRe) out += getSafeRegexEmbed() + "\n";
1643
- const declKW = isEsm ? "const" : "var";
1644
- out += `${declKW} R=Object.freeze({valid:true,errors:Object.freeze([])});\n`;
1645
-
1646
- // User format functions are referenced as _uf_<name> by the hybrid and error
1647
- // bodies. Collect them across all schemas (deduped by name) and embed via
1648
- // Function#toString so the bundle stays self-contained.
1649
- const fmtSeen = new Set();
1650
- for (const e of entries) {
1651
- if (!e || !e.fmt) continue;
1652
- for (const { name, fn } of e.fmt) {
1653
- if (fmtSeen.has(name)) continue;
1654
- fmtSeen.add(name);
1655
- out += `${declKW} ${name}=${fn.toString()};\n`;
1656
- }
1657
- }
1658
-
1659
- // Shared hybrid factories
1660
- out += `${declKW} H=[\n`;
1661
- out += bodies
1662
- .map((b) => `function(R,E){return function(d){${b}}}`)
1663
- .join(",\n");
1664
- out += "\n];\n";
1665
-
1666
- // Shared error functions
1667
- out += `${declKW} EF=[\n`;
1668
- out += errBodies.map((b) => `function(d){var _all=true;${b}}`).join(",\n");
1669
- out += "\n];\n";
1670
-
1671
- // Build validators from shared templates
1672
- const arrBody = indices
1673
- .map(([hi, ei]) => {
1674
- if (hi < 0) return "null";
1675
- if (ei >= 0) return `H[${hi}](R,EF[${ei}])`;
1676
- return `H[${hi}](R,function(){return{valid:false,errors:[]}})`;
1677
- })
1678
- .join(",");
1679
- if (isEsm) {
1680
- out += `const validators=[${arrBody}];\nexport default validators;\nexport { validators };\n`;
1681
- } else {
1682
- out += `module.exports=[${arrBody}];\n`;
1683
- }
1684
-
1685
- return out;
1417
+ return require('./lib/aot').bundleCompact(Validator, schemas, opts);
1686
1418
  };
1687
1419
 
1688
1420
  Validator.loadBundle = function (mods, schemas, opts) {
1689
- return schemas.map((schema, i) => {
1690
- if (mods[i]) return Validator.fromStandalone(mods[i], schema, opts);
1691
- return new Validator(schema, opts);
1692
- });
1421
+ return require('./lib/aot').loadBundle(Validator, mods, schemas, opts);
1693
1422
  };
1694
1423
 
1695
1424
  const parseJSON = native ? native.parseJSON : JSON.parse;
@@ -1764,6 +1493,8 @@ module.exports = {
1764
1493
  Validator,
1765
1494
  compile,
1766
1495
  validate,
1496
+ validateAsync,
1497
+ parseAsync,
1767
1498
  version,
1768
1499
  createPaddedBuffer,
1769
1500
  SIMDJSON_PADDING,
package/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import mod from './index.js';
2
- export const { Validator, validate, version, createPaddedBuffer, SIMDJSON_PADDING, defineSchema, renderPretty, renderCompact, renderJSON } = mod;
2
+ export const { Validator, validate, validateAsync, parseAsync, version, createPaddedBuffer, SIMDJSON_PADDING, defineSchema, renderPretty, renderCompact, renderJSON } = mod;
3
3
  export default mod;
package/lib/aot-build.js CHANGED
@@ -231,4 +231,33 @@ async function watch(opts, onReport) {
231
231
  };
232
232
  }
233
233
 
234
- module.exports = { build, expandGlobs, parseSchemaFile, outputPathFor, watch };
234
+ // AOT primitives also surface here so consumers can do
235
+ // import { bundleStandalone, toStandaloneModule, bundleCompact } from 'ata-validator/build'
236
+ // without poking at the Validator class. The Validator-bound forms (Validator.bundleStandalone,
237
+ // validator.toStandaloneModule) remain available from the default entry; this is the
238
+ // programmatic alias for code that wants the build surface in one place.
239
+ const aot = require('./aot');
240
+
241
+ function bundleStandalone(schemas, opts) {
242
+ return aot.bundleStandalone(Validator, schemas, opts);
243
+ }
244
+
245
+ function bundleCompact(schemas, opts) {
246
+ return aot.bundleCompact(Validator, schemas, opts);
247
+ }
248
+
249
+ function toStandaloneModule(schema, opts) {
250
+ const v = schema instanceof Validator ? schema : new Validator(schema, opts);
251
+ return aot.toStandaloneModule(v, opts);
252
+ }
253
+
254
+ module.exports = {
255
+ build,
256
+ expandGlobs,
257
+ parseSchemaFile,
258
+ outputPathFor,
259
+ watch,
260
+ bundleStandalone,
261
+ bundleCompact,
262
+ toStandaloneModule,
263
+ };