import { fail , EMPTY_ARRAY , isPrimitive , getStateTreeNode , isStateTreeNode , isPrimitiveType , IAnyType , ExtractCSTWithSTN , isTypeCheckingEnabled , devMode } from "../../internal" ; /** Validation context entry, this is, where the validation should run against which type */ export interface IValidationContextEntry { /** Subpath where the validation should be run, or an empty string to validate it all */ path : string ; /** Type to validate the subpath against */ type : IAnyType ; } /** Array of validation context entries */ export type IValidationContext = IValidationContextEntry [ ] ; /** Type validation error */ export interface IValidationError { /** Validation context */ context : IValidationContext ; /** Value that was being validated, either a snapshot or an instance */ value : any ; /** Error message */ message ? : string ; } /** Type validation result, which is an array of type validation errors */ export type IValidationResult = IValidationError [ ] ; function safeStringify ( value ) { try { return JSON . stringify ( value ) ; } catch ( e ) { return `<Unserializable: ${ e }>` ; } } internal hidden export function prettyPrintValue ( value ) { return typeof value === "function" ? `<function${ value . name ? " " + value . name : "" }>` : isStateTreeNode ( value ) ? `<${ value }>` : `\`${ safeStringify ( value ) }\`` ; } function shortenPrintValue ( valueInString ) { return valueInString . length < 280 ? valueInString : `${ valueInString . substring ( 0 , 272 ) }......${ valueInString . substring ( valueInString . length - 8 ) }` ; } function toErrorString ( error ) { const { value } = error ; const type = error . context [ error . context . length - 1 ] . type ! ; const fullPath = error . context . map ( ( { path } ) => path ) . filter ( ( path ) => path . length > 0 ) . join ( "/" ) ; const pathPrefix = fullPath . length > 0 ? `at path "/${ fullPath }" ` : `` ; const currentTypename = isStateTreeNode ( value ) ? `value of type ${ getStateTreeNode ( value ) . type . name }:` : isPrimitive ( value ) ? "value" : "snapshot" ; const isSnapshotCompatible = type && isStateTreeNode ( value ) && type . is ( getStateTreeNode ( value ) . snapshot ) ; return ( `${ pathPrefix }${ currentTypename } ${ prettyPrintValue ( value ) } is not assignable ${ type ? `to type: \`${ type . name }\`` : `` }` + ( error . message ? ` (${ error . message })` : "" ) + ( type ? isPrimitiveType ( type ) || isPrimitive ( value ) ? `.` : `, expected an instance of \`${ ( type as IAnyType ) . name }\` or a snapshot like \`${ ( type as IAnyType ) . describe ( ) }\` instead.` + ( isSnapshotCompatible ? " (Note that a snapshot of the provided value is compatible with the targeted type)" : "" ) : `.` ) ) ; } internal hidden export function getContextForPath ( context , path , type ) { return context . concat ( [ { path , type } ] ) ; } internal hidden export function typeCheckSuccess ( ) { return EMPTY_ARRAY as any ; } internal hidden export function typeCheckFailure ( context , value , message ? ) { return [ { context , value , message } ] ; } internal hidden export function flattenTypeErrors ( errors ) { return errors . reduce ( ( a , i ) => a . concat ( i ) , [ ] ) ; } internal hidden export function typecheckInternal < IT extends IAnyType > ( type , value ) { if ( isTypeCheckingEnabled ( ) ) { typecheck ( type , value ) ; } } param type param value export function typecheck < IT extends IAnyType > ( type , value ) { const errors = type . validate ( value , [ { path : "" , type } ] ) ; if ( errors . length > 0 ) { throw fail ( validationErrorsToString ( type , value , errors ) ) ; } } function validationErrorsToString < IT extends IAnyType > ( type , value , errors ) { if ( errors . length === 0 ) { return undefined ; } return ( `Error while converting ${ shortenPrintValue ( prettyPrintValue ( value ) ) } to \`${ type . name }\`:\n\n    ` + errors . map ( toErrorString ) . join ( "\n    " ) ) ; }