@typeonce/effect-machine 0.31.2 → 0.33.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 +168 -162
  2. package/dist/Machine.d.ts +495 -284
  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 +134 -403
  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 +63 -35
  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 +11 -13
  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 +259 -0
  120. package/package.json +1 -1
  121. package/src/Machine.ts +1053 -538
  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 +222 -716
  131. package/src/internal/machine/planner.ts +74 -62
  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 +23 -24
  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,8 +139,8 @@ 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
168
- readonly reenter: boolean
142
+ readonly resolver?: (context: any, enqueue: unknown) => unknown
143
+ readonly reenterSource: boolean
169
144
  readonly declinable: boolean
170
145
  }
171
146
 
@@ -174,7 +149,7 @@ type BranchesTransitionDescriptor = {
174
149
  readonly type: "branches"
175
150
  readonly declarations: unknown
176
151
  readonly resolve: (context: any, enqueue: unknown) => unknown
177
- readonly reenter: boolean
152
+ readonly reenterSource: boolean
178
153
  readonly declinable: boolean
179
154
  }
180
155
 
@@ -193,38 +168,95 @@ const plainTargetSelection = (selection: Topology.TargetSelection): Topology.Tar
193
168
  ? Topology.noneTargetSelection
194
169
  : Topology.makeTargetSelection(selection.kind, selection.path, selection.scope, selection.updatePath)
195
170
 
