@typeonce/effect-machine 0.14.0 → 0.15.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 CHANGED
@@ -53,14 +53,14 @@ const State = Schema.TaggedUnion({
53
53
  Running: { count: Schema.Number }
54
54
  })
55
55
 
56
- const Event = Schema.TaggedUnion({
57
- Start: {},
58
- Increment: {},
59
- Stop: {}
60
- })
61
-
62
- const States = Machine.defineStates(State.cases)
63
- const CounterEvent = Machine.events(Event)
56
+ const States = Machine.states(State.cases)
57
+ const CounterEvent = Machine.events(
58
+ Schema.TaggedUnion({
59
+ Start: {},
60
+ Increment: {},
61
+ Stop: {}
62
+ })
63
+ )
64
64
 
65
65
  const CounterDefinition = Machine.make({
66
66
  id: "Counter",
@@ -102,6 +102,16 @@ const program = Effect.gen(function*() {
102
102
  })
103
103
  ```
104
104
 
105
+ `handle` creates a complete implementation boundary. Its result does not
106
+ expose `handle`, so all behavior for one machine belongs in the same handler
107
+ tree. Reuse the definition when multiple independent implementations are
108
+ useful, such as production and testing variants:
109
+
110
+ ```ts
111
+ const ProductionCounter = CounterDefinition.handle(productionHandlers)
112
+ const TestingCounter = CounterDefinition.handle(testingHandlers)
113
+ ```
114
+
105
115
  `Machine.start` returns a `MachineRef` with `send`, `state`, `snapshot`,
106
116
  `changes`, `emissions`, `join`, and `stop`. Sending enqueues an event; observe
107
117
  `changes` or use the testing probe when work must be causally acknowledged.
@@ -110,13 +120,21 @@ const program = Effect.gen(function*() {
110
120
 
111
121
  Use this order to preserve inference and keep boundaries explicit:
112
122
 
113
- 1. Define domain, state, public-event, internal-event, and emitted-event schemas.
114
- 2. Declare topology with `Machine.defineStates`.
115
- 3. Create public and internal event descriptors with `Machine.events` and
116
- `Machine.internalEvents`.
117
- 4. Create the machine protocol and initializer with `Machine.make`.
118
- 5. Implement every active state with `.handle(...)`.
119
- 6. Add runtime, Atom, testing, or cluster adapters at the application boundary.
123
+ 1. Define domain schemas used by state and by shared event fields.
124
+ 2. Declare topology with `Machine.states`, naming a tagged state union
125
+ when its `.cases` are reused.
126
+ 3. Create event descriptors with `Machine.events`, `Machine.internalEvents`,
127
+ and `Machine.emittedEvents`, passing tagged unions or tagged classes directly.
128
+ 4. Create the machine and implement every active state with
129
+ `Machine.make({...}).handle({...})`.
130
+ 5. Add child descriptors, then runtime, Atom, testing, or cluster adapters at
131
+ the application boundary.
132
+
133
+ Keep one-off topology inline in `Machine.states`. Use `Machine.state` only when
134
+ the same active state definition is mounted more than once; tagged schemas are
135
+ already reusable without it. For repeated finite regions, derive names with
136
+ `States.path(...)` so every literal in the path family is checked against the
137
+ complete tree. Type full-snapshot helpers as `Machine.Snapshot<typeof States>`.
120
138
 
121
139
  ### Construct state through builders
122
140
 
@@ -147,7 +165,7 @@ Submit: Machine.transition({
147
165
  Omit `schema` when a state represents control flow but owns no data:
148
166
 
149
167
  ```ts
150
- const States = Machine.defineStates({
168
+ const States = Machine.states({
151
169
  Form: {
152
170
  initial: "Editing",
153
171
  states: {
@@ -178,19 +196,21 @@ belong in `internalEvents`. Ephemeral outward notifications have their own
178
196
  `emittedEvents` protocol:
179
197
 
180
198
  ```ts
181
- const Command = Schema.TaggedUnion({ Save: {} })
182
- const Internal = Schema.TaggedUnion({
183
- Saved: { id: Schema.String },
184
- SaveFailed: { message: Schema.String }
185
- })
186
- const Emitted = Schema.TaggedUnion({
187
- SaveObserved: { id: Schema.String }
188
- })
189
-
190
- export const CommandEvent = Machine.events(Command)
199
+ export const CommandEvent = Machine.events(
200
+ Schema.TaggedUnion({ Save: {} })
201
+ )
191
202
  export type PublicCommandEvent = Machine.EventOf<typeof CommandEvent>
192
- const InternalEvent = Machine.internalEvents(Internal)
193
- const Emissions = Machine.emittedEvents(Emitted)
203
+ const InternalEvent = Machine.internalEvents(
204
+ Schema.TaggedUnion({
205
+ Saved: { id: Schema.String },
206
+ SaveFailed: { message: Schema.String }
207
+ })
208
+ )
209
+ const Emissions = Machine.emittedEvents(
210
+ Schema.TaggedUnion({
211
+ SaveObserved: { id: Schema.String }
212
+ })
213
+ )
194
214
 
195
215
  const definition = Machine.make({
196
216
  states: States.states,
@@ -374,7 +394,7 @@ change; use `{ reenter: true, transition }` when the source must restart. With
374
394
 
375
395
  ## Statechart capabilities
376
396
 
377
- `Machine.defineStates` supports:
397
+ `Machine.states` supports:
378
398
 
379
399
  - atomic states;
380
400
  - compound states with one active child;
@@ -384,7 +404,7 @@ change; use `{ reenter: true, transition }` when the source must restart. With
384
404
  - shallow and deep history states.
385
405
 
386
406
  Declare topology—including finality, output schemas, choices, and history—only
387
- in `defineStates`. Handlers implement behavior and output computation without
407
+ in `states`. Handlers implement behavior and output computation without
388
408
  repeating structural metadata. Final children complete their parent, so
389
409
  `onDone` belongs on that compound or parallel parent.
390
410
 
@@ -446,31 +466,31 @@ invoke: Machine.invoke({
446
466
  })
447
467
  ```
448
468
 
449
- The standalone `Machine.invoke(...)` constructor does not know the owning
450
- definition, so its `self` and `parent` references are non-sendable. When an
451
- invocation callback sends through either reference, construct it through the
452
- owning definition so those references use its exact public input and
453
- `parentEvents` protocols:
469
+ Inside `.handle(...)`, `Machine.invoke(...)` receives the owning machine's
470
+ public input and `parentEvents` protocols contextually. Its source and lifecycle
471
+ callbacks can send through `self` and `parent` while retaining the invoked
472
+ Effect's output and error inference:
454
473
 
455
474
  ```ts
456
- const definition = Machine.make({
475
+ const machine = Machine.make({
457
476
  events: Commands,
458
477
  internalEvents: InternalEvents,
459
478
  parentEvents: ParentEvents
460
479
  // ...
461
- })
462
-
463
- const machine = definition.handle({
480
+ }).handle({
464
481
  Saving: {
465
- invoke: definition.invoke({
482
+ invoke: Machine.invoke({
466
483
  id: "notify-parent",
467
- effect: ({ parent }) =>
468
- parent === undefined
469
- ? Effect.void
470
- : parent.send(ParentEvents.SaveStarted()),
484
+ effect: () => saveDocument,
471
485
  onDone: Machine.transition({
472
486
  target: (to) => to.none(),
473
- resolve: () => undefined
487
+ resolve: ({ parent, self }, enqueue) => {
488
+ enqueue.sendTo(self, Commands.Save())
489
+ if (parent !== undefined) {
490
+ enqueue.sendTo(parent, ParentEvents.ChildFinished({ id: "job-1" }))
491
+ }
492
+ return undefined
493
+ }
474
494
  }),
475
495
  onFailure: Machine.transition({
476
496
  target: (to) => to.none(),
@@ -481,6 +501,9 @@ const machine = definition.handle({
481
501
  })
482
502
  ```
483
503
 
504
+ The machine-bound `definition.invoke(...)` form remains equivalent when a
505
+ definition is already named; it is not required for `self` or `parent` typing.
506
+
484
507
  A direct `invoke: { ... }` object is also supported when its lifecycle handlers
485
508
  do not need source-derived context. Reuse one exported
486
509
  `Machine.child(id, machine)` descriptor for invocation, `sendTo`, and child