@typeonce/effect-machine 0.7.0 → 0.8.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 (54) hide show
  1. package/README.md +21 -0
  2. package/dist/Machine.d.ts +159 -140
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +3 -1
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/atom.d.ts +8 -2
  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/configuration.d.ts.map +1 -1
  10. package/dist/internal/machine/configuration.js +103 -20
  11. package/dist/internal/machine/configuration.js.map +1 -1
  12. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  13. package/dist/internal/machine/executionPlan.js +6 -0
  14. package/dist/internal/machine/executionPlan.js.map +1 -1
  15. package/dist/internal/machine/invocation.js +1 -1
  16. package/dist/internal/machine/invocation.js.map +1 -1
  17. package/dist/internal/machine/machine.d.ts.map +1 -1
  18. package/dist/internal/machine/machine.js +22 -16
  19. package/dist/internal/machine/machine.js.map +1 -1
  20. package/dist/internal/machine/planner.d.ts +10 -0
  21. package/dist/internal/machine/planner.d.ts.map +1 -1
  22. package/dist/internal/machine/planner.js +45 -35
  23. package/dist/internal/machine/planner.js.map +1 -1
  24. package/dist/internal/machine/serialization.d.ts.map +1 -1
  25. package/dist/internal/machine/serialization.js +53 -21
  26. package/dist/internal/machine/serialization.js.map +1 -1
  27. package/dist/internal/machine/stateDefinition.d.ts +4 -4
  28. package/dist/internal/machine/stateDefinition.d.ts.map +1 -1
  29. package/dist/internal/machine/stateDefinition.js +18 -11
  30. package/dist/internal/machine/stateDefinition.js.map +1 -1
  31. package/dist/internal/machine/topology.d.ts +5 -5
  32. package/dist/internal/machine/topology.d.ts.map +1 -1
  33. package/dist/internal/machine/topology.js +16 -13
  34. package/dist/internal/machine/topology.js.map +1 -1
  35. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  36. package/dist/internal/testing/machine/verification.js +10 -3
  37. package/dist/internal/testing/machine/verification.js.map +1 -1
  38. package/dist/unstable/reactivity/AtomMachine.d.ts +8 -2
  39. package/dist/unstable/reactivity/AtomMachine.d.ts.map +1 -1
  40. package/dist/unstable/reactivity/AtomMachine.js.map +1 -1
  41. package/docs/agent-guide.md +56 -28
  42. package/package.json +2 -2
  43. package/src/Machine.ts +371 -296
  44. package/src/internal/machine/atom.ts +10 -3
  45. package/src/internal/machine/configuration.ts +102 -23
  46. package/src/internal/machine/executionPlan.ts +6 -0
  47. package/src/internal/machine/invocation.ts +1 -1
  48. package/src/internal/machine/machine.ts +85 -57
  49. package/src/internal/machine/planner.ts +49 -38
  50. package/src/internal/machine/serialization.ts +56 -31
  51. package/src/internal/machine/stateDefinition.ts +22 -11
  52. package/src/internal/machine/topology.ts +21 -18
  53. package/src/internal/testing/machine/verification.ts +13 -3
  54. package/src/unstable/reactivity/AtomMachine.ts +9 -2
@@ -431,6 +431,13 @@ type SnapshotIdentifier<State> = SnapshotNode<State> extends infer Node ?
431
431
  Node extends { readonly path: infer Path extends string } ? Path : never
432
432
  : never
433
433
 
434
+ type ValuedSnapshotIdentifier<State> = SnapshotNode<State> extends infer Node ?
435
+ Node extends { readonly path: infer Path extends string; readonly value: infer Value } ?
436
+ [Value] extends [undefined] ? never
437
+ : Path
438
+ : never
439
+ : never
440
+
434
441
  type SnapshotValueByIdentifier<State, Path extends SnapshotIdentifier<State>> = SnapshotNode<State> extends infer Node ?
435
442
  Node extends { readonly path: Path; readonly value: infer Value } ? Value : never
436
443
  : never
@@ -443,7 +450,7 @@ type ChildState<Child extends Machine.ChildMachine.Any> = RefState<Machine.Child
443
450
 
444
451
  const selectValueByPath = <
