@typeonce/effect-machine 0.7.0 → 0.9.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 (77) hide show
  1. package/README.md +85 -24
  2. package/dist/Machine.d.ts +545 -444
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +69 -172
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/activities.d.ts +5 -12
  7. package/dist/internal/machine/activities.d.ts.map +1 -1
  8. package/dist/internal/machine/activities.js +47 -17
  9. package/dist/internal/machine/activities.js.map +1 -1
  10. package/dist/internal/machine/atom.d.ts +10 -4
  11. package/dist/internal/machine/atom.d.ts.map +1 -1
  12. package/dist/internal/machine/atom.js.map +1 -1
  13. package/dist/internal/machine/configuration.d.ts.map +1 -1
  14. package/dist/internal/machine/configuration.js +103 -20
  15. package/dist/internal/machine/configuration.js.map +1 -1
  16. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  17. package/dist/internal/machine/executionPlan.js +31 -0
  18. package/dist/internal/machine/executionPlan.js.map +1 -1
  19. package/dist/internal/machine/invocation.d.ts +10 -2
  20. package/dist/internal/machine/invocation.d.ts.map +1 -1
  21. package/dist/internal/machine/invocation.js +93 -34
  22. package/dist/internal/machine/invocation.js.map +1 -1
  23. package/dist/internal/machine/invocationEvent.d.ts +39 -0
  24. package/dist/internal/machine/invocationEvent.d.ts.map +1 -0
  25. package/dist/internal/machine/invocationEvent.js +43 -0
  26. package/dist/internal/machine/invocationEvent.js.map +1 -0
  27. package/dist/internal/machine/machine.d.ts +6 -50
  28. package/dist/internal/machine/machine.d.ts.map +1 -1
  29. package/dist/internal/machine/machine.js +30 -81
  30. package/dist/internal/machine/machine.js.map +1 -1
  31. package/dist/internal/machine/planner.d.ts +12 -1
  32. package/dist/internal/machine/planner.d.ts.map +1 -1
  33. package/dist/internal/machine/planner.js +90 -37
  34. package/dist/internal/machine/planner.js.map +1 -1
  35. package/dist/internal/machine/protocol.d.ts +9 -0
  36. package/dist/internal/machine/protocol.d.ts.map +1 -1
  37. package/dist/internal/machine/protocol.js +114 -0
  38. package/dist/internal/machine/protocol.js.map +1 -1
  39. package/dist/internal/machine/serialization.d.ts.map +1 -1
  40. package/dist/internal/machine/serialization.js +53 -21
  41. package/dist/internal/machine/serialization.js.map +1 -1
  42. package/dist/internal/machine/stateDefinition.d.ts +4 -4
  43. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  44. package/dist/internal/machine/stateDefinition.js +18 -11
  45. package/dist/internal/machine/stateDefinition.js.map +1 -1
  46. package/dist/internal/machine/symbols.d.ts +2 -0
  47. package/dist/internal/machine/symbols.d.ts.map +1 -1
  48. package/dist/internal/machine/symbols.js +2 -0
  49. package/dist/internal/machine/symbols.js.map +1 -1
  50. package/dist/internal/machine/topology.d.ts +5 -5
  51. package/dist/internal/machine/topology.d.ts.map +1 -1
  52. package/dist/internal/machine/topology.js +39 -13
  53. package/dist/internal/machine/topology.js.map +1 -1
  54. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  55. package/dist/internal/testing/machine/verification.js +10 -3
  56. package/dist/internal/testing/machine/verification.js.map +1 -1
  57. package/dist/unstable/reactivity/AtomMachine.d.ts +11 -5
  58. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  59. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  60. package/docs/agent-guide.md +174 -121
  61. package/package.json +3 -3
  62. package/src/Machine.ts +1336 -872
  63. package/src/internal/machine/activities.ts +48 -33
  64. package/src/internal/machine/atom.ts +13 -6
  65. package/src/internal/machine/configuration.ts +102 -23
  66. package/src/internal/machine/executionPlan.ts +35 -0
  67. package/src/internal/machine/invocation.ts +180 -49
  68. package/src/internal/machine/invocationEvent.ts +72 -0
  69. package/src/internal/machine/machine.ts +105 -403
  70. package/src/internal/machine/planner.ts +110 -48
  71. package/src/internal/machine/protocol.ts +149 -0
  72. package/src/internal/machine/serialization.ts +56 -31
  73. package/src/internal/machine/stateDefinition.ts +22 -11
  74. package/src/internal/machine/symbols.ts +3 -0
  75. package/src/internal/machine/topology.ts +44 -18
  76. package/src/internal/testing/machine/verification.ts +13 -3
  77. package/src/unstable/reactivity/AtomMachine.ts +12 -5
