@typeonce/effect-machine 0.19.1 → 0.21.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 (54) hide show
  1. package/README.md +90 -14
  2. package/dist/Machine.d.ts +155 -18
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +15 -3
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts.map +1 -1
  7. package/dist/internal/machine/atom.js +18 -4
  8. package/dist/internal/machine/atom.js.map +1 -1
  9. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  10. package/dist/internal/machine/executionPlan.js +33 -3
  11. package/dist/internal/machine/executionPlan.js.map +1 -1
  12. package/dist/internal/machine/invocation.d.ts.map +1 -1
  13. package/dist/internal/machine/invocation.js +7 -0
  14. package/dist/internal/machine/invocation.js.map +1 -1
  15. package/dist/internal/machine/machine.d.ts +1 -0
  16. package/dist/internal/machine/machine.d.ts.map +1 -1
  17. package/dist/internal/machine/machine.js +49 -13
  18. package/dist/internal/machine/machine.js.map +1 -1
  19. package/dist/internal/machine/planner.d.ts +11 -2
  20. package/dist/internal/machine/planner.d.ts.map +1 -1
  21. package/dist/internal/machine/planner.js +49 -9
  22. package/dist/internal/machine/planner.js.map +1 -1
  23. package/dist/internal/machine/runtime.d.ts +4 -3
  24. package/dist/internal/machine/runtime.d.ts.map +1 -1
  25. package/dist/internal/machine/runtime.js +12 -1
  26. package/dist/internal/machine/runtime.js.map +1 -1
  27. package/dist/internal/machine/topology.d.ts +9 -1
  28. package/dist/internal/machine/topology.d.ts.map +1 -1
  29. package/dist/internal/machine/topology.js +7 -0
  30. package/dist/internal/machine/topology.js.map +1 -1
  31. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  32. package/dist/internal/testing/machine/verification.js +15 -3
  33. package/dist/internal/testing/machine/verification.js.map +1 -1
  34. package/dist/testing/MachineTest.d.ts +2 -0
  35. package/dist/testing/MachineTest.d.ts.map +1 -1
  36. package/dist/testing/MachineTest.js.map +1 -1
  37. package/dist/unstable/reactivity/AtomMachine.d.ts +10 -8
  38. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  39. package/dist/unstable/reactivity/AtomMachine.js +2 -2
  40. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  41. package/docs/agent-guide.md +48 -0
  42. package/docs/effect-atom-react.md +28 -0
  43. package/package.json +1 -1
  44. package/src/Machine.ts +371 -45
  45. package/src/internal/machine/atom.ts +26 -18
  46. package/src/internal/machine/executionPlan.ts +37 -3
  47. package/src/internal/machine/invocation.ts +13 -1
  48. package/src/internal/machine/machine.ts +75 -15
  49. package/src/internal/machine/planner.ts +64 -11
  50. package/src/internal/machine/runtime.ts +48 -19
  51. package/src/internal/machine/topology.ts +18 -1
  52. package/src/internal/testing/machine/verification.ts +16 -3
  53. package/src/testing/MachineTest.ts +2 -0
  54. package/src/unstable/reactivity/AtomMachine.ts +10 -8
@@ -29,6 +29,8 @@ export const DeclinedTypeId: unique symbol = Symbol("effect/Machine/Declined")
29
29
 
30
30
  export const TargetSelectionTypeId: unique symbol = Symbol("effect/Machine/TargetSelection")
31
31
 
32
+ export const StateUpdateTypeId: unique symbol = Symbol("effect/Machine/StateUpdate")
33
+
32
34
  export const SelectedBranchTypeId: unique symbol = Symbol("effect/Machine/SelectedBranch")
33
35
 
