import { get , set } from 'lodash' ; import type { JsonShape , OptionalPromise , NonPrimitiveAndNonArrayKeys , DeepRequired , IsNever , } from './types' ; /**
 * This class is responsible for validating configuration object in a form of json and produce validated object based on validating `rules` added via `addRule` method.
 */ export default class FromJsonValidator < From extends JsonShape < To > , To extends object > { /**
     *  Records:
     * - keys are stringified array paths to the properties
     * - values are functions accepting
     */ private rules : [ PropertyKey [ ] , ( value : unknown , config : To ) => OptionalPromise < unknown > ] [ ] = [ ] ; param name param validatingFunction addRule < TK1 extends NonPrimitiveAndNonArrayKeys < DeepRequired < To > > , TK2 extends NonPrimitiveAndNonArrayKeys < DeepRequired < To > [ TK1 ] > , TK3 extends NonPrimitiveAndNonArrayKeys < DeepRequired < To > [ TK1 ] [ TK2 ] > > ( name , validatingFunction = value => value ) { const idx = this . rules . findIndex ( ( [ propertyPath ] ) => propertyPath . join ( '.' ) === name . join ( '.' ) ) ; if ( idx === - 1 ) { this . rules . push ( [ name , validatingFunction ] ) ; } else { this . rules [ idx ] = [ name , validatingFunction ] ; } return this ; } async validate ( jsonConfig ) { const config = { } ; const errors = [ ] ; for ( const [ propertyPath , validatingFunc ] of this . rules ) { try { const rawValue = get ( jsonConfig , propertyPath ) ; if ( rawValue === undefined ) { continue ; } const value = await validatingFunc ( rawValue , config ) ; set ( config , propertyPath , value ) ; } catch ( e ) { errors . push ( [ propertyPath , e ] ) ; } } if ( errors . length > 0 ) { throw new Error ( `Validating error:\n${ this . formatErrors ( errors ) }` ) ; } return config ; } private formatErrors ( errors ) { return errors . map ( ( [ propertyPath , error ] ) => { return `  '${ propertyPath . map ( el => String ( el ) ) . join ( '.' ) }': ${ error . message }` ; } ) . join ( '\n' ) ; } }