ata-validator 0.17.4 → 0.17.5
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 +6 -0
- package/lib/js-compiler.js +14 -25
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
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.5 - 2026-05-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Compiled validators resolve draft-07 plain-name anchors. A `$defs`/`definitions` entry that declares an anchor with `$id: "#name"` and is referenced by `$ref: "#name"` now compiles through the codegen on every path (boolean, error, combined) instead of bailing. The bail forced a fallback that could not resolve sibling cross-schema refs, which surfaced as `cannot resolve $ref`. This is how shared schemas are referenced under Fastify.
|
|
10
|
+
|
|
5
11
|
## 0.17.4 - 2026-05-25
|
|
6
12
|
|
|
7
13
|
### Fixed
|
package/lib/js-compiler.js
CHANGED
|
@@ -699,23 +699,6 @@ function canResolveDynamicRefs(target, callingSchema, schemaMap) {
|
|
|
699
699
|
|
|
700
700
|
// Recursively check if a schema can be safely compiled to JS codegen.
|
|
701
701
|
// Returns false if any sub-schema contains features codegen gets wrong.
|
|
702
|
-
// Does the schema (recursively) contain an anchor-style `$ref` equal to anchorId
|
|
703
|
-
// (e.g. `$ref: '#address'`)? Used to keep codegen away from anchor refs it can't
|
|
704
|
-
// resolve in the combined/error paths.
|
|
705
|
-
function schemaHasAnchorRef(node, anchorId) {
|
|
706
|
-
if (!node || typeof node !== 'object') return false
|
|
707
|
-
if (Array.isArray(node)) {
|
|
708
|
-
for (const x of node) if (schemaHasAnchorRef(x, anchorId)) return true
|
|
709
|
-
return false
|
|
710
|
-
}
|
|
711
|
-
if (node.$ref === anchorId) return true
|
|
712
|
-
for (const k in node) {
|
|
713
|
-
if (k === '$ref') continue
|
|
714
|
-
if (schemaHasAnchorRef(node[k], anchorId)) return true
|
|
715
|
-
}
|
|
716
|
-
return false
|
|
717
|
-
}
|
|
718
|
-
|
|
719
702
|
function codegenSafe(schema, schemaMap) {
|
|
720
703
|
if (typeof schema === 'boolean') return true
|
|
721
704
|
if (typeof schema !== 'object' || schema === null) return true
|
|
@@ -826,14 +809,10 @@ function codegenSafe(schema, schemaMap) {
|
|
|
826
809
|
if (typeof def === 'boolean') return false
|
|
827
810
|
if (typeof def === 'object' && def !== null) {
|
|
828
811
|
// A non-fragment $id (e.g. 'sub.json', 'http://...') opens a new base URI
|
|
829
|
-
// scope — bail. A fragment-only $id ('#name') is a draft-07 anchor;
|
|
830
|
-
//
|
|
831
|
-
//
|
|
832
|
-
|
|
833
|
-
if (def.$id) {
|
|
834
|
-
if (!def.$id.startsWith('#')) return false
|
|
835
|
-
if (schemaHasAnchorRef(schema, def.$id)) return false
|
|
836
|
-
}
|
|
812
|
+
// scope — bail. A fragment-only $id ('#name') is a draft-07 anchor; the
|
|
813
|
+
// codegen anchor maps register it (see "Build anchors map"), so anchor
|
|
814
|
+
// refs (`$ref: '#name'`) resolve to it on all paths.
|
|
815
|
+
if (def.$id && !def.$id.startsWith('#')) return false
|
|
837
816
|
if (def.$ref) return false // nested ref chain — bail
|
|
838
817
|
if (!codegenSafe(def, schemaMap)) return false
|
|
839
818
|
}
|
|
@@ -933,12 +912,15 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
933
912
|
// Root schema's own $dynamicAnchor / $anchor
|
|
934
913
|
if (schema.$dynamicAnchor) anchors['#' + schema.$dynamicAnchor] = schema
|
|
935
914
|
if (schema.$anchor) anchors['#' + schema.$anchor] = schema
|
|
915
|
+
// Draft-07 plain-name anchor: declared as `$id: "#name"`.
|
|
916
|
+
if (typeof schema.$id === 'string' && schema.$id.startsWith('#')) anchors[schema.$id] = schema
|
|
936
917
|
// Anchors from $defs
|
|
937
918
|
if (rootDefs) {
|
|
938
919
|
for (const def of Object.values(rootDefs)) {
|
|
939
920
|
if (def && typeof def === 'object') {
|
|
940
921
|
if (def.$dynamicAnchor) anchors['#' + def.$dynamicAnchor] = def
|
|
941
922
|
if (def.$anchor) anchors['#' + def.$anchor] = def
|
|
923
|
+
if (typeof def.$id === 'string' && def.$id.startsWith('#')) anchors[def.$id] = def
|
|
942
924
|
}
|
|
943
925
|
}
|
|
944
926
|
}
|
|
@@ -948,6 +930,7 @@ function compileToJSCodegen(schema, schemaMap, userFormats) {
|
|
|
948
930
|
if (ext && typeof ext === 'object') {
|
|
949
931
|
if (ext.$dynamicAnchor && !anchors['#' + ext.$dynamicAnchor]) anchors['#' + ext.$dynamicAnchor] = ext
|
|
950
932
|
if (ext.$anchor && !anchors['#' + ext.$anchor]) anchors['#' + ext.$anchor] = ext
|
|
933
|
+
if (typeof ext.$id === 'string' && ext.$id.startsWith('#') && !anchors[ext.$id]) anchors[ext.$id] = ext
|
|
951
934
|
}
|
|
952
935
|
}
|
|
953
936
|
}
|
|
@@ -2592,11 +2575,13 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
2592
2575
|
const eAnchors = {}
|
|
2593
2576
|
if (schema.$dynamicAnchor) eAnchors['#' + schema.$dynamicAnchor] = schema
|
|
2594
2577
|
if (schema.$anchor) eAnchors['#' + schema.$anchor] = schema
|
|
2578
|
+
if (typeof schema.$id === 'string' && schema.$id.startsWith('#')) eAnchors[schema.$id] = schema
|
|
2595
2579
|
if (eRootDefs) {
|
|
2596
2580
|
for (const def of Object.values(eRootDefs)) {
|
|
2597
2581
|
if (def && typeof def === 'object') {
|
|
2598
2582
|
if (def.$dynamicAnchor) eAnchors['#' + def.$dynamicAnchor] = def
|
|
2599
2583
|
if (def.$anchor) eAnchors['#' + def.$anchor] = def
|
|
2584
|
+
if (typeof def.$id === 'string' && def.$id.startsWith('#')) eAnchors[def.$id] = def
|
|
2600
2585
|
}
|
|
2601
2586
|
}
|
|
2602
2587
|
}
|
|
@@ -2605,6 +2590,7 @@ function compileToJSCodegenWithErrors(schema, schemaMap, userFormats, sourceOpts
|
|
|
2605
2590
|
if (ext && typeof ext === 'object') {
|
|
2606
2591
|
if (ext.$dynamicAnchor && !eAnchors['#' + ext.$dynamicAnchor]) eAnchors['#' + ext.$dynamicAnchor] = ext
|
|
2607
2592
|
if (ext.$anchor && !eAnchors['#' + ext.$anchor]) eAnchors['#' + ext.$anchor] = ext
|
|
2593
|
+
if (typeof ext.$id === 'string' && ext.$id.startsWith('#') && !eAnchors[ext.$id]) eAnchors[ext.$id] = ext
|
|
2608
2594
|
}
|
|
2609
2595
|
}
|
|
2610
2596
|
}
|
|
@@ -3210,11 +3196,13 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
|
|
|
3210
3196
|
const cAnchors = {}
|
|
3211
3197
|
if (schema.$dynamicAnchor) cAnchors['#' + schema.$dynamicAnchor] = schema
|
|
3212
3198
|
if (schema.$anchor) cAnchors['#' + schema.$anchor] = schema
|
|
3199
|
+
if (typeof schema.$id === 'string' && schema.$id.startsWith('#')) cAnchors[schema.$id] = schema
|
|
3213
3200
|
if (cRootDefs) {
|
|
3214
3201
|
for (const def of Object.values(cRootDefs)) {
|
|
3215
3202
|
if (def && typeof def === 'object') {
|
|
3216
3203
|
if (def.$dynamicAnchor) cAnchors['#' + def.$dynamicAnchor] = def
|
|
3217
3204
|
if (def.$anchor) cAnchors['#' + def.$anchor] = def
|
|
3205
|
+
if (typeof def.$id === 'string' && def.$id.startsWith('#')) cAnchors[def.$id] = def
|
|
3218
3206
|
}
|
|
3219
3207
|
}
|
|
3220
3208
|
}
|
|
@@ -3223,6 +3211,7 @@ function compileToJSCombined(schema, VALID_RESULT, schemaMap, userFormats) {
|
|
|
3223
3211
|
if (ext && typeof ext === 'object') {
|
|
3224
3212
|
if (ext.$dynamicAnchor && !cAnchors['#' + ext.$dynamicAnchor]) cAnchors['#' + ext.$dynamicAnchor] = ext
|
|
3225
3213
|
if (ext.$anchor && !cAnchors['#' + ext.$anchor]) cAnchors['#' + ext.$anchor] = ext
|
|
3214
|
+
if (typeof ext.$id === 'string' && ext.$id.startsWith('#') && !cAnchors[ext.$id]) cAnchors[ext.$id] = ext
|
|
3226
3215
|
}
|
|
3227
3216
|
}
|
|
3228
3217
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.5",
|
|
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_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",
|
|
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_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_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",
|