@typeonce/effect-machine 0.20.0 → 0.21.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 (34) hide show
  1. package/README.md +41 -0
  2. package/dist/Machine.d.ts +62 -16
  3. package/dist/Machine.d.ts.map +1 -1
  4. package/dist/Machine.js +4 -3
  5. package/dist/Machine.js.map +1 -1
  6. package/dist/internal/machine/executionPlan.d.ts.map +1 -1
  7. package/dist/internal/machine/executionPlan.js +33 -3
  8. package/dist/internal/machine/executionPlan.js.map +1 -1
  9. package/dist/internal/machine/machine.d.ts.map +1 -1
  10. package/dist/internal/machine/machine.js +38 -9
  11. package/dist/internal/machine/machine.js.map +1 -1
  12. package/dist/internal/machine/planner.d.ts +11 -2
  13. package/dist/internal/machine/planner.d.ts.map +1 -1
  14. package/dist/internal/machine/planner.js +49 -9
  15. package/dist/internal/machine/planner.js.map +1 -1
  16. package/dist/internal/machine/topology.d.ts +9 -1
  17. package/dist/internal/machine/topology.d.ts.map +1 -1
  18. package/dist/internal/machine/topology.js +7 -0
  19. package/dist/internal/machine/topology.js.map +1 -1
  20. package/dist/internal/testing/machine/verification.d.ts.map +1 -1
  21. package/dist/internal/testing/machine/verification.js +15 -3
  22. package/dist/internal/testing/machine/verification.js.map +1 -1
  23. package/dist/testing/MachineTest.d.ts +2 -0
  24. package/dist/testing/MachineTest.d.ts.map +1 -1
  25. package/dist/testing/MachineTest.js.map +1 -1
  26. package/docs/agent-guide.md +17 -0
  27. package/package.json +1 -1
  28. package/src/Machine.ts +230 -43
  29. package/src/internal/machine/executionPlan.ts +37 -3
  30. package/src/internal/machine/machine.ts +57 -9
  31. package/src/internal/machine/planner.ts +64 -11
  32. package/src/internal/machine/topology.ts +18 -1
  33. package/src/internal/testing/machine/verification.ts +16 -3
  34. package/src/testing/MachineTest.ts +2 -0
package/README.md CHANGED
@@ -420,6 +420,47 @@ choice destinations remain calls such as `to.full.Running()`. Runtime named
420
420
  branch builders remain callable, including `select.unchanged()`, because their
421
421
  result carries the selected branch evidence.
422
422
 
423
+ ### Update an active scope value
424
+
425
+ Use `to.local.update(...)` to replace the value owned by the nearest active
426
+ compound scope without rebuilding its active child. Use
427
+ `to.branch.<path>.update(...)` for a valued compound or parallel ancestor of the
428
+ handler source:
429
+
430
+ ```ts
431
+ Increment: ;
432
+ ;((to) =>
433
+ to.branch.root.session.update(({ ancestors, target }) => target.from({ count: ancestors["root.session"].count + 1 })))
434
+ ```
435
+
436
+ The update keeps the exact active descendants, their values, history records,
437
+ completion outputs, and unrelated parallel regions. It runs no exit or entry
438
+ actions and does not restart state-owned work. Eventless stabilization still
439
+ runs, so an `always` transition can react to the new value.
440
+
441
+ `update` is callable when used directly and is also a static selection for a
442
+ named branch:
443
+
444
+ ```ts
445
+ to.branches({
446
+ changed: { target: to.local.update },
447
+ unchanged: { target: to.none }
448
+ }).resolve(({ select, event }) =>
449
+ event.changed
450
+ ? select.changed.from({ count: event.count })
451
+ : select.unchanged()
452
+ )
453
+ ```
454
+
455
+ The resolver must return `target(value)` or `target.from(input)`. It may return
456
+ `decline()` only with `{ declinable: true }`. Pass `{ reenter: true }` on event
457
+ or invocation transitions when the handler source should exit and enter again.
458
+ Reentry applies to that source, not to the ancestor whose value changed.
459
+
460
+ The selector omits `update` for schema-less scopes, atomic and final states,
461
+ inactive branches, parallel sibling regions, and choice resolvers. Updating a
462
+ parallel sibling requires an event handled by that region.
463
+
423
464
  Use `declinable: true` when a resolver may decide that its transition is not
424
465
  enabled. Only that resolver receives `decline()`, and its return type expands to
