@typeonce/effect-machine 0.2.0 → 0.4.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 (150) hide show
  1. package/README.md +345 -42
  2. package/dist/Machine.d.ts +1273 -347
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +161 -426
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/activities.d.ts +57 -0
  7. package/dist/internal/machine/activities.d.ts.map +1 -0
  8. package/dist/internal/machine/activities.js +49 -0
  9. package/dist/internal/machine/activities.js.map +1 -0
  10. package/dist/internal/machine/atom.d.ts +69 -0
  11. package/dist/internal/machine/atom.d.ts.map +1 -0
  12. package/dist/{AtomMachine.js → internal/machine/atom.js} +19 -94
  13. package/dist/internal/machine/atom.js.map +1 -0
  14. package/dist/internal/machine/cluster.d.ts +63 -0
  15. package/dist/internal/machine/cluster.d.ts.map +1 -0
  16. package/dist/{ClusterMachine.js → internal/machine/cluster.js} +15 -119
  17. package/dist/internal/machine/cluster.js.map +1 -0
  18. package/dist/internal/machine/command.d.ts +16 -0
  19. package/dist/internal/machine/command.d.ts.map +1 -0
  20. package/dist/internal/machine/command.js +45 -0
  21. package/dist/internal/machine/command.js.map +1 -0
  22. package/dist/internal/machine/commandRuntime.d.ts +13 -0
  23. package/dist/internal/machine/commandRuntime.d.ts.map +1 -0
  24. package/dist/internal/machine/commandRuntime.js +16 -0
  25. package/dist/internal/machine/commandRuntime.js.map +1 -0
  26. package/dist/internal/{machineModel.d.ts → machine/configuration.d.ts} +32 -38
  27. package/dist/internal/machine/configuration.d.ts.map +1 -0
  28. package/dist/internal/machine/configuration.js +965 -0
  29. package/dist/internal/machine/configuration.js.map +1 -0
  30. package/dist/internal/{machineErrors.d.ts → machine/errors.d.ts} +3 -3
  31. package/dist/internal/machine/errors.d.ts.map +1 -0
  32. package/dist/internal/{machineErrors.js → machine/errors.js} +1 -1
  33. package/dist/internal/machine/errors.js.map +1 -0
  34. package/dist/internal/machine/executionPlan.d.ts +49 -0
  35. package/dist/internal/machine/executionPlan.d.ts.map +1 -0
  36. package/dist/internal/machine/executionPlan.js +667 -0
  37. package/dist/internal/machine/executionPlan.js.map +1 -0
  38. package/dist/internal/machine/invocation.d.ts +23 -0
  39. package/dist/internal/machine/invocation.d.ts.map +1 -0
  40. package/dist/internal/machine/invocation.js +77 -0
  41. package/dist/internal/machine/invocation.js.map +1 -0
  42. package/dist/internal/machine/machine.d.ts +183 -0
  43. package/dist/internal/machine/machine.d.ts.map +1 -0
  44. package/dist/internal/machine/machine.js +552 -0
  45. package/dist/internal/machine/machine.js.map +1 -0
  46. package/dist/internal/machine/planner.d.ts +182 -0
  47. package/dist/internal/machine/planner.d.ts.map +1 -0
  48. package/dist/internal/machine/planner.js +1082 -0
  49. package/dist/internal/machine/planner.js.map +1 -0
  50. package/dist/internal/{machineProcess.d.ts → machine/process.d.ts} +10 -4
  51. package/dist/internal/machine/process.d.ts.map +1 -0
  52. package/dist/internal/machine/process.js +446 -0
  53. package/dist/internal/machine/process.js.map +1 -0
  54. package/dist/internal/machine/protocol.d.ts +34 -0
  55. package/dist/internal/machine/protocol.d.ts.map +1 -0
  56. package/dist/internal/machine/protocol.js +182 -0
  57. package/dist/internal/machine/protocol.js.map +1 -0
  58. package/dist/internal/machine/readiness.d.ts +4 -0
  59. package/dist/internal/machine/readiness.d.ts.map +1 -0
  60. package/dist/internal/machine/readiness.js +2 -0
  61. package/dist/internal/machine/readiness.js.map +1 -0
  62. package/dist/internal/{machineRuntime.d.ts → machine/runtime.d.ts} +104 -2
  63. package/dist/internal/machine/runtime.d.ts.map +1 -0
  64. package/dist/internal/machine/runtime.js +1381 -0
  65. package/dist/internal/machine/runtime.js.map +1 -0
  66. package/dist/internal/machine/serialization.d.ts +14 -0
  67. package/dist/internal/machine/serialization.d.ts.map +1 -0
  68. package/dist/internal/machine/serialization.js +338 -0
  69. package/dist/internal/machine/serialization.js.map +1 -0
  70. package/dist/internal/machine/stateDefinition.d.ts +15 -0
  71. package/dist/internal/machine/stateDefinition.d.ts.map +1 -0
  72. package/dist/internal/machine/stateDefinition.js +210 -0
  73. package/dist/internal/machine/stateDefinition.js.map +1 -0
  74. package/dist/internal/machine/symbols.d.ts +3 -0
  75. package/dist/internal/machine/symbols.d.ts.map +1 -0
  76. package/dist/internal/machine/symbols.js +3 -0
  77. package/dist/internal/machine/symbols.js.map +1 -0
  78. package/dist/internal/machine/topology.d.ts +98 -0
  79. package/dist/internal/machine/topology.d.ts.map +1 -0
  80. package/dist/internal/machine/topology.js +347 -0
  81. package/dist/internal/machine/topology.js.map +1 -0
  82. package/dist/internal/testing/machine/arbitrary.d.ts +36 -0
  83. package/dist/internal/testing/machine/arbitrary.d.ts.map +1 -0
  84. package/dist/internal/testing/machine/arbitrary.js +68 -0
  85. package/dist/internal/testing/machine/arbitrary.js.map +1 -0
  86. package/dist/internal/testing/machine/finiteModel.d.ts +322 -0
  87. package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -0
  88. package/dist/internal/testing/machine/finiteModel.js +976 -0
  89. package/dist/internal/testing/machine/finiteModel.js.map +1 -0
  90. package/dist/internal/testing/machine/referenceModel.d.ts +203 -0
  91. package/dist/internal/testing/machine/referenceModel.d.ts.map +1 -0
  92. package/dist/internal/testing/machine/referenceModel.js +1012 -0
  93. package/dist/internal/testing/machine/referenceModel.js.map +1 -0
  94. package/dist/internal/testing/machine/runtime.d.ts +328 -0
  95. package/dist/internal/testing/machine/runtime.d.ts.map +1 -0
  96. package/dist/internal/testing/machine/runtime.js +437 -0
  97. package/dist/internal/testing/machine/runtime.js.map +1 -0
  98. package/dist/internal/testing/machine/verification.d.ts +33 -0
  99. package/dist/internal/testing/machine/verification.d.ts.map +1 -0
  100. package/dist/internal/testing/machine/verification.js +1446 -0
  101. package/dist/internal/testing/machine/verification.js.map +1 -0
  102. package/dist/testing/MachineTest.d.ts +606 -0
  103. package/dist/testing/MachineTest.d.ts.map +1 -0
  104. package/dist/testing/MachineTest.js +102 -0
  105. package/dist/testing/MachineTest.js.map +1 -0
  106. package/dist/testing/index.d.ts +7 -0
  107. package/dist/testing/index.d.ts.map +1 -0
  108. package/dist/testing/index.js +7 -0
  109. package/dist/testing/index.js.map +1 -0
  110. package/dist/{ClusterMachine.d.ts → unstable/cluster/ClusterMachine.d.ts} +22 -30
  111. package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -0
  112. package/dist/unstable/cluster/ClusterMachine.js +126 -0
  113. package/dist/unstable/cluster/ClusterMachine.js.map +1 -0
  114. package/dist/{cluster.js → unstable/cluster/index.d.ts} +1 -1
  115. package/dist/unstable/cluster/index.d.ts.map +1 -0
  116. package/dist/{cluster.d.ts → unstable/cluster/index.js} +1 -1
  117. package/dist/unstable/cluster/index.js.map +1 -0
  118. package/dist/{AtomMachine.d.ts → unstable/reactivity/AtomMachine.d.ts} +40 -23
  119. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -0
  120. package/dist/unstable/reactivity/AtomMachine.js +113 -0
  121. package/dist/unstable/reactivity/AtomMachine.js.map +1 -0
  122. package/dist/{reactivity.js → unstable/reactivity/index.d.ts} +1 -1
  123. package/dist/unstable/reactivity/index.d.ts.map +1 -0
  124. package/dist/{reactivity.d.ts → unstable/reactivity/index.js} +1 -1
  125. package/dist/unstable/reactivity/index.js.map +1 -0
  126. package/docs/agent-guide.md +271 -38
  127. package/package.json +23 -13
  128. package/dist/AtomMachine.d.ts.map +0 -1
  129. package/dist/AtomMachine.js.map +0 -1
  130. package/dist/ClusterMachine.d.ts.map +0 -1
  131. package/dist/ClusterMachine.js.map +0 -1
  132. package/dist/cluster.d.ts.map +0 -1
  133. package/dist/cluster.js.map +0 -1
  134. package/dist/internal/machineErrors.d.ts.map +0 -1
  135. package/dist/internal/machineErrors.js.map +0 -1
  136. package/dist/internal/machineModel.d.ts.map +0 -1
  137. package/dist/internal/machineModel.js +0 -766
  138. package/dist/internal/machineModel.js.map +0 -1
  139. package/dist/internal/machinePlanner.d.ts +0 -74
  140. package/dist/internal/machinePlanner.d.ts.map +0 -1
  141. package/dist/internal/machinePlanner.js +0 -621
  142. package/dist/internal/machinePlanner.js.map +0 -1
  143. package/dist/internal/machineProcess.d.ts.map +0 -1
  144. package/dist/internal/machineProcess.js +0 -181
  145. package/dist/internal/machineProcess.js.map +0 -1
  146. package/dist/internal/machineRuntime.d.ts.map +0 -1
  147. package/dist/internal/machineRuntime.js +0 -365
  148. package/dist/internal/machineRuntime.js.map +0 -1
  149. package/dist/reactivity.d.ts.map +0 -1
  150. package/dist/reactivity.js.map +0 -1
