1- import curry from "just-curry-it" ;
21import { v4 as uuid } from "uuid" ;
3- import * as Browser from "@hyperjump/browser" ;
2+ import { jrefTypeOf } from "@hyperjump/browser/jref " ;
43import * 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" ;
116
127
138export const URI = "uri" , UUID = "uuid" ;
@@ -19,32 +14,36 @@ const defaultOptions = {
1914} ;
2015
2116export const bundle = async ( url , options = { } ) => {
22- loadKeywordSupport ( ) ;
2317 const fullOptions = { ...defaultOptions , ...options } ;
2418
2519 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+
2823 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+ }
2928
30- const externalIds = await Validation . collectExternalIds ( new Set ( ) , mainSchema , mainSchema ) ;
29+ return await doBundling ( mainSchema . uri , bundled , fullOptions ) ;
30+ } ;
3131
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 ) ;
3734
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 ) ) ) {
4039 continue ;
4140 }
4241
43- const externalSchema = await getSchema ( uri ) ;
42+ const externalSchema = await getSchema ( uri , contextSchema ) ;
4443 const embeddedSchema = toSchema ( externalSchema , {
45- contextUri : externalSchema . document . baseUri . startsWith ( "file:" ) ? contextUri : undefined ,
44+ contextUri : externalSchema . document . baseUri . startsWith ( "file:" ) ? fullOptions . contextUri : undefined ,
4645 includeDialect : fullOptions . alwaysIncludeDialect ? "always" : "auto" ,
47- contextDialectId : contextDialectId
46+ contextDialectId : fullOptions . contextDialectId
4847 } ) ;
4948 let id ;
5049 if ( fullOptions . definitionNamingStrategy === URI ) {
@@ -56,216 +55,29 @@ export const bundle = async (url, options = {}) => {
5655 } else {
5756 throw Error ( `Unknown definition naming stragety: ${ fullOptions . definitionNamingStrategy } ` ) ;
5857 }
59- const pointer = JsonPointer . append ( id , bundlingLocation ) ;
58+ const pointer = JsonPointer . append ( id , fullOptions . bundlingLocation ) ;
6059 JsonPointer . assign ( pointer , bundled , embeddedSchema ) ;
60+
61+ bundled = await doBundling ( uri , bundled , fullOptions , schema , visited ) ;
6162 }
6263
6364 return bundled ;
6465} ;
6566
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 ;
27082 }
27183} ;
0 commit comments