@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,15 +3,18 @@ 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 * as Schema from "effect/Schema";
6
7
  import * as Activities from "./activities.js";
7
8
  import * as Configuration from "./configuration.js";
9
+ import * as InvocationDefinition from "./invocationDefinition.js";
8
10
  import * as internalPlanner from "./planner.js";
9
11
  import * as internalProcess from "./process.js";
10
12
  import * as Protocol from "./protocol.js";
11
13
  import * as internalRuntime from "./runtime.js";
12
14
  import * as Serialization from "./serialization.js";
13
15
  import * as StateDefinition from "./stateDefinition.js";
14
- import { ChildMachineLogicTypeId, SnapshotBuilderStateTypeId } from "./symbols.js";
16
+ import { ChildMachineLogicTypeId } from "./symbols.js";
17
+ import { getLocalTargetScope, getTargetBuilderNode, makeSnapshotBuilder, makeTargetBuilder, withFrom } from "./targetBuilder.js";
15
18
  import * as Topology from "./topology.js";
16
19
  export { ChildAlreadyExistsError, InfiniteTransitionError, MachineSchemaDecodeError, MachineSchemaEncodeError, ProcessLocalError, StartupError, StoppedError } from "./errors.js";
17
20
  export { ChildMachineLogicTypeId, InitialEventTypeId, SnapshotBuilderStateTypeId } from "./symbols.js";
