@typeonce/effect-machine 0.14.1 → 0.16.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.
Files changed (74) hide show
  1. package/README.md +99 -48
  2. package/dist/Machine.d.ts +436 -156
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +114 -57
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/activities.d.ts +2 -0
  7. package/dist/internal/machine/activities.d.ts.map +1 -1
  8. package/dist/internal/machine/activities.js +4 -0
  9. package/dist/internal/machine/activities.js.map +1 -1
  10. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  11. package/dist/internal/machine/executionPlan.js +6 -1
  12. package/dist/internal/machine/executionPlan.js.map +1 -1
  13. package/dist/internal/machine/invocation.d.ts +1 -1
  14. package/dist/internal/machine/invocation.d.ts.map +1 -1
  15. package/dist/internal/machine/invocation.js +25 -3
  16. package/dist/internal/machine/invocation.js.map +1 -1
  17. package/dist/internal/machine/invocationEvent.d.ts +8 -0
  18. package/dist/internal/machine/invocationEvent.d.ts.map +1 -1
  19. package/dist/internal/machine/invocationEvent.js +8 -0
  20. package/dist/internal/machine/invocationEvent.js.map +1 -1
  21. package/dist/internal/machine/machine.d.ts +9 -4
  22. package/dist/internal/machine/machine.d.ts.map +1 -1
  23. package/dist/internal/machine/machine.js +131 -55
  24. package/dist/internal/machine/machine.js.map +1 -1
  25. package/dist/internal/machine/planner.d.ts +7 -0
  26. package/dist/internal/machine/planner.d.ts.map +1 -1
  27. package/dist/internal/machine/planner.js +20 -11
  28. package/dist/internal/machine/planner.js.map +1 -1
  29. package/dist/internal/machine/runtime.d.ts +1 -0
  30. package/dist/internal/machine/runtime.d.ts.map +1 -1
  31. package/dist/internal/machine/runtime.js +25 -16
  32. package/dist/internal/machine/runtime.js.map +1 -1
  33. package/dist/internal/machine/stateDefinition.d.ts +3 -1
  34. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  35. package/dist/internal/machine/stateDefinition.js +35 -0
  36. package/dist/internal/machine/stateDefinition.js.map +1 -1
  37. package/dist/internal/machine/topology.d.ts +11 -0
  38. package/dist/internal/machine/topology.d.ts.map +1 -1
  39. package/dist/internal/machine/topology.js +17 -6
  40. package/dist/internal/machine/topology.js.map +1 -1
  41. package/dist/internal/testing/machine/finiteModel.js +1 -1
  42. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  43. package/dist/internal/testing/machine/transitionCoverage.d.ts.map +1 -1
  44. package/dist/internal/testing/machine/transitionCoverage.js +6 -2
  45. package/dist/internal/testing/machine/transitionCoverage.js.map +1 -1
  46. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  47. package/dist/internal/testing/machine/verification.js +6 -0
  48. package/dist/internal/testing/machine/verification.js.map +1 -1
  49. package/dist/testing/MachineTest.d.ts +9 -8
  50. package/dist/testing/MachineTest.d.ts.map +1 -1
  51. package/dist/testing/MachineTest.js +7 -7
  52. package/dist/testing/MachineTest.js.map +1 -1
  53. package/dist/unstable/cluster/ClusterMachine.d.ts +1 -1
  54. package/dist/unstable/cluster/ClusterMachine.js +1 -1
  55. package/dist/unstable/reactivity/AtomMachine.d.ts +3 -3
  56. package/dist/unstable/reactivity/AtomMachine.js +3 -3
  57. package/docs/agent-guide.md +207 -88
  58. package/package.json +1 -1
  59. package/src/Machine.ts +871 -256
  60. package/src/internal/machine/activities.ts +7 -0
  61. package/src/internal/machine/executionPlan.ts +6 -1
  62. package/src/internal/machine/invocation.ts +39 -4
  63. package/src/internal/machine/invocationEvent.ts +16 -0
  64. package/src/internal/machine/machine.ts +187 -67
  65. package/src/internal/machine/planner.ts +17 -3
  66. package/src/internal/machine/runtime.ts +61 -25
  67. package/src/internal/machine/stateDefinition.ts +41 -1
  68. package/src/internal/machine/topology.ts +31 -2
  69. package/src/internal/testing/machine/finiteModel.ts +1 -1
  70. package/src/internal/testing/machine/transitionCoverage.ts +6 -2
  71. package/src/internal/testing/machine/verification.ts +11 -0
  72. package/src/testing/MachineTest.ts +9 -7
  73. package/src/unstable/cluster/ClusterMachine.ts +1 -1
  74. package/src/unstable/reactivity/AtomMachine.ts +3 -3
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
 