425
466
  accept the opaque declined result:
package/dist/Machine.d.ts CHANGED
@@ -2234,9 +2234,10 @@ export declare namespace Machine {
2234
2234
  *
2235
2235
  * **Details**
2236
2236
  *
2237
- * Every branch exposes its selected target without executing its resolver.
2238
- * A compound local or branch target covers its descendants;
2239
- * `undefined` identifies an explicitly targetless branch.
2237
+ * Every branch exposes its static selection without executing its resolver.
2238
+ * A compound local or branch target covers its descendants. An `update`
2239
+ * selection keeps `target` undefined and records its value owner in
2240
+ * `selection.path`; `none` identifies an explicitly targetless branch.
2240
2241
  *
2241
2242
  * @category models
2242
2243
  * @since 0.4.0
@@ -2841,6 +2842,20 @@ export declare namespace Machine {
2841
2842
  readonly [AncestorStateId in ValuedStateIdentifier<States>]: StateByIdentifier<States, AncestorStateId>;
2842
2843
  }>;
2843
2844
  }
2845
+ /**
2846
+ * Opaque instruction that replaces one active compound or parallel state's
2847
+ * value without changing its active descendants.
2848
+ *
2849
+ * @category models
2850
+ * @since 0.21.0
2851
+ */
2852
+ interface StateUpdate<States extends StateSchemas, StateId extends ValuedStateIdentifier<States>> {
2853
+ readonly [Topology.StateUpdateTypeId]: typeof Topology.StateUpdateTypeId;
2854
+ readonly path: StateId;
2855
+ readonly value: StateByIdentifier<States, StateId>;
2856
+ }
2857
+ /** @internal */
2858
+ type StateUpdateBuilder<States extends StateSchemas, StateId extends ValuedStateIdentifier<States>> = ((value: StateByIdentifier<States, StateId>) => StateUpdate<States, StateId>) & FromMethod<readonly [input: SchemaByIdentifier<States, StateId>["~type.make.in"]], StateUpdate<States, StateId>>;
2844
2859
  /**
2845
2860
  * Opaque result returned by an explicitly targetless transition.
2846
2861
  *
@@ -3045,6 +3060,20 @@ export declare namespace Machine {
3045
3060
  type SelectionNode<AllStates extends StateSchemas, Node, Path extends string, Scope extends "local" | "branch", Builder> = Node extends ChoiceStateNodeConfig ? SelectionMethod<Builder, Path, "choice"> : Node extends {
3046
3061
  readonly states: infer Children extends StateSchemas;
3047
3062
  } ? SelectionMethod<Builder, Path> & InitialSelectionMethod<Builder, Path> & SelectionTreeWithPrefix<AllStates, Children, Path, Scope, Builder> : SelectionMethod<Builder, Path>;
3063
+ /** @internal */
3064
+ type StateUpdateSelectionForNode<AllStates extends StateSchemas, Node, Path extends string> = Node extends {
3065
+ readonly states: StateSchemas;
3066
+ } ? NodeSchema<Node> extends never ? {} : {
3067
+ readonly update: SelectionValue<StateUpdateBuilder<AllStates, Extract<Path, ValuedStateIdentifier<AllStates>>>, Path, "update">;
3068
+ } : {};
3069
+ /** @internal */
3070
+ type BranchUpdateSelectionPath<AllStates extends StateSchemas, Node, Path extends string, Rest extends string> = StateUpdateSelectionForNode<AllStates, Node, Path> & (Node extends {
3071
+ readonly states: infer Children extends StateSchemas;
3072
+ } ? Rest extends `${infer Head}.${infer Tail}` ? Head extends keyof Children ? {
3073
+ readonly [Key in Head]: BranchUpdateSelectionPath<AllStates, Children[Head], JoinPath<Path, Head>, Tail>;
3074
+ } : {} : Rest extends keyof Children ? {
3075
+ readonly [Key in Rest]: StateUpdateSelectionForNode<AllStates, Children[Rest], JoinPath<Path, Rest>>;
3076
+ } : {} : {});
3048
3077
  type FullSelectionNode<AllStates extends StateSchemas, Node, Path extends StateIdentifier<AllStates>, Builder> = Node extends {
3049
3078
  readonly states: StateSchemas;
3050
3079
  } ? SelectionMethod<Builder, Path> & InitialSelectionMethod<Builder, Path> : SelectionMethod<Builder, Path>;
