import { StateValue , ActivityMap , EventObject , HistoryValue , ActionObject , EventType , StateValueMap , StateConfig , SCXML , StateSchema , TransitionDefinition , Typestate , ActorRef , StateMachine , SimpleEventsOf } from './types' ; import { EMPTY_ACTIVITY_MAP } from './constants' ; import { matchesState , keys , isString , warn } from './utils' ; import { StateNode } from './StateNode' ; import { getMeta , nextEvents } from './stateUtils' ; import { initEvent } from './actions' ; import { IS_PRODUCTION } from './environment' ; export function stateValuesEqual ( a , b ) { if ( a === b ) { return true ; } if ( a === undefined || b === undefined ) { return false ; } if ( isString ( a ) || isString ( b ) ) { return a === b ; } const aKeys = keys ( a as StateValueMap ) ; const bKeys = keys ( b as StateValueMap ) ; return ( aKeys . length === bKeys . length && aKeys . every ( ( key ) => stateValuesEqual ( a [ key ] , b [ key ] ) ) ) ; } export function isState < TContext , TEvent extends EventObject , TStateSchema extends StateSchema < TContext > = any , TTypestate extends Typestate < TContext > = { value : any ; context : TContext ; } > ( state ) { if ( isString ( state ) ) { return false ; } return 'value' in state && 'history' in state ; } export function bindActionToState < TC , TE extends EventObject > ( action , state ) { const { exec } = action ; const boundAction = { ... action , exec : exec !== undefined ? ( ) => exec ( state . context , state . event as TE , { action , state , _event : state . _event } ) : undefined } ; return boundAction ; } export class State < TContext , TEvent extends EventObject = EventObject , TStateSchema extends StateSchema < TContext > = any , TTypestate extends Typestate < TContext > = { value : any ; context : TContext ; } > { public value : StateValue ; public context : TContext ; public historyValue ? : HistoryValue | undefined ; public history ? : State < TContext , TEvent , TStateSchema , TTypestate > ; public actions : Array < ActionObject < TContext , TEvent > > = [ ] ; public activities : ActivityMap = EMPTY_ACTIVITY_MAP ; public meta : any = { } ; public events : TEvent [ ] = [ ] ; public event : TEvent ; public _event : SCXML . Event < TEvent > ; public _sessionid : string | null ; /**
     * Indicates whether the state has changed from the previous state. A state is considered "changed" if:
     *
     * - Its value is not equal to its previous value, or:
     * - It has any new actions (side-effects) to execute.
     *
     * An initial state (with no history) will return `undefined`.
     */ public changed : boolean | undefined ; /**
     * Indicates whether the state is a final state.
     */ public done : boolean | undefined ; /**
     * The enabled state nodes representative of the state value.
     */ public configuration : Array < StateNode < TContext , any , TEvent , any > > ; /**
     * The next events that will cause a transition from the current state.
     */ public nextEvents : EventType [ ] ; /**
     * The transition definitions that resulted in this state.
     */ public transitions : Array < TransitionDefinition < TContext , TEvent > > ; /**
     * An object mapping actor IDs to spawned actors/invoked services.
     */ public children : Record < string , ActorRef < any > > ; public tags : Set < string > ; public machine : StateMachine < TContext , any , TEvent , TTypestate > | undefined ; param stateValue param context public static from < TC , TE extends EventObject = EventObject > ( stateValue , context ? ) { if ( stateValue instanceof State ) { if ( stateValue . context !== context ) { return new State < TC , TE > ( { value : stateValue . value , context : context as TC , _event : stateValue . _event , _sessionid : null , historyValue : stateValue . historyValue , history : stateValue . history , actions : [ ] , activities : stateValue . activities , meta : { } , events : [ ] , configuration : [ ] , transitions : [ ] , children : { } } ) ; } return stateValue ; } const _event = initEvent as SCXML . Event < TE > ; return new State < TC , TE > ( { value : stateValue , context : context as TC , _event , _sessionid : null , historyValue : undefined , history : undefined , actions : [ ] , activities : undefined , meta : undefined , events : [ ] , configuration : [ ] , transitions : [ ] , children : { } } ) ; } param config public static create < TC , TE extends EventObject = EventObject > ( config ) { return new State ( config ) ; } param stateValue param context public static inert < TC , TE extends EventObject = EventObject > ( stateValue , context ) { if ( stateValue instanceof State ) { if ( ! stateValue . actions . length ) { return stateValue as State < TC , TE > ; } const _event = initEvent as SCXML . Event < TE > ; return new State < TC , TE > ( { value : stateValue . value , context , _event , _sessionid : null , historyValue : stateValue . historyValue , history : stateValue . history , activities : stateValue . activities , configuration : stateValue . configuration , transitions : [ ] , children : { } } ) ; } return State . from < TC , TE > ( stateValue , context ) ; } param value param context param historyValue param history param actions param activities param meta param events param configuration constructor ( config ) { this . value = config . value ; this . context = config . context ; this . _event = config . _event ; this . _sessionid = config . _sessionid ; this . event = this . _event . data ; this . historyValue = config . historyValue ; this . history = config . history as this ; this . actions = config . actions || [ ] ; this . activities = config . activities || EMPTY_ACTIVITY_MAP ; this . meta = getMeta ( config . configuration ) ; this . events = config . events || [ ] ; this . matches = this . matches . bind ( this ) ; this . toStrings = this . toStrings . bind ( this ) ; this . configuration = config . configuration ; this . transitions = config . transitions ; this . children = config . children ; this . done = ! ! config . done ; this . tags = ( Array . isArray ( config . tags ) ? new Set ( config . tags ) : config . tags ) ?? new Set ( ) ; this . machine = config . machine ; Object . defineProperty ( this , 'nextEvents' , { get : ( ) => { return nextEvents ( this . configuration ) ; } } ) ; } param stateValue param delimiter public toStrings ( stateValue = this . value , delimiter = '.' ) { if ( isString ( stateValue ) ) { return [ stateValue ] ; } const valueKeys = keys ( stateValue ) ; return valueKeys . concat ( ... valueKeys . map ( ( key ) => this . toStrings ( stateValue [ key ] , delimiter ) . map ( ( s ) => key + delimiter + s ) ) ) ; } public toJSON ( ) { const { configuration , transitions , tags , machine , ... jsonValues } = this ; return { ... jsonValues , tags : Array . from ( tags ) } ; } param parentStateValue public matches < TSV extends TTypestate [ 'value' ] > ( parentStateValue ) { return matchesState ( parentStateValue as StateValue , this . value ) ; } param tag public hasTag ( tag ) { return this . tags . has ( tag ) ; } param event returns public can ( event ) { if ( IS_PRODUCTION ) { warn ( ! ! this . machine , `state.can(...) used outside of a machine-created State object; this will always return false.` ) ; } const transitionData = this . machine ?. getTransitionData ( this , event ) ; return ( ! ! transitionData ?. transitions . length && transitionData . transitions . some ( ( t ) => t . target !== undefined || t . actions . length ) ) ; } }