ata-validator 0.12.4 → 0.12.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/lib/js-compiler.js +85 -2
- package/package.json +1 -1
package/lib/js-compiler.js
CHANGED
|
@@ -496,6 +496,50 @@ function collectDefs(schema) {
|
|
|
496
496
|
return defs
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
+
// Walk a JSON-pointer fragment ("/foo/bar/0") into a schema object.
|
|
500
|
+
// Returns the target node, or null if any segment is missing.
|
|
501
|
+
function walkJsonPointer(root, fragment) {
|
|
502
|
+
if (!fragment || fragment === '/' || fragment === '#') return root
|
|
503
|
+
const path = fragment.startsWith('#') ? fragment.slice(1) : fragment
|
|
504
|
+
if (!path.startsWith('/')) return null
|
|
505
|
+
const parts = path.split('/').slice(1).map(s => s.replace(/~1/g, '/').replace(/~0/g, '~'))
|
|
506
|
+
let target = root
|
|
507
|
+
for (const p of parts) {
|
|
508
|
+
if (target == null || typeof target !== 'object') return null
|
|
509
|
+
target = target[p]
|
|
510
|
+
}
|
|
511
|
+
return target == null ? null : target
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Resolve a cross-schema $ref of the form "<id>#/<json-pointer>" (or just "<id>").
|
|
515
|
+
// Returns { schema, fullId } where fullId is the resolved $id of the host schema.
|
|
516
|
+
function resolveCrossSchemaRef(ref, schemaMap) {
|
|
517
|
+
if (!schemaMap) return null
|
|
518
|
+
const hashIdx = ref.indexOf('#')
|
|
519
|
+
const baseId = hashIdx >= 0 ? ref.slice(0, hashIdx) : ref
|
|
520
|
+
const fragment = hashIdx >= 0 ? ref.slice(hashIdx) : ''
|
|
521
|
+
if (!baseId) return null
|
|
522
|
+
|
|
523
|
+
let baseSchema = null
|
|
524
|
+
let fullId = null
|
|
525
|
+
if (schemaMap.has(baseId)) {
|
|
526
|
+
baseSchema = schemaMap.get(baseId)
|
|
527
|
+
fullId = baseId
|
|
528
|
+
} else if (!ref.includes('://')) {
|
|
529
|
+
for (const [id] of schemaMap) {
|
|
530
|
+
if (id.endsWith('/' + baseId)) {
|
|
531
|
+
baseSchema = schemaMap.get(id)
|
|
532
|
+
fullId = id
|
|
533
|
+
break
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (!baseSchema) return null
|
|
538
|
+
const target = fragment ? walkJsonPointer(baseSchema, fragment) : baseSchema
|
|
539
|
+
if (target == null) return null
|
|
540
|
+
return { schema: target, fullId }
|
|
541
|
+
}
|
|
542
|
+
|
|
499
543
|
function resolveRef(ref, defs, schemaMap) {
|
|
500
544
|
// Self-reference: "#" — treat as permissive to avoid infinite recursion
|
|
501
545
|
if (ref === '#') return () => true
|
|
@@ -520,7 +564,15 @@ function resolveRef(ref, defs, schemaMap) {
|
|
|
520
564
|
const fn = compileToJS(resolved, null, schemaMap)
|
|
521
565
|
return fn || (() => true)
|
|
522
566
|
}
|
|
523
|
-
// 3. Cross-schema ref
|
|
567
|
+
// 3. Cross-schema ref with JSON pointer fragment ("<id>#/<path>")
|
|
568
|
+
if (schemaMap && ref.includes('#')) {
|
|
569
|
+
const r = resolveCrossSchemaRef(ref, schemaMap)
|
|
570
|
+
if (r) {
|
|
571
|
+
const fn = compileToJS(r.schema, null, schemaMap)
|
|
572
|
+
return fn || (() => true)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
// 4. Cross-schema ref (relative URI resolution, no fragment)
|
|
524
576
|
if (schemaMap && !ref.includes('://') && !ref.startsWith('#')) {
|
|
525
577
|
for (const [id] of schemaMap) {
|
|
526
578
|
if (id.endsWith('/' + ref)) {
|
|
@@ -652,6 +704,11 @@ function codegenSafe(schema, schemaMap) {
|
|
|
652
704
|
if (id.endsWith('/' + schema.$ref)) { isResolvable = true; resolvedTarget = schemaMap.get(id); break }
|
|
653
705
|
}
|
|
654
706
|
}
|
|
707
|
+
// Cross-schema ref with JSON pointer fragment: "<id>#/<path>"
|
|
708
|
+
if (!isLocal && !isResolvable && schemaMap && schema.$ref.includes('#') && !schema.$ref.startsWith('#')) {
|
|
709
|
+
const r = resolveCrossSchemaRef(schema.$ref, schemaMap)
|
|
710
|
+
if (r) { isResolvable = true; resolvedTarget = r.schema }
|
|
711
|
+
}
|
|
655
712
|
// Anchor-style ref: #name (not #/path, not bare #) — resolvable at compile time via anchors map
|
|
656
713
|
const isAnchorRef = !isLocal && !isResolvable && schema.$ref.length > 1 && schema.$ref.startsWith('#') && !schema.$ref.startsWith('#/')
|
|
657
714
|
if (!isLocal && !isResolvable && !isAnchorRef) return false
|
|
@@ -1093,13 +1150,17 @@ function genCode(schema, v, lines, ctx, knownType) {
|
|
|
1093
1150
|
}
|
|
1094
1151
|
}
|
|
1095
1152
|
} else if (schema.$ref !== '#' && ctx.schemaMap) {
|
|
1096
|
-
// 2. Cross-schema ref (exact match or
|
|
1153
|
+
// 2. Cross-schema ref (exact match, relative URI, or JSON pointer fragment)
|
|
1097
1154
|
let resolved = ctx.schemaMap.get(schema.$ref)
|
|
1098
1155
|
if (!resolved && !schema.$ref.includes('://') && !schema.$ref.startsWith('#')) {
|
|
1099
1156
|
for (const [id, s] of ctx.schemaMap) {
|
|
1100
1157
|
if (id.endsWith('/' + schema.$ref)) { resolved = s; break }
|
|
1101
1158
|
}
|
|
1102
1159
|
}
|
|
1160
|
+
if (!resolved && schema.$ref.includes('#') && !schema.$ref.startsWith('#')) {
|
|
1161
|
+
const r = resolveCrossSchemaRef(schema.$ref, ctx.schemaMap)
|
|
1162
|
+
if (r) resolved = r.schema
|
|
1163
|
+
}
|
|
1103
1164
|
if (resolved) {
|
|
1104
1165
|
if (ctx.refStack.has(schema.$ref)) { if (!hasSiblings) return }
|
|
1105
1166
|
else {
|
|
@@ -2516,6 +2577,17 @@ function genCodeE(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
2516
2577
|
ctx.refStack.delete(schema.$ref)
|
|
2517
2578
|
return
|
|
2518
2579
|
}
|
|
2580
|
+
// Cross-schema ref with JSON pointer fragment ("<id>#/<path>")
|
|
2581
|
+
if (ctx.schemaMap && schema.$ref.includes('#') && !schema.$ref.startsWith('#')) {
|
|
2582
|
+
const r = resolveCrossSchemaRef(schema.$ref, ctx.schemaMap)
|
|
2583
|
+
if (r) {
|
|
2584
|
+
if (ctx.refStack.has(schema.$ref)) return
|
|
2585
|
+
ctx.refStack.add(schema.$ref)
|
|
2586
|
+
genCodeE(r.schema, v, pathExpr, lines, ctx, schemaPrefix)
|
|
2587
|
+
ctx.refStack.delete(schema.$ref)
|
|
2588
|
+
return
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2519
2591
|
}
|
|
2520
2592
|
|
|
2521
2593
|
// $dynamicRef — resolve via anchors map
|
|
@@ -3022,6 +3094,17 @@ function genCodeC(schema, v, pathExpr, lines, ctx, schemaPrefix) {
|
|
|
3022
3094
|
ctx.refStack.delete(schema.$ref)
|
|
3023
3095
|
return
|
|
3024
3096
|
}
|
|
3097
|
+
// Cross-schema ref with JSON pointer fragment ("<id>#/<path>")
|
|
3098
|
+
if (ctx.schemaMap && schema.$ref.includes('#') && !schema.$ref.startsWith('#')) {
|
|
3099
|
+
const r = resolveCrossSchemaRef(schema.$ref, ctx.schemaMap)
|
|
3100
|
+
if (r) {
|
|
3101
|
+
if (ctx.refStack.has(schema.$ref)) return
|
|
3102
|
+
ctx.refStack.add(schema.$ref)
|
|
3103
|
+
genCodeC(r.schema, v, pathExpr, lines, ctx, schemaPrefix)
|
|
3104
|
+
ctx.refStack.delete(schema.$ref)
|
|
3105
|
+
return
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3025
3108
|
}
|
|
3026
3109
|
|
|
3027
3110
|
// $dynamicRef — resolve via anchors map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ata-validator",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.5",
|
|
4
4
|
"description": "Ultra-fast JSON Schema validator. 5x faster validation, 159,000x faster compilation. Works without native addon. Cross-schema $ref, Draft 2020-12 + Draft 7, V8-optimized JS codegen, simdjson, RE2, multi-core. Standard Schema V1 compatible.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "index.mjs",
|