11import * as Instance from "@hyperjump/json-schema/instance/experimental" ;
2+ import * as JsonPointer from "@hyperjump/json-pointer" ;
3+ import * as Pact from "@hyperjump/pact" ;
24import { getErrors } from "../json-schema-errors.js" ;
35
46/**
57 * @import { ErrorHandler, ErrorObject, NormalizedOutput } from "../index.d.ts"
68 */
79
8- /** @type (alternative: NormalizedOutput, propLocation: string) => boolean */
9- const propertyPasses = ( alternative , propLocation ) => {
10- const propOutput = alternative [ propLocation ] ;
11- if ( ! propOutput || Object . keys ( propOutput ) . length === 0 ) return false ;
12- return Object . values ( propOutput ) . every ( ( keywordResults ) =>
13- Object . values ( keywordResults ) . every ( ( v ) => v === true )
14- ) ;
15- } ;
16-
1710/** @type ErrorHandler */
1811const oneOfErrorHandler = async ( normalizedErrors , instance , localization ) => {
1912 /** @type ErrorObject[] */
2013 const errors = [ ] ;
2114
22- for ( const schemaLocation in normalizedErrors [
23- "https://json-schema.org/keyword/oneOf"
24- ] ) {
25- const oneOf
26- = normalizedErrors [ "https://json-schema.org/keyword/oneOf" ] [ schemaLocation ] ;
15+ for ( const schemaLocation in normalizedErrors [ "https://json-schema.org/keyword/oneOf" ] ) {
16+ const oneOf = normalizedErrors [ "https://json-schema.org/keyword/oneOf" ] [ schemaLocation ] ;
2717 if ( typeof oneOf === "boolean" ) {
2818 continue ;
2919 }
@@ -35,11 +25,7 @@ const oneOfErrorHandler = async (normalizedErrors, instance, localization) => {
3525 const failingAlternatives = [ ] ;
3626
3727 for ( const alternative of oneOf ) {
38- const alternativeErrors = await getErrors (
39- alternative ,
40- instance ,
41- localization
42- ) ;
28+ const alternativeErrors = await getErrors ( alternative , instance , localization ) ;
4329 if ( alternativeErrors . length ) {
4430 failingAlternatives . push ( alternativeErrors ) ;
4531 } else {
@@ -64,54 +50,52 @@ const oneOfErrorHandler = async (normalizedErrors, instance, localization) => {
6450 let filtered = oneOf ;
6551
6652 if ( Instance . typeOf ( instance ) === "object" ) {
67- const instanceProps = new Set (
68- [ ...Instance . keys ( instance ) ] . map (
69- ( keyNode ) => /** @type {string } */ ( Instance . value ( keyNode ) )
53+ const instanceProps = Pact . collectSet (
54+ Pact . map (
55+ ( keyNode ) => /** @type {string } */ ( Instance . value ( keyNode ) ) ,
56+ Instance . keys ( instance )
7057 )
7158 ) ;
7259 const prefix = `${ instanceLocation } /` ;
7360
74- filtered = filtered . filter ( ( alternative ) => {
61+ filtered = [ ] ;
62+ for ( const alternative of oneOf ) {
63+ const typeResults = alternative [ instanceLocation ] [ "https://json-schema.org/keyword/type" ] ;
64+ if ( typeResults && ! Object . values ( typeResults ) . every ( ( isValid ) => isValid ) ) {
65+ continue ;
66+ }
67+
7568 const declaredProps = Object . keys ( alternative )
7669 . filter ( ( loc ) => loc . startsWith ( prefix ) )
77- . map ( ( loc ) => loc . slice ( prefix . length ) ) ;
70+ . map ( ( loc ) => /** @type { string } */ ( Pact . head ( JsonPointer . pointerSegments ( loc . slice ( prefix . length - 1 ) ) ) ) ) ;
7871
79- if ( declaredProps . length === 0 ) return true ;
80- return declaredProps . some ( ( prop ) => instanceProps . has ( prop ) ) ;
81- } ) ;
72+ if ( declaredProps . length > 0 && ! declaredProps . some ( ( prop ) => instanceProps . has ( prop ) ) ) {
73+ continue ;
74+ }
8275
83- filtered = filtered . filter ( ( alternative ) =>
84- [ ...instanceProps ] . some ( ( prop ) =>
85- propertyPasses ( alternative , `${ instanceLocation } /${ prop } ` )
86- )
87- ) ;
76+ if ( ! Pact . some ( ( prop ) => propertyPasses ( alternative [ JsonPointer . append ( prop , instanceLocation ) ] ) , instanceProps ) ) {
77+ continue ;
78+ }
8879
89- if ( filtered . length === 0 ) {
90- filtered = oneOf ;
80+ filtered . push ( alternative ) ;
9181 }
9282 } else {
93- filtered = filtered . filter ( ( alternative ) => {
94- const typeResults
95- = alternative [ instanceLocation ] ?. [
96- "https://json-schema.org/keyword/type"
97- ] ;
98- return (
99- ! typeResults || Object . values ( typeResults ) . every ( ( isValid ) => isValid )
100- ) ;
101- } ) ;
102-
103- if ( filtered . length === 0 ) {
104- filtered = oneOf ;
83+ filtered = [ ] ;
84+ for ( const alternative of oneOf ) {
85+ const typeResults = alternative [ instanceLocation ] [ "https://json-schema.org/keyword/type" ] ;
86+ if ( ! typeResults || Object . values ( typeResults ) . every ( ( isValid ) => isValid ) ) {
87+ filtered . push ( alternative ) ;
88+ }
10589 }
10690 }
10791
92+ if ( filtered . length === 0 ) {
93+ filtered = oneOf ;
94+ }
95+
10896 const alternatives = [ ] ;
10997 for ( const alternative of filtered ) {
110- const alternativeErrors = await getErrors (
111- alternative ,
112- instance ,
113- localization
114- ) ;
98+ const alternativeErrors = await getErrors ( alternative , instance , localization ) ;
11599 if ( alternativeErrors . length ) {
116100 alternatives . push ( alternativeErrors ) ;
117101 }
@@ -136,4 +120,12 @@ const oneOfErrorHandler = async (normalizedErrors, instance, localization) => {
136120 return errors ;
137121} ;
138122
123+ /** @type (propOutput: NormalizedOutput[string] | undefined) => boolean */
124+ const propertyPasses = ( propOutput ) => {
125+ if ( ! propOutput || Object . keys ( propOutput ) . length === 0 ) return false ;
126+ return Object . values ( propOutput ) . every ( ( keywordResults ) =>
127+ Object . values ( keywordResults ) . every ( ( v ) => v === true )
128+ ) ;
129+ } ;
130+
139131export default oneOfErrorHandler ;
0 commit comments