ata-validator 0.17.3 → 0.17.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to ata-validator are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/), and this project adheres to semantic versioning.
4
4
 
5
+ ## 0.17.4 - 2026-05-25
6
+
7
+ ### Fixed
8
+
9
+ - Standalone output now embeds user-supplied format functions. `toStandaloneModule` and `bundleCompact` referenced the `_uf_<name>` format helpers without declaring them, so the generated module threw `_uf_<name> is not defined` on the first validation, and their error path skipped the custom format entirely (so `validate` disagreed with `isValid`). Both now serialize the format functions via `Function#toString` and run them on the error path too, matching `bundleStandalone`.
10
+ - Compiled validators report per-property errors for schema-valued `additionalProperties`. The AOT error path previously emitted a single generic `validation failed`; it now validates each undeclared property against the subschema and reports the precise `/<key>` error, matching the runtime validator.
11
+ - `ata --version` (and `-V`) prints the CLI version instead of failing with `unknown command`.
12
+
5
13
  ## 0.17.3 - 2026-05-25
6
14
 
7
15
  ### Security
package/bin/ata.js CHANGED
@@ -45,6 +45,7 @@ Build options:
45
45
  --dual Emit both .compiled.mjs (with source) and .compiled.min.mjs (without)
46
46
 
47
47
  -h, --help Show this message
48
+ -V, --version Print the ata-validator version
48
49
 
49
50
  Examples:
50
51
  ata compile schemas/user.json -o src/generated/user.validator.mjs
@@ -386,6 +387,10 @@ function main() {
386
387
 
387
388
  const cmd = argv[0];
388
389
  if (cmd === '-h' || cmd === '--help' || cmd === 'help') { usage(); return; }
390
+ if (cmd === '-V' || cmd === '--version' || cmd === 'version') {
391
+ process.stdout.write(require('../package.json').version + '\n');
392
+ return;
393
+ }
389
394
 
390
395
  const rest = argv.slice(1);
391
396
  let args;
package/index.js CHANGED
@@ -1214,7 +1214,7 @@ module.exports = { boolFn, hybridFactory, errFn };
1214
1214
  jsErrFn = compileToJSCodegenWithErrors(
1215
1215
  typeof this._schemaObj === 'object' ? this._schemaObj : {},
1216
1216
  null,
1217
- undefined,
1217
+ this._userFormats,
1218
1218
  (source && sourceMap && schemaFile) ? { sourceMap, schemaFile } : null,
1219
1219
  );
1220
1220
  const errSrc = jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : '';
@@ -1266,6 +1266,16 @@ module.exports = { boolFn, hybridFactory, errFn };
1266
1266
  // them at module scope too, or _fn references undefined names (e.g. _af1_b0).
1267
1267
  const preambleDecls = jsFn._preambleSource ? jsFn._preambleSource + '\n' : '';
1268
1268
 
1269
+ // User-supplied format functions are referenced as _uf_<name> by both the
1270
+ // boolean (_fn) and error (errFn) bodies. Embed them via Function#toString
1271
+ // so the standalone module stays self-contained.
1272
+ let formatDecls = '';
1273
+ if (jsFn._formatClosures && jsFn._formatClosures.length > 0) {
1274
+ formatDecls = jsFn._formatClosures
1275
+ .map(({ name, fn }) => `const ${name} = ${fn.toString()};`)
1276
+ .join('\n') + '\n';
1277
+ }
1278
+
1269
1279
  const validBody = errCore
1270
1280
  ? 'return _fn(data) ? VALID : { valid: false, errors: errFn(data, true).errors }'
1271
1281
  : 'return _fn(data) ? VALID : ABORT';
@@ -1288,7 +1298,7 @@ const ABORT = Object.freeze({
1288
1298
  path: '',
1289
1299
  })]),
1290
1300
  });
