@typeonce/effect-machine 0.19.1 → 0.20.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 +49 -14
- package/dist/Machine.d.ts +93 -2
- package/dist/Machine.d.ts.map +1 -1
- package/dist/Machine.js +11 -0
- package/dist/Machine.js.map +1 -1
- package/dist/internal/machine/atom.d.ts.map +1 -1
- package/dist/internal/machine/atom.js +18 -4
- package/dist/internal/machine/atom.js.map +1 -1
- package/dist/internal/machine/invocation.d.ts.map +1 -1
- package/dist/internal/machine/invocation.js +7 -0
- package/dist/internal/machine/invocation.js.map +1 -1
- package/dist/internal/machine/machine.d.ts +1 -0
- package/dist/internal/machine/machine.d.ts.map +1 -1
- package/dist/internal/machine/machine.js +11 -4
- package/dist/internal/machine/machine.js.map +1 -1
- package/dist/internal/machine/runtime.d.ts +4 -3
- package/dist/internal/machine/runtime.d.ts.map +1 -1
- package/dist/internal/machine/runtime.js +12 -1
- package/dist/internal/machine/runtime.js.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.d.ts +10 -8
- package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
- package/dist/unstable/reactivity/AtomMachine.js +2 -2
- package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
- package/docs/agent-guide.md +31 -0
- package/docs/effect-atom-react.md +28 -0
- package/package.json +1 -1
- package/src/Machine.ts +141 -2
- package/src/internal/machine/atom.ts +26 -18
- package/src/internal/machine/invocation.ts +13 -1
- package/src/internal/machine/machine.ts +18 -6
- package/src/internal/machine/runtime.ts +48 -19
- package/src/unstable/reactivity/AtomMachine.ts +10 -8
package/docs/agent-guide.md
CHANGED
|
@@ -315,6 +315,37 @@ machine.
|
|
|
315
315
|
Do not start a promise inside a transition callback. A transition has no
|
|
316
316
|
lifetime in which to own that work. A state does.
|
|
317
317
|
|
|
318
|
+
### Choose state-owned or process-owned children
|
|
319
|
+
|
|
320
|
+
Use `from.child(...)` when the child belongs to one state and must stop when
|
|
321
|
+
that state exits. Use a child family and `children.spawn(...)` when runtime
|
|
322
|
+
events determine the ids or cardinality and the children must survive owner
|
|
323
|
+
state changes:
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
const Worker = Machine.childFamily(workerMachine)
|
|
327
|
+
|
|
328
|
+
Commissioning: {
|
|
329
|
+
invoke: (from) =>
|
|
330
|
+
from.effect("start-workers", ({ children, state }) =>
|
|
331
|
+
Effect.forEach(
|
|
332
|
+
state.workers,
|
|
333
|
+
(input) => children.spawn(Worker(input.id), { input }),
|
|
334
|
+
{ discard: true }
|
|
335
|
+
)
|
|
336
|
+
)
|
|
337
|
+
.onDone((to) => to.full.Running())
|
|
338
|
+
.onFailure((to) => to.full.Failed())
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The Effect owns the startup attempt. The machine process owns every child that
|
|
343
|
+
starts successfully. Leaving `Commissioning` does not stop those children.
|
|
344
|
+
Stop one with `children.stop(Worker(id))` inside an Effect or
|
|
345
|
+
`enqueue.stop(Worker(id))` inside a transition. A duplicate active id fails
|
|
346
|
+
instead of replacing the existing child, and a partially successful group is
|
|
347
|
+
not rolled back automatically.
|
|
348
|
+
|
|
318
349
|
## Keep transition decisions synchronous
|
|
319
350
|
|
|
320
351
|
A transition should choose the next state from the current snapshot and event.
|
|
@@ -200,3 +200,31 @@ the inferred dialog events.
|
|
|
200
200
|
|
|
201
201
|
Use `dialogId` in atom labels for diagnostics. Do not pass it into
|
|
202
202
|
`dialogMachine` as unused fake input.
|
|
203
|
+
|
|
204
|
+
## 4. Selecting process-owned child machines
|
|
205
|
+
|
|
206
|
+
Bind a machine definition once when a parent owns a runtime-sized set of child
|
|
207
|
+
machines:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const Plant = Machine.childFamily(plantMachine)
|
|
211
|
+
|
|
212
|
+
export const centralMachineAtom = machineAtoms.make(centralMachine)
|
|
213
|
+
|
|
214
|
+
export const plantScopeFamily = Atom.family((plantId: string) => {
|
|
215
|
+
const plant = centralMachineAtom.child(Plant(plantId))
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
stateAtom: plant.state,
|
|
219
|
+
isBrokenAtom: AtomMachine.matchesChild(plant, "Broken"),
|
|
220
|
+
sendAtom: plant.send,
|
|
221
|
+
stopAtom: plant.stop
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`Plant(plantId)` may be reconstructed wherever the id is available. Child
|
|
227
|
+
lookup and bridge reuse match by machine identity and id, not descriptor object
|
|
228
|
+
identity. Before the parent spawns that child, selectors contain `Option.none`
|
|
229
|
+
and `matchesChild` is `false`. They follow the child after startup and return to
|
|
230
|
+
the inactive values after it stops.
|
package/package.json
CHANGED
package/src/Machine.ts
CHANGED
|
@@ -2265,7 +2265,15 @@ export declare namespace Logic {
|
|
|
2265
2265
|
* @category models
|
|
2266
2266
|
* @since 0.4.0
|
|
2267
2267
|
*/
|
|
2268
|
-
export interface Spawn {
|
|
2268
|
+
export interface Spawn<OwnerEvent = unknown> {
|
|
2269
|
+
<const Child extends ChildMachine.Any>(
|
|
2270
|
+
child: Child & ChildMachine.Executable<Child> & ChildMachine.ParentCompatibility<Child, OwnerEvent>,
|
|
2271
|
+
...options: ChildMachine.SpawnArgs<Child>
|
|
2272
|
+
): Effect.Effect<
|
|
2273
|
+
ChildMachine.Ref<Child>,
|
|
2274
|
+
ChildAlreadyExistsError | ChildMachine.StartError<Child>,
|
|
2275
|
+
ChildMachine.StartRequirements<Child>
|
|
2276
|
+
>
|
|
2269
2277
|
<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError = never>(
|
|
2270
2278
|
logic: Logic<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError>
|
|
2271
2279
|
): Effect.Effect<
|
|
@@ -2305,7 +2313,7 @@ export declare namespace Logic {
|
|
|
2305
2313
|
readonly parent: Address<unknown> | undefined
|
|
2306
2314
|
|
|
2307
2315
|
/** Starts a child process owned by this scope. */
|
|
2308
|
-
readonly spawn: Spawn
|
|
2316
|
+
readonly spawn: Spawn<Event>
|
|
2309
2317
|
|
|
2310
2318
|
/** Sends an event to a machine target or typed parent-local child address. */
|
|
2311
2319
|
readonly sendTo: {
|
|
@@ -2345,6 +2353,7 @@ export declare namespace Logic {
|
|
|
2345
2353
|
|
|
2346
2354
|
const ChildAddressTypeId = "~effect/Machine/ChildAddress"
|
|
2347
2355
|
const ChildAddressCompatibilityErrorTypeId = "~effect/Machine/ChildAddressCompatibilityError"
|
|
2356
|
+
const ChildParentCompatibilityErrorTypeId = "~effect/Machine/ChildParentCompatibilityError"
|
|
2348
2357
|
const ChildMachineTypeId = "~effect/Machine/ChildMachine"
|
|
2349
2358
|
type InvokeLifecycleId = string & { readonly [ChildAddressTypeId]?: never }
|
|
2350
2359
|
|
|
@@ -2389,6 +2398,95 @@ export declare namespace ChildMachine {
|
|
|
2389
2398
|
*/
|
|
2390
2399
|
export type Any = ChildMachine<string, Machine.Any>
|
|
2391
2400
|
|
|
2401
|
+
/**
|
|
2402
|
+
* Bound constructor for an open family of child descriptors that share one
|
|
2403
|
+
* machine definition.
|
|
2404
|
+
*
|
|
2405
|
+
* @category models
|
|
2406
|
+
* @since 0.20.0
|
|
2407
|
+
*/
|
|
2408
|
+
export interface Family<M extends Machine.Any> {
|
|
2409
|
+
<const Id extends string>(id: Id): ChildMachine<Id, M>
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
/**
|
|
2413
|
+
* Ensures a child machine's declared owner protocol is accepted by the
|
|
2414
|
+
* process that will own it.
|
|
2415
|
+
*
|
|
2416
|
+
* @category utility types
|
|
2417
|
+
* @since 0.20.0
|
|
2418
|
+
*/
|
|
2419
|
+
export type ParentCompatibility<Child extends Any, OwnerEvent> = Child extends ChildMachine<string, infer M> ?
|
|
2420
|
+
Machine.Any extends M ? {
|
|
2421
|
+
readonly [ChildParentCompatibilityErrorTypeId]: {
|
|
2422
|
+
readonly child: unknown
|
|
2423
|
+
readonly owner: OwnerEvent
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
: [Machine.EventOf<Machine.ParentEvents<M>>] extends [OwnerEvent] ? unknown :
|
|
2427
|
+
{
|
|
2428
|
+
readonly [ChildParentCompatibilityErrorTypeId]: {
|
|
2429
|
+
readonly child: Machine.EventOf<Machine.ParentEvents<M>>
|
|
2430
|
+
readonly owner: OwnerEvent
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
: never
|
|
2434
|
+
|
|
2435
|
+
/**
|
|
2436
|
+
* Ensures the selected child machine has complete handlers and outputs.
|
|
2437
|
+
*
|
|
2438
|
+
* @category utility types
|
|
2439
|
+
* @since 0.20.0
|
|
2440
|
+
*/
|
|
2441
|
+
export type Executable<Child extends Any> = Child["machine"] extends EnsureExecutable<
|
|
2442
|
+
Machine.States<Child["machine"]>,
|
|
2443
|
+
Machine.UnhandledStates<Child["machine"]>,
|
|
2444
|
+
Machine.OutputStates<Child["machine"]>
|
|
2445
|
+
> ? unknown
|
|
2446
|
+
: never
|
|
2447
|
+
|
|
2448
|
+
/**
|
|
2449
|
+
* Startup arguments accepted while spawning a child machine.
|
|
2450
|
+
*
|
|
2451
|
+
* @category utility types
|
|
2452
|
+
* @since 0.20.0
|
|
2453
|
+
*/
|
|
2454
|
+
export type SpawnArgs<Child extends Any> = Machine.InputSchema<Child["machine"]> extends typeof Schema.Void ?
|
|
2455
|
+
[options?: { readonly input?: never }]
|
|
2456
|
+
: [options: { readonly input: Machine.Input<Child["machine"]> }]
|
|
2457
|
+
|
|
2458
|
+
/**
|
|
2459
|
+
* Typed failures that may occur before a spawned child becomes active.
|
|
2460
|
+
*
|
|
2461
|
+
* @category utility types
|
|
2462
|
+
* @since 0.20.0
|
|
2463
|
+
*/
|
|
2464
|
+
export type StartError<Child extends Any> = Child extends ChildMachine<string, infer M> ?
|
|
2465
|
+
| Machine.InitialError<M>
|
|
2466
|
+
| Machine.Error<M>
|
|
2467
|
+
| ActionError<Machine.InitialServices<M> | Machine.Services<M>>
|
|
2468
|
+
| InfiniteTransitionError
|
|
2469
|
+
| MachineSchemaDecodeError
|
|
2470
|
+
| StartupError
|
|
2471
|
+
| StoppedError
|
|
2472
|
+
: never
|
|
2473
|
+
|
|
2474
|
+
/**
|
|
2475
|
+
* Services needed to initialize a spawned child machine.
|
|
2476
|
+
*
|
|
2477
|
+
* @category utility types
|
|
2478
|
+
* @since 0.20.0
|
|
2479
|
+
*/
|
|
2480
|
+
export type StartRequirements<Child extends Any> = Child extends ChildMachine<string, infer M> ? Exclude<
|
|
2481
|
+
ExcludeCompatibleRuntime<
|
|
2482
|
+
Exclude<ExecutionServices<Machine.InitialServices<M> | Machine.Services<M>>, MachineRuntimeRequirement>,
|
|
2483
|
+
Machine.Event<M>,
|
|
2484
|
+
Machine.Emit<M>
|
|
2485
|
+
>,
|
|
2486
|
+
Scope.Scope
|
|
2487
|
+
>
|
|
2488
|
+
: never
|
|
2489
|
+
|
|
2392
2490
|
/**
|
|
2393
2491
|
* Running machine reference selected by a child descriptor.
|
|
2394
2492
|
*
|
|
@@ -2418,6 +2516,34 @@ export declare namespace ChildMachine {
|
|
|
2418
2516
|
: never
|
|
2419
2517
|
}
|
|
2420
2518
|
|
|
2519
|
+
/**
|
|
2520
|
+
* Effectful operations for child machines owned directly by the current
|
|
2521
|
+
* machine process.
|
|
2522
|
+
*
|
|
2523
|
+
* @category models
|
|
2524
|
+
* @since 0.20.0
|
|
2525
|
+
*/
|
|
2526
|
+
export interface ChildOwner<OwnerEvent> {
|
|
2527
|
+
/** Starts a process-owned child and returns once initialization succeeds. */
|
|
2528
|
+
readonly spawn: <const Child extends ChildMachine.Any>(
|
|
2529
|
+
child: Child & ChildMachine.Executable<Child> & ChildMachine.ParentCompatibility<Child, OwnerEvent>,
|
|
2530
|
+
...options: ChildMachine.SpawnArgs<Child>
|
|
2531
|
+
) => Effect.Effect<
|
|
2532
|
+
ChildMachine.Ref<Child>,
|
|
2533
|
+
ChildAlreadyExistsError | ChildMachine.StartError<Child>,
|
|
2534
|
+
ChildMachine.StartRequirements<Child>
|
|
2535
|
+
>
|
|
2536
|
+
|
|
2537
|
+
/** Sends an event to one active child. Missing children are ignored. */
|
|
2538
|
+
readonly sendTo: <Child extends ChildMachine.Any>(
|
|
2539
|
+
child: Child,
|
|
2540
|
+
event: ChildMachine.Event<Child>
|
|
2541
|
+
) => Effect.Effect<void, StoppedError>
|
|
2542
|
+
|
|
2543
|
+
/** Stops one active child. Missing children are ignored. */
|
|
2544
|
+
readonly stop: <Child extends ChildMachine.Any>(child: Child) => Effect.Effect<void>
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2421
2547
|
/**
|
|
2422
2548
|
* Parent-local address for a child process that can receive events.
|
|
2423
2549
|
*
|
|
@@ -4799,6 +4925,8 @@ export declare namespace Machine {
|
|
|
4799
4925
|
InputEvents extends ReadonlyArray<TaggedSchema> = Events,
|
|
4800
4926
|
ParentEvents extends ReadonlyArray<TaggedSchema> = readonly []
|
|
4801
4927
|
> = MachineReferences<InputEvents, ParentEvents> & {
|
|
4928
|
+
/** Process-owned child operations for dynamic child machine lifecycles. */
|
|
4929
|
+
readonly children: ChildOwner<EventOf<InputEvents>>
|
|
4802
4930
|
/** Value owned by the state that owns this invocation. */
|
|
4803
4931
|
readonly state: StateByIdentifier<States, StateId>
|
|
4804
4932
|
/** Value owned by the nearest schema-backed ancestor, when one exists. */
|
|
@@ -8917,6 +9045,17 @@ export const logic: <
|
|
|
8917
9045
|
export const child: <const Id extends string, M extends Machine.Any>(id: Id, machine: M) => ChildMachine<Id, M> =
|
|
8918
9046
|
internal.child
|
|
8919
9047
|
|
|
9048
|
+
/**
|
|
9049
|
+
* Binds one machine definition to an open family of runtime child ids.
|
|
9050
|
+
*
|
|
9051
|
+
* Descriptors created by the returned function are interchangeable with
|
|
9052
|
+
* {@link child} descriptors for the same id and machine definition.
|
|
9053
|
+
*
|
|
9054
|
+
* @category constructors
|
|
9055
|
+
* @since 0.20.0
|
|
9056
|
+
*/
|
|
9057
|
+
export const childFamily: <M extends Machine.Any>(machine: M) => ChildMachine.Family<M> = internal.childFamily
|
|
9058
|
+
|
|
8920
9059
|
/**
|
|
8921
9060
|
* Creates a typed parent-local address for lower-level child process logic.
|
|
8922
9061
|
*
|
|
@@ -342,15 +342,7 @@ const makeChildFromRefAtom = <Child extends Machine.ChildMachine.Any, StartError
|
|
|
342
342
|
}
|
|
343
343
|
)
|
|
344
344
|
|
|
345
|
-
const
|
|
346
|
-
makeChildFromRefAtom(
|
|
347
|
-
makeChildRefAtom(ref as any, nested),
|
|
348
|
-
nested
|
|
349
|
-
)
|
|
350
|
-
)
|
|
351
|
-
const child = <Nested extends Machine.ChildMachine.Any>(
|
|
352
|
-
nested: Nested
|
|
353
|
-
): ChildMachineAtom<Nested, StartError> => childFamily(nested) as ChildMachineAtom<Nested, StartError>
|
|
345
|
+
const child = makeChildSelector<StartError>(ref as any)
|
|
354
346
|
|
|
355
347
|
return {
|
|
356
348
|
ref,
|
|
@@ -363,6 +355,30 @@ const makeChildFromRefAtom = <Child extends Machine.ChildMachine.Any, StartError
|
|
|
363
355
|
}
|
|
364
356
|
}
|
|
365
357
|
|
|
358
|
+
const makeChildSelector = <StartError>(
|
|
359
|
+
parentRef: Atom.Atom<
|
|
360
|
+
AsyncResult.AsyncResult<Option.Option<Machine.MachineRef<any, any, any, any, any>>, StartError>
|
|
361
|
+
>
|
|
362
|
+
) => {
|
|
363
|
+
const byMachine = new WeakMap<object, (id: string) => ChildMachineAtom<Machine.ChildMachine.Any, StartError>>()
|
|
364
|
+
return <Child extends Machine.ChildMachine.Any>(descriptor: Child): ChildMachineAtom<Child, StartError> => {
|
|
365
|
+
let family = byMachine.get(descriptor.machine)
|
|
366
|
+
if (family === undefined) {
|
|
367
|
+
const machine = descriptor.machine
|
|
368
|
+
const atoms = Atom.family((id: string) => {
|
|
369
|
+
const child = internalMachine.child(id, machine)
|
|
370
|
+
return makeChildFromRefAtom(
|
|
371
|
+
makeChildRefAtom(parentRef as any, child),
|
|
372
|
+
child
|
|
373
|
+
)
|
|
374
|
+
})
|
|
375
|
+
family = (id) => atoms(id) as ChildMachineAtom<Machine.ChildMachine.Any, StartError>
|
|
376
|
+
byMachine.set(machine, family)
|
|
377
|
+
}
|
|
378
|
+
return family(descriptor.id) as ChildMachineAtom<Child, StartError>
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
366
382
|
const makeFromRefAtom = <State, Event, Error, Output, StartError, Emitted>(
|
|
367
383
|
ref: Atom.Atom<AsyncResult.AsyncResult<Machine.MachineRef<State, Event, Error, Output, Emitted>, StartError>>
|
|
368
384
|
): MachineAtom<State, Event, Error, Output, StartError, Emitted> => {
|
|
@@ -438,15 +454,7 @@ const makeFromRefAtom = <State, Event, Error, Output, StartError, Emitted>(
|
|
|
438
454
|
)
|
|
439
455
|
|
|
440
456
|
const optionalRef = Atom.mapResult(ref, Option.some)
|
|
441
|
-
const
|
|
442
|
-
makeChildFromRefAtom(
|
|
443
|
-
makeChildRefAtom(optionalRef as any, descriptor),
|
|
444
|
-
descriptor
|
|
445
|
-
)
|
|
446
|
-
)
|
|
447
|
-
const child = <Child extends Machine.ChildMachine.Any>(
|
|
448
|
-
descriptor: Child
|
|
449
|
-
): ChildMachineAtom<Child, StartError> => childFamily(descriptor) as ChildMachineAtom<Child, StartError>
|
|
457
|
+
const child = makeChildSelector<StartError>(optionalRef as any)
|
|
450
458
|
|
|
451
459
|
return {
|
|
452
460
|
ref,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import * as Cause from "effect/Cause"
|
|
8
8
|
import * as Effect from "effect/Effect"
|
|
9
9
|
import * as Stream from "effect/Stream"
|
|
10
|
-
import type { ChildMachine, Inspection, Logic, Machine } from "../../Machine.js"
|
|
10
|
+
import type { ChildMachine, ChildOwner, Inspection, Logic, Machine } from "../../Machine.js"
|
|
11
11
|
import * as Configuration from "./configuration.js"
|
|
12
12
|
import { InfiniteTransitionError, MachineSchemaDecodeError, StoppedError } from "./errors.js"
|
|
13
13
|
import * as InvocationEvent from "./invocationEvent.js"
|
|
@@ -63,6 +63,16 @@ const streamLogic = (
|
|
|
63
63
|
const resolveValue = (value: unknown, context: Machine.InvokeContext<any, any, any, any>): unknown =>
|
|
64
64
|
typeof value === "function" ? value(context) : value
|
|
65
65
|
|
|
66
|
+
const makeChildOwner = (scope: Runtime.ProcessScope<any>): ChildOwner<any> => ({
|
|
67
|
+
spawn:
|
|
68
|
+
((descriptor: ChildMachine.Any, options?: { readonly input?: unknown }) =>
|
|
69
|
+
(scope.spawn as any)(descriptor, options)) as ChildOwner<any>["spawn"],
|
|
70
|
+
sendTo: ((descriptor: ChildMachine.Any, event: unknown) => scope.sendTo(descriptor, event)) as ChildOwner<
|
|
71
|
+
any
|
|
72
|
+
>["sendTo"],
|
|
73
|
+
stop: ((descriptor: ChildMachine.Any) => scope.stopChild(descriptor)) as ChildOwner<any>["stop"]
|
|
74
|
+
})
|
|
75
|
+
|
|
66
76
|
const resolveOne = (
|
|
67
77
|
raw: Record<PropertyKey, any>,
|
|
68
78
|
context: Machine.InvokeContext<any, any, any, any>,
|
|
@@ -291,11 +301,13 @@ export const startAll = (
|
|
|
291
301
|
paths: ReadonlyArray<string>,
|
|
292
302
|
event: Machine.LifecycleEvent<any>
|
|
293
303
|
): Effect.Effect<void, any, any> | undefined => {
|
|
304
|
+
const children = makeChildOwner(scope)
|
|
294
305
|
const effects = Planner.sortEntryPaths(machine, paths)
|
|
295
306
|
.filter((path) => configuration.active.has(path))
|
|
296
307
|
.flatMap((path) => {
|
|
297
308
|
const context = {
|
|
298
309
|
...(Configuration.getMachineReferences(configuration) ?? { self: scope.self, parent: scope.parent }),
|
|
310
|
+
children,
|
|
299
311
|
state: configuration.values.get(path),
|
|
300
312
|
containingState: Configuration.getParentValue(machine, configuration, path),
|
|
301
313
|
ancestors: Configuration.getParentValues(machine, configuration, path),
|
|
@@ -2124,15 +2124,30 @@ export const transition = <State, Event, Error = never, Requirements = never>(
|
|
|
2124
2124
|
export const child = <const Id extends string, M extends Machine.Any>(
|
|
2125
2125
|
id: Id,
|
|
2126
2126
|
machine: M
|
|
2127
|
+
): ChildMachine<Id, M> =>
|
|
2128
|
+
makeChild(id, machine, (input) =>
|
|
2129
|
+
machine.input === undefined
|
|
2130
|
+
? (internalProcess.toProcessLogic as any)(machine)
|
|
2131
|
+
: (internalProcess.toProcessLogic as any)(machine, input))
|
|
2132
|
+
|
|
2133
|
+
const makeChild = <const Id extends string, M extends Machine.Any>(
|
|
2134
|
+
id: Id,
|
|
2135
|
+
machine: M,
|
|
2136
|
+
makeLogic: (input?: unknown) => Logic<any, any, any, any, any, any>
|
|
2127
2137
|
): ChildMachine<Id, M> => ({
|
|
2128
2138
|
[ChildMachineTypeId]: ChildMachineTypeId,
|
|
2129
2139
|
id,
|
|
2130
2140
|
machine,
|
|
2131
|
-
[ChildMachineLogicTypeId]:
|
|
2141
|
+
[ChildMachineLogicTypeId]: makeLogic
|
|
2142
|
+
})
|
|
2143
|
+
|
|
2144
|
+
export const childFamily = <M extends Machine.Any>(machine: M): ChildMachine.Family<M> => {
|
|
2145
|
+
const makeLogic = (input?: unknown): Logic<any, any, any, any, any, any> =>
|
|
2132
2146
|
machine.input === undefined
|
|
2133
2147
|
? (internalProcess.toProcessLogic as any)(machine)
|
|
2134
2148
|
: (internalProcess.toProcessLogic as any)(machine, input)
|
|
2135
|
-
|
|
2149
|
+
return (id) => makeChild(id, machine, makeLogic)
|
|
2150
|
+
}
|
|
2136
2151
|
|
|
2137
2152
|
export const childAddress = <Event = never>(id: string): ChildAddress<Event> => id as ChildAddress<Event>
|
|
2138
2153
|
|
|
@@ -2174,10 +2189,7 @@ export const spawn: {
|
|
|
2174
2189
|
SpawnError<Options>,
|
|
2175
2190
|
ChildInitialError
|
|
2176
2191
|
>
|
|
2177
|
-
} = ((
|
|
2178
|
-
logic: Logic<any, any, any, any, any, any>,
|
|
2179
|
-
options?: SpawnOptions
|
|
2180
|
-
) =>
|
|
2192
|
+
} = ((logic: Logic<any, any, any, any, any, any>, options?: SpawnOptions) =>
|
|
2181
2193
|
Effect.flatMap(
|
|
2182
2194
|
internalRuntime.MachineRuntime,
|
|
2183
2195
|
(runtime) => options === undefined ? runtime.spawn(logic) : (runtime.spawn as any)(logic, options)
|
|
@@ -18,9 +18,10 @@ import * as Scope from "effect/Scope"
|
|
|
18
18
|
import * as Stream from "effect/Stream"
|
|
19
19
|
import * as SynchronizedRef from "effect/SynchronizedRef"
|
|
20
20
|
import type * as Take from "effect/Take"
|
|
21
|
-
import type { Inspection, Machine as MachineDefinition, MachineTarget } from "../../Machine.js"
|
|
21
|
+
import type { ChildMachine, Inspection, Machine as MachineDefinition, MachineTarget } from "../../Machine.js"
|
|
22
22
|
import { ChildAlreadyExistsError, StoppedError } from "./errors.js"
|
|
23
23
|
import * as InspectionRuntime from "./inspectionRuntime.js"
|
|
24
|
+
import { ChildMachineLogicTypeId } from "./symbols.js"
|
|
24
25
|
|
|
25
26
|
type ChildDescriptor = {
|
|
26
27
|
readonly id: string
|
|
@@ -376,7 +377,7 @@ const sendMachineTarget = (
|
|
|
376
377
|
export interface ProcessScope<Event> {
|
|
377
378
|
readonly self: ProcessAddress<Event>
|
|
378
379
|
readonly parent: ProcessAddress<unknown> | undefined
|
|
379
|
-
readonly spawn: ProcessSpawn
|
|
380
|
+
readonly spawn: ProcessSpawn<Event>
|
|
380
381
|
readonly sendParent: (event: unknown) => Effect.Effect<void, StoppedError>
|
|
381
382
|
readonly emit: (event: unknown) => Effect.Effect<void>
|
|
382
383
|
readonly sendTo: {
|
|
@@ -552,7 +553,15 @@ export interface ProcessLogic<
|
|
|
552
553
|
run(context: ProcessContext<State, Event>): Effect.Effect<Output, Error, Requirements>
|
|
553
554
|
}
|
|
554
555
|
|
|
555
|
-
export interface ProcessSpawn {
|
|
556
|
+
export interface ProcessSpawn<OwnerEvent = unknown> {
|
|
557
|
+
<const Child extends ChildMachine.Any>(
|
|
558
|
+
child: Child & ChildMachine.Executable<Child> & ChildMachine.ParentCompatibility<Child, OwnerEvent>,
|
|
559
|
+
...options: ChildMachine.SpawnArgs<Child>
|
|
560
|
+
): Effect.Effect<
|
|
561
|
+
ChildMachine.Ref<Child>,
|
|
562
|
+
ChildAlreadyExistsError | ChildMachine.StartError<Child>,
|
|
563
|
+
ChildMachine.StartRequirements<Child>
|
|
564
|
+
>
|
|
556
565
|
<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError = never>(
|
|
557
566
|
logic: ProcessLogic<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError>
|
|
558
567
|
): Effect.Effect<
|
|
@@ -1207,6 +1216,14 @@ const makeChildRuntimeSync = (
|
|
|
1207
1216
|
})
|
|
1208
1217
|
}
|
|
1209
1218
|
|
|
1219
|
+
function spawn<const Child extends ChildMachine.Any>(
|
|
1220
|
+
child: Child & ChildMachine.Executable<Child> & ChildMachine.ParentCompatibility<Child, unknown>,
|
|
1221
|
+
...options: ChildMachine.SpawnArgs<Child>
|
|
1222
|
+
): Effect.Effect<
|
|
1223
|
+
ChildMachine.Ref<Child>,
|
|
1224
|
+
ChildAlreadyExistsError | ChildMachine.StartError<Child>,
|
|
1225
|
+
ChildMachine.StartRequirements<Child>
|
|
1226
|
+
>
|
|
1210
1227
|
function spawn<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError = never>(
|
|
1211
1228
|
logic: ProcessLogic<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError>
|
|
1212
1229
|
): Effect.Effect<
|
|
@@ -1232,32 +1249,44 @@ const makeChildRuntimeSync = (
|
|
|
1232
1249
|
ChildAlreadyExistsError | ChildInitialError,
|
|
1233
1250
|
Exclude<ChildRequirements, Scope.Scope>
|
|
1234
1251
|
>
|
|
1235
|
-
function spawn
|
|
1236
|
-
|
|
1237
|
-
|
|
1252
|
+
function spawn(
|
|
1253
|
+
logicOrChild: ProcessLogic<any, any, any, any, any, any> | ChildMachine.Any,
|
|
1254
|
+
options?: {
|
|
1238
1255
|
readonly id: string
|
|
1239
1256
|
readonly descriptor?: ChildDescriptor
|
|
1240
1257
|
readonly onOutcome?: (
|
|
1241
|
-
outcome: RuntimeOutcome<
|
|
1258
|
+
outcome: RuntimeOutcome<any, any, any>
|
|
1242
1259
|
) => Effect.Effect<void>
|
|
1243
1260
|
readonly [activeSnapshotObserver]?: (
|
|
1244
|
-
snapshot: Extract<RuntimeSnapshot<
|
|
1261
|
+
snapshot: Extract<RuntimeSnapshot<any, any, any>, { readonly status: "active" }>
|
|
1245
1262
|
) => Effect.Effect<void>
|
|
1246
1263
|
readonly [sendParentOverride]?: (event: unknown) => Effect.Effect<void, StoppedError>
|
|
1247
|
-
}
|
|
1248
|
-
): Effect.Effect<
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1264
|
+
} | { readonly input?: unknown }
|
|
1265
|
+
): Effect.Effect<MachineRef<any, any, any, any>, any, any> {
|
|
1266
|
+
const descriptor = typeof logicOrChild === "object" && logicOrChild !== null &&
|
|
1267
|
+
ChildMachineLogicTypeId in logicOrChild
|
|
1268
|
+
? logicOrChild as ChildMachine.Any
|
|
1269
|
+
: undefined
|
|
1270
|
+
const logic = descriptor === undefined
|
|
1271
|
+
? logicOrChild as ProcessLogic<any, any, any, any, any, any>
|
|
1272
|
+
: descriptor[ChildMachineLogicTypeId](
|
|
1273
|
+
(options as { readonly input?: unknown } | undefined)?.input
|
|
1274
|
+
) as unknown as ProcessLogic<any, any, any, any, any, any>
|
|
1275
|
+
const spawnOptions = descriptor === undefined
|
|
1276
|
+
? options as {
|
|
1277
|
+
readonly id: string
|
|
1278
|
+
readonly descriptor?: ChildDescriptor
|
|
1279
|
+
readonly onOutcome?: (outcome: RuntimeOutcome<any, any, any>) => Effect.Effect<void>
|
|
1280
|
+
readonly [activeSnapshotObserver]?: (
|
|
1281
|
+
snapshot: Extract<RuntimeSnapshot<any, any, any>, { readonly status: "active" }>
|
|
1282
|
+
) => Effect.Effect<void>
|
|
1283
|
+
readonly [sendParentOverride]?: (event: unknown) => Effect.Effect<void, StoppedError>
|
|
1284
|
+
} | undefined
|
|
1285
|
+
: { id: descriptor.id, descriptor }
|
|
1253
1286
|
const token = Symbol()
|
|
1254
1287
|
const key = spawnOptions?.id ?? token
|
|
1255
1288
|
let startedChild: MachineRef<any, any, any, any> | undefined
|
|
1256
|
-
return Effect.suspend(()
|
|
1257
|
-
MachineRef<ChildState, ChildEvent, ChildError, ChildOutput>,
|
|
1258
|
-
ChildAlreadyExistsError | ChildInitialError,
|
|
1259
|
-
Exclude<ChildRequirements, Scope.Scope>
|
|
1260
|
-
> => {
|
|
1289
|
+
return Effect.suspend(() => {
|
|
1261
1290
|
if (registry.closed) {
|
|
1262
1291
|
return Effect.interrupt
|
|
1263
1292
|
}
|
|
@@ -148,9 +148,9 @@ export interface MachineAtom<State, Event, Error = never, Output = never, StartE
|
|
|
148
148
|
readonly stop: Atom.Writable<AsyncResult.AsyncResult<void, StartError | NotReadyError>, void>
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
|
-
* Creates a reactive bridge for a directly
|
|
152
|
-
*
|
|
153
|
-
* referenced.
|
|
151
|
+
* Creates a reactive bridge for a directly owned child machine.
|
|
152
|
+
* Descriptors with the same id and machine definition return the same live
|
|
153
|
+
* bridge while it remains referenced.
|
|
154
154
|
*
|
|
155
155
|
* @since 0.4.0
|
|
156
156
|
*/
|
|
@@ -206,7 +206,8 @@ export const childEmissions: <Child extends Machine.ChildMachine.Any, StartError
|
|
|
206
206
|
> = internal.childEmissions
|
|
207
207
|
|
|
208
208
|
/**
|
|
209
|
-
* Reactive access to one
|
|
209
|
+
* Reactive access to one directly owned child machine selected by its
|
|
210
|
+
* descriptor.
|
|
210
211
|
*
|
|
211
212
|
* **Details**
|
|
212
213
|
*
|
|
@@ -295,8 +296,9 @@ export interface ChildMachineAtom<Child extends Machine.ChildMachine.Any, StartE
|
|
|
295
296
|
void
|
|
296
297
|
>
|
|
297
298
|
/**
|
|
298
|
-
* Creates a reactive bridge for a directly owned nested child.
|
|
299
|
-
* same
|
|
299
|
+
* Creates a reactive bridge for a directly owned nested child. Descriptors
|
|
300
|
+
* with the same id and machine definition return the same live bridge while
|
|
301
|
+
* it remains referenced.
|
|
300
302
|
*
|
|
301
303
|
* @since 0.4.0
|
|
302
304
|
*/
|
|
@@ -418,7 +420,7 @@ export const selectSnapshot: <
|
|
|
418
420
|
> = internal.selectSnapshot
|
|
419
421
|
|
|
420
422
|
/**
|
|
421
|
-
* Selects the typed value for an active state path in
|
|
423
|
+
* Selects the typed value for an active state path in a directly owned child.
|
|
422
424
|
*
|
|
423
425
|
* Valid paths and their selected value types are inferred from the child
|
|
424
426
|
* bridge. An inactive child produces `Option.none()`. Keep the returned atom
|
|
@@ -513,7 +515,7 @@ export const matches: <
|
|
|
513
515
|
) => Atom.Atom<AsyncResult.AsyncResult<boolean, StartError | Error>> = internal.matches
|
|
514
516
|
|
|
515
517
|
/**
|
|
516
|
-
* Returns whether a state path is active in
|
|
518
|
+
* Returns whether a state path is active in a directly owned child.
|
|
517
519
|
*
|
|
518
520
|
* Valid paths are inferred from the child bridge snapshot.
|
|
519
521
|
* An inactive child produces `false`. Keep the returned atom stable when
|