ata-validator 1.0.0 → 1.0.2

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,20 @@
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
+ ## 1.0.2 - 2026-07-17
6
+
7
+ ### Fixed
8
+
9
+ - Cross-schema `$ref` pointers into `#/definitions/...` resolved to nothing after draft-07 normalization renamed the target to `$defs`, and the generated validator silently accepted invalid data. The pointer walk now treats `definitions` and `$defs` as aliases.
10
+ - Schemas passed by the caller (the `schemas` option, `addSchema()`, and the root schema) were normalized in place, mutating objects the caller still owns. Anyone reusing those objects afterwards, such as Fastify handing the same shared schema to its serializer, saw corrupted keys. Normalization now works on a copy; caller objects are never touched.
11
+ - The new copy preserves symbol-keyed markers, so `t.refine` refinements survive normalization and `validateAsync` keeps enforcing them.
12
+
13
+ ## 1.0.1 - 2026-07-16
14
+
15
+ ### Fixed
16
+
17
+ - `version()` reported 0.10.4 on platforms with a native prebuild: the `ATA_VERSION` constant in `include/ata.h` had not been bumped since 0.10.4 and the native answer takes precedence over `lib/version.js`. The header now carries the real version and the version-sync test checks it, so it cannot drift again.
18
+
5
19
  ## 1.0.0 - 2026-07-15
6
20
 
7
21
  1.0 is a stability commitment, not a feature release. The API surface, the error result shape, and the error code registry are now covered by the semver guarantees in [docs/STABILITY.md](docs/STABILITY.md).
package/include/ata.h CHANGED
@@ -8,16 +8,16 @@
8
8
  #include <variant>
9
9
  #include <vector>
10
10
 
11
- #define ATA_VERSION "0.10.4"
11
+ #define ATA_VERSION "1.0.2"
12
12
 
