import * as t from 'io-ts' ; import { Context , getFunctionName , ValidationError } from 'io-ts/lib' ; import { Reporter } from 'io-ts/lib/Reporter' ; import { RedspotError } from '../errors' ; import { ERRORS } from '../errors-list' ; function stringify ( v ) { if ( typeof v === 'function' ) { return getFunctionName ( v ) ; } if ( typeof v === 'number' && ! isFinite ( v ) ) { if ( isNaN ( v ) ) { return 'NaN' ; } return v > 0 ? 'Infinity' : '-Infinity' ; } return JSON . stringify ( v ) ; } function getContextPath ( context ) { const keysPath = context . slice ( 1 ) . map ( ( c ) => c . key ) . join ( '.' ) ; return `${ context [ 0 ] . type . name }.${ keysPath }` ; } function getMessage ( e ) { const lastContext = e . context [ e . context . length - 1 ] ; return e . message !== undefined ? e . message : getErrorMessage ( getContextPath ( e . context ) , e . value , lastContext . type . name ) ; } function getErrorMessage ( path , value , expectedType ) { return `Invalid value ${ stringify ( value ) } for ${ path } - Expected a value of type ${ expectedType }.` ; } export function failure ( es ) { return es . map ( getMessage ) ; } export function success ( ) { return [ ] ; } export const DotPathReporter = { report : ( validation ) => validation . fold ( failure , success ) } ; function optional < TypeT , OutputT > ( codec , name = `${ codec . name } | undefined` ) { return new t . Type ( name , ( u ) : u is TypeT | undefined => u === undefined || codec . is ( u ) , ( u , c ) => ( u === undefined ? t . success ( u ) : codec . validate ( u , c ) ) , ( a ) => ( a === undefined ? undefined : codec . encode ( a ) ) ) ; } const HEX_STRING_REGEX = /^(0x)?([0-9a-f]{2})+$/gi ; function isHexString ( v ) { if ( typeof v !== 'string' ) { return false ; } return v . trim ( ) . match ( HEX_STRING_REGEX ) !== null ; } export const hexString = new t . Type < string > ( 'hex string' , isHexString , ( u , c ) => ( isHexString ( u ) ? t . success ( u ) : t . failure ( u , c ) ) , t . identity ) ; const NetworkConfigAccounts = t . union ( [ t . array ( t . string ) , t . array ( t . type ( { mnemonic : optional ( t . string ) , initialIndex : optional ( t . number ) , count : optional ( t . number ) , path : optional ( t . string ) } ) , 'HDAccountsUserConfig' ) ] ) ; const HttpHeaders = t . record ( t . string , t . string , 'httpHeaders' ) ; const RedspotNetworkConfig = t . type ( { accounts : optional ( NetworkConfigAccounts ) , gasLimit : optional ( t . union ( [ t . string , t . number ] ) ) , from : optional ( t . string ) , types : optional ( t . record ( t . string , t . unknown ) ) , endpoint : optional ( t . string ) , httpHeaders : optional ( HttpHeaders ) } ) ; const NetworkConfig = RedspotNetworkConfig ; const Networks = t . record ( t . string , NetworkConfig ) ; const ProjectPaths = t . type ( { root : optional ( t . string ) , cache : optional ( t . string ) , artifacts : optional ( t . string ) , tests : optional ( t . string ) } ) ; const RedspotConfig = t . type ( { defaultNetwork : optional ( t . string ) , networks : optional ( Networks ) , paths : optional ( ProjectPaths ) } , 'RedspotConfig' ) ; param config export function validateConfig ( config ) { const errors = getValidationErrors ( config ) ; if ( errors . length === 0 ) { return ; } let errorList = errors . join ( '\n  * ' ) ; errorList = `  * ${ errorList }` ; throw new RedspotError ( ERRORS . GENERAL . INVALID_CONFIG , { errors : errorList } ) ; } export function getValidationErrors ( config ) { const errors = [ ] ; if ( errors . length > 0 ) { return errors ; } const result = RedspotConfig . decode ( config ) ; if ( result . isRight ( ) ) { return errors ; } const ioTsErrors = DotPathReporter . report ( result ) ; return [ ... errors , ... ioTsErrors ] ; }