@typeonce/effect-machine 0.16.0 → 0.18.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 (68) hide show
  1. package/README.md +157 -150
  2. package/dist/Machine.d.ts +416 -592
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +55 -136
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +7 -7
  7. package/dist/internal/machine/atom.d.ts.map +1 -1
  8. package/dist/internal/machine/atom.js.map +1 -1
  9. package/dist/internal/machine/cluster.d.ts +2 -2
  10. package/dist/internal/machine/cluster.d.ts.map +1 -1
  11. package/dist/internal/machine/cluster.js +6 -5
  12. package/dist/internal/machine/cluster.js.map +1 -1
  13. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  14. package/dist/internal/machine/executionPlan.js +10 -3
  15. package/dist/internal/machine/executionPlan.js.map +1 -1
  16. package/dist/internal/machine/invocation.d.ts.map +1 -1
  17. package/dist/internal/machine/invocation.js +1 -1
  18. package/dist/internal/machine/invocation.js.map +1 -1
  19. package/dist/internal/machine/machine.d.ts +8 -6
  20. package/dist/internal/machine/machine.d.ts.map +1 -1
  21. package/dist/internal/machine/machine.js +239 -35
  22. package/dist/internal/machine/machine.js.map +1 -1
  23. package/dist/internal/machine/planner.d.ts +19 -4
  24. package/dist/internal/machine/planner.d.ts.map +1 -1
  25. package/dist/internal/machine/planner.js +56 -20
  26. package/dist/internal/machine/planner.js.map +1 -1
  27. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  28. package/dist/internal/machine/stateDefinition.js +14 -0
  29. package/dist/internal/machine/stateDefinition.js.map +1 -1
  30. package/dist/internal/machine/topology.d.ts +9 -0
  31. package/dist/internal/machine/topology.d.ts.map +1 -1
  32. package/dist/internal/machine/topology.js +16 -0
  33. package/dist/internal/machine/topology.js.map +1 -1
  34. package/dist/internal/testing/machine/finiteModel.d.ts.map +1 -1
  35. package/dist/internal/testing/machine/finiteModel.js +15 -20
  36. package/dist/internal/testing/machine/finiteModel.js.map +1 -1
  37. package/dist/internal/testing/machine/transitionCoverage.d.ts.map +1 -1
  38. package/dist/internal/testing/machine/transitionCoverage.js +2 -0
  39. package/dist/internal/testing/machine/transitionCoverage.js.map +1 -1
  40. package/dist/testing/MachineTest.d.ts +15 -15
  41. package/dist/testing/MachineTest.d.ts.map +1 -1
  42. package/dist/testing/MachineTest.js +5 -8
  43. package/dist/testing/MachineTest.js.map +1 -1
  44. package/dist/unstable/cluster/ClusterMachine.d.ts +2 -2
  45. package/dist/unstable/cluster/ClusterMachine.d.ts.map +1 -1
  46. package/dist/unstable/cluster/ClusterMachine.js.map +1 -1
  47. package/dist/unstable/reactivity/AtomMachine.d.ts +13 -13
  48. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  49. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  50. package/docs/agent-guide.md +229 -193
  51. package/package.json +1 -1
  52. package/src/Machine.ts +1686 -2234
  53. package/src/internal/machine/atom.ts +20 -15
  54. package/src/internal/machine/cluster.ts +14 -9
  55. package/src/internal/machine/executionPlan.ts +8 -3
  56. package/src/internal/machine/invocation.ts +1 -1
  57. package/src/internal/machine/machine.ts +365 -48
  58. package/src/internal/machine/planner.ts +89 -27
  59. package/src/internal/machine/stateDefinition.ts +39 -0
  60. package/src/internal/machine/topology.ts +28 -0
  61. package/src/internal/testing/machine/exploration.ts +1 -1
  62. package/src/internal/testing/machine/finiteModel.ts +18 -21
  63. package/src/internal/testing/machine/trace.ts +1 -1
  64. package/src/internal/testing/machine/transitionCoverage.ts +2 -0
  65. package/src/internal/testing/machine/verification.ts +1 -1
  66. package/src/testing/MachineTest.ts +18 -15
  67. package/src/unstable/cluster/ClusterMachine.ts +8 -4
  68. package/src/unstable/reactivity/AtomMachine.ts +20 -13