@@ -3052,11 +3081,13 @@ export declare namespace Machine {
3052
3081
  readonly [Key in Extract<ActiveStateKey<States>, keyof FullTargetBuilder<States>>]: FullSelectionNode<States, States[Key], Extract<Key, StateIdentifier<States>>, FullTargetBuilder<States>[Key]>;
3053
3082
  };
3054
3083
  type BranchTargetSelector<States extends StateSchemas, Source extends StateNodeIdentifier<States>, Root extends string = Source extends `${infer Head}.${string}` ? Head : Source> = Root extends ActiveStateKey<States> ? Root extends keyof BranchTargetBuilder<States, Source> ? {
3055
- readonly [Key in Root]: SelectionNode<States, States[Key], Key, "branch", BranchTargetBuilder<States, Source>[Key]>;
3084
+ readonly [Key in Root]: SelectionNode<States, States[Key], Key, "branch", BranchTargetBuilder<States, Source>[Key]> & (Source extends ChoiceIdentifier<States> ? {} : Source extends `${Key}.${infer Rest}` ? BranchUpdateSelectionPath<States, States[Key], Key, Rest> : StateUpdateSelectionForNode<States, States[Key], Key>);
3056
3085
  } : {} : {};
3057
- type LocalTargetSelector<States extends StateSchemas, Source extends StateNodeIdentifier<States>> = NearestCompoundScope<States, Source> extends infer Scope extends StateIdentifier<States> ? ChildrenOf<States, Scope> extends infer Children extends StateSchemas ? LocalTargetBuilder<States, Source> extends infer Builder ? SelectionTreeWithPrefix<States, Children, Scope, "local", Builder> & ("with" extends keyof Builder ? {
3086
+ type LocalTargetSelector<States extends StateSchemas, Source extends StateNodeIdentifier<States>> = NearestCompoundScope<States, Source> extends infer Scope ? [Scope] extends [never] ? {} : Scope extends StateIdentifier<States> ? ChildrenOf<States, Scope> extends infer Children extends StateSchemas ? LocalTargetBuilder<States, Source> extends infer Builder ? SelectionTreeWithPrefix<States, Children, Scope, "local", Builder> & ("with" extends keyof Builder ? {
3058
3087
  readonly with: SelectionValue<Builder["with"], Scope>;
3059
- } : {}) : {} : {} : {};
3088
+ } : {}) & (Source extends ChoiceIdentifier<States> ? {} : Scope extends ValuedStateIdentifier<States> ? {
3089
+ readonly update: SelectionValue<StateUpdateBuilder<States, Scope>, Scope, "update">;
3090
+ } : {}) : {} : {} : {} : {};
3060
3091
  type HistorySelectionTree<AllStates extends StateSchemas, States extends StateSchemas, Prefix extends string, Builder> = {
3061
3092
  readonly [Key in Extract<HistoryContainingKey<States>, keyof Builder>]: States[Key] extends HistoryStateNodeConfig ? SelectionValue<Builder[Key], JoinPath<Prefix, Key>, "history"> : States[Key] extends {
3062
3093
  readonly states: infer Children extends StateSchemas;
@@ -3074,9 +3105,9 @@ export declare namespace Machine {
3074
3105
  interface TargetSelector<States extends StateSchemas, Source extends StateNodeIdentifier<States>> {
3075
3106
  /** Handles the trigger without selecting a destination. */
3076
3107
  readonly none: SelectionValue<TargetBuilder<States, Source>["none"], never, "none">;
3077
- /** Selects a destination inside the nearest active compound scope. */
3108
+ /** Selects a destination or updates the nearest active compound scope. */
3078
3109
  readonly local: LocalTargetSelector<States, Source>;
3079
- /** Selects a destination elsewhere under the currently active root. */
3110
+ /** Selects a destination or updates a valued active ancestor under the current root. */
3080
3111
  readonly branch: BranchTargetSelector<States, Source>;
3081
3112
  /** Selects a complete destination under any top-level state. */
3082
3113
  readonly full: FullTargetSelector<States>;
@@ -3365,14 +3396,14 @@ export declare namespace Machine {
3365
3396
  * **Details**
3366
3397
  *
3367
3398
  * Handlers return snapshots for complete state replacement, target builder
3368
- * results for path-safe partial transitions, or `target.none()` for an
3369
- * explicitly targetless transition. Raw decoded state values and `void` are
3370
- * not accepted at transition boundaries.
3399
+ * results for path-safe partial transitions, state-value updates, or
3400
+ * `target.none()` for an explicitly targetless transition. Raw decoded state
3401
+ * values and `void` are not accepted at transition boundaries.
3371
3402
  *
3372
3403
  * @category utility types
3373
3404
  * @since 0.4.0
3374
3405
  */
3375
- type HandlerResult<States extends StateSchemas, E, R> = Snapshot<States> | Target<States, StateIdentifier<States>> | HistoryTarget<States, HistoryIdentifier<States>> | ChoiceTarget<States, ChoiceIdentifier<States>> | StateConstruction<Snapshot<States> | Target<States, StateIdentifier<States>> | HistoryTarget<States, HistoryIdentifier<States>> | ChoiceTarget<States, ChoiceIdentifier<States>>> | NoTarget;
3406
+ type HandlerResult<States extends StateSchemas, E, R> = Snapshot<States> | Target<States, StateIdentifier<States>> | HistoryTarget<States, HistoryIdentifier<States>> | ChoiceTarget<States, ChoiceIdentifier<States>> | StateUpdate<States, ValuedStateIdentifier<States>> | StateConstruction<Snapshot<States> | Target<States, StateIdentifier<States>> | HistoryTarget<States, HistoryIdentifier<States>> | ChoiceTarget<States, ChoiceIdentifier<States>> | StateUpdate<States, ValuedStateIdentifier<States>>> | NoTarget;
3376
3407
  /** A choice resolver must always select a typed target synchronously. */
3377
3408
  type ChoiceResult<States extends StateSchemas, E, R> = Snapshot<States> | Target<States, StateIdentifier<States>> | HistoryTarget<States, HistoryIdentifier<States>> | ChoiceTarget<States, ChoiceIdentifier<States>> | StateConstruction<Snapshot<States> | Target<States, StateIdentifier<States>> | HistoryTarget<States, HistoryIdentifier<States>> | ChoiceTarget<States, ChoiceIdentifier<States>>>;
3378
3409
  /**
@@ -3633,6 +3664,10 @@ export declare namespace Machine {
3633
3664
  }
3634
3665
  type TransitionResolver<Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, Context, Selection> = (context: TransitionResolveContext<Context, Selection>, enqueue: Enqueue<EventOf<Events>, EmitOf<Emits>>) => SelectionKind<Selection> extends "none" ? undefined : SelectedTargetResult<Selection> | undefined;
3635
3666
  type DeclinableTransitionResolver<Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, Context, Selection> = (context: TransitionResolveContext<Context, Selection> & DeclineCapability, enqueue: Enqueue<EventOf<Events>, EmitOf<Emits>>) => (SelectionKind<Selection> extends "none" ? undefined : SelectedTargetResult<Selection> | undefined) | Declined;
3667
+ /** @internal */
3668
+ type StateUpdateResolver<Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, Context, Selection> = (context: TransitionResolveContext<Context, Selection>, enqueue: Enqueue<EventOf<Events>, EmitOf<Emits>>) => SelectedTargetResult<Selection>;
3669
+ /** @internal */
3670
+ type DeclinableStateUpdateResolver<Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, Context, Selection> = (context: TransitionResolveContext<Context, Selection> & DeclineCapability, enqueue: Enqueue<EventOf<Events>, EmitOf<Emits>>) => SelectedTargetResult<Selection> | Declined;
3636
3671
  /** One named destination declared by a branching transition. */
3637
3672
  interface TransitionBranchInput<Selection extends TargetSelection<any, any, any> = TargetSelection<any, any, any>> {
3638
3673
  /** Exact topology destination available to the branching resolver. */
@@ -3725,6 +3760,16 @@ export declare namespace Machine {
3725
3760
  /** Reenters the source using the selected target's default construction. */
3726
3761
  readonly reenter: () => BuiltTransition<States, Events, Emits, StateId, Context, Reenter, SelectionKind<Selection> extends "none" ? undefined : SelectedTargetResult<Selection> | undefined, "required">;
3727
3762
  } : {} : {});
3763
+ /** @internal */
3764
+ interface StateUpdateTransitionRequired<States extends StateSchemas, Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, StateId extends StateNodeIdentifier<States>, Context, Reenter extends boolean, Selection extends TargetSelection<any, any, "update">> {
3765
+ (resolve: StateUpdateResolver<Events, Emits, Context, Selection>, options?: TransitionRequiredOptions<Reenter>): BuiltTransition<States, Events, Emits, StateId, Context, Reenter, SelectedTargetResult<Selection>, "required">;
3766
+ }
3767
+ /** @internal */
3768
+ interface StateUpdateTransitionDeclinable<States extends StateSchemas, Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, StateId extends StateNodeIdentifier<States>, Context, Reenter extends boolean, Selection extends TargetSelection<any, any, "update">> {
3769
+ (resolve: DeclinableStateUpdateResolver<Events, Emits, Context, Selection>, options: TransitionDeclinableOptions<Reenter>): BuiltTransition<States, Events, Emits, StateId, Context, Reenter, SelectedTargetResult<Selection> | Declined, "declinable">;
3770
+ }
3771
+ /** @internal */
3772
+ type StateUpdateTransition<States extends StateSchemas, Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, StateId extends StateNodeIdentifier<States>, Context, Reenter extends boolean, Acceptance extends TransitionAcceptance, Selection extends TargetSelection<any, any, "update">> = Selection & StateUpdateTransitionRequired<States, Events, Emits, StateId, Context, Reenter, Selection> & ("declinable" extends Acceptance ? StateUpdateTransitionDeclinable<States, Events, Emits, StateId, Context, Reenter, Selection> : {});
3728
3773
  /** @internal Type evidence retained by a machine initial-entry declaration. */
3729
3774
  interface InitialBuilderEvidence<out Selection> {
3730
3775
  readonly [InitialBuilderTypeId]: Types.Covariant<Selection>;
@@ -3748,7 +3793,7 @@ export declare namespace Machine {
3748
3793
  type InitialSelector<States extends StateSchemas, Input = void> = InitialSelectorNode<Input, InitialTargetSelector<States>>;
3749
3794
  /** Target-first initial-entry declaration accepted by {@link make}. */
3750
3795
  type InitialBuilderInput<States extends StateSchemas, Input> = (to: InitialSelector<States, Input>) => InitialBuilderEvidence<TargetSelection<any, any, any>>;
3751
- type TransitionSelectorNode<States extends StateSchemas, Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, StateId extends StateNodeIdentifier<States>, Context, Reenter extends boolean, Acceptance extends TransitionAcceptance, Node> = Node extends (...args: infer Args) => infer Selection ? Selection extends TargetSelection<any, any, any> ? ((...args: Args) => TransitionTarget<States, Events, Emits, StateId, Context, Reenter, Acceptance, Selection>) & {
3796
+ type TransitionSelectorNode<States extends StateSchemas, Events extends ReadonlyArray<TaggedSchema>, Emits extends ReadonlyArray<TaggedSchema>, StateId extends StateNodeIdentifier<States>, Context, Reenter extends boolean, Acceptance extends TransitionAcceptance, Node> = Node extends TargetSelection<any, any, "update"> ? StateUpdateTransition<States, Events, Emits, StateId, Context, Reenter, Acceptance, Node> : Node extends (...args: infer Args) => infer Selection ? Selection extends TargetSelection<any, any, any> ? ((...args: Args) => TransitionTarget<States, Events, Emits, StateId, Context, Reenter, Acceptance, Selection>) & {
3752
3797
  readonly [Key in keyof Node]: TransitionSelectorNode<States, Events, Emits, StateId, Context, Reenter, Acceptance, Node[Key]>;
3753
3798
  } : never : Node extends TargetSelection<any, any, any> ? TransitionTarget<States, Events, Emits, StateId, Context, Reenter, Acceptance, Node> : {
3754
3799
  readonly [Key in keyof Node]: TransitionSelectorNode<States, Events, Emits, StateId, Context, Reenter, Acceptance, Node[Key]>;
@@ -4908,9 +4953,10 @@ export declare const initialDefinition: <M extends Machine.Any>(machine: M) => M
4908
4953
  *
4909
4954
  * Event handlers retain their handler-key order within each source state and
4910
4955
  * are followed by eventless and completion handlers. This function does not
4911
- * execute resolvers. Every direct, named, and targetless branch exposes the
4912
- * destination selected by its required static `target` declaration, while
4913
- * `acceptance` reports whether the resolver may decline the transition.
4956
+ * execute resolvers. Every branch exposes its static selection. State updates
4957
+ * retain the updated owner in `selection.path` while leaving `target`
4958
+ * undefined because they do not change topology. `acceptance` reports whether
4959
+ * the resolver may decline the transition.
4914
4960
  *
4915
4961
  * @category getters
4916
4962
  * @since 0.4.0