@typeonce/effect-machine 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/Machine.d.ts +24 -6
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts +5 -0
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +6 -3
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +1 -1
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +26 -0
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +23 -0
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +14 -0
- package/package.json +5 -5
- package/src/Machine.ts +6909 -0
- package/src/index.ts +1 -0
- package/src/internal/machine/activities.ts +108 -0
- package/src/internal/machine/atom.ts +684 -0
- package/src/internal/machine/cluster.ts +394 -0
- package/src/internal/machine/command.ts +58 -0
- package/src/internal/machine/commandRuntime.ts +43 -0
- package/src/internal/machine/configuration.ts +1331 -0
- package/src/internal/machine/errors.ts +87 -0
- package/src/internal/machine/executionPlan.ts +996 -0
- package/src/internal/machine/invocation.ts +119 -0
- package/src/internal/machine/machine.ts +1750 -0
- package/src/internal/machine/planner.ts +1933 -0
- package/src/internal/machine/process.ts +906 -0
- package/src/internal/machine/protocol.ts +322 -0
- package/src/internal/machine/readiness.ts +10 -0
- package/src/internal/machine/runtime.ts +2512 -0
- package/src/internal/machine/serialization.ts +498 -0
- package/src/internal/machine/stateDefinition.ts +270 -0
- package/src/internal/machine/symbols.ts +2 -0
- package/src/internal/machine/topology.ts +479 -0
- package/src/internal/testing/machine/arbitrary.ts +102 -0
- package/src/internal/testing/machine/exploration.ts +331 -0
- package/src/internal/testing/machine/finiteModel.ts +1498 -0
- package/src/internal/testing/machine/invariant.ts +372 -0
- package/src/internal/testing/machine/probe.ts +79 -0
- package/src/internal/testing/machine/referenceModel.ts +1505 -0
- package/src/internal/testing/machine/runtime.ts +1710 -0
- package/src/internal/testing/machine/runtimeInvariant.ts +486 -0
- package/src/internal/testing/machine/trace.ts +150 -0
- package/src/internal/testing/machine/verification.ts +1890 -0
- package/src/testing/MachineTest.ts +2067 -0
- package/src/testing/index.ts +7 -0
- package/src/unstable/cluster/ClusterMachine.ts +390 -0
- package/src/unstable/cluster/index.ts +1 -0
- package/src/unstable/reactivity/AtomMachine.ts +696 -0
- package/src/unstable/reactivity/index.ts +1 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal machine process integration.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.4.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Cause from "effect/Cause"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as Option from "effect/Option"
|
|
10
|
+
import type * as Schema from "effect/Schema"
|
|
11
|
+
import type { ActionError, ExecutionServices, Machine, Runtime } from "../../Machine.js"
|
|
12
|
+
import * as CommandRuntime from "./commandRuntime.js"
|
|
13
|
+
import * as Configuration from "./configuration.js"
|
|
14
|
+
import { InfiniteTransitionError, MachineSchemaDecodeError, StartupError } from "./errors.js"
|
|
15
|
+
import type { StoppedError } from "./errors.js"
|
|
16
|
+
import * as ExecutionPlan from "./executionPlan.js"
|
|
17
|
+
import * as Invocation from "./invocation.js"
|
|
18
|
+
import * as internalPlanner from "./planner.js"
|
|
19
|
+
import * as internalRuntime from "./runtime.js"
|
|
20
|
+
import * as Serialization from "./serialization.js"
|
|
21
|
+
|
|
22
|
+
type IsAny<A> = 0 extends (1 & A) ? true : false
|
|
23
|
+
|
|
24
|
+
type ExcludeCompatibleRuntime<Requirements, Events, Emits> = Requirements extends Runtime.Requirement<
|
|
25
|
+
infer RequiredEvents,
|
|
26
|
+
infer RequiredEmits
|
|
27
|
+
> ? IsAny<Requirements> extends true ? Requirements
|
|
28
|
+
: [RequiredEvents] extends [Events] ? [RequiredEmits] extends [Emits] ? never : Requirements
|
|
29
|
+
: Requirements
|
|
30
|
+
: Requirements
|
|
31
|
+
|
|
32
|
+
type ProcessEntry<States extends Machine.StateSchemas, Input extends Schema.Top> =
|
|
33
|
+
| {
|
|
34
|
+
readonly _tag: "Initial"
|
|
35
|
+
readonly args: [...Machine.InputArgs<Input>]
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
readonly _tag: "Resume"
|
|
39
|
+
readonly snapshot: Machine.Snapshot<States>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const runSequentialDiscard = <E, R>(
|
|
43
|
+
effects: ReadonlyArray<Effect.Effect<void, E, R>>
|
|
44
|
+
): Effect.Effect<void, E, R> =>
|
|
45
|
+
effects.length === 0
|
|
46
|
+
? Effect.void
|
|
47
|
+
: effects.length === 1
|
|
48
|
+
? effects[0]!
|
|
49
|
+
: Effect.all(effects, { discard: true })
|
|
50
|
+
|
|
51
|
+
const acknowledgedPlan = (
|
|
52
|
+
planned: {
|
|
53
|
+
readonly next: unknown
|
|
54
|
+
readonly commands: ReadonlyArray<unknown>
|
|
55
|
+
readonly emittedEvents: ReadonlyArray<unknown>
|
|
56
|
+
readonly microsteps: ReadonlyArray<{
|
|
57
|
+
readonly next: unknown
|
|
58
|
+
readonly event: unknown
|
|
59
|
+
readonly transitions?: ReadonlyArray<unknown>
|
|
60
|
+
readonly commands: ReadonlyArray<unknown>
|
|
61
|
+
readonly raisedEvents: ReadonlyArray<unknown>
|
|
62
|
+
readonly emittedEvents: ReadonlyArray<unknown>
|
|
63
|
+
readonly exitPaths: ReadonlyArray<string>
|
|
64
|
+
readonly entryPaths: ReadonlyArray<string>
|
|
65
|
+
readonly changed: boolean
|
|
66
|
+
}>
|
|
67
|
+
readonly done: boolean
|
|
68
|
+
readonly output: unknown
|
|
69
|
+
},
|
|
70
|
+
snapshot: (state: unknown) => unknown
|
|
71
|
+
): unknown => ({
|
|
72
|
+
next: snapshot(planned.next),
|
|
73
|
+
commands: planned.commands,
|
|
74
|
+
emittedEvents: planned.emittedEvents,
|
|
75
|
+
microsteps: planned.microsteps.map((microstep) => {
|
|
76
|
+
const { transitions: _, ...evidence } = microstep
|
|
77
|
+
return { ...evidence, next: snapshot(microstep.next) }
|
|
78
|
+
}),
|
|
79
|
+
done: planned.done,
|
|
80
|
+
output: planned.output
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const invokeCapabilityCache = new WeakMap<Machine.Any, boolean>()
|
|
84
|
+
|
|
85
|
+
const hasInvokeCapability = (machine: Machine.Any): boolean => {
|
|
86
|
+
const cached = invokeCapabilityCache.get(machine)
|
|
87
|
+
if (cached !== undefined) {
|
|
88
|
+
return cached
|
|
89
|
+
}
|
|
90
|
+
const hasInvokes = Object.values(
|
|
91
|
+
machine.handlers as Record<string, Machine.AnyStateConfig>
|
|
92
|
+
).some((config) => config.invoke !== undefined)
|
|
93
|
+
invokeCapabilityCache.set(machine, hasInvokes)
|
|
94
|
+
return hasInvokes
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const makeChildlessCompiledDrain = (
|
|
98
|
+
machine: Machine.Any,
|
|
99
|
+
checkInitialFinal: boolean
|
|
100
|
+
): (
|
|
101
|
+
context: internalRuntime.CompiledProcessContext<any, any>
|
|
102
|
+
) => Effect.Effect<Option.Option<any>, any, any> => {
|
|
103
|
+
const executionPlan = ExecutionPlan.compileExecutionPlan(machine)
|
|
104
|
+
return (context) => {
|
|
105
|
+
let current = context.state()
|
|
106
|
+
if (checkInitialFinal && internalPlanner.isFinalState(machine, current)) {
|
|
107
|
+
return internalPlanner.getFinalOutputEffect(
|
|
108
|
+
machine,
|
|
109
|
+
current,
|
|
110
|
+
internalPlanner.InitialEvent
|
|
111
|
+
).pipe(Effect.map(Option.some))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let configuration = context.executionState
|
|
115
|
+
let liveRuntime: Runtime<unknown, unknown> | undefined
|
|
116
|
+
let loop: Effect.Effect<Option.Option<any>, any, any>
|
|
117
|
+
loop = Effect.suspend(() => {
|
|
118
|
+
const pending = context.pollMessage()
|
|
119
|
+
if (Option.isNone(pending)) {
|
|
120
|
+
context.executionState = undefined
|
|
121
|
+
return Effect.succeed(Option.none())
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const message = pending.value
|
|
125
|
+
const acknowledged = internalRuntime.isAcknowledgedMessage(message)
|
|
126
|
+
const event = acknowledged ? message.event : message
|
|
127
|
+
const before = current
|
|
128
|
+
|
|
129
|
+
let planned
|
|
130
|
+
try {
|
|
131
|
+
planned = executionPlan.plan(
|
|
132
|
+
configuration ?? executionPlan.fromConfiguration(Configuration.normalizeConfigurationSync(machine, current)),
|
|
133
|
+
event,
|
|
134
|
+
acknowledged
|
|
135
|
+
)
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError
|
|
138
|
+
? Effect.fail(error)
|
|
139
|
+
: Effect.die(error)
|
|
140
|
+
}
|
|
141
|
+
configuration = planned.next
|
|
142
|
+
context.executionState = configuration
|
|
143
|
+
if (planned.microsteps.length === 0) {
|
|
144
|
+
if (acknowledged) {
|
|
145
|
+
context.completeMessage({ before, plan: acknowledgedPlan(planned, executionPlan.snapshot), after: current })
|
|
146
|
+
}
|
|
147
|
+
return loop
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const next = executionPlan.snapshot(planned.next)
|
|
151
|
+
const beforeCommit = planned.commands.length === 0
|
|
152
|
+
? undefined
|
|
153
|
+
: CommandRuntime.runCommands(planned.commands, context.scope)
|
|
154
|
+
const afterCommit = planned.emittedEvents.length === 0
|
|
155
|
+
? undefined
|
|
156
|
+
: CommandRuntime.runEmittedEvents(
|
|
157
|
+
planned.emittedEvents,
|
|
158
|
+
liveRuntime ??= CommandRuntime.makeLiveRuntime(machine, context.scope)
|
|
159
|
+
)
|
|
160
|
+
const commit = (): Effect.Effect<void> | undefined => {
|
|
161
|
+
const notification = context.commit(next)
|
|
162
|
+
current = next
|
|
163
|
+
return notification
|
|
164
|
+
}
|
|
165
|
+
const continueAfterCommit = (): Effect.Effect<Option.Option<any>, any, any> => {
|
|
166
|
+
const continued = Effect.suspend(() => {
|
|
167
|
+
if (acknowledged) {
|
|
168
|
+
context.completeMessage({ before, plan: acknowledgedPlan(planned, executionPlan.snapshot), after: next })
|
|
169
|
+
}
|
|
170
|
+
return planned.done ? Effect.succeed(Option.some(planned.output)) : loop
|
|
171
|
+
})
|
|
172
|
+
return afterCommit === undefined ? continued : afterCommit.pipe(Effect.andThen(continued))
|
|
173
|
+
}
|
|
174
|
+
const commitAndContinue = (): Effect.Effect<Option.Option<any>, any, any> => {
|
|
175
|
+
const notification = commit()
|
|
176
|
+
const continued = continueAfterCommit()
|
|
177
|
+
const effect = notification === undefined ? continued : notification.pipe(Effect.andThen(continued))
|
|
178
|
+
return notification === undefined && afterCommit === undefined
|
|
179
|
+
? effect
|
|
180
|
+
: context.runAfterChanges(effect)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (beforeCommit === undefined) {
|
|
184
|
+
return commitAndContinue()
|
|
185
|
+
}
|
|
186
|
+
return context.runAfterChanges(
|
|
187
|
+
beforeCommit.pipe(Effect.andThen(Effect.suspend(commitAndContinue)))
|
|
188
|
+
)
|
|
189
|
+
})
|
|
190
|
+
return internalRuntime.provideMachineRuntime(loop, context.scope)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class InvokeExecutionState {
|
|
195
|
+
initialized = false
|
|
196
|
+
initial:
|
|
197
|
+
| {
|
|
198
|
+
readonly configuration: unknown
|
|
199
|
+
readonly activeConfiguration: Configuration.ActiveConfiguration
|
|
200
|
+
readonly entryPaths: ReadonlyArray<string>
|
|
201
|
+
}
|
|
202
|
+
| undefined
|
|
203
|
+
|
|
204
|
+
constructor(initial?: {
|
|
205
|
+
readonly configuration: unknown
|
|
206
|
+
readonly activeConfiguration: Configuration.ActiveConfiguration
|
|
207
|
+
readonly entryPaths: ReadonlyArray<string>
|
|
208
|
+
}) {
|
|
209
|
+
this.initial = initial
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const makeInvokingCompiledDrain = (
|
|
214
|
+
machine: Machine.Any,
|
|
215
|
+
checkInitialFinal: boolean
|
|
216
|
+
): (
|
|
217
|
+
context: internalRuntime.CompiledProcessContext<any, any>
|
|
218
|
+
) => Effect.Effect<Option.Option<any>, any, any> => {
|
|
219
|
+
const executionPlan = ExecutionPlan.compileExecutionPlan(machine)
|
|
220
|
+
return (context) => {
|
|
221
|
+
let current = context.state()
|
|
222
|
+
if (checkInitialFinal && internalPlanner.isFinalState(machine, current)) {
|
|
223
|
+
return internalPlanner.getFinalOutputEffect(
|
|
224
|
+
machine,
|
|
225
|
+
current,
|
|
226
|
+
internalPlanner.InitialEvent
|
|
227
|
+
).pipe(Effect.map(Option.some))
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const scope = context.scope
|
|
231
|
+
const stored = context.executionState
|
|
232
|
+
const execution = stored instanceof InvokeExecutionState
|
|
233
|
+
? stored
|
|
234
|
+
: new InvokeExecutionState()
|
|
235
|
+
context.executionState = execution
|
|
236
|
+
let liveRuntime: Runtime<any, any> | undefined
|
|
237
|
+
let configuration: unknown
|
|
238
|
+
|
|
239
|
+
let loop: Effect.Effect<Option.Option<any>, any, any>
|
|
240
|
+
loop = Effect.suspend(() => {
|
|
241
|
+
const pending = context.pollMessage()
|
|
242
|
+
if (Option.isNone(pending)) {
|
|
243
|
+
configuration = undefined
|
|
244
|
+
return Effect.succeed(Option.none())
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const message = pending.value
|
|
248
|
+
const acknowledged = internalRuntime.isAcknowledgedMessage(message)
|
|
249
|
+
const event = acknowledged ? message.event : message
|
|
250
|
+
const before = current
|
|
251
|
+
|
|
252
|
+
let planned
|
|
253
|
+
try {
|
|
254
|
+
planned = executionPlan.plan(
|
|
255
|
+
configuration ??
|
|
256
|
+
executionPlan.fromConfiguration(Configuration.normalizeConfigurationSync(machine, current)),
|
|
257
|
+
event,
|
|
258
|
+
acknowledged
|
|
259
|
+
)
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError
|
|
262
|
+
? Effect.fail(error)
|
|
263
|
+
: Effect.die(error)
|
|
264
|
+
}
|
|
265
|
+
configuration = planned.next
|
|
266
|
+
if (planned.microsteps.length === 0) {
|
|
267
|
+
if (acknowledged) {
|
|
268
|
+
context.completeMessage({ before, plan: acknowledgedPlan(planned, executionPlan.snapshot), after: current })
|
|
269
|
+
}
|
|
270
|
+
return loop
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const changed = planned.microsteps.some((step) => step.changed)
|
|
274
|
+
const exitPaths = changed ? planned.microsteps.flatMap((step) => step.exitPaths) : []
|
|
275
|
+
const entryEvents = new Map<string, Machine.LifecycleEvent<any>>()
|
|
276
|
+
if (changed) {
|
|
277
|
+
for (const step of planned.microsteps) {
|
|
278
|
+
if (step.changed) {
|
|
279
|
+
for (const path of step.entryPaths) {
|
|
280
|
+
entryEvents.set(path, step.event)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const activeConfiguration = executionPlan.toConfiguration(planned.next)
|
|
287
|
+
const next = executionPlan.snapshot(planned.next)
|
|
288
|
+
const beforeCommit: Array<Effect.Effect<void, any, any>> = []
|
|
289
|
+
if (planned.commands.length > 0) {
|
|
290
|
+
beforeCommit.push(CommandRuntime.runCommands(planned.commands, scope))
|
|
291
|
+
}
|
|
292
|
+
if (changed) {
|
|
293
|
+
const stopping = context.ownedChildren.stopPaths(exitPaths)
|
|
294
|
+
if (stopping !== undefined) beforeCommit.push(stopping)
|
|
295
|
+
}
|
|
296
|
+
const afterCommit: Array<Effect.Effect<void, any, any>> = []
|
|
297
|
+
if (planned.emittedEvents.length > 0) {
|
|
298
|
+
afterCommit.push(
|
|
299
|
+
CommandRuntime.runEmittedEvents(
|
|
300
|
+
planned.emittedEvents,
|
|
301
|
+
liveRuntime ??= CommandRuntime.makeLiveRuntime(machine, scope)
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
if (planned.done) {
|
|
306
|
+
afterCommit.push(context.ownedChildren.stopAll())
|
|
307
|
+
} else if (changed) {
|
|
308
|
+
for (const [path, entryEvent] of entryEvents) {
|
|
309
|
+
const starting = Invocation.startAll(
|
|
310
|
+
machine,
|
|
311
|
+
scope,
|
|
312
|
+
context.ownedChildren,
|
|
313
|
+
activeConfiguration,
|
|
314
|
+
[path],
|
|
315
|
+
entryEvent
|
|
316
|
+
)
|
|
317
|
+
if (starting !== undefined) afterCommit.push(starting)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const commit = (): Effect.Effect<void> | undefined => {
|
|
322
|
+
const notification = context.commit(next)
|
|
323
|
+
current = next
|
|
324
|
+
return notification
|
|
325
|
+
}
|
|
326
|
+
const continueAfterCommit = (): Effect.Effect<Option.Option<any>, any, any> => {
|
|
327
|
+
const continued = Effect.suspend(() => {
|
|
328
|
+
if (acknowledged) {
|
|
329
|
+
context.completeMessage({ before, plan: acknowledgedPlan(planned, executionPlan.snapshot), after: next })
|
|
330
|
+
}
|
|
331
|
+
return planned.done ? Effect.succeed(Option.some(planned.output)) : loop
|
|
332
|
+
})
|
|
333
|
+
return afterCommit.length === 0
|
|
334
|
+
? continued
|
|
335
|
+
: runSequentialDiscard(afterCommit).pipe(Effect.andThen(continued))
|
|
336
|
+
}
|
|
337
|
+
const commitAndContinue = (): Effect.Effect<Option.Option<any>, any, any> => {
|
|
338
|
+
const notification = commit()
|
|
339
|
+
const continued = continueAfterCommit()
|
|
340
|
+
const effect = notification === undefined ? continued : notification.pipe(Effect.andThen(continued))
|
|
341
|
+
return notification === undefined && afterCommit.length === 0
|
|
342
|
+
? effect
|
|
343
|
+
: context.runAfterChanges(effect)
|
|
344
|
+
}
|
|
345
|
+
if (beforeCommit.length === 0) {
|
|
346
|
+
return commitAndContinue()
|
|
347
|
+
}
|
|
348
|
+
return context.runAfterChanges(
|
|
349
|
+
runSequentialDiscard(beforeCommit).pipe(Effect.andThen(Effect.suspend(commitAndContinue)))
|
|
350
|
+
)
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const initialize = (): Effect.Effect<Option.Option<any>, any, any> => {
|
|
354
|
+
if (execution.initialized) {
|
|
355
|
+
return loop
|
|
356
|
+
}
|
|
357
|
+
const seeded = execution.initial
|
|
358
|
+
const initialConfiguration = seeded?.activeConfiguration ??
|
|
359
|
+
Configuration.normalizeConfigurationSync(machine, current)
|
|
360
|
+
configuration = seeded?.configuration ?? executionPlan.fromConfiguration(initialConfiguration)
|
|
361
|
+
const starting = Invocation.startAll(
|
|
362
|
+
machine,
|
|
363
|
+
scope,
|
|
364
|
+
context.ownedChildren,
|
|
365
|
+
initialConfiguration,
|
|
366
|
+
seeded?.entryPaths ?? Configuration.getInitialEntryPaths(machine, initialConfiguration),
|
|
367
|
+
internalPlanner.InitialEvent
|
|
368
|
+
)
|
|
369
|
+
execution.initial = undefined
|
|
370
|
+
execution.initialized = true
|
|
371
|
+
return starting === undefined ? loop : starting.pipe(Effect.andThen(loop))
|
|
372
|
+
}
|
|
373
|
+
return internalRuntime.provideMachineRuntime(Effect.suspend(initialize), scope)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const makeProcessLogic: <
|
|
378
|
+
const States extends Machine.StateSchemas,
|
|
379
|
+
const Events extends ReadonlyArray<Machine.TaggedSchema>,
|
|
380
|
+
const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
381
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
382
|
+
UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>,
|
|
383
|
+
E = never,
|
|
384
|
+
R = never,
|
|
385
|
+
InitialE = never,
|
|
386
|
+
InitialR = never,
|
|
387
|
+
FinalStates extends Machine.StateIdentifier<States> = never,
|
|
388
|
+
Output = never
|
|
389
|
+
>(
|
|
390
|
+
machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits>,
|
|
391
|
+
entry: ProcessEntry<States, Input>
|
|
392
|
+
) => internalRuntime.ProcessLogic<
|
|
393
|
+
Machine.Snapshot<States>,
|
|
394
|
+
Machine.EventOf<Events>,
|
|
395
|
+
E | ActionError<R> | InfiniteTransitionError | MachineSchemaDecodeError | StoppedError,
|
|
396
|
+
ExcludeCompatibleRuntime<
|
|
397
|
+
Exclude<ExecutionServices<InitialR | R>, internalRuntime.MachineRuntime>,
|
|
398
|
+
Machine.EventOf<Events>,
|
|
399
|
+
Machine.EmitOf<Emits>
|
|
400
|
+
>,
|
|
401
|
+
Output,
|
|
402
|
+
| InitialE
|
|
403
|
+
| E
|
|
404
|
+
| ActionError<InitialR | R>
|
|
405
|
+
| InfiniteTransitionError
|
|
406
|
+
| MachineSchemaDecodeError
|
|
407
|
+
| StartupError
|
|
408
|
+
| StoppedError
|
|
409
|
+
> = <
|
|
410
|
+
const States extends Machine.StateSchemas,
|
|
411
|
+
const Events extends ReadonlyArray<Machine.TaggedSchema>,
|
|
412
|
+
const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
413
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
414
|
+
UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>,
|
|
415
|
+
E = never,
|
|
416
|
+
R = never,
|
|
417
|
+
InitialE = never,
|
|
418
|
+
InitialR = never,
|
|
419
|
+
FinalStates extends Machine.StateIdentifier<States> = never,
|
|
420
|
+
Output = never
|
|
421
|
+
>(
|
|
422
|
+
machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits>,
|
|
423
|
+
entry: ProcessEntry<States, Input>
|
|
424
|
+
) => {
|
|
425
|
+
const hasInvokes = hasInvokeCapability(machine)
|
|
426
|
+
const executionPlan = ExecutionPlan.compileExecutionPlan(machine)
|
|
427
|
+
const initialArgs = entry._tag === "Initial" ? entry.args : []
|
|
428
|
+
const compiledInitial = entry._tag === "Initial" ? executionPlan.initial : undefined
|
|
429
|
+
const makeCompiledInitial = compiledInitial === undefined ? undefined : () => {
|
|
430
|
+
try {
|
|
431
|
+
const planned = compiledInitial(initialArgs)
|
|
432
|
+
const result = {
|
|
433
|
+
state: planned.state as Machine.Snapshot<States>,
|
|
434
|
+
done: planned.done,
|
|
435
|
+
output: planned.output as Output | undefined
|
|
436
|
+
}
|
|
437
|
+
return hasInvokes
|
|
438
|
+
? {
|
|
439
|
+
...result,
|
|
440
|
+
executionState: new InvokeExecutionState({
|
|
441
|
+
configuration: planned.configuration,
|
|
442
|
+
activeConfiguration: planned.activeConfiguration,
|
|
443
|
+
entryPaths: planned.initialEntryPaths
|
|
444
|
+
})
|
|
445
|
+
}
|
|
446
|
+
: result
|
|
447
|
+
} catch (error) {
|
|
448
|
+
throw error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError
|
|
449
|
+
? error
|
|
450
|
+
: new StartupError({ cause: Cause.die(error) })
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
const makeInitial = (
|
|
454
|
+
scope: internalRuntime.ProcessScope<Machine.EventOf<Events>>
|
|
455
|
+
) =>
|
|
456
|
+
compiledInitial === undefined
|
|
457
|
+
? internalRuntime.provideMachineRuntime(
|
|
458
|
+
internalPlanner.planInitial(machine, ...initialArgs).pipe(
|
|
459
|
+
Effect.flatMap((planned) => {
|
|
460
|
+
const commands = planned.commands.length === 0
|
|
461
|
+
? undefined
|
|
462
|
+
: CommandRuntime.runCommands(planned.commands, scope)
|
|
463
|
+
const emitted = planned.emittedEvents.length === 0
|
|
464
|
+
? undefined
|
|
465
|
+
: CommandRuntime.runEmittedEvents(
|
|
466
|
+
planned.emittedEvents,
|
|
467
|
+
CommandRuntime.makeLiveRuntime<Machine.EventOf<Events>, Machine.EmitOf<Emits>>(machine, scope)
|
|
468
|
+
)
|
|
469
|
+
const result = Effect.succeed({
|
|
470
|
+
state: planned.state,
|
|
471
|
+
done: planned.done,
|
|
472
|
+
output: planned.output
|
|
473
|
+
})
|
|
474
|
+
return commands === undefined
|
|
475
|
+
? emitted === undefined ? result : emitted.pipe(Effect.andThen(result))
|
|
476
|
+
: emitted === undefined
|
|
477
|
+
? commands.pipe(Effect.andThen(result))
|
|
478
|
+
: commands.pipe(Effect.andThen(emitted), Effect.andThen(result))
|
|
479
|
+
})
|
|
480
|
+
),
|
|
481
|
+
scope
|
|
482
|
+
)
|
|
483
|
+
: Effect.try({ try: makeCompiledInitial!, catch: (error) => error as any })
|
|
484
|
+
return ({
|
|
485
|
+
execution: {
|
|
486
|
+
_tag: "Compiled",
|
|
487
|
+
childless: !hasInvokes,
|
|
488
|
+
initial: entry._tag === "Initial" ? makeInitial : undefined,
|
|
489
|
+
initialSync: makeCompiledInitial,
|
|
490
|
+
drain: {
|
|
491
|
+
_tag: "Owned",
|
|
492
|
+
run: hasInvokes
|
|
493
|
+
? makeInvokingCompiledDrain(machine, entry._tag === "Resume")
|
|
494
|
+
: makeChildlessCompiledDrain(machine, entry._tag === "Resume")
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
initial: (scope) =>
|
|
498
|
+
entry._tag === "Resume"
|
|
499
|
+
? internalRuntime.provideMachineRuntime(Serialization.normalizeSnapshotEffect(machine, entry.snapshot), scope)
|
|
500
|
+
: makeInitial(scope).pipe(Effect.map((initialized) => initialized.state)),
|
|
501
|
+
run: (context) =>
|
|
502
|
+
internalRuntime.provideMachineRuntime(
|
|
503
|
+
Effect.gen(function*() {
|
|
504
|
+
const { completeMessage, pollMessage, receiveMessage, state, setState } = context
|
|
505
|
+
if (completeMessage === undefined || pollMessage === undefined || receiveMessage === undefined) {
|
|
506
|
+
return yield* Effect.die(new Error("Machine statechart started without acknowledged mailbox access"))
|
|
507
|
+
}
|
|
508
|
+
let terminal: { readonly output: Output } | undefined
|
|
509
|
+
|
|
510
|
+
let current = yield* state
|
|
511
|
+
if (internalPlanner.isFinalState(machine, current)) {
|
|
512
|
+
return yield* internalPlanner.getFinalOutputEffect<States, Events, Output>(
|
|
513
|
+
machine,
|
|
514
|
+
current,
|
|
515
|
+
internalPlanner.InitialEvent
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (!hasInvokes) {
|
|
520
|
+
// A queued batch is produced entirely by this worker, so its
|
|
521
|
+
// configuration is already validated. Drop both caches before
|
|
522
|
+
// blocking again so idle machines retain only the public snapshot.
|
|
523
|
+
// Keeping the loop in this generator avoids a suspended generator
|
|
524
|
+
// per iteration; every iteration still crosses Effect boundaries,
|
|
525
|
+
// so the Effect scheduler remains responsible for cooperative yield.
|
|
526
|
+
let configuration: Configuration.ActiveConfiguration | undefined
|
|
527
|
+
let pendingMessage: Option.Option<internalRuntime.ProcessMessage<Machine.EventOf<Events>>> = Option.none()
|
|
528
|
+
let liveRuntime: Runtime<Machine.EventOf<Events>, Machine.EmitOf<Emits>> | undefined
|
|
529
|
+
while (terminal === undefined) {
|
|
530
|
+
const message = Option.isSome(pendingMessage) ? pendingMessage.value : yield* receiveMessage
|
|
531
|
+
pendingMessage = Option.none()
|
|
532
|
+
const acknowledged = internalRuntime.isAcknowledgedMessage(message)
|
|
533
|
+
const event = acknowledged ? message.event : message
|
|
534
|
+
const before = current
|
|
535
|
+
let planned
|
|
536
|
+
try {
|
|
537
|
+
planned = internalPlanner.planConfiguration(
|
|
538
|
+
machine,
|
|
539
|
+
configuration ?? Configuration.normalizeConfigurationSync(machine, current),
|
|
540
|
+
event
|
|
541
|
+
)
|
|
542
|
+
} catch (error) {
|
|
543
|
+
if (error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError) {
|
|
544
|
+
return yield* error
|
|
545
|
+
}
|
|
546
|
+
throw error
|
|
547
|
+
}
|
|
548
|
+
configuration = planned.next
|
|
549
|
+
|
|
550
|
+
if (planned.microsteps.length > 0) {
|
|
551
|
+
const next = Configuration.snapshotFromConfiguration<States>(machine, planned.next)
|
|
552
|
+
yield* CommandRuntime.runCommands(planned.commands, context)
|
|
553
|
+
yield* setState(next)
|
|
554
|
+
current = next
|
|
555
|
+
if (planned.emittedEvents.length > 0) {
|
|
556
|
+
yield* CommandRuntime.runEmittedEvents(
|
|
557
|
+
planned.emittedEvents as ReadonlyArray<Machine.EmitOf<Emits>>,
|
|
558
|
+
liveRuntime ??= CommandRuntime.makeLiveRuntime(machine, context)
|
|
559
|
+
)
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (planned.done) {
|
|
563
|
+
terminal = { output: planned.output }
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (acknowledged) {
|
|
568
|
+
completeMessage({
|
|
569
|
+
before,
|
|
570
|
+
plan: acknowledgedPlan(
|
|
571
|
+
planned,
|
|
572
|
+
(state) =>
|
|
573
|
+
Configuration.snapshotFromConfiguration(machine, state as Configuration.ActiveConfiguration)
|
|
574
|
+
),
|
|
575
|
+
after: current
|
|
576
|
+
})
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
if (terminal === undefined) {
|
|
580
|
+
pendingMessage = yield* pollMessage
|
|
581
|
+
if (Option.isNone(pendingMessage)) {
|
|
582
|
+
configuration = undefined
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (terminal === undefined) {
|
|
588
|
+
return yield* Effect.die(
|
|
589
|
+
new Error("Machine process stopped receiving events before reaching a terminal configuration")
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
return terminal.output
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// The execution descriptor requests this owner-local capability only
|
|
596
|
+
// when an invoking statechart is deliberately run by the generic
|
|
597
|
+
// reference strategy.
|
|
598
|
+
const ownedChildren = context.ownedChildren
|
|
599
|
+
if (ownedChildren === undefined) {
|
|
600
|
+
return yield* Effect.die(new Error("Invoking statechart started without an owned child runtime"))
|
|
601
|
+
}
|
|
602
|
+
const startInvokes: (
|
|
603
|
+
configuration: Configuration.ActiveConfiguration,
|
|
604
|
+
paths: ReadonlyArray<string>,
|
|
605
|
+
event: Machine.LifecycleEvent<Events>
|
|
606
|
+
) => Effect.Effect<void, E | MachineSchemaDecodeError, R> = (configuration, paths, event) =>
|
|
607
|
+
(Invocation.startAll(
|
|
608
|
+
machine,
|
|
609
|
+
context,
|
|
610
|
+
ownedChildren,
|
|
611
|
+
configuration,
|
|
612
|
+
paths,
|
|
613
|
+
event
|
|
614
|
+
) ?? Effect.void) as Effect.Effect<void, E | MachineSchemaDecodeError, R>
|
|
615
|
+
const stopInvokes = (paths: ReadonlyArray<string>): Effect.Effect<void> =>
|
|
616
|
+
ownedChildren.stopPaths(paths) ?? Effect.void
|
|
617
|
+
|
|
618
|
+
return yield* Effect.gen(function*() {
|
|
619
|
+
let configuration: Configuration.ActiveConfiguration | undefined = yield* Configuration
|
|
620
|
+
.normalizeConfigurationEffect(
|
|
621
|
+
machine,
|
|
622
|
+
current
|
|
623
|
+
)
|
|
624
|
+
yield* startInvokes(
|
|
625
|
+
configuration,
|
|
626
|
+
Configuration.getInitialEntryPaths(machine, configuration),
|
|
627
|
+
internalPlanner.InitialEvent
|
|
628
|
+
)
|
|
629
|
+
// As above, keep the normalized configuration only while this
|
|
630
|
+
// worker can continue draining an already queued batch.
|
|
631
|
+
configuration = undefined
|
|
632
|
+
let pendingMessage: Option.Option<internalRuntime.ProcessMessage<Machine.EventOf<Events>>> = Option.none()
|
|
633
|
+
let liveRuntime: Runtime<Machine.EventOf<Events>, Machine.EmitOf<Emits>> | undefined
|
|
634
|
+
|
|
635
|
+
// Match the compact non-invoke loop while retaining state-scoped
|
|
636
|
+
// child lifecycle work at the same ordered Effect boundaries.
|
|
637
|
+
while (terminal === undefined) {
|
|
638
|
+
const message = Option.isSome(pendingMessage) ? pendingMessage.value : yield* receiveMessage
|
|
639
|
+
pendingMessage = Option.none()
|
|
640
|
+
const acknowledged = internalRuntime.isAcknowledgedMessage(message)
|
|
641
|
+
const event = acknowledged ? message.event : message
|
|
642
|
+
const before = current
|
|
643
|
+
let planned
|
|
644
|
+
try {
|
|
645
|
+
planned = internalPlanner.planConfiguration(
|
|
646
|
+
machine,
|
|
647
|
+
configuration ?? Configuration.normalizeConfigurationSync(machine, current),
|
|
648
|
+
event
|
|
649
|
+
)
|
|
650
|
+
} catch (error) {
|
|
651
|
+
if (error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError) {
|
|
652
|
+
return yield* error
|
|
653
|
+
}
|
|
654
|
+
throw error
|
|
655
|
+
}
|
|
656
|
+
configuration = planned.next
|
|
657
|
+
if (planned.microsteps.length > 0) {
|
|
658
|
+
const changed = planned.microsteps.some((step) => step.changed)
|
|
659
|
+
const exitPaths = planned.microsteps.flatMap((step) => step.exitPaths)
|
|
660
|
+
const entryEvents = new Map<string, Machine.LifecycleEvent<Events>>()
|
|
661
|
+
for (const step of planned.microsteps) {
|
|
662
|
+
if (step.changed) {
|
|
663
|
+
for (const path of step.entryPaths) {
|
|
664
|
+
entryEvents.set(path, step.event as Machine.LifecycleEvent<Events>)
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const next = Configuration.snapshotFromConfiguration<States>(machine, planned.next)
|
|
670
|
+
yield* CommandRuntime.runCommands(planned.commands, context)
|
|
671
|
+
if (changed) {
|
|
672
|
+
yield* stopInvokes(exitPaths)
|
|
673
|
+
}
|
|
674
|
+
yield* setState(next)
|
|
675
|
+
current = next
|
|
676
|
+
if (planned.emittedEvents.length > 0) {
|
|
677
|
+
yield* CommandRuntime.runEmittedEvents(
|
|
678
|
+
planned.emittedEvents as ReadonlyArray<Machine.EmitOf<Emits>>,
|
|
679
|
+
liveRuntime ??= CommandRuntime.makeLiveRuntime(machine, context)
|
|
680
|
+
)
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (planned.done) {
|
|
684
|
+
terminal = { output: planned.output }
|
|
685
|
+
yield* ownedChildren.stopAll()
|
|
686
|
+
} else if (changed) {
|
|
687
|
+
for (const [path, entryEvent] of entryEvents) {
|
|
688
|
+
yield* startInvokes(planned.next, [path], entryEvent)
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if (acknowledged) {
|
|
694
|
+
completeMessage({
|
|
695
|
+
before,
|
|
696
|
+
plan: acknowledgedPlan(
|
|
697
|
+
planned,
|
|
698
|
+
(state) =>
|
|
699
|
+
Configuration.snapshotFromConfiguration(machine, state as Configuration.ActiveConfiguration)
|
|
700
|
+
),
|
|
701
|
+
after: current
|
|
702
|
+
})
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (terminal === undefined) {
|
|
706
|
+
pendingMessage = yield* pollMessage
|
|
707
|
+
if (Option.isNone(pendingMessage)) {
|
|
708
|
+
configuration = undefined
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (terminal === undefined) {
|
|
714
|
+
return yield* Effect.die(
|
|
715
|
+
new Error("Machine process stopped receiving events before reaching a terminal configuration")
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
return terminal.output
|
|
719
|
+
}).pipe(
|
|
720
|
+
Effect.onExit(() => ownedChildren.stopAll())
|
|
721
|
+
)
|
|
722
|
+
}),
|
|
723
|
+
context
|
|
724
|
+
)
|
|
725
|
+
}) as internalRuntime.ProcessLogic<
|
|
726
|
+
Machine.Snapshot<States>,
|
|
727
|
+
Machine.EventOf<Events>,
|
|
728
|
+
E | ActionError<R> | InfiniteTransitionError | MachineSchemaDecodeError | StoppedError,
|
|
729
|
+
ExcludeCompatibleRuntime<
|
|
730
|
+
Exclude<ExecutionServices<InitialR | R>, internalRuntime.MachineRuntime>,
|
|
731
|
+
Machine.EventOf<Events>,
|
|
732
|
+
Machine.EmitOf<Emits>
|
|
733
|
+
>,
|
|
734
|
+
Output,
|
|
735
|
+
| InitialE
|
|
736
|
+
| E
|
|
737
|
+
| ActionError<InitialR | R>
|
|
738
|
+
| InfiniteTransitionError
|
|
739
|
+
| MachineSchemaDecodeError
|
|
740
|
+
| StartupError
|
|
741
|
+
| StoppedError
|
|
742
|
+
>
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const initialProcessLogicCache = new WeakMap<
|
|
746
|
+
Machine.Any,
|
|
747
|
+
internalRuntime.ProcessLogic<any, any, any, any, any, any>
|
|
748
|
+
>()
|
|
749
|
+
|
|
750
|
+
export const toProcessLogic: <
|
|
751
|
+
const States extends Machine.StateSchemas,
|
|
752
|
+
const Events extends ReadonlyArray<Machine.TaggedSchema>,
|
|
753
|
+
const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
754
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
755
|
+
UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>,
|
|
756
|
+
E = never,
|
|
757
|
+
R = never,
|
|
758
|
+
InitialE = never,
|
|
759
|
+
InitialR = never,
|
|
760
|
+
FinalStates extends Machine.StateIdentifier<States> = never,
|
|
761
|
+
Output = never
|
|
762
|
+
>(
|
|
763
|
+
machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits>,
|
|
764
|
+
...args: [...Machine.InputArgs<Input>]
|
|
765
|
+
) => internalRuntime.ProcessLogic<
|
|
766
|
+
Machine.Snapshot<States>,
|
|
767
|
+
Machine.EventOf<Events>,
|
|
768
|
+
E | ActionError<R> | InfiniteTransitionError | MachineSchemaDecodeError | StoppedError,
|
|
769
|
+
ExcludeCompatibleRuntime<
|
|
770
|
+
Exclude<ExecutionServices<InitialR | R>, internalRuntime.MachineRuntime>,
|
|
771
|
+
Machine.EventOf<Events>,
|
|
772
|
+
Machine.EmitOf<Emits>
|
|
773
|
+
>,
|
|
774
|
+
Output,
|
|
775
|
+
| InitialE
|
|
776
|
+
| E
|
|
777
|
+
| ActionError<InitialR | R>
|
|
778
|
+
| InfiniteTransitionError
|
|
779
|
+
| MachineSchemaDecodeError
|
|
780
|
+
| StartupError
|
|
781
|
+
| StoppedError
|
|
782
|
+
> = (machine, ...args) => {
|
|
783
|
+
if (args.length > 0) {
|
|
784
|
+
return makeProcessLogic(machine, { _tag: "Initial", args })
|
|
785
|
+
}
|
|
786
|
+
// The execution descriptor stores process-local invoke sessions by each
|
|
787
|
+
// runtime address and evaluates initialization/services on every start. A
|
|
788
|
+
// zero-argument descriptor is therefore safe to share for the lifetime of
|
|
789
|
+
// its immutable machine definition. Input-bearing and resumed starts retain
|
|
790
|
+
// their instance-specific entry values below.
|
|
791
|
+
const cached = initialProcessLogicCache.get(machine)
|
|
792
|
+
if (cached !== undefined) {
|
|
793
|
+
return cached as any
|
|
794
|
+
}
|
|
795
|
+
const logic = makeProcessLogic(machine, { _tag: "Initial", args })
|
|
796
|
+
initialProcessLogicCache.set(machine, logic as any)
|
|
797
|
+
return logic
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const toResumedProcessLogic = (
|
|
801
|
+
machine: Machine.Any,
|
|
802
|
+
snapshot: Machine.Snapshot<any>
|
|
803
|
+
): internalRuntime.ProcessLogic<any, any, any, any, any, any> =>
|
|
804
|
+
(makeProcessLogic as any)(machine, { _tag: "Resume", snapshot })
|
|
805
|
+
|
|
806
|
+
/** @internal Test-only runtime strategy selection for a fresh machine. */
|
|
807
|
+
export const startWithRuntimeStrategyForTesting = (
|
|
808
|
+
machine: Machine.Any,
|
|
809
|
+
strategy: internalRuntime.ProcessRuntimeStrategy,
|
|
810
|
+
...args: ReadonlyArray<unknown>
|
|
811
|
+
): Effect.Effect<internalRuntime.MachineRef<any, any, any, any>, any, any> =>
|
|
812
|
+
internalRuntime.startProcessWithStrategyForTesting(
|
|
813
|
+
(toProcessLogic as any)(machine, ...args),
|
|
814
|
+
strategy,
|
|
815
|
+
machine.id === undefined ? undefined : { id: machine.id }
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
/** @internal Test-only runtime strategy selection for a resumed machine. */
|
|
819
|
+
export const resumeWithRuntimeStrategyForTesting = (
|
|
820
|
+
machine: Machine.Any,
|
|
821
|
+
snapshot: Machine.Snapshot<any>,
|
|
822
|
+
strategy: internalRuntime.ProcessRuntimeStrategy
|
|
823
|
+
): Effect.Effect<internalRuntime.MachineRef<any, any, any, any>, any, any> =>
|
|
824
|
+
internalRuntime.startProcessWithStrategyForTesting(
|
|
825
|
+
toResumedProcessLogic(machine, snapshot),
|
|
826
|
+
strategy,
|
|
827
|
+
machine.id === undefined ? undefined : { id: machine.id }
|
|
828
|
+
)
|
|
829
|
+
|
|
830
|
+
export const start: <
|
|
831
|
+
const States extends Machine.StateSchemas,
|
|
832
|
+
const Events extends ReadonlyArray<Machine.TaggedSchema>,
|
|
833
|
+
const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
834
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
835
|
+
UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>,
|
|
836
|
+
E = never,
|
|
837
|
+
R = never,
|
|
838
|
+
InitialE = never,
|
|
839
|
+
InitialR = never,
|
|
840
|
+
FinalStates extends Machine.StateIdentifier<States> = never,
|
|
841
|
+
Output = never
|
|
842
|
+
>(
|
|
843
|
+
machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits>,
|
|
844
|
+
...args: [...Machine.InputArgs<Input>]
|
|
845
|
+
) => Effect.Effect<
|
|
846
|
+
internalRuntime.MachineRef<
|
|
847
|
+
Machine.Snapshot<States>,
|
|
848
|
+
Machine.EventOf<Events>,
|
|
849
|
+
| E
|
|
850
|
+
| ActionError<R>
|
|
851
|
+
| InfiniteTransitionError
|
|
852
|
+
| MachineSchemaDecodeError
|
|
853
|
+
| StoppedError,
|
|
854
|
+
Output
|
|
855
|
+
>,
|
|
856
|
+
| InitialE
|
|
857
|
+
| E
|
|
858
|
+
| ActionError<InitialR | R>
|
|
859
|
+
| InfiniteTransitionError
|
|
860
|
+
| MachineSchemaDecodeError
|
|
861
|
+
| StartupError
|
|
862
|
+
| StoppedError,
|
|
863
|
+
ExcludeCompatibleRuntime<
|
|
864
|
+
Exclude<ExecutionServices<InitialR | R>, internalRuntime.MachineRuntime>,
|
|
865
|
+
Machine.EventOf<Events>,
|
|
866
|
+
Machine.EmitOf<Emits>
|
|
867
|
+
>
|
|
868
|
+
> = (machine, ...args) =>
|
|
869
|
+
internalRuntime.startProcess(
|
|
870
|
+
toProcessLogic(machine, ...args),
|
|
871
|
+
machine.id === undefined ? undefined : { id: machine.id }
|
|
872
|
+
) as any
|
|
873
|
+
|
|
874
|
+
export const resume: <
|
|
875
|
+
const States extends Machine.StateSchemas,
|
|
876
|
+
const Events extends ReadonlyArray<Machine.TaggedSchema>,
|
|
877
|
+
const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
|
|
878
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
879
|
+
UnhandledStates extends Machine.StateIdentifier<States> = Machine.StateIdentifier<States>,
|
|
880
|
+
E = never,
|
|
881
|
+
R = never,
|
|
882
|
+
InitialE = never,
|
|
883
|
+
InitialR = never,
|
|
884
|
+
FinalStates extends Machine.StateIdentifier<States> = never,
|
|
885
|
+
Output = never
|
|
886
|
+
>(
|
|
887
|
+
machine: Machine<States, Events, Input, UnhandledStates, E, R, InitialE, InitialR, FinalStates, Output, Emits>,
|
|
888
|
+
snapshot: Machine.Snapshot<States>
|
|
889
|
+
) => Effect.Effect<
|
|
890
|
+
internalRuntime.MachineRef<
|
|
891
|
+
Machine.Snapshot<States>,
|
|
892
|
+
Machine.EventOf<Events>,
|
|
893
|
+
E | ActionError<R> | InfiniteTransitionError | MachineSchemaDecodeError | StoppedError,
|
|
894
|
+
Output
|
|
895
|
+
>,
|
|
896
|
+
MachineSchemaDecodeError,
|
|
897
|
+
ExcludeCompatibleRuntime<
|
|
898
|
+
Exclude<ExecutionServices<R>, internalRuntime.MachineRuntime>,
|
|
899
|
+
Machine.EventOf<Events>,
|
|
900
|
+
Machine.EmitOf<Emits>
|
|
901
|
+
>
|
|
902
|
+
> = (machine, snapshot) =>
|
|
903
|
+
internalRuntime.startProcess(
|
|
904
|
+
toResumedProcessLogic(machine, snapshot),
|
|
905
|
+
machine.id === undefined ? undefined : { id: machine.id }
|
|
906
|
+
) as any
|