import { StateNode , ActionObject , Guard , InvokeDefinition } from './' ; import { mapValues , isFunction } from './utils' ; interface JSONFunction { $function : string ; } export function stringifyFunction ( fn ) { return { $function : fn . toString ( ) } ; } function getStateNodeId ( stateNode ) { return `#${ stateNode . id }` ; } interface TransitionConfig { target : string [ ] ; source : string ; actions : Array < ActionObject < any , any > > ; cond : Guard < any , any > | undefined ; eventType : string ; } interface StateNodeConfig { type : StateNode [ 'type' ] ; id : string ; key : string ; initial ? : string ; entry : Array < ActionObject < any , any > > ; exit : Array < ActionObject < any , any > > ; on : { [ key ] : TransitionConfig [ ] ; } ; invoke : Array < InvokeDefinition < any , any > > ; states : Record < string , StateNodeConfig > ; } export function machineToJSON ( stateNode ) { const config = { type : stateNode . type , initial : stateNode . initial === undefined ? undefined : String ( stateNode . initial ) , id : stateNode . id , key : stateNode . key , entry : stateNode . onEntry , exit : stateNode . onExit , on : mapValues ( stateNode . on , ( transition ) => { return transition . map ( ( t ) => { return { target : t . target ? t . target . map ( getStateNodeId ) : [ ] , source : getStateNodeId ( t . source ) , actions : t . actions , cond : t . cond , eventType : t . eventType } ; } ) ; } ) , invoke : stateNode . invoke , states : { } } ; Object . values ( stateNode . states ) . forEach ( ( sn ) => { config . states [ sn . key ] = machineToJSON ( sn ) ; } ) ; return config ; } export function stringify ( machine ) { return JSON . stringify ( machineToJSON ( machine ) , ( _ , value ) => { if ( isFunction ( value ) ) { return { $function : value . toString ( ) } ; } return value ; } ) ; } export function parse ( machineString ) { const config = JSON . parse ( machineString , ( _ , value ) => { if ( typeof value === 'object' && '$function' in value ) { return new Function ( value . value ) ; } return value ; } ) ; return config ; } export function jsonify < T extends Record < string , any > > ( value ) { Object . defineProperty ( value , 'toJSON' , { value : ( ) => mapValues ( value , ( subValue ) => { if ( isFunction ( subValue ) ) { return stringifyFunction ( subValue ) ; } else if ( typeof subValue === 'object' && ! Array . isArray ( subValue ) ) { return mapValues ( subValue , ( subSubValue ) => { if ( isFunction ( subSubValue ) ) { return stringifyFunction ( subSubValue ) ; } return subSubValue ; } ) ; } return subValue ; } ) } ) ; return value ; }