@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,636 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atom bridge for running machines.
|
|
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 Equal from "effect/Equal"
|
|
10
|
+
import * as Option from "effect/Option"
|
|
11
|
+
import type * as Schema from "effect/Schema"
|
|
12
|
+
import type * as Scope from "effect/Scope"
|
|
13
|
+
import * as Stream from "effect/Stream"
|
|
14
|
+
import { AsyncResult, Atom, type AtomRegistry } from "effect/unstable/reactivity"
|
|
15
|
+
import type * as Machine from "../../Machine.js"
|
|
16
|
+
import type { Bound, ChildMachineAtom, MachineAtom } from "../../unstable/reactivity/AtomMachine.js"
|
|
17
|
+
import * as internalMachine from "./machine.js"
|
|
18
|
+
import type { EnsureExecutable } from "./readiness.js"
|
|
19
|
+
import * as Topology from "./topology.js"
|
|
20
|
+
|
|
21
|
+
export class NotReadyError extends Data.TaggedError("NotReadyError") {}
|
|
22
|
+
|
|
23
|
+
export class ChildNotActiveError extends Data.TaggedError("ChildNotActiveError")<{
|
|
24
|
+
readonly id: string
|
|
25
|
+
}> {}
|
|
26
|
+
|
|
27
|
+
type AtomSupportedRequirements = Scope.Scope | AtomRegistry.AtomRegistry
|
|
28
|
+
|
|
29
|
+
type ExternalRequirements<Requirements> = Exclude<Requirements, AtomSupportedRequirements>
|
|
30
|
+
|
|
31
|
+
const ExternalRequirementsTypeId = "~effect/reactivity/AtomMachine/ExternalRequirements"
|
|
32
|
+
|
|
33
|
+
type EnsureNoExternalRequirements<Requirements> = [ExternalRequirements<Requirements>] extends [never] ? unknown : {
|
|
34
|
+
readonly [ExternalRequirementsTypeId]: ExternalRequirements<Requirements>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type IsAny<A> = 0 extends (1 & A) ? true : false
|
|
38
|
+
|
|
39
|
+
type ExcludeCompatibleMachineRuntime<Requirements, Events, Emits> = Requirements extends
|
|
40
|
+
Machine.Runtime.Requirement<infer RequiredEvents, infer RequiredEmits> ?
|
|
41
|
+
IsAny<Requirements> extends true ? Requirements
|
|
42
|
+
: [RequiredEvents] extends [Events] ? [RequiredEmits] extends [Emits] ? never : Requirements
|
|
43
|
+
: Requirements
|
|
44
|
+
: Requirements
|
|
45
|
+
|
|
46
|
+
type MachineRequirements<InitialR, R, Events, Emits> = ExcludeCompatibleMachineRuntime<
|
|
47
|
+
Machine.ExecutionServices<InitialR | R>,
|
|
48
|
+
Events,
|
|
49
|
+
Emits
|
|
50
|
+
>
|
|
51
|
+
|
|
52
|
+
type MachineResumeRequirements<R, Events, Emits> = ExcludeCompatibleMachineRuntime<
|
|
53
|
+
Machine.ExecutionServices<R>,
|
|
54
|
+
Events,
|
|
55
|
+
Emits
|
|
56
|
+
>
|
|
57
|
+
|
|
58
|
+
type MachineRuntimeError<E, R> =
|
|
59
|
+
| E
|
|
60
|
+
| Machine.ActionError<R>
|
|
61
|
+
| Machine.InfiniteTransitionError
|
|
62
|
+
| Machine.MachineSchemaDecodeError
|
|
63
|
+
| Machine.StoppedError
|
|
64
|
+
|
|
65
|
+
type MachineStartError<InitialE, E, InitialR, R, RuntimeError = never> =
|
|
66
|
+
| InitialE
|
|
67
|
+
| E
|
|
68
|
+
| Machine.ActionError<InitialR | R>
|
|
69
|
+
| Machine.InfiniteTransitionError
|
|
70
|
+
| Machine.MachineSchemaDecodeError
|
|
71
|
+
| Machine.StartupError
|
|
72
|
+
| Machine.StoppedError
|
|
73
|
+
| RuntimeError
|
|
74
|
+
|
|
75
|
+
const runMachineAtomEffect = <State, Event, Error, Output, StartError, Requirements>(
|
|
76
|
+
get: Atom.AtomContext,
|
|
77
|
+
start: Effect.Effect<Machine.MachineRef<State, Event, Error, Output>, StartError, Requirements>
|
|
78
|
+
): Effect.Effect<never, StartError, Requirements> =>
|
|
79
|
+
Effect.scoped(
|
|
80
|
+
Effect.acquireRelease(start, (ref) => ref.stop).pipe(
|
|
81
|
+
Effect.tap((ref) => Effect.sync(() => get.setSelf(AsyncResult.success(ref)))),
|
|
82
|
+
Effect.flatMap(() => Effect.never)
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const startMachineAtomEffect = <
|
|
87
|
+
const States extends Machine.Machine.StateSchemas,
|
|
88
|
+
const Events extends ReadonlyArray<Machine.Machine.TaggedSchema>,
|
|
89
|
+
const Emits extends ReadonlyArray<Machine.Machine.TaggedSchema> = any,
|
|
90
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
91
|
+
UnhandledStates extends Machine.Machine.StateIdentifier<States> = Machine.Machine.StateIdentifier<States>,
|
|
92
|
+
E = never,
|
|
93
|
+
R = never,
|
|
94
|
+
InitialE = never,
|
|
95
|
+
InitialR = never,
|
|
96
|
+
FinalStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
97
|
+
Output = never,
|
|
98
|
+
OutputStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
99
|
+
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
|
|
100
|
+
>(
|
|
101
|
+
get: Atom.AtomContext,
|
|
102
|
+
machine:
|
|
103
|
+
& Machine.Machine<
|
|
104
|
+
States,
|
|
105
|
+
Events,
|
|
106
|
+
Input,
|
|
107
|
+
UnhandledStates,
|
|
108
|
+
E,
|
|
109
|
+
R,
|
|
110
|
+
InitialE,
|
|
111
|
+
InitialR,
|
|
112
|
+
FinalStates,
|
|
113
|
+
Output,
|
|
114
|
+
Emits,
|
|
115
|
+
OutputStates,
|
|
116
|
+
InputEvents
|
|
117
|
+
>
|
|
118
|
+
& EnsureExecutable<States, UnhandledStates, OutputStates>,
|
|
119
|
+
args: [...Machine.Machine.InputArgs<Input>]
|
|
120
|
+
): Effect.Effect<
|
|
121
|
+
Machine.MachineRef<
|
|
122
|
+
Machine.Machine.Snapshot<States>,
|
|
123
|
+
Machine.Machine.EventOf<InputEvents>,
|
|
124
|
+
MachineRuntimeError<E, R>,
|
|
125
|
+
Output
|
|
126
|
+
>,
|
|
127
|
+
MachineStartError<InitialE, E, InitialR, R>,
|
|
128
|
+
MachineRequirements<InitialR, R, Machine.Machine.EventOf<Events>, Machine.Machine.EmitOf<Emits>>
|
|
129
|
+
> => runMachineAtomEffect(get, internalMachine.start(machine, ...args))
|
|
130
|
+
|
|
131
|
+
const resumeMachineAtomEffect = (
|
|
132
|
+
get: Atom.AtomContext,
|
|
133
|
+
machine: Machine.Machine.Any,
|
|
134
|
+
snapshot: Machine.Machine.Snapshot<any>
|
|
135
|
+
) => runMachineAtomEffect(get, internalMachine.resume(machine as any, snapshot as any))
|
|
136
|
+
|
|
137
|
+
type RefState<Ref> = Ref extends Machine.MachineRef<infer State, any, any, any> ? State : never
|
|
138
|
+
type RefError<Ref> = Ref extends Machine.MachineRef<any, any, infer Error, any> ? Error : never
|
|
139
|
+
type RefOutput<Ref> = Ref extends Machine.MachineRef<any, any, any, infer Output> ? Output : never
|
|
140
|
+
|
|
141
|
+
const makeRuntimeResultAtom = <State, Error, Output, StartError>(
|
|
142
|
+
snapshot: Atom.Atom<AsyncResult.AsyncResult<Machine.RuntimeSnapshot<State, Error, Output>, StartError>>
|
|
143
|
+
): Atom.Atom<AsyncResult.AsyncResult<State, StartError | Error>> =>
|
|
144
|
+
Atom.readable((get): AsyncResult.AsyncResult<State, StartError | Error> => {
|
|
145
|
+
const current = get(snapshot)
|
|
146
|
+
if (AsyncResult.isInitial(current)) {
|
|
147
|
+
return AsyncResult.initial(current.waiting)
|
|
148
|
+
} else if (AsyncResult.isFailure(current)) {
|
|
149
|
+
return AsyncResult.failureWithPrevious(current.cause, {
|
|
150
|
+
previous: get.self(),
|
|
151
|
+
waiting: current.waiting
|
|
152
|
+
})
|
|
153
|
+
} else if (current.value.status === "error") {
|
|
154
|
+
return AsyncResult.failureWithPrevious(current.value.cause, {
|
|
155
|
+
previous: get.self(),
|
|
156
|
+
waiting: current.waiting
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
return AsyncResult.success(current.value.state, {
|
|
160
|
+
waiting: current.waiting
|
|
161
|
+
})
|
|
162
|
+
}).pipe(Atom.withEquality(Equal.equals))
|
|
163
|
+
|
|
164
|
+
const makeChildRuntimeResultAtom = <State, Error, Output, StartError>(
|
|
165
|
+
snapshot: Atom.Atom<
|
|
166
|
+
AsyncResult.AsyncResult<Option.Option<Machine.RuntimeSnapshot<State, Error, Output>>, StartError>
|
|
167
|
+
>
|
|
168
|
+
): Atom.Atom<AsyncResult.AsyncResult<Option.Option<State>, StartError | Error>> =>
|
|
169
|
+
Atom.readable((get): AsyncResult.AsyncResult<Option.Option<State>, StartError | Error> => {
|
|
170
|
+
const current = get(snapshot)
|
|
171
|
+
if (AsyncResult.isInitial(current)) {
|
|
172
|
+
return AsyncResult.initial(current.waiting)
|
|
173
|
+
} else if (AsyncResult.isFailure(current)) {
|
|
174
|
+
return AsyncResult.failureWithPrevious(current.cause, {
|
|
175
|
+
previous: get.self(),
|
|
176
|
+
waiting: current.waiting
|
|
177
|
+
})
|
|
178
|
+
} else if (Option.isNone(current.value)) {
|
|
179
|
+
const previous = get.self<AsyncResult.AsyncResult<Option.Option<State>, StartError | Error>>()
|
|
180
|
+
if (Option.isSome(previous) && AsyncResult.isFailure(previous.value)) {
|
|
181
|
+
return previous.value
|
|
182
|
+
}
|
|
183
|
+
return AsyncResult.success(Option.none(), {
|
|
184
|
+
waiting: current.waiting
|
|
185
|
+
})
|
|
186
|
+
} else if (current.value.value.status === "error") {
|
|
187
|
+
return AsyncResult.failureWithPrevious(current.value.value.cause, {
|
|
188
|
+
previous: get.self(),
|
|
189
|
+
waiting: current.waiting
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
return AsyncResult.success(Option.some(current.value.value.state), {
|
|
193
|
+
waiting: current.waiting
|
|
194
|
+
})
|
|
195
|
+
}).pipe(Atom.withEquality(Equal.equals))
|
|
196
|
+
|
|
197
|
+
const makeChildRefAtom = <Child extends Machine.ChildMachine.Any, StartError>(
|
|
198
|
+
parentRef: Atom.Atom<
|
|
199
|
+
AsyncResult.AsyncResult<Option.Option<Machine.MachineRef<any, any, any, any>>, StartError>
|
|
200
|
+
>,
|
|
201
|
+
child: Child
|
|
202
|
+
): Atom.Atom<AsyncResult.AsyncResult<Option.Option<Machine.ChildMachine.Ref<Child>>, StartError>> =>
|
|
203
|
+
Atom.readable((get) => {
|
|
204
|
+
const parent = get(parentRef)
|
|
205
|
+
if (AsyncResult.isInitial(parent)) {
|
|
206
|
+
return AsyncResult.initial(parent.waiting)
|
|
207
|
+
} else if (AsyncResult.isFailure(parent)) {
|
|
208
|
+
return AsyncResult.failureWithPrevious(parent.cause, {
|
|
209
|
+
previous: get.self(),
|
|
210
|
+
waiting: parent.waiting
|
|
211
|
+
})
|
|
212
|
+
} else if (Option.isNone(parent.value)) {
|
|
213
|
+
return AsyncResult.success(Option.none())
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const handle = parent.value.value
|
|
217
|
+
const current = Effect.runSync(handle.child(child))
|
|
218
|
+
const cancel = Effect.runCallback(
|
|
219
|
+
Effect.yieldNow.pipe(
|
|
220
|
+
Effect.andThen(
|
|
221
|
+
handle.childChanges(child).pipe(
|
|
222
|
+
Stream.runForEach((ref) => Effect.sync(() => get.setSelf(AsyncResult.success(ref))))
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
get.addFinalizer(cancel)
|
|
228
|
+
return AsyncResult.success(current)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
const makeChildFromRefAtom = <Child extends Machine.ChildMachine.Any, StartError>(
|
|
232
|
+
ref: Atom.Atom<AsyncResult.AsyncResult<Option.Option<Machine.ChildMachine.Ref<Child>>, StartError>>,
|
|
233
|
+
descriptor: Child
|
|
234
|
+
): ChildMachineAtom<Child, StartError> => {
|
|
235
|
+
type Ref = Machine.ChildMachine.Ref<Child>
|
|
236
|
+
type State = RefState<Ref>
|
|
237
|
+
type Error = RefError<Ref>
|
|
238
|
+
type Output = RefOutput<Ref>
|
|
239
|
+
|
|
240
|
+
const snapshot = Atom.readable((get): AsyncResult.AsyncResult<
|
|
241
|
+
Option.Option<Machine.RuntimeSnapshot<State, Error, Output>>,
|
|
242
|
+
StartError
|
|
243
|
+
> => {
|
|
244
|
+
const result = get(ref)
|
|
245
|
+
if (AsyncResult.isInitial(result)) {
|
|
246
|
+
return AsyncResult.initial(result.waiting)
|
|
247
|
+
} else if (AsyncResult.isFailure(result)) {
|
|
248
|
+
return AsyncResult.failureWithPrevious(result.cause, {
|
|
249
|
+
previous: get.self(),
|
|
250
|
+
waiting: result.waiting
|
|
251
|
+
})
|
|
252
|
+
} else if (Option.isNone(result.value)) {
|
|
253
|
+
return AsyncResult.success(Option.none())
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const handle = result.value.value as unknown as Machine.MachineRef<State, any, Error, Output>
|
|
257
|
+
const cancel = Effect.runCallback(
|
|
258
|
+
handle.changes.pipe(
|
|
259
|
+
Stream.runForEach((snapshot) => Effect.sync(() => get.setSelf(AsyncResult.success(Option.some(snapshot)))))
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
get.addFinalizer(cancel)
|
|
263
|
+
return AsyncResult.success(Option.some(Effect.runSync(handle.snapshot)))
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
const send = Atom.writable<
|
|
267
|
+
AsyncResult.AsyncResult<void, StartError | NotReadyError | ChildNotActiveError | Machine.StoppedError>,
|
|
268
|
+
Machine.ChildMachine.Event<Child>
|
|
269
|
+
>(
|
|
270
|
+
(get) => AsyncResult.map(get(ref), () => undefined),
|
|
271
|
+
(ctx, event) => {
|
|
272
|
+
const result = ctx.get(ref)
|
|
273
|
+
if (AsyncResult.isInitial(result)) {
|
|
274
|
+
ctx.setSelf(AsyncResult.fail(new NotReadyError()))
|
|
275
|
+
} else if (AsyncResult.isFailure(result)) {
|
|
276
|
+
ctx.setSelf(AsyncResult.map(result, () => undefined))
|
|
277
|
+
} else if (Option.isNone(result.value)) {
|
|
278
|
+
ctx.setSelf(AsyncResult.fail(new ChildNotActiveError({ id: descriptor.id })))
|
|
279
|
+
} else {
|
|
280
|
+
Effect.runCallback(result.value.value.send(event as never), {
|
|
281
|
+
onExit: (exit) => ctx.setSelf(AsyncResult.fromExit(exit))
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
const stop = Atom.writable<
|
|
288
|
+
AsyncResult.AsyncResult<void, StartError | NotReadyError | ChildNotActiveError>,
|
|
289
|
+
void
|
|
290
|
+
>(
|
|
291
|
+
(get) => AsyncResult.map(get(ref), () => undefined),
|
|
292
|
+
(ctx) => {
|
|
293
|
+
const result = ctx.get(ref)
|
|
294
|
+
if (AsyncResult.isInitial(result)) {
|
|
295
|
+
ctx.setSelf(AsyncResult.fail(new NotReadyError()))
|
|
296
|
+
} else if (AsyncResult.isFailure(result)) {
|
|
297
|
+
ctx.setSelf(AsyncResult.map(result, () => undefined))
|
|
298
|
+
} else if (Option.isNone(result.value)) {
|
|
299
|
+
ctx.setSelf(AsyncResult.fail(new ChildNotActiveError({ id: descriptor.id })))
|
|
300
|
+
} else {
|
|
301
|
+
Effect.runCallback(result.value.value.stop)
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
const childFamily = Atom.family((nested: Machine.ChildMachine.Any) =>
|
|
307
|
+
makeChildFromRefAtom(
|
|
308
|
+
makeChildRefAtom(ref as any, nested),
|
|
309
|
+
nested
|
|
310
|
+
)
|
|
311
|
+
)
|
|
312
|
+
const child = <Nested extends Machine.ChildMachine.Any>(
|
|
313
|
+
nested: Nested
|
|
314
|
+
): ChildMachineAtom<Nested, StartError> => childFamily(nested) as ChildMachineAtom<Nested, StartError>
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
ref,
|
|
318
|
+
snapshot,
|
|
319
|
+
state: Atom.mapResult(snapshot, Option.map((snapshot) => snapshot.state)),
|
|
320
|
+
result: makeChildRuntimeResultAtom(snapshot),
|
|
321
|
+
send,
|
|
322
|
+
stop,
|
|
323
|
+
child
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const makeFromRefAtom = <State, Event, Error, Output, StartError>(
|
|
328
|
+
ref: Atom.Atom<AsyncResult.AsyncResult<Machine.MachineRef<State, Event, Error, Output>, StartError>>
|
|
329
|
+
): MachineAtom<State, Event, Error, Output, StartError> => {
|
|
330
|
+
const snapshot = Atom.readable((
|
|
331
|
+
get
|
|
332
|
+
): AsyncResult.AsyncResult<Machine.RuntimeSnapshot<State, Error, Output>, StartError> => {
|
|
333
|
+
const result = get(ref)
|
|
334
|
+
if (AsyncResult.isInitial(result)) {
|
|
335
|
+
return AsyncResult.initial(result.waiting)
|
|
336
|
+
} else if (AsyncResult.isFailure(result)) {
|
|
337
|
+
return AsyncResult.failureWithPrevious(result.cause, {
|
|
338
|
+
previous: get.self<AsyncResult.AsyncResult<Machine.RuntimeSnapshot<State, Error, Output>, StartError>>(),
|
|
339
|
+
waiting: result.waiting
|
|
340
|
+
})
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const handle = result.value
|
|
344
|
+
const cancel = Effect.runCallback(
|
|
345
|
+
handle.changes.pipe(
|
|
346
|
+
Stream.runForEach((snapshot) =>
|
|
347
|
+
Effect.sync(() =>
|
|
348
|
+
get.setSelf(
|
|
349
|
+
AsyncResult.success(snapshot, {
|
|
350
|
+
waiting: snapshot.status === "active"
|
|
351
|
+
})
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
)
|
|
357
|
+
get.addFinalizer(cancel)
|
|
358
|
+
|
|
359
|
+
const current = Effect.runSync(handle.snapshot)
|
|
360
|
+
return AsyncResult.success(current, {
|
|
361
|
+
waiting: current.status === "active"
|
|
362
|
+
})
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
const send = Atom.writable<
|
|
366
|
+
AsyncResult.AsyncResult<void, StartError | NotReadyError | Machine.StoppedError>,
|
|
367
|
+
Event
|
|
368
|
+
>(
|
|
369
|
+
(get) => AsyncResult.map(get(ref), () => undefined),
|
|
370
|
+
(ctx, event: Event) => {
|
|
371
|
+
const result = ctx.get(ref)
|
|
372
|
+
if (AsyncResult.isInitial(result)) {
|
|
373
|
+
ctx.setSelf(AsyncResult.fail(new NotReadyError()))
|
|
374
|
+
} else if (AsyncResult.isFailure(result)) {
|
|
375
|
+
ctx.setSelf(AsyncResult.map(result, () => undefined))
|
|
376
|
+
} else {
|
|
377
|
+
Effect.runCallback(result.value.send(event), {
|
|
378
|
+
onExit: (exit) =>
|
|
379
|
+
ctx.setSelf(
|
|
380
|
+
AsyncResult.fromExit(exit)
|
|
381
|
+
)
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
const stop = Atom.writable<AsyncResult.AsyncResult<void, StartError | NotReadyError>, void>(
|
|
388
|
+
(get) => AsyncResult.map(get(ref), () => undefined),
|
|
389
|
+
(ctx) => {
|
|
390
|
+
const result = ctx.get(ref)
|
|
391
|
+
if (AsyncResult.isInitial(result)) {
|
|
392
|
+
ctx.setSelf(AsyncResult.fail(new NotReadyError()))
|
|
393
|
+
} else if (AsyncResult.isFailure(result)) {
|
|
394
|
+
ctx.setSelf(AsyncResult.map(result, () => undefined))
|
|
395
|
+
} else {
|
|
396
|
+
Effect.runCallback(result.value.stop)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
const optionalRef = Atom.mapResult(ref, Option.some)
|
|
402
|
+
const childFamily = Atom.family((descriptor: Machine.ChildMachine.Any) =>
|
|
403
|
+
makeChildFromRefAtom(
|
|
404
|
+
makeChildRefAtom(optionalRef as any, descriptor),
|
|
405
|
+
descriptor
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
const child = <Child extends Machine.ChildMachine.Any>(
|
|
409
|
+
descriptor: Child
|
|
410
|
+
): ChildMachineAtom<Child, StartError> => childFamily(descriptor) as ChildMachineAtom<Child, StartError>
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
ref,
|
|
414
|
+
snapshot,
|
|
415
|
+
state: Atom.mapResult(snapshot, (snapshot) => snapshot.state),
|
|
416
|
+
result: makeRuntimeResultAtom(snapshot),
|
|
417
|
+
send,
|
|
418
|
+
stop,
|
|
419
|
+
child
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
type SnapshotNode<State> = State extends Machine.Machine.AtomicSnapshot<string, unknown> ?
|
|
424
|
+
| State
|
|
425
|
+
| (State extends { readonly state: infer Child } ? SnapshotNode<Child>
|
|
426
|
+
: State extends { readonly states: infer Regions } ? SnapshotNode<Regions[keyof Regions]>
|
|
427
|
+
: never)
|
|
428
|
+
: never
|
|
429
|
+
|
|
430
|
+
type SnapshotIdentifier<State> = SnapshotNode<State> extends infer Node ?
|
|
431
|
+
Node extends { readonly path: infer Path extends string } ? Path : never
|
|
432
|
+
: never
|
|
433
|
+
|
|
434
|
+
type SnapshotValueByIdentifier<State, Path extends SnapshotIdentifier<State>> = SnapshotNode<State> extends infer Node ?
|
|
435
|
+
Node extends { readonly path: Path; readonly value: infer Value } ? Value : never
|
|
436
|
+
: never
|
|
437
|
+
|
|
438
|
+
type ChildState<Child extends Machine.ChildMachine.Any> = RefState<Machine.ChildMachine.Ref<Child>>
|
|
439
|
+
|
|
440
|
+
const selectSnapshot = <
|
|
441
|
+
State extends Machine.Machine.AtomicSnapshot<string, unknown>,
|
|
442
|
+
Path extends SnapshotIdentifier<State>
|
|
443
|
+
>(
|
|
444
|
+
snapshot: State,
|
|
445
|
+
path: Path
|
|
446
|
+
): Option.Option<SnapshotValueByIdentifier<State, Path>> =>
|
|
447
|
+
Topology.getSnapshotByPath(snapshot, path).pipe(
|
|
448
|
+
Option.map((snapshot) => snapshot.value)
|
|
449
|
+
) as Option.Option<SnapshotValueByIdentifier<State, Path>>
|
|
450
|
+
|
|
451
|
+
export const select = <
|
|
452
|
+
State extends Machine.Machine.AtomicSnapshot<string, unknown>,
|
|
453
|
+
Event,
|
|
454
|
+
Error,
|
|
455
|
+
Output,
|
|
456
|
+
StartError,
|
|
457
|
+
const Path extends SnapshotIdentifier<State>
|
|
458
|
+
>(
|
|
459
|
+
self: MachineAtom<State, Event, Error, Output, StartError>,
|
|
460
|
+
path: Path
|
|
461
|
+
): Atom.Atom<
|
|
462
|
+
AsyncResult.AsyncResult<Option.Option<SnapshotValueByIdentifier<State, Path>>, StartError | Error>
|
|
463
|
+
> =>
|
|
464
|
+
Atom.mapResult(self.result, (snapshot) => selectSnapshot(snapshot, path)).pipe(
|
|
465
|
+
Atom.withEquality(Equal.equals)
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
export const selectChild = <
|
|
469
|
+
Child extends Machine.ChildMachine.Any,
|
|
470
|
+
StartError,
|
|
471
|
+
const Path extends SnapshotIdentifier<ChildState<Child>>
|
|
472
|
+
>(
|
|
473
|
+
self: ChildMachineAtom<Child, StartError>,
|
|
474
|
+
path: Path
|
|
475
|
+
): Atom.Atom<
|
|
476
|
+
AsyncResult.AsyncResult<
|
|
477
|
+
Option.Option<SnapshotValueByIdentifier<ChildState<Child>, Path>>,
|
|
478
|
+
StartError | RefError<Machine.ChildMachine.Ref<Child>>
|
|
479
|
+
>
|
|
480
|
+
> =>
|
|
481
|
+
Atom.mapResult(
|
|
482
|
+
self.result,
|
|
483
|
+
Option.flatMap((snapshot) => selectSnapshot(snapshot, path))
|
|
484
|
+
).pipe(Atom.withEquality(Equal.equals))
|
|
485
|
+
|
|
486
|
+
export const matches = <
|
|
487
|
+
State extends Machine.Machine.AtomicSnapshot<string, unknown>,
|
|
488
|
+
Event,
|
|
489
|
+
Error,
|
|
490
|
+
Output,
|
|
491
|
+
StartError,
|
|
492
|
+
const Path extends SnapshotIdentifier<State>
|
|
493
|
+
>(
|
|
494
|
+
self: MachineAtom<State, Event, Error, Output, StartError>,
|
|
495
|
+
path: Path
|
|
496
|
+
): Atom.Atom<AsyncResult.AsyncResult<boolean, StartError | Error>> =>
|
|
497
|
+
Atom.mapResult(self.result, (snapshot) => Option.isSome(Topology.getSnapshotByPath(snapshot, path))).pipe(
|
|
498
|
+
Atom.withEquality(Equal.equals)
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
export const matchesChild = <
|
|
502
|
+
Child extends Machine.ChildMachine.Any,
|
|
503
|
+
StartError,
|
|
504
|
+
const Path extends SnapshotIdentifier<ChildState<Child>>
|
|
505
|
+
>(
|
|
506
|
+
self: ChildMachineAtom<Child, StartError>,
|
|
507
|
+
path: Path
|
|
508
|
+
): Atom.Atom<
|
|
509
|
+
AsyncResult.AsyncResult<boolean, StartError | RefError<Machine.ChildMachine.Ref<Child>>>
|
|
510
|
+
> =>
|
|
511
|
+
Atom.mapResult(
|
|
512
|
+
self.result,
|
|
513
|
+
Option.exists((snapshot) => Option.isSome(Topology.getSnapshotByPath(snapshot, path)))
|
|
514
|
+
).pipe(Atom.withEquality(Equal.equals))
|
|
515
|
+
|
|
516
|
+
type MachineResumeRequirementsOf<M extends Machine.Machine.Any> = MachineResumeRequirements<
|
|
517
|
+
Machine.Machine.Services<M>,
|
|
518
|
+
Machine.Machine.Event<M>,
|
|
519
|
+
Machine.Machine.Emit<M>
|
|
520
|
+
>
|
|
521
|
+
|
|
522
|
+
type EnsureMachineExecutable<M extends Machine.Machine.Any> = IsAny<Machine.Machine.States<M>> extends true ? {
|
|
523
|
+
readonly "~effect/reactivity/AtomMachine/ConcreteMachineRequired": M
|
|
524
|
+
}
|
|
525
|
+
: EnsureExecutable<
|
|
526
|
+
Machine.Machine.States<M>,
|
|
527
|
+
Machine.Machine.UnhandledStates<M>,
|
|
528
|
+
Machine.Machine.OutputStates<M>
|
|
529
|
+
>
|
|
530
|
+
|
|
531
|
+
type ResumedMachineAtomOf<M extends Machine.Machine.Any, RuntimeError> = MachineAtom<
|
|
532
|
+
Machine.Machine.Snapshot<Machine.Machine.States<M>>,
|
|
533
|
+
Machine.Machine.InputEvent<M>,
|
|
534
|
+
MachineRuntimeError<Machine.Machine.Error<M>, Machine.Machine.Services<M>>,
|
|
535
|
+
Machine.Machine.Output<M>,
|
|
536
|
+
Machine.MachineSchemaDecodeError | RuntimeError
|
|
537
|
+
>
|
|
538
|
+
|
|
539
|
+
export const make: {
|
|
540
|
+
<
|
|
541
|
+
const States extends Machine.Machine.StateSchemas,
|
|
542
|
+
const Events extends ReadonlyArray<Machine.Machine.TaggedSchema>,
|
|
543
|
+
const Emits extends ReadonlyArray<Machine.Machine.TaggedSchema> = any,
|
|
544
|
+
const Input extends Schema.Top = typeof Schema.Void,
|
|
545
|
+
UnhandledStates extends Machine.Machine.StateIdentifier<States> = Machine.Machine.StateIdentifier<States>,
|
|
546
|
+
E = never,
|
|
547
|
+
R = never,
|
|
548
|
+
InitialE = never,
|
|
549
|
+
InitialR = never,
|
|
550
|
+
FinalStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
551
|
+
Output = never,
|
|
552
|
+
OutputStates extends Machine.Machine.StateIdentifier<States> = never,
|
|
553
|
+
InputEvents extends ReadonlyArray<Machine.Machine.TaggedSchema> = Events
|
|
554
|
+
>(
|
|
555
|
+
machine:
|
|
556
|
+
& Machine.Machine<
|
|
557
|
+
States,
|
|
558
|
+
Events,
|
|
559
|
+
Input,
|
|
560
|
+
UnhandledStates,
|
|
561
|
+
E,
|
|
562
|
+
R,
|
|
563
|
+
InitialE,
|
|
564
|
+
InitialR,
|
|
565
|
+
FinalStates,
|
|
566
|
+
Output,
|
|
567
|
+
Emits,
|
|
568
|
+
OutputStates,
|
|
569
|
+
InputEvents
|
|
570
|
+
>
|
|
571
|
+
& EnsureNoExternalRequirements<
|
|
572
|
+
MachineRequirements<
|
|
573
|
+
InitialR,
|
|
574
|
+
R,
|
|
575
|
+
Machine.Machine.EventOf<Events>,
|
|
576
|
+
Machine.Machine.EmitOf<Emits>
|
|
577
|
+
>
|
|
578
|
+
>
|
|
579
|
+
& EnsureExecutable<States, UnhandledStates, OutputStates>,
|
|
580
|
+
...args: [...Machine.Machine.InputArgs<Input>]
|
|
581
|
+
): MachineAtom<
|
|
582
|
+
Machine.Machine.Snapshot<States>,
|
|
583
|
+
Machine.Machine.EventOf<InputEvents>,
|
|
584
|
+
MachineRuntimeError<E, R>,
|
|
585
|
+
Output,
|
|
586
|
+
MachineStartError<InitialE, E, InitialR, R>
|
|
587
|
+
>
|
|
588
|
+
} = ((machine: Machine.Machine.Any, ...args: ReadonlyArray<unknown>) => {
|
|
589
|
+
const ref = Atom.make((get) => startMachineAtomEffect(get, machine as any, args as []))
|
|
590
|
+
return makeFromRefAtom(ref as any)
|
|
591
|
+
}) as any
|
|
592
|
+
|
|
593
|
+
export const resume: {
|
|
594
|
+
<M extends Machine.Machine.Any>(
|
|
595
|
+
machine:
|
|
596
|
+
& M
|
|
597
|
+
& EnsureNoExternalRequirements<MachineResumeRequirementsOf<NoInfer<M>>>
|
|
598
|
+
& EnsureMachineExecutable<NoInfer<M>>,
|
|
599
|
+
snapshot: Machine.Machine.Snapshot<Machine.Machine.States<M>>
|
|
600
|
+
): ResumedMachineAtomOf<M, never>
|
|
601
|
+
} = ((machine: Machine.Machine.Any, snapshot: Machine.Machine.Snapshot<any>) => {
|
|
602
|
+
const ref = Atom.make((get) => resumeMachineAtomEffect(get, machine, snapshot))
|
|
603
|
+
return makeFromRefAtom(ref as any)
|
|
604
|
+
}) as any
|
|
605
|
+
|
|
606
|
+
const makeWithRuntime = (
|
|
607
|
+
runtime: Atom.AtomRuntime<any, any>,
|
|
608
|
+
machine: Machine.Machine.Any,
|
|
609
|
+
args: ReadonlyArray<unknown>
|
|
610
|
+
): MachineAtom<any, any, any, any, any> => {
|
|
611
|
+
const ref = runtime.atom((get) => startMachineAtomEffect(get, machine as any, args as []))
|
|
612
|
+
return makeFromRefAtom(ref as any)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const resumeWithRuntime = (
|
|
616
|
+
runtime: Atom.AtomRuntime<any, any>,
|
|
617
|
+
machine: Machine.Machine.Any,
|
|
618
|
+
snapshot: Machine.Machine.Snapshot<any>
|
|
619
|
+
): MachineAtom<any, any, any, any, any> => {
|
|
620
|
+
const ref = runtime.atom((get) => resumeMachineAtomEffect(get, machine, snapshot))
|
|
621
|
+
return makeFromRefAtom(ref as any)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export const bind = <Services, RuntimeError>(
|
|
625
|
+
runtime: Atom.AtomRuntime<Services, RuntimeError>
|
|
626
|
+
): Bound<Services, RuntimeError> => ({
|
|
627
|
+
make:
|
|
628
|
+
((machine: Machine.Machine.Any, ...args: ReadonlyArray<unknown>) =>
|
|
629
|
+
makeWithRuntime(runtime, machine, args)) as Bound<
|
|
630
|
+
Services,
|
|
631
|
+
RuntimeError
|
|
632
|
+
>["make"],
|
|
633
|
+
resume:
|
|
634
|
+
((machine: Machine.Machine.Any, snapshot: Machine.Machine.Snapshot<any>) =>
|
|
635
|
+
resumeWithRuntime(runtime, machine, snapshot)) as Bound<Services, RuntimeError>["resume"]
|
|
636
|
+
})
|