@typeonce/effect-machine 0.6.0 → 0.6.1
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/package.json +5 -5
- package/src/Machine.ts +6873 -0
- package/src/index.ts +1 -0
- package/src/internal/machine/activities.ts +108 -0
- package/src/internal/machine/atom.ts +636 -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 +1747 -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 +649 -0
- package/src/unstable/reactivity/index.ts +1 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User-defined semantic invariants over planner traces.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.4.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Data from "effect/Data"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as Machine from "../../../Machine.js"
|
|
10
|
+
import type {
|
|
11
|
+
Invariant as MachineInvariant,
|
|
12
|
+
InvariantBuilder,
|
|
13
|
+
InvariantCheckResult,
|
|
14
|
+
InvariantOptions,
|
|
15
|
+
InvariantOutcome,
|
|
16
|
+
InvariantReport,
|
|
17
|
+
InvariantScope,
|
|
18
|
+
InvariantViolation,
|
|
19
|
+
StateInvariant,
|
|
20
|
+
StateInvariantContext,
|
|
21
|
+
StateInvariantOptions,
|
|
22
|
+
StateObservation,
|
|
23
|
+
StepInvariant,
|
|
24
|
+
StepInvariantContext,
|
|
25
|
+
Trace,
|
|
26
|
+
TraceInvariant,
|
|
27
|
+
TraceInvariantContext
|
|
28
|
+
} from "../../../testing/MachineTest.js"
|
|
29
|
+
|
|
30
|
+
type AnyMachine = Machine.Machine.Any
|
|
31
|
+
|
|
32
|
+
type StatePath<M extends AnyMachine> = Machine.Machine.StateIdentifier<Machine.Machine.States<M>>
|
|
33
|
+
|
|
34
|
+
interface Observation<M extends AnyMachine, Context> {
|
|
35
|
+
readonly context: Context
|
|
36
|
+
readonly observationIndex?: number
|
|
37
|
+
readonly eventIndex: number | undefined
|
|
38
|
+
readonly microstepIndex?: number
|
|
39
|
+
readonly phase?: StateObservation
|
|
40
|
+
readonly configuration?: ReadonlyArray<StatePath<M>>
|
|
41
|
+
readonly event?: Machine.Machine.Event<M> | Machine.InitialEvent
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const validateName = (name: string): void => {
|
|
45
|
+
if (name.trim().length === 0) {
|
|
46
|
+
throw new Error("MachineTest.Invariant expected name to be a non-empty string")
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const validateRequirement = <Context>(options: InvariantOptions<Context>): void => {
|
|
51
|
+
const minimum = options.require?.minObservations
|
|
52
|
+
if (minimum !== undefined && (!Number.isSafeInteger(minimum) || minimum < 0)) {
|
|
53
|
+
throw new Error("MachineTest.Invariant expected minObservations to be a non-negative safe integer")
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const stateInvariant = <M extends AnyMachine>(
|
|
58
|
+
name: string,
|
|
59
|
+
check: (context: StateInvariantContext<M>) => InvariantOutcome,
|
|
60
|
+
options: StateInvariantOptions<M> = {}
|
|
61
|
+
): StateInvariant<M> => {
|
|
62
|
+
validateName(name)
|
|
63
|
+
validateRequirement(options)
|
|
64
|
+
return Object.freeze({
|
|
65
|
+
_tag: "StateInvariant" as const,
|
|
66
|
+
name,
|
|
67
|
+
observe: options.observe ?? "settled",
|
|
68
|
+
check,
|
|
69
|
+
...(options.when === undefined ? {} : { when: options.when }),
|
|
70
|
+
...(options.require === undefined ? {} : { require: Object.freeze({ ...options.require }) })
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const stepInvariant = <M extends AnyMachine>(
|
|
75
|
+
name: string,
|
|
76
|
+
check: (context: StepInvariantContext<M>) => InvariantOutcome,
|
|
77
|
+
options: InvariantOptions<StepInvariantContext<M>> = {}
|
|
78
|
+
): StepInvariant<M> => {
|
|
79
|
+
validateName(name)
|
|
80
|
+
validateRequirement(options)
|
|
81
|
+
return Object.freeze({
|
|
82
|
+
_tag: "StepInvariant" as const,
|
|
83
|
+
name,
|
|
84
|
+
check,
|
|
85
|
+
...(options.when === undefined ? {} : { when: options.when }),
|
|
86
|
+
...(options.require === undefined ? {} : { require: Object.freeze({ ...options.require }) })
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const traceInvariant = <M extends AnyMachine>(
|
|
91
|
+
name: string,
|
|
92
|
+
check: (context: TraceInvariantContext<M>) => InvariantOutcome,
|
|
93
|
+
options: InvariantOptions<TraceInvariantContext<M>> = {}
|
|
94
|
+
): TraceInvariant<M> => {
|
|
95
|
+
validateName(name)
|
|
96
|
+
validateRequirement(options)
|
|
97
|
+
return Object.freeze({
|
|
98
|
+
_tag: "TraceInvariant" as const,
|
|
99
|
+
name,
|
|
100
|
+
check,
|
|
101
|
+
...(options.when === undefined ? {} : { when: options.when }),
|
|
102
|
+
...(options.require === undefined ? {} : { require: Object.freeze({ ...options.require }) })
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const Invariant = Object.freeze({
|
|
107
|
+
state: stateInvariant,
|
|
108
|
+
step: stepInvariant,
|
|
109
|
+
trace: traceInvariant
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
export const invariants = <M extends AnyMachine>(_machine: M): InvariantBuilder<M> =>
|
|
113
|
+
Object.freeze({
|
|
114
|
+
state: (
|
|
115
|
+
name: string,
|
|
116
|
+
check: (context: StateInvariantContext<M>) => InvariantOutcome,
|
|
117
|
+
options?: StateInvariantOptions<M>
|
|
118
|
+
) => stateInvariant(name, check, options),
|
|
119
|
+
step: (
|
|
120
|
+
name: string,
|
|
121
|
+
check: (context: StepInvariantContext<M>) => InvariantOutcome,
|
|
122
|
+
options?: InvariantOptions<StepInvariantContext<M>>
|
|
123
|
+
) => stepInvariant(name, check, options),
|
|
124
|
+
trace: (
|
|
125
|
+
name: string,
|
|
126
|
+
check: (context: TraceInvariantContext<M>) => InvariantOutcome,
|
|
127
|
+
options?: InvariantOptions<TraceInvariantContext<M>>
|
|
128
|
+
) => traceInvariant(name, check, options)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
export class InvariantError<M extends AnyMachine = AnyMachine> extends Data.TaggedError(
|
|
132
|
+
"MachineTestInvariantError"
|
|
133
|
+
)<{
|
|
134
|
+
readonly trace: Trace<M>
|
|
135
|
+
readonly violations: ReadonlyArray<InvariantViolation<M>>
|
|
136
|
+
readonly report: InvariantReport
|
|
137
|
+
}> {}
|
|
138
|
+
|
|
139
|
+
const configuration = <M extends AnyMachine>(
|
|
140
|
+
machine: M,
|
|
141
|
+
snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>
|
|
142
|
+
): ReadonlyArray<StatePath<M>> => Machine.configuration(machine, snapshot).map(({ path }) => path)
|
|
143
|
+
|
|
144
|
+
const stateObservations = <M extends AnyMachine>(
|
|
145
|
+
machine: M,
|
|
146
|
+
trace: Trace<M>,
|
|
147
|
+
observe: StateInvariant<M>["observe"]
|
|
148
|
+
): ReadonlyArray<Observation<M, StateInvariantContext<M>>> => {
|
|
149
|
+
const observations: Array<Observation<M, StateInvariantContext<M>>> = []
|
|
150
|
+
let observationIndex = 0
|
|
151
|
+
const add = (
|
|
152
|
+
snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>,
|
|
153
|
+
paths: ReadonlyArray<StatePath<M>>,
|
|
154
|
+
phase: StateObservation,
|
|
155
|
+
eventIndex: number | undefined,
|
|
156
|
+
microstepIndex: number | undefined,
|
|
157
|
+
event: Machine.Machine.Event<M> | Machine.InitialEvent | undefined
|
|
158
|
+
): void => {
|
|
159
|
+
const context: StateInvariantContext<M> = {
|
|
160
|
+
machine,
|
|
161
|
+
trace,
|
|
162
|
+
snapshot,
|
|
163
|
+
configuration: paths,
|
|
164
|
+
observationIndex,
|
|
165
|
+
phase,
|
|
166
|
+
eventIndex,
|
|
167
|
+
microstepIndex,
|
|
168
|
+
event
|
|
169
|
+
}
|
|
170
|
+
observations.push({
|
|
171
|
+
context,
|
|
172
|
+
observationIndex,
|
|
173
|
+
eventIndex,
|
|
174
|
+
...(microstepIndex === undefined ? {} : { microstepIndex }),
|
|
175
|
+
phase,
|
|
176
|
+
configuration: paths,
|
|
177
|
+
...(event === undefined ? {} : { event })
|
|
178
|
+
})
|
|
179
|
+
observationIndex += 1
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (observe === "final") {
|
|
183
|
+
add(trace.final, trace.finalConfiguration, "final", undefined, undefined, undefined)
|
|
184
|
+
return observations
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (observe === "settled" || observe === "all") {
|
|
188
|
+
add(
|
|
189
|
+
trace.initial.plan.state,
|
|
190
|
+
trace.initial.configuration,
|
|
191
|
+
"initial",
|
|
192
|
+
undefined,
|
|
193
|
+
undefined,
|
|
194
|
+
undefined
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (observe === "microsteps" || observe === "all") {
|
|
199
|
+
trace.initial.plan.microsteps.forEach((microstep, microstepIndex) => {
|
|
200
|
+
add(
|
|
201
|
+
microstep.next,
|
|
202
|
+
configuration(machine, microstep.next),
|
|
203
|
+
"microstep",
|
|
204
|
+
undefined,
|
|
205
|
+
microstepIndex,
|
|
206
|
+
microstep.event
|
|
207
|
+
)
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
for (const step of trace.steps) {
|
|
212
|
+
if (observe === "microsteps" || observe === "all") {
|
|
213
|
+
step.plan.microsteps.forEach((microstep, microstepIndex) => {
|
|
214
|
+
add(
|
|
215
|
+
microstep.next,
|
|
216
|
+
configuration(machine, microstep.next),
|
|
217
|
+
"microstep",
|
|
218
|
+
step.index,
|
|
219
|
+
microstepIndex,
|
|
220
|
+
microstep.event
|
|
221
|
+
)
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
if (observe === "settled" || observe === "all") {
|
|
225
|
+
add(
|
|
226
|
+
step.after,
|
|
227
|
+
step.afterConfiguration,
|
|
228
|
+
"event",
|
|
229
|
+
step.index,
|
|
230
|
+
undefined,
|
|
231
|
+
step.event
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return observations
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const stepObservations = <M extends AnyMachine>(
|
|
240
|
+
machine: M,
|
|
241
|
+
trace: Trace<M>
|
|
242
|
+
): ReadonlyArray<Observation<M, StepInvariantContext<M>>> =>
|
|
243
|
+
trace.steps.map((step) => ({
|
|
244
|
+
context: {
|
|
245
|
+
machine,
|
|
246
|
+
trace,
|
|
247
|
+
step,
|
|
248
|
+
index: step.index,
|
|
249
|
+
before: step.before,
|
|
250
|
+
beforeConfiguration: step.beforeConfiguration,
|
|
251
|
+
event: step.event,
|
|
252
|
+
plan: step.plan,
|
|
253
|
+
after: step.after,
|
|
254
|
+
afterConfiguration: step.afterConfiguration
|
|
255
|
+
},
|
|
256
|
+
eventIndex: step.index,
|
|
257
|
+
event: step.event
|
|
258
|
+
}))
|
|
259
|
+
|
|
260
|
+
const traceObservations = <M extends AnyMachine>(
|
|
261
|
+
machine: M,
|
|
262
|
+
trace: Trace<M>
|
|
263
|
+
): ReadonlyArray<Observation<M, TraceInvariantContext<M>>> => [{
|
|
264
|
+
context: { machine, trace },
|
|
265
|
+
eventIndex: undefined
|
|
266
|
+
}]
|
|
267
|
+
|
|
268
|
+
const scopeOf = <M extends AnyMachine>(invariant: MachineInvariant<M>): InvariantScope => {
|
|
269
|
+
switch (invariant._tag) {
|
|
270
|
+
case "StateInvariant":
|
|
271
|
+
return "state"
|
|
272
|
+
case "StepInvariant":
|
|
273
|
+
return "step"
|
|
274
|
+
case "TraceInvariant":
|
|
275
|
+
return "trace"
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const messageOf = (outcome: InvariantOutcome): string | undefined =>
|
|
280
|
+
outcome === true ? undefined : typeof outcome === "string" ? outcome : "Invariant predicate returned false"
|
|
281
|
+
|
|
282
|
+
type EvaluatedInvariant<Context> = InvariantOptions<Context> & {
|
|
283
|
+
readonly name: string
|
|
284
|
+
readonly check: (context: Context) => InvariantOutcome
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const evaluateInvariant = <M extends AnyMachine, Context>(
|
|
288
|
+
invariant: EvaluatedInvariant<Context>,
|
|
289
|
+
scope: InvariantScope,
|
|
290
|
+
observations: ReadonlyArray<Observation<M, Context>>
|
|
291
|
+
): {
|
|
292
|
+
readonly check: InvariantCheckResult
|
|
293
|
+
readonly violations: ReadonlyArray<InvariantViolation<M>>
|
|
294
|
+
} => {
|
|
295
|
+
let observed = 0
|
|
296
|
+
let failures = 0
|
|
297
|
+
const violations: Array<InvariantViolation<M>> = []
|
|
298
|
+
|
|
299
|
+
for (const observation of observations) {
|
|
300
|
+
if (invariant.when !== undefined && !invariant.when(observation.context)) continue
|
|
301
|
+
observed += 1
|
|
302
|
+
const message = messageOf(invariant.check(observation.context))
|
|
303
|
+
if (message === undefined) continue
|
|
304
|
+
failures += 1
|
|
305
|
+
violations.push({
|
|
306
|
+
invariant: invariant.name,
|
|
307
|
+
scope,
|
|
308
|
+
kind: "predicate",
|
|
309
|
+
...(observation.observationIndex === undefined ? {} : { observationIndex: observation.observationIndex }),
|
|
310
|
+
eventIndex: observation.eventIndex,
|
|
311
|
+
...(observation.microstepIndex === undefined ? {} : { microstepIndex: observation.microstepIndex }),
|
|
312
|
+
...(observation.phase === undefined ? {} : { phase: observation.phase }),
|
|
313
|
+
...(observation.configuration === undefined ? {} : { configuration: observation.configuration }),
|
|
314
|
+
...(observation.event === undefined ? {} : { event: observation.event }),
|
|
315
|
+
message
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const minimum = invariant.require?.minObservations ?? 0
|
|
320
|
+
const insufficient = observed < minimum
|
|
321
|
+
if (insufficient) {
|
|
322
|
+
violations.push({
|
|
323
|
+
invariant: invariant.name,
|
|
324
|
+
scope,
|
|
325
|
+
kind: "observations",
|
|
326
|
+
eventIndex: undefined,
|
|
327
|
+
message: `Invariant required at least ${minimum} observation${minimum === 1 ? "" : "s"} but observed ${observed}`
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
check: {
|
|
332
|
+
invariant: invariant.name,
|
|
333
|
+
scope,
|
|
334
|
+
status: failures > 0 ? "failed" : insufficient ? "insufficient" : observed === 0 ? "untested" : "passed",
|
|
335
|
+
observations: observed,
|
|
336
|
+
failures
|
|
337
|
+
},
|
|
338
|
+
violations
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export const checkInvariants = <M extends AnyMachine>(
|
|
343
|
+
machine: M,
|
|
344
|
+
trace: Trace<M>,
|
|
345
|
+
invariants: ReadonlyArray<MachineInvariant<M>>
|
|
346
|
+
): Effect.Effect<InvariantReport, InvariantError<M>> =>
|
|
347
|
+
Effect.suspend(() => {
|
|
348
|
+
const checks: Array<InvariantCheckResult> = []
|
|
349
|
+
const violations: Array<InvariantViolation<M>> = []
|
|
350
|
+
|
|
351
|
+
for (const invariant of invariants) {
|
|
352
|
+
const scope = scopeOf(invariant)
|
|
353
|
+
const result = invariant._tag === "StateInvariant"
|
|
354
|
+
? evaluateInvariant(invariant, scope, stateObservations(machine, trace, invariant.observe))
|
|
355
|
+
: invariant._tag === "StepInvariant"
|
|
356
|
+
? evaluateInvariant(invariant, scope, stepObservations(machine, trace))
|
|
357
|
+
: evaluateInvariant(invariant, scope, traceObservations(machine, trace))
|
|
358
|
+
checks.push(result.check)
|
|
359
|
+
violations.push(...result.violations)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const report: InvariantReport = { checks }
|
|
363
|
+
return violations.length === 0
|
|
364
|
+
? Effect.succeed(report)
|
|
365
|
+
: Effect.fail(new InvariantError({ trace, violations, report }))
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
export const assertInvariants = <M extends AnyMachine>(
|
|
369
|
+
machine: M,
|
|
370
|
+
trace: Trace<M>,
|
|
371
|
+
invariants: ReadonlyArray<MachineInvariant<M>>
|
|
372
|
+
): Effect.Effect<void, InvariantError<M>> => checkInvariants(machine, trace, invariants).pipe(Effect.asVoid)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Causal testing access to a running statechart.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.4.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Data from "effect/Data"
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import type * as Machine from "../../../Machine.js"
|
|
10
|
+
import type { Probe, ProbePlan } from "../../../testing/MachineTest.js"
|
|
11
|
+
import * as Runtime from "../../machine/runtime.js"
|
|
12
|
+
|
|
13
|
+
type AnyMachine = Machine.Machine.Any
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Raised when a reference was not created by the managed statechart runtime.
|
|
17
|
+
*
|
|
18
|
+
* @category errors
|
|
19
|
+
* @since 0.4.0
|
|
20
|
+
*/
|
|
21
|
+
export class ProbeUnavailableError extends Data.TaggedError("MachineTestProbeUnavailableError")<{
|
|
22
|
+
readonly message: string
|
|
23
|
+
}> {}
|
|
24
|
+
|
|
25
|
+
/** @internal */
|
|
26
|
+
export const probe = <M extends AnyMachine, Error, Output>(
|
|
27
|
+
machine: M,
|
|
28
|
+
ref: Machine.MachineRef<
|
|
29
|
+
Machine.Machine.Snapshot<Machine.Machine.States<M>>,
|
|
30
|
+
Machine.Machine.InputEvent<M>,
|
|
31
|
+
Error,
|
|
32
|
+
Output
|
|
33
|
+
>
|
|
34
|
+
): Effect.Effect<Probe<M, Error, Output>, ProbeUnavailableError> => {
|
|
35
|
+
const acknowledged = (ref as Runtime.MachineRef<
|
|
36
|
+
Machine.Machine.Snapshot<Machine.Machine.States<M>>,
|
|
37
|
+
Machine.Machine.InputEvent<M>,
|
|
38
|
+
Error,
|
|
39
|
+
Output
|
|
40
|
+
>)[Runtime.acknowledgedSend]
|
|
41
|
+
if (acknowledged === undefined) {
|
|
42
|
+
return Effect.fail(
|
|
43
|
+
new ProbeUnavailableError({
|
|
44
|
+
message: "MachineTest.probe requires a managed statechart reference created by Machine.start or Machine.resume"
|
|
45
|
+
})
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return Effect.succeed(Object.freeze({
|
|
50
|
+
machine,
|
|
51
|
+
ref,
|
|
52
|
+
await: Object.freeze({
|
|
53
|
+
none: { _tag: "None" } as const,
|
|
54
|
+
until: (
|
|
55
|
+
predicate: (
|
|
56
|
+
snapshot: Machine.RuntimeSnapshot<
|
|
57
|
+
Machine.Machine.Snapshot<Machine.Machine.States<M>>,
|
|
58
|
+
Error,
|
|
59
|
+
Output
|
|
60
|
+
>
|
|
61
|
+
) => boolean
|
|
62
|
+
) => ({ _tag: "Until", predicate } as const)
|
|
63
|
+
}),
|
|
64
|
+
sendAndAwait: (event: Machine.Machine.InputEvent<M>) =>
|
|
65
|
+
acknowledged.call(ref, event).pipe(
|
|
66
|
+
Effect.map(({ after, before, plan }) => {
|
|
67
|
+
const eventPlan = plan as ProbePlan<M>
|
|
68
|
+
return Object.freeze({
|
|
69
|
+
event,
|
|
70
|
+
before,
|
|
71
|
+
plan: eventPlan,
|
|
72
|
+
after,
|
|
73
|
+
handled: eventPlan.microsteps.length > 0,
|
|
74
|
+
configurationChanged: eventPlan.microsteps.some((microstep) => microstep.changed)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
)
|
|
78
|
+
}))
|
|
79
|
+
}
|