Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] createInvoke(…) helper function #5070

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
23 changes: 12 additions & 11 deletions packages/core/src/actions/spawnChild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,17 +172,18 @@ type SpawnArguments<
TExpressionEvent extends EventObject,
TEvent extends EventObject,
TActor extends ProvidedActor
> = IsLiteralString<TActor['src']> extends true
? DistributeActors<TContext, TExpressionEvent, TEvent, TActor>
: [
src: string | AnyActorLogic,
options?: {
id?: ResolvableActorId<TContext, TExpressionEvent, TEvent, string>;
systemId?: string;
input?: unknown;
syncSnapshot?: boolean;
}
];
> =
IsLiteralString<TActor['src']> extends true
? DistributeActors<TContext, TExpressionEvent, TEvent, TActor>
: [
src: string | AnyActorLogic,
options?: {
id?: ResolvableActorId<TContext, TExpressionEvent, TEvent, string>;
systemId?: string;
input?: unknown;
syncSnapshot?: boolean;
}
];

export function spawnChild<
TContext extends MachineContext,
Expand Down
6 changes: 1 addition & 5 deletions packages/core/src/actors/transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
160 changes: 160 additions & 0 deletions packages/core/src/invoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import {
ActorLogic,

Check failure on line 2 in packages/core/src/invoke.ts

View workflow job for this annotation

GitHub Actions / build

'ActorLogic' is defined but never used. Allowed unused vars must match /^_/u
AnyActorLogic,
DoneActorEvent,
DoNotInfer,

Check failure on line 5 in packages/core/src/invoke.ts

View workflow job for this annotation

GitHub Actions / build

'DoNotInfer' is defined but never used. Allowed unused vars must match /^_/u
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<TContext, TEvent, NonReducibleUnknown, TEvent>
| NonReducibleUnknown;
onDone?:
| string
| SingleOrArray<
TransitionConfigOrTarget<
TContext,
DoneActorEvent<any>, // 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<TContext, TEvent, InputFrom<TSrc>, TEvent> | InputFrom<TSrc>;
onDone?:
| string
| SingleOrArray<
TransitionConfigOrTarget<
TContext,
DoneActorEvent<OutputFrom<TSrc>>,
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<TSrc>,
TEvent,
TActor,
TAction,
TGuard,
TDelay,
TEmitted,
TMeta
>
>;
}): InvokeObject<
TContext,
TEvent,
TActor,
TAction,
TGuard,
TDelay,
TEmitted,
TMeta
> {
return null!;
}
4 changes: 2 additions & 2 deletions packages/core/src/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ export function createSystem<T extends ActorSystemInfo>(
...event,
rootId: rootActor.sessionId
};
inspectionObservers.forEach(
(observer) => observer.next?.(resolvedInspectionEvent)
inspectionObservers.forEach((observer) =>
observer.next?.(resolvedInspectionEvent)
);
};

Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> = { [K in keyof T]: T[K] };

Expand Down Expand Up @@ -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,
Expand Down
25 changes: 25 additions & 0 deletions packages/core/test/invoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' };

Expand Down Expand Up @@ -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;
}
}
})
});
2 changes: 1 addition & 1 deletion packages/xstate-graph/src/TestModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ export function createTestModel<TMachine extends AnyStateMachine>(
},
events: (state) => {
const events =
typeof getEvents === 'function' ? getEvents(state) : getEvents ?? [];
typeof getEvents === 'function' ? getEvents(state) : (getEvents ?? []);

return __unsafe_getAllOwnEventDescriptors(state).flatMap(
(eventType: string) => {
Expand Down
Loading