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.
Files changed (2) hide show
  1. package/lib/js-compiler.js +85 -2
  2. package/package.json +1 -1
@@ -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 (relative URI resolution)
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 relative URI)
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.4",
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",