import { Validator } from 'jsonschema' ; import { assert , logIf , LogLevel } from './auxiliaries' ; import { ChangeLookup } from './changelookup' ; param instance param schema function complementProperty ( instance , schema ) { const propertiesSchema = ( schema as any ) [ 'properties' ] ; const props = Object . getOwnPropertyNames ( propertiesSchema ) ; for ( const key of props ) { const propertySchema = propertiesSchema [ key ] ; const type = propertySchema [ 'type' ] ; const isObject = type === 'object' ; const isDefined = instance . hasOwnProperty ( key ) ; const hasProperties = propertySchema . hasOwnProperty ( 'properties' ) ; const hasDefault = propertySchema . hasOwnProperty ( 'default' ) ; assert ( ( hasProperties && isObject ) || ( ! hasProperties && ! isObject ) , `expected property '${ key }' to be of type 'object', given '${ propertySchema [ 'type' ] }'` ) ; if ( isDefined && hasProperties ) { properties . complement ( instance [ key ] , propertySchema ) ; } else if ( ! isDefined && hasDefault ) { Object . defineProperty ( instance , key , { value : propertySchema [ 'default' ] , writable : true , } ) ; } } } param instance param schema function complementArray ( instance , schema ) { const itemsSchema = ( schema as any ) [ 'items' ] ; if ( itemsSchema [ 'type' ] !== 'object' ) { return ; } for ( const name of Object . getOwnPropertyNames ( instance ) ) { if ( name === 'length' ) { continue ; } properties . complement ( instance [ name ] , itemsSchema ) ; } } namespace properties { param instance param schema param references returns export function validate ( instance , schema , references ? ) { const validator = new Validator ( ) ; if ( references !== undefined ) { for ( const reference of references ) { validator . addSchema ( reference [ 0 ] , reference [ 1 ] ) ; } } const result = validator . validate ( instance , schema ) ; logIf ( ! result . valid , LogLevel . Warning , `schema expected to be valid:\n${ result . toString ( ) }` ) ; return result . valid ; } param instance param schema export function complement ( instance , schema ) { if ( instance === undefined ) { return ; } assert ( ( schema . hasOwnProperty ( 'properties' ) && ( schema as any ) [ 'type' ] === 'object' ) || ( schema . hasOwnProperty ( 'items' ) && ( schema as any ) [ 'type' ] === 'array' ) , `expected schema to have 'properties' or 'items', given ${ schema }` ) ; switch ( ( schema as any ) [ 'type' ] ) { case 'object' : complementProperty ( instance , schema ) ; break ; case 'array' : complementArray ( instance , schema ) ; break ; } } param objectL param objectR param lookup returns export function compare ( objectL , objectR , tracker ? , path = '' ) { const track = tracker !== undefined ; assert ( ! track || ( tracker as object ) . hasOwnProperty ( 'any' ) , `expected allocation lookup object to have 'any' key` ) ; if ( ( objectL === undefined && objectR !== undefined ) || ( objectL !== undefined && objectR === undefined ) ) { if ( track ) { ( tracker as ChangeLookup ) . alter ( path ) ; } return true ; } let equals = true ; let types = [ typeof objectL , typeof objectR ] ; const isArray = [ objectL instanceof Array , objectR instanceof Array ] ; if ( ( ! isArray [ 0 ] || ! isArray [ 1 ] ) && ( types [ 0 ] !== 'object' || types [ 1 ] !== 'object' ) ) { equals = objectL === objectR ; if ( ! equals && track ) { ( tracker as ChangeLookup ) . alter ( `${ path }` ) ; } return ! equals ; } const names = Array . from ( new Set < string > ( Array < string > ( ) . concat ( Object . getOwnPropertyNames ( objectL ) , Object . getOwnPropertyNames ( objectR ) ) ) . values ( ) ) ; for ( const name of names ) { if ( name === 'length' ) { continue ; } const values = [ ( objectL as any ) [ name ] , ( objectR as any ) [ name ] ] ; types = [ typeof values [ 0 ] , typeof values [ 1 ] ] ; const propertyPath = `${ path }${ path . length > 0 && ! isArray [ 0 ] ? '.' : '' }${ ! isArray [ 0 ] ? name : '' }` ; if ( ( values [ 0 ] instanceof Array && values [ 1 ] instanceof Array ) || ( types [ 0 ] === 'object' && types [ 1 ] === 'object' ) ) { equals = equals && ! compare ( values [ 0 ] , values [ 1 ] , tracker , propertyPath ) ; } else { if ( types [ 0 ] === types [ 1 ] && values [ 0 ] === values [ 1 ] ) { continue ; } equals = false ; if ( track ) { ( tracker as ChangeLookup ) . alter ( propertyPath ) ; } } } return ! equals ; } } export = properties ;