@xstate/effect 0.1.0-alpha.5 → 0.1.0-alpha.7
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.
package/README.md
CHANGED
|
@@ -7,14 +7,14 @@ Combine XState's event-driven workflows with Effect services, scopes, clocks and
|
|
|
7
7
|
- Provide services once for the actor and its child logic.
|
|
8
8
|
- Keep actor lifetime inside your Effect application.
|
|
9
9
|
|
|
10
|
-
This package is experimental. It targets XState v6 alpha and Effect 4
|
|
10
|
+
This package is experimental. It targets XState v6 alpha and Effect 4.
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
<!-- package name and peer dependencies from package.json -->
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install @xstate/effect@alpha xstate@alpha effect
|
|
17
|
+
npm install @xstate/effect@alpha xstate@alpha effect@^4
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## XState Effect: Quick start
|
|
@@ -256,7 +256,7 @@ See [atoms and React](docs/atoms-and-react.md) for complete examples with import
|
|
|
256
256
|
- Timed waits fail with Effect's `Cause.TimeoutError`.
|
|
257
257
|
- `join` preserves a task's typed failure. Unexpected machine errors have type `unknown`.
|
|
258
258
|
|
|
259
|
-
See [testing and errors](docs/testing-and-errors.md) for executable deadline, retry and failure examples. Persisted snapshots record actor state;
|
|
259
|
+
See [testing and errors](docs/testing-and-errors.md) for executable deadline, retry and failure examples. Persisted snapshots record actor state; pass one to `createEffectActor(logic, { snapshot })` to resume the actor. See [persisting and restoring](docs/actors.md#persisting-and-restoring).
|
|
260
260
|
|
|
261
261
|
## Tracing
|
|
262
262
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AsyncResult, Atom } from 'effect/
|
|
1
|
+
import { AsyncResult, Atom } from 'effect/reactivity';
|
|
2
2
|
import type { AnyActorLogic, ErrorFrom, EventFromLogic, SnapshotFrom } from 'xstate';
|
|
3
3
|
import { type EffectActorOptionsArgs } from "./createEffectActor.js";
|
|
4
4
|
import type { EffectActor } from "./effectActor.js";
|
|
@@ -8,7 +8,7 @@ import type { RequirementsFrom } from "./types.js";
|
|
|
8
8
|
export { NotReadyError } from "./errors.js";
|
|
9
9
|
/**
|
|
10
10
|
* Atoms that expose one actor to a reactive UI through
|
|
11
|
-
* `effect/
|
|
11
|
+
* `effect/reactivity`. Read them with the atom bindings for your
|
|
12
12
|
* framework, such as `@effect/atom-react`.
|
|
13
13
|
*/
|
|
14
14
|
export interface ActorAtoms<TLogic extends AnyActorLogic, ER = never> {
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { Effect, Scope } from 'effect';
|
|
2
|
-
import { type AnyActorLogic, type InputFrom } from 'xstate';
|
|
2
|
+
import { type ActorOptions, type AnyActorLogic, type InputFrom, type RequiredActorOptionsFor, type RequiredActorOptionsKeys } from 'xstate';
|
|
3
3
|
import { EffectActor } from "./effectActor.js";
|
|
4
4
|
import type { RequirementsFrom } from "./types.js";
|
|
5
5
|
/** Options for {@link createEffectActor}. */
|
|
6
|
-
export type EffectActorOptions<TLogic extends AnyActorLogic> =
|
|
6
|
+
export type EffectActorOptions<TLogic extends AnyActorLogic> = {
|
|
7
7
|
readonly input?: InputFrom<TLogic>;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
/**
|
|
9
|
+
* A snapshot from `actor.getPersistedSnapshot()`. The actor resumes in that
|
|
10
|
+
* state without re-running entry actions, and pending timers keep their
|
|
11
|
+
* original deadlines, as with XState's `createActor(logic, { snapshot })`.
|
|
12
|
+
* State-machine children resume their persisted state and completed
|
|
13
|
+
* children stay done. Effect tasks and streams that were running, at the
|
|
14
|
+
* root or as children, start again from the beginning. A restored actor
|
|
15
|
+
* does not need `input`.
|
|
16
|
+
*/
|
|
17
|
+
readonly snapshot?: ActorOptions<TLogic>['snapshot'];
|
|
18
|
+
} & RequiredActorOptionsFor<TLogic>;
|
|
19
|
+
export type EffectActorOptionsArgs<TLogic extends AnyActorLogic> = [
|
|
20
|
+
RequiredActorOptionsKeys<TLogic>
|
|
21
|
+
] extends [never] ? [options?: EffectActorOptions<TLogic>] : [options: EffectActorOptions<TLogic>];
|
|
12
22
|
/**
|
|
13
23
|
* Creates and starts an actor as an Effect interpreter over pure transitions.
|
|
14
24
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Queue, Effect, Fiber, Scope, Exit, Cause, Context, Duration, Data } from 'effect';
|
|
2
|
-
import { stopActor, terminateActor, deliverEvent } from 'xstate';
|
|
1
|
+
import { Queue, Effect, Fiber, Scope, Exit, Cause, Context, Clock, Duration, Data } from 'effect';
|
|
2
|
+
import { stopActor, terminateActor, deliverEvent, isMachineSnapshot } from 'xstate';
|
|
3
3
|
import { createDurable } from 'xstate/durable';
|
|
4
4
|
|
|
5
5
|
/** A mailbox item that reports a failed fire-and-forget action. */
|
|
@@ -416,6 +416,7 @@ function withActorScope(effect) {
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
const XSTATE_TIMER = 'xstate.timer';
|
|
419
|
+
const XSTATE_INIT = '@xstate.init';
|
|
419
420
|
|
|
420
421
|
/** Options for {@link createEffectActor}. */
|
|
421
422
|
|
|
@@ -456,7 +457,11 @@ function createEffectActor(logic, ...[options]) {
|
|
|
456
457
|
const timerKey = (source, id) => `${source.sessionId}:${id}`;
|
|
457
458
|
const inspectors = new Set();
|
|
458
459
|
let rootAnnounced = false;
|
|
460
|
+
const clock = Context.get(context, Clock.Clock);
|
|
459
461
|
const durable = createDurable(logic, {
|
|
462
|
+
// Timer deadlines are measured on the same clock the timers sleep
|
|
463
|
+
// on, so a restored timer resumes with its remaining delay.
|
|
464
|
+
now: () => clock.currentTimeMillisUnsafe(),
|
|
460
465
|
executeAction: (action, _metadata, runtime) => {
|
|
461
466
|
// Fire-and-forget: the action starts now and the loop continues.
|
|
462
467
|
// A rejection reaches the machine as an execution error.
|
|
@@ -590,10 +595,29 @@ function createEffectActor(logic, ...[options]) {
|
|
|
590
595
|
closeEffectHost(host);
|
|
591
596
|
};
|
|
592
597
|
|
|
593
|
-
// The first transition runs here so the handle is
|
|
594
|
-
// Effect succeeds, and the initial actions start before
|
|
595
|
-
|
|
598
|
+
// The first transition (or the restore) runs here so the handle is
|
|
599
|
+
// ready when this Effect succeeds, and the initial actions start before
|
|
600
|
+
// any send. Restoring runs no entry actions; its effects resume
|
|
601
|
+
// machine children, restart running Effect children and re-arm pending
|
|
602
|
+
// timers.
|
|
603
|
+
let [snapshot, effects] = options?.snapshot ? durable.restore(options.snapshot) : durable.initialTransition(options?.input);
|
|
604
|
+
const initial = snapshot;
|
|
605
|
+
if (options?.snapshot && !isMachineSnapshot(initial) && initial.status === 'active') {
|
|
606
|
+
// A restored machine resumes through the effects above. Other logic
|
|
607
|
+
// (an Effect task or stream at the root) reattaches its active work by
|
|
608
|
+
// replaying its init event, as its restored `start` does under
|
|
609
|
+
// `createActor`.
|
|
610
|
+
const [started, startEffects] = durable.transition(snapshot, {
|
|
611
|
+
type: XSTATE_INIT,
|
|
612
|
+
input: 'input' in initial ? initial.input : undefined
|
|
613
|
+
});
|
|
614
|
+
snapshot = started;
|
|
615
|
+
effects = [...effects, ...startEffects];
|
|
616
|
+
}
|
|
596
617
|
const root = durable.getActorRef(snapshot);
|
|
618
|
+
// Restored children are created by the snapshot, not spawned through
|
|
619
|
+
// the adapter; they find this host through their parent chain.
|
|
620
|
+
bindEffectHost(root, host);
|
|
597
621
|
// The root exists from here on; later announcements are step
|
|
598
622
|
// re-materializations, not new actors.
|
|
599
623
|
rootAnnounced = true;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Cause } from 'effect';
|
|
2
|
-
import { Atom, AsyncResult } from 'effect/
|
|
3
|
-
import { c as createEffectActor, N as NotReadyError, t as taggedState } from './state-
|
|
4
|
-
export { N as NotReadyError } from './state-
|
|
2
|
+
import { Atom, AsyncResult } from 'effect/reactivity';
|
|
3
|
+
import { c as createEffectActor, N as NotReadyError, t as taggedState } from './state-b3d9b3e2.js';
|
|
4
|
+
export { N as NotReadyError } from './state-b3d9b3e2.js';
|
|
5
5
|
import 'xstate';
|
|
6
6
|
import 'xstate/durable';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Atoms that expose one actor to a reactive UI through
|
|
10
|
-
* `effect/
|
|
10
|
+
* `effect/reactivity`. Read them with the atom bindings for your
|
|
11
11
|
* framework, such as `@effect/atom-react`.
|
|
12
12
|
*/
|
|
13
13
|
|
package/dist/xstate-effect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Schema, Effect, Stream, Exit, Cause, Queue } from 'effect';
|
|
2
2
|
import { createLogic, setup } from 'xstate';
|
|
3
|
-
import { s as startHostedEffect, r as relayToParent, E as EffectInterruptedError, A as ActorStoppedError, a as runHostedEffect } from './state-
|
|
4
|
-
export { d as ActorScope, A as ActorStoppedError, b as EffectActor, E as EffectInterruptedError, c as createEffectActor, t as taggedState, w as withActorScope } from './state-
|
|
3
|
+
import { s as startHostedEffect, r as relayToParent, E as EffectInterruptedError, A as ActorStoppedError, a as runHostedEffect } from './state-b3d9b3e2.js';
|
|
4
|
+
export { d as ActorScope, A as ActorStoppedError, b as EffectActor, E as EffectInterruptedError, c as createEffectActor, t as taggedState, w as withActorScope } from './state-b3d9b3e2.js';
|
|
5
5
|
import { dual } from 'effect/Function';
|
|
6
6
|
import 'xstate/durable';
|
|
7
7
|
|
|
@@ -658,6 +658,7 @@ function wrapActions(actions) {
|
|
|
658
658
|
for (const key of Object.keys(actions)) {
|
|
659
659
|
const action = actions[key];
|
|
660
660
|
wrapped[key] = args => {
|
|
661
|
+
// machine.provide can replace Effect actions with plain actions.
|
|
661
662
|
const result = action(args);
|
|
662
663
|
return Effect.isEffect(result) ? runHostedEffect(args.self, result, `action.${key}`) : result;
|
|
663
664
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xstate/effect",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Effect integration for XState",
|
|
6
6
|
"keywords": [
|
|
@@ -29,12 +29,13 @@
|
|
|
29
29
|
"url": "https://github.com/statelyai/xstate/issues"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"effect": "^4.0.0
|
|
33
|
-
"xstate": "^6.0.0-alpha.
|
|
32
|
+
"effect": "^4.0.0",
|
|
33
|
+
"xstate": "^6.0.0-alpha.65"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"effect": "4.0.0
|
|
37
|
-
"
|
|
36
|
+
"effect": "4.0.0",
|
|
37
|
+
"fast-check": "4.10.2",
|
|
38
|
+
"xstate": "^6.0.0-alpha.65"
|
|
38
39
|
},
|
|
39
40
|
"preconstruct": {
|
|
40
41
|
"entrypoints": [
|