34
36
  interface StateInput {
@@ -73,7 +75,7 @@ export interface Declined {
73
75
  readonly [DeclinedTypeId]: typeof DeclinedTypeId
74
76
  }
75
77
 
76
- export type TargetSelectionKind = "state" | "initial" | "history" | "choice" | "none"
78
+ export type TargetSelectionKind = "state" | "initial" | "history" | "choice" | "update" | "none"
77
79
 
78
80
  export type TargetSelectionScope = "local" | "branch" | "full" | "initial"
79
81
 
@@ -111,6 +113,21 @@ export const noneTargetSelection: TargetSelection = makeTargetSelection("none",
111
113
 
112
114
  export const isTargetSelection = (u: unknown): u is TargetSelection => hasProperty(u, TargetSelectionTypeId)
113
115
 
116
+ export interface StateUpdate {
117
+ readonly [StateUpdateTypeId]: typeof StateUpdateTypeId
118
+ readonly path: string
119
+ readonly value: unknown
120
+ }
121
+
122
+ export const makeStateUpdate = (path: string, value: unknown): StateUpdate =>
123
+ Object.freeze({
124
+ [StateUpdateTypeId]: StateUpdateTypeId,
125
+ path,
126
+ value
127
+ })
128
+
129
+ export const isStateUpdate = (u: unknown): u is StateUpdate => hasProperty(u, StateUpdateTypeId)
130
+
114
131
  export const makeSelectedBranch = (
115
132
  owner: object,
116
133
  branchIndex: number,
@@ -475,7 +475,7 @@ const targetWithinSelection = (
475
475
  nodeByPath: ReadonlyMap<string, Machine.Machine.StateNode>
476
476
  ): boolean => {
477
477
  const selection = branch.selection
478
- if (selection.kind === "none") return target === undefined
478
+ if (selection.kind === "none" || selection.kind === "update") return target === undefined
479
479
  if (target === undefined || selection.path === undefined) return false
480
480
  if (target === selection.path) return true
481
481
  const selectedNode = nodeByPath.get(selection.path)
@@ -564,6 +564,7 @@ export const coverage = <M extends AnyMachine>(
564
564
  const exitHits = new Set<number>()
565
565
 
566
566
  const transitionCoverage = makeTransitionCoverageCollector(machine)
567
+ const transitionDefinitions = Machine.transitionDefinitions(machine)
567
568
 
568
569
  const declaredEvents = publicEventTags(machine)
569
570
  const declaredEventTags = declaredEvents.tags
@@ -577,6 +578,7 @@ export const coverage = <M extends AnyMachine>(
577
578
  let microsteps = 0
578
579
  let changedMicrosteps = 0
579
580
  let targetlessTransitions = 0
581
+ let stateUpdates = 0
580
582
  let raisedEvents = 0
581
583
  let emittedEvents = 0
582
584
  let eventTriggered = 0
@@ -630,7 +632,13 @@ export const coverage = <M extends AnyMachine>(
630
632
  hitPaths(microstep.exitPaths, exitHits)
631
633
  observeSnapshot(microstep.next)
632
634
  for (const retained of microstep.transitions) {
633
- if (retained.target === undefined) targetlessTransitions += 1
635
+ const definition = transitionDefinitions.find((candidate) =>
636
+ candidate.source === retained.source && candidate.reenter === retained.reenter &&
637
+ sameTransitionTrigger(candidate.trigger, retained.trigger)
638
+ )
639
+ const branch = definition?.branches[retained.branchIndex]
640
+ if (branch?.selection.kind === "update") stateUpdates += 1
641
+ else if (retained.target === undefined) targetlessTransitions += 1
634
642
  if (retained.trigger.type === "event") eventTriggered += 1
635
643
  else if (retained.trigger.type === "always") alwaysTriggered += 1
636
644
  else if (retained.trigger.type === "done") doneTriggered += 1
@@ -718,6 +726,7 @@ export const coverage = <M extends AnyMachine>(
718
726
  total: microsteps,
719
727
  changed: changedMicrosteps,
720
728
  targetless: targetlessTransitions,
729
+ updates: stateUpdates,
721
730
  raised: raisedEvents,
722
731
  emitted: emittedEvents,
723
732
  eventTriggered,
@@ -1598,7 +1607,11 @@ export const verify = <M extends AnyMachine>(
1598
1607
  const resolvedTarget = transition.resolvedTarget === undefined ? undefined : String(transition.resolvedTarget)
1599
1608
  const targetNode = target === undefined ? undefined : byPath.get(target)
1600
1609
  let expected = target
1601
- let explanation = target === undefined ? "an unresolved targetless transition" : `target "${target}"`
1610
+ let explanation = branches[index]?.selection.kind === "update"
1611
+ ? `update owner "${String(branches[index]?.selection.path)}"`
1612
+ : target === undefined
1613
+ ? "an unresolved targetless transition"
1614
+ : `target "${target}"`
1602
1615
 
1603
1616
  if (transition.trigger.type === "choice") {
1604
1617
  explanation = target === undefined ? "a targetless choice edge" : `choice edge target "${target}"`
@@ -1822,6 +1822,8 @@ export interface MicrostepCoverageEvidence {
1822
1822
  readonly total: number
1823
1823
  readonly changed: number
1824
1824
  readonly targetless: number
1825
+ /** Retained state-value update operations. */
1826
+ readonly updates: number
1825
1827
  readonly raised: number
1826
1828
  readonly emitted: number
1827
1829
  readonly eventTriggered: number
@@ -148,9 +148,9 @@ export interface MachineAtom<State, Event, Error = never, Output = never, StartE
148
148
  readonly stop: Atom.Writable<AsyncResult.AsyncResult<void, StartError | NotReadyError>, void>
149
149
 
150
150
  /**
151
- * Creates a reactive bridge for a directly invoked child machine.
152
- * Reusing the same descriptor returns the same live bridge while it remains
153
- * referenced.
151
+ * Creates a reactive bridge for a directly owned child machine.
152
+ * Descriptors with the same id and machine definition return the same live
153
+ * bridge while it remains referenced.
154
154
  *
155
155
  * @since 0.4.0
156
156
  */
@@ -206,7 +206,8 @@ export const childEmissions: <Child extends Machine.ChildMachine.Any, StartError
206
206
  > = internal.childEmissions
207
207
 
208
208
  /**
209
- * Reactive access to one invoked child machine selected by its descriptor.
209
+ * Reactive access to one directly owned child machine selected by its
210
+ * descriptor.
210
211
  *
211
212
  * **Details**
212
213
  *
@@ -295,8 +296,9 @@ export interface ChildMachineAtom<Child extends Machine.ChildMachine.Any, StartE
295
296
  void
296
297
  >
297
298
  /**
298
- * Creates a reactive bridge for a directly owned nested child. Reusing the
299
- * same descriptor returns the same live bridge while it remains referenced.
299
+ * Creates a reactive bridge for a directly owned nested child. Descriptors
300
+ * with the same id and machine definition return the same live bridge while
301
+ * it remains referenced.
300
302
  *
301
303
  * @since 0.4.0
302
304
  */
@@ -418,7 +420,7 @@ export const selectSnapshot: <
418
420
  > = internal.selectSnapshot
419
421
 
420
422
  /**
421
- * Selects the typed value for an active state path in an invoked child.
423
+ * Selects the typed value for an active state path in a directly owned child.
422
424
  *
423
425
  * Valid paths and their selected value types are inferred from the child
424
426
  * bridge. An inactive child produces `Option.none()`. Keep the returned atom
@@ -513,7 +515,7 @@ export const matches: <
513
515
  ) => Atom.Atom<AsyncResult.AsyncResult<boolean, StartError | Error>> = internal.matches
514
516
 
515
517
  /**
516
- * Returns whether a state path is active in an invoked child.
518
+ * Returns whether a state path is active in a directly owned child.
517
519
  *
518
520
  * Valid paths are inferred from the child bridge snapshot.
519
521
  * An inactive child produces `false`. Keep the returned atom stable when