package/README.md CHANGED
@@ -40,12 +40,14 @@ const Event = Schema.TaggedUnion({
40
40
 
41
41
  const States = Machine.defineStates(State.cases)
42
42
 
43
- const Counter = Machine.make({
43
+ const CounterDefinition = Machine.make({
44
44
  id: "Counter",
45
45
  states: States.states,
46
46
  events: [Event],
47
47
  initial: () => States.initial.Idle.from()
48
- }).handle({
48
+ })
49
+
50
+ const Counter = CounterDefinition.handle({
49
51
  Idle: {
50
52
  on: {
51
53
  Start: ({ target }) => target.full.Running.from({ count: 0 })
@@ -59,10 +61,12 @@ const Counter = Machine.make({
59
61
  }
60
62
  })
61
63
 
64
+ const CounterEvent = Machine.events(Counter)
65
+
62
66
  const program = Effect.gen(function*() {
63
67
  const ref = yield* Machine.start(Counter)
64
- yield* ref.send(Machine.event(Counter, Event.cases.Start))
65
- yield* ref.send(Machine.event(Counter, Event.cases.Increment))
68
+ yield* ref.send(CounterEvent.Start())
69
+ yield* ref.send(CounterEvent.Increment())
66
70
  })
67
71
  ```
68
72
 
@@ -94,6 +98,27 @@ defaults, refinements, and tagged-class identity are therefore preserved, and
94
98
  decode failures remain typed machine failures. Pass a value directly only when
95
99
  it is already decoded, such as a value returned by `Machine.retag`.
96
100
 
101
+ Omit `schema` when a state represents control flow but owns no data:
102
+
103
+ ```ts
104
+ const States = Machine.defineStates({
105
+ Form: {
106
+ initial: "Editing",
107
+ states: {
108
+ Editing: {},
109
+ Saving
110
+ }
111
+ }
112
+ })
113
+
114
+ States.initial.Form.from((form) => form.Editing.from())
115
+ ```
116
+
117
+ Schema-less states remain active, targetable, matchable, and visible through
118
+ `getSnapshot`, but have no value to read. Their builders expose only `.from`,
119
+ their handler `state` is `undefined`, and `get` / `getWithParents` accept only
120
+ schema-backed paths. Add a schema later if the state starts owning data.
121
+
97
122
  Put data on the narrowest state where it is valid. If sibling phases share
98
123
  data, put it on their compound parent.
99
124
 
@@ -109,20 +134,32 @@ const Internal = Schema.TaggedUnion({
109
134
  SaveFailed: { message: Schema.String }
110
135
  })
111
136
 
112
- const machine = Machine.make({
137
+ const definition = Machine.make({
113
138
  states: States.states,
114
139
  events: [Command],
115
140
  internalEvents: [Internal],
116
141
  initial: () => States.initial.Idle.from()
117
142
  })
143
+
144
+ const CommandEvent = Machine.events(definition)
145
+ const InternalEvent = Machine.internalEvents(definition)
118
146
  ```
119
147
 
120
148
  Handlers see both protocols. Typed `send` and `Machine.plan` accept only public
121
149
  events. Event tags must be unique and public/internal tags must be disjoint.
122
150
 
123
- Use `Machine.event(machine, schema, fields?)` for reusable machine-owned event
124
- values. Ordinary objects and schema-constructed values are also accepted and
125
- decoded at the machine boundary.
151
+ Use `Machine.events(machine)` and `Machine.internalEvents(machine)` as the
152
+ standard constructors for their respective protocols:
153
+
154
+ ```ts
155
+ ref.send(CommandEvent.Save())
156
+ enqueue.raise(InternalEvent.Saved({ id: "entry-1" }))
157
+ ```
158
+
159
+ The returned constructors preserve each schema's make input, including required
160
+ fields and constructor defaults. They defer schema construction until delivery,
161
+ so invalid values fail planning or the running machine with
162
+ `MachineSchemaDecodeError` instead of throwing at the call site.
126
163
 
127
164
  ### Choose the target by scope
128
165
 
@@ -164,29 +201,48 @@ State-scoped work starts on entry and is interrupted on exit:
164
201
 
165
202
  ```ts
166
203
  Loading: {
167
- invoke: Machine.invokeEffect({
204
+ invoke: Machine.invoke({
168
205
  id: "save-document",
169
206
  effect: saveDocument,
170
- onSuccess: (entry) => Internal.cases.Saved.make({ id: entry.id }),
171
- onFailure: (error) => Internal.cases.SaveFailed.make({ message: String(error) })
207
+ onDone: ({ output, target }) => target.full.Saved({ id: output.id }),
208
+ onFailure: ({ error, target }) => target.full.Failed({ message: String(error) })
172
209
  })
173
210
  }
174
211
 
175
212
  Waiting: {
176
- invoke: Machine.after(
177
- "3 seconds",
178
- Internal.cases.SaveFailed.make({ message: "Timed out" })
179
- )
213
+ invoke: Machine.invoke({
214
+ id: "save-timeout",
215
+ after: "3 seconds",
216
+ onDone: ({ target }) => target.full.Failed({ message: "Timed out" })
217
+ })
180
218
  }
181
219
  ```
182
220
 
183
- Use `Machine.invokeEffect` for one Effect, `Machine.after` for a cancellable
184
- delay, and lower-level `Machine.invoke` only for custom process behavior or
185
- snapshot mapping. Use one exported `Machine.child(id, machine)` descriptor for
186
- `invokeMachine`, `sendTo`, and child lookup.
221
+ Use `effect` for one Effect, `after` for a cancellable delay, `logic` for a
222
+ reusable process, and `child` for a complete child statechart—all through
223
+ `Machine.invoke({...})`. The helper is an identity at runtime and preserves
224
+ owner-context and source-channel inference across lifecycle handlers, including
225
+ for state-dependent Effects:
226
+
227
+ ```ts
228
+ invoke: Machine.invoke({
229
+ id: "load-document",
230
+ effect: ({ state }) => loadDocument(state.documentId),
231
+ onDone: ({ output, target }) => target.full.Ready({ document: output }),
232
+ onFailure: ({ error, target }) => target.full.Failed({ message: error.message })
233
+ })
234
+ ```
235
+
236
+ A direct `invoke: { ... }` object is also supported when its lifecycle handlers
237
+ do not need source-derived context. Reuse one exported
238
+ `Machine.child(id, machine)` descriptor for invocation, `sendTo`, and child
239
+ lookup.
187
240
 
188
- Expected failures should become internal events. An unrecovered invoke or child
189
- failure terminates the owning runtime.
241
+ `onDone` is required for a non-`never` output, and `onFailure` is required for a
242
+ non-`never` typed error; each handler is omitted when its channel is `never`.
243
+ Defects, interruption, and source-construction failures terminate the owning
244
+ runtime. `effect: Effect.sleep(...)` is valid, but `after` keeps timers explicit
245
+ and makes static durations visible through activity inspection.
190
246
 
191
247
  ## Reactivity
192
248
 
@@ -239,14 +295,19 @@ The testing entrypoint provides complementary layers:
239
295
  import { MachineTest } from "@typeonce/effect-machine/testing"
240
296
 
241
297
  const trace = yield* MachineTest.run(Counter, {
242
- events: [Event.cases.Start.make({}), Event.cases.Increment.make({})]
298
+ events: [
299
+ Machine.event(Counter, Event.cases.Start),
300
+ Machine.event(Counter, Event.cases.Increment)
301
+ ]
243
302
  })
244
303
 
245
304
  yield* MachineTest.verify(Counter, trace)
246
305
  ```
247
306
 
248
- Pure planner tests do not execute invokes or time. Use a started machine and a
249
- probe when those semantics matter.
307
+ `MachineTest` scenarios retain decoded event values for model inspection, so
308
+ this is the main case for the eager `Machine.event` API. Pure planner tests do
309
+ not execute invokes or time. Use a started machine and a probe when those
310
+ semantics matter.
250
311
 
251
312
  ## Entrypoints
252
313