import { getEventType , toStatePath , toStateValue , mapValues , path , toStatePaths , pathToStateValue , flatten , mapFilterValues , nestedPath , toArray , keys , isBuiltInEvent , partition , updateHistoryValue , warn , isArray , isFunction , isString , toGuard , isMachine , toSCXMLEvent , mapContext , toTransitionConfigArray , normalizeTarget , evaluateGuard , createInvokeId } from './utils' ; import { Event , StateValue , TransitionConfig , StateTransition , StateValueMap , MachineOptions , EventObject , HistoryStateNodeConfig , HistoryValue , StateNodeDefinition , TransitionDefinition , DelayedTransitionDefinition , ActivityDefinition , StateNodeConfig , StateSchema , StatesDefinition , StateNodesConfig , FinalStateNodeConfig , InvokeDefinition , ActionObject , Mapper , PropertyMapper , NullEvent , MachineConfig , InvokeCreator , DoneEventObject , SingleOrArray , SendActionObject , SpecialTargets , SCXML , RaiseActionObject , ActivityActionObject , InvokeActionObject , Typestate , TransitionDefinitionMap , DelayExpr , InvokeSourceDefinition , MachineSchema , ActorRef , StateMachine } from './types' ; import { matchesState } from './utils' ; import { State , stateValuesEqual } from './State' ; import * as actionTypes from './actionTypes' ; import { start , stop , toActivityDefinition , send , cancel , after , raise , done , doneInvoke , error , toActionObject , initEvent , toActionObjects , resolveActions } from './actions' ; import { IS_PRODUCTION } from './environment' ; import { STATE_DELIMITER } from './constants' ; import { getValue , getConfiguration , has , getChildren , getAllStateNodes , isInFinalState , isLeafNode , getTagsFromConfiguration } from './stateUtils' ; import { createInvocableActor } from './Actor' ; import { toInvokeDefinition } from './invokeUtils' ; const NULL_EVENT = '' ; const STATE_IDENTIFIER = '#' ; const WILDCARD = '*' ; const EMPTY_OBJECT = { } ; const isStateId = ( str ) => str [ 0 ] === STATE_IDENTIFIER ; const createDefaultOptions = < TContext > ( ) : MachineOptions < TContext , any > => ( { actions : { } , guards : { } , services : { } , activities : { } , delays : { } } ) ; const validateArrayifiedTransitions = < TContext > ( stateNode , event , transitions ) => { const hasNonLastUnguardedTarget = transitions . slice ( 0 , - 1 ) . some ( ( transition ) => ! ( 'cond' in transition ) && ! ( 'in' in transition ) && ( isString ( transition . target ) || isMachine ( transition . target ) ) ) ; const eventText = event === NULL_EVENT ? 'the transient event' : `event '${ event }'` ; warn ( ! hasNonLastUnguardedTarget , `One or more transitions for ${ eventText } on state '${ stateNode . id }' are unreachable. ` + `Make sure that the default transition is the last one defined.` ) ; } ; class StateNode < TContext = any , TStateSchema extends StateSchema = any , TEvent extends EventObject = EventObject , TTypestate extends Typestate < TContext > = { value : any ; context : TContext ; } > { /**
     * The relative key of the state node, which represents its location in the overall state value.
     */ public key : string ; /**
     * The unique ID of the state node.
     */ public id : string ; /**
     * The machine's own version.
     */ public version ? : string ; /**
     * The type of this state node:
     *
     *  - `'atomic'` - no child state nodes
     *  - `'compound'` - nested child state nodes (XOR)
     *  - `'parallel'` - orthogonal nested child state nodes (AND)
     *  - `'history'` - history state node
     *  - `'final'` - final state node
     */ public type : 'atomic' | 'compound' | 'parallel' | 'final' | 'history' ; /**
     * The string path from the root machine node to this node.
     */ public path : string [ ] ; /**
     * The initial state node key.
     */ public initial ? : keyof TStateSchema [ 'states' ] ; /**
     * (DEPRECATED) Whether the state node is a parallel state node.
     *
     * Use `type: 'parallel'` instead.
     */ public parallel ? : boolean ; /**
     * Whether the state node is "transient". A state node is considered transient if it has
     * an immediate transition from a "null event" (empty string), taken upon entering the state node.
     */ private _transient : boolean ; /**
     * The child state nodes.
     */ public states : StateNodesConfig < TContext , TStateSchema , TEvent > ; /**
     * The type of history on this state node. Can be:
     *
     *  - `'shallow'` - recalls only top-level historical state value
     *  - `'deep'` - recalls historical state value at all levels
     */ public history : false | 'shallow' | 'deep' ; /**
     * The action(s) to be executed upon entering the state node.
     */ public onEntry : Array < ActionObject < TContext , TEvent > > ; /**
     * The action(s) to be executed upon exiting the state node.
     */ public onExit : Array < ActionObject < TContext , TEvent > > ; /**
     * The activities to be started upon entering the state node,
     * and stopped upon exiting the state node.
     */ public activities : Array < ActivityDefinition < TContext , TEvent > > ; public strict : boolean ; /**
     * The parent state node.
     */ public parent ? : StateNode < TContext , any , TEvent , any > ; /**
     * The root machine node.
     */ public machine : StateNode < TContext , any , TEvent , TTypestate > ; /**
     * The meta data associated with this state node, which will be returned in State instances.
     */ public meta ? : TStateSchema extends { meta : infer D ; } ? D : any ; /**
     * The data sent with the "done.state._id_" event if this is a final state node.
     */ public doneData ? : Mapper < TContext , TEvent , any > | PropertyMapper < TContext , TEvent , any > ; /**
     * The string delimiter for serializing the path to a string. The default is "."
     */ public delimiter : string ; /**
     * The order this state node appears. Corresponds to the implicit SCXML document order.
     */ public order : number = - 1 ; /**
     * The services invoked by this state node.
     */ public invoke : Array < InvokeDefinition < TContext , TEvent > > ; public options : MachineOptions < TContext , TEvent > ; public schema : MachineSchema < TContext , TEvent > ; public __xstatenode : true = true ; public description ? : string ; private __cache = { events : undefined as Array < TEvent [ 'type' ] > | undefined , relativeValue : new Map ( ) as Map < StateNode < TContext > , StateValue > , initialStateValue : undefined as StateValue | undefined , initialState : undefined as State < TContext , TEvent > | undefined , on : undefined as TransitionDefinitionMap < TContext , TEvent > | undefined , transitions : undefined as Array < TransitionDefinition < TContext , TEvent > > | undefined , candidates : { } as { [ K in TEvent [ 'type' ] | NullEvent [ 'type' ] | '*' ] : Array < TransitionDefinition < TContext , K extends TEvent [ 'type' ] ? Extract < TEvent , { type : K ; } > : EventObject > > | undefined ; } , delayedTransitions : undefined as Array < DelayedTransitionDefinition < TContext , TEvent > > | undefined } ; private idMap : Record < string , StateNode < TContext , any , TEvent > > = { } ; public tags : string [ ] = [ ] ; constructor ( /**
     * The raw config used to create the machine.
     */ public config , options ? , /**
     * The initial extended state
     */ private _context = ( 'context' in config ? ( config as StateMachine < TContext , any , TEvent > ) . context : undefined ) as any ) { this . options = Object . assign ( createDefaultOptions < TContext > ( ) , options ) ; this . parent = this . options . _parent ; this . key = this . config . key || this . options . _key || this . config . id || '(machine)' ; this . machine = this . parent ? this . parent . machine : this ; this . path = this . parent ? this . parent . path . concat ( this . key ) : [ ] ; this . delimiter = this . config . delimiter || ( this . parent ? this . parent . delimiter : STATE_DELIMITER ) ; this . id = this . config . id || [ this . machine . key , ... this . path ] . join ( this . delimiter ) ; this . version = this . parent ? this . parent . version : ( this . config as MachineConfig < TContext , TStateSchema , TEvent > ) . version ; this . type = this . config . type || ( this . config . parallel ? 'parallel' : this . config . states && keys ( this . config . states ) . length ? 'compound' : this . config . history ? 'history' : 'atomic' ) ; this . schema = this . parent ? this . machine . schema : ( this . config as MachineConfig < TContext , TStateSchema , TEvent > ) . schema ?? ( { } as this [ 'schema' ] ) ; this . description = this . config . description ; if ( ! IS_PRODUCTION ) { warn ( ! ( 'parallel' in this . config ) , `The "parallel" property is deprecated and will be removed in version 4.1. ${ this . config . parallel ? `Replace with \`type: 'parallel'\`` : `Use \`type: '${ this . type }'\`` } in the config for state node '${ this . id }' instead.` ) ; } this . initial = this . config . initial ; this . states = ( this . config . states ? mapValues ( this . config . states , ( stateConfig , key ) => { const stateNode = new StateNode ( stateConfig , { _parent : this , _key : key as string } ) ; Object . assign ( this . idMap , { [ stateNode . id ] : stateNode , ... stateNode . idMap } ) ; return stateNode ; } ) : EMPTY_OBJECT ) as StateNodesConfig < TContext , TStateSchema , TEvent > ; let order = 0 ; function dfs ( stateNode ) { stateNode . order = order ++ ; for ( const child of getChildren ( stateNode ) ) { dfs ( child ) ; } } dfs ( this ) ; this . history = this . config . history === true ? 'shallow' : this . config . history || false ; this . _transient = ! ! this . config . always || ( ! this . config . on ? false : Array . isArray ( this . config . on ) ? this . config . on . some ( ( { event } ) => { return event === NULL_EVENT ; } ) : NULL_EVENT in this . config . on ) ; this . strict = ! ! this . config . strict ; this . onEntry = toArray ( this . config . entry || this . config . onEntry ) . map ( ( action ) => toActionObject ( action ) ) ; this . onExit = toArray ( this . config . exit || this . config . onExit ) . map ( ( action ) => toActionObject ( action ) ) ; this . meta = this . config . meta ; this . doneData = this . type === 'final' ? ( this . config as FinalStateNodeConfig < TContext , TEvent > ) . data : undefined ; this . invoke = toArray ( this . config . invoke ) . map ( ( invokeConfig , i ) => { if ( isMachine ( invokeConfig ) ) { const invokeId = createInvokeId ( this . id , i ) ; this . machine . options . services = { [ invokeId ] : invokeConfig , ... this . machine . options . services } ; return toInvokeDefinition ( { src : invokeId , id : invokeId } ) ; } else if ( isString ( invokeConfig . src ) ) { const invokeId = invokeConfig . id || createInvokeId ( this . id , i ) ; return toInvokeDefinition ( { ... invokeConfig , id : invokeId , src : invokeConfig . src as string } ) ; } else if ( isMachine ( invokeConfig . src ) || isFunction ( invokeConfig . src ) ) { const invokeId = invokeConfig . id || createInvokeId ( this . id , i ) ; this . machine . options . services = { [ invokeId ] : invokeConfig . src as InvokeCreator < TContext , TEvent > , ... this . machine . options . services } ; return toInvokeDefinition ( { id : invokeId , ... invokeConfig , src : invokeId } ) ; } else { const invokeSource = invokeConfig . src as InvokeSourceDefinition ; return toInvokeDefinition ( { id : createInvokeId ( this . id , i ) , ... invokeConfig , src : invokeSource } ) ; } } ) ; this . activities = toArray ( this . config . activities ) . concat ( this . invoke ) . map ( ( activity ) => toActivityDefinition ( activity ) ) ; this . transition = this . transition . bind ( this ) ; this . tags = toArray ( this . config . tags ) ; } private _init ( ) { if ( this . __cache . transitions ) { return ; } getAllStateNodes ( this ) . forEach ( ( stateNode ) => stateNode . on ) ; } param options param context public withConfig ( options , context ? ) { const { actions , activities , guards , services , delays } = this . options ; return new StateNode ( this . config , { actions : { ... actions , ... options . actions } , activities : { ... activities , ... options . activities } , guards : { ... guards , ... options . guards } , services : { ... services , ... options . services } , delays : { ... delays , ... options . delays } } , context ?? this . context ) ; } param context public withContext ( context ) { return new StateNode ( this . config , this . options , context ) ; } public get context ( ) : TContext { return isFunction ( this . _context ) ? this . _context ( ) : this . _context ; } /**
     * The well-structured state node definition.
     */ public get definition ( ) : StateNodeDefinition < TContext , TStateSchema , TEvent > { return { id : this . id , key : this . key , version : this . version , context : this . context , type : this . type , initial : this . initial , history : this . history , states : mapValues ( this . states , ( state ) => state . definition ) as StatesDefinition < TContext , TStateSchema , TEvent > , on : this . on , transitions : this . transitions , entry : this . onEntry , exit : this . onExit , activities : this . activities || [ ] , meta : this . meta , order : this . order || - 1 , data : this . doneData , invoke : this . invoke , description : this . description , tags : this . tags } ; } public toJSON ( ) { return this . definition ; } /**
     * The mapping of events to transitions.
     */ public get on ( ) : TransitionDefinitionMap < TContext , TEvent > { if ( this . __cache . on ) { return this . __cache . on ; } const transitions = this . transitions ; return ( this . __cache . on = transitions . reduce ( ( map , transition ) => { map [ transition . eventType ] = map [ transition . eventType ] || [ ] ; map [ transition . eventType ] . push ( transition as any ) ; return map ; } , { } as TransitionDefinitionMap < TContext , TEvent > ) ) ; } public get after ( ) : Array < DelayedTransitionDefinition < TContext , TEvent > > { return ( this . __cache . delayedTransitions || ( ( this . __cache . delayedTransitions = this . getDelayedTransitions ( ) ) , this . __cache . delayedTransitions ) ) ; } /**
     * All the transitions that can be taken from this state node.
     */ public get transitions ( ) : Array < TransitionDefinition < TContext , TEvent > > { return ( this . __cache . transitions || ( ( this . __cache . transitions = this . formatTransitions ( ) ) , this . __cache . transitions ) ) ; } private getCandidates ( eventName ) { if ( this . __cache . candidates [ eventName ] ) { return this . __cache . candidates [ eventName ] ; } const transient = eventName === NULL_EVENT ; const candidates = this . transitions . filter ( ( transition ) => { const sameEventType = transition . eventType === eventName ; return transient ? sameEventType : sameEventType || transition . eventType === WILDCARD ; } ) as any ; this . __cache . candidates [ eventName ] = candidates ; return candidates ; } /**
     * All delayed transitions from the config.
     */ private getDelayedTransitions ( ) { const afterConfig = this . config . after ; if ( ! afterConfig ) { return [ ] ; } const mutateEntryExit = ( delay , i ) => { const delayRef = isFunction ( delay ) ? `${ this . id }:delay[${ i }]` : delay ; const eventType = after ( delayRef , this . id ) ; this . onEntry . push ( send ( eventType , { delay } ) ) ; this . onExit . push ( cancel ( eventType ) ) ; return eventType ; } ; const delayedTransitions = isArray ( afterConfig ) ? afterConfig . map ( ( transition , i ) => { const eventType = mutateEntryExit ( transition . delay , i ) ; return { ... transition , event : eventType } ; } ) : flatten ( keys ( afterConfig ) . map ( ( delay , i ) => { const configTransition = afterConfig [ delay ] ; const resolvedTransition = isString ( configTransition ) ? { target : configTransition } : configTransition ; const resolvedDelay = ! isNaN ( + delay ) ? + delay : delay ; const eventType = mutateEntryExit ( resolvedDelay , i ) ; return toArray ( resolvedTransition ) . map ( ( transition ) => ( { ... transition , event : eventType , delay : resolvedDelay } ) ) ; } ) ) ; return delayedTransitions . map ( ( delayedTransition ) => { const { delay } = delayedTransition ; return { ... this . formatTransition ( delayedTransition ) , delay } ; } ) ; } param state public getStateNodes ( state ) { if ( ! state ) { return [ ] ; } const stateValue = state instanceof State ? state . value : toStateValue ( state , this . delimiter ) ; if ( isString ( stateValue ) ) { const initialStateValue = this . getStateNode ( stateValue ) . initial ; return initialStateValue !== undefined ? this . getStateNodes ( { [ stateValue ] : initialStateValue } as StateValue ) : [ this , this . states [ stateValue ] ] ; } const subStateKeys = keys ( stateValue ) ; const subStateNodes = [ this ] ; subStateNodes . push ( ... flatten ( subStateKeys . map ( ( subStateKey ) => this . getStateNode ( subStateKey ) . getStateNodes ( stateValue [ subStateKey ] ) ) ) ) ; return subStateNodes ; } param event public handles ( event ) { const eventType = getEventType < TEvent > ( event ) ; return this . events . includes ( eventType ) ; } param state public resolveState ( state ) { const configuration = Array . from ( getConfiguration ( [ ] , this . getStateNodes ( state . value ) ) ) ; return new State ( { ... state , value : this . resolve ( state . value ) , configuration , done : isInFinalState ( configuration , this ) , tags : getTagsFromConfiguration ( configuration ) } ) ; } private transitionLeafNode ( stateValue , state , _event ) { const stateNode = this . getStateNode ( stateValue ) ; const next = stateNode . next ( state , _event ) ; if ( ! next || ! next . transitions . length ) { return this . next ( state , _event ) ; } return next ; } private transitionCompoundNode ( stateValue , state , _event ) { const subStateKeys = keys ( stateValue ) ; const stateNode = this . getStateNode ( subStateKeys [ 0 ] ) ; const next = stateNode . _transition ( stateValue [ subStateKeys [ 0 ] ] , state , _event ) ; if ( ! next || ! next . transitions . length ) { return this . next ( state , _event ) ; } return next ; } private transitionParallelNode ( stateValue , state , _event ) { const transitionMap = { } ; for ( const subStateKey of keys ( stateValue ) ) { const subStateValue = stateValue [ subStateKey ] ; if ( ! subStateValue ) { continue ; } const subStateNode = this . getStateNode ( subStateKey ) ; const next = subStateNode . _transition ( subStateValue , state , _event ) ; if ( next ) { transitionMap [ subStateKey ] = next ; } } const stateTransitions = keys ( transitionMap ) . map ( ( key ) => transitionMap [ key ] ) ; const enabledTransitions = flatten ( stateTransitions . map ( ( st ) => st . transitions ) ) ; const willTransition = stateTransitions . some ( ( st ) => st . transitions . length > 0 ) ; if ( ! willTransition ) { return this . next ( state , _event ) ; } const entryNodes = flatten ( stateTransitions . map ( ( t ) => t . entrySet ) ) ; const configuration = flatten ( keys ( transitionMap ) . map ( ( key ) => transitionMap [ key ] . configuration ) ) ; return { transitions : enabledTransitions , entrySet : entryNodes , exitSet : flatten ( stateTransitions . map ( ( t ) => t . exitSet ) ) , configuration , source : state , actions : flatten ( keys ( transitionMap ) . map ( ( key ) => { return transitionMap [ key ] . actions ; } ) ) } ; } private _transition ( stateValue , state , _event ) { if ( isString ( stateValue ) ) { return this . transitionLeafNode ( stateValue , state , _event ) ; } if ( keys ( stateValue ) . length === 1 ) { return this . transitionCompoundNode ( stateValue , state , _event ) ; } return this . transitionParallelNode ( stateValue , state , _event ) ; } public getTransitionData ( state , event ) { return this . _transition ( state . value , state , toSCXMLEvent ( event ) ) ; } private next ( state , _event ) { const eventName = _event . name ; const actions = [ ] ; let nextStateNodes = [ ] ; let selectedTransition ; for ( const candidate of this . getCandidates ( eventName ) ) { const { cond , in : stateIn } = candidate ; const resolvedContext = state . context ; const isInState = stateIn ? isString ( stateIn ) && isStateId ( stateIn ) ? state . matches ( toStateValue ( this . getStateNodeById ( stateIn ) . path , this . delimiter ) ) : matchesState ( toStateValue ( stateIn , this . delimiter ) , path ( this . path . slice ( 0 , - 2 ) ) ( state . value ) ) : true ; let guardPassed = false ; try { guardPassed = ! cond || evaluateGuard ( this . machine , cond , resolvedContext , _event , state ) ; } catch ( err ) { throw new Error ( `Unable to evaluate guard '${ cond ! . name || cond ! . type }' in transition for event '${ eventName }' in state node '${ this . id }':\n${ err . message }` ) ; } if ( guardPassed && isInState ) { if ( candidate . target !== undefined ) { nextStateNodes = candidate . target ; } actions . push ( ... candidate . actions ) ; selectedTransition = candidate ; break ; } } if ( ! selectedTransition ) { return undefined ; } if ( ! nextStateNodes . length ) { return { transitions : [ selectedTransition ] , entrySet : [ ] , exitSet : [ ] , configuration : state . value ? [ this ] : [ ] , source : state , actions } ; } const allNextStateNodes = flatten ( nextStateNodes . map ( ( stateNode ) => { return this . getRelativeStateNodes ( stateNode , state . historyValue ) ; } ) ) ; const isInternal = ! ! selectedTransition . internal ; const reentryNodes = isInternal ? [ ] : flatten ( allNextStateNodes . map ( ( n ) => this . nodesFromChild ( n ) ) ) ; return { transitions : [ selectedTransition ] , entrySet : reentryNodes , exitSet : isInternal ? [ ] : [ this ] , configuration : allNextStateNodes , source : state , actions } ; } private nodesFromChild ( childStateNode ) { if ( childStateNode . escapes ( this ) ) { return [ ] ; } const nodes = [ ] ; let marker = childStateNode ; while ( marker && marker !== this ) { nodes . push ( marker ) ; marker = marker . parent ; } nodes . push ( this ) ; return nodes ; } /**
     * Whether the given state node "escapes" this state node. If the `stateNode` is equal to or the parent of
     * this state node, it does not escape.
     */ private escapes ( stateNode ) { if ( this === stateNode ) { return false ; } let parent = this . parent ; while ( parent ) { if ( parent === stateNode ) { return false ; } parent = parent . parent ; } return true ; } private getActions ( transition , currentContext , _event , prevState ? ) { const prevConfig = getConfiguration ( [ ] , prevState ? this . getStateNodes ( prevState . value ) : [ this ] ) ; const resolvedConfig = transition . configuration . length ? getConfiguration ( prevConfig , transition . configuration ) : prevConfig ; for ( const sn of resolvedConfig ) { if ( ! has ( prevConfig , sn ) ) { transition . entrySet . push ( sn ) ; } } for ( const sn of prevConfig ) { if ( ! has ( resolvedConfig , sn ) || has ( transition . exitSet , sn . parent ) ) { transition . exitSet . push ( sn ) ; } } const doneEvents = flatten ( transition . entrySet . map ( ( sn ) => { const events = [ ] ; if ( sn . type !== 'final' ) { return events ; } const parent = sn . parent ! ; if ( ! parent . parent ) { return events ; } events . push ( done ( sn . id , sn . doneData ) , done ( parent . id , sn . doneData ? mapContext ( sn . doneData , currentContext , _event ) : undefined ) ) ; const grandparent = parent . parent ! ; if ( grandparent . type === 'parallel' ) { if ( getChildren ( grandparent ) . every ( ( parentNode ) => isInFinalState ( transition . configuration , parentNode ) ) ) { events . push ( done ( grandparent . id ) ) ; } } return events ; } ) ) ; transition . exitSet . sort ( ( a , b ) => b . order - a . order ) ; transition . entrySet . sort ( ( a , b ) => a . order - b . order ) ; const entryStates = new Set ( transition . entrySet ) ; const exitStates = new Set ( transition . exitSet ) ; const [ entryActions , exitActions ] = [ flatten ( Array . from ( entryStates ) . map ( ( stateNode ) => { return [ ... stateNode . activities . map ( ( activity ) => start ( activity ) ) , ... stateNode . onEntry ] ; } ) ) . concat ( doneEvents . map ( raise ) as Array < ActionObject < TContext , TEvent > > ) , flatten ( Array . from ( exitStates ) . map ( ( stateNode ) => [ ... stateNode . onExit , ... stateNode . activities . map ( ( activity ) => stop ( activity ) ) ] ) ) ] ; const actions = toActionObjects ( exitActions . concat ( transition . actions ) . concat ( entryActions ) , this . machine . options . actions ) as Array < ActionObject < TContext , TEvent > > ; return actions ; } param state param event param context public transition ( state = this . initialState , event , context ? ) { const _event = toSCXMLEvent ( event ) ; let currentState ; if ( state instanceof State ) { currentState = context === undefined ? state : this . resolveState ( State . from ( state , context ) ) ; } else { const resolvedStateValue = isString ( state ) ? this . resolve ( pathToStateValue ( this . getResolvedPath ( state ) ) ) : this . resolve ( state ) ; const resolvedContext = context ?? this . machine . context ; currentState = this . resolveState ( State . from < TContext , TEvent > ( resolvedStateValue , resolvedContext ) ) ; } if ( ! IS_PRODUCTION && _event . name === WILDCARD ) { throw new Error ( `An event cannot have the wildcard type ('${ WILDCARD }')` ) ; } if ( this . strict ) { if ( ! this . events . includes ( _event . name ) && ! isBuiltInEvent ( _event . name ) ) { throw new Error ( `Machine '${ this . id }' does not accept event '${ _event . name }'` ) ; } } const stateTransition = this . _transition ( currentState . value , currentState , _event ) || { transitions : [ ] , configuration : [ ] , entrySet : [ ] , exitSet : [ ] , source : currentState , actions : [ ] } ; const prevConfig = getConfiguration ( [ ] , this . getStateNodes ( currentState . value ) ) ; const resolvedConfig = stateTransition . configuration . length ? getConfiguration ( prevConfig , stateTransition . configuration ) : prevConfig ; stateTransition . configuration = [ ... resolvedConfig ] ; return this . resolveTransition ( stateTransition , currentState , _event ) ; } private resolveRaisedTransition ( state , _event , originalEvent ) { const currentActions = state . actions ; state = this . transition ( state , _event as SCXML . Event < TEvent > ) ; state . _event = originalEvent ; state . event = originalEvent . data ; state . actions . unshift ( ... currentActions ) ; return state ; } private resolveTransition ( stateTransition , currentState ? , _event = initEvent as SCXML . Event < TEvent > , context = this . machine . context ) { const { configuration } = stateTransition ; const willTransition = ! currentState || stateTransition . transitions . length > 0 ; const resolvedStateValue = willTransition ? getValue ( this . machine , configuration ) : undefined ; const historyValue = currentState ? currentState . historyValue ? currentState . historyValue : stateTransition . source ? ( this . machine . historyValue ( currentState . value ) as HistoryValue ) : undefined : undefined ; const currentContext = currentState ? currentState . context : context ; const actions = this . getActions ( stateTransition , currentContext , _event , currentState ) ; const activities = currentState ? { ... currentState . activities } : { } ; for ( const action of actions ) { if ( action . type === actionTypes . start ) { activities [ action . activity ! . id || action . activity ! . type ] = action as ActivityDefinition < TContext , TEvent > ; } else if ( action . type === actionTypes . stop ) { activities [ action . activity ! . id || action . activity ! . type ] = false ; } } const [ resolvedActions , updatedContext ] = resolveActions ( this , currentState , currentContext , _event , actions , this . machine . config . preserveActionOrder ) ; const [ raisedEvents , nonRaisedActions ] = partition ( resolvedActions , ( action ) : action is RaiseActionObject < TEvent > | SendActionObject < TContext , TEvent , TEvent > => action . type === actionTypes . raise || ( action . type === actionTypes . send && ( action as SendActionObject < TContext , TEvent > ) . to === SpecialTargets . Internal ) ) ; const invokeActions = resolvedActions . filter ( ( action ) => { return ( action . type === actionTypes . start && ( action as ActivityActionObject < TContext , TEvent > ) . activity ?. type === actionTypes . invoke ) ; } ) as Array < InvokeActionObject < TContext , TEvent > > ; const children = invokeActions . reduce ( ( acc , action ) => { acc [ action . activity . id ] = createInvocableActor ( action . activity , this . machine , updatedContext , _event ) ; return acc ; } , currentState ? { ... currentState . children } : ( { } as Record < string , ActorRef < any > > ) ) ; const resolvedConfiguration = willTransition ? stateTransition . configuration : currentState ? currentState . configuration : [ ] ; const isDone = isInFinalState ( resolvedConfiguration , this ) ; const nextState = new State < TContext , TEvent , TStateSchema , TTypestate > ( { value : resolvedStateValue || currentState ! . value , context : updatedContext , _event , _sessionid : currentState ? currentState . _sessionid : null , historyValue : resolvedStateValue ? historyValue ? updateHistoryValue ( historyValue , resolvedStateValue ) : undefined : currentState ? currentState . historyValue : undefined , history : ! resolvedStateValue || stateTransition . source ? currentState : undefined , actions : resolvedStateValue ? nonRaisedActions : ( [ ] as any [ ] ) , activities : resolvedStateValue ? activities : currentState ? currentState . activities : { } , events : [ ] , configuration : resolvedConfiguration , transitions : stateTransition . transitions , children , done : isDone , tags : currentState ?. tags , machine : this } ) ; const didUpdateContext = currentContext !== updatedContext ; nextState . changed = _event . name === actionTypes . update || didUpdateContext ; const { history } = nextState ; if ( history ) { delete history . history ; } const isTransient = ! isDone && ( this . _transient || configuration . some ( ( stateNode ) => { return stateNode . _transient ; } ) ) ; if ( ! willTransition && ( ! isTransient || _event . name === NULL_EVENT ) ) { return nextState ; } let maybeNextState = nextState ; if ( ! isDone ) { if ( isTransient ) { maybeNextState = this . resolveRaisedTransition ( maybeNextState , { type : actionTypes . nullEvent } , _event ) ; } while ( raisedEvents . length ) { const raisedEvent = raisedEvents . shift ( ) ! ; maybeNextState = this . resolveRaisedTransition ( maybeNextState , raisedEvent . _event , _event ) ; } } const changed = maybeNextState . changed || ( history ? ! ! maybeNextState . actions . length || didUpdateContext || typeof history . value !== typeof maybeNextState . value || ! stateValuesEqual ( maybeNextState . value , history . value ) : undefined ) ; maybeNextState . changed = changed ; maybeNextState . history = history ; maybeNextState . tags = getTagsFromConfiguration ( maybeNextState . configuration ) ; return maybeNextState ; } /**
     * Returns the child state node from its relative `stateKey`, or throws.
     */ public getStateNode ( stateKey ) { if ( isStateId ( stateKey ) ) { return this . machine . getStateNodeById ( stateKey ) ; } if ( ! this . states ) { throw new Error ( `Unable to retrieve child state '${ stateKey }' from '${ this . id }'; no child states exist.` ) ; } const result = this . states [ stateKey ] ; if ( ! result ) { throw new Error ( `Child state '${ stateKey }' does not exist on '${ this . id }'` ) ; } return result ; } param stateId public getStateNodeById ( stateId ) { const resolvedStateId = isStateId ( stateId ) ? stateId . slice ( STATE_IDENTIFIER . length ) : stateId ; if ( resolvedStateId === this . id ) { return this ; } const stateNode = this . machine . idMap [ resolvedStateId ] ; if ( ! stateNode ) { throw new Error ( `Child state node '#${ resolvedStateId }' does not exist on machine '${ this . id }'` ) ; } return stateNode ; } param statePath public getStateNodeByPath ( statePath ) { if ( typeof statePath === 'string' && isStateId ( statePath ) ) { try { return this . getStateNodeById ( statePath . slice ( 1 ) ) ; } catch ( e ) { } } const arrayStatePath = toStatePath ( statePath , this . delimiter ) . slice ( ) ; let currentStateNode = this ; while ( arrayStatePath . length ) { const key = arrayStatePath . shift ( ) ! ; if ( ! key . length ) { break ; } currentStateNode = currentStateNode . getStateNode ( key ) ; } return currentStateNode ; } param stateValue public resolve ( stateValue ) { if ( ! stateValue ) { return this . initialStateValue || EMPTY_OBJECT ; } switch ( this . type ) { case 'parallel' : return mapValues ( this . initialStateValue as Record < string , StateValue > , ( subStateValue , subStateKey ) => { return subStateValue ? this . getStateNode ( subStateKey ) . resolve ( stateValue [ subStateKey ] || subStateValue ) : EMPTY_OBJECT ; } ) ; case 'compound' : if ( isString ( stateValue ) ) { const subStateNode = this . getStateNode ( stateValue ) ; if ( subStateNode . type === 'parallel' || subStateNode . type === 'compound' ) { return { [ stateValue ] : subStateNode . initialStateValue ! } ; } return stateValue ; } if ( ! keys ( stateValue ) . length ) { return this . initialStateValue || { } ; } return mapValues ( stateValue , ( subStateValue , subStateKey ) => { return subStateValue ? this . getStateNode ( subStateKey as string ) . resolve ( subStateValue ) : EMPTY_OBJECT ; } ) ; default : return stateValue || EMPTY_OBJECT ; } } private getResolvedPath ( stateIdentifier ) { if ( isStateId ( stateIdentifier ) ) { const stateNode = this . machine . idMap [ stateIdentifier . slice ( STATE_IDENTIFIER . length ) ] ; if ( ! stateNode ) { throw new Error ( `Unable to find state node '${ stateIdentifier }'` ) ; } return stateNode . path ; } return toStatePath ( stateIdentifier , this . delimiter ) ; } private get initialStateValue ( ) : StateValue | undefined { if ( this . __cache . initialStateValue ) { return this . __cache . initialStateValue ; } let initialStateValue ; if ( this . type === 'parallel' ) { initialStateValue = mapFilterValues ( this . states as Record < string , StateNode < TContext , any , TEvent > > , ( state ) => state . initialStateValue || EMPTY_OBJECT , ( stateNode ) => ! ( stateNode . type === 'history' ) ) ; } else if ( this . initial !== undefined ) { if ( ! this . states [ this . initial ] ) { throw new Error ( `Initial state '${ this . initial }' not found on '${ this . key }'` ) ; } initialStateValue = ( isLeafNode ( this . states [ this . initial ] ) ? this . initial : { [ this . initial ] : this . states [ this . initial ] . initialStateValue } ) as StateValue ; } else { initialStateValue = { } ; } this . __cache . initialStateValue = initialStateValue ; return this . __cache . initialStateValue ; } public getInitialState ( stateValue , context ? ) { const configuration = this . getStateNodes ( stateValue ) ; return this . resolveTransition ( { configuration , entrySet : configuration , exitSet : [ ] , transitions : [ ] , source : undefined , actions : [ ] } , undefined , undefined , context ) ; } /**
     * The initial State instance, which includes all actions to be executed from
     * entering the initial state.
     */ public get initialState ( ) : State < TContext , TEvent , TStateSchema , TTypestate > { this . _init ( ) ; const { initialStateValue } = this ; if ( ! initialStateValue ) { throw new Error ( `Cannot retrieve initial state from simple state '${ this . id }'.` ) ; } return this . getInitialState ( initialStateValue ) ; } /**
     * The target state value of the history state node, if it exists. This represents the
     * default state value to transition to if no history value exists yet.
     */ public get target ( ) : StateValue | undefined { let target ; if ( this . type === 'history' ) { const historyConfig = this . config as HistoryStateNodeConfig < TContext , TEvent > ; if ( isString ( historyConfig . target ) ) { target = isStateId ( historyConfig . target ) ? pathToStateValue ( this . machine . getStateNodeById ( historyConfig . target ) . path . slice ( this . path . length - 1 ) ) : historyConfig . target ; } else { target = historyConfig . target ; } } return target ; } param relativeStateId param history param resolve public getRelativeStateNodes ( relativeStateId , historyValue ? , resolve = true ) { return resolve ? relativeStateId . type === 'history' ? relativeStateId . resolveHistory ( historyValue ) : relativeStateId . initialStateNodes : [ relativeStateId ] ; } public get initialStateNodes ( ) : Array < StateNode < TContext , any , TEvent , any > > { if ( isLeafNode ( this ) ) { return [ this ] ; } if ( this . type === 'compound' && ! this . initial ) { if ( ! IS_PRODUCTION ) { warn ( false , `Compound state node '${ this . id }' has no initial state.` ) ; } return [ this ] ; } const initialStateNodePaths = toStatePaths ( this . initialStateValue ! ) ; return flatten ( initialStateNodePaths . map ( ( initialPath ) => this . getFromRelativePath ( initialPath ) ) ) ; } param relativePath param historyValue public getFromRelativePath ( relativePath ) { if ( ! relativePath . length ) { return [ this ] ; } const [ stateKey , ... childStatePath ] = relativePath ; if ( ! this . states ) { throw new Error ( `Cannot retrieve subPath '${ stateKey }' from node with no states` ) ; } const childStateNode = this . getStateNode ( stateKey ) ; if ( childStateNode . type === 'history' ) { return childStateNode . resolveHistory ( ) ; } if ( ! this . states [ stateKey ] ) { throw new Error ( `Child state '${ stateKey }' does not exist on '${ this . id }'` ) ; } return this . states [ stateKey ] . getFromRelativePath ( childStatePath ) ; } private historyValue ( relativeStateValue ? ) { if ( ! keys ( this . states ) . length ) { return undefined ; } return { current : relativeStateValue || this . initialStateValue , states : mapFilterValues < StateNode < TContext , any , TEvent > , HistoryValue | undefined > ( this . states , ( stateNode , key ) => { if ( ! relativeStateValue ) { return stateNode . historyValue ( ) ; } const subStateValue = isString ( relativeStateValue ) ? undefined : relativeStateValue [ key ] ; return stateNode . historyValue ( subStateValue || stateNode . initialStateValue ) ; } , ( stateNode ) => ! stateNode . history ) } ; } param historyValue private resolveHistory ( historyValue ? ) { if ( this . type !== 'history' ) { return [ this ] ; } const parent = this . parent ! ; if ( ! historyValue ) { const historyTarget = this . target ; return historyTarget ? flatten ( toStatePaths ( historyTarget ) . map ( ( relativeChildPath ) => parent . getFromRelativePath ( relativeChildPath ) ) ) : parent . initialStateNodes ; } const subHistoryValue = nestedPath < HistoryValue > ( parent . path , 'states' ) ( historyValue ) . current ; if ( isString ( subHistoryValue ) ) { return [ parent . getStateNode ( subHistoryValue ) ] ; } return flatten ( toStatePaths ( subHistoryValue ! ) . map ( ( subStatePath ) => { return this . history === 'deep' ? parent . getFromRelativePath ( subStatePath ) : [ parent . states [ subStatePath [ 0 ] ] ] ; } ) ) ; } /**
     * All the state node IDs of this state node and its descendant state nodes.
     */ public get stateIds ( ) : string [ ] { const childStateIds = flatten ( keys ( this . states ) . map ( ( stateKey ) => { return this . states [ stateKey ] . stateIds ; } ) ) ; return [ this . id ] . concat ( childStateIds ) ; } /**
     * All the event types accepted by this state node and its descendants.
     */ public get events ( ) : Array < TEvent [ 'type' ] > { if ( this . __cache . events ) { return this . __cache . events ; } const { states } = this ; const events = new Set ( this . ownEvents ) ; if ( states ) { for ( const stateId of keys ( states ) ) { const state = states [ stateId ] ; if ( state . states ) { for ( const event of state . events ) { events . add ( `${ event }` ) ; } } } } return ( this . __cache . events = Array . from ( events ) ) ; } /**
     * All the events that have transitions directly from this state node.
     *
     * Excludes any inert events.
     */ public get ownEvents ( ) : Array < TEvent [ 'type' ] > { const events = new Set ( this . transitions . filter ( ( transition ) => { return ! ( ! transition . target && ! transition . actions . length && transition . internal ) ; } ) . map ( ( transition ) => transition . eventType ) ) ; return Array . from ( events ) ; } private resolveTarget ( _target ) { if ( _target === undefined ) { return undefined ; } return _target . map ( ( target ) => { if ( ! isString ( target ) ) { return target ; } const isInternalTarget = target [ 0 ] === this . delimiter ; if ( isInternalTarget && ! this . parent ) { return this . getStateNodeByPath ( target . slice ( 1 ) ) ; } const resolvedTarget = isInternalTarget ? this . key + target : target ; if ( this . parent ) { try { const targetStateNode = this . parent . getStateNodeByPath ( resolvedTarget ) ; return targetStateNode ; } catch ( err ) { throw new Error ( `Invalid transition definition for state node '${ this . id }':\n${ err . message }` ) ; } } else { return this . getStateNodeByPath ( resolvedTarget ) ; } } ) ; } private formatTransition ( transitionConfig ) { const normalizedTarget = normalizeTarget ( transitionConfig . target ) ; const internal = 'internal' in transitionConfig ? transitionConfig . internal : normalizedTarget ? normalizedTarget . some ( ( _target ) => isString ( _target ) && _target [ 0 ] === this . delimiter ) : true ; const { guards } = this . machine . options ; const target = this . resolveTarget ( normalizedTarget ) ; const transition = { ... transitionConfig , actions : toActionObjects ( toArray ( transitionConfig . actions ) ) , cond : toGuard ( transitionConfig . cond , guards ) , target , source : this as any , internal , eventType : transitionConfig . event , toJSON : ( ) => ( { ... transition , target : transition . target ? transition . target . map ( ( t ) => `#${ t . id }` ) : undefined , source : `#${ this . id }` } ) } ; return transition ; } private formatTransitions ( ) { let onConfig ; if ( ! this . config . on ) { onConfig = [ ] ; } else if ( Array . isArray ( this . config . on ) ) { onConfig = this . config . on ; } else { const { [ WILDCARD ] : wildcardConfigs = [ ] , ... strictTransitionConfigs } = this . config . on ; onConfig = flatten ( keys ( strictTransitionConfigs ) . map ( ( key ) => { if ( ! IS_PRODUCTION && key === NULL_EVENT ) { warn ( false , `Empty string transition configs (e.g., \`{ on: { '': ... }}\`) for transient transitions are deprecated. Specify the transition in the \`{ always: ... }\` property instead. ` + `Please check the \`on\` configuration for "#${ this . id }".` ) ; } const transitionConfigArray = toTransitionConfigArray < TContext , EventObject > ( key , strictTransitionConfigs [ key as string ] ) ; if ( ! IS_PRODUCTION ) { validateArrayifiedTransitions ( this , key , transitionConfigArray ) ; } return transitionConfigArray ; } ) . concat ( toTransitionConfigArray ( WILDCARD , wildcardConfigs as SingleOrArray < TransitionConfig < TContext , EventObject > & { event : '*' ; } > ) ) ) ; } const eventlessConfig = this . config . always ? toTransitionConfigArray ( '' , this . config . always ) : [ ] ; const doneConfig = this . config . onDone ? toTransitionConfigArray ( String ( done ( this . id ) ) , this . config . onDone ) : [ ] ; if ( ! IS_PRODUCTION ) { warn ( ! ( this . config . onDone && ! this . parent ) , `Root nodes cannot have an ".onDone" transition. Please check the config of "${ this . id }".` ) ; } const invokeConfig = flatten ( this . invoke . map ( ( invokeDef ) => { const settleTransitions = [ ] ; if ( invokeDef . onDone ) { settleTransitions . push ( ... toTransitionConfigArray ( String ( doneInvoke ( invokeDef . id ) ) , invokeDef . onDone ) ) ; } if ( invokeDef . onError ) { settleTransitions . push ( ... toTransitionConfigArray ( String ( error ( invokeDef . id ) ) , invokeDef . onError ) ) ; } return settleTransitions ; } ) ) ; const delayedTransitions = this . after ; const formattedTransitions = flatten ( [ ... doneConfig , ... invokeConfig , ... onConfig , ... eventlessConfig ] . map ( ( transitionConfig ) => toArray ( transitionConfig ) . map ( ( transition ) => this . formatTransition ( transition ) ) ) ) ; for ( const delayedTransition of delayedTransitions ) { formattedTransitions . push ( delayedTransition as any ) ; } return formattedTransitions ; } } export { StateNode } ;