445
452
  State extends Machine.Machine.AtomicSnapshot<string, unknown>,
446
- Path extends SnapshotIdentifier<State>
453
+ Path extends ValuedSnapshotIdentifier<State>
447
454
  >(
448
455
  snapshot: State,
449
456
  path: Path
@@ -467,7 +474,7 @@ export const select = <
467
474
  Error,
468
475
  Output,
469
476
  StartError,
470
- const Path extends SnapshotIdentifier<State>
477
+ const Path extends ValuedSnapshotIdentifier<State>
471
478
  >(
472
479
  self: MachineAtom<State, Event, Error, Output, StartError>,
473
480
  path: Path
@@ -498,7 +505,7 @@ export const selectSnapshot = <
498
505
  export const selectChild = <
499
506
  Child extends Machine.ChildMachine.Any,
500
507
  StartError,
501
- const Path extends SnapshotIdentifier<ChildState<Child>>
508
+ const Path extends ValuedSnapshotIdentifier<ChildState<Child>>
502
509
  >(
503
510
  self: ChildMachineAtom<Child, StartError>,
504
511
  path: Path
@@ -84,7 +84,7 @@ const historyFromSnapshot = (
84
84
  const active = new Set<string>()
85
85
  const values = new Map<string, unknown>()
86
86
  for (const activePath of entry.active) {
87
- if (active.has(activePath) || !Object.prototype.hasOwnProperty.call(entry.values, activePath)) {
87
+ if (active.has(activePath)) {
88
88
  throw new Error(`Machine snapshot contains invalid remembered state "${activePath}"`)
89
89
  }
90
90
  const node = getNode(machine, activePath)
@@ -96,9 +96,15 @@ const historyFromSnapshot = (
96
96
  throw new Error(`Machine snapshot contains invalid remembered value for "${activePath}"`)
97
97
  }
98
98
  active.add(activePath)
99
- values.set(activePath, decodeStateValueSync(machine, node, entry.values[activePath]))
99
+ const hasValue = Object.prototype.hasOwnProperty.call(entry.values, activePath)
100
+ if (node.schema === undefined) {
101
+ if (hasValue) throw new Error(`Machine snapshot contains a value for structural state "${activePath}"`)
102
+ } else {
103
+ if (!hasValue) throw new Error(`Machine snapshot omits remembered value for "${activePath}"`)
104
+ values.set(activePath, decodeStateValueSync(machine, node, entry.values[activePath]))
105
+ }
100
106
  }
101
- if (!active.has(historyNode.parent) || Object.keys(entry.values).length !== active.size) {
107
+ if (!active.has(historyNode.parent) || Object.keys(entry.values).length !== values.size) {
102
108
  throw new Error(`Machine snapshot contains incomplete history record "${path}"`)
103
109
  }
104
110
  history.set(path, {
@@ -138,7 +144,6 @@ const historyFromSnapshotEffect = Effect.fnUntraced(function*(
138
144
  const node = machine.stateNodes.byPath.get(activePath)
139
145
  if (
140
146
  active.has(activePath) || node === undefined || node.type === "history" || node.type === "choice" ||
141
- !Object.prototype.hasOwnProperty.call(entry.values, activePath) ||
142
147
  !(isPathInSubtree(activePath, historyNode.parent) ||
143
148
  getPathToRoot(machine, historyNode.parent).includes(activePath))
144
149
  ) {
@@ -152,15 +157,39 @@ const historyFromSnapshotEffect = Effect.fnUntraced(function*(
152
157
  )
153
158
  }
154
159
  active.add(activePath)
155
- values.set(
156
- activePath,
157
- yield* decodeBoundary(machine, getStateNodeSchema(node), entry.values[activePath], {
158
- boundary: "history",
159
- state: activePath
160
- })
161
- )
160
+ const hasValue = Object.prototype.hasOwnProperty.call(entry.values, activePath)
161
+ if (node.schema === undefined) {
162
+ if (hasValue) {
163
+ return yield* Effect.fail(
164
+ new MachineSchemaDecodeError({
165
+ machineId: machine.id,
166
+ boundary: "history",
167
+ state: activePath,
168
+ cause: Cause.die(new Error(`Machine snapshot contains a value for structural state "${activePath}"`))
169
+ })
170
+ )
171
+ }
172
+ } else {
173
+ if (!hasValue) {
174
+ return yield* Effect.fail(
175
+ new MachineSchemaDecodeError({
176
+ machineId: machine.id,
177
+ boundary: "history",
178
+ state: activePath,
179
+ cause: Cause.die(new Error(`Machine snapshot omits remembered value for "${activePath}"`))
180
+ })
181
+ )
182
+ }
183
+ values.set(
184
+ activePath,
185
+ yield* decodeBoundary(machine, getStateNodeSchema(node), entry.values[activePath], {
186
+ boundary: "history",
187
+ state: activePath
188
+ })
189
+ )
190
+ }
162
191
  }
163
- if (!active.has(historyNode.parent) || Object.keys(entry.values).length !== active.size) {
192
+ if (!active.has(historyNode.parent) || Object.keys(entry.values).length !== values.size) {
164
193
  return yield* Effect.fail(
165
194
  new MachineSchemaDecodeError({
166
195
  machineId: machine.id,
@@ -212,7 +241,7 @@ const historyToSnapshot = (
212
241
  entries[path] = {
213
242
  mode: record.mode,
214
243
  active,
215
- values: Object.fromEntries(active.map((path) => [path, record.values.get(path)]))
244
+ values: Object.fromEntries(record.values)
216
245
  }
217
246
  }
218
247
  return entries
@@ -329,7 +358,9 @@ export const getParentValues = (
329
358
  const paths = getPathToRoot(machine, path)
330
359
  for (let index = 0; index < paths.length - 1; index++) {
331
360
  const parent = paths[index]!
332
- parents[parent] = getActiveValue(configuration, parent)
361
+ if (configuration.values.has(parent)) {
362
+ parents[parent] = configuration.values.get(parent)
363
+ }
333
364
  }
334
365
  return parents
335
366
  }
@@ -340,7 +371,7 @@ export const getParentValue = (
340
371
  path: string
341
372
  ): unknown => {
342
373
  const parent = getNode(machine, path).parent
343
- return parent === undefined ? undefined : getActiveValue(configuration, parent)
374
+ return parent === undefined ? undefined : configuration.values.get(parent)
344
375
  }
345
376
 
346
377
  export const getInitialEntryPaths = (
@@ -368,7 +399,7 @@ export const snapshotFromPath = <const States extends Machine.StateSchemas>(
368
399
  const node = getNode(machine, path)
369
400
  const snapshot: Record<string, unknown> = {
370
401
  path,
371
- value: getActiveValue(configuration, path)
402
+ value: configuration.values.get(path)
372
403
  }
373
404
  if (node.type === "compound") {
374
405
  const child = node.children.find((child) => configuration.active.has(child))
@@ -438,9 +469,14 @@ export const configurationFromSnapshot = (
438
469
 
439
470
  const visit = (current: Machine.AtomicSnapshot<string, unknown>): void => {
440
471
  const node = getNode(machine, String(current.path))
441
- const value = decodeStateValueSync(machine, node, current.value)
442
472
  active.add(node.path)
443
- values.set(node.path, value)
473
+ if (node.schema === undefined) {
474
+ if (current.value !== undefined) {
475
+ throw new Error(`Machine structural snapshot "${node.path}" cannot contain a value`)
476
+ }
477
+ } else {
478
+ values.set(node.path, decodeStateValueSync(machine, node, current.value))
479
+ }
444
480
  if (node.type === "compound") {
445
481
  if (!hasProperty(current, "state") || !isSnapshot(current.state)) {
446
482
  throw new Error(`Machine expected compound snapshot "${node.path}" to include an active child state`)
@@ -520,9 +556,14 @@ export const configurationFromSnapshotEffect = Effect.fnUntraced(function*(
520
556
  current: Machine.AtomicSnapshot<string, unknown>
521
557
  ) {
522
558
  const node = getNode(machine, String(current.path))
523
- const value = yield* decodeStateValue(machine, node, current.value)
524
559
  active.add(node.path)
525
- values.set(node.path, value)
560
+ if (node.schema === undefined) {
561
+ if (current.value !== undefined) {
562
+ throw new Error(`Machine structural snapshot "${node.path}" cannot contain a value`)
563
+ }
564
+ } else {
565
+ values.set(node.path, yield* decodeStateValue(machine, node, current.value))
566
+ }
526
567
  if (node.type === "compound") {
527
568
  if (!hasProperty(current, "state") || !isSnapshot(current.state)) {
528
569
  throw new Error(`Machine expected compound snapshot "${node.path}" to include an active child state`)
@@ -645,7 +686,9 @@ export const captureHistory = (
645
686
  }
646
687
  const values = new Map<string, unknown>()
647
688
  for (const path of active) {
648
- values.set(path, getActiveValue(current, path))
689
+ if (current.values.has(path)) {
690
+ values.set(path, current.values.get(path))
691
+ }
649
692
  }
650
693
  history.set(node.path, {
651
694
  mode,
@@ -722,6 +765,15 @@ export const configurationFromTargetPathEffect = Effect.fnUntraced(function*(
722
765
  for (const currentPath of paths) {
723
766
  const currentNode = getNode(machine, currentPath)
724
767
  active.add(currentPath)
768
+ if (currentNode.schema === undefined) {
769
+ const supplied = currentPath === node.path ?
770
+ value
771
+ : providedValues !== undefined && hasOwn(providedValues, currentPath) ?
772
+ providedValues[currentPath]
773
+ : undefined
774
+ if (supplied !== undefined) throw new Error(`Machine structural target "${currentPath}" cannot contain a value`)
775
+ continue
776
+ }
725
777
  if (currentPath === node.path) {
726
778
  values.set(currentPath, yield* decodeStateValue(machine, currentNode, value))
727
779
  } else if (providedValues !== undefined && hasOwn(providedValues, currentPath)) {
@@ -783,6 +835,12 @@ export const configurationFromTargetSnapshotEffect = Effect.fnUntraced(function*
783
835
  for (const ancestor of paths.slice(0, -1)) {
784
836
  const node = getNode(machine, ancestor)
785
837
  active.add(ancestor)
838
+ if (node.schema === undefined) {
839
+ if (providedValues !== undefined && hasOwn(providedValues, ancestor)) {
840
+ throw new Error(`Machine structural target "${ancestor}" cannot contain a value`)
841
+ }
842
+ continue
843
+ }
786
844
  if (providedValues !== undefined && hasOwn(providedValues, ancestor)) {
787
845
  values.set(ancestor, yield* decodeStateValue(machine, node, providedValues[ancestor]))
788
846
  } else if (current.values.has(ancestor)) {
@@ -816,6 +874,9 @@ export const configurationFromTargetSnapshotEffect = Effect.fnUntraced(function*
816
874
  if (!isPathInSubtree(providedPath, child)) continue
817
875
  const providedNode = getNode(machine, providedPath)
818
876
  active.add(providedPath)
877
+ if (providedNode.schema === undefined) {
878
+ throw new Error(`Machine structural target "${providedPath}" cannot contain a value`)
879
+ }
819
880
  values.set(providedPath, yield* decodeStateValue(machine, providedNode, providedValue))
820
881
  }
821
882
  }
@@ -885,6 +946,15 @@ const configurationFromTargetPathSync = (
885
946
  for (const currentPath of paths) {
886
947
  const currentNode = getNode(machine, currentPath)
887
948
  active.add(currentPath)
949
+ if (currentNode.schema === undefined) {
950
+ const supplied = currentPath === node.path ?
951
+ value
952
+ : providedValues !== undefined && hasOwn(providedValues, currentPath) ?
953
+ providedValues[currentPath]
954
+ : undefined
955
+ if (supplied !== undefined) throw new Error(`Machine structural target "${currentPath}" cannot contain a value`)
956
+ continue
957
+ }
888
958
  if (currentPath === node.path) {
889
959
  values.set(currentPath, decodeStateValueSync(machine, currentNode, value))
890
960
  } else if (providedValues !== undefined && hasOwn(providedValues, currentPath)) {
@@ -932,6 +1002,12 @@ const configurationFromTargetSnapshotSync = (
932
1002
  for (const ancestor of paths.slice(0, -1)) {
933
1003
  const node = getNode(machine, ancestor)
934
1004
  active.add(ancestor)
1005
+ if (node.schema === undefined) {
1006
+ if (providedValues !== undefined && hasOwn(providedValues, ancestor)) {
1007
+ throw new Error(`Machine structural target "${ancestor}" cannot contain a value`)
1008
+ }
1009
+ continue
1010
+ }
935
1011
  if (providedValues !== undefined && hasOwn(providedValues, ancestor)) {
936
1012
  values.set(ancestor, decodeStateValueSync(machine, node, providedValues[ancestor]))
937
1013
  } else if (current.values.has(ancestor)) {
@@ -960,6 +1036,9 @@ const configurationFromTargetSnapshotSync = (
960
1036
  if (!isPathInSubtree(providedPath, child)) continue
961
1037
  const providedNode = getNode(machine, providedPath)
962
1038
  active.add(providedPath)
1039
+ if (providedNode.schema === undefined) {
1040
+ throw new Error(`Machine structural target "${providedPath}" cannot contain a value`)
1041
+ }
963
1042
  values.set(providedPath, decodeStateValueSync(machine, providedNode, providedValue))
964
1043
  }
965
1044
  }
@@ -1098,7 +1177,7 @@ export const resolveFinalOutputEffect: <
1098
1177
  ) {
1099
1178
  const node = getNode(machine, path)
1100
1179
  const output = getStateConfigByPath(machine, path)?.output?.({
1101
- state: getActiveValue(configuration, path),
1180
+ state: configuration.values.get(path),
1102
1181
  parent: getParentValue(machine, configuration, path),
1103
1182
  parents: getParentValues(machine, configuration, path),
1104
1183
  event,
@@ -1251,7 +1330,7 @@ const resolveFinalOutputSync = <const Events extends ReadonlyArray<Machine.Tagge
1251
1330
  ): unknown => {
1252
1331
  const node = getNode(machine, path)
1253
1332
  const output = getStateConfigByPath(machine, path)?.output?.({
1254
- state: getActiveValue(configuration, path),
1333
+ state: configuration.values.get(path),
1255
1334
  parent: getParentValue(machine, configuration, path),
1256
1335
  parents: getParentValues(machine, configuration, path),
1257
1336
  event,
@@ -100,6 +100,12 @@ const compileIndexedExecutionDescriptor = (
100
100
  if (node.type === "choice" || node.type === "history") {
101
101
  return undefined
102
102
  }
103
+ // Structural active states deliberately use the generic semantic
104
+ // reference until the indexed value table represents absent values as an
105
+ // explicit capability rather than an `undefined` slot.
106
+ if (node.schema === undefined) {
107
+ return undefined
108
+ }
103
109
  if (node.type === "atomic" || node.type === "final") {
104
110
  leafPaths.push(node.path)
105
111
  }
@@ -109,7 +109,7 @@ export const startAll = (
109
109
  .filter((path) => configuration.active.has(path))
110
110
  .flatMap((path) =>
111
111
  resolve(Configuration.getStateConfigByPath(machine, path), {
112
- state: Configuration.getActiveValue(configuration, path),
112
+ state: configuration.values.get(path),
113
113
  parent: Configuration.getParentValue(machine, configuration, path),
114
114
  parents: Configuration.getParentValues(machine, configuration, path),
115
115
  event
@@ -278,10 +278,14 @@ type FromMethodKind = "leaf" | "nested"
278
278
 
279
279
  const withFrom = <Method extends (value: unknown, ...args: ReadonlyArray<any>) => unknown>(
280
280
  method: Method,
281
- kind: FromMethodKind
281
+ kind: FromMethodKind,
282
+ valued: boolean
282
283
  ): Method & { readonly from: (...args: ReadonlyArray<any>) => unknown } => {
283
284
  Object.defineProperty(method, "from", {
284
285
  value: (...args: ReadonlyArray<any>) => {
286
+ if (!valued) {
287
+ return method(undefined, ...args)
288
+ }
285
289
  const omitted = args.length === 0 || (kind === "nested" && args.length === 1 && typeof args[0] === "function")
286
290
  const input = omitted ? {} : args[0]
287
291
  const rest = omitted ? args : args.slice(1)
@@ -312,7 +316,8 @@ const makeSnapshotBuilder = (
312
316
  builder[key] = withFrom(
313
317
  (value: unknown, selector?: (builder: unknown) => unknown) =>
314
318
  makeSnapshotForNode(definition, key, value, selector, options),
315
- node.states === undefined ? "leaf" : "nested"
319
+ node.states === undefined ? "leaf" : "nested",
320
+ node.schema !== undefined
316
321
  )
317
322
  }
318
323
  return builder
@@ -339,14 +344,18 @@ const makeParallelSnapshotBuilder = (
339
344
  }
340
345
  const path = options.prefix === "" ? key : `${options.prefix}.${key}`
341
346
  const node = Topology.getStateNodeDefinition(path, definition)
342
- builder[key] = withFrom((value: unknown, selector?: (builder: unknown) => unknown) => {
343
- const nextRegions: Record<string, unknown> = {}
344
- for (const regionKey of Object.keys(regions)) {
345
- nextRegions[regionKey] = regions[regionKey]
346
- }
347
- nextRegions[key] = makeSnapshotForNode(definition, key, value, selector, options)
348
- return makeParallelSnapshotBuilder(states, options, nextRegions)
349
- }, node.states === undefined ? "leaf" : "nested")
347
+ builder[key] = withFrom(
348
+ (value: unknown, selector?: (builder: unknown) => unknown) => {
349
+ const nextRegions: Record<string, unknown> = {}
350
+ for (const regionKey of Object.keys(regions)) {
351
+ nextRegions[regionKey] = regions[regionKey]
352
+ }
353
+ nextRegions[key] = makeSnapshotForNode(definition, key, value, selector, options)
354
+ return makeParallelSnapshotBuilder(states, options, nextRegions)
355
+ },
356
+ node.states === undefined ? "leaf" : "nested",
357
+ node.schema !== undefined
358
+ )
350
359
  }
351
360
  return builder
352
361
  }
@@ -525,13 +534,25 @@ const makeLocalTargetChildBuilder = (
525
534
  builder[child.key] = () => Topology.makeChoiceTarget(child.path, parent.path, values)
526
535
  continue
527
536
  }
528
- builder[child.key] = withFrom((value: unknown, selector?: (builder: unknown) => unknown) => {
529
- if (child.type === "atomic" || child.type === "final") {
530
- return makeTargetWithValues(child.path, value, values)
531
- }
532
- if (child.type === "parallel") {
533
- if (source !== child.path && !source.startsWith(`${child.path}.`)) {
534
- return makeParallelTarget(states, child, value, selector, values)
537
+ builder[child.key] = withFrom(
538
+ (value: unknown, selector?: (builder: unknown) => unknown) => {
539
+ if (child.type === "atomic" || child.type === "final") {
540
+ return makeTargetWithValues(child.path, value, values)
541
+ }
542
+ if (child.type === "parallel") {
543
+ if (source !== child.path && !source.startsWith(`${child.path}.`)) {
544
+ return makeParallelTarget(states, child, value, selector, values)
545
+ }
546
+ if (selector === undefined) {
547
+ throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`)
548
+ }
549
+ return selector(makeLocalTargetChildBuilder(
550
+ states,
551
+ stateNodes,
552
+ child.path,
553
+ child.schema === undefined ? values : extendTargetValues(values, child.path, value),
554
+ source
555
+ ))
535
556
  }
536
557
  if (selector === undefined) {
537
558
  throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`)
@@ -540,21 +561,13 @@ const makeLocalTargetChildBuilder = (
540
561
  states,
541
562
  stateNodes,
542
563
  child.path,
543
- extendTargetValues(values, child.path, value),
564
+ child.schema === undefined ? values : extendTargetValues(values, child.path, value),
544
565
  source
545
566
  ))
546
- }
547
- if (selector === undefined) {
548
- throw new Error(`Machine expected target "${child.path}" builder to provide an active child state`)
549
- }
550
- return selector(makeLocalTargetChildBuilder(
551
- states,
552
- stateNodes,
553
- child.path,
554
- extendTargetValues(values, child.path, value),
555
- source
556
- ))
557
- }, child.type === "atomic" || child.type === "final" ? "leaf" : "nested")
567
+ },
568
+ child.type === "atomic" || child.type === "final" ? "leaf" : "nested",
569
+ child.schema !== undefined
570
+ )
558
571
  }
559
572
  return builder
560
573
  }
@@ -569,12 +582,19 @@ const makeLocalTargetBuilder = (
569
582
  return {}
570
583
  }
571
584
  const builder = makeLocalTargetChildBuilder(states, stateNodes, scope, undefined, source) as Record<string, unknown>
572
- builder.with = withFrom((value: unknown, selector?: (builder: unknown) => unknown) => {
573
- if (selector === undefined) {
574
- throw new Error(`Machine expected target "${scope}" builder to provide an active child state`)
575
- }
576
- return selector(makeLocalTargetChildBuilder(states, stateNodes, scope, { [scope]: value }, source))
577
- }, "nested")
585
+ const scopeNode = getTargetBuilderNode(stateNodes, scope)
586
+ if (scopeNode.schema !== undefined) {
587
+ builder.with = withFrom(
588
+ (value: unknown, selector?: (builder: unknown) => unknown) => {
589
+ if (selector === undefined) {
590
+ throw new Error(`Machine expected target "${scope}" builder to provide an active child state`)
591
+ }
592
+ return selector(makeLocalTargetChildBuilder(states, stateNodes, scope, { [scope]: value }, source))
593
+ },
594
+ "nested",
595
+ true
596
+ )
597
+ }
578
598
  return builder
579
599
  }
580
600
 
@@ -610,12 +630,31 @@ const makeBranchTargetNodeBuilder = (
610
630
  ): unknown => {
611
631
  const node = getTargetBuilderNode(stateNodes, path)
612
632
  if (node.type === "atomic" || node.type === "final") {
613
- return withFrom((value: unknown) => makeTargetWithValues(node.path, value, values), "leaf")
633
+ return withFrom(
634
+ (value: unknown) => makeTargetWithValues(node.path, value, values),
635
+ "leaf",
636
+ node.schema !== undefined
637
+ )
614
638
  }
615
- const builder = withFrom((value: unknown, selector?: (builder: unknown) => unknown) => {
616
- if (node.type === "parallel") {
617
- if (source !== node.path && !source.startsWith(`${node.path}.`)) {
618
- return makeParallelTarget(states, node, value, selector, values)
639
+ const builder = withFrom(
640
+ (value: unknown, selector?: (builder: unknown) => unknown) => {
641
+ if (node.type === "parallel") {
642
+ if (source !== node.path && !source.startsWith(`${node.path}.`)) {
643
+ return makeParallelTarget(states, node, value, selector, values)
644
+ }
645
+ if (selector === undefined) {
646
+ throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`)
647
+ }
648
+ const nextBuilder: Record<string, unknown> = {}
649
+ addBranchTargetChildren(
650
+ nextBuilder,
651
+ states,
652
+ stateNodes,
653
+ node.path,
654
+ node.schema === undefined ? values : extendTargetValues(values, node.path, value),
655
+ source
656
+ )
657
+ return selector(nextBuilder)
619
658
  }
620
659
  if (selector === undefined) {
621
660
  throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`)
@@ -626,25 +665,14 @@ const makeBranchTargetNodeBuilder = (
626
665
  states,
627
666
  stateNodes,
628
667
  node.path,
629
- extendTargetValues(values, node.path, value),
668
+ node.schema === undefined ? values : extendTargetValues(values, node.path, value),
630
669
  source
631
670
  )
632
671
  return selector(nextBuilder)
633
- }
634
- if (selector === undefined) {
635
- throw new Error(`Machine expected target "${node.path}" builder to provide an active child state`)
636
- }
637
- const nextBuilder: Record<string, unknown> = {}
638
- addBranchTargetChildren(
639
- nextBuilder,
640
- states,
641
- stateNodes,
642
- node.path,
643
- extendTargetValues(values, node.path, value),
644
- source
645
- )
646
- return selector(nextBuilder)
647
- }, "nested") as unknown as Record<string, unknown>
672
+ },
673
+ "nested",
674
+ node.schema !== undefined
675
+ ) as unknown as Record<string, unknown>
648
676
  if (node.type !== "parallel" || source === node.path || source.startsWith(`${node.path}.`)) {
649
677
  addBranchTargetChildren(builder, states, stateNodes, node.path, values, source)
650
678
  }