@@ -67,6 +67,22 @@ interface Collected<Event> {
6767 readonly emittedEvents : Array < unknown >
6868}
6969
70+ const targetBuilderCache = new WeakMap < object , Map < string , unknown > > ( )
71+
72+ const getTargetBuilder = ( machine : Machine . Any , path : string ) : any => {
73+ let byPath = targetBuilderCache . get ( machine )
74+ if ( byPath === undefined ) {
75+ byPath = new Map ( )
76+ targetBuilderCache . set ( machine , byPath )
77+ }
78+ if ( byPath . has ( path ) ) {
79+ return byPath . get ( path )
80+ }
81+ const builder = machine . makeTargetBuilder ( path as any )
82+ byPath . set ( path , builder )
83+ return builder
84+ }
85+
7086const makeCollector = < Event > ( machine : Machine . Any ) : Collected < Event > => {
7187 const commands : Array < RuntimeCommand > = [ ]
7288 const raisedEvents : Array < Event > = [ ]
@@ -389,7 +405,7 @@ const resolveHistoryTarget = (
389405 }
390406 const collected = collectTransition ( machine , fallback , {
391407 event,
392- target : machine . makeTargetBuilder ( target . parent ) . full ,
408+ target : getTargetBuilder ( machine , target . parent ) . full ,
393409 parent : target . parent
394410 } )
395411 if ( collected . state === undefined || isHistoryTarget ( collected . state ) || ! isSnapshot ( collected . state ) ) {
@@ -591,7 +607,7 @@ const makeTransitionContext = <
591607 parents : getParentValues ( machine , configuration , path ) as Machine . ParentStateValues < States , StateId > ,
592608 event,
593609 snapshot,
594- target : machine . makeTargetBuilder ( path as StateId )
610+ target : getTargetBuilder ( machine , path )
595611} )
596612
597613const makeDoneContext = <
@@ -613,7 +629,7 @@ const makeDoneContext = <
613629 event,
614630 output : output as Machine . CompletionOutputByIdentifier < States , StateId > ,
615631 snapshot,
616- target : machine . makeTargetBuilder ( path as StateId )
632+ target : getTargetBuilder ( machine , path )
617633} )
618634
619635const collectStateActions = <
@@ -715,7 +731,7 @@ const selectAlwaysTransitions = <
715731 > ,
716732 event,
717733 snapshot : capturedSnapshot ( ) ,
718- target : machine . makeTargetBuilder ( path as Machine . StateIdentifier < States > )
734+ target : getTargetBuilder ( machine , path )
719735 }
720736 } )
721737 }
@@ -1081,7 +1097,7 @@ const resolveChoiceTarget = (
10811097 parent : getParentValue ( machine , provisional , node . path ) ,
10821098 parents : getParentValues ( machine , provisional , node . path ) ,
10831099 event,
1084- target : machine . makeTargetBuilder ( node . path as any )
1100+ target : getTargetBuilder ( machine , node . path )
10851101 } )
10861102 if ( collected . state === undefined ) {
10871103 throw new Error ( `Machine choice resolver for "${ node . path } " must return a target` )
@@ -1930,6 +1946,180 @@ const macrostepConfiguration = <
19301946 return settle ( machine , step . next , decodedEvent , commands , raisedEvents , emittedEvents , microsteps )
19311947}
19321948
1949+ interface FlatExecutionDescriptor {
1950+ readonly paths : ReadonlySet < string >
1951+ }
1952+
1953+ const compileFlatExecutionDescriptor = ( machine : Machine . Any ) : FlatExecutionDescriptor | undefined => {
1954+ const paths = new Set < string > ( )
1955+ for ( const node of machine . stateNodes . byPath . values ( ) as Iterable < Machine . StateNode > ) {
1956+ if ( node . parent !== undefined || ( node . type !== "atomic" && node . type !== "final" ) ) {
1957+ return undefined
1958+ }
1959+ const config = machine . handlers [ node . path ] as Machine . AnyStateConfig | undefined
1960+ if (
1961+ config ?. entry !== undefined || config ?. exit !== undefined || config ?. always !== undefined ||
1962+ config ?. onDone !== undefined || config ?. invoke !== undefined
1963+ ) {
1964+ return undefined
1965+ }
1966+ paths . add ( node . path )
1967+ }
1968+ return paths . size === 0 ? undefined : { paths }
1969+ }
1970+
1971+ const planFlatConfiguration = (
1972+ machine : Machine . Any ,
1973+ descriptor : FlatExecutionDescriptor ,
1974+ configuration : ActiveConfiguration ,
1975+ input : unknown
1976+ ) : MacrostepPlan < ActiveConfiguration , any , any , any , any > => {
1977+ const decoded = decodeEventSync ( machine , input ) as { readonly _tag : PropertyKey }
1978+ if ( isActiveFinalConfiguration ( machine , configuration ) ) {
1979+ const completed = completeConfigurationSync ( machine , configuration , decoded )
1980+ const root = getRootPath ( machine , completed . configuration )
1981+ if ( ! completed . configuration . outputs . has ( root ) ) {
1982+ throw new Error ( "Machine reached a terminal configuration without a completed root output" )
1983+ }
1984+ return {
1985+ next : completed . configuration ,
1986+ commands : [ ] ,
1987+ emittedEvents : [ ] ,
1988+ microsteps : [ ] ,
1989+ done : true ,
1990+ output : completed . configuration . outputs . get ( root )
1991+ }
1992+ }
1993+
1994+ let current = configuration
1995+ let event : any = decoded
1996+ const pending : Array < any > = [ ]
1997+ let pendingIndex = 0
1998+ const commands : Array < RuntimeCommand > = [ ]
1999+ const emittedEvents : Array < unknown > = [ ]
2000+ const microsteps : Array < MicrostepPlan < ActiveConfiguration , any , any , any > > = [ ]
2001+ let iterations = 0
2002+
2003+ while ( true ) {
2004+ iterations += 1
2005+ if ( iterations > MaxMacrostepIterations ) {
2006+ throw new InfiniteTransitionError ( {
2007+ machineId : machine . id ,
2008+ state : String ( getLeafPath ( machine , current ) ) ,
2009+ maxIterations : MaxMacrostepIterations
2010+ } )
2011+ }
2012+
2013+ const source = getRootPath ( machine , current )
2014+ const transition = normalizeTransition ( machine . handlers [ source ] ?. on ?. [ event . _tag ] )
2015+ if ( transition !== undefined ) {
2016+ const snapshot = snapshotFromConfiguration ( machine , current )
2017+ const collected = collectTransition ( machine , transition . transition , {
2018+ state : getActiveValue ( current , source ) ,
2019+ parent : undefined ,
2020+ parents : { } ,
2021+ event,
2022+ snapshot,
2023+ target : getTargetBuilder ( machine , source )
2024+ } )
2025+ validateDeclaredTransitionTarget (
2026+ source ,
2027+ { type : "event" , event : event . _tag } ,
2028+ transition . targets ,
2029+ collected . state
2030+ )
2031+
2032+ let next = current
2033+ let targetPath : string | undefined
2034+ if ( collected . state !== undefined ) {
2035+ if ( ! isTarget ( collected . state ) && ! isSnapshot ( collected . state ) ) {
2036+ throw new Error ( "Machine expected transition target to be a snapshot or target builder result" )
2037+ }
2038+ targetPath = getTargetNodePath ( collected . state )
2039+ if ( ! descriptor . paths . has ( targetPath ) ) {
2040+ throw new Error ( `Machine expected flat transition target "${ targetPath } " to be a root state` )
2041+ }
2042+ next = normalizeTargetConfigurationSync ( machine , current , collected . state as any )
2043+ }
2044+ const changed = transition . reenter || source !== targetPath && targetPath !== undefined
2045+ const exitPaths = changed ? [ source ] : [ ]
2046+ const entryPaths = changed ? [ getRootPath ( machine , next ) ] : [ ]
2047+ const step = {
2048+ next,
2049+ event,
2050+ transitions : [ {
2051+ source,
2052+ trigger : { type : "event" as const , event : event . _tag } ,
2053+ reenter : transition . reenter ,
2054+ target : targetPath ,
2055+ resolvedTarget : targetPath
2056+ } ] ,
2057+ commands : collected . commands ,
2058+ raisedEvents : collected . raisedEvents ,
2059+ emittedEvents : collected . emittedEvents ,
2060+ exitPaths,
2061+ entryPaths,
2062+ changed
2063+ }
2064+ current = next
2065+ commands . push ( ...collected . commands )
2066+ pending . push ( ...collected . raisedEvents )
2067+ emittedEvents . push ( ...collected . emittedEvents )
2068+ microsteps . push ( step )
2069+
2070+ if ( isActiveFinalConfiguration ( machine , current ) ) {
2071+ const completed = completeConfigurationSync ( machine , current , event )
2072+ const root = getRootPath ( machine , completed . configuration )
2073+ if ( ! completed . configuration . outputs . has ( root ) ) {
2074+ throw new Error ( "Machine reached a terminal configuration without a completed root output" )
2075+ }
2076+ return {
2077+ next : completed . configuration ,
2078+ commands,
2079+ emittedEvents,
2080+ microsteps,
2081+ done : true ,
2082+ output : completed . configuration . outputs . get ( root )
2083+ }
2084+ }
2085+ }
2086+
2087+ if ( pendingIndex >= pending . length ) {
2088+ return {
2089+ next : current ,
2090+ commands,
2091+ emittedEvents,
2092+ microsteps,
2093+ done : false ,
2094+ output : undefined
2095+ }
2096+ }
2097+ event = pending [ pendingIndex ++ ]
2098+ }
2099+ }
2100+
2101+ export interface CompiledExecutionPlan {
2102+ readonly plan : (
2103+ configuration : ActiveConfiguration ,
2104+ event : unknown
2105+ ) => MacrostepPlan < ActiveConfiguration , any , any , any , any >
2106+ }
2107+
2108+ const executionPlanCache = new WeakMap < Machine . Any , CompiledExecutionPlan > ( )
2109+
2110+ export const compileExecutionPlan = ( machine : Machine . Any ) : CompiledExecutionPlan => {
2111+ const cached = executionPlanCache . get ( machine )
2112+ if ( cached !== undefined ) {
2113+ return cached
2114+ }
2115+ const flat = compileFlatExecutionDescriptor ( machine )
2116+ const compiled : CompiledExecutionPlan = flat === undefined
2117+ ? { plan : ( configuration , event ) => planConfiguration ( machine as any , configuration , event as any ) }
2118+ : { plan : ( configuration , event ) => planFlatConfiguration ( machine , flat , configuration , event ) }
2119+ executionPlanCache . set ( machine , compiled )
2120+ return compiled
2121+ }
2122+
19332123const snapshotMacrostep = <
19342124 const States extends Machine . StateSchemas ,
19352125 Event ,
0 commit comments