196
- const transitionOptions = (options: unknown): { readonly reenter: boolean; readonly declinable: boolean } => {
171
+ const transitionOptions = (options: unknown): { readonly declinable: boolean } => {
197
172
  const configuration = typeof options === "object" && options !== null
198
- ? options as { readonly reenter?: unknown; readonly declinable?: unknown }
173
+ ? options as { readonly declinable?: unknown }
199
174
  : {}
200
- return {
201
- reenter: configuration.reenter === true,
202
- declinable: configuration.declinable === true
175
+ if (hasProperty(configuration, "reenter")) {
176
+ throw new Error("Use .reenter() before construction instead of the resolver reenter option")
203
177
  }
178
+ return { declinable: configuration.declinable === true }
204
179
  }
205
180
 
206
181
  const makeDirectTransitionDescriptor = (
207
182
  selection: Topology.TargetSelection,
208
183
  resolve: ((context: any, enqueue: unknown) => unknown) | undefined,
209
- options: unknown
184
+ options: unknown,
185
+ reenterSource = false
210
186
  ): DirectTransitionDescriptor => {
211
- const shared: Omit<DirectTransitionDescriptor, "resolve"> = {
187
+ const shared: Omit<DirectTransitionDescriptor, "resolver"> = {
212
188
  [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
213
189
  type: "direct",
214
190
  selection: plainTargetSelection(selection),
191
+ reenterSource,
215
192
  ...transitionOptions(options)
216
193
  }
217
194
  return resolve === undefined
218
195
  ? Object.freeze(shared)
219
- : Object.freeze({ ...shared, resolve })
196
+ : Object.freeze({ ...shared, resolver: resolve })
220
197
  }
221
198
 
222
- const decorateTransitionSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
199
+ // The authored callback crosses an erased schema boundary here. Each builder
200
+ // retains its construction mode, and planning validates both resulting values.
201
+ const constructSelectionValue = (
202
+ selection: Topology.TargetSelection,
203
+ context: any,
204
+ method: "from" | "decoded",
205
+ value: unknown
206
+ ): unknown => {
207
+ if (selection.kind === "update") return context.owner[method](value)
208
+ if (selection.updatePath === undefined) return context.target[method](value)
209
+ const values = value as { readonly target: unknown; readonly update: unknown }
210
+ return context.target[method](values.target).update(context.owner[method](values.update))
211
+ }
212
+
213
+ const guardedSelection = (
214
+ selection: Topology.TargetSelection,
215
+ predicate: (context: any) => boolean,
216
+ reenterSource: boolean
217
+ ): object => {
218
+ const wrap = (resolve: (context: any, enqueue: unknown) => unknown, options?: unknown) =>
219
+ makeDirectTransitionDescriptor(
220
+ selection,
221
+ (context, enqueue) => predicate(context) ? resolve(context, enqueue) : Topology.makeDeclined(),
222
+ { ...transitionOptions(options), declinable: true },
223
+ reenterSource
224
+ )
225
+ return Object.freeze({
226
+ ...wrap((context) => selection.kind === "none" ? undefined : constructSelectedTarget(context.target)),
227
+ from: (value: (context: any) => unknown) =>
228
+ wrap((context) => constructSelectionValue(selection, context, "from", value(context))),
229
+ decoded: (value: (context: any) => unknown) =>
230
+ wrap((context) => constructSelectionValue(selection, context, "decoded", value(context))),
231
+ resolve: wrap
232
+ })
233
+ }
234
+
235
+ const decorateTransitionSelection = (
236
+ selection: Topology.TargetSelection,
237
+ reenterSource = false
238
+ ): Topology.TargetSelection =>
223
239
  Object.freeze({
224
240
  ...selection,
241
+ ...(reenterSource ? makeDirectTransitionDescriptor(selection, undefined, undefined, true) : {}),
242
+ guard: (predicate: (context: any) => boolean) => guardedSelection(selection, predicate, reenterSource),
243
+ from: (value: (context: any) => unknown) =>
244
+ makeDirectTransitionDescriptor(
245
+ selection,
246
+ (context) => constructSelectionValue(selection, context, "from", value(context)),
247
+ undefined,
248
+ reenterSource
249
+ ),
250
+ decoded: (value: (context: any) => unknown) =>
251
+ makeDirectTransitionDescriptor(
252
+ selection,
253
+ (context) => constructSelectionValue(selection, context, "decoded", value(context)),
254
+ undefined,
255
+ reenterSource
256
+ ),
225
257
  resolve: (resolve: (context: any, enqueue: unknown) => unknown, options?: unknown) =>
226
- makeDirectTransitionDescriptor(selection, resolve, options),
227
- reenter: () => makeDirectTransitionDescriptor(selection, undefined, { reenter: true }),
258
+ makeDirectTransitionDescriptor(selection, resolve, options, reenterSource),
259
+ reenter: () => decorateTransitionSelection(selection, true),
228
260
  updating: (owner: unknown) => {
229
261
  if (typeof owner !== "function") {
230
262
  throw new Error("Machine updating owner must be a state selector")
@@ -237,20 +269,12 @@ const decorateTransitionSelection = (selection: Topology.TargetSelection): Topol
237
269
  throw new Error("Machine updating owner must be addressed by one branch state selector")
238
270
  }
239
271
  return decorateTransitionSelection(
240
- Topology.makeTargetSelection(selection.kind, selection.path, selection.scope, ownerSelection.path)
272
+ Topology.makeTargetSelection(selection.kind, selection.path, selection.scope, ownerSelection.path),
273
+ reenterSource
241
274
  )
242
275
  }
243
276
  })
244
277
 
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
- }
253
-
254
278
  const noneTransitionSelection = decorateTransitionSelection(Topology.noneTargetSelection)
255
279
 
256
280
  const makeInitialBuilderDescriptor = (
@@ -266,6 +290,10 @@ const makeInitialBuilderDescriptor = (
266
290
  const decorateInitialSelection = (selection: Topology.TargetSelection): Topology.TargetSelection =>
267
291
  Object.freeze({
268
292
  ...selection,
293
+ from: (value: (context: any) => unknown) =>
294
+ makeInitialBuilderDescriptor(selection, (context) => context.target.from(value(context))),
295
+ decoded: (value: (context: any) => unknown) =>
296
+ makeInitialBuilderDescriptor(selection, (context) => context.target.decoded(value(context))),
269
297
  resolve: (resolve: (context: any) => unknown) => makeInitialBuilderDescriptor(selection, resolve)
270
298
  })
271
299
 
@@ -292,7 +320,12 @@ const decorateInitialSelectorNode = (node: unknown): unknown => {
292
320
 
293
321
  const decorateTransitionSelectorNode = (node: unknown): unknown => {
294
322
  if (Topology.isTargetSelection(node)) {
295
- if (node.kind === "update") return decorateStateUpdateSelection(node)
323
+ if (node.kind === "state" && node.scope === "full" && hasProperty(node, "initial")) {
324
+ return Object.freeze({
325
+ ...decorateTransitionSelection(node),
326
+ initial: decorateTransitionSelection(node.initial as Topology.TargetSelection)
327
+ })
328
+ }
296
329
  return node === Topology.noneTargetSelection ? noneTransitionSelection : decorateTransitionSelection(node)
297
330
  }
298
331
  if (typeof node === "function") {
@@ -314,29 +347,31 @@ const decorateTransitionSelectorNode = (node: unknown): unknown => {
314
347
  return node
315
348
  }
316
349
 
350
+ const decorateBranches = (declarations: unknown, reenterSource = false): object =>
351
+ Object.freeze({
352
+ reenter: () => decorateBranches(declarations, true),
353
+ resolve: (
354
+ resolve: (context: any, enqueue: unknown) => unknown,
355
+ options?: unknown
356
+ ): BranchesTransitionDescriptor =>
357
+ Object.freeze({
358
+ [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
359
+ type: "branches",
360
+ declarations,
361
+ resolve,
362
+ reenterSource,
363
+ ...transitionOptions(options)
364
+ })
365
+ })
366
+
317
367
  const makeTransitionSelector = (
318
368
  stateNodes: Machine.StateNodes,
319
369
  source: string
320
- ): unknown => {
321
- const selector = {
322
- ...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source)) as Record<string, unknown>
323
- }
324
- selector.branches = (declarations: unknown) =>
325
- Object.freeze({
326
- resolve: (
327
- resolve: (context: any, enqueue: unknown) => unknown,
328
- options?: unknown
329
- ): BranchesTransitionDescriptor =>
330
- Object.freeze({
331
- [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
332
- type: "branches",
333
- declarations,
334
- resolve,
335
- ...transitionOptions(options)
336
- })
337
- })
338
- return Object.freeze(selector)
339
- }
370
+ ): unknown =>
371
+ Object.freeze({
372
+ ...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source)) as Record<string, unknown>,
373
+ branches: (declarations: unknown) => decorateBranches(declarations)
374
+ })
340
375
 
341
376
  const normalizeTransitionBuilder = (
342
377
  transition: (selector: unknown) => unknown,
@@ -344,7 +379,7 @@ const normalizeTransitionBuilder = (
344
379
  path: string
345
380
  ): unknown => {
346
381
  const result = transition(makeTransitionSelector(stateNodes, path))
347
- if (Topology.isTargetSelection(result)) {
382
+ if (Topology.isTargetSelection(result) && !hasProperty(result, TransitionBuilderDescriptorTypeId)) {
348
383
  const selection = plainTargetSelection(result)
349
384
  return { target: () => selection }
350
385
  }
@@ -354,14 +389,14 @@ const normalizeTransitionBuilder = (
354
389
  return {
355
390
  branches: () => descriptor.declarations,
356
391
  resolve: descriptor.resolve,
357
- reenter: descriptor.reenter,
392
+ reenter: descriptor.reenterSource,
358
393
  declinable: descriptor.declinable
359
394
  }
360
395
  }
361
396
  return {
362
397
  target: () => descriptor.selection,
363
- resolve: descriptor.resolve,
364
- reenter: descriptor.reenter,
398
+ resolve: descriptor.resolver,
399
+ reenter: descriptor.reenterSource,
365
400
  declinable: descriptor.declinable
366
401
  }
367
402
  }
@@ -430,7 +465,7 @@ const makeSelectionNode = (
430
465
  }
431
466
  if (
432
467
  scope === "branch" && source !== undefined && node.schema !== undefined &&
433
- (source === path || source.startsWith(`${path}.`)) &&
468
+ (source === path || Configuration.isDescendantOf(source, path)) &&
434
469
  getTargetBuilderNode(stateNodes, source).type !== "choice"
435
470
  ) {
436
471
  Object.defineProperty(method, "update", {
@@ -439,6 +474,9 @@ const makeSelectionNode = (
439
474
  })
440
475
  }
441
476
  }
477
+ if (node.type === "atomic" && scope === "branch" && source === path && node.schema !== undefined) {
478
+ Object.defineProperty(method, "update", { value: makeStateUpdateSelection(path, "branch"), enumerable: true })
479
+ }
442
480
  return method
443
481
  }
444
482
 
@@ -463,15 +501,12 @@ const makeTargetSelector = (
463
501
  stateNodes: Machine.StateNodes,
464
502
  source: string
465
503
  ): 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
- }
504
+ const full = Object.assign({}, makeSelectionValue("state", "", "full"), {
505
+ initial: makeSelectionValue("initial", "", "full")
506
+ })
472
507
  const branch: Record<string, unknown> = {}
473
- const root = getTargetBuilderNode(stateNodes, source.split(".")[0]!)
474
- branch[root.key] = makeSelectionNode(stateNodes, root.path, "branch", source)
508
+ const root = getTargetBuilderNode(stateNodes, stateNodes.roots[0]!)
509
+ addSelectionChildren(branch, stateNodes, root.path, "branch", source)
475
510
  const local: Record<string, unknown> = {}
476
511
  const localScope = getLocalTargetScope(stateNodes, source)
477
512
  if (localScope !== undefined) {
@@ -486,10 +521,15 @@ const makeTargetSelector = (
486
521
  }
487
522
  return {
488
523
  none: Topology.noneTargetSelection,
524
+ self: getTargetBuilderNode(stateNodes, source).schema === undefined ||
525
+ getTargetBuilderNode(stateNodes, source).type === "final"
526
+ ? {}
527
+ : { update: makeStateUpdateSelection(source, "branch") },
528
+ root: makeSelectionNode(stateNodes, root.path, "branch", source),
489
529
  local,
490
530
  branch,
491
531
  full,
492
- history: makeHistorySelectionTree(stateNodes, undefined)
532
+ history: makeHistorySelectionTree(stateNodes, "")
493
533
  }
494
534
  }
495
535
 
@@ -580,6 +620,7 @@ const getSelectionBuilder = (
580
620
  let parts = selection.path!.split(".")
581
621
  if (selection.kind === "history") {
582
622
  builder = target.history
623
+ if (selection.path !== "") parts.unshift("")
583
624
  } else if (selection.scope === "local") {
584
625
  builder = target.local
585
626
  const scope = getLocalTargetScope(stateNodes, source)
@@ -588,11 +629,12 @@ const getSelectionBuilder = (
588
629
  builder = builder.with
589
630
  parts = []
590
631
  } else {
591
- parts = selection.path!.slice(scope.length + 1).split(".")
632
+ parts = (scope === "" ? selection.path! : selection.path!.slice(scope.length + 1)).split(".")
592
633
  }
593
634
  }
594
635
  } else if (selection.scope === "branch") {
595
636
  builder = target.branch
637
+ if (selection.path !== "") parts.unshift("")
596
638
  } else {
597
639
  builder = target.full
598
640
  }
@@ -645,7 +687,7 @@ const validateResolvedSelection = (
645
687
  (selectedNode?.type === "compound" || selectedNode?.type === "parallel")
646
688
  if (
647
689
  resultPath === undefined ||
648
- (resultPath !== selection.path && !(acceptsDescendant && resultPath.startsWith(`${selection.path}.`)))
690
+ (resultPath !== selection.path && !(acceptsDescendant && Configuration.isDescendantOf(resultPath, selection.path!)))
649
691
  ) {
650
692
  throw new Error(
651
693
  `Machine transition resolver selected "${selection.path}" but constructed "${resultPath ?? "<invalid>"}"`
@@ -737,6 +779,11 @@ const captureNamedBranches = (
737
779
  if (target.updatePath !== undefined) {
738
780
  throw new Error(`Machine transition branch "${key}" cannot declare an updating target`)
739
781
  }
782
+ if (hasProperty(target, TransitionBuilderDescriptorTypeId)) {
783
+ throw new Error(
784
+ `Machine transition branch "${key}" requires a topology selection; apply .reenter() to .branches(...)`
785
+ )
786
+ }
740
787
  if (title !== undefined && (typeof title !== "string" || title.length === 0)) {
741
788
  throw new Error(`Machine transition branch "${key}" title must be a non-empty string`)
742
789
  }
@@ -1005,9 +1052,9 @@ const captureInvokeDefinition = (
1005
1052
  captured[key] = captureTransition(captured[key], stateNodes, path, key)
1006
1053
  }
1007
1054
  }
1008
- return captured
1055
+ return InvocationDefinition.capture(captured, path)
1009
1056
  })
1010
- if (Array.isArray(authored)) return capturedDefinitions
1057
+ if (Array.isArray(authored)) return Object.freeze(capturedDefinitions)
1011
1058
  return capturedDefinitions[0]
1012
1059
  }
1013
1060
 
@@ -1028,6 +1075,14 @@ const flattenHandlers = (
1028
1075
  throw new Error(`Machine expected state "${path}" handler to be an object`)
1029
1076
  }
1030
1077
  const { states: childConfig, ...stateConfig } = nodeConfig as Record<string, unknown>
1078
+ if (typeof stateConfig.history === "object" && stateConfig.history !== null) {
1079
+ stateConfig.history = Object.freeze(Object.fromEntries(
1080
+ Object.entries(stateConfig.history).map(([key, entry]) => [
1081
+ key,
1082
+ typeof entry === "object" && entry !== null ? Object.freeze({ ...entry }) : entry
1083
+ ])
1084
+ ))
1085
+ }
1031
1086
  const on = stateConfig.on
1032
1087
  if (typeof on === "object" && on !== null) {
1033
1088
  const capturedOn = captureEventHandlers(on, stateNodes, path)
@@ -1073,7 +1128,7 @@ const flattenHandlers = (
1073
1128
  const makeHandle = (self: Definition.Any): Definition.Any["handle"] =>
1074
1129
  ((config: Record<string, unknown>) => {
1075
1130
  const handlers: Record<PropertyKey, Machine.AnyStateConfig> = Object.create(null)
1076
- flattenHandlers(handlers, self.stateNodes, self.states, "", config)
1131
+ flattenHandlers(handlers, self.stateNodes, self.states, "", { "": config })
1077
1132
  return makeWithHandlers(self, handlers)
1078
1133
  }) as Definition.Any["handle"]
1079
1134
 
@@ -1114,523 +1169,6 @@ export const isFinal = <
1114
1169
  state: Machine.Snapshot<States>
1115
1170
  ): state is Machine.SnapshotContainingFinal<States, FinalStates> => internalPlanner.isFinal(machine as any, state)
1116
1171
 
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
1172
  const getInitialSelectionBuilder = (
1635
1173
  initialBuilder: Record<string, any>,
1636
1174
  selection: Topology.TargetSelection
@@ -1648,17 +1186,18 @@ const getInitialSelectionBuilder = (
1648
1186
 
1649
1187
  const captureInitialBranch = (
1650
1188
  definition: unknown,
1651
- stateNodes: Machine.StateNodes,
1652
- initialBuilder: Record<string, any>
1189
+ initialBuilder: Record<string, any>,
1190
+ configuration: boolean
1653
1191
  ): {
1654
1192
  readonly selection: Topology.TargetSelection
1655
1193
  readonly resolve?: (context: any) => unknown
1656
1194
  readonly builder: Record<string, any>
1657
1195
  } => {
1658
- if (typeof definition !== "function") {
1196
+ if (definition !== undefined && typeof definition !== "function") {
1659
1197
  throw new Error("Machine initial definition must be a target-first callback")
1660
1198
  }
1661
- const result = definition(decorateInitialSelectorNode(makeInitialSelector(stateNodes)))
1199
+ const selector = decorateInitialSelection(makeSelectionValue(configuration ? "state" : "initial", "", "initial"))
1200
+ const result = definition === undefined ? selector : definition(selector)
1662
1201
  let selection: Topology.TargetSelection
1663
1202
  let resolve: ((context: any) => unknown) | undefined
1664
1203
  if (Topology.isTargetSelection(result)) {
@@ -1694,13 +1233,17 @@ const validateInitialSelection = (result: unknown, selection: Topology.TargetSel
1694
1233
  const compileInitial = (
1695
1234
  definition: unknown,
1696
1235
  states: Machine.StateTree,
1697
- stateNodes: Machine.StateNodes
1236
+ stateNodes: Machine.StateNodes,
1237
+ configuration = false
1698
1238
  ): {
1699
1239
  readonly initial: (input?: unknown) => unknown
1700
1240
  readonly definition: Machine.InitialDefinition
1701
1241
  } => {
1702
- const initialBuilder = makeSnapshotBuilder(states, { mode: "initial", prefix: "" }) as Record<string, any>
1703
- const branch = captureInitialBranch(definition, stateNodes, initialBuilder)
1242
+ const rootNode = getTargetBuilderNode(stateNodes, "")
1243
+ const initialBuilder = configuration ?
1244
+ makeSnapshotBuilder(states, { mode: "full", prefix: "" }) as Record<string, any>
1245
+ : { "": withFrom((value: unknown) => Topology.makeInitialTarget("", value), "leaf", rootNode.schema !== undefined) }
1246
+ const branch = captureInitialBranch(definition, initialBuilder, configuration)
1704
1247
  return {
1705
1248
  initial: (input?: unknown) => {
1706
1249
  const result = branch.resolve === undefined
@@ -1716,62 +1259,35 @@ const compileInitial = (
1716
1259
  }
1717
1260
  }
1718
1261
 
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
1262
+ export const state = (node: unknown): State<Machine.StateNodeConfig> => {
1263
+ const captured = StateDefinition.captureRoot(node) as Machine.StateNodeConfig
1264
+ const access = makeStateHelpers()
1265
+ return Object.freeze({ ...access, "~effect/Machine/State": "~effect/Machine/State", node: captured })
1266
+ }
1723
1267
 
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)
1268
+ const makeStateHelpers = (): Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }> => {
1729
1269
  return {
1730
- states: captured,
1731
- path: ((path: string) => path) as Machine.DefinedStates<States>["path"],
1270
+ path: ((path: string) => path) as Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }>["path"],
1732
1271
  get:
1733
1272
  ((snapshot: Machine.AtomicSnapshot<string, unknown>, path: string) =>
1734
1273
  Topology.getSnapshotByPath(snapshot, path).pipe(
1735
1274
  Option.map((snapshot) => snapshot.value)
1736
- )) as Machine.DefinedStates<States>["get"],
1275
+ )) as Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }>["get"],
1737
1276
  getWithParents: ((snapshot, path) => {
1738
1277
  const parents: Record<string, unknown> = {}
1739
1278
  return Topology.getSnapshotByPath(snapshot, path, parents).pipe(
1740
1279
  Option.map((snapshot) => ({ value: snapshot.value, parents }))
1741
1280
  )
1742
- }) as Machine.DefinedStates<States>["getWithParents"],
1743
- getSnapshot: Topology.getSnapshotByPath as unknown as Machine.DefinedStates<States>["getSnapshot"],
1281
+ }) as Machine.StateAccessors<{ readonly "": Machine.StateNodeConfig }>["getWithParents"],
1282
+ getSnapshot: Topology.getSnapshotByPath as unknown as Machine.StateAccessors<
1283
+ { readonly "": Machine.StateNodeConfig }
1284
+ >["getSnapshot"],
1744
1285
  matches:
1745
1286
  ((snapshot: Machine.AtomicSnapshot<string, unknown>, path: string) =>
1746
- Option.isSome(Topology.getSnapshotByPath(snapshot, path))) as Machine.DefinedStates<States>["matches"]
1287
+ Option.isSome(Topology.getSnapshotByPath(snapshot, path))) as Machine.StateAccessors<
1288
+ { readonly "": Machine.StateNodeConfig }
1289
+ >["matches"]
1747
1290
  }
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
1291
  }
1776
1292
 
1777
1293
  type MakeResult<
@@ -1796,40 +1312,7 @@ type MakeResult<
1796
1312
  Machine.ParentEventsOf<ParentDeclaration>
1797
1313
  >
1798
1314
 
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 = (<
1315
+ export const make = <
1833
1316
  const States extends Machine.StateSchemas,
1834
1317
  const InputEvents extends ReadonlyArray<Machine.TaggedSchema>,
1835
1318
  const Emits extends ReadonlyArray<Machine.TaggedSchema> = readonly [],
@@ -1841,7 +1324,8 @@ export const make: Make = (<
1841
1324
  >(
1842
1325
  config: {
1843
1326
  readonly id?: string
1844
- readonly states: States
1327
+ readonly root: State<Machine.StateNodeConfig>
1328
+ readonly initialConfiguration?: unknown
1845
1329
  readonly events: Machine.EventProtocol<"public", InputEvents>
1846
1330
  readonly internalEvents?: Machine.EventProtocol<"internal", InternalEvents>
1847
1331
  readonly emittedEvents?: Machine.EventProtocol<"emitted", Emits>
@@ -1850,25 +1334,31 @@ export const make: Make = (<
1850
1334
  readonly initial: unknown
1851
1335
  }
1852
1336
  ): MakeResult<States, InputEvents, Emits, Input, InitialE, InitialR, InternalEvents, ParentDeclaration> => {
1853
- StateDefinition.validateStateDefinitions(config.states, "Machine.make")
1337
+ const states = Object.freeze({ "": config.root.node })
1854
1338
  const self = Object.create(Proto)
1855
- self.states = config.states
1339
+ self.states = states
1340
+ self.root = config.root
1856
1341
  self.events = config.events
1857
1342
  self.internalEvents = config.internalEvents ?? Protocol.makeEventProtocol("internal", [] as const)
1858
1343
  self.emittedEvents = config.emittedEvents ?? Protocol.makeEventProtocol("emitted", [] as const)
1859
1344
  self.parent = config.parent
1860
1345
  self.input = config.input
1861
1346
  self.id = config.id
1862
- self.stateNodes = Topology.compileStateNodes(config.states)
1863
- const compiledInitial = compileInitial(config.initial, config.states, self.stateNodes)
1347
+ self.stateNodes = Topology.compileStateNodes(states)
1348
+ const compiledInitial = compileInitial(
1349
+ config.initialConfiguration ?? config.initial,
1350
+ states,
1351
+ self.stateNodes,
1352
+ config.initialConfiguration !== undefined
1353
+ )
1864
1354
  self.initial = compiledInitial.initial
1865
1355
  self.initialDefinition = compiledInitial.definition
1866
- self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes)
1356
+ self.makeTargetBuilder = makeTargetBuilder(states, self.stateNodes)
1867
1357
  self.handlers = Object.create(null)
1868
1358
  self.handle = makeHandle(self)
1869
1359
  Protocol.setProtocol(self)
1870
1360
  return self
1871
- }) as Make
1361
+ }
1872
1362
 
1873
1363
  const flattenEventProtocolInputs = <Kind extends Machine.EventProtocolKind>(
1874
1364
  kind: Kind,
@@ -1888,6 +1378,22 @@ export const events = <const Inputs extends ReadonlyArray<Machine.EventProtocolI
1888
1378
  flattenEventProtocolInputs("public", inputs)
1889
1379
  ) as Machine.EventProtocol<"public", Machine.EventProtocolInputSchemasOf<"public", Inputs>>
1890
1380
 
1381
+ const eventFieldSchemas = (
1382
+ cases: Readonly<Record<string, Schema.Struct.Fields>>
1383
+ ): ReadonlyArray<Machine.TaggedSchema> => {
1384
+ for (const [tag, fields] of Object.entries(cases)) {
1385
+ if (Object.hasOwn(fields, "_tag")) throw new Error(`Machine event "${tag}" fields cannot declare _tag`)
1386
+ }
1387
+ return Object.keys(cases).length === 0 ? [] : [Schema.TaggedUnion(cases)]
1388
+ }
1389
+
1390
+ export const eventsFromFields = (cases: Readonly<Record<string, Schema.Struct.Fields>>) =>
1391
+ Protocol.makeEventProtocol("public", eventFieldSchemas(cases))
1392
+ export const internalEventsFromFields = (cases: Readonly<Record<string, Schema.Struct.Fields>>) =>
1393
+ Protocol.makeEventProtocol("internal", eventFieldSchemas(cases))
1394
+ export const emittedEventsFromFields = (cases: Readonly<Record<string, Schema.Struct.Fields>>) =>
1395
+ Protocol.makeEventProtocol("emitted", eventFieldSchemas(cases))
1396
+
1891
1397
  const makeParent = <
1892
1398
  const Mode extends ParentMode,
1893
1399
  const Events extends ReadonlyArray<Machine.TaggedSchema>
@@ -2038,7 +1544,7 @@ export const planInitial: <
2038
1544
  readonly initialEntryPaths: ReadonlyArray<Machine.StateIdentifier<States>>
2039
1545
  readonly state: Machine.Snapshot<States>
2040
1546
  readonly commands: ReadonlyArray<Command>
2041
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1547
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2042
1548
  readonly microsteps: ReadonlyArray<{
2043
1549
  readonly next: Machine.Snapshot<States>
2044
1550
  readonly event: Machine.EventOf<Events> | InitialEventModel
@@ -2051,7 +1557,7 @@ export const planInitial: <
2051
1557
  >
2052
1558
  readonly commands: ReadonlyArray<Command>
2053
1559
  readonly raisedEvents: ReadonlyArray<Machine.EventOf<Events>>
2054
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1560
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2055
1561
  readonly exitPaths: ReadonlyArray<string>
2056
1562
  readonly entryPaths: ReadonlyArray<string>
2057
1563
  readonly changed: boolean
@@ -2216,7 +1722,7 @@ export const plan: <
2216
1722
  & {
2217
1723
  readonly next: Machine.Snapshot<States>
2218
1724
  readonly commands: ReadonlyArray<Command>
2219
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1725
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2220
1726
  readonly microsteps: ReadonlyArray<{
2221
1727
  readonly next: Machine.Snapshot<States>
2222
1728
  readonly event: Machine.EventOf<Events> | InitialEventModel
@@ -2229,7 +1735,7 @@ export const plan: <
2229
1735
  >
2230
1736
  readonly commands: ReadonlyArray<Command>
2231
1737
  readonly raisedEvents: ReadonlyArray<Machine.EventOf<Events>>
2232
- readonly emittedEvents: ReadonlyArray<Machine.EmitOf<Emits>>
1738
+ readonly emittedEvents: ReadonlyArray<Machine.EmittedEventOf<Emits>>
2233
1739
  readonly exitPaths: ReadonlyArray<string>
2234
1740
  readonly entryPaths: ReadonlyArray<string>
2235
1741
  readonly changed: boolean
@@ -2450,7 +1956,7 @@ export const start: <
2450
1956
  ExcludeCompatibleRuntime<
2451
1957
  ExecutionServices<InitialR | R>,
2452
1958
  Machine.EventOf<Events>,
2453
- Machine.EmitOf<Emits>
1959
+ Machine.EmittedEventOf<Emits>
2454
1960
  >
2455
1961
  > = internalProcess.start as any
2456
1962
 
@@ -2503,6 +2009,6 @@ export const resume: <
2503
2009
  ExcludeCompatibleRuntime<
2504
2010
  ExecutionServices<R>,
2505
2011
  Machine.EventOf<Events>,
2506
- Machine.EmitOf<Emits>
2012
+ Machine.EmittedEventOf<Emits>
2507
2013
  >
2508
2014
  > = internalProcess.resume as any