import { Action , Event , EventObject , SingleOrArray , SendAction , SendActionOptions , CancelAction , ActionObject , ActionType , Assigner , PropertyAssigner , AssignAction , ActionFunction , ActionFunctionMap , ActivityActionObject , ActionTypes , ActivityDefinition , SpecialTargets , RaiseAction , RaiseActionObject , DoneEvent , ErrorPlatformEvent , DoneEventObject , SendExpr , SendActionObject , PureAction , LogExpr , LogAction , LogActionObject , DelayFunctionMap , SCXML , ExprWithMeta , ChooseCondition , ChooseAction , AnyEventObject , Expr , Cast } from './types' ; import * as actionTypes from './actionTypes' ; import { getEventType , isFunction , isString , toEventObject , toSCXMLEvent , partition , flatten , updateContext , warn , toGuard , evaluateGuard , toArray , isArray } from './utils' ; import { State } from './State' ; import { StateNode } from './StateNode' ; import { IS_PRODUCTION } from './environment' ; import { ActorRef , EventFrom , StopAction , StopActionObject } from '.' ; export { actionTypes } ; export const initEvent = toSCXMLEvent ( { type : actionTypes . init } ) ; export function getActionFunction < TContext , TEvent extends EventObject > ( actionType , actionFunctionMap ? ) { return actionFunctionMap ? actionFunctionMap [ actionType ] || undefined : undefined ; } export function toActionObject < TContext , TEvent extends EventObject > ( action , actionFunctionMap ? ) { let actionObject ; if ( isString ( action ) || typeof action === 'number' ) { const exec = getActionFunction ( action , actionFunctionMap ) ; if ( isFunction ( exec ) ) { actionObject = { type : action , exec } ; } else if ( exec ) { actionObject = exec ; } else { actionObject = { type : action , exec : undefined } ; } } else if ( isFunction ( action ) ) { actionObject = { type : action . name || action . toString ( ) , exec : action } ; } else { const exec = getActionFunction ( action . type , actionFunctionMap ) ; if ( isFunction ( exec ) ) { actionObject = { ... action , exec } ; } else if ( exec ) { const actionType = exec . type || action . type ; actionObject = { ... exec , ... action , type : actionType } as ActionObject < TContext , TEvent > ; } else { actionObject = action as ActionObject < TContext , TEvent > ; } } return actionObject ; } export const toActionObjects = < TContext , TEvent extends EventObject > ( action ? , actionFunctionMap ? ) : Array < ActionObject < TContext , TEvent > > => { if ( ! action ) { return [ ] ; } const actions = isArray ( action ) ? action : [ action ] ; return actions . map ( ( subAction ) => toActionObject ( subAction , actionFunctionMap ) ) ; } ; export function toActivityDefinition < TContext , TEvent extends EventObject > ( action ) { const actionObject = toActionObject ( action ) ; return { id : isString ( action ) ? action : actionObject . id , ... actionObject , type : actionObject . type } ; } param eventType export function raise < TContext , TEvent extends EventObject > ( event ) { if ( ! isString ( event ) ) { return send ( event , { to : SpecialTargets . Internal } ) ; } return { type : actionTypes . raise , event } ; } export function resolveRaise < TEvent extends EventObject > ( action ) { return { type : actionTypes . raise , _event : toSCXMLEvent ( action . event ) } ; } param event param options export function send < TContext , TEvent extends EventObject , TSentEvent extends EventObject = AnyEventObject > ( event , options ? ) { return { to : options ? options . to : undefined , type : actionTypes . send , event : isFunction ( event ) ? event : toEventObject < TSentEvent > ( event ) , delay : options ? options . delay : undefined , id : options && options . id !== undefined ? options . id : isFunction ( event ) ? event . name : ( getEventType < TSentEvent > ( event ) as string ) } ; } export function resolveSend < TContext , TEvent extends EventObject , TSentEvent extends EventObject > ( action , ctx , _event , delaysMap ? ) { const meta = { _event } ; const resolvedEvent = toSCXMLEvent ( isFunction ( action . event ) ? action . event ( ctx , _event . data , meta ) : action . event ) ; let resolvedDelay ; if ( isString ( action . delay ) ) { const configDelay = delaysMap && delaysMap [ action . delay ] ; resolvedDelay = isFunction ( configDelay ) ? configDelay ( ctx , _event . data , meta ) : configDelay ; } else { resolvedDelay = isFunction ( action . delay ) ? action . delay ( ctx , _event . data , meta ) : action . delay ; } const resolvedTarget = isFunction ( action . to ) ? action . to ( ctx , _event . data , meta ) : action . to ; return { ... action , to : resolvedTarget , _event : resolvedEvent , event : resolvedEvent . data , delay : resolvedDelay } ; } param event param options export function sendParent < TContext , TEvent extends EventObject , TSentEvent extends EventObject = AnyEventObject > ( event , options ? ) { return send < TContext , TEvent , TSentEvent > ( event , { ... options , to : SpecialTargets . Parent } ) ; } type InferEvent < E extends EventObject > = { [ T in E [ 'type' ] ] : { type : T ; } & Extract < E , { type : T ; } > ; } [ E [ 'type' ] ] ; param actor param event param options returns export function sendTo < TContext , TEvent extends EventObject , TActor extends ActorRef < EventObject > > ( actor , event , options ? ) { return send < TContext , TEvent , any > ( event , { ... options , to : actor } ) ; } /**
 * Sends an update event to this machine's parent.
 */ export function sendUpdate < TContext , TEvent extends EventObject > ( ) { return sendParent < TContext , TEvent , { type : ActionTypes . Update ; } > ( actionTypes . update ) ; } param event param options export function respond < TContext , TEvent extends EventObject , TSentEvent extends EventObject = AnyEventObject > ( event , options ? ) { return send < TContext , TEvent > ( event , { ... options , to : ( _ , __ , { _event } ) => { return _event . origin ! ; } } ) ; } const defaultLogExpr = < TContext , TEvent extends EventObject > ( context , event ) => ( { context , event } ) ; param expr param label export function log < TContext , TEvent extends EventObject > ( expr = defaultLogExpr , label ? ) { return { type : actionTypes . log , label , expr } ; } export const resolveLog = < TContext , TEvent extends EventObject > ( action , ctx , _event ) : LogActionObject < TContext , TEvent > => ( { ... action , value : isString ( action . expr ) ? action . expr : action . expr ( ctx , _event . data , { _event } ) } ) ; param sendId export const cancel = ( sendId ) : CancelAction => { return { type : actionTypes . cancel , sendId } ; } ; param activity export function start < TContext , TEvent extends EventObject > ( activity ) { const activityDef = toActivityDefinition ( activity ) ; return { type : ActionTypes . Start , activity : activityDef , exec : undefined } ; } param actorRef export function stop < TContext , TEvent extends EventObject > ( actorRef ) { const activity = isFunction ( actorRef ) ? actorRef : toActivityDefinition ( actorRef ) ; return { type : ActionTypes . Stop , activity , exec : undefined } ; } export function resolveStop < TContext , TEvent extends EventObject > ( action , context , _event ) { const actorRefOrString = isFunction ( action . activity ) ? action . activity ( context , _event . data ) : action . activity ; const resolvedActorRef = typeof actorRefOrString === 'string' ? { id : actorRefOrString } : actorRefOrString ; const actionObject = { type : ActionTypes . Stop as const , activity : resolvedActorRef } ; return actionObject ; } param assignment export const assign = < TContext , TEvent extends EventObject = EventObject > ( assignment ) : AssignAction < TContext , TEvent > => { return { type : actionTypes . assign , assignment } ; } ; export function isActionObject < TContext , TEvent extends EventObject > ( action ) { return typeof action === 'object' && 'type' in action ; } param delayRef param id export function after ( delayRef , id ? ) { const idSuffix = id ? `#${ id }` : '' ; return `${ ActionTypes . After }(${ delayRef })${ idSuffix }` ; } param id param data export function done ( id , data ? ) { const type = `${ ActionTypes . DoneState }.${ id }` ; const eventObject = { type , data } ; eventObject . toString = ( ) => type ; return eventObject as DoneEvent ; } param id param data export function doneInvoke ( id , data ? ) { const type = `${ ActionTypes . DoneInvoke }.${ id }` ; const eventObject = { type , data } ; eventObject . toString = ( ) => type ; return eventObject as DoneEvent ; } export function error ( id , data ? ) { const type = `${ ActionTypes . ErrorPlatform }.${ id }` ; const eventObject = { type , data } ; eventObject . toString = ( ) => type ; return eventObject as ErrorPlatformEvent & string ; } export function pure < TContext , TEvent extends EventObject > ( getActions ) { return { type : ActionTypes . Pure , get : getActions } ; } param target param options export function forwardTo < TContext , TEvent extends EventObject > ( target , options ? ) { return send < TContext , TEvent > ( ( _ , event ) => event , { ... options , to : target } ) ; } param errorData param options export function escalate < TContext , TEvent extends EventObject , TErrorData = any > ( errorData , options ? ) { return sendParent < TContext , TEvent > ( ( context , event , meta ) => { return { type : actionTypes . error , data : isFunction ( errorData ) ? errorData ( context , event , meta ) : errorData } ; } , { ... options , to : SpecialTargets . Parent } ) ; } export function choose < TContext , TEvent extends EventObject > ( conds ) { return { type : ActionTypes . Choose , conds } ; } export function resolveActions < TContext , TEvent extends EventObject > ( machine , currentState , currentContext , _event , actions , preserveActionOrder = false ) { const [ assignActions , otherActions ] = preserveActionOrder ? [ [ ] , actions ] : partition ( actions , ( action ) : action is AssignAction < TContext , TEvent > => action . type === actionTypes . assign ) ; let updatedContext = assignActions . length ? updateContext ( currentContext , _event , assignActions , currentState ) : currentContext ; const preservedContexts = preserveActionOrder ? [ currentContext ] : undefined ; const resolvedActions = flatten ( otherActions . map ( ( actionObject ) => { switch ( actionObject . type ) { case actionTypes . raise : return resolveRaise ( actionObject as RaiseAction < TEvent > ) ; case actionTypes . send : const sendAction = resolveSend ( actionObject as SendAction < TContext , TEvent , AnyEventObject > , updatedContext , _event , machine . options . delays ) as ActionObject < TContext , TEvent > ; if ( ! IS_PRODUCTION ) { warn ( ! isString ( actionObject . delay ) || typeof sendAction . delay === 'number' , `No delay reference for delay expression '${ actionObject . delay }' was found on machine '${ machine . id }'` ) ; } return sendAction ; case actionTypes . log : return resolveLog ( actionObject as LogAction < TContext , TEvent > , updatedContext , _event ) ; case actionTypes . choose : { const chooseAction = actionObject as ChooseAction < TContext , TEvent > ; const matchedActions = chooseAction . conds . find ( ( condition ) => { const guard = toGuard ( condition . cond , machine . options . guards ) ; return ( ! guard || evaluateGuard ( machine , guard , updatedContext , _event , currentState as any ) ) ; } ) ?. actions ; if ( ! matchedActions ) { return [ ] ; } const [ resolvedActionsFromChoose , resolvedContextFromChoose ] = resolveActions ( machine , currentState , updatedContext , _event , toActionObjects ( toArray ( matchedActions ) , machine . options . actions ) , preserveActionOrder ) ; updatedContext = resolvedContextFromChoose ; preservedContexts ?. push ( updatedContext ) ; return resolvedActionsFromChoose ; } case actionTypes . pure : { const matchedActions = ( actionObject as PureAction < TContext , TEvent > ) . get ( updatedContext , _event . data ) ; if ( ! matchedActions ) { return [ ] ; } const [ resolvedActionsFromPure , resolvedContext ] = resolveActions ( machine , currentState , updatedContext , _event , toActionObjects ( toArray ( matchedActions ) , machine . options . actions ) , preserveActionOrder ) ; updatedContext = resolvedContext ; preservedContexts ?. push ( updatedContext ) ; return resolvedActionsFromPure ; } case actionTypes . stop : { return resolveStop ( actionObject as StopAction < TContext , TEvent > , updatedContext , _event ) ; } case actionTypes . assign : { updatedContext = updateContext ( updatedContext , _event , [ actionObject as AssignAction < TContext , TEvent > ] , currentState ) ; preservedContexts ?. push ( updatedContext ) ; break ; } default : let resolvedActionObject = toActionObject ( actionObject , machine . options . actions ) ; const { exec } = resolvedActionObject ; if ( exec && preservedContexts ) { const contextIndex = preservedContexts . length - 1 ; resolvedActionObject = { ... resolvedActionObject , exec : ( _ctx , ... args ) => { exec ( preservedContexts [ contextIndex ] , ... args ) ; } } ; } return resolvedActionObject ; } } ) . filter ( ( a ) : a is ActionObject < TContext , TEvent > => ! ! a ) ) ; return [ resolvedActions , updatedContext ] ; }