@typeonce/effect-machine 0.8.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 (57) hide show
  1. package/README.md +64 -24
  2. package/dist/Machine.d.ts +386 -304
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +66 -171
  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 +2 -2
  11. package/dist/internal/machine/atom.d.ts.map +1 -1
  12. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  13. package/dist/internal/machine/executionPlan.js +25 -0
  14. package/dist/internal/machine/executionPlan.js.map +1 -1
  15. package/dist/internal/machine/invocation.d.ts +10 -2
  16. package/dist/internal/machine/invocation.d.ts.map +1 -1
  17. package/dist/internal/machine/invocation.js +93 -34
  18. package/dist/internal/machine/invocation.js.map +1 -1
  19. package/dist/internal/machine/invocationEvent.d.ts +39 -0
  20. package/dist/internal/machine/invocationEvent.d.ts.map +1 -0
  21. package/dist/internal/machine/invocationEvent.js +43 -0
  22. package/dist/internal/machine/invocationEvent.js.map +1 -0
  23. package/dist/internal/machine/machine.d.ts +6 -50
  24. package/dist/internal/machine/machine.d.ts.map +1 -1
  25. package/dist/internal/machine/machine.js +8 -65
  26. package/dist/internal/machine/machine.js.map +1 -1
  27. package/dist/internal/machine/planner.d.ts +2 -1
  28. package/dist/internal/machine/planner.d.ts.map +1 -1
  29. package/dist/internal/machine/planner.js +45 -2
  30. package/dist/internal/machine/planner.js.map +1 -1
  31. package/dist/internal/machine/protocol.d.ts +9 -0
  32. package/dist/internal/machine/protocol.d.ts.map +1 -1
  33. package/dist/internal/machine/protocol.js +114 -0
  34. package/dist/internal/machine/protocol.js.map +1 -1
  35. package/dist/internal/machine/symbols.d.ts +2 -0
  36. package/dist/internal/machine/symbols.d.ts.map +1 -1
  37. package/dist/internal/machine/symbols.js +2 -0
  38. package/dist/internal/machine/symbols.js.map +1 -1
  39. package/dist/internal/machine/topology.d.ts.map +1 -1
  40. package/dist/internal/machine/topology.js +23 -0
  41. package/dist/internal/machine/topology.js.map +1 -1
  42. package/dist/unstable/reactivity/AtomMachine.d.ts +3 -3
  43. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  44. package/docs/agent-guide.md +118 -93
  45. package/package.json +2 -2
  46. package/src/Machine.ts +1040 -651
  47. package/src/internal/machine/activities.ts +48 -33
  48. package/src/internal/machine/atom.ts +3 -3
  49. package/src/internal/machine/executionPlan.ts +29 -0
  50. package/src/internal/machine/invocation.ts +179 -48
  51. package/src/internal/machine/invocationEvent.ts +72 -0
  52. package/src/internal/machine/machine.ts +20 -346
  53. package/src/internal/machine/planner.ts +61 -10
  54. package/src/internal/machine/protocol.ts +149 -0
  55. package/src/internal/machine/symbols.ts +3 -0
  56. package/src/internal/machine/topology.ts +23 -0
  57. package/src/unstable/reactivity/AtomMachine.ts +3 -3
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
 
@@ -130,20 +134,32 @@ const Internal = Schema.TaggedUnion({
130
134
  SaveFailed: { message: Schema.String }
131
135
  })
132
136
 
133
- const machine = Machine.make({
137
+ const definition = Machine.make({
134
138
  states: States.states,
135
139
  events: [Command],
136
140
  internalEvents: [Internal],
137
141
  initial: () => States.initial.Idle.from()
138
142
  })
143
+
144
+ const CommandEvent = Machine.events(definition)
145
+ const InternalEvent = Machine.internalEvents(definition)
139
146
  ```
140
147
 
141
148
  Handlers see both protocols. Typed `send` and `Machine.plan` accept only public
142
149
  events. Event tags must be unique and public/internal tags must be disjoint.
143
150
 
144
- Use `Machine.event(machine, schema, fields?)` for reusable machine-owned event
145
- values. Ordinary objects and schema-constructed values are also accepted and
146
- 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.
147
163
 
148
164
  ### Choose the target by scope
149
165
 
@@ -185,29 +201,48 @@ State-scoped work starts on entry and is interrupted on exit:
185
201
 
186
202
  ```ts
187
203
  Loading: {
188
- invoke: Machine.invokeEffect({
204
+ invoke: Machine.invoke({
189
205
  id: "save-document",
190
206
  effect: saveDocument,
191
- onSuccess: (entry) => Internal.cases.Saved.make({ id: entry.id }),
192
- 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) })
193
209
  })
194
210
  }
195
211
 
196
212
  Waiting: {
197
- invoke: Machine.after(
198
- "3 seconds",
199
- Internal.cases.SaveFailed.make({ message: "Timed out" })
200
- )
213
+ invoke: Machine.invoke({
214
+ id: "save-timeout",
215
+ after: "3 seconds",
216
+ onDone: ({ target }) => target.full.Failed({ message: "Timed out" })
217
+ })
201
218
  }
202
219
  ```
203
220
 
204
- Use `Machine.invokeEffect` for one Effect, `Machine.after` for a cancellable
205
- delay, and lower-level `Machine.invoke` only for custom process behavior or
206
- snapshot mapping. Use one exported `Machine.child(id, machine)` descriptor for
207
- `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.
208
240
 
209
- Expected failures should become internal events. An unrecovered invoke or child
210
- 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.
211
246
 
212
247
  ## Reactivity
213
248
 
@@ -260,14 +295,19 @@ The testing entrypoint provides complementary layers:
260
295
  import { MachineTest } from "@typeonce/effect-machine/testing"
261
296
 
262
297
  const trace = yield* MachineTest.run(Counter, {
263
- 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
+ ]
264
302
  })
265
303
 
266
304
  yield* MachineTest.verify(Counter, trace)
267
305
  ```
268
306
 
269
- Pure planner tests do not execute invokes or time. Use a started machine and a
270
- 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.
271
311
 
272
312
  ## Entrypoints
273
313