ata-validator 0.12.3 → 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/index.d.ts +42 -3
- package/lib/js-compiler.js +85 -2
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -4,8 +4,17 @@ export interface ValidationError {
|
|
|
4
4
|
schemaPath: string;
|
|
5
5
|
params: Record<string, unknown>;
|
|
6
6
|
message: string;
|
|
7
|
+
/**
|
|
8
|
+
* The schema object that owns the failing keyword. Populated only when the
|
|
9
|
+
* Validator was constructed with `verbose: true`. Matches ajv's `verbose`
|
|
10
|
+
* behavior.
|
|
11
|
+
*/
|
|
12
|
+
parentSchema?: object;
|
|
7
13
|
}
|
|
8
14
|
|
|
15
|
+
/** A user-supplied format checker. Receives the candidate value, returns true if valid. */
|
|
16
|
+
export type FormatChecker = (value: string) => boolean;
|
|
17
|
+
|
|
9
18
|
export interface ValidationResult {
|
|
10
19
|
valid: boolean;
|
|
11
20
|
errors: ValidationError[];
|
|
@@ -21,6 +30,26 @@ export interface ValidatorOptions {
|
|
|
21
30
|
coerceTypes?: boolean;
|
|
22
31
|
removeAdditional?: boolean;
|
|
23
32
|
schemas?: Record<string, object> | object[];
|
|
33
|
+
/**
|
|
34
|
+
* Custom format checkers. Keys are format names referenced from `format` in
|
|
35
|
+
* the schema. Values are functions that return true when the input is valid.
|
|
36
|
+
*/
|
|
37
|
+
formats?: Record<string, FormatChecker>;
|
|
38
|
+
/**
|
|
39
|
+
* When true, validation errors include `parentSchema` (the schema object
|
|
40
|
+
* that produced the error). Matches ajv's `verbose: true`.
|
|
41
|
+
*/
|
|
42
|
+
verbose?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* When true, validate() returns a shared frozen result on the first failure
|
|
45
|
+
* instead of collecting full error details. Smaller hot-path allocation.
|
|
46
|
+
*/
|
|
47
|
+
abortEarly?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface BundleStandaloneOptions extends ValidatorOptions {
|
|
51
|
+
/** Module format for the emitted bundle. Default: 'cjs'. */
|
|
52
|
+
format?: 'esm' | 'cjs';
|
|
24
53
|
}
|
|
25
54
|
|
|
26
55
|
export interface StandardSchemaV1Props {
|
|
@@ -30,7 +59,13 @@ export interface StandardSchemaV1Props {
|
|
|
30
59
|
value: unknown
|
|
31
60
|
):
|
|
32
61
|
| { value: unknown }
|
|
33
|
-
| {
|
|
62
|
+
| {
|
|
63
|
+
issues: Array<{
|
|
64
|
+
message: string;
|
|
65
|
+
/** Array indices are emitted as numbers, object keys as strings. */
|
|
66
|
+
path?: ReadonlyArray<{ key: PropertyKey }>;
|
|
67
|
+
}>;
|
|
68
|
+
};
|
|
34
69
|
}
|
|
35
70
|
|
|
36
71
|
export interface StandaloneModule {
|
|
@@ -98,8 +133,12 @@ export class Validator {
|
|
|
98
133
|
/** Bundle multiple schemas into a single JS module string. Load with Validator.loadBundle(). */
|
|
99
134
|
static bundle(schemas: object[], options?: ValidatorOptions): string;
|
|
100
135
|
|
|
101
|
-
/**
|
|
102
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Bundle multiple schemas into a self-contained JS module with no
|
|
138
|
+
* ata-validator runtime dependency. Cross-schema `$ref` resolves between
|
|
139
|
+
* the supplied schemas. Set `format: 'esm'` for ESM output (default 'cjs').
|
|
140
|
+
*/
|
|
141
|
+
static bundleStandalone(schemas: object[], options?: BundleStandaloneOptions): string;
|
|
103
142
|
|
|
104
143
|
/** Bundle multiple schemas with deduplicated shared templates. Smaller output than bundle(). */
|
|
105
144
|
static bundleCompact(schemas: object[], options?: ValidatorOptions): string;
|
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",
|