1291
- ${closureDecls}${preambleDecls}const _fn = function(d) {
1301
+ ${closureDecls}${preambleDecls}${formatDecls}const _fn = function(d) {
1292
1302
  ${src}
1293
1303
  };
1294
1304
  ${errCore}function isValid(data) { return _fn(data); }
@@ -1575,6 +1585,7 @@ Validator.bundleCompact = function (schemas, opts) {
1575
1585
  const jsErrFn = compileToJSCodegenWithErrors(
1576
1586
  typeof schema === "string" ? JSON.parse(schema) : schema,
1577
1587
  v._schemaMap,
1588
+ v._userFormats,
1578
1589
  );
1579
1590
  if (jsFn._usesSafeRe || (jsErrFn && jsErrFn._usesSafeRe)) bundleUsesSafeRe = true;
1580
1591
  // Hoisted anyOf/oneOf branch helpers (e.g. `_af1_b0`) must travel with the
@@ -1586,6 +1597,7 @@ Validator.bundleCompact = function (schemas, opts) {
1586
1597
  return {
1587
1598
  hybrid,
1588
1599
  err: jsErrFn && jsErrFn._errSource ? jsErrFn._errSource : null,
1600
+ fmt: jsFn._formatClosures || null,
1589
1601
  };
1590
1602
  });
1591
1603
 
@@ -1624,6 +1636,19 @@ Validator.bundleCompact = function (schemas, opts) {
1624
1636
  const declKW = isEsm ? "const" : "var";
1625
1637
  out += `${declKW} R=Object.freeze({valid:true,errors:Object.freeze([])});\n`;
1626
1638
 
1639
+ // User format functions are referenced as _uf_<name> by the hybrid and error
1640
+ // bodies. Collect them across all schemas (deduped by name) and embed via
1641
+ // Function#toString so the bundle stays self-contained.
1642
+ const fmtSeen = new Set();
1643
+ for (const e of entries) {
1644
+ if (!e || !e.fmt) continue;
1645
+ for (const { name, fn } of e.fmt) {
1646
+ if (fmtSeen.has(name)) continue;
1647
+ fmtSeen.add(name);
1648
+ out += `${declKW} ${name}=${fn.toString()};\n`;
1649
+ }
1650
+ }
1651
+
1627
1652
  // Shared hybrid factories
1628
1653
  out += `${declKW} H=[\n`;
1629
1654
  out += bodies
@@ -2558,9 +2558,6 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
2558
2558
  if (s.includes('unevaluatedProperties') || s.includes('unevaluatedItems')) return null
2559
2559
  // Bail on self-referencing schemas — error codegen doesn't support recursion
2560
2560
  if (s.includes('"$ref":"#"')) return null
2561
- // Bail on additionalProperties as a schema — genCodeE does not emit the
2562
- // per-key sub-schema loop. Let the closure path handle detailed errors.
2563
- if (hasAdditionalPropertiesSchema(schema)) return null
2564
2561
  }
2565
2562
  if (typeof schema === 'boolean') {
2566
2563
  return schema
@@ -3012,6 +3009,21 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
3012
3009
  }
3013
3010
  }
3014
3011
 
3012
+ // additionalProperties as a schema — validate each property not declared in
3013
+ // `properties` against the subschema, with a dynamic /<key> path. codegenSafe
3014
+ // guarantees no patternProperties or composition here, so "additional" is
3015
+ // simply "not declared".
3016
+ if (typeof schema.additionalProperties === 'object' && schema.additionalProperties !== null) {
3017
+ const ki = ctx.varCounter++
3018
+ const known = Object.keys(schema.properties || {})
3019
+ const guard = known.length ? `const _ak${ki}=new Set([${known.map((k) => JSON.stringify(k)).join(',')}]);` : ''
3020
+ const keep = known.length ? `if(!_ak${ki}.has(_k${ki})){` : `{`
3021
+ lines.push(`if(typeof ${v}==='object'&&${v}!==null&&!Array.isArray(${v})){${guard}for(const _k${ki} in ${v}){${keep}`)
3022
+ const p = pathExpr ? `${pathExpr}+'/'+_k${ki}` : `'/'+_k${ki}`
3023
+ genCodeE(schema.additionalProperties, `${v}[_k${ki}]`, p, lines, ctx, schemaPrefix + '/additionalProperties')
3024
+ lines.push(`}}}`)
3025
+ }
3026
+
3015
3027
  // dependentSchemas
3016
3028
  if (schema.dependentSchemas) {
3017
3029
  for (const [key, depSchema] of Object.entries(schema.dependentSchemas)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "0.17.3",
3
+ "version": "0.17.4",
4
4
  "description": "JSON Schema validation with first-class TypeScript and zero runtime cost. AOT compile to per-schema ESM modules with zero validator dependency. Generic Validator<T> for TypeBox/Zod/Valibot composition. Optional runtime API. Standard Schema V1 compatible.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -42,7 +42,7 @@
42
42
  "rebuild": "cmake-js rebuild --target ata",
43
43
  "prebuild": "pkg-prebuilds-copy --baseDir build/Release --source ata.node --name=ata --strip --napi_version=10",
44
44
  "prebuild-all": "npm run prebuild -- --arch x64 && npm run prebuild -- --arch arm64",
45
- "test": "node test.js && node tests/test_no_native.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_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.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_rich_errors_optout.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.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 benchmark/bench_aot_size.mjs",
45
+ "test": "node test.js && node tests/test_no_native.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_additional_props_errors.js && node tests/test_typed_validator_runner.js && node tests/test_define_schema.js && node tests/test_error_codes_lock.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_rich_errors_optout.js && node tests/test_source_positions.js && node tests/fuzz_positions.js && node tests/test_data_positions.js && node tests/test_render_shared.js && node tests/test_renderers.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",
46
46
  "bench:size": "node benchmark/bench_aot_size.mjs",
47
47
  "test:suite": "node tests/run_suite.js",
48
48
  "test:compat": "node tests/test_compat.js",