1- import * as Schema from "@hyperjump/browser" ;
1+ import * as crypto from "node:crypto" ;
2+ import * as fs from "node:fs/promises" ;
3+ import * as path from "node:path" ;
4+ import jsonStringify from "json-stringify-deterministic" ;
5+ import { applyEdits , modify } from "jsonc-parser" ;
6+
27import * as Pact from "@hyperjump/pact" ;
38import * as JsonPointer from "@hyperjump/json-pointer" ;
4- import { toAbsoluteIri } from "@hyperjump/uri" ;
5- import { registerSchema , unregisterSchema } from "@hyperjump/json-schema/draft-2020-12" ;
6- import { getSchema , getKeywordId } from "@hyperjump/json-schema/experimental" ;
9+ import * as Schema from "@hyperjump/browser" ;
10+ import { registerSchema , unregisterSchema } from "@hyperjump/json-schema" ;
11+ import { getSchema , getKeywordId , getKeywordName } from "@hyperjump/json-schema/experimental" ;
12+ import "@hyperjump/json-schema/draft-2020-12" ;
713import "@hyperjump/json-schema/draft-2019-09" ;
814import "@hyperjump/json-schema/draft-07" ;
915import "@hyperjump/json-schema/draft-06" ;
1016import "@hyperjump/json-schema/draft-04" ;
1117
18+ const DIALECT_MAP = {
19+ "v1" : "https://json-schema.org/v1" ,
20+ "draft2020-12" : "https://json-schema.org/draft/2020-12/schema" ,
21+ "draft2019-09" : "https://json-schema.org/draft/2019-09/schema" ,
22+ "draft7" : "http://json-schema.org/draft-07/schema" ,
23+ "draft6" : "http://json-schema.org/draft-06/schema" ,
24+ "draft4" : "http://json-schema.org/draft-04/schema" ,
25+ "draft3" : "http://json-schema.org/draft-03/schema"
26+ } ;
27+
28+ export const generateIdsFor = async ( version ) => {
29+ const dialectUri = DIALECT_MAP [ version ] ;
30+
31+ const registeredSchemas = await loadRemotes ( version ) ;
32+
33+ for ( const entry of await fs . readdir ( `./tests/${ version } ` , { recursive : true , withFileTypes : true } ) ) {
34+ if ( ! entry . isFile ( ) || path . extname ( entry . name ) !== ".json" ) {
35+ continue ;
36+ }
37+
38+ const edits = [ ] ;
39+ const filePath = path . resolve ( entry . parentPath , entry . name ) ;
40+ const json = await fs . readFile ( filePath , "utf8" )
41+ const suite = JSON . parse ( json ) ;
42+
43+ for ( let testCaseIndex = 0 ; testCaseIndex < suite . length ; testCaseIndex ++ ) {
44+ const testCase = suite [ testCaseIndex ] ;
45+
46+ try {
47+ const normalizedSchema = await normalizeSchema ( testCase . schema , dialectUri ) ;
48+
49+ for ( let testIndex = 0 ; testIndex < testCase . tests . length ; testIndex ++ ) {
50+ const test = testCase . tests [ testIndex ] ;
51+ const id = generateTestId ( normalizedSchema , test . data , test . valid ) ;
52+
53+ edits . push ( ...modify ( json , [ testCaseIndex , "tests" , testIndex , "id" ] , id , {
54+ formattingOptions : {
55+ insertSpaces : true ,
56+ tabSize : 4
57+ }
58+ } ) ) ;
59+ }
60+ } catch ( error ) {
61+ console . log ( `Failed to generate an ID for ${ version } ${ entry . name } : ${ testCase . description } ` ) ;
62+ // console.log(error);
63+ }
64+ }
65+
66+ if ( edits . length > 0 ) {
67+ const updatedJson = applyEdits ( json , edits ) ;
68+ await fs . writeFile ( filePath , updatedJson ) ;
69+ }
70+ }
71+
72+ for ( const remoteUri of registeredSchemas ) {
73+ unregisterSchema ( remoteUri ) ;
74+ }
75+ } ;
76+
77+ export const loadRemotes = async ( version , filePath = `./remotes` , url = "" ) => {
78+ const registeredSchemas = [ ] ;
1279
13- const sanitizeTopLevelId = ( schema ) => {
14- if ( typeof schema !== "object" || schema === null ) return schema ;
15- const copy = { ...schema } ;
16- if ( typeof copy . $id === "string" && copy . $id . startsWith ( "file:" ) ) {
17- copy . $id = copy . $id . replace ( / ^ f i l e : / , "x-file:" ) ;
80+ for ( const entry of await fs . readdir ( filePath , { withFileTypes : true } ) ) {
81+ if ( entry . isFile ( ) && path . extname ( entry . name ) === ".json" ) {
82+ const remote = JSON . parse ( await fs . readFile ( `${ filePath } /${ entry . name } ` , "utf8" ) ) ;
83+ const schemaUri = `http://localhost:1234${ url } /${ entry . name } ` ;
84+ registerSchema ( remote , schemaUri , DIALECT_MAP [ version ] ) ;
85+ registeredSchemas . push ( schemaUri ) ;
86+ } else if ( entry . isDirectory ( ) && entry . name === version || ! ( entry . name in DIALECT_MAP ) ) {
87+ registeredSchemas . push ( ...await loadRemotes ( version , `${ filePath } /${ entry . name } ` , `${ url } /${ entry . name } ` ) ) ;
88+ }
1889 }
19- return copy ;
90+
91+ return registeredSchemas ;
2092} ;
21- // ===========================================
2293
94+ export const generateTestId = ( normalizedSchema , testData , testValid ) => {
95+ return crypto
96+ . createHash ( "md5" )
97+ . update ( jsonStringify ( normalizedSchema ) + jsonStringify ( testData ) + testValid )
98+ . digest ( "hex" ) ;
99+ } ;
23100
24- export const normalize = async ( rawSchema , dialectUri ) => {
101+ export const normalizeSchema = async ( rawSchema , dialectUri ) => {
25102 const schemaUri = "https://test-suite.json-schema.org/main" ;
26103
27-
28- const safeSchema = sanitizeTopLevelId ( rawSchema ) ;
29-
30104 try {
31-
105+ const safeSchema = sanitizeTopLevelId ( rawSchema , dialectUri ) ;
32106 registerSchema ( safeSchema , schemaUri , dialectUri ) ;
33107
34108 const schema = await getSchema ( schemaUri ) ;
@@ -40,6 +114,20 @@ export const normalize = async (rawSchema, dialectUri) => {
40114 }
41115} ;
42116
117+ const sanitizeTopLevelId = ( schema , dialectUri ) => {
118+ if ( typeof schema !== "object" ) {
119+ return schema ;
120+ }
121+
122+ const idToken = getKeywordName ( dialectUri , "https://json-schema.org/keyword/id" )
123+ ?? getKeywordName ( dialectUri , "https://json-schema.org/keyword/draft-04/id" ) ;
124+ if ( idToken in schema ) {
125+ schema [ idToken ] = schema [ idToken ] . replace ( / ^ f i l e : / , "x-file:" ) ;
126+ }
127+
128+ return schema ;
129+ } ;
130+
43131const compile = async ( schema , ast ) => {
44132 if ( ! ( schema . document . baseUri in ast . metaData ) ) {
45133 ast . metaData [ schema . document . baseUri ] = {
@@ -124,16 +212,13 @@ const keywordHandlers = {
124212 "https://json-schema.org/keyword/dependentSchemas" : objectApplicator ,
125213 "https://json-schema.org/keyword/deprecated" : simpleValue ,
126214 "https://json-schema.org/keyword/description" : simpleValue ,
127- "https://json-schema.org/keyword/dynamicRef" : simpleValue , // base dynamicRef
128-
129- "https://json-schema.org/keyword/draft-2020-12/dynamicRef" : simpleValue ,
130-
131-
215+ "https://json-schema.org/keyword/dynamicRef" : simpleValue ,
132216 "https://json-schema.org/keyword/else" : simpleApplicator ,
133217 "https://json-schema.org/keyword/enum" : simpleValue ,
134218 "https://json-schema.org/keyword/examples" : simpleValue ,
135219 "https://json-schema.org/keyword/exclusiveMaximum" : simpleValue ,
136220 "https://json-schema.org/keyword/exclusiveMinimum" : simpleValue ,
221+ "https://json-schema.org/keyword/format" : simpleValue ,
137222 "https://json-schema.org/keyword/if" : simpleApplicator ,
138223 "https://json-schema.org/keyword/items" : simpleApplicator ,
139224 "https://json-schema.org/keyword/maxContains" : simpleValue ,
@@ -153,6 +238,22 @@ const keywordHandlers = {
153238 "https://json-schema.org/keyword/patternProperties" : objectApplicator ,
154239 "https://json-schema.org/keyword/prefixItems" : arrayApplicator ,
155240 "https://json-schema.org/keyword/properties" : objectApplicator ,
241+ "https://json-schema.org/keyword/propertyDependencies" : ( keyword , ast ) => {
242+ return Pact . pipe (
243+ Schema . entries ( keyword ) ,
244+ Pact . asyncMap ( async ( [ propertyName , valueSchemaMap ] ) => {
245+ return [
246+ propertyName ,
247+ await Pact . pipe (
248+ Schema . entries ( valueSchemaMap ) ,
249+ Pact . asyncMap ( async ( [ propertyValue , schema ] ) => [ propertyValue , await compile ( schema , ast ) ] ) ,
250+ Pact . asyncCollectObject
251+ )
252+ ] ;
253+ } ) ,
254+ Pact . asyncCollectObject
255+ ) ;
256+ } ,
156257 "https://json-schema.org/keyword/propertyNames" : simpleApplicator ,
157258 "https://json-schema.org/keyword/readOnly" : simpleValue ,
158259 "https://json-schema.org/keyword/ref" : compile ,
@@ -166,6 +267,7 @@ const keywordHandlers = {
166267 "https://json-schema.org/keyword/unknown" : simpleValue ,
167268 "https://json-schema.org/keyword/writeOnly" : simpleValue ,
168269
270+ "https://json-schema.org/keyword/draft-2020-12/dynamicRef" : simpleValue ,
169271 "https://json-schema.org/keyword/draft-2020-12/format" : simpleValue ,
170272 "https://json-schema.org/keyword/draft-2020-12/format-assertion" : simpleValue ,
171273
@@ -200,4 +302,4 @@ const keywordHandlers = {
200302 } ,
201303 "https://json-schema.org/keyword/draft-04/maximum" : simpleValue ,
202304 "https://json-schema.org/keyword/draft-04/minimum" : simpleValue
203- } ;
305+ } ;
0 commit comments