@@ -0,0 +1,1082 @@
1
+ /**
2
+ * Internal machine planning helpers.
3
+ *
4
+ * @since 4.0.0
5
+ */
6
+ import * as Cause from "effect/Cause";
7
+ import * as Effect from "effect/Effect";
8
+ import { getTargetBuilder, makeCollector } from "./command.js";
9
+ import { captureHistory, compareDocumentOrder, completeConfigurationEffect, completeConfigurationSync, configurationFromHistoryRecord, getActiveLeafPathFrom, getActiveLeafPaths, getActiveValue, getHistoryRecord, getInitialEntryPaths, getLeafPath, getParentValue, getParentValues, getPathToRoot, getRootPath, isActiveFinalConfiguration, isDescendantOf, isPathInSubtree, normalizeConfiguration, normalizeConfigurationEffect, normalizeConfigurationSync, normalizeTargetConfigurationSync, pathDepth, snapshotFromConfiguration, snapshotFromConfigurationAtPath, validateInitialConfiguration } from "./configuration.js";
10
+ import { InfiniteTransitionError, MachineSchemaDecodeError, StartupError } from "./errors.js";
11
+ import { decodeEventSync, decodeInputSync, decodeStateValueSync } from "./protocol.js";
12
+ import { InitialEventTypeId } from "./symbols.js";
13
+ import { getNode, isChoiceTarget, isHistoryTarget, isSnapshot, isTarget, makeChoiceTarget, makeTarget, TargetSnapshotTypeId } from "./topology.js";
14
+ export const normalizeTransition = (transition) => {
15
+ if (transition === undefined) {
16
+ return undefined;
17
+ }
18
+ return typeof transition === "function"
19
+ ? { reenter: false, targets: undefined, transition }
20
+ : {
21
+ reenter: transition.reenter === true,
22
+ targets: transition.targets,
23
+ transition: transition.transition
24
+ };
25
+ };
26
+ const collectStateAction = (machine, handler, context) => {
27
+ const collected = makeCollector(machine);
28
+ if (handler === undefined) {
29
+ return {
30
+ commands: collected.commands,
31
+ raisedEvents: collected.raisedEvents,
32
+ emittedEvents: collected.emittedEvents
33
+ };
34
+ }
35
+ handler(context, collected.enqueue);
36
+ return {
37
+ commands: collected.commands,
38
+ raisedEvents: collected.raisedEvents,
39
+ emittedEvents: collected.emittedEvents
40
+ };
41
+ };
42
+ const collectTransition = (machine, transition, context) => {
43
+ const collected = makeCollector(machine);
44
+ const state = transition(context, collected.enqueue);
45
+ return {
46
+ state,
47
+ commands: collected.commands,
48
+ raisedEvents: collected.raisedEvents,
49
+ emittedEvents: collected.emittedEvents
50
+ };
51
+ };
52
+ const collectStateInitializer = (machine, handler, context) => {
53
+ const collected = makeCollector(machine);
54
+ return {
55
+ value: handler(context, collected.enqueue),
56
+ commands: collected.commands,
57
+ raisedEvents: collected.raisedEvents,
58
+ emittedEvents: collected.emittedEvents
59
+ };
60
+ };
61
+ /** Completes the intentionally partial configuration held by shallow history.
62
+ * Only a compound node with no remembered child invokes an initializer; deep
63
+ * history never reaches this path. */
64
+ const completeHistoryConfiguration = (machine, configuration, event) => {
65
+ const active = new Set(configuration.active);
66
+ const values = new Map(configuration.values);
67
+ const commands = [];
68
+ const raisedEvents = [];
69
+ const emittedEvents = [];
70
+ let changed = true;
71
+ while (changed) {
72
+ changed = false;
73
+ for (const path of Array.from(active).sort((left, right) => compareDocumentOrder(machine, left, right))) {
74
+ const node = getNode(machine, path);
75
+ if (node.type === "compound" && !node.children.some((child) => active.has(child))) {
76
+ if (node.initial === undefined) {
77
+ throw new Error(`Machine shallow history expected compound state "${path}" to have an initial child`);
78
+ }
79
+ const initializer = machine.handlers[path]?.initial;
80
+ if (initializer === undefined) {
81
+ throw new Error(`Machine shallow history requires an initial value implementation for state "${path}"`);
82
+ }
83
+ const current = {
84
+ active,
85
+ values,
86
+ outputs: configuration.outputs,
87
+ history: configuration.history
88
+ };
89
+ const initialized = collectStateInitializer(machine, initializer, {
90
+ state: getActiveValue(current, path),
91
+ parent: getParentValue(machine, current, path),
92
+ parents: getParentValues(machine, current, path),
93
+ event
94
+ });
95
+ const child = getNode(machine, node.initial);
96
+ active.add(child.path);
97
+ values.set(child.path, decodeStateValueSync(machine, child, initialized.value));
98
+ commands.push(...initialized.commands);
99
+ raisedEvents.push(...initialized.raisedEvents);
100
+ emittedEvents.push(...initialized.emittedEvents);
101
+ changed = true;
102
+ }
103
+ if (node.type === "parallel") {
104
+ const missing = node.children.filter((childPath) => !active.has(childPath));
105
+ if (missing.length > 0) {
106
+ const initializer = machine.handlers[path]?.initial;
107
+ if (initializer === undefined) {
108
+ throw new Error(`Machine shallow history requires an initial value implementation for state "${path}"`);
109
+ }
110
+ const current = {
111
+ active,
112
+ values,
113
+ outputs: configuration.outputs,
114
+ history: configuration.history
115
+ };
116
+ const initialized = collectStateInitializer(machine, initializer, {
117
+ state: getActiveValue(current, path),
118
+ parent: getParentValue(machine, current, path),
119
+ parents: getParentValues(machine, current, path),
120
+ event
121
+ });
122
+ if (typeof initialized.value !== "object" || initialized.value === null) {
123
+ throw new Error(`Machine parallel state initializer for "${path}" must return its region values`);
124
+ }
125
+ for (const childPath of missing) {
126
+ const child = getNode(machine, childPath);
127
+ if (!Object.prototype.hasOwnProperty.call(initialized.value, child.key)) {
128
+ throw new Error(`Machine parallel state initializer for "${path}" must return region "${child.key}"`);
129
+ }
130
+ active.add(child.path);
131
+ values.set(child.path, decodeStateValueSync(machine, child, initialized.value[child.key]));
132
+ }
133
+ commands.push(...initialized.commands);
134
+ raisedEvents.push(...initialized.raisedEvents);
135
+ emittedEvents.push(...initialized.emittedEvents);
136
+ changed = true;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ return {
142
+ configuration: {
143
+ active,
144
+ values,
145
+ outputs: new Map(),
146
+ history: configuration.history
147
+ },
148
+ commands,
149
+ raisedEvents,
150
+ emittedEvents
151
+ };
152
+ };
153
+ const resolveHistoryTarget = (machine, configuration, target, event) => {
154
+ const node = getNode(machine, target.path);
155
+ if (node.type !== "history" || node.parent !== target.parent) {
156
+ throw new Error(`Machine expected history target "${target.path}" to resolve to its declared parent`);
157
+ }
158
+ const record = getHistoryRecord(configuration, target.path);
159
+ if (record !== undefined) {
160
+ const restored = configurationFromHistoryRecord(machine, configuration, record);
161
+ // Deep records are already complete below the history parent. This pass is
162
+ // still required for parallel ancestors outside that parent whose other
163
+ // regions must be initialized when the ancestor is re-entered.
164
+ const completed = completeHistoryConfiguration(machine, restored, event);
165
+ const snapshot = snapshotFromConfigurationAtPath(machine, completed.configuration, target.parent);
166
+ const values = Object.fromEntries(completed.configuration.values);
167
+ return {
168
+ target: makeTarget(target.parent, snapshot.value, {
169
+ snapshot: snapshot,
170
+ values: values
171
+ }),
172
+ commands: completed.commands,
173
+ raisedEvents: completed.raisedEvents,
174
+ emittedEvents: completed.emittedEvents,
175
+ transitions: []
176
+ };
177
+ }
178
+ const key = node.key;
179
+ const fallback = machine.handlers[target.parent]?.history?.[key]?.default;
180
+ if (fallback === undefined) {
181
+ throw new Error(`Machine history state "${target.path}" requires a default implementation`);
182
+ }
183
+ const collected = collectTransition(machine, fallback, {
184
+ event,
185
+ target: getTargetBuilder(machine, target.parent).full,
186
+ parent: target.parent
187
+ });
188
+ if (collected.state === undefined || isHistoryTarget(collected.state) || !isSnapshot(collected.state)) {
189
+ throw new Error(`Machine history default for "${target.path}" must return a complete snapshot containing its owner`);
190
+ }
191
+ const fallbackChoice = choiceFromTarget(collected.state);
192
+ const choiceResolution = fallbackChoice === undefined
193
+ ? undefined
194
+ : resolveChoiceTarget(machine, {
195
+ active: new Set(),
196
+ values: new Map(),
197
+ outputs: new Map(),
198
+ history: configuration.history
199
+ }, collected.state, event);
200
+ let fallbackConfiguration = choiceResolution === undefined
201
+ ? normalizeConfigurationSync(machine, collected.state)
202
+ : normalizeTargetConfigurationSync(machine, {
203
+ active: new Set(),
204
+ values: new Map(),
205
+ outputs: new Map(),
206
+ history: configuration.history
207
+ }, choiceResolution.target);
208
+ for (const additionalTarget of choiceResolution?.additionalTargets ?? []) {
209
+ fallbackConfiguration = normalizeTargetConfigurationSync(machine, fallbackConfiguration, additionalTarget);
210
+ }
211
+ if (!fallbackConfiguration.active.has(target.parent)) {
212
+ throw new Error(`Machine history default for "${target.path}" returned a configuration that does not contain owner state "${target.parent}"`);
213
+ }
214
+ const snapshot = snapshotFromConfigurationAtPath(machine, fallbackConfiguration, target.parent);
215
+ const values = Object.fromEntries(fallbackConfiguration.values);
216
+ return {
217
+ target: makeTarget(target.parent, snapshot.value, {
218
+ snapshot: snapshot,
219
+ values: values
220
+ }),
221
+ commands: [...collected.commands, ...(choiceResolution?.commands ?? [])],
222
+ raisedEvents: [...collected.raisedEvents, ...(choiceResolution?.raisedEvents ?? [])],
223
+ emittedEvents: [...collected.emittedEvents, ...(choiceResolution?.emittedEvents ?? [])],
224
+ transitions: choiceResolution?.transitions ?? []
225
+ };
226
+ };
227
+ const getCandidatePaths = (machine, configuration) => Array.from(configuration.active)
228
+ .sort((left, right) => {
229
+ const depth = pathDepth(machine, right) - pathDepth(machine, left);
230
+ return depth === 0 ? compareDocumentOrder(machine, left, right) : depth;
231
+ });
232
+ const getLeafCandidatePaths = (machine, leaf) => [...getPathToRoot(machine, leaf)].reverse();
233
+ export const getLeastCommonAncestor = (machine, left, right) => {
234
+ const leftPath = getPathToRoot(machine, left);
235
+ const rightPath = getPathToRoot(machine, right);
236
+ let ancestor = undefined;
237
+ const length = Math.min(leftPath.length, rightPath.length);
238
+ for (let index = 0; index < length; index++) {
239
+ if (leftPath[index] !== rightPath[index]) {
240
+ break;
241
+ }
242
+ ancestor = leftPath[index];
243
+ }
244
+ return ancestor;
245
+ };
246
+ export const broadenTransitionBoundary = (naturalBoundary, reentryBoundary) => {
247
+ if (naturalBoundary === undefined || reentryBoundary === undefined)
248
+ return undefined;
249
+ return naturalBoundary === reentryBoundary || isDescendantOf(naturalBoundary, reentryBoundary)
250
+ ? reentryBoundary
251
+ : naturalBoundary;
252
+ };
253
+ export const getExitPaths = (machine, configuration, boundary) => sortExitPaths(machine, Array.from(configuration.active)
254
+ .filter((path) => boundary === undefined || isDescendantOf(path, boundary)));
255
+ export const getEntryPaths = (machine, configuration, boundary) => sortEntryPaths(machine, Array.from(configuration.active).filter((path) => boundary === undefined || isDescendantOf(path, boundary)));
256
+ const hasSameActivePaths = (left, right) => left.active.size === right.active.size && Array.from(left.active).every((path) => right.active.has(path));
257
+ export const sortExitPaths = (machine, paths) => Array.from(new Set(paths))
258
+ .sort((left, right) => {
259
+ const depth = getPathToRoot(machine, right).length - getPathToRoot(machine, left).length;
260
+ return depth === 0 ? getNode(machine, right).order - getNode(machine, left).order : depth;
261
+ });
262
+ export const sortEntryPaths = (machine, paths) => Array.from(new Set(paths))
263
+ .sort((left, right) => {
264
+ const depth = getPathToRoot(machine, left).length - getPathToRoot(machine, right).length;
265
+ return depth === 0 ? compareDocumentOrder(machine, left, right) : depth;
266
+ });
267
+ const makeStateActionContext = (machine, configuration, path, event) => ({
268
+ state: getActiveValue(configuration, path),
269
+ parent: getParentValue(machine, configuration, path),
270
+ parents: getParentValues(machine, configuration, path),
271
+ event
272
+ });
273
+ const makeTransitionContext = (machine, configuration, path, event, snapshot) => ({
274
+ state: getActiveValue(configuration, path),
275
+ parent: getParentValue(machine, configuration, path),
276
+ parents: getParentValues(machine, configuration, path),
277
+ event,
278
+ snapshot,
279
+ target: getTargetBuilder(machine, path)
280
+ });
281
+ const makeDoneContext = (machine, configuration, path, event, output, snapshot) => ({
282
+ state: getActiveValue(configuration, path),
283
+ parent: getParentValue(machine, configuration, path),
284
+ parents: getParentValues(machine, configuration, path),
285
+ event,
286
+ output: output,
287
+ snapshot,
288
+ target: getTargetBuilder(machine, path)
289
+ });
290
+ const collectStateActions = (machine, configuration, paths, event, key) => {
291
+ const commands = [];
292
+ const raisedEvents = [];
293
+ const emittedEvents = [];
294
+ for (const path of paths) {
295
+ const collected = collectStateAction(machine, machine.handlers[path]?.[key], makeStateActionContext(machine, configuration, path, event));
296
+ commands.push(...collected.commands);
297
+ raisedEvents.push(...collected.raisedEvents);
298
+ emittedEvents.push(...collected.emittedEvents);
299
+ }
300
+ return { commands, emittedEvents, raisedEvents };
301
+ };
302
+ const selectAlwaysTransitions = (machine, configuration, event) => {
303
+ const selected = [];
304
+ const selectedSources = new Set();
305
+ let snapshot;
306
+ const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration(machine, configuration);
307
+ for (const leaf of getActiveLeafPaths(machine, configuration)) {
308
+ for (const path of getLeafCandidatePaths(machine, leaf)) {
309
+ const always = normalizeTransition(machine.handlers[path]?.always);
310
+ if (always !== undefined) {
311
+ if (!selectedSources.has(path)) {
312
+ selectedSources.add(path);
313
+ selected.push({
314
+ sourcePath: path,
315
+ leafPath: leaf,
316
+ trigger: { type: "always" },
317
+ transition: always,
318
+ context: {
319
+ state: getActiveValue(configuration, path),
320
+ parent: getParentValue(machine, configuration, path),
321
+ parents: getParentValues(machine, configuration, path),
322
+ event,
323
+ snapshot: capturedSnapshot(),
324
+ target: getTargetBuilder(machine, path)
325
+ }
326
+ });
327
+ }
328
+ break;
329
+ }
330
+ }
331
+ }
332
+ return selected;
333
+ };
334
+ const selectDoneTransitions = (machine, configuration, event, completions) => {
335
+ const selected = [];
336
+ const selectedSources = new Set();
337
+ let snapshot;
338
+ const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration(machine, configuration);
339
+ for (const completion of completions) {
340
+ const onDone = normalizeTransition(machine.handlers[completion.path]?.onDone);
341
+ if (onDone !== undefined && !selectedSources.has(completion.path)) {
342
+ selectedSources.add(completion.path);
343
+ selected.push({
344
+ sourcePath: completion.path,
345
+ leafPath: getActiveLeafPathFrom(machine, configuration, completion.path),
346
+ trigger: { type: "done" },
347
+ transition: onDone,
348
+ context: makeDoneContext(machine, configuration, completion.path, event, completion.output, capturedSnapshot())
349
+ });
350
+ }
351
+ }
352
+ return selected;
353
+ };
354
+ const selectEventTransitions = (machine, configuration, event) => {
355
+ const selected = [];
356
+ const selectedSources = new Set();
357
+ let snapshot;
358
+ const capturedSnapshot = () => snapshot ??= snapshotFromConfiguration(machine, configuration);
359
+ for (const leaf of getActiveLeafPaths(machine, configuration)) {
360
+ for (const path of getLeafCandidatePaths(machine, leaf)) {
361
+ const transition = normalizeTransition(machine.handlers[path]?.on?.[event._tag]);
362
+ if (transition !== undefined) {
363
+ if (!selectedSources.has(path)) {
364
+ selectedSources.add(path);
365
+ selected.push({
366
+ sourcePath: path,
367
+ leafPath: leaf,
368
+ trigger: { type: "event", event: event._tag },
369
+ transition: transition,
370
+ context: makeTransitionContext(machine, configuration, path, event, capturedSnapshot())
371
+ });
372
+ }
373
+ break;
374
+ }
375
+ }
376
+ }
377
+ return selected;
378
+ };
379
+ export const getTargetNodePath = (target) => {
380
+ if (isHistoryTarget(target) || isChoiceTarget(target)) {
381
+ return String(target.path);
382
+ }
383
+ if (isTarget(target)) {
384
+ return String(target.path);
385
+ }
386
+ if (isSnapshot(target)) {
387
+ return String(target.path);
388
+ }
389
+ throw new Error("Machine expected transition target to be a snapshot or target builder result");
390
+ };
391
+ export const validateDeclaredTransitionTarget = (sourcePath, trigger, declaredTargets, target) => {
392
+ if (declaredTargets === undefined || target === undefined) {
393
+ return;
394
+ }
395
+ const actual = typeof target === "object" && target !== null && "path" in target
396
+ ? String(target.path)
397
+ : "<unknown>";
398
+ if (!declaredTargets.some((path) => actual === path || actual.startsWith(`${path}.`))) {
399
+ const triggerLabel = trigger.type === "event" ? String(trigger.event) : trigger.type;
400
+ throw new Error(`Machine transition from "${sourcePath}" on "${triggerLabel}" returned target "${actual}" outside declared targets: ${declaredTargets.length === 0 ? "none" : declaredTargets.map((path) => `"${path}"`).join(", ")}`);
401
+ }
402
+ };
403
+ const hasPathIntersection = (left, right) => {
404
+ for (const path of left) {
405
+ if (right.includes(path)) {
406
+ return true;
407
+ }
408
+ }
409
+ return false;
410
+ };
411
+ export const sortEvaluatedTransitions = (machine, transitions) => Array.from(transitions)
412
+ .sort((left, right) => compareDocumentOrder(machine, left.selection.sourcePath, right.selection.sourcePath));
413
+ const removePreemptedAncestorSelections = (selections) => selections.filter((selection) => !selections.some((other) => other.sourcePath !== selection.sourcePath && isDescendantOf(other.sourcePath, selection.sourcePath)));
414
+ export const removeConflictingTransitions = (machine, transitions) => {
415
+ const filtered = [];
416
+ for (const transition of sortEvaluatedTransitions(machine, transitions)) {
417
+ let preempted = false;
418
+ const transitionsToRemove = new Set();
419
+ for (const selected of filtered) {
420
+ if (hasPathIntersection(transition.exitPaths, selected.exitPaths)) {
421
+ if (isDescendantOf(transition.selection.sourcePath, selected.selection.sourcePath)) {
422
+ transitionsToRemove.add(selected);
423
+ }
424
+ else {
425
+ preempted = true;
426
+ break;
427
+ }
428
+ }
429
+ }
430
+ if (!preempted) {
431
+ for (const removed of transitionsToRemove) {
432
+ const index = filtered.indexOf(removed);
433
+ if (index >= 0) {
434
+ filtered.splice(index, 1);
435
+ }
436
+ }
437
+ filtered.push(transition);
438
+ }
439
+ }
440
+ return filtered;
441
+ };
442
+ const choicesFromTarget = (target, inheritedValues = {}) => {
443
+ const values = { ...inheritedValues };
444
+ const choices = [];
445
+ const visit = (current) => {
446
+ if (isChoiceTarget(current)) {
447
+ Object.assign(values, current.values ?? {});
448
+ choices.push(current);
449
+ return;
450
+ }
451
+ if (typeof current !== "object" || current === null || !("path" in current) || !("value" in current))
452
+ return;
453
+ values[String(current.path)] = current.value;
454
+ if ("state" in current)
455
+ visit(current.state);
456
+ if ("states" in current && typeof current.states === "object" && current.states !== null) {
457
+ for (const state of Object.values(current.states))
458
+ visit(state);
459
+ }
460
+ };
461
+ visit(target);
462
+ return choices.map((choice) => ({
463
+ target: makeChoiceTarget(choice.path, choice.parent, values),
464
+ values
465
+ }));
466
+ };
467
+ const choiceFromTarget = (target, inheritedValues = {}) => choicesFromTarget(target, inheritedValues)[0];
468
+ const withChoiceValues = (target, values) => {
469
+ if (isChoiceTarget(target)) {
470
+ return makeChoiceTarget(target.path, target.parent, { ...values, ...(target.values ?? {}) });
471
+ }
472
+ if (!isTarget(target) || Object.keys(values).length === 0)
473
+ return target;
474
+ const snapshot = target[TargetSnapshotTypeId];
475
+ return makeTarget(target.path, target.value, {
476
+ ...(snapshot === undefined ? {} : { snapshot }),
477
+ values: { ...values, ...(target.values ?? {}) }
478
+ });
479
+ };
480
+ const resolveChoiceTarget = (machine, configuration, initialTarget, event) => {
481
+ const pending = [initialTarget];
482
+ const resolvedTargets = [];
483
+ const commands = [];
484
+ const raisedEvents = [];
485
+ const emittedEvents = [];
486
+ const transitions = [];
487
+ let iterations = 0;
488
+ while (pending.length > 0) {
489
+ let current = pending.shift();
490
+ while (true) {
491
+ const extractedChoices = choicesFromTarget(current);
492
+ const extracted = extractedChoices[0];
493
+ if (extracted === undefined)
494
+ break;
495
+ pending.unshift(...extractedChoices.slice(1).map(({ target }) => target));
496
+ iterations += 1;
497
+ if (iterations > MaxMacrostepIterations) {
498
+ throw new InfiniteTransitionError({
499
+ machineId: machine.id,
500
+ state: extracted.target.path,
501
+ maxIterations: MaxMacrostepIterations
502
+ });
503
+ }
504
+ const node = getNode(machine, extracted.target.path);
505
+ if (node.type !== "choice" || node.parent !== extracted.target.parent) {
506
+ throw new Error(`Machine expected choice target "${extracted.target.path}" to resolve to its declared parent`);
507
+ }
508
+ const choice = machine.handlers[node.path]?.choice;
509
+ if (choice === undefined || typeof choice.transition !== "function") {
510
+ throw new Error(`Machine choice state "${node.path}" requires an implementation`);
511
+ }
512
+ const provisional = {
513
+ active: new Set([
514
+ ...configuration.active,
515
+ ...Object.keys(extracted.values)
516
+ ]),
517
+ values: new Map([
518
+ ...configuration.values,
519
+ ...Object.entries(extracted.values)
520
+ ]),
521
+ outputs: configuration.outputs,
522
+ history: configuration.history
523
+ };
524
+ const collected = collectTransition(machine, choice.transition, {
525
+ parent: getParentValue(machine, provisional, node.path),
526
+ parents: getParentValues(machine, provisional, node.path),
527
+ event,
528
+ target: getTargetBuilder(machine, node.path)
529
+ });
530
+ if (collected.state === undefined) {
531
+ throw new Error(`Machine choice resolver for "${node.path}" must return a target`);
532
+ }
533
+ validateDeclaredTransitionTarget(node.path, { type: "choice" }, choice.targets, collected.state);
534
+ const returnedPath = getTargetNodePath(collected.state);
535
+ current = withChoiceValues(collected.state, extracted.values);
536
+ const nested = choiceFromTarget(current);
537
+ transitions.push({
538
+ source: node.path,
539
+ trigger: { type: "choice" },
540
+ reenter: false,
541
+ target: returnedPath,
542
+ resolvedTarget: nested?.target.path ?? returnedPath
543
+ });
544
+ commands.push(...collected.commands);
545
+ raisedEvents.push(...collected.raisedEvents);
546
+ emittedEvents.push(...collected.emittedEvents);
547
+ }
548
+ if (!isTarget(current) && !isSnapshot(current) && !isHistoryTarget(current)) {
549
+ throw new Error("Machine choice resolver must return a concrete typed target");
550
+ }
551
+ resolvedTargets.push(current);
552
+ }
553
+ return {
554
+ target: resolvedTargets[0],
555
+ additionalTargets: resolvedTargets.slice(1),
556
+ commands,
557
+ raisedEvents,
558
+ emittedEvents,
559
+ transitions
560
+ };
561
+ };
562
+ const collectEvaluatedTransition = (machine, state, selection) => {
563
+ const stateIdentifier = selection.leafPath;
564
+ const transitionResult = collectTransition(machine, selection.transition.transition, selection.context);
565
+ const unresolvedTarget = transitionResult.state === undefined
566
+ ? undefined
567
+ : transitionResult.state;
568
+ validateDeclaredTransitionTarget(selection.sourcePath, selection.trigger, selection.transition.targets, unresolvedTarget);
569
+ const choiceResolution = unresolvedTarget === undefined
570
+ ? undefined
571
+ : resolveChoiceTarget(machine, state, unresolvedTarget, selection.context.event);
572
+ const choiceResolvedTarget = choiceResolution?.target ?? unresolvedTarget;
573
+ let historyResolution;
574
+ const reenteredHistoryTarget = isHistoryTarget(choiceResolvedTarget) && selection.transition.reenter &&
575
+ state.active.has(choiceResolvedTarget.parent)
576
+ ? choiceResolvedTarget
577
+ : undefined;
578
+ if (choiceResolvedTarget !== undefined && isHistoryTarget(choiceResolvedTarget)) {
579
+ // A reentering transition may exit the history node's own parent. SCXML
580
+ // history observes that same exit, so resolve against a provisional
581
+ // capture rather than an older record (or the default).
582
+ const provisionalBoundary = selection.transition.reenter
583
+ ? getNode(machine, selection.sourcePath).parent
584
+ : getLeastCommonAncestor(machine, stateIdentifier, choiceResolvedTarget.parent);
585
+ const provisionalExitPaths = reenteredHistoryTarget !== undefined
586
+ ? sortExitPaths(machine, Array.from(state.active).filter((path) => isPathInSubtree(path, choiceResolvedTarget.parent)))
587
+ : getExitPaths(machine, state, provisionalBoundary);
588
+ const stateAtHistoryResolution = provisionalExitPaths.includes(choiceResolvedTarget.parent)
589
+ ? captureHistory(machine, state, state, provisionalExitPaths)
590
+ : state;
591
+ historyResolution = resolveHistoryTarget(machine, stateAtHistoryResolution, choiceResolvedTarget, selection.context.event);
592
+ }
593
+ const target = historyResolution === undefined
594
+ ? choiceResolvedTarget
595
+ : historyResolution.target;
596
+ const additionalHistoryActions = [];
597
+ const additionalHistoryRaisedEvents = [];
598
+ const additionalHistoryEmittedEvents = [];
599
+ const additionalHistoryChoiceTransitions = [];
600
+ const additionalChoiceTargets = [];
601
+ for (const additionalTarget of choiceResolution?.additionalTargets ?? []) {
602
+ if (!isHistoryTarget(additionalTarget)) {
603
+ additionalChoiceTargets.push(additionalTarget);
604
+ continue;
605
+ }
606
+ const resolved = resolveHistoryTarget(machine, state, additionalTarget, selection.context.event);
607
+ additionalChoiceTargets.push(resolved.target);
608
+ additionalHistoryActions.push(...resolved.commands);
609
+ additionalHistoryRaisedEvents.push(...resolved.raisedEvents);
610
+ additionalHistoryEmittedEvents.push(...resolved.emittedEvents);
611
+ additionalHistoryChoiceTransitions.push(...resolved.transitions);
612
+ }
613
+ const targetPath = target === undefined
614
+ ? undefined
615
+ : additionalChoiceTargets.length === 0 || unresolvedTarget === undefined
616
+ ? getTargetNodePath(target)
617
+ : getTargetNodePath(unresolvedTarget);
618
+ let stateAfterTransition = target === undefined
619
+ ? state
620
+ : normalizeTargetConfigurationSync(machine, state, target);
621
+ for (const additionalTarget of additionalChoiceTargets) {
622
+ stateAfterTransition = normalizeTargetConfigurationSync(machine, stateAfterTransition, additionalTarget);
623
+ }
624
+ const changed = selection.transition.reenter || !hasSameActivePaths(state, stateAfterTransition);
625
+ if (!changed) {
626
+ return {
627
+ selection,
628
+ unresolvedTarget,
629
+ target,
630
+ commands: [
631
+ ...transitionResult.commands,
632
+ ...(choiceResolution?.commands ?? []),
633
+ ...(historyResolution?.commands ?? []),
634
+ ...additionalHistoryActions
635
+ ],
636
+ raisedEvents: [
637
+ ...transitionResult.raisedEvents,
638
+ ...(choiceResolution?.raisedEvents ?? []),
639
+ ...(historyResolution?.raisedEvents ?? []),
640
+ ...additionalHistoryRaisedEvents
641
+ ],
642
+ emittedEvents: [
643
+ ...transitionResult.emittedEvents,
644
+ ...(choiceResolution?.emittedEvents ?? []),
645
+ ...(historyResolution?.emittedEvents ?? []),
646
+ ...additionalHistoryEmittedEvents
647
+ ],
648
+ changed,
649
+ exitPaths: [],
650
+ entryPaths: [],
651
+ choiceTransitions: [
652
+ ...(choiceResolution?.transitions ?? []),
653
+ ...(historyResolution?.transitions ?? []),
654
+ ...additionalHistoryChoiceTransitions
655
+ ]
656
+ };
657
+ }
658
+ const naturalBoundary = targetPath === undefined
659
+ ? getNode(machine, selection.sourcePath).parent
660
+ : getLeastCommonAncestor(machine, stateIdentifier, targetPath);
661
+ const reentryBoundary = getNode(machine, selection.sourcePath).parent;
662
+ const boundary = selection.transition.reenter
663
+ ? broadenTransitionBoundary(naturalBoundary, reentryBoundary)
664
+ : naturalBoundary;
665
+ return {
666
+ selection,
667
+ unresolvedTarget,
668
+ target,
669
+ commands: [
670
+ ...transitionResult.commands,
671
+ ...(choiceResolution?.commands ?? []),
672
+ ...(historyResolution?.commands ?? []),
673
+ ...additionalHistoryActions
674
+ ],
675
+ raisedEvents: [
676
+ ...transitionResult.raisedEvents,
677
+ ...(choiceResolution?.raisedEvents ?? []),
678
+ ...(historyResolution?.raisedEvents ?? []),
679
+ ...additionalHistoryRaisedEvents
680
+ ],
681
+ emittedEvents: [
682
+ ...transitionResult.emittedEvents,
683
+ ...(choiceResolution?.emittedEvents ?? []),
684
+ ...(historyResolution?.emittedEvents ?? []),
685
+ ...additionalHistoryEmittedEvents
686
+ ],
687
+ changed,
688
+ exitPaths: reenteredHistoryTarget !== undefined
689
+ ? sortExitPaths(machine, Array.from(state.active).filter((path) => isPathInSubtree(path, reenteredHistoryTarget.parent)))
690
+ : getExitPaths(machine, state, boundary),
691
+ entryPaths: reenteredHistoryTarget !== undefined
692
+ ? sortEntryPaths(machine, Array.from(stateAfterTransition.active).filter((path) => isPathInSubtree(path, reenteredHistoryTarget.parent)))
693
+ : getEntryPaths(machine, stateAfterTransition, boundary),
694
+ choiceTransitions: [
695
+ ...(choiceResolution?.transitions ?? []),
696
+ ...(historyResolution?.transitions ?? []),
697
+ ...additionalHistoryChoiceTransitions
698
+ ]
699
+ };
700
+ };
701
+ export const MaxMacrostepIterations = 1000;
702
+ export const InitialEvent = { _tag: InitialEventTypeId };
703
+ export const isFinalState = (machine, state) => isActiveFinalConfiguration(machine, normalizeConfiguration(machine, state));
704
+ export const getFinalOutputEffect = (machine, state, event) => normalizeConfigurationEffect(machine, state).pipe(Effect.flatMap((configuration) => completeConfigurationEffect(machine, configuration, event)), Effect.flatMap((completed) => {
705
+ const root = getRootPath(machine, completed.configuration);
706
+ if (!isActiveFinalConfiguration(machine, completed.configuration)
707
+ || !completed.configuration.outputs.has(root)) {
708
+ return Effect.die(new Error("Machine attempted to read terminal output from a non-terminal configuration"));
709
+ }
710
+ return Effect.succeed(completed.configuration.outputs.get(root));
711
+ }));
712
+ export const isFinal = (machine, state) => isFinalState(machine, state);
713
+ export const planInitialSync = (machine, ...args) => {
714
+ const inputArgs = machine.input === undefined
715
+ ? args
716
+ : args.length === 0
717
+ ? (decodeInputSync(machine, machine.input, undefined), args)
718
+ : [decodeInputSync(machine, machine.input, args[0])];
719
+ const state = machine.initial(...inputArgs);
720
+ const emptyConfiguration = {
721
+ active: new Set(),
722
+ values: new Map(),
723
+ outputs: new Map(),
724
+ history: new Map()
725
+ };
726
+ const initialChoice = choiceFromTarget(state);
727
+ const choiceResolution = initialChoice === undefined
728
+ ? undefined
729
+ : resolveChoiceTarget(machine, emptyConfiguration, state, InitialEvent);
730
+ const initialHistoryActions = [];
731
+ const initialHistoryRaisedEvents = [];
732
+ const initialHistoryEmittedEvents = [];
733
+ const initialHistoryChoiceTransitions = [];
734
+ const resolvedInitialTargets = [];
735
+ for (const target of choiceResolution === undefined
736
+ ? []
737
+ : [choiceResolution.target, ...choiceResolution.additionalTargets]) {
738
+ if (!isHistoryTarget(target)) {
739
+ resolvedInitialTargets.push(target);
740
+ continue;
741
+ }
742
+ const history = resolveHistoryTarget(machine, emptyConfiguration, target, InitialEvent);
743
+ resolvedInitialTargets.push(history.target);
744
+ initialHistoryActions.push(...history.commands);
745
+ initialHistoryRaisedEvents.push(...history.raisedEvents);
746
+ initialHistoryEmittedEvents.push(...history.emittedEvents);
747
+ initialHistoryChoiceTransitions.push(...history.transitions);
748
+ }
749
+ let resolvedConfiguration = choiceResolution === undefined
750
+ ? normalizeConfigurationSync(machine, state)
751
+ : normalizeTargetConfigurationSync(machine, emptyConfiguration, resolvedInitialTargets[0]);
752
+ for (const additionalTarget of resolvedInitialTargets.slice(1)) {
753
+ resolvedConfiguration = normalizeTargetConfigurationSync(machine, resolvedConfiguration, additionalTarget);
754
+ }
755
+ const configuration = resolvedConfiguration;
756
+ validateInitialConfiguration(machine, configuration);
757
+ const startingState = snapshotFromConfiguration(machine, configuration);
758
+ const initialEntryPaths = getInitialEntryPaths(machine, configuration);
759
+ const commands = [
760
+ ...(choiceResolution?.commands ?? []),
761
+ ...initialHistoryActions
762
+ ];
763
+ const raisedEvents = [
764
+ ...(choiceResolution?.raisedEvents ?? []),
765
+ ...initialHistoryRaisedEvents
766
+ ];
767
+ const emittedEvents = [
768
+ ...(choiceResolution?.emittedEvents ?? []),
769
+ ...initialHistoryEmittedEvents
770
+ ];
771
+ const entry = collectStateActions(machine, configuration, initialEntryPaths, InitialEvent, "entry");
772
+ const settled = settle(machine, configuration, InitialEvent, [...entry.commands], [...raisedEvents, ...entry.raisedEvents], [...emittedEvents, ...entry.emittedEvents], choiceResolution === undefined ? [] : [{
773
+ next: configuration,
774
+ event: InitialEvent,
775
+ transitions: [...choiceResolution.transitions, ...initialHistoryChoiceTransitions],
776
+ commands: [...choiceResolution.commands, ...initialHistoryActions],
777
+ raisedEvents: [
778
+ ...choiceResolution.raisedEvents,
779
+ ...initialHistoryRaisedEvents
780
+ ],
781
+ emittedEvents: [...choiceResolution.emittedEvents, ...initialHistoryEmittedEvents],
782
+ exitPaths: [],
783
+ entryPaths: [],
784
+ changed: false
785
+ }]);
786
+ const planned = {
787
+ startingState,
788
+ initialEntryPaths,
789
+ state: snapshotFromConfiguration(machine, settled.next),
790
+ commands: [
791
+ ...commands,
792
+ ...settled.commands
793
+ ],
794
+ emittedEvents: settled.emittedEvents,
795
+ microsteps: settled.microsteps.map((step) => ({
796
+ next: snapshotFromConfiguration(machine, step.next),
797
+ event: step.event,
798
+ transitions: step.transitions,
799
+ commands: step.commands,
800
+ raisedEvents: step.raisedEvents,
801
+ emittedEvents: step.emittedEvents,
802
+ exitPaths: step.exitPaths,
803
+ entryPaths: step.entryPaths,
804
+ changed: step.changed
805
+ }))
806
+ };
807
+ return settled.done
808
+ ? { ...planned, done: true, output: settled.output }
809
+ : { ...planned, done: false, output: undefined };
810
+ };
811
+ export const enabled = (machine, state) => {
812
+ if (isFinalState(machine, state)) {
813
+ return [];
814
+ }
815
+ const configuration = normalizeConfiguration(machine, state);
816
+ const tags = [];
817
+ const seen = new Set();
818
+ for (const path of getCandidatePaths(machine, configuration)) {
819
+ for (const tag of Reflect.ownKeys(machine.handlers[path]?.on ?? {})) {
820
+ if (!seen.has(tag)) {
821
+ seen.add(tag);
822
+ tags.push(tag);
823
+ }
824
+ }
825
+ }
826
+ return tags;
827
+ };
828
+ const microstep = (machine, state, event, selections) => {
829
+ if (selections.length === 0) {
830
+ return {
831
+ next: state,
832
+ event,
833
+ transitions: [],
834
+ commands: [],
835
+ raisedEvents: [],
836
+ emittedEvents: [],
837
+ exitPaths: [],
838
+ entryPaths: [],
839
+ changed: false
840
+ };
841
+ }
842
+ const activeSelections = removePreemptedAncestorSelections(selections);
843
+ const evaluatedTransitions = [];
844
+ for (const selection of activeSelections) {
845
+ evaluatedTransitions.push(collectEvaluatedTransition(machine, state, selection));
846
+ }
847
+ const transitions = removeConflictingTransitions(machine, evaluatedTransitions);
848
+ const sortedTransitions = sortEvaluatedTransitions(machine, transitions);
849
+ const retainedTransitions = sortedTransitions.flatMap((transition) => [
850
+ {
851
+ source: transition.selection.sourcePath,
852
+ trigger: transition.selection.trigger,
853
+ reenter: transition.selection.transition.reenter,
854
+ target: transition.unresolvedTarget === undefined ? undefined : getTargetNodePath(transition.unresolvedTarget),
855
+ resolvedTarget: transition.target === undefined ? undefined : getTargetNodePath(transition.target)
856
+ },
857
+ ...transition.choiceTransitions
858
+ ]);
859
+ let stateAfterTransition = state;
860
+ // Value-only targets are evaluated against the original configuration. If
861
+ // one is applied after a control-changing transition, it can resurrect a
862
+ // branch that the changing transition exited. Apply value-only updates
863
+ // first so later control targets remain authoritative while still
864
+ // preserving updates made in unaffected parallel regions.
865
+ const targetApplicationOrder = [
866
+ ...sortedTransitions.filter((transition) => !transition.changed),
867
+ ...sortedTransitions.filter((transition) => transition.changed)
868
+ ];
869
+ for (const transition of targetApplicationOrder) {
870
+ if (transition.target !== undefined) {
871
+ stateAfterTransition = normalizeTargetConfigurationSync(machine, stateAfterTransition, transition.target);
872
+ }
873
+ }
874
+ const changed = transitions.some((transition) => transition.changed);
875
+ const transitionActions = sortedTransitions
876
+ .flatMap((transition) => transition.commands);
877
+ const transitionRaisedEvents = sortedTransitions
878
+ .flatMap((transition) => transition.raisedEvents);
879
+ const transitionEmittedEvents = sortedTransitions
880
+ .flatMap((transition) => transition.emittedEvents);
881
+ if (!changed) {
882
+ return {
883
+ next: stateAfterTransition,
884
+ event,
885
+ transitions: retainedTransitions,
886
+ commands: transitionActions,
887
+ raisedEvents: transitionRaisedEvents,
888
+ emittedEvents: transitionEmittedEvents,
889
+ exitPaths: [],
890
+ entryPaths: [],
891
+ changed: false
892
+ };
893
+ }
894
+ const exitPaths = sortExitPaths(machine, sortedTransitions.flatMap((transition) => transition.exitPaths));
895
+ const entryPaths = sortEntryPaths(machine, sortedTransitions.flatMap((transition) => transition.entryPaths));
896
+ stateAfterTransition = captureHistory(machine, state, stateAfterTransition, exitPaths);
897
+ const exit = collectStateActions(machine, state, exitPaths, event, "exit");
898
+ const entry = collectStateActions(machine, stateAfterTransition, entryPaths, event, "entry");
899
+ return {
900
+ next: stateAfterTransition,
901
+ event,
902
+ transitions: retainedTransitions,
903
+ commands: [...exit.commands, ...transitionActions, ...entry.commands],
904
+ raisedEvents: [...exit.raisedEvents, ...transitionRaisedEvents, ...entry.raisedEvents],
905
+ emittedEvents: [...exit.emittedEvents, ...transitionEmittedEvents, ...entry.emittedEvents],
906
+ exitPaths,
907
+ entryPaths,
908
+ changed: true
909
+ };
910
+ };
911
+ const settle = (machine, state, event, commands, raisedEvents, emittedEvents, microsteps) => {
912
+ let currentState = state;
913
+ let currentEvent = event;
914
+ let shouldRunAlways = true;
915
+ let iterations = 0;
916
+ let raisedEventIndex = 0;
917
+ let completedTerminal = false;
918
+ let finalOutput = undefined;
919
+ const pendingCompletions = [];
920
+ while (true) {
921
+ iterations += 1;
922
+ if (iterations > MaxMacrostepIterations) {
923
+ throw new InfiniteTransitionError({
924
+ machineId: machine.id,
925
+ state: String(getLeafPath(machine, currentState)),
926
+ maxIterations: MaxMacrostepIterations
927
+ });
928
+ }
929
+ const completed = completeConfigurationSync(machine, currentState, currentEvent);
930
+ currentState = completed.configuration;
931
+ pendingCompletions.push(...completed.completions.filter((completion) => machine.handlers[completion.path]?.onDone !== undefined));
932
+ while (pendingCompletions.length > 0 && !currentState.active.has(pendingCompletions[0].path)) {
933
+ pendingCompletions.shift();
934
+ }
935
+ const done = selectDoneTransitions(machine, currentState, currentEvent, pendingCompletions.length === 0 ? [] : [pendingCompletions.shift()]);
936
+ if (done.length > 0) {
937
+ const doneStep = microstep(machine, currentState, currentEvent, done);
938
+ commands.push(...doneStep.commands);
939
+ raisedEvents.push(...doneStep.raisedEvents);
940
+ emittedEvents.push(...doneStep.emittedEvents);
941
+ microsteps.push(doneStep);
942
+ currentState = doneStep.next;
943
+ shouldRunAlways = doneStep.changed;
944
+ continue;
945
+ }
946
+ if (isActiveFinalConfiguration(machine, currentState)) {
947
+ const root = getRootPath(machine, currentState);
948
+ if (!currentState.outputs.has(root)) {
949
+ throw new Error("Machine reached a terminal configuration without a completed root output");
950
+ }
951
+ completedTerminal = true;
952
+ finalOutput = currentState.outputs.get(root);
953
+ break;
954
+ }
955
+ const always = shouldRunAlways
956
+ ? selectAlwaysTransitions(machine, currentState, currentEvent)
957
+ : [];
958
+ if (always.length > 0) {
959
+ const alwaysStep = microstep(machine, currentState, currentEvent, always);
960
+ commands.push(...alwaysStep.commands);
961
+ raisedEvents.push(...alwaysStep.raisedEvents);
962
+ emittedEvents.push(...alwaysStep.emittedEvents);
963
+ microsteps.push(alwaysStep);
964
+ currentState = alwaysStep.next;
965
+ shouldRunAlways = alwaysStep.changed;
966
+ continue;
967
+ }
968
+ const raisedEventValue = raisedEvents[raisedEventIndex];
969
+ if (raisedEventValue === undefined) {
970
+ break;
971
+ }
972
+ raisedEventIndex += 1;
973
+ // Planning runtime validates and normalizes every event before it enters this
974
+ // internal queue, so decoding it again here only repeats schema work.
975
+ const raisedEvent = raisedEventValue;
976
+ currentEvent = raisedEvent;
977
+ const raisedSelections = selectEventTransitions(machine, currentState, raisedEvent);
978
+ if (raisedSelections.length === 0) {
979
+ shouldRunAlways = true;
980
+ continue;
981
+ }
982
+ const raisedStep = microstep(machine, currentState, raisedEvent, raisedSelections);
983
+ commands.push(...raisedStep.commands);
984
+ raisedEvents.push(...raisedStep.raisedEvents);
985
+ emittedEvents.push(...raisedStep.emittedEvents);
986
+ microsteps.push(raisedStep);
987
+ currentState = raisedStep.next;
988
+ shouldRunAlways = true;
989
+ }
990
+ const result = {
991
+ next: currentState,
992
+ commands,
993
+ emittedEvents,
994
+ microsteps
995
+ };
996
+ return completedTerminal
997
+ ? { ...result, done: true, output: finalOutput }
998
+ : { ...result, done: false, output: undefined };
999
+ };
1000
+ const macrostepConfiguration = (machine, configuration, event) => {
1001
+ const decodedEvent = decodeEventSync(machine, event);
1002
+ if (isActiveFinalConfiguration(machine, configuration)) {
1003
+ const completed = completeConfigurationSync(machine, configuration, decodedEvent);
1004
+ const root = getRootPath(machine, completed.configuration);
1005
+ if (!completed.configuration.outputs.has(root)) {
1006
+ throw new Error("Machine reached a terminal configuration without a completed root output");
1007
+ }
1008
+ return {
1009
+ next: completed.configuration,
1010
+ commands: [],
1011
+ emittedEvents: [],
1012
+ microsteps: [],
1013
+ done: true,
1014
+ output: completed.configuration.outputs.get(root)
1015
+ };
1016
+ }
1017
+ const selections = selectEventTransitions(machine, configuration, decodedEvent);
1018
+ if (selections.length === 0) {
1019
+ return {
1020
+ next: configuration,
1021
+ commands: [],
1022
+ emittedEvents: [],
1023
+ microsteps: [],
1024
+ done: false,
1025
+ output: undefined
1026
+ };
1027
+ }
1028
+ const step = microstep(machine, configuration, decodedEvent, selections);
1029
+ const commands = [...step.commands];
1030
+ const raisedEvents = [...step.raisedEvents];
1031
+ const emittedEvents = [...step.emittedEvents];
1032
+ const microsteps = [step];
1033
+ return settle(machine, step.next, decodedEvent, commands, raisedEvents, emittedEvents, microsteps);
1034
+ };
1035
+ const snapshotMacrostep = (machine, settled) => {
1036
+ const planned = {
1037
+ next: snapshotFromConfiguration(machine, settled.next),
1038
+ commands: settled.commands,
1039
+ emittedEvents: settled.emittedEvents,
1040
+ microsteps: settled.microsteps.map((step) => ({
1041
+ next: snapshotFromConfiguration(machine, step.next),
1042
+ event: step.event,
1043
+ transitions: step.transitions,
1044
+ commands: step.commands,
1045
+ raisedEvents: step.raisedEvents,
1046
+ emittedEvents: step.emittedEvents,
1047
+ exitPaths: step.exitPaths,
1048
+ entryPaths: step.entryPaths,
1049
+ changed: step.changed
1050
+ }))
1051
+ };
1052
+ return settled.done
1053
+ ? { ...planned, done: true, output: settled.output }
1054
+ : { ...planned, done: false, output: undefined };
1055
+ };
1056
+ const macrostep = (machine, state, event) => {
1057
+ const configuration = normalizeConfigurationSync(machine, state);
1058
+ const settled = macrostepConfiguration(machine, configuration, event);
1059
+ return snapshotMacrostep(machine, settled);
1060
+ };
1061
+ export const planSync = macrostep;
1062
+ export const planConfiguration = macrostepConfiguration;
1063
+ const planningEffect = (thunk) => Effect.suspend(() => {
1064
+ try {
1065
+ return Effect.succeed(thunk());
1066
+ }
1067
+ catch (error) {
1068
+ return error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError
1069
+ ? Effect.fail(error)
1070
+ : Effect.die(error);
1071
+ }
1072
+ });
1073
+ export const plan = (machine, state, event) => planningEffect(() => planSync(machine, state, event));
1074
+ export const planInitial = (machine, ...args) => Effect.try({
1075
+ try: () => planInitialSync(machine, ...args),
1076
+ catch: (error) => {
1077
+ return error instanceof InfiniteTransitionError || error instanceof MachineSchemaDecodeError
1078
+ ? error
1079
+ : new StartupError({ cause: Cause.die(error) });
1080
+ }
1081
+ });
1082
+ //# sourceMappingURL=planner.js.map