@typeonce/effect-machine 0.19.0 → 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 +50 -15
- 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 +302 -1449
- package/docs/effect-atom-react.md +230 -0
- package/package.json +4 -4
- 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/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Cluster and are exposed only through explicit integration boundaries.
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
36
36
|
```sh
|
|
37
|
-
pnpm add @typeonce/effect-machine effect@4.0.0-rc.
|
|
37
|
+
pnpm add @typeonce/effect-machine effect@4.0.0-rc.111
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
`effect` is an exact peer dependency. Install the version above and upgrade it
|
|
@@ -582,6 +582,46 @@ entered. Use an Effect containing `Effect.sleep(...)` for generic work, while
|
|
|
582
582
|
`from.timer(...)` keeps timer intent explicit and makes static durations visible
|
|
583
583
|
through activity inspection.
|
|
584
584
|
|
|
585
|
+
### Spawn dynamic child machines
|
|
586
|
+
|
|
587
|
+
Use `from.child(...)` when a state owns a fixed child lifecycle. Use the
|
|
588
|
+
`children` context inside an invoked Effect when the machine process owns an
|
|
589
|
+
open set of children that must survive state changes:
|
|
590
|
+
|
|
591
|
+
```ts
|
|
592
|
+
const Plant = Machine.childFamily(plantMachine)
|
|
593
|
+
|
|
594
|
+
const central = Machine.make({
|
|
595
|
+
events: Machine.events(ResourcesOffered, PlantBroken)
|
|
596
|
+
// ...
|
|
597
|
+
}).handle({
|
|
598
|
+
Commissioning: {
|
|
599
|
+
invoke: (from) =>
|
|
600
|
+
from.effect("commission-wave", ({ children, state }) =>
|
|
601
|
+
Effect.forEach(
|
|
602
|
+
state.plants,
|
|
603
|
+
(input) => children.spawn(Plant(input.id), { input }),
|
|
604
|
+
{ discard: true }
|
|
605
|
+
))
|
|
606
|
+
.onDone((to) => to.full.Operating())
|
|
607
|
+
.onFailure((to) => to.full.CommissioningFailed())
|
|
608
|
+
}
|
|
609
|
+
})
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
`children.spawn` completes after initialization. The new child remains owned
|
|
613
|
+
by the machine process after the commissioning Effect completes or its state
|
|
614
|
+
exits. `children.sendTo` and `children.stop` address one active child from an
|
|
615
|
+
Effect; transition resolvers use `enqueue.sendTo` and `enqueue.stop` with the
|
|
616
|
+
same descriptor. Duplicate active ids fail with `ChildAlreadyExistsError` and
|
|
617
|
+
do not replace the existing child. Earlier successful spawns remain active if
|
|
618
|
+
a later spawn in the same wave fails.
|
|
619
|
+
|
|
620
|
+
The child machine's declared `Machine.parent(...)` events must be accepted by
|
|
621
|
+
the owner. This is checked at each spawn call even though ids and cardinality
|
|
622
|
+
remain dynamic. `scope.spawn(child, { input })` provides the same descriptor
|
|
623
|
+
form for lower-level process logic, where the process event protocol is known.
|
|
624
|
+
|
|
585
625
|
## Reactivity
|
|
586
626
|
|
|
587
627
|
`AtomMachine` runs one lazy machine instance per `AtomRegistry`:
|
|
@@ -603,6 +643,15 @@ The bridge exposes `ref`, `snapshot`, `state`, fail-aware `result`, writable
|
|
|
603
643
|
equality-aware derivations. React applications using `@effect/atom-react` need
|
|
604
644
|
a `RegistryProvider`.
|
|
605
645
|
|
|
646
|
+
Descriptors reconstructed from a `Machine.childFamily` resolve the same child
|
|
647
|
+
bridge by machine identity and id:
|
|
648
|
+
|
|
649
|
+
```ts
|
|
650
|
+
const Plant = Machine.childFamily(plantMachine)
|
|
651
|
+
const plantAtom = centralAtom.child(Plant(selectedPlantId))
|
|
652
|
+
const brokenAtom = AtomMachine.matchesChild(plantAtom, "Broken")
|
|
653
|
+
```
|
|
654
|
+
|
|
606
655
|
Emissions stay streams rather than becoming retained atom state:
|
|
607
656
|
|
|
608
657
|
```ts
|
|
@@ -679,20 +728,6 @@ import { MachineTest } from "@typeonce/effect-machine/testing"
|
|
|
679
728
|
|
|
680
729
|
Each ESM entrypoint is independent and tree-shakeable.
|
|
681
730
|
|
|
682
|
-
## Examples
|
|
683
|
-
|
|
684
|
-
Every package directly under [`examples/`](./examples) has its own lockfile and
|
|
685
|
-
`check` script.
|
|
686
|
-
|
|
687
|
-
| Example | What it demonstrates |
|
|
688
|
-
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
689
|
-
| [Playground](./examples/playground) | Five focused React examples: atomic turnstile commands, state-scoped traffic-light timers, hierarchical microwave safety, a resource-owned media player, and a worker-hosted machine synchronized across tabs |
|
|
690
|
-
| [Pokémon](./examples/pokemon) | Compound workflow states, invoked child machines, typed emissions, Atom reactivity, and a live Effect service |
|
|
691
|
-
| [Platformer](./examples/platformer) | Nested parallel statecharts, typed deep history, raised events, state-scoped timers, deterministic model tests, and a playable SVG adapter |
|
|
692
|
-
|
|
693
|
-
The playground is the shortest path from one concept to working code. The
|
|
694
|
-
standalone examples show larger composition and ownership boundaries.
|
|
695
|
-
|
|
696
731
|
## Reference and development
|
|
697
732
|
|
|
698
733
|
- [API reference](https://effect-machine.typeonce.dev)
|
package/dist/Machine.d.ts
CHANGED
|
@@ -1258,7 +1258,8 @@ export declare namespace Logic {
|
|
|
1258
1258
|
* @category models
|
|
1259
1259
|
* @since 0.4.0
|
|
1260
1260
|
*/
|
|
1261
|
-
interface Spawn {
|
|
1261
|
+
interface Spawn<OwnerEvent = unknown> {
|
|
1262
|
+
<const Child extends ChildMachine.Any>(child: Child & ChildMachine.Executable<Child> & ChildMachine.ParentCompatibility<Child, OwnerEvent>, ...options: ChildMachine.SpawnArgs<Child>): Effect.Effect<ChildMachine.Ref<Child>, ChildAlreadyExistsError | ChildMachine.StartError<Child>, ChildMachine.StartRequirements<Child>>;
|
|
1262
1263
|
<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError = never>(logic: Logic<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError>): Effect.Effect<MachineRef<ChildState, ChildEvent, ChildError, ChildOutput>, ChildInitialError, Exclude<ChildRequirements, Scope.Scope>>;
|
|
1263
1264
|
<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, Options extends SpawnOptions, ChildInitialError = never>(logic: Logic<ChildState, ChildEvent, ChildError, ChildRequirements, ChildOutput, ChildInitialError>, options: Options & ChildAddress.OptionsCompatibility<Options, ChildEvent>): Effect.Effect<MachineRef<ChildState, ChildEvent, ChildError, ChildOutput>, SpawnIdError<Options> | ChildInitialError, Exclude<ChildRequirements, Scope.Scope>>;
|
|
1264
1265
|
}
|
|
@@ -1274,7 +1275,7 @@ export declare namespace Logic {
|
|
|
1274
1275
|
/** Address of the owning process, when one exists. */
|
|
1275
1276
|
readonly parent: Address<unknown> | undefined;
|
|
1276
1277
|
/** Starts a child process owned by this scope. */
|
|
1277
|
-
readonly spawn: Spawn
|
|
1278
|
+
readonly spawn: Spawn<Event>;
|
|
1278
1279
|
/** Sends an event to a machine target or typed parent-local child address. */
|
|
1279
1280
|
readonly sendTo: {
|
|
1280
1281
|
<TargetEvent>(target: MachineTarget<TargetEvent>, event: TargetEvent): Effect.Effect<void, StoppedError>;
|
|
@@ -1302,6 +1303,7 @@ export declare namespace Logic {
|
|
|
1302
1303
|
}
|
|
1303
1304
|
declare const ChildAddressTypeId = "~effect/Machine/ChildAddress";
|
|
1304
1305
|
declare const ChildAddressCompatibilityErrorTypeId = "~effect/Machine/ChildAddressCompatibilityError";
|
|
1306
|
+
declare const ChildParentCompatibilityErrorTypeId = "~effect/Machine/ChildParentCompatibilityError";
|
|
1305
1307
|
declare const ChildMachineTypeId = "~effect/Machine/ChildMachine";
|
|
1306
1308
|
type InvokeLifecycleId = string & {
|
|
1307
1309
|
readonly [ChildAddressTypeId]?: never;
|
|
@@ -1342,6 +1344,68 @@ export declare namespace ChildMachine {
|
|
|
1342
1344
|
* @since 0.4.0
|
|
1343
1345
|
*/
|
|
1344
1346
|
type Any = ChildMachine<string, Machine.Any>;
|
|
1347
|
+
/**
|
|
1348
|
+
* Bound constructor for an open family of child descriptors that share one
|
|
1349
|
+
* machine definition.
|
|
1350
|
+
*
|
|
1351
|
+
* @category models
|
|
1352
|
+
* @since 0.20.0
|
|
1353
|
+
*/
|
|
1354
|
+
interface Family<M extends Machine.Any> {
|
|
1355
|
+
<const Id extends string>(id: Id): ChildMachine<Id, M>;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Ensures a child machine's declared owner protocol is accepted by the
|
|
1359
|
+
* process that will own it.
|
|
1360
|
+
*
|
|
1361
|
+
* @category utility types
|
|
1362
|
+
* @since 0.20.0
|
|
1363
|
+
*/
|
|
1364
|
+
type ParentCompatibility<Child extends Any, OwnerEvent> = Child extends ChildMachine<string, infer M> ? Machine.Any extends M ? {
|
|
1365
|
+
readonly [ChildParentCompatibilityErrorTypeId]: {
|
|
1366
|
+
readonly child: unknown;
|
|
1367
|
+
readonly owner: OwnerEvent;
|
|
1368
|
+
};
|
|
1369
|
+
} : [Machine.EventOf<Machine.ParentEvents<M>>] extends [OwnerEvent] ? unknown : {
|
|
1370
|
+
readonly [ChildParentCompatibilityErrorTypeId]: {
|
|
1371
|
+
readonly child: Machine.EventOf<Machine.ParentEvents<M>>;
|
|
1372
|
+
readonly owner: OwnerEvent;
|
|
1373
|
+
};
|
|
1374
|
+
} : never;
|
|
1375
|
+
/**
|
|
1376
|
+
* Ensures the selected child machine has complete handlers and outputs.
|
|
1377
|
+
*
|
|
1378
|
+
* @category utility types
|
|
1379
|
+
* @since 0.20.0
|
|
1380
|
+
*/
|
|
1381
|
+
type Executable<Child extends Any> = Child["machine"] extends EnsureExecutable<Machine.States<Child["machine"]>, Machine.UnhandledStates<Child["machine"]>, Machine.OutputStates<Child["machine"]>> ? unknown : never;
|
|
1382
|
+
/**
|
|
1383
|
+
* Startup arguments accepted while spawning a child machine.
|
|
1384
|
+
*
|
|
1385
|
+
* @category utility types
|
|
1386
|
+
* @since 0.20.0
|
|
1387
|
+
*/
|
|
1388
|
+
type SpawnArgs<Child extends Any> = Machine.InputSchema<Child["machine"]> extends typeof Schema.Void ? [
|
|
1389
|
+
options?: {
|
|
1390
|
+
readonly input?: never;
|
|
1391
|
+
}
|
|
1392
|
+
] : [options: {
|
|
1393
|
+
readonly input: Machine.Input<Child["machine"]>;
|
|
1394
|
+
}];
|
|
1395
|
+
/**
|
|
1396
|
+
* Typed failures that may occur before a spawned child becomes active.
|
|
1397
|
+
*
|
|
1398
|
+
* @category utility types
|
|
1399
|
+
* @since 0.20.0
|
|
1400
|
+
*/
|
|
1401
|
+
type StartError<Child extends Any> = Child extends ChildMachine<string, infer M> ? Machine.InitialError<M> | Machine.Error<M> | ActionError<Machine.InitialServices<M> | Machine.Services<M>> | InfiniteTransitionError | MachineSchemaDecodeError | StartupError | StoppedError : never;
|
|
1402
|
+
/**
|
|
1403
|
+
* Services needed to initialize a spawned child machine.
|
|
1404
|
+
*
|
|
1405
|
+
* @category utility types
|
|
1406
|
+
* @since 0.20.0
|
|
1407
|
+
*/
|
|
1408
|
+
type StartRequirements<Child extends Any> = Child extends ChildMachine<string, infer M> ? Exclude<ExcludeCompatibleRuntime<Exclude<ExecutionServices<Machine.InitialServices<M> | Machine.Services<M>>, MachineRuntimeRequirement>, Machine.Event<M>, Machine.Emit<M>>, Scope.Scope> : never;
|
|
1345
1409
|
/**
|
|
1346
1410
|
* Running machine reference selected by a child descriptor.
|
|
1347
1411
|
*
|
|
@@ -1357,6 +1421,21 @@ export declare namespace ChildMachine {
|
|
|
1357
1421
|
*/
|
|
1358
1422
|
type Event<Child> = Child extends ChildMachine<string, infer M> ? Machine.EventInput<Machine.InputEvent<M>> : never;
|
|
1359
1423
|
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Effectful operations for child machines owned directly by the current
|
|
1426
|
+
* machine process.
|
|
1427
|
+
*
|
|
1428
|
+
* @category models
|
|
1429
|
+
* @since 0.20.0
|
|
1430
|
+
*/
|
|
1431
|
+
export interface ChildOwner<OwnerEvent> {
|
|
1432
|
+
/** Starts a process-owned child and returns once initialization succeeds. */
|
|
1433
|
+
readonly spawn: <const Child extends ChildMachine.Any>(child: Child & ChildMachine.Executable<Child> & ChildMachine.ParentCompatibility<Child, OwnerEvent>, ...options: ChildMachine.SpawnArgs<Child>) => Effect.Effect<ChildMachine.Ref<Child>, ChildAlreadyExistsError | ChildMachine.StartError<Child>, ChildMachine.StartRequirements<Child>>;
|
|
1434
|
+
/** Sends an event to one active child. Missing children are ignored. */
|
|
1435
|
+
readonly sendTo: <Child extends ChildMachine.Any>(child: Child, event: ChildMachine.Event<Child>) => Effect.Effect<void, StoppedError>;
|
|
1436
|
+
/** Stops one active child. Missing children are ignored. */
|
|
1437
|
+
readonly stop: <Child extends ChildMachine.Any>(child: Child) => Effect.Effect<void>;
|
|
1438
|
+
}
|
|
1360
1439
|
/**
|
|
1361
1440
|
* Parent-local address for a child process that can receive events.
|
|
1362
1441
|
*
|
|
@@ -3060,6 +3139,8 @@ export declare namespace Machine {
|
|
|
3060
3139
|
* @since 0.4.0
|
|
3061
3140
|
*/
|
|
3062
3141
|
type InvokeContext<States extends StateSchemas, Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, StateId extends StateIdentifier<States>, InputEvents extends ReadonlyArray<TaggedSchema> = Events, ParentEvents extends ReadonlyArray<TaggedSchema> = readonly []> = MachineReferences<InputEvents, ParentEvents> & {
|
|
3142
|
+
/** Process-owned child operations for dynamic child machine lifecycles. */
|
|
3143
|
+
readonly children: ChildOwner<EventOf<InputEvents>>;
|
|
3063
3144
|
/** Value owned by the state that owns this invocation. */
|
|
3064
3145
|
readonly state: StateByIdentifier<States, StateId>;
|
|
3065
3146
|
/** Value owned by the nearest schema-backed ancestor, when one exists. */
|
|
@@ -4989,6 +5070,16 @@ export declare const logic: <State, Event = never, Output = void, Error = never,
|
|
|
4989
5070
|
* @since 0.4.0
|
|
4990
5071
|
*/
|
|
4991
5072
|
export declare const child: <const Id extends string, M extends Machine.Any>(id: Id, machine: M) => ChildMachine<Id, M>;
|
|
5073
|
+
/**
|
|
5074
|
+
* Binds one machine definition to an open family of runtime child ids.
|
|
5075
|
+
*
|
|
5076
|
+
* Descriptors created by the returned function are interchangeable with
|
|
5077
|
+
* {@link child} descriptors for the same id and machine definition.
|
|
5078
|
+
*
|
|
5079
|
+
* @category constructors
|
|
5080
|
+
* @since 0.20.0
|
|
5081
|
+
*/
|
|
5082
|
+
export declare const childFamily: <M extends Machine.Any>(machine: M) => ChildMachine.Family<M>;
|
|
4992
5083
|
/**
|
|
4993
5084
|
* Creates a typed parent-local address for lower-level child process logic.
|
|
4994
5085
|
*
|