@typeonce/effect-machine 0.31.1 → 0.32.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 (151) hide show
  1. package/README.md +145 -140
  2. package/dist/Machine.d.ts +428 -254
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +91 -111
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +3 -4
  7. package/dist/internal/machine/atom.d.ts.map +1 -1
  8. package/dist/internal/machine/atom.js +0 -2
  9. package/dist/internal/machine/atom.js.map +1 -1
  10. package/dist/internal/machine/childRegistry.d.ts +52 -0
  11. package/dist/internal/machine/childRegistry.d.ts.map +1 -0
  12. package/dist/internal/machine/childRegistry.js +74 -0
  13. package/dist/internal/machine/childRegistry.js.map +1 -0
  14. package/dist/internal/machine/cluster.d.ts +2 -3
  15. package/dist/internal/machine/cluster.d.ts.map +1 -1
  16. package/dist/internal/machine/cluster.js.map +1 -1
  17. package/dist/internal/machine/configuration.d.ts.map +1 -1
  18. package/dist/internal/machine/configuration.js +14 -4
  19. package/dist/internal/machine/configuration.js.map +1 -1
  20. package/dist/internal/machine/executionPlan.d.ts +1 -0
  21. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  22. package/dist/internal/machine/executionPlan.js +97 -53
  23. package/dist/internal/machine/executionPlan.js.map +1 -1
  24. package/dist/internal/machine/invocation.d.ts.map +1 -1
  25. package/dist/internal/machine/invocation.js +4 -2
  26. package/dist/internal/machine/invocation.js.map +1 -1
  27. package/dist/internal/machine/invocationDefinition.d.ts +35 -0
  28. package/dist/internal/machine/invocationDefinition.d.ts.map +1 -0
  29. package/dist/internal/machine/invocationDefinition.js +21 -0
  30. package/dist/internal/machine/invocationDefinition.js.map +1 -0
  31. package/dist/internal/machine/invocationEvent.d.ts +0 -2
  32. package/dist/internal/machine/invocationEvent.d.ts.map +1 -1
  33. package/dist/internal/machine/invocationEvent.js +0 -8
  34. package/dist/internal/machine/invocationEvent.js.map +1 -1
  35. package/dist/internal/machine/machine.d.ts +21 -43
  36. package/dist/internal/machine/machine.d.ts.map +1 -1
  37. package/dist/internal/machine/machine.js +98 -374
  38. package/dist/internal/machine/machine.js.map +1 -1
  39. package/dist/internal/machine/planner.d.ts +13 -19
  40. package/dist/internal/machine/planner.d.ts.map +1 -1
  41. package/dist/internal/machine/planner.js +71 -39
  42. package/dist/internal/machine/planner.js.map +1 -1
  43. package/dist/internal/machine/process.d.ts +6 -8
  44. package/dist/internal/machine/process.d.ts.map +1 -1
  45. package/dist/internal/machine/process.js +1 -0
  46. package/dist/internal/machine/process.js.map +1 -1
  47. package/dist/internal/machine/protocol.d.ts +2 -2
  48. package/dist/internal/machine/protocol.d.ts.map +1 -1
  49. package/dist/internal/machine/protocol.js +4 -18
  50. package/dist/internal/machine/protocol.js.map +1 -1
  51. package/dist/internal/machine/readiness.d.ts +1 -1
  52. package/dist/internal/machine/readiness.d.ts.map +1 -1
  53. package/dist/internal/machine/requirements.d.ts +4 -0
  54. package/dist/internal/machine/requirements.d.ts.map +1 -0
  55. package/dist/internal/machine/requirements.js +2 -0
  56. package/dist/internal/machine/requirements.js.map +1 -0
  57. package/dist/internal/machine/runtime.d.ts +4 -58
  58. package/dist/internal/machine/runtime.d.ts.map +1 -1
  59. package/dist/internal/machine/runtime.js +1 -69
  60. package/dist/internal/machine/runtime.js.map +1 -1
  61. package/dist/internal/machine/serialization.d.ts.map +1 -1
  62. package/dist/internal/machine/serialization.js +16 -0
  63. package/dist/internal/machine/serialization.js.map +1 -1
  64. package/dist/internal/machine/stateDefinition.d.ts +3 -1
  65. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  66. package/dist/internal/machine/stateDefinition.js +36 -2
  67. package/dist/internal/machine/stateDefinition.js.map +1 -1
  68. package/dist/internal/machine/targetBuilder.d.ts +16 -0
  69. package/dist/internal/machine/targetBuilder.d.ts.map +1 -0
  70. package/dist/internal/machine/targetBuilder.js +333 -0
  71. package/dist/internal/machine/targetBuilder.js.map +1 -0
  72. package/dist/internal/machine/topology.js +3 -3
  73. package/dist/internal/machine/topology.js.map +1 -1
  74. package/dist/internal/machine/transition.d.ts +16 -0
  75. package/dist/internal/machine/transition.d.ts.map +1 -0
  76. package/dist/internal/machine/transition.js +2 -0
  77. package/dist/internal/machine/transition.js.map +1 -0
  78. package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
  79. package/dist/internal/testing/machine/finiteModel.js +10 -12
  80. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  81. package/dist/internal/testing/machine/format.d.ts +7 -0
  82. package/dist/internal/testing/machine/format.d.ts.map +1 -0
  83. package/dist/internal/testing/machine/format.js +104 -0
  84. package/dist/internal/testing/machine/format.js.map +1 -0
  85. package/dist/internal/testing/machine/probe.d.ts +1 -1
  86. package/dist/internal/testing/machine/probe.d.ts.map +1 -1
  87. package/dist/internal/testing/machine/probe.js +1 -1
  88. package/dist/internal/testing/machine/probe.js.map +1 -1
  89. package/dist/internal/testing/machine/referenceModel.d.ts.map +1 -1
  90. package/dist/internal/testing/machine/referenceModel.js +15 -19
  91. package/dist/internal/testing/machine/referenceModel.js.map +1 -1
  92. package/dist/internal/testing/machine/trace.d.ts +1 -1
  93. package/dist/internal/testing/machine/trace.d.ts.map +1 -1
  94. package/dist/internal/testing/machine/trace.js +3 -3
  95. package/dist/internal/testing/machine/trace.js.map +1 -1
  96. package/dist/internal/testing/machine/value.d.ts +8 -0
  97. package/dist/internal/testing/machine/value.d.ts.map +1 -0
  98. package/dist/internal/testing/machine/value.js +82 -0
  99. package/dist/internal/testing/machine/value.js.map +1 -0
  100. package/dist/internal/testing/machine/verification.d.ts +2 -13
  101. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  102. package/dist/internal/testing/machine/verification.js +21 -113
  103. package/dist/internal/testing/machine/verification.js.map +1 -1
  104. package/dist/testing/MachineTest.d.ts +89 -104
  105. package/dist/testing/MachineTest.d.ts.map +1 -1
  106. package/dist/testing/MachineTest.js +70 -84
  107. package/dist/testing/MachineTest.js.map +1 -1
  108. package/dist/unstable/cluster/ClusterMachine.d.ts +10 -13
  109. package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
  110. package/dist/unstable/cluster/ClusterMachine.js +5 -8
  111. package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
  112. package/dist/unstable/reactivity/AtomMachine.d.ts +26 -61
  113. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  114. package/dist/unstable/reactivity/AtomMachine.js +17 -26
  115. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  116. package/docs/agent-guide.md +45 -45
  117. package/docs/effect-atom-react.md +35 -63
  118. package/docs/machine-review.md +7 -7
  119. package/docs/root-api.md +214 -0
  120. package/package.json +1 -1
  121. package/src/Machine.ts +778 -437
  122. package/src/internal/machine/atom.ts +3 -13
  123. package/src/internal/machine/childRegistry.ts +141 -0
  124. package/src/internal/machine/cluster.ts +5 -14
  125. package/src/internal/machine/configuration.ts +20 -4
  126. package/src/internal/machine/executionPlan.ts +109 -52
  127. package/src/internal/machine/invocation.ts +10 -6
  128. package/src/internal/machine/invocationDefinition.ts +52 -0
  129. package/src/internal/machine/invocationEvent.ts +0 -8
  130. package/src/internal/machine/machine.ts +154 -679
  131. package/src/internal/machine/planner.ts +82 -66
  132. package/src/internal/machine/process.ts +14 -21
  133. package/src/internal/machine/protocol.ts +9 -32
  134. package/src/internal/machine/readiness.ts +1 -0
  135. package/src/internal/machine/requirements.ts +11 -0
  136. package/src/internal/machine/runtime.ts +17 -186
  137. package/src/internal/machine/serialization.ts +22 -0
  138. package/src/internal/machine/stateDefinition.ts +36 -3
  139. package/src/internal/machine/targetBuilder.ts +525 -0
  140. package/src/internal/machine/topology.ts +3 -3
  141. package/src/internal/machine/transition.ts +26 -0
  142. package/src/internal/testing/machine/finiteModel.ts +22 -23
  143. package/src/internal/testing/machine/format.ts +120 -0
  144. package/src/internal/testing/machine/probe.ts +4 -4
  145. package/src/internal/testing/machine/referenceModel.ts +25 -17
  146. package/src/internal/testing/machine/trace.ts +4 -4
  147. package/src/internal/testing/machine/value.ts +77 -0
  148. package/src/internal/testing/machine/verification.ts +904 -1109
  149. package/src/testing/MachineTest.ts +103 -114
  150. package/src/unstable/cluster/ClusterMachine.ts +10 -20
  151. package/src/unstable/reactivity/AtomMachine.ts +42 -79