@@ -393,7 +413,7 @@ synchronous. Conditions use ordinary TypeScript control flow. Callbacks may
393
413
  select state and enqueue explicit `raise`, `emit`, `sendTo`, or `stop` commands;
394
414
  arbitrary asynchronous Effects do not run inside planning.
395
415
 
396
- ## Effects, timers, and child machines
416
+ ## Effects, Streams, timers, and child machines
397
417
 
398
418
  State-scoped work starts on entry and is interrupted on exit:
399
419
 
@@ -425,8 +445,9 @@ Waiting: {
425
445
  }
426
446
  ```
427
447
 
428
- Use `effect` for one Effect, `after` for a cancellable delay, `logic` for a
429
- reusable process, and `child` for a complete child statechart—all through
448
+ Use `effect` for one Effect, `stream` for a sequence of externally produced
449
+ values, `after` for a cancellable delay, `logic` for a reusable process, and
450
+ `child` for a complete child statechart—all through
430
451
  `Machine.invoke({...})`. The helper is an identity at runtime and preserves
431
452
  owner-context and source-channel inference across lifecycle handlers, including
432
453
  for state-dependent Effects:
@@ -446,31 +467,58 @@ invoke: Machine.invoke({
446
467
  })
447
468
  ```
448
469
 
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:
470
+ A Stream source remains independent of the parent event protocol. Each element
471
+ is mapped by `onElement`, and the next element is not pulled until that parent
472
+ macrostep commits:
454
473
 
455
474
  ```ts
456
- const definition = Machine.make({
475
+ invoke: Machine.invoke({
476
+ id: "channel",
477
+ stream: () => channelMessages,
478
+ onElement: {
479
+ target: Machine.targetless,
480
+ resolve: ({ element }, enqueue) => {
481
+ enqueue.raise(Events.MessageReceived({ message: element }))
482
+ }
483
+ },
484
+ onDone: { target: Machine.targetless },
485
+ onFailure: Machine.transition({
486
+ target: (to) => to.full.Failed(),
487
+ resolve: ({ error, target }) => target.from({ error })
488
+ })
489
+ })
490
+ ```
491
+
492
+ `target: Machine.targetless` is the direct shorthand for a non-reentering
493
+ transition that keeps the current configuration. Its optional `resolve`
494
+ callback may enqueue commands and must return `undefined`. Use
495
+ `Machine.transition(...)` for transitions that select state or reenter.
496
+
497
+ Inside `.handle(...)`, `Machine.invoke(...)` receives the owning machine's
498
+ public input and `parentEvents` protocols contextually. Its source and lifecycle
499
+ callbacks can send through `self` and `parent` while retaining the invoked
500
+ Effect's output and error inference:
501
+
502
+ ```ts
503
+ const machine = Machine.make({
457
504
  events: Commands,
458
505
  internalEvents: InternalEvents,
459
506
  parentEvents: ParentEvents
460
507
  // ...
461
- })
462
-
463
- const machine = definition.handle({
508
+ }).handle({
464
509
  Saving: {
465
- invoke: definition.invoke({
510
+ invoke: Machine.invoke({
466
511
  id: "notify-parent",
467
- effect: ({ parent }) =>
468
- parent === undefined
469
- ? Effect.void
470
- : parent.send(ParentEvents.SaveStarted()),
512
+ effect: () => saveDocument,
471
513
  onDone: Machine.transition({
472
514
  target: (to) => to.none(),
473
- resolve: () => undefined
515
+ resolve: ({ parent, self }, enqueue) => {
516
+ enqueue.sendTo(self, Commands.Save())
517
+ if (parent !== undefined) {
518
+ enqueue.sendTo(parent, ParentEvents.ChildFinished({ id: "job-1" }))
519
+ }
520
+ return undefined
521
+ }
474
522
  }),
475
523
  onFailure: Machine.transition({
476
524
  target: (to) => to.none(),
@@ -481,6 +529,9 @@ const machine = definition.handle({
481
529
  })
482
530
  ```
483
531
 
532
+ The machine-bound `definition.invoke(...)` form remains equivalent when a
533
+ definition is already named; it is not required for `self` or `parent` typing.
534
+
484
535
  A direct `invoke: { ... }` object is also supported when its lifecycle handlers
485
536
  do not need source-derived context. Reuse one exported
486
537
  `Machine.child(id, machine)` descriptor for invocation, `sendTo`, and child