diff --git a/README.md b/README.md index a7f00e8ac0..8a5ab89b2a 100644 --- a/README.md +++ b/README.md @@ -181,16 +181,16 @@ Read [📽 the slides](http://slides.com/davidkhourshid/finite-state-machines) ( ## Packages -| Package | Description | -| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | -| 🤖 `xstate` | Core finite state machine and statecharts library + interpreter | -| [📉 `@xstate/graph`](https://github.com/statelyai/xstate/tree/main/packages/xstate-graph) | Graph traversal and model-based testing utilities using XState | -| [⚛️ `@xstate/react`](https://github.com/statelyai/xstate/tree/main/packages/xstate-react) | React hooks and utilities for using XState in React applications | -| [💚 `@xstate/vue`](https://github.com/statelyai/xstate/tree/main/packages/xstate-vue) | Vue composition functions and utilities for using XState in Vue applications | -| [🎷 `@xstate/svelte`](https://github.com/statelyai/xstate/tree/main/packages/xstate-svelte) | Svelte utilities for using XState in Svelte applications | -| [🥏 `@xstate/solid`](https://github.com/statelyai/xstate/tree/main/packages/xstate-solid) | Solid hooks and utilities for using XState in Solid applications | -| [🔍 `@statelyai/inspect`](https://github.com/statelyai/inspect) | Inspection utilities for XState | -| [🏪 `@xstate/store`](https://github.com/statelyai/xstate/tree/main/packages/xstate-store) | Small library for simple state management | +| Package | Description | +| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| 🤖 `xstate` | Core finite state machine and statecharts library + interpreter | +| [📉 `@xstate/graph`](https://github.com/statelyai/xstate/tree/main/packages/xstate-graph) | Graph traversal and model-based testing utilities using XState | +| [⚛️ `@xstate/react`](https://github.com/statelyai/xstate/tree/main/packages/xstate-react) | React hooks and utilities for using XState in React applications | +| [💚 `@xstate/vue`](https://github.com/statelyai/xstate/tree/main/packages/xstate-vue) | Vue composition functions and utilities for using XState in Vue applications | +| [🎷 `@xstate/svelte`](https://github.com/statelyai/xstate/tree/main/packages/xstate-svelte) | Svelte utilities for using XState in Svelte applications | +| [🥏 `@xstate/solid`](https://github.com/statelyai/xstate/tree/main/packages/xstate-solid) | Solid hooks and utilities for using XState in Solid applications | +| [🔍 `@statelyai/inspect`](https://github.com/statelyai/inspect) | Inspection utilities for XState | +| [🏪 `@xstate/store`](https://github.com/statelyai/xstate/tree/main/packages/xstate-store) | Small library for simple state management | ## Finite State Machines diff --git a/packages/core/src/actions/spawnChild.ts b/packages/core/src/actions/spawnChild.ts index d2b7eaf78e..bbf285830c 100644 --- a/packages/core/src/actions/spawnChild.ts +++ b/packages/core/src/actions/spawnChild.ts @@ -172,17 +172,18 @@ type SpawnArguments< TExpressionEvent extends EventObject, TEvent extends EventObject, TActor extends ProvidedActor -> = IsLiteralString extends true - ? DistributeActors - : [ - src: string | AnyActorLogic, - options?: { - id?: ResolvableActorId; - systemId?: string; - input?: unknown; - syncSnapshot?: boolean; - } - ]; +> = + IsLiteralString extends true + ? DistributeActors + : [ + src: string | AnyActorLogic, + options?: { + id?: ResolvableActorId; + systemId?: string; + input?: unknown; + syncSnapshot?: boolean; + } + ]; export function spawnChild< TContext extends MachineContext, diff --git a/packages/core/src/actors/transition.ts b/packages/core/src/actors/transition.ts index 0706aef460..5b29a14361 100644 --- a/packages/core/src/actors/transition.ts +++ b/packages/core/src/actors/transition.ts @@ -196,11 +196,7 @@ export function fromTransition< transition: (snapshot, event, actorScope) => { return { ...snapshot, - context: transition( - snapshot.context, - event, - actorScope as any - ) + context: transition(snapshot.context, event, actorScope as any) }; }, getInitialSnapshot: (_, input) => { diff --git a/packages/core/src/invoke.ts b/packages/core/src/invoke.ts new file mode 100644 index 0000000000..301d9ead7f --- /dev/null +++ b/packages/core/src/invoke.ts @@ -0,0 +1,160 @@ +import { + ActorLogic, + AnyActorLogic, + DoneActorEvent, + DoNotInfer, + ErrorActorEvent, + EventObject, + InputFrom, + MachineContext, + Mapper, + MetaObject, + NonReducibleUnknown, + OutputFrom, + ParameterizedObject, + ProvidedActor, + SingleOrArray, + SnapshotEvent, + SnapshotFrom, + TransitionConfigOrTarget +} from './types'; + +export type InvokeObject< + TContext extends MachineContext, + TEvent extends EventObject, + TActor extends ProvidedActor, + TAction extends ParameterizedObject, + TGuard extends ParameterizedObject, + TDelay extends string, + TEmitted extends EventObject, + TMeta extends MetaObject +> = { + (context: TContext): void; + id?: string; + systemId?: string; + src: AnyActorLogic; + input?: + | Mapper + | NonReducibleUnknown; + onDone?: + | string + | SingleOrArray< + TransitionConfigOrTarget< + TContext, + DoneActorEvent, // TODO: consider replacing with `unknown` + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; + onError?: + | string + | SingleOrArray< + TransitionConfigOrTarget< + TContext, + ErrorActorEvent, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; + + onSnapshot?: + | string + | SingleOrArray< + TransitionConfigOrTarget< + TContext, + SnapshotEvent, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; +}; + +export function createInvoke< + TContext extends MachineContext, + TEvent extends EventObject, + TActor extends ProvidedActor, + TAction extends ParameterizedObject, + TGuard extends ParameterizedObject, + TDelay extends string, + TEmitted extends EventObject, + TMeta extends MetaObject, + // Logic-specific types + TSrc extends AnyActorLogic +>(_config: { + id?: string; + systemId?: string; + src: TSrc; + input?: Mapper, TEvent> | InputFrom; + onDone?: + | string + | SingleOrArray< + TransitionConfigOrTarget< + TContext, + DoneActorEvent>, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; + onError?: + | string + | SingleOrArray< + TransitionConfigOrTarget< + TContext, + ErrorActorEvent, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; + + onSnapshot?: + | string + | SingleOrArray< + TransitionConfigOrTarget< + TContext, + SnapshotFrom, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; +}): InvokeObject< + TContext, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta +> { + return null!; +} diff --git a/packages/core/src/system.ts b/packages/core/src/system.ts index cb0402c768..1ff355cc05 100644 --- a/packages/core/src/system.ts +++ b/packages/core/src/system.ts @@ -161,8 +161,8 @@ export function createSystem( ...event, rootId: rootActor.sessionId }; - inspectionObservers.forEach( - (observer) => observer.next?.(resolvedInspectionEvent) + inspectionObservers.forEach((observer) => + observer.next?.(resolvedInspectionEvent) ); }; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 624d75c194..397147c32d 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -8,6 +8,7 @@ import type { Actor, ProcessingStatus } from './createActor.ts'; import { Spawner } from './spawn.ts'; import { AnyActorSystem, Clock } from './system.js'; import { InspectionEvent } from './inspection.ts'; +import { InvokeObject } from './invoke.ts'; export type Identity = { [K in keyof T]: T[K] }; @@ -916,6 +917,20 @@ export interface StateNodeConfig< TMeta > >; + + invoke2?: SingleOrArray< + InvokeObject< + TContext, + TEvent, + TActor, + TAction, + TGuard, + TDelay, + TEmitted, + TMeta + > + >; + /** The mapping of event types to their potential transition(s). */ on?: TransitionsConfig< TContext, diff --git a/packages/core/test/invoke.test.ts b/packages/core/test/invoke.test.ts index 344b67cfe2..db613a13fc 100644 --- a/packages/core/test/invoke.test.ts +++ b/packages/core/test/invoke.test.ts @@ -24,6 +24,7 @@ import { AnyEventObject } from '../src/index.ts'; import { sleep } from '@xstate-repo/jest-utils'; +import { createInvoke } from '../src/invoke.ts'; const user = { name: 'David' }; @@ -3437,3 +3438,27 @@ describe('invoke input', () => { createActor(machine).start(); }); }); + +const machine = createMachine({ + context: { number: 42 }, + invoke2: createInvoke({ + id: 'double', + input: ({ context }) => ({ + count: context.number + }), + src: fromPromise(async ({ input }: { input: { count: number } }) => { + return input.count * 2; + }), + onDone: { + actions: ({ context, event }) => { + context.number satisfies number; + // @ts-expect-error + context.number satisfies string; + + event.output satisfies number; + // @ts-expect-error + event.output satisfies string; + } + } + }) +}); diff --git a/packages/xstate-graph/src/TestModel.ts b/packages/xstate-graph/src/TestModel.ts index 7d5cba504a..ac6d70219e 100644 --- a/packages/xstate-graph/src/TestModel.ts +++ b/packages/xstate-graph/src/TestModel.ts @@ -455,7 +455,7 @@ export function createTestModel( }, events: (state) => { const events = - typeof getEvents === 'function' ? getEvents(state) : getEvents ?? []; + typeof getEvents === 'function' ? getEvents(state) : (getEvents ?? []); return __unsafe_getAllOwnEventDescriptors(state).flatMap( (eventType: string) => {