@@ -34,6 +37,7 @@ const Proto = {
34
37
  const makeWithHandlers = (self, handlers) => {
35
38
  const machine = Object.create(Proto);
36
39
  machine.states = self.states;
40
+ machine.root = self.root;
37
41
  machine.events = self.events;
38
42
  machine.internalEvents = self.internalEvents;
39
43
  machine.emittedEvents = self.emittedEvents;
@@ -57,26 +61,50 @@ const transitionOptions = (options) => {
57
61
  const configuration = typeof options === "object" && options !== null
58
62
  ? options
59
63
  : {};
60
- return {
61
- reenter: configuration.reenter === true,
62
- declinable: configuration.declinable === true
63
- };
64
+ if (hasProperty(configuration, "reenter")) {
65
+ throw new Error("Use .reenter() before construction instead of the resolver reenter option");
66
+ }
67
+ return { declinable: configuration.declinable === true };
64
68
  };
65
- const makeDirectTransitionDescriptor = (selection, resolve, options) => {
69
+ const makeDirectTransitionDescriptor = (selection, resolve, options, reenterSource = false) => {
66
70
  const shared = {
67
71
  [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
68
72
  type: "direct",
69
73
  selection: plainTargetSelection(selection),
74
+ reenterSource,
70
75
  ...transitionOptions(options)
71
76
  };
72
77
  return resolve === undefined
73
78
  ? Object.freeze(shared)
74
- : Object.freeze({ ...shared, resolve });
79
+ : Object.freeze({ ...shared, resolver: resolve });
80
+ };
81
+ // The authored callback crosses an erased schema boundary here. Each builder
82
+ // retains its construction mode, and planning validates both resulting values.
83
+ const constructSelectionValue = (selection, context, method, value) => {
84
+ if (selection.kind === "update")
85
+ return context.owner[method](value);
86
+ if (selection.updatePath === undefined)
87
+ return context.target[method](value);
88
+ const values = value;
89
+ return context.target[method](values.target).update(context.owner[method](values.update));
90
+ };
91
+ const guardedSelection = (selection, predicate, reenterSource) => {
92
+ const wrap = (resolve, options) => makeDirectTransitionDescriptor(selection, (context, enqueue) => predicate(context) ? resolve(context, enqueue) : Topology.makeDeclined(), { ...transitionOptions(options), declinable: true }, reenterSource);
93
+ return Object.freeze({
94
+ ...wrap((context) => selection.kind === "none" ? undefined : constructSelectedTarget(context.target)),
95
+ from: (value) => wrap((context) => constructSelectionValue(selection, context, "from", value(context))),
96
+ decoded: (value) => wrap((context) => constructSelectionValue(selection, context, "decoded", value(context))),
97
+ resolve: wrap
98
+ });
75
99
  };
76
- const decorateTransitionSelection = (selection) => Object.freeze({
100
+ const decorateTransitionSelection = (selection, reenterSource = false) => Object.freeze({
77
101
  ...selection,
78
- resolve: (resolve, options) => makeDirectTransitionDescriptor(selection, resolve, options),
79
- reenter: () => makeDirectTransitionDescriptor(selection, undefined, { reenter: true }),
102
+ ...(reenterSource ? makeDirectTransitionDescriptor(selection, undefined, undefined, true) : {}),
103
+ guard: (predicate) => guardedSelection(selection, predicate, reenterSource),
104
+ from: (value) => makeDirectTransitionDescriptor(selection, (context) => constructSelectionValue(selection, context, "from", value(context)), undefined, reenterSource),
105
+ decoded: (value) => makeDirectTransitionDescriptor(selection, (context) => constructSelectionValue(selection, context, "decoded", value(context)), undefined, reenterSource),
106
+ resolve: (resolve, options) => makeDirectTransitionDescriptor(selection, resolve, options, reenterSource),
107
+ reenter: () => decorateTransitionSelection(selection, true),
80
108
  updating: (owner) => {
81
109
  if (typeof owner !== "function") {
82
110
  throw new Error("Machine updating owner must be a state selector");
@@ -86,14 +114,9 @@ const decorateTransitionSelection = (selection) => Object.freeze({
86
114
  ownerSelection.scope !== "branch") {
87
115
  throw new Error("Machine updating owner must be addressed by one branch state selector");
88
116
  }
89
- return decorateTransitionSelection(Topology.makeTargetSelection(selection.kind, selection.path, selection.scope, ownerSelection.path));
117
+ return decorateTransitionSelection(Topology.makeTargetSelection(selection.kind, selection.path, selection.scope, ownerSelection.path), reenterSource);
90
118
  }
91
119
  });
92
- const decorateStateUpdateSelection = (selection) => {
93
- const update = (resolve, options) => makeDirectTransitionDescriptor(selection, resolve, options);
94
- Object.assign(update, selection);
95
- return Object.freeze(update);
96
- };
97
120
  const noneTransitionSelection = decorateTransitionSelection(Topology.noneTargetSelection);
98
121
  const makeInitialBuilderDescriptor = (selection, resolve) => Object.freeze({
99
122
  [InitialBuilderDescriptorTypeId]: InitialBuilderDescriptorTypeId,
@@ -102,6 +125,8 @@ const makeInitialBuilderDescriptor = (selection, resolve) => Object.freeze({
102
125
  });
103
126
  const decorateInitialSelection = (selection) => Object.freeze({
104
127
  ...selection,
128
+ from: (value) => makeInitialBuilderDescriptor(selection, (context) => context.target.from(value(context))),
129
+ decoded: (value) => makeInitialBuilderDescriptor(selection, (context) => context.target.decoded(value(context))),
105
130
  resolve: (resolve) => makeInitialBuilderDescriptor(selection, resolve)
106
131
  });
107
132
  const decorateInitialSelectorNode = (node) => {
@@ -125,8 +150,12 @@ const decorateInitialSelectorNode = (node) => {
125
150
  };
126
151
  const decorateTransitionSelectorNode = (node) => {
127
152
  if (Topology.isTargetSelection(node)) {
128
- if (node.kind === "update")
129
- return decorateStateUpdateSelection(node);
153
+ if (node.kind === "state" && node.scope === "full" && hasProperty(node, "initial")) {
154
+ return Object.freeze({
155
+ ...decorateTransitionSelection(node),
156
+ initial: decorateTransitionSelection(node.initial)
157
+ });
158
+ }
130
159
  return node === Topology.noneTargetSelection ? noneTransitionSelection : decorateTransitionSelection(node);
131
160
  }
132
161
  if (typeof node === "function") {
@@ -145,24 +174,24 @@ const decorateTransitionSelectorNode = (node) => {
145
174
  }
146
175
  return node;
147
176
  };
148
- const makeTransitionSelector = (stateNodes, source) => {
149
- const selector = {
150
- ...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source))
151
- };
152
- selector.branches = (declarations) => Object.freeze({
153
- resolve: (resolve, options) => Object.freeze({
154
- [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
155
- type: "branches",
156
- declarations,
157
- resolve,
158
- ...transitionOptions(options)
159
- })
160
- });
161
- return Object.freeze(selector);
162
- };
177
+ const decorateBranches = (declarations, reenterSource = false) => Object.freeze({
178
+ reenter: () => decorateBranches(declarations, true),
179
+ resolve: (resolve, options) => Object.freeze({
180
+ [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
181
+ type: "branches",
182
+ declarations,
183
+ resolve,
184
+ reenterSource,
185
+ ...transitionOptions(options)
186
+ })
187
+ });
188
+ const makeTransitionSelector = (stateNodes, source) => Object.freeze({
189
+ ...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source)),
190
+ branches: (declarations) => decorateBranches(declarations)
191
+ });
163
192
  const normalizeTransitionBuilder = (transition, stateNodes, path) => {
164
193
  const result = transition(makeTransitionSelector(stateNodes, path));
165
- if (Topology.isTargetSelection(result)) {
194
+ if (Topology.isTargetSelection(result) && !hasProperty(result, TransitionBuilderDescriptorTypeId)) {
166
195
  const selection = plainTargetSelection(result);
167
196
  return { target: () => selection };
168
197
  }
@@ -173,14 +202,14 @@ const normalizeTransitionBuilder = (transition, stateNodes, path) => {
173
202
  return {
174
203
  branches: () => descriptor.declarations,
175
204
  resolve: descriptor.resolve,
176
- reenter: descriptor.reenter,
205
+ reenter: descriptor.reenterSource,
177
206
  declinable: descriptor.declinable
178
207
  };
179
208
  }
180
209
  return {
181
210
  target: () => descriptor.selection,
182
- resolve: descriptor.resolve,
183
- reenter: descriptor.reenter,
211
+ resolve: descriptor.resolver,
212
+ reenter: descriptor.reenterSource,
184
213
  declinable: descriptor.declinable
185
214
  };
186
215
  };
@@ -215,7 +244,7 @@ const makeSelectionNode = (stateNodes, path, scope, source) => {
215
244
  addSelectionChildren(method, stateNodes, path, scope, source);
216
245
  }
217
246
  if (scope === "branch" && source !== undefined && node.schema !== undefined &&
218
- (source === path || source.startsWith(`${path}.`)) &&
247
+ (source === path || Configuration.isDescendantOf(source, path)) &&
219
248
  getTargetBuilderNode(stateNodes, source).type !== "choice") {
220
249
  Object.defineProperty(method, "update", {
221
250
  value: makeStateUpdateSelection(path, "branch"),
@@ -223,6 +252,9 @@ const makeSelectionNode = (stateNodes, path, scope, source) => {
223
252
  });
224
253
  }
225
254
  }
255
+ if (node.type === "atomic" && scope === "branch" && source === path && node.schema !== undefined) {
256
+ Object.defineProperty(method, "update", { value: makeStateUpdateSelection(path, "branch"), enumerable: true });
257
+ }
226
258
  return method;
227
259
  };
228
260
  const makeHistorySelectionTree = (stateNodes, parent) => {
@@ -242,15 +274,12 @@ const makeHistorySelectionTree = (stateNodes, parent) => {
242
274
  return builder;
243
275
  };
244
276
  const makeTargetSelector = (stateNodes, source) => {
245
- const full = {};
246
- for (const node of stateNodes.byPath.values()) {
247
- if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
248
- full[node.key] = makeSelectionNode(stateNodes, node.path, "full");
249
- }
250
- }
277
+ const full = Object.assign({}, makeSelectionValue("state", "", "full"), {
278
+ initial: makeSelectionValue("initial", "", "full")
279
+ });
251
280
  const branch = {};
252
- const root = getTargetBuilderNode(stateNodes, source.split(".")[0]);
253
- branch[root.key] = makeSelectionNode(stateNodes, root.path, "branch", source);
281
+ const root = getTargetBuilderNode(stateNodes, stateNodes.roots[0]);
282
+ addSelectionChildren(branch, stateNodes, root.path, "branch", source);
254
283
  const local = {};
255
284
  const localScope = getLocalTargetScope(stateNodes, source);
256
285
  if (localScope !== undefined) {
@@ -265,10 +294,15 @@ const makeTargetSelector = (stateNodes, source) => {
265
294
  }
266
295
  return {
267
296
  none: Topology.noneTargetSelection,
297
+ self: getTargetBuilderNode(stateNodes, source).schema === undefined ||
298
+ getTargetBuilderNode(stateNodes, source).type === "final"
299
+ ? {}
300
+ : { update: makeStateUpdateSelection(source, "branch") },
301
+ root: makeSelectionNode(stateNodes, root.path, "branch", source),
268
302
  local,
269
303
  branch,
270
304
  full,
271
- history: makeHistorySelectionTree(stateNodes, undefined)
305
+ history: makeHistorySelectionTree(stateNodes, "")
272
306
  };
273
307
  };
274
308
  const captureDefinitionBranch = (branch, selector, stateNodes, path, trigger) => {
@@ -327,6 +361,8 @@ const getSelectionBuilder = (target, selection, stateNodes, source) => {
327
361
  let parts = selection.path.split(".");
328
362
  if (selection.kind === "history") {
329
363
  builder = target.history;
364
+ if (selection.path !== "")
365
+ parts.unshift("");
330
366
  }
331
367
  else if (selection.scope === "local") {
332
368
  builder = target.local;
@@ -337,12 +373,14 @@ const getSelectionBuilder = (target, selection, stateNodes, source) => {
337
373
  parts = [];
338
374
  }
339
375
  else {
340
- parts = selection.path.slice(scope.length + 1).split(".");
376
+ parts = (scope === "" ? selection.path : selection.path.slice(scope.length + 1)).split(".");
341
377
  }
342
378
  }
343
379
  }
344
380
  else if (selection.scope === "branch") {
345
381
  builder = target.branch;
382
+ if (selection.path !== "")
383
+ parts.unshift("");
346
384
  }
347
385
  else {
348
386
  builder = target.full;
@@ -390,7 +428,7 @@ const validateResolvedSelection = (result, selection, stateNodes) => {
390
428
  const acceptsDescendant = (selection.scope === "local" || selection.scope === "branch") &&
391
429
  (selectedNode?.type === "compound" || selectedNode?.type === "parallel");
392
430
  if (resultPath === undefined ||
393
- (resultPath !== selection.path && !(acceptsDescendant && resultPath.startsWith(`${selection.path}.`)))) {
431
+ (resultPath !== selection.path && !(acceptsDescendant && Configuration.isDescendantOf(resultPath, selection.path)))) {
394
432
  throw new Error(`Machine transition resolver selected "${selection.path}" but constructed "${resultPath ?? "<invalid>"}"`);
395
433
  }
396
434
  };
@@ -457,6 +495,9 @@ const captureNamedBranches = (declarations, path, trigger) => {
457
495
  if (target.updatePath !== undefined) {
458
496
  throw new Error(`Machine transition branch "${key}" cannot declare an updating target`);
459
497
  }
498
+ if (hasProperty(target, TransitionBuilderDescriptorTypeId)) {
499
+ throw new Error(`Machine transition branch "${key}" requires a topology selection; apply .reenter() to .branches(...)`);
500
+ }
460
501
  if (title !== undefined && (typeof title !== "string" || title.length === 0)) {
461
502
  throw new Error(`Machine transition branch "${key}" title must be a non-empty string`);
462
503
  }
@@ -654,10 +695,10 @@ const captureInvokeDefinition = (invoke, stateNodes, path) => {
654
695
  captured[key] = captureTransition(captured[key], stateNodes, path, key);
655
696
  }
656
697
  }
657
- return captured;
698
+ return InvocationDefinition.capture(captured, path);
658
699
  });
659
700
  if (Array.isArray(authored))
660
- return capturedDefinitions;
701
+ return Object.freeze(capturedDefinitions);
661
702
  return capturedDefinitions[0];
662
703
  };
663
704
  const flattenHandlers = (handlers, stateNodes, states, prefix, config) => {
@@ -671,6 +712,12 @@ const flattenHandlers = (handlers, stateNodes, states, prefix, config) => {
671
712
  throw new Error(`Machine expected state "${path}" handler to be an object`);
672
713
  }
673
714
  const { states: childConfig, ...stateConfig } = nodeConfig;
715
+ if (typeof stateConfig.history === "object" && stateConfig.history !== null) {
716
+ stateConfig.history = Object.freeze(Object.fromEntries(Object.entries(stateConfig.history).map(([key, entry]) => [
717
+ key,
718
+ typeof entry === "object" && entry !== null ? Object.freeze({ ...entry }) : entry
719
+ ])));
720
+ }
674
721
  const on = stateConfig.on;
675
722
  if (typeof on === "object" && on !== null) {
676
723
  const capturedOn = captureEventHandlers(on, stateNodes, path);
@@ -712,340 +759,11 @@ const flattenHandlers = (handlers, stateNodes, states, prefix, config) => {
712
759
  };
713
760
  const makeHandle = (self) => ((config) => {
714
761
  const handlers = Object.create(null);
715
- flattenHandlers(handlers, self.stateNodes, self.states, "", config);
762
+ flattenHandlers(handlers, self.stateNodes, self.states, "", { "": config });
716
763
  return makeWithHandlers(self, handlers);
717
764
  });
718
765
  export const isMachine = (u) => hasProperty(u, TypeId) && u[TypeId] === TypeId;
719
766
  export const isFinal = (machine, state) => internalPlanner.isFinal(machine, state);
720
- const withFrom = (method, kind, valued) => {
721
- const builder = {};
722
- if (valued) {
723
- Object.defineProperty(builder, "decoded", {
724
- value: method,
725
- enumerable: false
726
- });
727
- }
728
- Object.defineProperty(builder, "from", {
729
- value: (...args) => {
730
- if (!valued) {
731
- return method(undefined, ...args);
732
- }
733
- const omitted = args.length === 0 || (kind === "nested" && args.length === 1 && typeof args[0] === "function");
734
- const input = omitted ? {} : args[0];
735
- const rest = omitted ? args : args.slice(1);
736
- return method(Topology.makeStateInput(input), ...rest);
737
- },
738
- enumerable: false
739
- });
740
- return builder;
741
- };
742
- const withInitial = (builder, path, valued, values) => {
743
- const initial = withFrom((value) => Topology.makeInitialTarget(path, value, values), "leaf", valued);
744
- Object.defineProperty(builder, "initial", {
745
- value: initial,
746
- enumerable: false
747
- });
748
- return builder;
749
- };
750
- const makeSnapshotBuilder = (states, options) => {
751
- const builder = {};
752
- for (const key of Object.keys(states)) {
753
- const definition = states[key];
754
- const pseudoType = definition.type;
755
- if (pseudoType === "history") {
756
- continue;
757
- }
758
- const path = options.prefix === "" ? key : `${options.prefix}.${key}`;
759
- if (pseudoType === "choice") {
760
- builder[key] = () => Topology.makeChoiceTarget(path, getParentPathRuntime(path));
761
- continue;
762
- }
763
- const node = Topology.getStateNodeDefinition(path, definition);
764
- const method = withFrom((value, selector) => makeSnapshotForNode(definition, key, value, selector, options), node.states === undefined ? "leaf" : "nested", node.schema !== undefined);
765
- builder[key] = node.states === undefined || options.mode !== "full" || options.prefix !== ""
766
- ? method
767
- : withInitial(method, path, node.schema !== undefined);
768
- }
769
- return builder;
770
- };
771
- const makeParallelSnapshotBuilder = (states, options, regions) => {
772
- const builder = {};
773
- Object.defineProperty(builder, SnapshotBuilderStateTypeId, {
774
- value: regions,
775
- enumerable: false
776
- });
777
- for (const key of Object.keys(states)) {
778
- const definition = states[key];
779
- const pseudoType = definition.type;
780
- if (pseudoType === "history" || pseudoType === "choice") {
781
- continue;
782
- }
783
- if (hasProperty(regions, key)) {
784
- continue;
785
- }
786
- const path = options.prefix === "" ? key : `${options.prefix}.${key}`;
787
- const node = Topology.getStateNodeDefinition(path, definition);
788
- const method = withFrom((value, selector) => {
789
- const nextRegions = {};
790
- for (const regionKey of Object.keys(regions)) {
791
- nextRegions[regionKey] = regions[regionKey];
792
- }
793
- nextRegions[key] = makeSnapshotForNode(definition, key, value, selector, options);
794
- return makeParallelSnapshotBuilder(states, options, nextRegions);
795
- }, node.states === undefined ? "leaf" : "nested", node.schema !== undefined);
796
- builder[key] = method;
797
- }
798
- return builder;
799
- };
800
- const getParallelSnapshotBuilderRegions = (path, states, builder) => {
801
- if (typeof builder !== "object" || builder === null || !hasProperty(builder, SnapshotBuilderStateTypeId)) {
802
- throw new Error(`Machine expected parallel state "${path}" builder callback to return a builder`);
803
- }
804
- const regions = builder[SnapshotBuilderStateTypeId];
805
- for (const key of Object.keys(states)) {
806
- const pseudoType = states[key].type;
807
- if (pseudoType === "history" || pseudoType === "choice") {
808
- continue;
809
- }
810
- if (!hasProperty(regions, key)) {
811
- throw new Error(`Machine expected parallel state "${path}" builder callback to provide region "${key}"`);
812
- }
813
- }
814
- return regions;
815
- };
816
- const makeSnapshotForNode = (definition, key, value, selector, options) => {
817
- const path = options.prefix === "" ? key : `${options.prefix}.${key}`;
818
- const node = Topology.getStateNodeDefinition(path, definition);
819
- const snapshot = {
820
- path,
821
- value
822
- };
823
- if (node.states === undefined) {
824
- return snapshot;
825
- }
826
- if (selector === undefined) {
827
- throw new Error(`Machine expected state "${path}" builder to provide active child states`);
828
- }
829
- if (node.type === "parallel") {
830
- const builder = makeParallelSnapshotBuilder(node.states, { ...options, prefix: path }, {});
831
- const selected = selector(builder);
832
- snapshot.states = getParallelSnapshotBuilderRegions(path, node.states, selected);
833
- return snapshot;
834
- }
835
- const childStates = options.mode === "initial" && node.initial !== undefined
836
- ? { [node.initial]: node.states[node.initial] }
837
- : node.states;
838
- const selected = selector(makeSnapshotBuilder(childStates, { ...options, prefix: path }));
839
- snapshot.state = selected;
840
- return snapshot;
841
- };
842
- const getTargetBuilderNode = (stateNodes, path) => {
843
- const node = stateNodes.byPath.get(path);
844
- if (node === undefined) {
845
- throw new Error(`Machine expected state path "${path}" to exist`);
846
- }
847
- return node;
848
- };
849
- const getLocalTargetScope = (stateNodes, source) => {
850
- let current = source;
851
- while (current !== undefined) {
852
- const node = stateNodes.byPath.get(current);
853
- if (node === undefined) {
854
- return undefined;
855
- }
856
- if (node.type === "compound") {
857
- return node.path;
858
- }
859
- current = node.parent;
860
- }
861
- return undefined;
862
- };
863
- const hasTargetValues = (values) => values !== undefined && Object.keys(values).length > 0;
864
- const makeTargetWithValues = (path, value, values) => hasTargetValues(values)
865
- ? Topology.makeTarget(path, value, { values: values })
866
- : Topology.makeTarget(path, value);
867
- const getTargetBuilderDefinition = (states, targetPath) => {
868
- let children = states;
869
- let path = "";
870
- let definition;
871
- for (const key of targetPath.split(".")) {
872
- if (!hasProperty(children, key)) {
873
- throw new Error(`Machine expected state path "${targetPath}" to exist`);
874
- }
875
- definition = children[key];
876
- path = path === "" ? key : `${path}.${key}`;
877
- const node = Topology.getStateNodeDefinition(path, definition);
878
- children = node.states ?? {};
879
- }
880
- return definition;
881
- };
882
- const makeParallelTarget = (states, node, value, selector, values) => {
883
- if (selector === undefined) {
884
- throw new Error(`Machine expected parallel target "${node.path}" builder to provide every active region`);
885
- }
886
- const snapshot = makeSnapshotForNode(getTargetBuilderDefinition(states, node.path), node.key, value, selector, { mode: "full", prefix: node.parent ?? "" });
887
- return Topology.makeTarget(node.path, value, {
888
- snapshot: snapshot,
889
- values: values
890
- });
891
- };
892
- const extendTargetValues = (values, path, value) => {
893
- const next = {};
894
- if (values !== undefined) {
895
- for (const key of Object.keys(values)) {
896
- next[key] = values[key];
897
- }
898
- }
899
- next[path] = value;
900
- return next;
901
- };
902
- const makeLocalTargetChildBuilder = (states, stateNodes, parentPath, values, source) => {
903
- const parent = getTargetBuilderNode(stateNodes, parentPath);
904
- const builder = {};
905
- for (const childPath of Array.from(stateNodes.byPath.values())
906
- .filter((node) => node.parent === parent.path && node.type !== "history")
907
- .map((node) => node.path)) {
908
- const child = getTargetBuilderNode(stateNodes, childPath);
909
- if (child.type === "choice") {
910
- builder[child.key] = () => Topology.makeChoiceTarget(child.path, parent.path, values);
911
- continue;
912
- }
913
- const method = withFrom((value, selector) => {
914
- if (child.type === "atomic" || child.type === "final") {
915
- return makeTargetWithValues(child.path, value, values);
916
- }
917
- if (child.type === "parallel") {
918
- if (source !== child.path && !source.startsWith(`${child.path}.`)) {
919
- return makeParallelTarget(states, child, value, selector, values);
920
- }
921
- if (selector === undefined) {
922
- throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`);
923
- }
924
- return selector(makeLocalTargetChildBuilder(states, stateNodes, child.path, child.schema === undefined ? values : extendTargetValues(values, child.path, value), source));
925
- }
926
- if (selector === undefined) {
927
- throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`);
928
- }
929
- return selector(makeLocalTargetChildBuilder(states, stateNodes, child.path, child.schema === undefined ? values : extendTargetValues(values, child.path, value), source));
930
- }, child.type === "atomic" || child.type === "final" ? "leaf" : "nested", child.schema !== undefined);
931
- builder[child.key] = child.type === "atomic" || child.type === "final"
932
- ? method
933
- : withInitial(method, child.path, child.schema !== undefined, values);
934
- }
935
- return builder;
936
- };
937
- const makeLocalTargetBuilder = (states, stateNodes, source) => {
938
- const scope = getLocalTargetScope(stateNodes, source);
939
- if (scope === undefined) {
940
- return {};
941
- }
942
- const builder = makeLocalTargetChildBuilder(states, stateNodes, scope, undefined, source);
943
- const scopeNode = getTargetBuilderNode(stateNodes, scope);
944
- if (scopeNode.schema !== undefined) {
945
- builder.with = withFrom((value, selector) => {
946
- if (selector === undefined) {
947
- throw new Error(`Machine expected target "${scope}" builder to provide an active child state`);
948
- }
949
- return selector(makeLocalTargetChildBuilder(states, stateNodes, scope, { [scope]: value }, source));
950
- }, "nested", true);
951
- }
952
- return builder;
953
- };
954
- const addBranchTargetChildren = (builder, states, stateNodes, parentPath, values, source) => {
955
- const parent = getTargetBuilderNode(stateNodes, parentPath);
956
- for (const childPath of Array.from(stateNodes.byPath.values())
957
- .filter((node) => node.parent === parent.path && node.type !== "history")
958
- .map((node) => node.path)) {
959
- const child = getTargetBuilderNode(stateNodes, childPath);
960
- if (child.type === "choice") {
961
- builder[child.key] = () => Topology.makeChoiceTarget(child.path, parent.path, values);
962
- continue;
963
- }
964
- builder[child.key] = makeBranchTargetNodeBuilder(states, stateNodes, child.path, values, source);
965
- }
966
- };
967
- const makeBranchTargetNodeBuilder = (states, stateNodes, path, values, source) => {
968
- const node = getTargetBuilderNode(stateNodes, path);
969
- if (node.type === "atomic" || node.type === "final") {
970
- return withFrom((value) => makeTargetWithValues(node.path, value, values), "leaf", node.schema !== undefined);
971
- }
972
- const builder = withFrom((value, selector) => {
973
- if (node.type === "parallel") {
974
- if (source !== node.path && !source.startsWith(`${node.path}.`)) {
975
- return makeParallelTarget(states, node, value, selector, values);
976
- }
977
- if (selector === undefined) {
978
- throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`);
979
- }
980
- const nextBuilder = {};
981
- addBranchTargetChildren(nextBuilder, states, stateNodes, node.path, node.schema === undefined ? values : extendTargetValues(values, node.path, value), source);
982
- return selector(nextBuilder);
983
- }
984
- if (selector === undefined) {
985
- throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`);
986
- }
987
- const nextBuilder = {};
988
- addBranchTargetChildren(nextBuilder, states, stateNodes, node.path, node.schema === undefined ? values : extendTargetValues(values, node.path, value), source);
989
- return selector(nextBuilder);
990
- }, "nested", node.schema !== undefined);
991
- withInitial(builder, node.path, node.schema !== undefined, values);
992
- if (node.type !== "parallel" || source === node.path || source.startsWith(`${node.path}.`)) {
993
- addBranchTargetChildren(builder, states, stateNodes, node.path, values, source);
994
- }
995
- return builder;
996
- };
997
- const makeBranchTargetBuilder = (states, stateNodes, source) => {
998
- const rootPath = source.split(".")[0];
999
- const root = getTargetBuilderNode(stateNodes, rootPath);
1000
- return {
1001
- [root.key]: makeBranchTargetNodeBuilder(states, stateNodes, root.path, undefined, source)
1002
- };
1003
- };
1004
- const makeHistoryTargetBuilder = (states, prefix) => {
1005
- const builder = {};
1006
- for (const key of Object.keys(states)) {
1007
- const path = prefix === "" ? key : `${prefix}.${key}`;
1008
- const definition = Topology.getStateNodeDefinition(path, states[key]);
1009
- if (definition.type === "history") {
1010
- const parent = getParentPathRuntime(path);
1011
- builder[key] = () => Topology.makeHistoryTarget(path, parent);
1012
- continue;
1013
- }
1014
- if (definition.states !== undefined) {
1015
- builder[key] = makeHistoryTargetBuilder(definition.states, path);
1016
- }
1017
- }
1018
- return builder;
1019
- };
1020
- const getParentPathRuntime = (path) => {
1021
- const separator = path.lastIndexOf(".");
1022
- if (separator < 0) {
1023
- throw new Error(`Machine expected history state "${path}" to have an active parent`);
1024
- }
1025
- return path.slice(0, separator);
1026
- };
1027
- const makeTargetBuilder = (states, stateNodes) => {
1028
- const full = makeSnapshotBuilder(states, { mode: "full", prefix: "" });
1029
- const history = makeHistoryTargetBuilder(states, "");
1030
- return (source) => ({
1031
- none: Topology.makeNoTarget,
1032
- local: makeLocalTargetBuilder(states, stateNodes, source),
1033
- branch: makeBranchTargetBuilder(states, stateNodes, source),
1034
- full,
1035
- history
1036
- });
1037
- };
1038
- const makeInitialSelector = (stateNodes) => {
1039
- const selector = {};
1040
- for (const node of stateNodes.byPath.values()) {
1041
- if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
1042
- selector[node.key] = node.type === "atomic" || node.type === "final"
1043
- ? makeSelectionMethod("state", node.path, "initial")
1044
- : Object.freeze({ initial: makeSelectionValue("initial", node.path, "initial") });
1045
- }
1046
- }
1047
- return Object.freeze(selector);
1048
- };
1049
767
  const getInitialSelectionBuilder = (initialBuilder, selection) => {
1050
768
  const path = selection.path;
1051
769
  if (path === undefined || path.includes(".")) {
@@ -1057,11 +775,12 @@ const getInitialSelectionBuilder = (initialBuilder, selection) => {
1057
775
  }
1058
776
  return builder;
1059
777
  };
1060
- const captureInitialBranch = (definition, stateNodes, initialBuilder) => {
1061
- if (typeof definition !== "function") {
778
+ const captureInitialBranch = (definition, initialBuilder, configuration) => {
779
+ if (definition !== undefined && typeof definition !== "function") {
1062
780
  throw new Error("Machine initial definition must be a target-first callback");
1063
781
  }
1064
- const result = definition(decorateInitialSelectorNode(makeInitialSelector(stateNodes)));
782
+ const selector = decorateInitialSelection(makeSelectionValue(configuration ? "state" : "initial", "", "initial"));
783
+ const result = definition === undefined ? selector : definition(selector);
1065
784
  let selection;
1066
785
  let resolve;
1067
786
  if (Topology.isTargetSelection(result)) {
@@ -1092,9 +811,12 @@ const validateInitialSelection = (result, selection) => {
1092
811
  throw new Error(`Machine initial resolver selected "${selection.path}" but constructed "${resultPath}"`);
1093
812
  }
1094
813
  };
1095
- const compileInitial = (definition, states, stateNodes) => {
1096
- const initialBuilder = makeSnapshotBuilder(states, { mode: "initial", prefix: "" });
1097
- const branch = captureInitialBranch(definition, stateNodes, initialBuilder);
814
+ const compileInitial = (definition, states, stateNodes, configuration = false) => {
815
+ const rootNode = getTargetBuilderNode(stateNodes, "");
816
+ const initialBuilder = configuration ?
817
+ makeSnapshotBuilder(states, { mode: "full", prefix: "" })
818
+ : { "": withFrom((value) => Topology.makeInitialTarget("", value), "leaf", rootNode.schema !== undefined) };
819
+ const branch = captureInitialBranch(definition, initialBuilder, configuration);
1098
820
  return {
1099
821
  initial: (input) => {
1100
822
  const result = branch.resolve === undefined
@@ -1109,15 +831,13 @@ const compileInitial = (definition, states, stateNodes) => {
1109
831
  })
1110
832
  };
1111
833
  };
1112
- export const state = ((node) => {
1113
- StateDefinition.validateStateDefinitions({ state: node }, "Machine.state");
1114
- return StateDefinition.captureStateDefinitions({ state: node }).state;
1115
- });
1116
- export const states = ((states) => {
1117
- StateDefinition.validateStateDefinitions(states, "Machine.states");
1118
- const captured = StateDefinition.captureStateDefinitions(states);
834
+ export const state = (node) => {
835
+ const captured = StateDefinition.captureRoot(node);
836
+ const access = makeStateHelpers();
837
+ return Object.freeze({ ...access, "~effect/Machine/State": "~effect/Machine/State", node: captured });
838
+ };
839
+ const makeStateHelpers = () => {
1119
840
  return {
1120
- states: captured,
1121
841
  path: ((path) => path),
1122
842
  get: ((snapshot, path) => Topology.getSnapshotByPath(snapshot, path).pipe(Option.map((snapshot) => snapshot.value))),
1123
843
  getWithParents: ((snapshot, path) => {
@@ -1127,31 +847,42 @@ export const states = ((states) => {
1127
847
  getSnapshot: Topology.getSnapshotByPath,
1128
848
  matches: ((snapshot, path) => Option.isSome(Topology.getSnapshotByPath(snapshot, path)))
1129
849
  };
1130
- });
1131
- export const make = ((config) => {
1132
- StateDefinition.validateStateDefinitions(config.states, "Machine.make");
850
+ };
851
+ export const make = (config) => {
852
+ const states = Object.freeze({ "": config.root.node });
1133
853
  const self = Object.create(Proto);
1134
- self.states = config.states;
854
+ self.states = states;
855
+ self.root = config.root;
1135
856
  self.events = config.events;
1136
857
  self.internalEvents = config.internalEvents ?? Protocol.makeEventProtocol("internal", []);
1137
858
  self.emittedEvents = config.emittedEvents ?? Protocol.makeEventProtocol("emitted", []);
1138
859
  self.parent = config.parent;
1139
860
  self.input = config.input;
1140
861
  self.id = config.id;
1141
- self.stateNodes = Topology.compileStateNodes(config.states);
1142
- const compiledInitial = compileInitial(config.initial, config.states, self.stateNodes);
862
+ self.stateNodes = Topology.compileStateNodes(states);
863
+ const compiledInitial = compileInitial(config.initialConfiguration ?? config.initial, states, self.stateNodes, config.initialConfiguration !== undefined);
1143
864
  self.initial = compiledInitial.initial;
1144
865
  self.initialDefinition = compiledInitial.definition;
1145
- self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes);
866
+ self.makeTargetBuilder = makeTargetBuilder(states, self.stateNodes);
1146
867
  self.handlers = Object.create(null);
1147
868
  self.handle = makeHandle(self);
1148
869
  Protocol.setProtocol(self);
1149
870
  return self;
1150
- });
871
+ };
1151
872
  const flattenEventProtocolInputs = (kind, inputs) => inputs.flatMap((input) => Protocol.isEventProtocol(input, kind)
1152
873
  ? Protocol.eventProtocolSchemas(input)
1153
874
  : [input]);
1154
875
  export const events = (...inputs) => Protocol.makeEventProtocol("public", flattenEventProtocolInputs("public", inputs));
876
+ const eventFieldSchemas = (cases) => {
877
+ for (const [tag, fields] of Object.entries(cases)) {
878
+ if (Object.hasOwn(fields, "_tag"))
879
+ throw new Error(`Machine event "${tag}" fields cannot declare _tag`);
880
+ }
881
+ return Object.keys(cases).length === 0 ? [] : [Schema.TaggedUnion(cases)];
882
+ };
883
+ export const eventsFromFields = (cases) => Protocol.makeEventProtocol("public", eventFieldSchemas(cases));
884
+ export const internalEventsFromFields = (cases) => Protocol.makeEventProtocol("internal", eventFieldSchemas(cases));
885
+ export const emittedEventsFromFields = (cases) => Protocol.makeEventProtocol("emitted", eventFieldSchemas(cases));
1155
886
  const makeParent = (mode, events) => {
1156
887
  if (!Protocol.isEventProtocol(events, "public")) {
1157
888
  throw new Error("Machine parent declarations require a protocol created with Machine.events");