13
13
  namespace ata {
14
14
 
15
- inline constexpr uint32_t VERSION_MAJOR = 0;
16
- inline constexpr uint32_t VERSION_MINOR = 10;
17
- inline constexpr uint32_t VERSION_REVISION = 4;
15
+ inline constexpr uint32_t VERSION_MAJOR = 1;
16
+ inline constexpr uint32_t VERSION_MINOR = 0;
17
+ inline constexpr uint32_t VERSION_REVISION = 2;
18
18
 
19
19
  inline constexpr std::string_view version() noexcept {
20
- return "0.10.4";
20
+ return ATA_VERSION;
21
21
  }
22
22
 
23
23
  enum class error_code : uint8_t {
package/index.js CHANGED
@@ -344,22 +344,57 @@ function createPaddedBuffer(jsonStr) {
344
344
  return { buffer: padded, length: jsonBuf.length };
345
345
  }
346
346
 
347
+ // Deep-clone a value, copying own symbol keys by reference at every level.
348
+ // Arrays and plain objects are cloned recursively; primitives, RegExp,
349
+ // functions, and other non-plain values are returned as-is. Symbol values
350
+ // (e.g. refinement lists, OPTIONAL markers) are owned by the caller's builder
351
+ // and sharing them is correct — they are never mutated by normalization.
352
+ function _deepCloneWithSymbols(v) {
353
+ if (v === null || typeof v !== 'object') return v;
354
+ if (Array.isArray(v)) {
355
+ const a = new Array(v.length);
356
+ for (let i = 0; i < v.length; i++) a[i] = _deepCloneWithSymbols(v[i]);
357
+ return a;
358
+ }
359
+ // Only clone plain objects (skip RegExp, Date, etc.).
360
+ if (Object.getPrototypeOf(v) !== Object.prototype && Object.getPrototypeOf(v) !== null) return v;
361
+ const out = Object.create(null);
362
+ for (const k of Object.keys(v)) Object.defineProperty(out, k, { value: _deepCloneWithSymbols(v[k]), writable: true, enumerable: true, configurable: true });
363
+ for (const sym of Object.getOwnPropertySymbols(v)) out[sym] = v[sym];
364
+ return Object.setPrototypeOf(out, Object.prototype);
365
+ }
366
+
367
+ // Normalize a caller-provided schema without mutating the original.
368
+ // Clones only when normalization would change the object (draft-07 keys
369
+ // present or nullable fields present). Internal-only — not exported.
370
+ function _normalizeCallerSchema(s) {
371
+ const needsDraft7 = s && s.$schema && (
372
+ s.$schema === 'http://json-schema.org/draft-07/schema#' ||
373
+ s.$schema === 'http://json-schema.org/draft-07/schema'
374
+ )
375
+ const str = JSON.stringify(s)
376
+ const copy = _deepCloneWithSymbols(s)
377
+ if (needsDraft7) normalizeDraft7(copy)
378
+ normalizeNullable(copy)
379
+ // Return original when normalization produced no change, copy otherwise.
380
+ // Change-detection uses JSON content only; symbols do not affect it.
381
+ return JSON.stringify(copy) === str ? s : copy
382
+ }
383
+
347
384
  function buildSchemaMap(schemas) {
348
385
  if (!schemas) return null
349
386
  const map = new Map()
350
387
  if (Array.isArray(schemas)) {
351
388
  for (const s of schemas) {
352
- normalizeDraft7(s)
353
- normalizeNullable(s)
354
- const id = s.$id
389
+ const normalized = _normalizeCallerSchema(s)
390
+ const id = normalized.$id
355
391
  if (!id) throw new Error('Schema in schemas option must have $id')
356
- map.set(id, s)
392
+ map.set(id, normalized)
357
393
  }
358
394
  } else {
359
395
  for (const [key, s] of Object.entries(schemas)) {
360
- normalizeDraft7(s)
361
- normalizeNullable(s)
362
- map.set(s.$id || key, s)
396
+ const normalized = _normalizeCallerSchema(s)
397
+ map.set(normalized.$id || key, normalized)
363
398
  }
364
399
  }
365
400
  return map
@@ -449,7 +484,6 @@ function resolveSchemaForPreprocess(schema, schemaMap) {
449
484
  class Validator {
450
485
  constructor(schema, opts) {
451
486
  const options = opts || {};
452
- const schemaObj = typeof schema === "string" ? JSON.parse(schema) : schema;
453
487
 
454
488
  // Ultra-fast path: same schema object reference -> return cached instance
455
489
  // JS constructor returning an object makes `new` return that object
@@ -459,10 +493,12 @@ class Validator {
459
493
  if (hit) return hit;
460
494
  }
461
495
 
462
- // Draft 7 normalization — convert keywords to 2020-12 equivalents in-place
463
- normalizeDraft7(schemaObj);
464
- // OpenAPI nullable -> type union with 'null'
465
- normalizeNullable(schemaObj);
496
+ // When schema is a string, JSON.parse already produces a fresh object.
497
+ // When schema is an object, normalization runs on a clone so the caller's
498
+ // object is never touched.
499
+ const schemaObj = typeof schema === "string"
500
+ ? JSON.parse(schema)
501
+ : _normalizeCallerSchema(schema);
466
502
 
467
503
  this._schemaStr = null; // lazy: computed on first use
468
504
  this._schemaObj = schemaObj;
@@ -1132,10 +1168,9 @@ class Validator {
1132
1168
  if (!schema || !schema.$id) {
1133
1169
  throw new Error('Schema must have $id')
1134
1170
  }
1135
- // Apply Draft 7 normalization if needed
1136
- normalizeDraft7(schema)
1137
- normalizeNullable(schema)
1138
- this._schemaMap.set(schema.$id, schema)
1171
+ // Normalize a copy so the caller's object is never mutated.
1172
+ const normalized = _normalizeCallerSchema(schema)
1173
+ this._schemaMap.set(normalized.$id, normalized)
1139
1174
  }
1140
1175
 
1141
1176
  _ensureCodegen() {
@@ -549,6 +549,11 @@ function walkJsonPointer(root, fragment) {
549
549
  let target = root
550
550
  for (const p of parts) {
551
551
  if (target == null || typeof target !== 'object') return null
552
+ if (!(p in target)) {
553
+ const alt = p === 'definitions' ? '$defs' : p === '$defs' ? 'definitions' : null
554
+ if (alt !== null && alt in target) { target = target[alt]; continue }
555
+ return null
556
+ }
552
557
  target = target[p]
553
558
  }
554
559
  return target == null ? null : target
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.0.0';
10
+ module.exports = '1.0.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ata-validator",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
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",
@@ -48,7 +48,7 @@
48
48
  "rebuild": "cmake-js rebuild --target ata",
49
49
  "prebuild": "pkg-prebuilds-copy --baseDir build/Release --source ata.node --name=ata --strip --napi_version=10",
50
50
  "prebuild-all": "npm run prebuild -- --arch x64 && npm run prebuild -- --arch arm64",
51
- "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.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_additional_props_errors.js && node tests/test_id_anchor_refs.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_error_messages.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",
51
+ "test": "node test.js && node tests/test_removed_aot_methods.js && node tests/test_no_native.js && node tests/test_browser_nofs.js && node tests/test_browser_imports_guard.js && node tests/test_version_sync.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_additional_props_errors.js && node tests/test_id_anchor_refs.js && node tests/test_defs_pointer_alias.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_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_error_messages.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",
52
52
  "bench:size": "node benchmark/bench_aot_size.mjs",
53
53
  "test:suite": "node tests/run_suite.js",
54
54
  "test:compat": "node tests/test_compat.js",