diff --git a/README.md b/README.md index 59b9bad..3e65943 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ const CounterDefinition = Machine.make({ id: "Counter", states: States.states, events: CounterEvent, - initial: (to) => to.Idle().resolve(({ target }) => target.from()) + initial: (to) => to.Idle() }) const Counter = CounterDefinition.handle({ @@ -81,7 +81,7 @@ const Counter = CounterDefinition.handle({ Running: { on: { Increment: (to) => to.full.Running().resolve(({ state, target }) => target.from({ count: state.count + 1 })), - Stop: (to) => to.full.Idle().resolve(({ target }) => target.from()) + Stop: (to) => to.full.Idle() } } }) @@ -235,7 +235,7 @@ const definition = Machine.make({ events: CommandEvent, internalEvents: InternalEvent, emittedEvents: Emissions, - initial: (to) => to.Idle().resolve(({ target }) => target.from()) + initial: (to) => to.Idle() }) ``` @@ -347,7 +347,7 @@ const child = Machine.make({ states: ChildStates.states, events: ChildEvents, parent: Machine.parent(ParentEvents), - initial: (to) => to.Working().resolve(({ target }) => target.from()) + initial: (to) => to.Working() }).handle({ Working: { on: { @@ -398,9 +398,13 @@ paths. `parent` always means the owning machine target. | `target.history` | Restoring a declared history node | The remembered configuration or its typed default | Every required transition handler selects a target from its inline `to` -builder. A bare selection uses the target schema's default construction; call -`.resolve(...)` when construction depends on handler context. An absent handler -ignores the trigger; `to.none` handles +builder. Return a bare selection such as `to.full.Idle()` when the selected +builder supports zero-argument construction; the machine applies the same +default construction as `target.from()`. This includes empty schemas and +schemas whose constructor fields are all optional or defaulted. TypeScript +rejects the bare form when state data or nested configuration is required. +Call `.resolve(...)` when construction depends on handler context or the +transition needs to enqueue commands. An absent handler ignores the trigger; `to.none` handles it and retains queued commands, raised events, and emitted events without selecting a destination. Concrete destinations stay narrowed inside their resolver, and `to.branches({...})` gives the resolver only the declared named diff --git a/docs/agent-guide.md b/docs/agent-guide.md index 8bf50bf..fc34f64 100644 --- a/docs/agent-guide.md +++ b/docs/agent-guide.md @@ -372,7 +372,7 @@ const States = Machine.states({ const machine = Machine.make({ states: States.states, events: Machine.events(), - initial: (to) => to.Done().resolve(({ target }) => target.from()) + initial: (to) => to.Done() }).handle({ Done: { output: () => "done" @@ -483,11 +483,11 @@ checkout: { Target it without a value: ```ts -Resume: (to) => to.history.checkout.exact.resolve(({ target }) => target()) +Resume: (to) => to.history.checkout.exact ``` -Each declared history leaf is a topology value; the resolver's selected -history builder remains callable to construct restoration evidence. +Each declared history leaf is a topology value and can be returned directly +when no resolver work is needed. Deep history restores the complete remembered subtree and its decoded values. Shallow history restores only parent and direct-child values. If the remembered @@ -569,8 +569,11 @@ Refresh: (to) => to.full.Ready().resolve(({ state, target }) => target.from({ value: state.value }), { reenter: true }) ``` -When no resolver is needed, use the selected target directly and append -`.reenter()` only when restart semantics are intentional: +When no resolver is needed and the selected builder supports zero-argument +construction, return the selected target directly. This applies the same +default construction as `target.from()`; the compiler rejects the shorthand +when state data or nested configuration is required. Append `.reenter()` only +when restart semantics are intentional: ```ts Finish: (to) => to.full.Done() @@ -949,7 +952,7 @@ const definition = Machine.make({ states: States.states, events: Events, internalEvents: InternalEvents, - initial: (to) => to.Idle().resolve(({ target }) => target.from()) + initial: (to) => to.Idle() }) ``` @@ -1117,7 +1120,7 @@ machine.handle({ Waiting: { invoke: (from) => from.timer("clear-status", "3 seconds") - .onDone((to) => to.full.Clear().resolve(({ target }) => target())) + .onDone((to) => to.full.Clear()) } }) ``` @@ -1485,7 +1488,7 @@ reference model when correctness of the expected behavior matters. Select the initial root separately from constructing its value: ```ts -initial: (to) => to.Idle().resolve(({ target }) => target.from()) +initial: (to) => to.Idle() ``` ### Invoked child expects events not accepted by the parent diff --git a/examples/platformer/src/machine.ts b/examples/platformer/src/machine.ts index 25f53c2..d9b2147 100644 --- a/examples/platformer/src/machine.ts +++ b/examples/platformer/src/machine.ts @@ -331,16 +331,12 @@ export const CharacterMachine = Machine.make({ states: { AirJumpGroundLock: { invoke: (from) => - from.timer("ground-air-jump-unlock", "120 millis").onDone((to) => - to.local.AirJumpReady().resolve(({ target }) => target.from()) - ), + from.timer("ground-air-jump-unlock", "120 millis").onDone((to) => to.local.AirJumpReady()), on: {} }, AirJumpWallLock: { invoke: (from) => - from.timer("wall-air-jump-unlock", "240 millis").onDone((to) => - to.local.AirJumpReady().resolve(({ target }) => target.from()) - ), + from.timer("wall-air-jump-unlock", "240 millis").onDone((to) => to.local.AirJumpReady()), on: {} }, AirJumpReady: { @@ -361,7 +357,7 @@ export const CharacterMachine = Machine.make({ }, Paused: { on: { - Resume: (to) => to.history.Character.locomotion.Playing.resume.resolve(({ target }) => target()) + Resume: (to) => to.history.Character.locomotion.Playing.resume } } } diff --git a/examples/playground/src/examples/microwave/machine.ts b/examples/playground/src/examples/microwave/machine.ts index 3b43150..95edca5 100644 --- a/examples/playground/src/examples/microwave/machine.ts +++ b/examples/playground/src/examples/microwave/machine.ts @@ -40,7 +40,7 @@ export const MicrowaveMachine = Machine.make({ states: { Closed: { on: { - DoorOpened: (to) => to.branch.Oven.Open().resolve(({ target }) => target.from()) + DoorOpened: (to) => to.branch.Oven.Open() }, states: { Idle: { @@ -56,7 +56,7 @@ export const MicrowaveMachine = Machine.make({ ) ), on: { - PowerPressed: (to) => to.local.Idle().resolve(({ target }) => target.from()) + PowerPressed: (to) => to.local.Idle() } } } diff --git a/examples/playground/src/examples/traffic-light/machine.ts b/examples/playground/src/examples/traffic-light/machine.ts index c0bccc3..ed66f8b 100644 --- a/examples/playground/src/examples/traffic-light/machine.ts +++ b/examples/playground/src/examples/traffic-light/machine.ts @@ -25,42 +25,30 @@ export const TrafficLightMachine = Machine.make({ id: "TrafficLight", states: TrafficLightStates.states, events: TrafficLightEvents, - initial: (to) => to.Red().resolve(({ target }) => target.from()) + initial: (to) => to.Red() }).handle({ Red: { - invoke: (from) => - from.timer("red-timer", trafficLightDurations.Red).onDone((to) => - to.full.RedYellow().resolve(({ target }) => target.from()) - ), + invoke: (from) => from.timer("red-timer", trafficLightDurations.Red).onDone((to) => to.full.RedYellow()), on: { Reset: (to) => to.full.Red().resolve(({ target }) => target.from(), { reenter: true }) } }, RedYellow: { - invoke: (from) => - from.timer("red-yellow-timer", trafficLightDurations.RedYellow).onDone((to) => - to.full.Green().resolve(({ target }) => target.from()) - ), + invoke: (from) => from.timer("red-yellow-timer", trafficLightDurations.RedYellow).onDone((to) => to.full.Green()), on: { - Reset: (to) => to.full.Red().resolve(({ target }) => target.from()) + Reset: (to) => to.full.Red() } }, Green: { - invoke: (from) => - from.timer("green-timer", trafficLightDurations.Green).onDone((to) => - to.full.Yellow().resolve(({ target }) => target.from()) - ), + invoke: (from) => from.timer("green-timer", trafficLightDurations.Green).onDone((to) => to.full.Yellow()), on: { - Reset: (to) => to.full.Red().resolve(({ target }) => target.from()) + Reset: (to) => to.full.Red() } }, Yellow: { - invoke: (from) => - from.timer("yellow-timer", trafficLightDurations.Yellow).onDone((to) => - to.full.Red().resolve(({ target }) => target.from()) - ), + invoke: (from) => from.timer("yellow-timer", trafficLightDurations.Yellow).onDone((to) => to.full.Red()), on: { - Reset: (to) => to.full.Red().resolve(({ target }) => target.from()) + Reset: (to) => to.full.Red() } } }) diff --git a/examples/playground/src/examples/turnstile/machine.ts b/examples/playground/src/examples/turnstile/machine.ts index c37e483..102979d 100644 --- a/examples/playground/src/examples/turnstile/machine.ts +++ b/examples/playground/src/examples/turnstile/machine.ts @@ -17,16 +17,16 @@ export const TurnstileMachine = Machine.make({ id: "Turnstile", states: TurnstileStates.states, events: TurnstileEvents, - initial: (to) => to.Locked().resolve(({ target }) => target.from()) + initial: (to) => to.Locked() }).handle({ Locked: { on: { - CoinInserted: (to) => to.full.Unlocked().resolve(({ target }) => target.from()) + CoinInserted: (to) => to.full.Unlocked() } }, Unlocked: { on: { - GatePushed: (to) => to.full.Locked().resolve(({ target }) => target.from()) + GatePushed: (to) => to.full.Locked() } } }) diff --git a/examples/pokemon/src/machine.ts b/examples/pokemon/src/machine.ts index bb46988..9d001de 100644 --- a/examples/pokemon/src/machine.ts +++ b/examples/pokemon/src/machine.ts @@ -20,7 +20,7 @@ export const ReplaceChild = Machine.child("replace", ReplaceMachine) export const machine = Machine.make({ states: States.states, events: TeamEvents, - initial: (to) => to.Loading().resolve(({ target }) => target.from()) + initial: (to) => to.Loading() }).handle({ Loading: { invoke: (from) => @@ -29,14 +29,14 @@ export const machine = Machine.make({ const service = yield* PokemonService return yield* service.getRandomTeam() })).onDone((to) => to.full.ActiveTeam().resolve(({ output, target }) => target.from({ team: output }))) - .onFailure((to) => to.full.Failed().resolve(({ target }) => target.from())) + .onFailure((to) => to.full.Failed()) }, ActiveTeam: { invoke: ( from ) => [ - from.child(SelectionChild).onFailure((to) => to.full.Failed().resolve(({ target }) => target.from())), - from.child(ReplaceChild).onFailure((to) => to.full.Failed().resolve(({ target }) => target.from())) + from.child(SelectionChild).onFailure((to) => to.full.Failed()), + from.child(ReplaceChild).onFailure((to) => to.full.Failed()) ], on: { ReplaceInTeam: (to) => diff --git a/examples/pokemon/src/machines/replace.ts b/examples/pokemon/src/machines/replace.ts index 43f1313..4ed2c93 100644 --- a/examples/pokemon/src/machines/replace.ts +++ b/examples/pokemon/src/machines/replace.ts @@ -22,7 +22,7 @@ export const ReplaceMachine = Machine.make({ events: ReplaceEvents, internalEvents: ReplaceInternalEvents, parent: Machine.parent(TeamEvents), - initial: (to) => to.Idle().resolve(({ target }) => target.from()) + initial: (to) => to.Idle() }).handle({ Idle: { on: { @@ -45,7 +45,7 @@ export const ReplaceMachine = Machine.make({ to.none.resolve(({ output }, enqueue) => { enqueue.raise(output) }) - ).onFailure((to) => to.full.Idle().resolve(({ target }) => target.from())), + ).onFailure((to) => to.full.Idle()), on: { Replaced: (to) => to.full.Idle().resolve(({ event, parent, state, target }, enqueue) => { diff --git a/examples/pokemon/src/machines/selection.ts b/examples/pokemon/src/machines/selection.ts index fc7c188..067b860 100644 --- a/examples/pokemon/src/machines/selection.ts +++ b/examples/pokemon/src/machines/selection.ts @@ -108,7 +108,7 @@ export const SelectionMachine = Machine.make({ to.none.resolve(({ output }, enqueue) => { enqueue.raise(output) }) - ).onFailure((to) => to.local.NoPokemon().resolve(({ target }) => target.from())), + ).onFailure((to) => to.local.NoPokemon()), on: { SearchResult: (to) => to.branches({