1
1
import jsonStringify from "json-stringify-deterministic" ;
2
- import { parseIriReference , toAbsoluteIri } from "@hyperjump/uri" ;
2
+ import * as JsonPointer from "@hyperjump/json-pointer" ;
3
+ import { parseIriReference , resolveIri , toAbsoluteIri } from "@hyperjump/uri" ;
3
4
import {
4
5
assertNodeType ,
5
6
toJsonNode ,
6
- isNodeType ,
7
7
jsonObjectHas ,
8
8
jsonObjectKeys ,
9
9
jsonPointerGet ,
10
10
jsonPointerStep ,
11
- jsonValue ,
12
- toSchemaNode
11
+ jsonValue
13
12
} from "./jsonast-util.js" ;
14
13
15
14
/**
16
15
* @import {
17
16
* Json,
18
17
* JsonNode,
19
18
* JsonObjectNode,
20
- * JsonStringNode,
21
- * SchemaNode
19
+ * JsonStringNode
22
20
* } from "./jsonast.d.ts"
23
21
*/
24
22
25
23
26
24
/** @type (schema: Json, instance: Json) => boolean */
27
25
export const validate = ( schema , instance ) => {
28
26
registerSchema ( schema , "" ) ;
29
- const schemaNode = /** @type NonNullable<SchemaNode > */ ( schemaRegistry . get ( "" ) ) ;
27
+ const schemaNode = /** @type NonNullable<JsonNode > */ ( schemaRegistry . get ( "" ) ) ;
30
28
const isValid = validateSchema ( schemaNode , toJsonNode ( instance ) ) ;
31
29
schemaRegistry . delete ( "" ) ;
32
30
return isValid ;
33
31
} ;
34
32
35
- /** @type (schemaNode: SchemaNode , instanceNode: JsonNode) => boolean */
33
+ /** @type (schemaNode: JsonNode , instanceNode: JsonNode) => boolean */
36
34
const validateSchema = ( schemaNode , instanceNode ) => {
37
35
if ( schemaNode . type === "json" ) {
38
36
switch ( schemaNode . jsonType ) {
@@ -53,35 +51,38 @@ const validateSchema = (schemaNode, instanceNode) => {
53
51
throw Error ( "Invalid Schema" ) ;
54
52
} ;
55
53
56
- /** @type Map<string, SchemaNode > */
54
+ /** @type Map<string, JsonNode > */
57
55
const schemaRegistry = new Map ( ) ;
58
56
59
57
/** @type (schema: Json, uri: string) => void */
60
58
export const registerSchema = ( schema , uri ) => {
61
- schemaRegistry . set ( uri , toSchemaNode ( schema , uri ) ) ;
59
+ schemaRegistry . set ( uri , toJsonNode ( schema , uri ) ) ;
62
60
} ;
63
61
64
62
/**
65
63
* @typedef {(
66
- * keywordNode: SchemaNode ,
64
+ * keywordNode: JsonNode ,
67
65
* instanceNode: JsonNode,
68
- * schemaNode: JsonObjectNode<SchemaNode>
66
+ * schemaNode: JsonObjectNode
69
67
* ) => boolean} KeywordHandler
70
68
*/
71
69
72
70
/** @type Map<string, KeywordHandler> */
73
71
const keywordHandlers = new Map ( ) ;
74
72
75
73
keywordHandlers . set ( "$ref" , ( refNode , instanceNode ) => {
76
- assertNodeType ( refNode , "reference" ) ;
77
- const pointer = decodeURI ( parseIriReference ( refNode . value ) . fragment ?? "" ) ;
78
- const uri = refNode . value . startsWith ( "#" ) ? "" : toAbsoluteIri ( refNode . value ) ;
74
+ assertNodeType ( refNode , "string" ) ;
75
+
76
+ const uri = refNode . location . startsWith ( "#" )
77
+ ? refNode . value . startsWith ( "#" ) ? "" : toAbsoluteIri ( refNode . value )
78
+ : toAbsoluteIri ( resolveIri ( refNode . value , toAbsoluteIri ( refNode . location ) ) ) ;
79
79
80
80
const schemaNode = schemaRegistry . get ( uri ) ;
81
81
if ( ! schemaNode ) {
82
82
throw Error ( `Invalid reference: ${ uri } ` ) ;
83
83
}
84
84
85
+ const pointer = decodeURI ( parseIriReference ( refNode . value ) . fragment ?? "" ) ;
85
86
const referencedSchemaNode = jsonPointerGet ( pointer , schemaNode , uri ) ;
86
87
87
88
return validateSchema ( referencedSchemaNode , instanceNode ) ;
@@ -96,7 +97,7 @@ keywordHandlers.set("additionalProperties", (additionalPropertiesNode, instanceN
96
97
97
98
if ( jsonObjectHas ( "properties" , schemaNode ) ) {
98
99
const propertiesNode = jsonPointerStep ( "properties" , schemaNode ) ;
99
- if ( isNodeType ( propertiesNode , "object" ) ) {
100
+ if ( propertiesNode . jsonType === "object" ) {
100
101
for ( const propertyName of jsonObjectKeys ( propertiesNode ) ) {
101
102
propertyPatterns . push ( `^${ regexEscape ( propertyName ) } $` ) ;
102
103
}
@@ -105,7 +106,7 @@ keywordHandlers.set("additionalProperties", (additionalPropertiesNode, instanceN
105
106
106
107
if ( jsonObjectHas ( "patternProperties" , schemaNode ) ) {
107
108
const patternPropertiesNode = jsonPointerStep ( "patternProperties" , schemaNode ) ;
108
- if ( isNodeType ( patternPropertiesNode , "object" ) ) {
109
+ if ( patternPropertiesNode . jsonType === "object" ) {
109
110
propertyPatterns . push ( ...jsonObjectKeys ( patternPropertiesNode ) ) ;
110
111
}
111
112
}
@@ -171,15 +172,15 @@ keywordHandlers.set("contains", (containsNode, instanceNode, schemaNode) => {
171
172
let minContains = 1 ;
172
173
if ( jsonObjectHas ( "minContains" , schemaNode ) ) {
173
174
const minContainsNode = jsonPointerStep ( "minContains" , schemaNode ) ;
174
- if ( isNodeType ( minContainsNode , "number" ) ) {
175
+ if ( minContainsNode . jsonType === "number" ) {
175
176
minContains = minContainsNode . value ;
176
177
}
177
178
}
178
179
179
180
let maxContains = Number . MAX_SAFE_INTEGER ;
180
181
if ( jsonObjectHas ( "maxContains" , schemaNode ) ) {
181
182
const maxContainsNode = jsonPointerStep ( "maxContains" , schemaNode ) ;
182
- if ( isNodeType ( maxContainsNode , "number" ) ) {
183
+ if ( maxContainsNode . jsonType === "number" ) {
183
184
maxContains = maxContainsNode . value ;
184
185
}
185
186
}
@@ -242,7 +243,7 @@ keywordHandlers.set("items", (itemsNode, instanceNode, schemaNode) => {
242
243
let numberOfPrefixItems = 0 ;
243
244
if ( jsonObjectHas ( "prefixItems" , schemaNode ) ) {
244
245
const prefixItemsNode = jsonPointerStep ( "prefixItems" , schemaNode ) ;
245
- if ( isNodeType ( prefixItemsNode , "array" ) ) {
246
+ if ( prefixItemsNode . jsonType === "array" ) {
246
247
numberOfPrefixItems = prefixItemsNode . children . length ;
247
248
}
248
249
}
@@ -321,7 +322,8 @@ keywordHandlers.set("propertyNames", (propertyNamesNode, instanceNode) => {
321
322
const keyNode = {
322
323
type : "json" ,
323
324
jsonType : "string" ,
324
- value : propertyNode . children [ 0 ] . value
325
+ value : propertyNode . children [ 0 ] . value ,
326
+ location : JsonPointer . append ( propertyNode . children [ 0 ] . value , instanceNode . location )
325
327
} ;
326
328
if ( ! validateSchema ( propertyNamesNode , keyNode ) ) {
327
329
return false ;
0 commit comments