1
- import curry from "just-curry-it" ;
2
1
import { v4 as uuid } from "uuid" ;
3
- import * as Browser from "@hyperjump/browser" ;
2
+ import { jrefTypeOf } from "@hyperjump/browser/jref " ;
4
3
import * as JsonPointer from "@hyperjump/json-pointer" ;
5
- import { asyncCollectSet , asyncFilter , asyncFlatten , asyncMap , pipe } from "@hyperjump/pact" ;
6
- import {
7
- Validation ,
8
- canonicalUri , getSchema , toSchema ,
9
- getKeywordName , getKeyword , getKeywordByName
10
- } from "../lib/experimental.js" ;
4
+ import { resolveIri , toAbsoluteIri } from "@hyperjump/uri" ;
5
+ import { getSchema , toSchema , getKeywordName } from "../lib/experimental.js" ;
11
6
12
7
13
8
export const URI = "uri" , UUID = "uuid" ;
@@ -19,32 +14,36 @@ const defaultOptions = {
19
14
} ;
20
15
21
16
export const bundle = async ( url , options = { } ) => {
22
- loadKeywordSupport ( ) ;
23
17
const fullOptions = { ...defaultOptions , ...options } ;
24
18
25
19
const mainSchema = await getSchema ( url ) ;
26
- const contextUri = mainSchema . document . baseUri ;
27
- const contextDialectId = mainSchema . document . dialectId ;
20
+ fullOptions . contextUri = mainSchema . document . baseUri ;
21
+ fullOptions . contextDialectId = mainSchema . document . dialectId ;
22
+
28
23
const bundled = toSchema ( mainSchema ) ;
24
+ fullOptions . bundlingLocation = "/" + getKeywordName ( fullOptions . contextDialectId , "https://json-schema.org/keyword/definitions" ) ;
25
+ if ( JsonPointer . get ( fullOptions . bundlingLocation , bundled ) === undefined ) {
26
+ JsonPointer . assign ( fullOptions . bundlingLocation , bundled , { } ) ;
27
+ }
29
28
30
- const externalIds = await Validation . collectExternalIds ( new Set ( ) , mainSchema , mainSchema ) ;
29
+ return await doBundling ( mainSchema . uri , bundled , fullOptions ) ;
30
+ } ;
31
31
32
- // Bundle
33
- const bundlingLocation = "/" + getKeywordName ( contextDialectId , "https://json-schema.org/keyword/definitions" ) ;
34
- if ( JsonPointer . get ( bundlingLocation , bundled ) === undefined && externalIds . size > 0 ) {
35
- JsonPointer . assign ( bundlingLocation , bundled , { } ) ;
36
- }
32
+ const doBundling = async ( schemaUri , bundled , fullOptions , contextSchema , visited = new Set ( ) ) => {
33
+ visited . add ( schemaUri ) ;
37
34
38
- for ( const uri of externalIds ) {
39
- if ( fullOptions . externalSchemas . includes ( uri ) ) {
35
+ const schema = await getSchema ( schemaUri , contextSchema ) ;
36
+ for ( const reference of allReferences ( schema . document . root ) ) {
37
+ const uri = toAbsoluteIri ( resolveIri ( reference . href , schema . document . baseUri ) ) ;
38
+ if ( visited . has ( uri ) || fullOptions . externalSchemas . includes ( uri ) || ( uri in schema . document . embedded && ! ( uri in schema . _cache ) ) ) {
40
39
continue ;
41
40
}
42
41
43
- const externalSchema = await getSchema ( uri ) ;
42
+ const externalSchema = await getSchema ( uri , contextSchema ) ;
44
43
const embeddedSchema = toSchema ( externalSchema , {
45
- contextUri : externalSchema . document . baseUri . startsWith ( "file:" ) ? contextUri : undefined ,
44
+ contextUri : externalSchema . document . baseUri . startsWith ( "file:" ) ? fullOptions . contextUri : undefined ,
46
45
includeDialect : fullOptions . alwaysIncludeDialect ? "always" : "auto" ,
47
- contextDialectId : contextDialectId
46
+ contextDialectId : fullOptions . contextDialectId
48
47
} ) ;
49
48
let id ;
50
49
if ( fullOptions . definitionNamingStrategy === URI ) {
@@ -56,216 +55,29 @@ export const bundle = async (url, options = {}) => {
56
55
} else {
57
56
throw Error ( `Unknown definition naming stragety: ${ fullOptions . definitionNamingStrategy } ` ) ;
58
57
}
59
- const pointer = JsonPointer . append ( id , bundlingLocation ) ;
58
+ const pointer = JsonPointer . append ( id , fullOptions . bundlingLocation ) ;
60
59
JsonPointer . assign ( pointer , bundled , embeddedSchema ) ;
60
+
61
+ bundled = await doBundling ( uri , bundled , fullOptions , schema , visited ) ;
61
62
}
62
63
63
64
return bundled ;
64
65
} ;
65
66
66
- Validation . collectExternalIds = curry ( async ( visited , parentSchema , schema ) => {
67
- const uri = canonicalUri ( schema ) ;
68
- if ( visited . has ( uri ) || Browser . typeOf ( schema ) === "boolean" ) {
69
- return new Set ( ) ;
70
- }
71
-
72
- visited . add ( uri ) ;
73
-
74
- const externalIds = await pipe (
75
- Browser . entries ( schema ) ,
76
- asyncMap ( async ( [ keyword , keywordSchema ] ) => {
77
- const keywordHandler = getKeywordByName ( keyword , schema . document . dialectId ) ;
78
-
79
- return "collectExternalIds" in keywordHandler
80
- ? await keywordHandler . collectExternalIds ( visited , schema , keywordSchema )
81
- : new Set ( ) ;
82
- } ) ,
83
- asyncFlatten ,
84
- asyncCollectSet
85
- ) ;
86
-
87
- if ( parentSchema . document . baseUri !== schema . document . baseUri
88
- && ( ! ( schema . document . baseUri in parentSchema . document . embedded ) || schema . document . baseUri in parentSchema . _cache )
89
- ) {
90
- externalIds . add ( schema . document . baseUri ) ;
91
- }
92
-
93
- return externalIds ;
94
- } ) ;
95
-
96
- const collectExternalIdsFromArrayOfSchemas = ( visited , parentSchema , schema ) => pipe (
97
- Browser . iter ( schema ) ,
98
- asyncMap ( Validation . collectExternalIds ( visited , parentSchema ) ) ,
99
- asyncFlatten ,
100
- asyncCollectSet
101
- ) ;
102
-
103
- const collectExternalIdsFromObjectOfSchemas = async ( visited , parentSchema , schema ) => pipe (
104
- Browser . values ( schema ) ,
105
- asyncMap ( Validation . collectExternalIds ( visited , parentSchema ) ) ,
106
- asyncFlatten ,
107
- asyncCollectSet
108
- ) ;
109
-
110
- const loadKeywordSupport = ( ) => {
111
- // Stable
112
-
113
- const additionalProperties = getKeyword ( "https://json-schema.org/keyword/additionalProperties" ) ;
114
- if ( additionalProperties ) {
115
- additionalProperties . collectExternalIds = Validation . collectExternalIds ;
116
- }
117
-
118
- const allOf = getKeyword ( "https://json-schema.org/keyword/allOf" ) ;
119
- if ( allOf ) {
120
- allOf . collectExternalIds = collectExternalIdsFromArrayOfSchemas ;
121
- }
122
-
123
- const anyOf = getKeyword ( "https://json-schema.org/keyword/anyOf" ) ;
124
- if ( anyOf ) {
125
- anyOf . collectExternalIds = collectExternalIdsFromArrayOfSchemas ;
126
- }
127
-
128
- const contains = getKeyword ( "https://json-schema.org/keyword/contains" ) ;
129
- if ( contains ) {
130
- contains . collectExternalIds = Validation . collectExternalIds ;
131
- }
132
-
133
- const dependentSchemas = getKeyword ( "https://json-schema.org/keyword/dependentSchemas" ) ;
134
- if ( dependentSchemas ) {
135
- dependentSchemas . collectExternalIds = collectExternalIdsFromObjectOfSchemas ;
136
- }
137
-
138
- const if_ = getKeyword ( "https://json-schema.org/keyword/if" ) ;
139
- if ( if_ ) {
140
- if_ . collectExternalIds = Validation . collectExternalIds ;
141
- }
142
-
143
- const then = getKeyword ( "https://json-schema.org/keyword/then" ) ;
144
- if ( then ) {
145
- then . collectExternalIds = Validation . collectExternalIds ;
146
- }
147
-
148
- const else_ = getKeyword ( "https://json-schema.org/keyword/else" ) ;
149
- if ( else_ ) {
150
- else_ . collectExternalIds = Validation . collectExternalIds ;
151
- }
152
-
153
- const items = getKeyword ( "https://json-schema.org/keyword/items" ) ;
154
- if ( items ) {
155
- items . collectExternalIds = Validation . collectExternalIds ;
156
- }
157
-
158
- const not = getKeyword ( "https://json-schema.org/keyword/not" ) ;
159
- if ( not ) {
160
- not . collectExternalIds = Validation . collectExternalIds ;
161
- }
162
-
163
- const oneOf = getKeyword ( "https://json-schema.org/keyword/oneOf" ) ;
164
- if ( oneOf ) {
165
- oneOf . collectExternalIds = collectExternalIdsFromArrayOfSchemas ;
166
- }
167
-
168
- const patternProperties = getKeyword ( "https://json-schema.org/keyword/patternProperties" ) ;
169
- if ( patternProperties ) {
170
- patternProperties . collectExternalIds = collectExternalIdsFromObjectOfSchemas ;
171
- }
172
-
173
- const prefixItems = getKeyword ( "https://json-schema.org/keyword/prefixItems" ) ;
174
- if ( prefixItems ) {
175
- prefixItems . collectExternalIds = collectExternalIdsFromArrayOfSchemas ;
176
- }
177
-
178
- const properties = getKeyword ( "https://json-schema.org/keyword/properties" ) ;
179
- if ( properties ) {
180
- properties . collectExternalIds = collectExternalIdsFromObjectOfSchemas ;
181
- }
182
-
183
- const propertyNames = getKeyword ( "https://json-schema.org/keyword/propertyNames" ) ;
184
- if ( propertyNames ) {
185
- propertyNames . collectExternalIds = Validation . collectExternalIds ;
186
- }
187
-
188
- const ref = getKeyword ( "https://json-schema.org/keyword/ref" ) ;
189
- if ( ref ) {
190
- ref . collectExternalIds = Validation . collectExternalIds ;
191
- }
192
-
193
- const unevaluatedItems = getKeyword ( "https://json-schema.org/keyword/unevaluatedItems" ) ;
194
- if ( unevaluatedItems ) {
195
- unevaluatedItems . collectExternalIds = Validation . collectExternalIds ;
196
- }
197
-
198
- const unevaluatedProperties = getKeyword ( "https://json-schema.org/keyword/unevaluatedProperties" ) ;
199
- if ( unevaluatedProperties ) {
200
- unevaluatedProperties . collectExternalIds = Validation . collectExternalIds ;
201
- }
202
-
203
- // Draft-04
204
-
205
- const additionalItems4 = getKeyword ( "https://json-schema.org/keyword/draft-04/additionalItems" ) ;
206
- if ( additionalItems4 ) {
207
- additionalItems4 . collectExternalIds = Validation . collectExternalIds ;
208
- }
209
-
210
- const dependencies = getKeyword ( "https://json-schema.org/keyword/draft-04/dependencies" ) ;
211
- if ( dependencies ) {
212
- dependencies . collectExternalIds = ( visited , parentSchema , schema ) => pipe (
213
- Browser . values ( schema ) ,
214
- asyncFilter ( ( subSchema ) => Browser . typeOf ( subSchema ) === "object" ) ,
215
- asyncMap ( Validation . collectExternalIds ( visited , parentSchema ) ) ,
216
- asyncFlatten ,
217
- asyncCollectSet
218
- ) ;
219
- }
220
-
221
- const items4 = getKeyword ( "https://json-schema.org/keyword/draft-04/items" ) ;
222
- if ( items4 ) {
223
- items4 . collectExternalIds = ( visited , parentSchema , schema ) => Browser . typeOf ( schema ) === "array"
224
- ? collectExternalIdsFromArrayOfSchemas ( visited , parentSchema , schema )
225
- : Validation . collectExternalIds ( visited , parentSchema , schema ) ;
226
- }
227
-
228
- // Draft-06
229
-
230
- const contains6 = getKeyword ( "https://json-schema.org/keyword/draft-06/contains" ) ;
231
- if ( contains6 ) {
232
- contains6 . collectExternalIds = Validation . collectExternalIds ;
233
- }
234
-
235
- // Draft-2019-09
236
-
237
- const contains19 = getKeyword ( "https://json-schema.org/keyword/draft-2019-09/contains" ) ;
238
- if ( contains19 ) {
239
- contains19 . collectExternalIds = Validation . collectExternalIds ;
240
- }
241
-
242
- // Extensions
243
-
244
- const propertyDependencies = getKeyword ( "https://json-schema.org/keyword/propertyDependencies" ) ;
245
- if ( propertyDependencies ) {
246
- propertyDependencies . collectExternalIds = ( visited , parentSchema , schema ) => pipe (
247
- Browser . values ( schema ) ,
248
- asyncMap ( ( mapping ) => Browser . values ( mapping ) ) ,
249
- asyncFlatten ,
250
- asyncMap ( Validation . collectExternalIds ( visited , parentSchema ) ) ,
251
- asyncFlatten ,
252
- asyncCollectSet
253
- ) ;
254
- }
255
-
256
- const conditional = getKeyword ( "https://json-schema.org/keyword/conditional" ) ;
257
- if ( conditional ) {
258
- conditional . collectExternalIds = collectExternalIdsFromArrayOfSchemas ;
259
- }
260
-
261
- const itemPattern = getKeyword ( "https://json-schema.org/keyword/itemPattern" ) ;
262
- if ( itemPattern ) {
263
- itemPattern . collectExternalIds = ( visited , parentSchema , schema ) => pipe (
264
- Browser . iter ( schema ) ,
265
- asyncFilter ( ( item ) => Browser . typeOf ( item ) === "object" ) ,
266
- asyncMap ( Validation . collectExternalIds ( visited , parentSchema ) ) ,
267
- asyncFlatten ,
268
- asyncCollectSet
269
- ) ;
67
+ const allReferences = function * ( node ) {
68
+ switch ( jrefTypeOf ( node ) ) {
69
+ case "object" :
70
+ for ( const property in node ) {
71
+ yield * allReferences ( node [ property ] ) ;
72
+ }
73
+ break ;
74
+ case "array" :
75
+ for ( const item of node ) {
76
+ yield * allReferences ( item ) ;
77
+ }
78
+ break ;
79
+ case "reference" :
80
+ yield node ;
81
+ break ;
270
82
}
271
83
} ;
0 commit comments