@@ -3,7 +3,7 @@ import * as Inspectable from "effect/Inspectable"
3
3
  import * as Option from "effect/Option"
4
4
  import { Prototype as PipeablePrototype } from "effect/Pipeable"
5
5
  import { hasProperty } from "effect/Predicate"
6
- import type * as Schema from "effect/Schema"
6
+ import * as Schema from "effect/Schema"
7
7
  import type * as Scope from "effect/Scope"
8
8
  import type * as Stream from "effect/Stream"
9
9
  import type {
@@ -21,22 +21,31 @@ import type {
21
21
  MachineSchemaEncodeError,
22
22
  Parent,
23
23
  ParentMode,
24
- Runtime,
25
24
  RuntimeOutcome,
26
25
  SpawnOptions,
26
+ State,
27
27
  StoppedError
28
28
  } from "../../Machine.js"
29
29
  import * as Activities from "./activities.js"
30
30
  import * as Configuration from "./configuration.js"
31
31
  import type { ChildAlreadyExistsError, InfiniteTransitionError, StartupError } from "./errors.js"
32
+ import * as InvocationDefinition from "./invocationDefinition.js"
32
33
  import * as internalPlanner from "./planner.js"
33
34
  import * as internalProcess from "./process.js"
34
35
  import * as Protocol from "./protocol.js"
35
36
  import type { EnsureExecutable } from "./readiness.js"
37
+ import type { ExcludeCompatibleRuntime } from "./requirements.js"
36
38
  import * as internalRuntime from "./runtime.js"
37
39
  import * as Serialization from "./serialization.js"
38
40
  import * as StateDefinition from "./stateDefinition.js"
39
- import { ChildMachineLogicTypeId, SnapshotBuilderStateTypeId } from "./symbols.js"
41
+ import { ChildMachineLogicTypeId } from "./symbols.js"
42
+ import {
43
+ getLocalTargetScope,
44
+ getTargetBuilderNode,
45
+ makeSnapshotBuilder,
46
+ makeTargetBuilder,
47
+ withFrom
48
+ } from "./targetBuilder.js"
40
49
  import * as Topology from "./topology.js"
41
50
 
42
51
  export {
@@ -56,15 +65,8 @@ export const InvokeTypeId: unique symbol = Symbol.for("effect/Machine/Invoke")
56
65
  export const TransitionTypeId: unique symbol = Symbol.for("effect/Machine/Transition")
57
66
  const InvokeBuilderDescriptorTypeId: unique symbol = Symbol("effect/Machine/InvokeBuilderDescriptor")
58
67
  const ChildMachineTypeId = "~effect/Machine/ChildMachine"
59
- type IsAny<A> = 0 extends 1 & A ? true : false
60
68
  type MachineRuntimeRequirement = internalRuntime.MachineRuntime
61
- type ExcludeCompatibleRuntime<Requirements, Events, Emits> = Requirements extends Runtime.Requirement<
62
- infer RequiredEvents,
63
- infer RequiredEmits
64
- > ? IsAny<Requirements> extends true ? Requirements
65
- : [RequiredEvents] extends [Events] ? [RequiredEmits] extends [Emits] ? never : Requirements
66
- : Requirements
67
- : Requirements
69
+
68
70
  type SpawnRequirements<Requirements> = Exclude<Requirements, Scope.Scope>
69
71
  type SpawnIdError<Options extends SpawnOptions> = "id" extends keyof Options ? Options extends {
70
72
  readonly id?: infer Id
@@ -77,34 +79,6 @@ type SpawnResult<State, Event, Error, Requirements, Output, SpawnError, InitialE
77
79
  SpawnError | InitialError,
78
80
  MachineRuntimeRequirement | SpawnRequirements<Requirements>
79
81
  >
80
- type DefineStateTreeInput<States extends Machine.StateSchemas> = States
81
- type ValidateDefinedStates<States extends Machine.StateSchemas> = [States] extends
82
- [Machine.ValidateStateSchemas<States>] ? []
83
- : [validation: Machine.ValidateStateSchemas<States>]
84
- type InvalidDefinedStateTreeInput<States extends Machine.StateSchemas> = [States] extends
85
- [Machine.ValidateStateSchemas<States>] ? never
86
- : States & Machine.ValidateStateSchemas<States>
87
- type ReusableStateNodeConfig =
88
- | Machine.AtomicStateNodeConfig
89
- | Machine.CompoundStateNodeConfig
90
- | Machine.ParallelStateNodeConfig
91
- interface StateConstructor {
92
- <const Node extends ReusableStateNodeConfig>(node: Node): Node
93
- }
94
- interface StatesConstructor {
95
- <const States extends Machine.StateSchemas>(
96
- states: States,
97
- ..._validation: ValidateDefinedStates<NoInfer<States>>
98
- ): Machine.DefinedStates<States>
99
- <const States extends Machine.StateSchemas>(states: InvalidDefinedStateTreeInput<States>): never
100
- }
101
- type ValidateInputEventProtocol<InputEvents extends ReadonlyArray<Machine.TaggedSchema>> = InputEvents extends
102
- ReadonlyArray<Machine.TaggedSchema> ? unknown : never
103
- type ValidateInternalEventProtocol<
104
- InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
105
- InternalEvents extends ReadonlyArray<Machine.TaggedSchema>
106
- > = InputEvents | InternalEvents extends ReadonlyArray<Machine.TaggedSchema> ? unknown : never
107
-
108
82
  const Proto = {
109
83
  ...Inspectable.BaseProto,
110
84
  ...PipeablePrototype,
@@ -122,6 +96,7 @@ const makeWithHandlers = (
122
96
  ): Machine.Any => {
123
97
  const machine = Object.create(Proto)
124
98
  machine.states = self.states
99
+ machine.root = self.root
125
100
  machine.events = self.events
126
101
  machine.internalEvents = self.internalEvents
127
102
  machine.emittedEvents = self.emittedEvents
@@ -164,7 +139,7 @@ type DirectTransitionDescriptor = {
164
139
  readonly [TransitionBuilderDescriptorTypeId]: typeof TransitionBuilderDescriptorTypeId
165
140
  readonly type: "direct"
166
141
  readonly selection: Topology.TargetSelection
167
- readonly resolve?: (context: any, enqueue: unknown) => unknown
142
+ readonly resolver?: (context: any, enqueue: unknown) => unknown
168
143
  readonly reenter: boolean
169
144
  readonly declinable: boolean
170
145
  }
@@ -208,7 +183,7 @@ const makeDirectTransitionDescriptor = (
208
183
  resolve: ((context: any, enqueue: unknown) => unknown) | undefined,
209
184
  options: unknown
210
185
  ): DirectTransitionDescriptor => {
211
- const shared: Omit<DirectTransitionDescriptor, "resolve"> = {
186
+ const shared: Omit<DirectTransitionDescriptor, "resolver"> = {
212
187
  [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
213
188
  type: "direct",
214
189
  selection: plainTargetSelection(selection),
@@ -216,12 +191,34 @@ const makeDirectTransitionDescriptor = (
216
191
  }
217
192
  return resolve === undefined
218
193
  ? Object.freeze(shared)
219
- : Object.freeze({ ...shared, resolve })
194
+ : Object.freeze({ ...shared, resolver: resolve })
195
+ }
196
+
197
+ const guardedSelection = (selection: Topology.TargetSelection, predicate: (context: any) => boolean): object => {
198
+ const wrap = (resolve: (context: any, enqueue: unknown) => unknown, options?: unknown) =>
199
+ makeDirectTransitionDescriptor(
200
+ selection,
201
+ (context, enqueue) => predicate(context) ? resolve(context, enqueue) : Topology.makeDeclined(),
202
+ { ...transitionOptions(options), declinable: true }
203
+ )
204
+ return Object.freeze({
205
+ ...wrap((
206
+ context
207
+ ) => (selection.kind === "none" ? Topology.makeNoTarget() : constructSelectedTarget(context.target))),
208
+ from: (value: (context: any) => unknown) => wrap((context) => context.target.from(value(context))),
209
+ decoded: (value: (context: any) => unknown) => wrap((context) => context.target.decoded(value(context))),
210
+ resolve: wrap
211
+ })
220
212
  }
221
213
 
222
214
  const decorateTransitionSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
223
215
  Object.freeze({
224
216
  ...selection,
217
+ guard: (predicate: (context: any) => boolean) => guardedSelection(selection, predicate),
218
+ from: (value: (context: any) => unknown) =>
219
+ makeDirectTransitionDescriptor(selection, (context) => context.target.from(value(context)), undefined),
220
+ decoded: (value: (context: any) => unknown) =>
221
+ makeDirectTransitionDescriptor(selection, (context) => context.target.decoded(value(context)), undefined),
225
222
  resolve: (resolve: (context: any, enqueue: unknown) => unknown, options?: unknown) =>
226
223
  makeDirectTransitionDescriptor(selection, resolve, options),
227
224
  reenter: () => makeDirectTransitionDescriptor(selection, undefined, { reenter: true }),
@@ -242,14 +239,16 @@ const decorateTransitionSelection = (selection: Topology.TargetSelection): Topol
242
239
  }
243
240
  })
244
241
 
245
- const decorateStateUpdateSelection = (selection: Topology.TargetSelection): Topology.TargetSelection => {
246
- const update = (
247
- resolve: (context: any, enqueue: unknown) => unknown,
248
- options?: unknown
249
- ) => makeDirectTransitionDescriptor(selection, resolve, options)
250
- Object.assign(update, selection)
251
- return Object.freeze(update) as unknown as Topology.TargetSelection
252
- }
242
+ const decorateStateUpdateSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
243
+ Object.freeze({
244
+ ...selection,
245
+ from: (value: (context: any) => unknown) =>
246
+ makeDirectTransitionDescriptor(selection, (context) => context.owner.from(value(context)), undefined),
247
+ decoded: (value: (context: any) => unknown) =>
248
+ makeDirectTransitionDescriptor(selection, (context) => context.owner.decoded(value(context)), undefined),
249
+ resolve: (resolve: (context: any, enqueue: unknown) => unknown, options?: unknown) =>
250
+ makeDirectTransitionDescriptor(selection, resolve, options)
251
+ })
253
252
 
254
253
  const noneTransitionSelection = decorateTransitionSelection(Topology.noneTargetSelection)
255
254
 
@@ -266,6 +265,10 @@ const makeInitialBuilderDescriptor = (
266
265
  const decorateInitialSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
267
266
  Object.freeze({
268
267
  ...selection,
268
+ from: (value: (context: any) => unknown) =>
269
+ makeInitialBuilderDescriptor(selection, (context) => context.target.from(value(context))),
270
+ decoded: (value: (context: any) => unknown) =>
271
+ makeInitialBuilderDescriptor(selection, (context) => context.target.decoded(value(context))),
269
272
  resolve: (resolve: (context: any) => unknown) => makeInitialBuilderDescriptor(selection, resolve)
270
273
  })
271
274
 
@@ -292,6 +295,12 @@ const decorateInitialSelectorNode = (node: unknown): unknown => {
292
295
 
293
296
  const decorateTransitionSelectorNode = (node: unknown): unknown => {
294
297
  if (Topology.isTargetSelection(node)) {
298
+ if (node.kind === "state" && node.scope === "full" && hasProperty(node, "initial")) {
299
+ return Object.freeze({
300
+ ...decorateTransitionSelection(node),
301
+ initial: decorateTransitionSelection(node.initial as Topology.TargetSelection)
302
+ })
303
+ }
295
304
  if (node.kind === "update") return decorateStateUpdateSelection(node)
296
305
  return node === Topology.noneTargetSelection ? noneTransitionSelection : decorateTransitionSelection(node)
297
306
  }
@@ -360,7 +369,7 @@ const normalizeTransitionBuilder = (
360
369
  }
361
370
  return {
362
371
  target: () => descriptor.selection,
363
- resolve: descriptor.resolve,
372
+ resolve: descriptor.resolver,
364
373
  reenter: descriptor.reenter,
365
374
  declinable: descriptor.declinable
366
375
  }
@@ -430,7 +439,7 @@ const makeSelectionNode = (
430
439
  }
431
440
  if (
432
441
  scope === "branch" && source !== undefined && node.schema !== undefined &&
433
- (source === path || source.startsWith(`${path}.`)) &&
442
+ (source === path || Configuration.isDescendantOf(source, path)) &&
434
443
  getTargetBuilderNode(stateNodes, source).type !== "choice"
435
444
  ) {
436
445
  Object.defineProperty(method, "update", {
@@ -439,6 +448,9 @@ const makeSelectionNode = (
439
448
  })
440
449
  }
441
450
  }
451
+ if (node.type === "atomic" && scope === "branch" && source === path && node.schema !== undefined) {
452
+ Object.defineProperty(method, "update", { value: makeStateUpdateSelection(path, "branch"), enumerable: true })
453
+ }
442
454
  return method
443
455
  }
444
456
 
@@ -463,15 +475,12 @@ const makeTargetSelector = (
463
475
  stateNodes: Machine.StateNodes,
464
476
  source: string
465
477
  ): unknown => {
466
- const full: Record<string, unknown> = {}
467
- for (const node of stateNodes.byPath.values()) {
468
- if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
469
- full[node.key] = makeSelectionNode(stateNodes, node.path, "full")
470
- }
471
- }
478
+ const full = Object.assign({}, makeSelectionValue("state", "", "full"), {
479
+ initial: makeSelectionValue("initial", "", "full")
480
+ })
472
481
  const branch: Record<string, unknown> = {}
473
- const root = getTargetBuilderNode(stateNodes, source.split(".")[0]!)
474
- branch[root.key] = makeSelectionNode(stateNodes, root.path, "branch", source)
482
+ const root = getTargetBuilderNode(stateNodes, stateNodes.roots[0]!)
483
+ addSelectionChildren(branch, stateNodes, root.path, "branch", source)
475
484
  const local: Record<string, unknown> = {}
476
485
  const localScope = getLocalTargetScope(stateNodes, source)
477
486
  if (localScope !== undefined) {
@@ -486,10 +495,15 @@ const makeTargetSelector = (
486
495
  }
487
496
  return {
488
497
  none: Topology.noneTargetSelection,
498
+ self: getTargetBuilderNode(stateNodes, source).schema === undefined ||
499
+ getTargetBuilderNode(stateNodes, source).type === "final"
500
+ ? {}
501
+ : { update: makeStateUpdateSelection(source, "branch") },
502
+ root: makeSelectionNode(stateNodes, root.path, "branch", source),
489
503
  local,
490
504
  branch,
491
505
  full,
492
- history: makeHistorySelectionTree(stateNodes, undefined)
506
+ history: makeHistorySelectionTree(stateNodes, "")
493
507
  }
494
508
  }
495
509
 
@@ -580,6 +594,7 @@ const getSelectionBuilder = (
580
594
  let parts = selection.path!.split(".")
581
595
  if (selection.kind === "history") {
582
596
  builder = target.history
597
+ if (selection.path !== "") parts.unshift("")
583
598
  } else if (selection.scope === "local") {
584
599
  builder = target.local
585
600
  const scope = getLocalTargetScope(stateNodes, source)
@@ -588,11 +603,12 @@ const getSelectionBuilder = (
588
603
  builder = builder.with
589
604
  parts = []
590
605
  } else {
591
- parts = selection.path!.slice(scope.length + 1).split(".")
606
+ parts = (scope === "" ? selection.path! : selection.path!.slice(scope.length + 1)).split(".")
592
607
  }
593
608
  }
594
609
  } else if (selection.scope === "branch") {
595
610
  builder = target.branch
611
+ if (selection.path !== "") parts.unshift("")
596
612
  } else {
597
613
  builder = target.full
598
614
  }
@@ -645,7 +661,7 @@ const validateResolvedSelection = (
645
661
  (selectedNode?.type === "compound" || selectedNode?.type === "parallel")
646
662
  if (
647
663
  resultPath === undefined ||
648
- (resultPath !== selection.path && !(acceptsDescendant && resultPath.startsWith(`${selection.path}.`)))
664
+ (resultPath !== selection.path && !(acceptsDescendant && Configuration.isDescendantOf(resultPath, selection.path!)))
649
665
  ) {
650
666
  throw new Error(
651
667
  `Machine transition resolver selected "${selection.path}" but constructed "${resultPath ?? "<invalid>"}"`
@@ -1005,9 +1021,9 @@ const captureInvokeDefinition = (
1005
1021
  captured[key] = captureTransition(captured[key], stateNodes, path, key)
1006
1022
  }
1007
1023
  }
1008
- return captured
1024
+ return InvocationDefinition.capture(captured, path)
1009
1025
  })
1010
- if (Array.isArray(authored)) return capturedDefinitions
1026
+ if (Array.isArray(authored)) return Object.freeze(capturedDefinitions)
1011
1027
  return capturedDefinitions[0]
1012
1028
  }
1013
1029
 
@@ -1028,6 +1044,14 @@ const flattenHandlers = (
1028
1044
  throw new Error(`Machine expected state "${path}" handler to be an object`)
1029
1045
  }
1030
1046
  const { states: childConfig, ...stateConfig } = nodeConfig as Record<string, unknown>
1047
+ if (typeof stateConfig.history === "object" && stateConfig.history !== null) {
1048
+ stateConfig.history = Object.freeze(Object.fromEntries(
1049
+ Object.entries(stateConfig.history).map(([key, entry]) => [
1050
+ key,
1051
+ typeof entry === "object" && entry !== null ? Object.freeze({ ...entry }) : entry
1052
+ ])
1053
+ ))
1054
+ }
1031
1055
  const on = stateConfig.on
1032
1056
  if (typeof on === "object" && on !== null) {
1033
1057
  const capturedOn = captureEventHandlers(on, stateNodes, path)
@@ -1073,7 +1097,7 @@ const flattenHandlers = (
1073
1097
  const makeHandle = (self: Definition.Any): Definition.Any["handle"] =>
1074
1098
  ((config: Record<string, unknown>) => {
1075
1099
  const handlers: Record<PropertyKey, Machine.AnyStateConfig> = Object.create(null)
1076
- flattenHandlers(handlers, self.stateNodes, self.states, "", config)
1100
+ flattenHandlers(handlers, self.stateNodes, self.states, "", { "": config })
1077
1101
  return makeWithHandlers(self, handlers)
1078
1102
  }) as Definition.Any["handle"]
1079
1103
 
@@ -1114,523 +1138,6 @@ export const isFinal = <
1114
1138
  state: Machine.Snapshot<States>
1115
1139
  ): state is Machine.SnapshotContainingFinal<States, FinalStates> => internalPlanner.isFinal(machine as any, state)
1116
1140
 
1117
- type SnapshotBuilderOptions = {
1118
- readonly mode: "initial" | "full"
1119
- readonly prefix: string
1120
- }
1121
-
1122
- type FromMethodKind = "leaf" | "nested"
1123
-
1124
- const withFrom = <Method extends (value: unknown, ...args: ReadonlyArray<any>) => unknown>(
1125
- method: Method,
1126
- kind: FromMethodKind,
1127
- valued: boolean
1128
- ): {
1129
- readonly decoded?: Method
1130
- readonly from: (...args: ReadonlyArray<any>) => unknown
1131
- } => {
1132
- const builder: Record<string, unknown> = {}
1133
- if (valued) {
1134
- Object.defineProperty(builder, "decoded", {
1135
- value: method,
1136
- enumerable: false
1137
- })
1138
- }
1139
- Object.defineProperty(builder, "from", {
1140
- value: (...args: ReadonlyArray<any>) => {
1141
- if (!valued) {
1142
- return method(undefined, ...args)
1143
- }
1144
- const omitted = args.length === 0 || (kind === "nested" && args.length === 1 && typeof args[0] === "function")
1145
- const input = omitted ? {} : args[0]
1146
- const rest = omitted ? args : args.slice(1)
1147
- return method(Topology.makeStateInput(input), ...rest)
1148
- },
1149
- enumerable: false
1150
- })
1151
- return builder as {
1152
- readonly decoded?: Method
1153
- readonly from: (...args: ReadonlyArray<any>) => unknown
1154
- }
1155
- }
1156
-
1157
- const withInitial = <Builder extends object>(
1158
- builder: Builder,
1159
- path: string,
1160
- valued: boolean,
1161
- values?: Readonly<Record<string, unknown>>
1162
- ): Builder => {
1163
- const initial = withFrom(
1164
- (value: unknown) => Topology.makeInitialTarget(path, value, values),
1165
- "leaf",
1166
- valued
1167
- )
1168
- Object.defineProperty(builder, "initial", {
1169
- value: initial,
1170
- enumerable: false
1171
- })
1172
- return builder
1173
- }
1174
-
1175
- const makeSnapshotBuilder = (
1176
- states: Machine.StateTree,
1177
- options: SnapshotBuilderOptions
1178
- ): unknown => {
1179
- const builder: Record<string, unknown> = {}
1180
- for (const key of Object.keys(states)) {
1181
- const definition = states[key]!
1182
- const pseudoType = (definition as { readonly type?: unknown }).type
1183
- if (pseudoType === "history") {
1184
- continue
1185
- }
1186
- const path = options.prefix === "" ? key : `${options.prefix}.${key}`
1187
- if (pseudoType === "choice") {
1188
- builder[key] = () => Topology.makeChoiceTarget(path, getParentPathRuntime(path))
1189
- continue
1190
- }
1191
- const node = Topology.getStateNodeDefinition(path, definition)
1192
- const method = withFrom(
1193
- (value: unknown, selector?: (builder: unknown) => unknown) =>
1194
- makeSnapshotForNode(definition, key, value, selector, options),
1195
- node.states === undefined ? "leaf" : "nested",
1196
- node.schema !== undefined
1197
- )
1198
- builder[key] = node.states === undefined || options.mode !== "full" || options.prefix !== ""
1199
- ? method
1200
- : withInitial(method, path, node.schema !== undefined)
1201
- }
1202
- return builder
1203
- }
1204
-
1205
- const makeParallelSnapshotBuilder = (
1206
- states: Machine.StateTree,
1207
- options: SnapshotBuilderOptions,
1208
- regions: Readonly<Record<string, unknown>>
1209
- ): unknown => {
1210
- const builder: Record<string, unknown> = {}
1211
- Object.defineProperty(builder, SnapshotBuilderStateTypeId, {
1212
- value: regions,
1213
- enumerable: false
1214
- })
1215
- for (const key of Object.keys(states)) {
1216
- const definition = states[key]!
1217
- const pseudoType = (definition as { readonly type?: unknown }).type
1218
- if (pseudoType === "history" || pseudoType === "choice") {
1219
- continue
1220
- }
1221
- if (hasProperty(regions, key)) {
1222
- continue
1223
- }
1224
- const path = options.prefix === "" ? key : `${options.prefix}.${key}`
1225
- const node = Topology.getStateNodeDefinition(path, definition)
1226
- const method = withFrom(
1227
- (value: unknown, selector?: (builder: unknown) => unknown) => {
1228
- const nextRegions: Record<string, unknown> = {}
1229
- for (const regionKey of Object.keys(regions)) {
1230
- nextRegions[regionKey] = regions[regionKey]
1231
- }
1232
- nextRegions[key] = makeSnapshotForNode(definition, key, value, selector, options)
1233
- return makeParallelSnapshotBuilder(states, options, nextRegions)
1234
- },
1235
- node.states === undefined ? "leaf" : "nested",
1236
- node.schema !== undefined
1237
- )
1238
- builder[key] = method
1239
- }
1240
- return builder
1241
- }
1242
-
1243
- const getParallelSnapshotBuilderRegions = (
1244
- path: string,
1245
- states: Machine.StateTree,
1246
- builder: unknown
1247
- ): Readonly<Record<string, unknown>> => {
1248
- if (typeof builder !== "object" || builder === null || !hasProperty(builder, SnapshotBuilderStateTypeId)) {
1249
- throw new Error(`Machine expected parallel state "${path}" builder callback to return a builder`)
1250
- }
1251
- const regions = (builder as { readonly [SnapshotBuilderStateTypeId]: Readonly<Record<string, unknown>> })[
1252
- SnapshotBuilderStateTypeId
1253
- ]
1254
- for (const key of Object.keys(states)) {
1255
- const pseudoType = (states[key] as { readonly type?: unknown }).type
1256
- if (pseudoType === "history" || pseudoType === "choice") {
1257
- continue
1258
- }
1259
- if (!hasProperty(regions, key)) {
1260
- throw new Error(`Machine expected parallel state "${path}" builder callback to provide region "${key}"`)
1261
- }
1262
- }
1263
- return regions
1264
- }
1265
-
1266
- const makeSnapshotForNode = (
1267
- definition: Machine.TaggedSchema | Machine.StateNodeConfig,
1268
- key: string,
1269
- value: unknown,
1270
- selector: ((builder: unknown) => unknown) | undefined,
1271
- options: SnapshotBuilderOptions
1272
- ): Record<string, unknown> => {
1273
- const path = options.prefix === "" ? key : `${options.prefix}.${key}`
1274
- const node = Topology.getStateNodeDefinition(path, definition)
1275
- const snapshot: Record<string, unknown> = {
1276
- path,
1277
- value
1278
- }
1279
- if (node.states === undefined) {
1280
- return snapshot
1281
- }
1282
- if (selector === undefined) {
1283
- throw new Error(`Machine expected state "${path}" builder to provide active child states`)
1284
- }
1285
- if (node.type === "parallel") {
1286
- const builder = makeParallelSnapshotBuilder(node.states, { ...options, prefix: path }, {})
1287
- const selected = selector(builder)
1288
- snapshot.states = getParallelSnapshotBuilderRegions(path, node.states, selected)
1289
- return snapshot
1290
- }
1291
- const childStates = options.mode === "initial" && node.initial !== undefined
1292
- ? { [node.initial]: node.states[node.initial]! }
1293
- : node.states
1294
- const selected = selector(makeSnapshotBuilder(childStates, { ...options, prefix: path }))
1295
- snapshot.state = selected
1296
- return snapshot
1297
- }
1298
-
1299
- const getTargetBuilderNode = (
1300
- stateNodes: Machine.StateNodes,
1301
- path: string
1302
- ): Machine.StateNode => {
1303
- const node = stateNodes.byPath.get(path)
1304
- if (node === undefined) {
1305
- throw new Error(`Machine expected state path "${path}" to exist`)
1306
- }
1307
- return node
1308
- }
1309
-
1310
- const getLocalTargetScope = (
1311
- stateNodes: Machine.StateNodes,
1312
- source: string
1313
- ): string | undefined => {
1314
- let current: string | undefined = source
1315
- while (current !== undefined) {
1316
- const node = stateNodes.byPath.get(current)
1317
- if (node === undefined) {
1318
- return undefined
1319
- }
1320
- if (node.type === "compound") {
1321
- return node.path
1322
- }
1323
- current = node.parent
1324
- }
1325
- return undefined
1326
- }
1327
-
1328
- const hasTargetValues = (
1329
- values: Readonly<Record<string, unknown>> | undefined
1330
- ): values is Readonly<Record<string, unknown>> => values !== undefined && Object.keys(values).length > 0
1331
-
1332
- const makeTargetWithValues = (
1333
- path: string,
1334
- value: unknown,
1335
- values: Readonly<Record<string, unknown>> | undefined
1336
- ): Machine.Target<any, any> =>
1337
- hasTargetValues(values)
1338
- ? Topology.makeTarget(path as any, value as any, { values: values as any })
1339
- : Topology.makeTarget(path as any, value as any)
1340
-
1341
- const getTargetBuilderDefinition = (
1342
- states: Machine.StateTree,
1343
- targetPath: string
1344
- ): Machine.TaggedSchema | Machine.StateNodeConfig => {
1345
- let children = states
1346
- let path = ""
1347
- let definition: Machine.TaggedSchema | Machine.StateNodeConfig | undefined
1348
- for (const key of targetPath.split(".")) {
1349
- if (!hasProperty(children, key)) {
1350
- throw new Error(`Machine expected state path "${targetPath}" to exist`)
1351
- }
1352
- definition = children[key]!
1353
- path = path === "" ? key : `${path}.${key}`
1354
- const node = Topology.getStateNodeDefinition(path, definition)
1355
- children = node.states ?? {}
1356
- }
1357
- return definition!
1358
- }
1359
-
1360
- const makeParallelTarget = (
1361
- states: Machine.StateTree,
1362
- node: Machine.StateNode,
1363
- value: unknown,
1364
- selector: ((builder: unknown) => unknown) | undefined,
1365
- values: Readonly<Record<string, unknown>> | undefined
1366
- ): Machine.Target<any, any> => {
1367
- if (selector === undefined) {
1368
- throw new Error(`Machine expected parallel target "${node.path}" builder to provide every active region`)
1369
- }
1370
- const snapshot = makeSnapshotForNode(
1371
- getTargetBuilderDefinition(states, node.path),
1372
- node.key,
1373
- value,
1374
- selector,
1375
- { mode: "full", prefix: node.parent ?? "" }
1376
- )
1377
- return Topology.makeTarget(node.path as any, value as any, {
1378
- snapshot: snapshot as any,
1379
- values: values as any
1380
- })
1381
- }
1382
-
1383
- const extendTargetValues = (
1384
- values: Readonly<Record<string, unknown>> | undefined,
1385
- path: string,
1386
- value: unknown
1387
- ): Readonly<Record<string, unknown>> => {
1388
- const next: Record<string, unknown> = {}
1389
- if (values !== undefined) {
1390
- for (const key of Object.keys(values)) {
1391
- next[key] = values[key]
1392
- }
1393
- }
1394
- next[path] = value
1395
- return next
1396
- }
1397
-
1398
- const makeLocalTargetChildBuilder = (
1399
- states: Machine.StateTree,
1400
- stateNodes: Machine.StateNodes,
1401
- parentPath: string,
1402
- values: Readonly<Record<string, unknown>> | undefined,
1403
- source: string
1404
- ): unknown => {
1405
- const parent = getTargetBuilderNode(stateNodes, parentPath)
1406
- const builder: Record<string, unknown> = {}
1407
- for (
1408
- const childPath of Array.from(stateNodes.byPath.values())
1409
- .filter((node) => node.parent === parent.path && node.type !== "history")
1410
- .map((node) => node.path)
1411
- ) {
1412
- const child = getTargetBuilderNode(stateNodes, childPath)
1413
- if (child.type === "choice") {
1414
- builder[child.key] = () => Topology.makeChoiceTarget(child.path, parent.path, values)
1415
- continue
1416
- }
1417
- const method = withFrom(
1418
- (value: unknown, selector?: (builder: unknown) => unknown) => {
1419
- if (child.type === "atomic" || child.type === "final") {
1420
- return makeTargetWithValues(child.path, value, values)
1421
- }
1422
- if (child.type === "parallel") {
1423
- if (source !== child.path && !source.startsWith(`${child.path}.`)) {
1424
- return makeParallelTarget(states, child, value, selector, values)
1425
- }
1426
- if (selector === undefined) {
1427
- throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`)
1428
- }
1429
- return selector(makeLocalTargetChildBuilder(
1430
- states,
1431
- stateNodes,
1432
- child.path,
1433
- child.schema === undefined ? values : extendTargetValues(values, child.path, value),
1434
- source
1435
- ))
1436
- }
1437
- if (selector === undefined) {
1438
- throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`)
1439
- }
1440
- return selector(makeLocalTargetChildBuilder(
1441
- states,
1442
- stateNodes,
1443
- child.path,
1444
- child.schema === undefined ? values : extendTargetValues(values, child.path, value),
1445
- source
1446
- ))
1447
- },
1448
- child.type === "atomic" || child.type === "final" ? "leaf" : "nested",
1449
- child.schema !== undefined
1450
- )
1451
- builder[child.key] = child.type === "atomic" || child.type === "final"
1452
- ? method
1453
- : withInitial(method, child.path, child.schema !== undefined, values)
1454
- }
1455
- return builder
1456
- }
1457
-
1458
- const makeLocalTargetBuilder = (
1459
- states: Machine.StateTree,
1460
- stateNodes: Machine.StateNodes,
1461
- source: string
1462
- ): unknown => {
1463
- const scope = getLocalTargetScope(stateNodes, source)
1464
- if (scope === undefined) {
1465
- return {}
1466
- }
1467
- const builder = makeLocalTargetChildBuilder(states, stateNodes, scope, undefined, source) as Record<string, unknown>
1468
- const scopeNode = getTargetBuilderNode(stateNodes, scope)
1469
- if (scopeNode.schema !== undefined) {
1470
- builder.with = withFrom(
1471
- (value: unknown, selector?: (builder: unknown) => unknown) => {
1472
- if (selector === undefined) {
1473
- throw new Error(`Machine expected target "${scope}" builder to provide an active child state`)
1474
- }
1475
- return selector(makeLocalTargetChildBuilder(states, stateNodes, scope, { [scope]: value }, source))
1476
- },
1477
- "nested",
1478
- true
1479
- )
1480
- }
1481
- return builder
1482
- }
1483
-
1484
- const addBranchTargetChildren = (
1485
- builder: Record<string, unknown>,
1486
- states: Machine.StateTree,
1487
- stateNodes: Machine.StateNodes,
1488
- parentPath: string,
1489
- values: Readonly<Record<string, unknown>> | undefined,
1490
- source: string
1491
- ): void => {
1492
- const parent = getTargetBuilderNode(stateNodes, parentPath)
1493
- for (
1494
- const childPath of Array.from(stateNodes.byPath.values())
1495
- .filter((node) => node.parent === parent.path && node.type !== "history")
1496
- .map((node) => node.path)
1497
- ) {
1498
- const child = getTargetBuilderNode(stateNodes, childPath)
1499
- if (child.type === "choice") {
1500
- builder[child.key] = () => Topology.makeChoiceTarget(child.path, parent.path, values)
1501
- continue
1502
- }
1503
- builder[child.key] = makeBranchTargetNodeBuilder(states, stateNodes, child.path, values, source)
1504
- }
1505
- }
1506
-
1507
- const makeBranchTargetNodeBuilder = (
1508
- states: Machine.StateTree,
1509
- stateNodes: Machine.StateNodes,
1510
- path: string,
1511
- values: Readonly<Record<string, unknown>> | undefined,
1512
- source: string
1513
- ): unknown => {
1514
- const node = getTargetBuilderNode(stateNodes, path)
1515
- if (node.type === "atomic" || node.type === "final") {
1516
- return withFrom(
1517
- (value: unknown) => makeTargetWithValues(node.path, value, values),
1518
- "leaf",
1519
- node.schema !== undefined
1520
- )
1521
- }
1522
- const builder = withFrom(
1523
- (value: unknown, selector?: (builder: unknown) => unknown) => {
1524
- if (node.type === "parallel") {
1525
- if (source !== node.path && !source.startsWith(`${node.path}.`)) {
1526
- return makeParallelTarget(states, node, value, selector, values)
1527
- }
1528
- if (selector === undefined) {
1529
- throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`)
1530
- }
1531
- const nextBuilder: Record<string, unknown> = {}
1532
- addBranchTargetChildren(
1533
- nextBuilder,
1534
- states,
1535
- stateNodes,
1536
- node.path,
1537
- node.schema === undefined ? values : extendTargetValues(values, node.path, value),
1538
- source
1539
- )
1540
- return selector(nextBuilder)
1541
- }
1542
- if (selector === undefined) {
1543
- throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`)
1544
- }
1545
- const nextBuilder: Record<string, unknown> = {}
1546
- addBranchTargetChildren(
1547
- nextBuilder,
1548
- states,
1549
- stateNodes,
1550
- node.path,
1551
- node.schema === undefined ? values : extendTargetValues(values, node.path, value),
1552
- source
1553
- )
1554
- return selector(nextBuilder)
1555
- },
1556
- "nested",
1557
- node.schema !== undefined
1558
- ) as unknown as Record<string, unknown>
1559
- withInitial(builder, node.path, node.schema !== undefined, values)
1560
- if (node.type !== "parallel" || source === node.path || source.startsWith(`${node.path}.`)) {
1561
- addBranchTargetChildren(builder, states, stateNodes, node.path, values, source)
1562
- }
1563
- return builder
1564
- }
1565
-
1566
- const makeBranchTargetBuilder = (
1567
- states: Machine.StateTree,
1568
- stateNodes: Machine.StateNodes,
1569
- source: string
1570
- ): unknown => {
1571
- const rootPath = source.split(".")[0]!
1572
- const root = getTargetBuilderNode(stateNodes, rootPath)
1573
- return {
1574
- [root.key]: makeBranchTargetNodeBuilder(states, stateNodes, root.path, undefined, source)
1575
- }
1576
- }
1577
-
1578
- const makeHistoryTargetBuilder = (
1579
- states: Machine.StateTree,
1580
- prefix: string
1581
- ): unknown => {
1582
- const builder: Record<string, unknown> = {}
1583
- for (const key of Object.keys(states)) {
1584
- const path = prefix === "" ? key : `${prefix}.${key}`
1585
- const definition = Topology.getStateNodeDefinition(path, states[key]!)
1586
- if (definition.type === "history") {
1587
- const parent = getParentPathRuntime(path)
1588
- builder[key] = () => Topology.makeHistoryTarget(path, parent)
1589
- continue
1590
- }
1591
- if (definition.states !== undefined) {
1592
- builder[key] = makeHistoryTargetBuilder(definition.states, path)
1593
- }
1594
- }
1595
- return builder
1596
- }
1597
-
1598
- const getParentPathRuntime = (path: string): string => {
1599
- const separator = path.lastIndexOf(".")
1600
- if (separator < 0) {
1601
- throw new Error(`Machine expected history state "${path}" to have an active parent`)
1602
- }
1603
- return path.slice(0, separator)
1604
- }
1605
-
1606
- const makeTargetBuilder = <const States extends Machine.StateSchemas>(
1607
- states: States,
1608
- stateNodes: Machine.StateNodes
1609
- ) => {
1610
- const full = makeSnapshotBuilder(states, { mode: "full", prefix: "" }) as Machine.FullTargetBuilder<States>
1611
- const history = makeHistoryTargetBuilder(states, "") as Machine.HistoryTargetBuilder<States>
1612
- return <Source extends Machine.StateNodeIdentifier<States>>(source: Source): Machine.TargetBuilder<States, Source> =>
1613
- ({
1614
- none: Topology.makeNoTarget,
1615
- local: makeLocalTargetBuilder(states, stateNodes, source),
1616
- branch: makeBranchTargetBuilder(states, stateNodes, source),
1617
- full,
1618
- history
1619
- }) as Machine.TargetBuilder<States, Source>
1620
- }
1621
-
1622
- const makeInitialSelector = (stateNodes: Machine.StateNodes): unknown => {
1623
- const selector: Record<string, unknown> = {}
1624
- for (const node of stateNodes.byPath.values()) {
1625
- if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
1626
- selector[node.key] = node.type === "atomic" || node.type === "final"
1627
- ? makeSelectionMethod("state", node.path, "initial")
1628
- : Object.freeze({ initial: makeSelectionValue("initial", node.path, "initial") })
1629
- }
1630
- }
1631
- return Object.freeze(selector)
1632
- }
1633
-
1634
1141
  const getInitialSelectionBuilder = (
1635
1142
  initialBuilder: Record<string, any>,
1636
1143
  selection: Topology.TargetSelection
@@ -1648,17 +1155,18 @@ const getInitialSelectionBuilder = (
1648
1155
 
1649
1156
  const captureInitialBranch = (
1650
1157
  definition: unknown,
1651
- stateNodes: Machine.StateNodes,
1652
- initialBuilder: Record<string, any>
1158
+ initialBuilder: Record<string, any>,
1159
+ configuration: boolean
1653
1160
  ): {
1654
1161
  readonly selection: Topology.TargetSelection
1655
1162
  readonly resolve?: (context: any) => unknown
1656
1163
  readonly builder: Record<string, any>
1657
1164
  } => {
1658
- if (typeof definition !== "function") {
1165
+ if (definition !== undefined && typeof definition !== "function") {
1659
1166
  throw new Error("Machine initial definition must be a target-first callback")
1660
1167
  }
1661
- const result = definition(decorateInitialSelectorNode(makeInitialSelector(stateNodes)))
1168
+ const selector = decorateInitialSelection(makeSelectionValue(configuration ? "state" : "initial", "", "initial"))
1169
+ const result = definition === undefined ? selector : definition(selector)
1662
1170
  let selection: Topology.TargetSelection
1663
1171
  let resolve: ((context: any) => unknown) | undefined
1664
1172
  if (Topology.isTargetSelection(result)) {
@@ -1694,13 +1202,17 @@ const validateInitialSelection = (result: unknown, selection: Topology.TargetSel
1694
1202
  const compileInitial = (
1695
1203
  definition: unknown,
1696
1204
  states: Machine.StateTree,
1697
- stateNodes: Machine.StateNodes
1205
+ stateNodes: Machine.StateNodes,
1206
+ configuration = false
1698
1207
  ): {
1699
1208
  readonly initial: (input?: unknown) => unknown
1700
1209
  readonly definition: Machine.InitialDefinition
1701
1210
  } => {
1702
- const initialBuilder = makeSnapshotBuilder(states, { mode: "initial", prefix: "" }) as Record<string, any>
1703
- const branch = captureInitialBranch(definition, stateNodes, initialBuilder)
1211
+ const rootNode = getTargetBuilderNode(stateNodes, "")
1212
+ const initialBuilder = configuration ?
1213
+ makeSnapshotBuilder(states, { mode: "full", prefix: "" }) as Record<string, any>
1214
+ : { "": withFrom((value: unknown) => Topology.makeInitialTarget("", value), "leaf", rootNode.schema !== undefined) }
1215
+ const branch = captureInitialBranch(definition, initialBuilder, configuration)
1704
1216
  return {
1705
1217
  initial: (input?: unknown) => {
1706
1218
  const result = branch.resolve === undefined
@@ -1716,62 +1228,35 @@ const compileInitial = (
1716
1228
  }
1717
1229
  }
1718
1230
 
1719
- export const state: StateConstructor = (<const Node extends ReusableStateNodeConfig>(node: Node): Node => {
1720
- StateDefinition.validateStateDefinitions({ state: node }, "Machine.state")
1721
- return StateDefinition.captureStateDefinitions({ state: node }).state
1722
- }) as StateConstructor
1231
+ export const state = (node: unknown): State<Machine.StateNodeConfig> => {
1232
+ const captured = StateDefinition.captureRoot(node) as Machine.StateNodeConfig
1233
+ const access = makeStateHelpers()
1234
+ return Object.freeze({ ...access, "~effect/Machine/State": "~effect/Machine/State", node: captured })
1235
+ }
1723
1236
 
1724
- export const states: StatesConstructor = (<const States extends Machine.StateSchemas>(
1725
- states: States
1726
- ): Machine.DefinedStates<States> => {
1727
- StateDefinition.validateStateDefinitions(states, "Machine.states")
1728
- const captured = StateDefinition.captureStateDefinitions(states)
1237
+ const makeStateHelpers = (): Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }> => {
1729
1238
  return {
1730
- states: captured,
1731
- path: ((path: string) => path) as Machine.DefinedStates<States>["path"],
1239
+ path: ((path: string) => path) as Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }>["path"],
1732
1240
  get:
1733
1241
  ((snapshot: Machine.AtomicSnapshot<string, unknown>, path: string) =>
1734
1242
  Topology.getSnapshotByPath(snapshot, path).pipe(
1735
1243
  Option.map((snapshot) => snapshot.value)
1736
- )) as Machine.DefinedStates<States>["get"],
1244
+ )) as Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }>["get"],
1737
1245
  getWithParents: ((snapshot, path) => {
1738
1246
  const parents: Record<string, unknown> = {}
1739
1247
  return Topology.getSnapshotByPath(snapshot, path, parents).pipe(
1740
1248
  Option.map((snapshot) => ({ value: snapshot.value, parents }))
1741
1249
  )
1742
- }) as Machine.DefinedStates<States>["getWithParents"],
1743
- getSnapshot: Topology.getSnapshotByPath as unknown as Machine.DefinedStates<States>["getSnapshot"],
1250
+ }) as Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }>["getWithParents"],
1251
+ getSnapshot: Topology.getSnapshotByPath as unknown as Machine.StateAccessors<
1252
+ { readonly "": Machine.StateNodeConfig }
1253
+ >["getSnapshot"],
1744
1254
  matches:
1745
1255
  ((snapshot: Machine.AtomicSnapshot<string, unknown>, path: string) =>
1746
- Option.isSome(Topology.getSnapshotByPath(snapshot, path))) as Machine.DefinedStates<States>["matches"]
1256
+ Option.isSome(Topology.getSnapshotByPath(snapshot, path))) as Machine.StateAccessors<
1257
+ { readonly "": Machine.StateNodeConfig }
1258
+ >["matches"]
1747
1259
  }
1748
- }) as StatesConstructor
1749
-
1750
- type MakeConfig<
1751
- States extends Machine.StateSchemas,
1752
- InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
1753
- Emits extends ReadonlyArray<Machine.TaggedSchema>,
1754
- Input extends Schema.Top,
1755
- InitialE,
1756
- InitialR,
1757
- InternalEvents extends ReadonlyArray<Machine.TaggedSchema>,
1758
- ParentDeclaration extends Parent.Any | undefined
1759
- > = {
1760
- readonly id?: string
1761
- readonly states: States & DefineStateTreeInput<NoInfer<States>>
1762
- readonly events:
1763
- & Machine.EventProtocol<"public", InputEvents>
1764
- & ValidateInputEventProtocol<NoInfer<InputEvents>>
1765
- readonly internalEvents?:
1766
- & Machine.EventProtocol<"internal", InternalEvents>
1767
- & ValidateInternalEventProtocol<
1768
- NoInfer<InputEvents>,
1769
- NoInfer<InternalEvents>
1770
- >
1771
- readonly emittedEvents?: Machine.EventProtocol<"emitted", Emits>
1772
- readonly parent?: ParentDeclaration
1773
- readonly input?: Input
1774
- readonly initial: unknown
1775
1260
  }
1776
1261
 
1777
1262
  type MakeResult<
@@ -1796,40 +1281,7 @@ type MakeResult<
1796
1281
  Machine.ParentEventsOf<ParentDeclaration>
1797
1282
  >
1798
1283
 
1799
- interface Make {
1800
- <
1801
- const States extends Machine.StateSchemas,
1802
- const InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
1803
- const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
1804
- const Input extends Schema.Top = typeof Schema.Void,
1805
- InitialE = never,
1806
- InitialR = never,
1807
- const InternalEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
1808
- const ParentDeclaration extends Parent.Any | undefined = undefined
1809
- >(
1810
- config: MakeConfig<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration>,
1811
- ..._validation: ValidateDefinedStates<NoInfer<States>>
1812
- ): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration>
1813
- <
1814
- const States extends Machine.StateSchemas,
1815
- const InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
1816
- const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
1817
- const Input extends Schema.Top = typeof Schema.Void,
1818
- InitialE = never,
1819
- InitialR = never,
1820
- const InternalEvents extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
1821
- const ParentDeclaration extends Parent.Any | undefined = undefined
1822
- >(
1823
- config:
1824
- & Omit<
1825
- MakeConfig<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration>,
1826
- "states"
1827
- >
1828
- & { readonly states: InvalidDefinedStateTreeInput<States> }
1829
- ): never
1830
- }
1831
-
1832
- export const make: Make = (<
1284
+ export const make = <
1833
1285
  const States extends Machine.StateSchemas,
1834
1286
  const InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
1835
1287
  const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
@@ -1841,7 +1293,8 @@ export const make: Make = (<
1841
1293
  >(
1842
1294
  config: {
1843
1295
  readonly id?: string
1844
- readonly states: States
1296
+ readonly root: State<Machine.StateNodeConfig>
1297
+ readonly initialConfiguration?: unknown
1845
1298
  readonly events: Machine.EventProtocol<"public", InputEvents>
1846
1299
  readonly internalEvents?: Machine.EventProtocol<"internal", InternalEvents>
1847
1300
  readonly emittedEvents?: Machine.EventProtocol<"emitted", Emits>
@@ -1850,25 +1303,31 @@ export const make: Make = (<
1850
1303
  readonly initial: unknown
1851
1304
  }
1852
1305
  ): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration> => {
1853
- StateDefinition.validateStateDefinitions(config.states, "Machine.make")
1306
+ const states = Object.freeze({ "": config.root.node })
1854
1307
  const self = Object.create(Proto)
1855
- self.states = config.states
1308
+ self.states = states
1309
+ self.root = config.root
1856
1310
  self.events = config.events
1857
1311
  self.internalEvents = config.internalEvents ?? Protocol.makeEventProtocol("internal", [] as const)
1858
1312
  self.emittedEvents = config.emittedEvents ?? Protocol.makeEventProtocol("emitted", [] as const)
1859
1313
  self.parent = config.parent
1860
1314
  self.input = config.input
1861
1315
  self.id = config.id
1862
- self.stateNodes = Topology.compileStateNodes(config.states)
1863
- const compiledInitial = compileInitial(config.initial, config.states, self.stateNodes)
1316
+ self.stateNodes = Topology.compileStateNodes(states)
1317
+ const compiledInitial = compileInitial(
1318
+ config.initialConfiguration ?? config.initial,
1319
+ states,
1320
+ self.stateNodes,
1321
+ config.initialConfiguration !== undefined
1322
+ )
1864
1323
  self.initial = compiledInitial.initial
1865
1324
  self.initialDefinition = compiledInitial.definition
1866
- self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes)
1325
+ self.makeTargetBuilder = makeTargetBuilder(states, self.stateNodes)
1867
1326
  self.handlers = Object.create(null)
1868
1327
  self.handle = makeHandle(self)
1869
1328
  Protocol.setProtocol(self)
1870
1329
  return self
1871
- }) as Make
1330
+ }
1872
1331
 
1873
1332
  const flattenEventProtocolInputs = <Kind extends Machine.EventProtocolKind>(
1874
1333
  kind: Kind,
@@ -1888,6 +1347,22 @@ export const events = <const Inputs extends ReadonlyArray<Machine.EventProtocolI
1888
1347
  flattenEventProtocolInputs("public", inputs)
1889
1348
  ) as Machine.EventProtocol<"public", Machine.EventProtocolInputSchemasOf<"public", Inputs>>
1890
1349
 
1350
+ const eventFieldSchemas = (
1351
+ cases: Readonly<Record<string, Schema.Struct.Fields>>
1352
+ ): ReadonlyArray<Machine.TaggedSchema> => {
1353
+ for (const [tag, fields] of Object.entries(cases)) {
1354
+ if (Object.hasOwn(fields, "_tag")) throw new Error(`Machine event "${tag}" fields cannot declare _tag`)
1355
+ }
1356
+ return Object.keys(cases).length === 0 ? [] : [Schema.TaggedUnion(cases)]
1357
+ }
1358
+
1359
+ export const eventsFromFields = (cases: Readonly<Record<string, Schema.Struct.Fields>>) =>
1360
+ Protocol.makeEventProtocol("public", eventFieldSchemas(cases))
1361
+ export const internalEventsFromFields = (cases: Readonly<Record<string, Schema.Struct.Fields>>) =>
1362
+ Protocol.makeEventProtocol("internal", eventFieldSchemas(cases))
1363
+ export const emittedEventsFromFields = (cases: Readonly<Record<string, Schema.Struct.Fields>>) =>
1364
+ Protocol.makeEventProtocol("emitted", eventFieldSchemas(cases))
1365
+
1891
1366
  const makeParent = <
1892
1367
  const Mode extends ParentMode,
1893
1368
  const Events extends ReadonlyArray<Machine.TaggedSchema>
@@ -2038,7 +1513,7 @@ export const planInitial: <
2038
1513
  readonly initialEntryPaths: ReadonlyArray<Machine.StateIdentifier<States>>
2039
1514
  readonly state: Machine.Snapshot<States>
2040
1515
  readonly commands: ReadonlyArray<Command>
2041
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1516
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2042
1517
  readonly microsteps: ReadonlyArray<{
2043
1518
  readonly next: Machine.Snapshot<States>
2044
1519
  readonly event: Machine.EventOf<Events> | InitialEventModel
@@ -2051,7 +1526,7 @@ export const planInitial: <
2051
1526
  >
2052
1527
  readonly commands: ReadonlyArray<Command>
2053
1528
  readonly raisedEvents: ReadonlyArray<Machine.EventOf<Events>>
2054
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1529
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2055
1530
  readonly exitPaths: ReadonlyArray<string>
2056
1531
  readonly entryPaths: ReadonlyArray<string>
2057
1532
  readonly changed: boolean
@@ -2216,7 +1691,7 @@ export const plan: <
2216
1691
  & {
2217
1692
  readonly next: Machine.Snapshot<States>
2218
1693
  readonly commands: ReadonlyArray<Command>
2219
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1694
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2220
1695
  readonly microsteps: ReadonlyArray<{
2221
1696
  readonly next: Machine.Snapshot<States>
2222
1697
  readonly event: Machine.EventOf<Events> | InitialEventModel
@@ -2229,7 +1704,7 @@ export const plan: <
2229
1704
  >
2230
1705
  readonly commands: ReadonlyArray<Command>
2231
1706
  readonly raisedEvents: ReadonlyArray<Machine.EventOf<Events>>
2232
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1707
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2233
1708
  readonly exitPaths: ReadonlyArray<string>
2234
1709
  readonly entryPaths: ReadonlyArray<string>
2235
1710
  readonly changed: boolean
@@ -2450,7 +1925,7 @@ export const start: <
2450
1925
  ExcludeCompatibleRuntime<
2451
1926
  ExecutionServices<InitialR | R>,
2452
1927
  Machine.EventOf<Events>,
2453
- Machine.EmitOf<Emits>
1928
+ Machine.EmittedEventOf<Emits>
2454
1929
  >
2455
1930
  > = internalProcess.start as any
2456
1931
 
@@ -2503,6 +1978,6 @@ export const resume: <
2503
1978
  ExcludeCompatibleRuntime<
2504
1979
  ExecutionServices<R>,
2505
1980
  Machine.EventOf<Events>,
2506
- Machine.EmitOf<Emits>
1981
+ Machine.EmittedEventOf<Emits>
2507
1982
  >
2508
1983
  > = internalProcess.resume as any