@@ -16,8 +16,10 @@ import * as Topology from "./topology.js";
16
16
  export { ChildAlreadyExistsError, InfiniteTransitionError, MachineSchemaDecodeError, MachineSchemaEncodeError, ProcessLocalError, StartupError, StoppedError } from "./errors.js";
17
17
  export { ChildMachineLogicTypeId, InitialEventTypeId, SnapshotBuilderStateTypeId } from "./symbols.js";
18
18
  const TypeId = "~effect/Machine";
19
+ const ParentTypeId = "~effect/Machine/Parent";
19
20
  export const InvokeTypeId = Symbol.for("effect/Machine/Invoke");
20
21
  export const TransitionTypeId = Symbol.for("effect/Machine/Transition");
22
+ const InvokeBuilderDescriptorTypeId = Symbol("effect/Machine/InvokeBuilderDescriptor");
21
23
  const ChildMachineTypeId = "~effect/Machine/ChildMachine";
22
24
  const Proto = {
23
25
  ...Inspectable.BaseProto,
@@ -29,14 +31,13 @@ const Proto = {
29
31
  };
30
32
  }
31
33
  };
32
- const makeBoundInvoke = (config) => config;
33
34
  const makeWithHandlers = (self, handlers) => {
34
35
  const machine = Object.create(Proto);
35
36
  machine.states = self.states;
36
37
  machine.events = self.events;
37
38
  machine.internalEvents = self.internalEvents;
38
39
  machine.emittedEvents = self.emittedEvents;
39
- machine.parentEvents = self.parentEvents;
40
+ machine.parent = self.parent;
40
41
  machine.input = self.input;
41
42
  machine.id = self.id;
42
43
  machine.initial = self.initial;
@@ -44,16 +45,134 @@ const makeWithHandlers = (self, handlers) => {
44
45
  machine.stateNodes = self.stateNodes;
45
46
  machine.makeTargetBuilder = self.makeTargetBuilder;
46
47
  machine.handlers = handlers;
47
- machine.invoke = makeBoundInvoke;
48
48
  Protocol.copyProtocol(self, machine);
49
49
  return machine;
50
50
  };
51
+ const TransitionBuilderDescriptorTypeId = Symbol("effect/Machine/TransitionBuilderDescriptor");
52
+ const InitialBuilderDescriptorTypeId = Symbol("effect/Machine/InitialBuilderDescriptor");
53
+ const plainTargetSelection = (selection) => selection.kind === "none"
54
+ ? Topology.noneTargetSelection
55
+ : Topology.makeTargetSelection(selection.kind, selection.path, selection.scope);
56
+ const transitionOptions = (options) => {
57
+ const configuration = typeof options === "object" && options !== null
58
+ ? options
59
+ : {};
60
+ return {
61
+ reenter: configuration.reenter === true,
62
+ declinable: configuration.declinable === true
63
+ };
64
+ };
65
+ const makeDirectTransitionDescriptor = (selection, resolve, options) => {
66
+ const shared = {
67
+ [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
68
+ type: "direct",
69
+ selection: plainTargetSelection(selection),
70
+ ...transitionOptions(options)
71
+ };
72
+ return resolve === undefined
73
+ ? Object.freeze(shared)
74
+ : Object.freeze({ ...shared, resolve });
75
+ };
76
+ const decorateTransitionSelection = (selection) => Object.freeze({
77
+ ...selection,
78
+ resolve: (resolve, options) => makeDirectTransitionDescriptor(selection, resolve, options),
79
+ reenter: () => makeDirectTransitionDescriptor(selection, undefined, { reenter: true })
80
+ });
81
+ const noneTransitionSelection = decorateTransitionSelection(Topology.noneTargetSelection);
82
+ const makeInitialBuilderDescriptor = (selection, resolve) => Object.freeze({
83
+ [InitialBuilderDescriptorTypeId]: InitialBuilderDescriptorTypeId,
84
+ selection: plainTargetSelection(selection),
85
+ resolve
86
+ });
87
+ const decorateInitialSelection = (selection) => Object.freeze({
88
+ ...selection,
89
+ resolve: (resolve) => makeInitialBuilderDescriptor(selection, resolve)
90
+ });
91
+ const decorateInitialSelectorNode = (node) => {
92
+ if (Topology.isTargetSelection(node))
93
+ return decorateInitialSelection(node);
94
+ if (typeof node === "function") {
95
+ const wrapped = ((...args) => decorateInitialSelection(node(...args)));
96
+ for (const key of Object.keys(node)) {
97
+ wrapped[key] = decorateInitialSelectorNode(node[key]);
98
+ }
99
+ return Object.freeze(wrapped);
100
+ }
101
+ if (typeof node === "object" && node !== null) {
102
+ const wrapped = {};
103
+ for (const key of Object.keys(node)) {
104
+ wrapped[key] = decorateInitialSelectorNode(node[key]);
105
+ }
106
+ return Object.freeze(wrapped);
107
+ }
108
+ return node;
109
+ };
110
+ const decorateTransitionSelectorNode = (node) => {
111
+ if (Topology.isTargetSelection(node)) {
112
+ return node === Topology.noneTargetSelection ? noneTransitionSelection : decorateTransitionSelection(node);
113
+ }
114
+ if (typeof node === "function") {
115
+ const wrapped = ((...args) => decorateTransitionSelection(node(...args)));
116
+ for (const key of Object.keys(node)) {
117
+ wrapped[key] = decorateTransitionSelectorNode(node[key]);
118
+ }
119
+ return Object.freeze(wrapped);
120
+ }
121
+ if (typeof node === "object" && node !== null) {
122
+ const wrapped = {};
123
+ for (const key of Object.keys(node)) {
124
+ wrapped[key] = decorateTransitionSelectorNode(node[key]);
125
+ }
126
+ return Object.freeze(wrapped);
127
+ }
128
+ return node;
129
+ };
130
+ const makeTransitionSelector = (stateNodes, source) => {
131
+ const selector = {
132
+ ...decorateTransitionSelectorNode(makeTargetSelector(stateNodes, source))
133
+ };
134
+ selector.branches = (declarations) => Object.freeze({
135
+ resolve: (resolve, options) => Object.freeze({
136
+ [TransitionBuilderDescriptorTypeId]: TransitionBuilderDescriptorTypeId,
137
+ type: "branches",
138
+ declarations,
139
+ resolve,
140
+ ...transitionOptions(options)
141
+ })
142
+ });
143
+ return Object.freeze(selector);
144
+ };
145
+ const normalizeTransitionBuilder = (transition, stateNodes, path) => {
146
+ const result = transition(makeTransitionSelector(stateNodes, path));
147
+ if (Topology.isTargetSelection(result)) {
148
+ const selection = plainTargetSelection(result);
149
+ return { target: () => selection };
150
+ }
151
+ if (!hasProperty(result, TransitionBuilderDescriptorTypeId))
152
+ return result;
153
+ const descriptor = result;
154
+ if (descriptor.type === "branches") {
155
+ return {
156
+ branches: () => descriptor.declarations,
157
+ resolve: descriptor.resolve,
158
+ reenter: descriptor.reenter,
159
+ declinable: descriptor.declinable
160
+ };
161
+ }
162
+ return {
163
+ target: () => descriptor.selection,
164
+ resolve: descriptor.resolve,
165
+ reenter: descriptor.reenter,
166
+ declinable: descriptor.declinable
167
+ };
168
+ };
51
169
  const transitionTargetSelection = (selection) => Object.freeze({
52
170
  path: selection.path,
53
171
  kind: selection.kind,
54
172
  scope: selection.scope
55
173
  });
56
174
  const makeSelectionMethod = (kind, path, scope) => () => Topology.makeTargetSelection(kind, path, scope);
175
+ const makeSelectionValue = (kind, path, scope) => Topology.makeTargetSelection(kind, path, scope);
57
176
  const addSelectionChildren = (builder, stateNodes, parent, scope) => {
58
177
  for (const node of stateNodes.byPath.values()) {
59
178
  if (node.parent !== parent || node.type === "history")
@@ -67,7 +186,7 @@ const makeSelectionNode = (stateNodes, path, scope) => {
67
186
  const method = makeSelectionMethod(kind, path, scope);
68
187
  if (node.type !== "atomic" && node.type !== "final" && node.type !== "choice" && node.type !== "history") {
69
188
  Object.defineProperty(method, "initial", {
70
- value: makeSelectionMethod("initial", path, scope),
189
+ value: makeSelectionValue("initial", path, scope),
71
190
  enumerable: true
72
191
  });
73
192
  if (scope === "local" || scope === "branch") {
@@ -82,7 +201,7 @@ const makeHistorySelectionTree = (stateNodes, parent) => {
82
201
  if (node.parent !== parent)
83
202
  continue;
84
203
  if (node.type === "history") {
85
- builder[node.key] = makeSelectionMethod("history", node.path, "full");
204
+ builder[node.key] = makeSelectionValue("history", node.path, "full");
86
205
  }
87
206
  else if (node.type !== "choice") {
88
207
  const children = makeHistorySelectionTree(stateNodes, node.path);
@@ -107,12 +226,12 @@ const makeTargetSelector = (stateNodes, source) => {
107
226
  if (localScope !== undefined) {
108
227
  const localScopeNode = getTargetBuilderNode(stateNodes, localScope);
109
228
  if (localScopeNode.schema !== undefined) {
110
- local.with = makeSelectionMethod("state", localScope, "local");
229
+ local.with = makeSelectionValue("state", localScope, "local");
111
230
  }
112
231
  addSelectionChildren(local, stateNodes, localScope, "local");
113
232
  }
114
233
  return {
115
- none: makeSelectionMethod("none", undefined, "local"),
234
+ none: Topology.noneTargetSelection,
116
235
  local,
117
236
  branch,
118
237
  full,
@@ -167,7 +286,7 @@ const getSelectionBuilder = (target, selection, stateNodes, source) => {
167
286
  }
168
287
  return builder;
169
288
  };
170
- const constructSelectedTarget = (builder) => typeof builder === "function" ? builder() : builder.from();
289
+ const constructSelectedTarget = (builder) => typeof builder?.from === "function" ? builder.from() : builder();
171
290
  const validateResolvedSelection = (result, selection, stateNodes) => {
172
291
  if (selection.kind === "none") {
173
292
  if (result !== undefined) {
@@ -198,7 +317,15 @@ const runCapturedBranch = (branch, context, enqueue, stateNodes, source) => {
198
317
  delete resolverContext.target;
199
318
  else
200
319
  resolverContext.target = selectedTarget;
320
+ if (branch.declinable === true)
321
+ resolverContext.decline = Topology.makeDeclined;
201
322
  const resolved = branch.resolve(resolverContext, enqueue);
323
+ if (Topology.isDeclined(resolved)) {
324
+ if (branch.declinable !== true) {
325
+ throw new Error(`Machine transition for state "${source}" returned decline without declaring declinable: true`);
326
+ }
327
+ return resolved;
328
+ }
202
329
  validateResolvedSelection(resolved, branch.selection, stateNodes);
203
330
  return resolved === undefined ? constructSelectedTarget(selectedTarget) : resolved;
204
331
  };
@@ -232,7 +359,7 @@ const captureNamedBranches = (declarations, path, trigger) => {
232
359
  if (title !== undefined && (typeof title !== "string" || title.length === 0)) {
233
360
  throw new Error(`Machine transition branch "${key}" title must be a non-empty string`);
234
361
  }
235
- return Object.freeze({ key, title: title ?? key, selection: target });
362
+ return Object.freeze({ key, title: title ?? key, selection: plainTargetSelection(target) });
236
363
  }));
237
364
  };
238
365
  const wrapSelectedBranchBuilder = (builder, owner, branchIndex, branchKey) => {
@@ -287,13 +414,18 @@ const validateSelectedBranchResult = (result, selection, stateNodes) => {
287
414
  }
288
415
  validateResolvedSelection(result, selection, stateNodes);
289
416
  };
290
- const captureTransition = (transition, stateNodes, path, trigger) => {
417
+ const captureTransition = (rawTransition, stateNodes, path, trigger) => {
418
+ if (typeof rawTransition !== "function") {
419
+ throw new Error(`Machine transition for state "${path}" on "${String(trigger)}" must be a target-first callback`);
420
+ }
421
+ const transition = normalizeTransitionBuilder(rawTransition, stateNodes, path);
291
422
  if (typeof transition !== "object" || transition === null) {
292
423
  throw new Error(`Machine transition for state "${path}" on "${String(trigger)}" must be an object`);
293
424
  }
294
425
  const definition = transition;
295
426
  const selector = makeTargetSelector(stateNodes, path);
296
427
  const reenter = definition.reenter === true;
428
+ const declinable = definition.declinable === true;
297
429
  if (hasProperty(definition, "branches")) {
298
430
  const branching = definition;
299
431
  if (typeof branching.branches !== "function" || typeof branching.resolve !== "function") {
@@ -306,7 +438,15 @@ const captureTransition = (transition, stateNodes, path, trigger) => {
306
438
  const resolverContext = { ...context };
307
439
  delete resolverContext.target;
308
440
  resolverContext.select = makeBranchSelectors(context, branches, owner, stateNodes, path);
441
+ if (declinable)
442
+ resolverContext.decline = Topology.makeDeclined;
309
443
  const selected = resolve(resolverContext, enqueue);
444
+ if (Topology.isDeclined(selected)) {
445
+ if (!declinable) {
446
+ throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" returned decline without declaring declinable: true`);
447
+ }
448
+ return { result: selected, branchIndex: -1, branchKey: undefined };
449
+ }
310
450
  if (!Topology.isSelectedBranch(selected) || selected.owner !== owner) {
311
451
  throw new Error(`Machine branching transition for state "${path}" on "${String(trigger)}" must select one declared branch`);
312
452
  }
@@ -323,6 +463,7 @@ const captureTransition = (transition, stateNodes, path, trigger) => {
323
463
  };
324
464
  return {
325
465
  reenter,
466
+ declinable,
326
467
  targets: [
327
468
  ...new Set(branches.flatMap((branch) => branch.selection.path === undefined ? [] : [branch.selection.path]))
328
469
  ],
@@ -345,6 +486,7 @@ const captureTransition = (transition, stateNodes, path, trigger) => {
345
486
  });
346
487
  return {
347
488
  reenter,
489
+ declinable,
348
490
  targets: branch.selection.path === undefined ? [] : [branch.selection.path],
349
491
  branches: [{
350
492
  type: "direct",
@@ -365,18 +507,55 @@ const captureEventHandlers = (on, stateNodes, path) => {
365
507
  }
366
508
  return captured;
367
509
  };
368
- const captureInvokeDefinition = (invoke, stateNodes, path) => {
369
- if (Array.isArray(invoke))
370
- return invoke.map((item) => captureInvokeDefinition(item, stateNodes, path));
371
- if (typeof invoke !== "object" || invoke === null)
372
- return invoke;
373
- const captured = { ...invoke };
374
- for (const key of ["onElement", "onDone", "onFailure", "onSnapshot"]) {
375
- if (captured[key] !== undefined) {
376
- captured[key] = captureTransition(captured[key], stateNodes, path, key);
510
+ const makeInvokeBuilder = (config, channels) => {
511
+ const builder = {
512
+ [InvokeBuilderDescriptorTypeId]: InvokeBuilderDescriptorTypeId,
513
+ config
514
+ };
515
+ for (const channel of channels) {
516
+ if (!hasProperty(config, channel)) {
517
+ builder[channel] = (handler) => makeInvokeBuilder({ ...config, [channel]: handler }, channels);
377
518
  }
378
519
  }
379
- return captured;
520
+ return Object.freeze(builder);
521
+ };
522
+ const invokeSelector = Object.freeze({
523
+ effect: (id, effect) => makeInvokeBuilder({ id, effect }, ["onDone", "onFailure"]),
524
+ stream: (id, stream) => makeInvokeBuilder({ id, stream }, ["onElement", "onDone", "onFailure"]),
525
+ timer: (id, after) => makeInvokeBuilder({ id, after }, ["onDone"]),
526
+ logic: (id, options) => makeInvokeBuilder({ id, ...options }, ["onSnapshot", "onDone", "onFailure"]),
527
+ child: (child, options) => makeInvokeBuilder(options === undefined ? { child } : { child, ...options }, [
528
+ "onSnapshot",
529
+ "onDone",
530
+ "onFailure"
531
+ ])
532
+ });
533
+ const invokeBuilderConfig = (value, path) => {
534
+ if (typeof value !== "object" || value === null ||
535
+ !hasProperty(value, InvokeBuilderDescriptorTypeId) ||
536
+ value[InvokeBuilderDescriptorTypeId] !== InvokeBuilderDescriptorTypeId) {
537
+ throw new Error(`Machine invocation for state "${path}" must be constructed from its source selector`);
538
+ }
539
+ return value.config;
540
+ };
541
+ const captureInvokeDefinition = (invoke, stateNodes, path) => {
542
+ if (typeof invoke !== "function") {
543
+ throw new Error(`Machine invocation for state "${path}" must be a source-first callback`);
544
+ }
545
+ const authored = invoke(invokeSelector);
546
+ const definitions = Array.isArray(authored) ? authored : [authored];
547
+ const capturedDefinitions = definitions.map((definition) => {
548
+ const captured = { ...invokeBuilderConfig(definition, path) };
549
+ for (const key of ["onElement", "onDone", "onFailure", "onSnapshot"]) {
550
+ if (captured[key] !== undefined) {
551
+ captured[key] = captureTransition(captured[key], stateNodes, path, key);
552
+ }
553
+ }
554
+ return captured;
555
+ });
556
+ if (Array.isArray(authored))
557
+ return capturedDefinitions;
558
+ return capturedDefinitions[0];
380
559
  };
381
560
  const flattenHandlers = (handlers, stateNodes, states, prefix, config) => {
382
561
  for (const key of Object.keys(config)) {
@@ -750,10 +929,12 @@ const makeInitialSelector = (stateNodes) => {
750
929
  const selector = {};
751
930
  for (const node of stateNodes.byPath.values()) {
752
931
  if (node.parent === undefined && node.type !== "history" && node.type !== "choice") {
753
- selector[node.key] = makeSelectionNode(stateNodes, node.path, "initial");
932
+ selector[node.key] = node.type === "atomic" || node.type === "final"
933
+ ? makeSelectionMethod("state", node.path, "initial")
934
+ : Object.freeze({ initial: makeSelectionValue("initial", node.path, "initial") });
754
935
  }
755
936
  }
756
- return selector;
937
+ return Object.freeze(selector);
757
938
  };
758
939
  const getInitialSelectionBuilder = (initialBuilder, selection) => {
759
940
  const path = selection.path;
@@ -766,12 +947,32 @@ const getInitialSelectionBuilder = (initialBuilder, selection) => {
766
947
  }
767
948
  return builder;
768
949
  };
769
- const captureInitialBranch = (branch, selector, initialBuilder) => {
770
- const captured = captureDefinitionBranch(branch, selector, "<machine>", "initial");
771
- if (captured.selection.kind !== "state" && captured.selection.kind !== "initial") {
950
+ const captureInitialBranch = (definition, stateNodes, initialBuilder) => {
951
+ if (typeof definition !== "function") {
952
+ throw new Error("Machine initial definition must be a target-first callback");
953
+ }
954
+ const result = definition(decorateInitialSelectorNode(makeInitialSelector(stateNodes)));
955
+ let selection;
956
+ let resolve;
957
+ if (Topology.isTargetSelection(result)) {
958
+ selection = plainTargetSelection(result);
959
+ }
960
+ else if (hasProperty(result, InitialBuilderDescriptorTypeId)) {
961
+ const descriptor = result;
962
+ selection = descriptor.selection;
963
+ resolve = descriptor.resolve;
964
+ }
965
+ else {
966
+ throw new Error("Machine initial definition must select exactly one target");
967
+ }
968
+ if (selection.kind !== "state" && selection.kind !== "initial") {
772
969
  throw new Error("Machine initial target must select a top-level state or its declared initial entry");
773
970
  }
774
- return { ...captured, builder: getInitialSelectionBuilder(initialBuilder, captured.selection) };
971
+ const captured = {
972
+ selection,
973
+ builder: getInitialSelectionBuilder(initialBuilder, selection)
974
+ };
975
+ return resolve === undefined ? Object.freeze(captured) : Object.freeze({ ...captured, resolve });
775
976
  };
776
977
  const validateInitialSelection = (result, selection) => {
777
978
  if (typeof result !== "object" || result === null || !hasProperty(result, "path") || result.path !== selection.path) {
@@ -782,17 +983,13 @@ const validateInitialSelection = (result, selection) => {
782
983
  }
783
984
  };
784
985
  const compileInitial = (definition, states, stateNodes) => {
785
- if (typeof definition !== "object" || definition === null) {
786
- throw new Error("Machine initial definition must be an object");
787
- }
788
- const selector = makeInitialSelector(stateNodes);
789
986
  const initialBuilder = makeSnapshotBuilder(states, { mode: "initial", prefix: "" });
790
- const branch = captureInitialBranch(definition, selector, initialBuilder);
987
+ const branch = captureInitialBranch(definition, stateNodes, initialBuilder);
791
988
  return {
792
989
  initial: (input) => {
793
990
  const result = branch.resolve === undefined
794
- ? branch.builder()
795
- : branch.resolve({ input, target: branch.builder }, undefined);
991
+ ? constructSelectedTarget(branch.builder)
992
+ : branch.resolve({ input, target: branch.builder });
796
993
  validateInitialSelection(result, branch.selection);
797
994
  return result;
798
995
  },
@@ -828,7 +1025,7 @@ export const make = ((config) => {
828
1025
  self.events = config.events;
829
1026
  self.internalEvents = config.internalEvents ?? Protocol.makeEventProtocol("internal", []);
830
1027
  self.emittedEvents = config.emittedEvents ?? Protocol.makeEventProtocol("emitted", []);
831
- self.parentEvents = config.parentEvents ?? Protocol.makeEventProtocol("public", []);
1028
+ self.parent = config.parent;
832
1029
  self.input = config.input;
833
1030
  self.id = config.id;
834
1031
  self.stateNodes = Topology.compileStateNodes(config.states);
@@ -838,7 +1035,6 @@ export const make = ((config) => {
838
1035
  self.makeTargetBuilder = makeTargetBuilder(config.states, self.stateNodes);
839
1036
  self.handlers = Object.create(null);
840
1037
  self.handle = makeHandle(self);
841
- self.invoke = makeBoundInvoke;
842
1038
  Protocol.setProtocol(self);
843
1039
  return self;
844
1040
  });
@@ -846,6 +1042,14 @@ const flattenEventProtocolInputs = (kind, inputs) => inputs.flatMap((input) => P
846
1042
  ? Protocol.eventProtocolSchemas(input)
847
1043
  : [input]);
848
1044
  export const events = (...inputs) => Protocol.makeEventProtocol("public", flattenEventProtocolInputs("public", inputs));
1045
+ const makeParent = (mode, events) => {
1046
+ if (!Protocol.isEventProtocol(events, "public")) {
1047
+ throw new Error("Machine parent declarations require a protocol created with Machine.events");
1048
+ }
1049
+ return Object.freeze({ [ParentTypeId]: ParentTypeId, mode, events });
1050
+ };
1051
+ export const parent = (events) => makeParent("required", events);
1052
+ export const optionalParent = (events) => makeParent("optional", events);
849
1053
  export const internalEvents = (...inputs) => Protocol.makeEventProtocol("internal", flattenEventProtocolInputs("internal", inputs));
850
1054
  export const emittedEvents = (...inputs) => Protocol.makeEventProtocol("emitted", flattenEventProtocolInputs("emitted", inputs));
851
1055
  export const encodeSnapshot = Serialization.encodeSnapshot;