@xstate/effect 0.1.0-alpha.7 → 1.0.0-alpha.11
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 +35 -18
- package/dist/declarations/src/actor.d.ts +4 -4
- package/dist/declarations/src/atom.d.ts +8 -9
- package/dist/declarations/src/effectActor.d.ts +19 -13
- package/dist/declarations/src/{fromEffect.d.ts → effectLogic.d.ts} +28 -30
- package/dist/declarations/src/errors.d.ts +23 -6
- package/dist/declarations/src/index.d.ts +4 -3
- package/dist/declarations/src/oxlint.d.ts +2 -0
- package/dist/declarations/src/persistence.d.ts +39 -0
- package/dist/declarations/src/schema.d.ts +2 -2
- package/dist/declarations/src/setupEffect.d.ts +48 -13
- package/dist/declarations/src/state.d.ts +8 -7
- package/dist/declarations/src/test.d.ts +11 -0
- package/dist/declarations/src/types.d.ts +2 -3
- package/dist/{state-b3d9b3e2.js → state-37b37ed4.js} +212 -88
- package/dist/xstate-effect-atom.js +10 -8
- package/dist/xstate-effect-oxlint.d.ts +3 -0
- package/dist/xstate-effect-oxlint.js +124 -0
- package/dist/xstate-effect-test.d.ts +2 -0
- package/dist/xstate-effect-test.js +11 -0
- package/dist/xstate-effect.js +231 -54
- package/package.json +17 -5
- package/dist/declarations/src/brands.d.ts +0 -1
package/README.md
CHANGED
|
@@ -9,14 +9,18 @@ Combine XState's event-driven workflows with Effect services, scopes, clocks and
|
|
|
9
9
|
|
|
10
10
|
This package is experimental. It targets XState v6 alpha and Effect 4.
|
|
11
11
|
|
|
12
|
+
In these docs, "transition effects" are the executable effect descriptors (`ExecutableEffect`) that an XState transition returns. "Effect" means an Effect.ts program.
|
|
13
|
+
|
|
12
14
|
## Installation
|
|
13
15
|
|
|
14
|
-
<!-- package name
|
|
16
|
+
<!-- package name, zero direct runtime dependencies and required versus optional peers from package.json -->
|
|
15
17
|
|
|
16
18
|
```bash
|
|
17
|
-
npm install @xstate/effect@
|
|
19
|
+
npm install @xstate/effect@latest xstate@alpha effect@^4
|
|
18
20
|
```
|
|
19
21
|
|
|
22
|
+
`@xstate/effect` has zero direct runtime dependencies. Effect and XState are its only required peers. Lint tooling is opt-in and installed as development dependencies.
|
|
23
|
+
|
|
20
24
|
## XState Effect: Quick start
|
|
21
25
|
|
|
22
26
|
<!-- public API from src/index.ts; actor lifetime from src/createEffectActor.ts -->
|
|
@@ -29,7 +33,7 @@ A release waits for approval, expires after 30 seconds, and allows cancellation
|
|
|
29
33
|
import { Context, Effect, Schema } from 'effect';
|
|
30
34
|
import {
|
|
31
35
|
createEffectActor,
|
|
32
|
-
|
|
36
|
+
createEffectLogic,
|
|
33
37
|
send,
|
|
34
38
|
setupEffect,
|
|
35
39
|
waitFor
|
|
@@ -40,7 +44,7 @@ export class Deployments extends Context.Service<
|
|
|
40
44
|
{ readonly deploy: (release: string) => Effect.Effect<string, Error> }
|
|
41
45
|
>()('@app/Deployments') {}
|
|
42
46
|
|
|
43
|
-
const deploy =
|
|
47
|
+
const deploy = createEffectLogic({
|
|
44
48
|
schemas: { input: Schema.Struct({ release: Schema.String }) },
|
|
45
49
|
effect: ({ input }) => Deployments.use((api) => api.deploy(input.release))
|
|
46
50
|
});
|
|
@@ -121,6 +125,10 @@ Start Effect-backed logic with `createEffectActor` inside an Effect scope.
|
|
|
121
125
|
|
|
122
126
|
</details>
|
|
123
127
|
|
|
128
|
+
<!-- declared Effect action typing from src/setupEffect.ts -->
|
|
129
|
+
|
|
130
|
+
Declared Effect actions infer context and event types from `setupEffect({ schemas })`, including schemas inherited through `extend`. See [Action argument types](docs/schemas-and-actions.md#action-argument-types) for custom arguments and safe defaults.
|
|
131
|
+
|
|
124
132
|
## Guides
|
|
125
133
|
|
|
126
134
|
| Guide | Examples |
|
|
@@ -134,7 +142,7 @@ Start Effect-backed logic with `createEffectActor` inside an Effect scope.
|
|
|
134
142
|
| [Atoms and React](docs/atoms-and-react.md) | Registry ownership, approval controls and useSelector. |
|
|
135
143
|
| [Testing and errors](docs/testing-and-errors.md) | TestClock, task retries, supervision and typed failures. |
|
|
136
144
|
|
|
137
|
-
|
|
145
|
+
Try the runnable examples in [examples/effect-workflows](../../examples/effect-workflows).
|
|
138
146
|
|
|
139
147
|
## Actors
|
|
140
148
|
|
|
@@ -183,13 +191,13 @@ The actor's later errors are observed through `join` or snapshots. Invalid input
|
|
|
183
191
|
|
|
184
192
|
## Effect actor logic
|
|
185
193
|
|
|
186
|
-
<!--
|
|
194
|
+
<!-- createEffectLogic, createEffectStreamLogic, createEffectEventStreamLogic from src/effectLogic.ts -->
|
|
187
195
|
|
|
188
196
|
| Function | Behavior |
|
|
189
197
|
| --- | --- |
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
198
|
+
| `createEffectLogic` | Run a task; its success value is the actor's output. |
|
|
199
|
+
| `createEffectStreamLogic` | Expose the latest stream item as snapshot context. |
|
|
200
|
+
| `createEffectEventStreamLogic` | Relay stream items to the parent as events. |
|
|
193
201
|
|
|
194
202
|
For example, a progress stream can feed an upload UI:
|
|
195
203
|
|
|
@@ -197,10 +205,14 @@ For example, a progress stream can feed an upload UI:
|
|
|
197
205
|
|
|
198
206
|
```ts
|
|
199
207
|
import { Effect, Option, Stream } from 'effect';
|
|
200
|
-
import {
|
|
208
|
+
import {
|
|
209
|
+
createEffectActor,
|
|
210
|
+
createEffectStreamLogic,
|
|
211
|
+
snapshots
|
|
212
|
+
} from '@xstate/effect';
|
|
201
213
|
|
|
202
214
|
// Demo upload progress. Replace with your upload SDK's progress stream.
|
|
203
|
-
const uploadProgress =
|
|
215
|
+
const uploadProgress = createEffectStreamLogic(Stream.make(0, 25, 60, 100));
|
|
204
216
|
|
|
205
217
|
const program = Effect.gen(function* () {
|
|
206
218
|
const actor = yield* createEffectActor(uploadProgress);
|
|
@@ -229,16 +241,20 @@ See [Effect actor logic](docs/effect-logic.md) for tasks and a stream that drive
|
|
|
229
241
|
<!-- effect action contract from src/setupEffect.ts; spawn guard from src/internal.ts -->
|
|
230
242
|
|
|
231
243
|
- Register background Effect actions in `setupEffect({ actions })`; enqueue them with explicit arguments.
|
|
232
|
-
- Invoke `
|
|
244
|
+
- Invoke `createEffectLogic` logic when the machine needs to wait for a result.
|
|
233
245
|
- Register spawned Effect logic in `actors`, then use `enq.spawn(args.actors.worker)`.
|
|
234
246
|
|
|
235
247
|
See [schemas and actions](docs/schemas-and-actions.md) for a complete audit example, runtime schema constraints and inline-source pitfalls.
|
|
236
248
|
|
|
249
|
+
<!-- lint plugin export from package.json; rule from src/oxlint.ts -->
|
|
250
|
+
|
|
251
|
+
Enable `xstate-effect/no-inline-effect` with the compiled `@xstate/effect/oxlint` plugin to catch discarded inline Effects and unsupported inline spawned Effect logic. See [Oxlint configuration](docs/schemas-and-actions.md#declared-sources) for installation and the rule's syntactic limits.
|
|
252
|
+
|
|
237
253
|
## Atoms and React
|
|
238
254
|
|
|
239
255
|
<!-- atom surface from src/atom.ts -->
|
|
240
256
|
|
|
241
|
-
`createActorAtoms` from `@xstate/effect/atom` exposes `actor`, `snapshot`, `result`, `send
|
|
257
|
+
`createActorAtoms` from `@xstate/effect/atom` exposes `actor`, `snapshot`, `result`, `send` and `select`. Machine logic also exposes `state`; tasks and streams do not. Its `Atom.runtime` must provide the logic's services. Pass `{ input }` when the logic requires input; missing or mismatched input is a type error.
|
|
242
258
|
|
|
243
259
|
- Read atoms with `@effect/atom-react` in React, or an `AtomRegistry` elsewhere.
|
|
244
260
|
- The registry owns their lifetime; dispose it when its owner shuts down.
|
|
@@ -250,16 +266,17 @@ See [atoms and React](docs/atoms-and-react.md) for complete examples with import
|
|
|
250
266
|
|
|
251
267
|
<!-- error types from src/errors.ts; timeout failure from src/actor.ts -->
|
|
252
268
|
|
|
269
|
+
- Use `sendAndAwait` from `@xstate/effect/test` to acknowledge a specific event, including ignored events.
|
|
253
270
|
- Use `TestClock` to drive deadlines without waiting for real time.
|
|
254
271
|
- `ActorStoppedError` reports a stop before an awaited result.
|
|
255
|
-
- `
|
|
272
|
+
- `EffectFailure<E>` carries the original `Cause<E>` in task and stream error snapshots, including defects and self-interruption.
|
|
256
273
|
- Timed waits fail with Effect's `Cause.TimeoutError`.
|
|
257
|
-
- `join` preserves a task's typed failure. Unexpected machine errors have type `unknown`.
|
|
274
|
+
- `join` replays the original Cause and preserves a task's typed failure. Unexpected machine errors have type `unknown`.
|
|
258
275
|
|
|
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).
|
|
276
|
+
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. Use `createSnapshotCodec` with a full bidirectional Effect schema for validated JSON persistence of versioned machines. See [persisting and restoring](docs/actors.md#persisting-and-restoring).
|
|
260
277
|
|
|
261
278
|
## Tracing
|
|
262
279
|
|
|
263
|
-
<!-- span names and attributes from src/internal.ts and src/
|
|
280
|
+
<!-- span names and attributes from src/internal.ts and src/effectLogic.ts -->
|
|
264
281
|
|
|
265
|
-
Hosted work uses spans named `
|
|
282
|
+
Hosted work uses spans named `createEffectLogic`, `createEffectStreamLogic`, `createEffectEventStreamLogic` and `action.<name>`. Each carries `xstate.actor.id` and `xstate.actor.address`. Provide a `Tracer` to record and export them.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Cause, Duration, Effect, Stream } from 'effect';
|
|
2
2
|
import type { Actor, ActorRef, AnyActorRef, AnyEventObject, EmittedFrom, EventRejection, ErrorFrom, InspectionEvent, OutputFrom, SnapshotFrom } from 'xstate';
|
|
3
3
|
import type { EffectActor } from "./effectActor.js";
|
|
4
|
-
import { ActorStoppedError } from "./errors.js";
|
|
4
|
+
import { ActorStoppedError, type UnwrapEffectFailure } from "./errors.js";
|
|
5
5
|
/** The event type accepted by an actor's `send` method. */
|
|
6
6
|
export type SendableEventFrom<TActor extends AnyActorRef> = Parameters<TActor['send']>[0];
|
|
7
7
|
/** The event type an actor emits through `actor.on(...)`. */
|
|
@@ -56,11 +56,11 @@ export declare const waitFor: {
|
|
|
56
56
|
};
|
|
57
57
|
/**
|
|
58
58
|
* Joins an actor's final result, like `Fiber.join`: succeeds with its `output`
|
|
59
|
-
* when it is done,
|
|
59
|
+
* when it is done, replays an Effect actor's original Cause when it errors, and fails with
|
|
60
60
|
* `ActorStoppedError` when it stops without output. Waits for a still-active
|
|
61
|
-
* actor to settle.
|
|
61
|
+
* actor to settle. Ordinary XState errors remain typed failures.
|
|
62
62
|
*/
|
|
63
|
-
export declare function join<TActor extends AnyActorRef>(actor: TActor): Effect.Effect<OutputFrom<TActor>, ErrorFrom<TActor
|
|
63
|
+
export declare function join<TActor extends AnyActorRef>(actor: TActor): Effect.Effect<OutputFrom<TActor>, UnwrapEffectFailure<ErrorFrom<TActor>> | ActorStoppedError>;
|
|
64
64
|
/**
|
|
65
65
|
* Streams the inspection events of the actor's system, as delivered to
|
|
66
66
|
* `system.inspect(…)`. The stream runs until it is interrupted or its scope
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AsyncResult, Atom } from 'effect/reactivity';
|
|
2
|
-
import type
|
|
2
|
+
import { type AnyStateMachine, type AnyActorLogic, type ErrorFrom, type EventFromLogic, type SnapshotFrom } from 'xstate';
|
|
3
3
|
import { type EffectActorOptionsArgs } from "./createEffectActor.js";
|
|
4
4
|
import type { EffectActor } from "./effectActor.js";
|
|
5
|
-
import { NotReadyError } from "./errors.js";
|
|
5
|
+
import { NotReadyError, type UnwrapEffectFailure } from "./errors.js";
|
|
6
6
|
import { type TaggedStateFrom } from "./state.js";
|
|
7
7
|
import type { RequirementsFrom } from "./types.js";
|
|
8
8
|
export { NotReadyError } from "./errors.js";
|
|
@@ -11,7 +11,7 @@ export { NotReadyError } from "./errors.js";
|
|
|
11
11
|
* `effect/reactivity`. Read them with the atom bindings for your
|
|
12
12
|
* framework, such as `@effect/atom-react`.
|
|
13
13
|
*/
|
|
14
|
-
|
|
14
|
+
interface BaseActorAtoms<TLogic extends AnyActorLogic, ER> {
|
|
15
15
|
/**
|
|
16
16
|
* The running actor. It starts when the atom is first read and stops when
|
|
17
17
|
* the atom is released, that is, when nothing reads or mounts it anymore.
|
|
@@ -23,7 +23,7 @@ export interface ActorAtoms<TLogic extends AnyActorLogic, ER = never> {
|
|
|
23
23
|
* The snapshot as a result: a `Failure` carrying the actor's error once
|
|
24
24
|
* the actor's status is `error`, so an error boundary can handle it.
|
|
25
25
|
*/
|
|
26
|
-
readonly result: Atom.Atom<AsyncResult.AsyncResult<SnapshotFrom<TLogic>, ER | ErrorFrom<TLogic
|
|
26
|
+
readonly result: Atom.Atom<AsyncResult.AsyncResult<SnapshotFrom<TLogic>, ER | UnwrapEffectFailure<ErrorFrom<TLogic>>>>;
|
|
27
27
|
/**
|
|
28
28
|
* Sends an event to the actor synchronously. Set it with the event, for
|
|
29
29
|
* example through `useAtomSet` in React. Its value reports the last send:
|
|
@@ -33,12 +33,11 @@ export interface ActorAtoms<TLogic extends AnyActorLogic, ER = never> {
|
|
|
33
33
|
readonly send: Atom.Writable<AsyncResult.AsyncResult<void, ER | NotReadyError>, EventFromLogic<TLogic>>;
|
|
34
34
|
/** Derives an atom of a value selected from the snapshot. */
|
|
35
35
|
readonly select: <T>(selector: (snapshot: SnapshotFrom<TLogic>) => T) => Atom.Atom<AsyncResult.AsyncResult<T, ER>>;
|
|
36
|
-
/**
|
|
37
|
-
* The snapshot as a tagged union over the machine's states, for
|
|
38
|
-
* `Match.tag` or a `switch` on `_tag`. See `taggedState`.
|
|
39
|
-
*/
|
|
40
|
-
readonly state: Atom.Atom<AsyncResult.AsyncResult<TaggedStateFrom<SnapshotFrom<TLogic>>, ER>>;
|
|
41
36
|
}
|
|
37
|
+
export type ActorAtoms<TLogic extends AnyActorLogic, ER = never> = BaseActorAtoms<TLogic, ER> & ([TLogic] extends [AnyStateMachine] ? {
|
|
38
|
+
/** The machine snapshot as a tagged union over its states. */
|
|
39
|
+
readonly state: Atom.Atom<AsyncResult.AsyncResult<TaggedStateFrom<SnapshotFrom<TLogic>>, ER>>;
|
|
40
|
+
} : {});
|
|
42
41
|
interface MissingRequirements<T> {
|
|
43
42
|
readonly 'The runtime does not provide services the logic requires': T;
|
|
44
43
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { Queue } from 'effect';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
export interface
|
|
5
|
-
|
|
6
|
-
readonly error: unknown;
|
|
1
|
+
import { Exit, Queue } from 'effect';
|
|
2
|
+
import { ActorStoppedError, EventRejectedError } from "./errors.js";
|
|
3
|
+
import type { ActorRef, AnyActor, AnyActorLogic, EmittedFrom, EventFromLogic, InspectionEvent, Observer, PersistedSnapshotOf, SnapshotFrom, Subscription } from 'xstate';
|
|
4
|
+
export interface Receipt<TSnapshot> {
|
|
5
|
+
resume?: (exit: Exit.Exit<TSnapshot, ActorStoppedError | EventRejectedError>) => void;
|
|
7
6
|
}
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export type MailboxItem<TEvent, TSnapshot> = {
|
|
8
|
+
readonly kind: 'event';
|
|
9
|
+
readonly event: TEvent;
|
|
10
|
+
readonly receipt?: Receipt<TSnapshot>;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: 'actionFailure';
|
|
13
|
+
readonly error: unknown;
|
|
14
|
+
};
|
|
11
15
|
declare const symbolObservable: typeof Symbol.observable;
|
|
12
16
|
/**
|
|
13
17
|
* Calls a listener and reports an exception it throws without letting it
|
|
@@ -34,11 +38,14 @@ export declare class EffectActor<TLogic extends AnyActorLogic> implements ActorR
|
|
|
34
38
|
private readonly _observers;
|
|
35
39
|
private readonly _listeners;
|
|
36
40
|
private _settled;
|
|
37
|
-
|
|
41
|
+
private readonly _pending;
|
|
42
|
+
constructor(logic: TLogic, root: AnyActor, snapshot: SnapshotFrom<TLogic>, _mailbox: Queue.Queue<MailboxItem<EventFromLogic<TLogic>, SnapshotFrom<TLogic>>>, _stopExecution: () => void, _inspectors: Set<(event: InspectionEvent) => void>);
|
|
38
43
|
getSnapshot(): SnapshotFrom<TLogic>;
|
|
39
|
-
getPersistedSnapshot():
|
|
44
|
+
getPersistedSnapshot(): PersistedSnapshotOf<TLogic>;
|
|
40
45
|
/** Sends an event; the actor processes it on its own fiber. */
|
|
41
46
|
send(event: EventFromLogic<TLogic>): void;
|
|
47
|
+
private _deliveryError;
|
|
48
|
+
private _stoppedError;
|
|
42
49
|
/** Stops the actor, its children and every Effect it hosts. */
|
|
43
50
|
stop(): this;
|
|
44
51
|
subscribe(observer: Observer<SnapshotFrom<TLogic>>): Subscription;
|
|
@@ -53,8 +60,7 @@ export declare class EffectActor<TLogic extends AnyActorLogic> implements ActorR
|
|
|
53
60
|
inspect(observer: Observer<InspectionEvent> | ((event: InspectionEvent) => void)): Subscription;
|
|
54
61
|
[symbolObservable](): this;
|
|
55
62
|
toJSON(): {
|
|
56
|
-
|
|
57
|
-
id: string;
|
|
63
|
+
$actor: string;
|
|
58
64
|
};
|
|
59
65
|
}
|
|
60
66
|
export {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Effect, Stream } from 'effect';
|
|
2
2
|
import { type ActorLogicValidator, type ActorLogic, type AnyActorRef, type AnyActorSystem, type AnyEventObject, type EventObject, type Snapshot, type StandardSchemaV1 } from 'xstate';
|
|
3
|
+
import { EffectFailure } from "./errors.js";
|
|
3
4
|
import type { EffectRequirements } from "./types.js";
|
|
4
5
|
import { type EffectSchemaLike, type ToStandardSchema, type ValidateEffectActorSchemas } from "./schema.js";
|
|
5
6
|
declare const effectLogicBrand: unique symbol;
|
|
6
7
|
/**
|
|
7
|
-
* Type-level marker attached to logic created by `
|
|
8
|
-
* `
|
|
8
|
+
* Type-level marker attached to logic created by `createEffectLogic`,
|
|
9
|
+
* `createEffectStreamLogic` and `createEffectEventStreamLogic`. It carries the logic's
|
|
9
10
|
* failure type and its Effect service requirements, which is how
|
|
10
11
|
* `RequirementsFrom` collects the `R` channel of `createEffectActor` without
|
|
11
12
|
* inspecting the logic at runtime.
|
|
@@ -17,15 +18,14 @@ export interface EffectLogicBrand<TError, TRequirements> {
|
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
|
-
* Snapshot of a `
|
|
21
|
+
* Snapshot of a `createEffectLogic` actor. The actor holds no context of its own: it
|
|
21
22
|
* is `active` while the Effect runs, `done` with the Effect's success value as
|
|
22
|
-
* `output`, or `error` with the
|
|
23
|
-
* `EffectInterruptedError` as `error`.
|
|
23
|
+
* `output`, or `error` with the original `Cause` wrapped in `EffectFailure` as `error`.
|
|
24
24
|
*/
|
|
25
25
|
export type EffectSnapshot<TOutput, TError, TInput> = Snapshot<TOutput> & {
|
|
26
26
|
readonly context: undefined;
|
|
27
27
|
readonly input: TInput | undefined;
|
|
28
|
-
readonly error: TError | undefined;
|
|
28
|
+
readonly error: EffectFailure<TError> | undefined;
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
31
|
* Argument passed to the function form of an Effect source. It gives the
|
|
@@ -41,7 +41,7 @@ export type EffectSourceArgs<TInput> = {
|
|
|
41
41
|
readonly emit: (event: AnyEventObject) => void;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
* What `
|
|
44
|
+
* What `createEffectLogic` accepts as its Effect: either an Effect value, or a
|
|
45
45
|
* function of {@link EffectSourceArgs} returning one. The function form is
|
|
46
46
|
* called once per actor start, so it sees that actor's input.
|
|
47
47
|
*/
|
|
@@ -58,7 +58,7 @@ type EffectSourceConfig<TOutput, TError, TInput, TRequirements, TSchemas extends
|
|
|
58
58
|
};
|
|
59
59
|
type SchemaOutput<TSchema extends EffectSchemaLike> = StandardSchemaV1.InferOutput<ToStandardSchema<TSchema>>;
|
|
60
60
|
type EffectSuccess<T> = T extends Effect.Effect<infer A, any, any> ? A : never;
|
|
61
|
-
type
|
|
61
|
+
type EffectError<T> = T extends Effect.Effect<any, infer E, any> ? E : never;
|
|
62
62
|
type EffectSchemaValidationConfig<TSchemas extends EffectActorSchemas> = {
|
|
63
63
|
readonly validator: ActorLogicValidator;
|
|
64
64
|
readonly schemas: TSchemas & ValidateEffectActorSchemas<TSchemas>;
|
|
@@ -67,7 +67,7 @@ type EffectSchemaValidationConfig<TSchemas extends EffectActorSchemas> = {
|
|
|
67
67
|
readonly schemas: TSchemas;
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
|
-
* Actor logic produced by `
|
|
70
|
+
* Actor logic produced by `createEffectLogic` and `createEffectEventStreamLogic`. It is
|
|
71
71
|
* ordinary XState actor logic carrying {@link EffectLogicBrand}, so a machine
|
|
72
72
|
* can invoke or spawn it, and `createEffectActor` can collect its
|
|
73
73
|
* requirements.
|
|
@@ -76,17 +76,17 @@ export type EffectActorLogic<TOutput, TError, TInput, TRequirements> = ActorLogi
|
|
|
76
76
|
readonly id?: string;
|
|
77
77
|
} & EffectLogicBrand<TError, TRequirements>;
|
|
78
78
|
/**
|
|
79
|
-
* Snapshot of a `
|
|
79
|
+
* Snapshot of a `createEffectStreamLogic` actor. `context` holds the most recent
|
|
80
80
|
* stream item, or `undefined` before the first one. The actor reaches `done`
|
|
81
81
|
* with no output when the stream completes, and `error` when it fails.
|
|
82
82
|
*/
|
|
83
83
|
export type EffectStreamSnapshot<TItem, TError, TInput> = Snapshot<undefined> & {
|
|
84
84
|
readonly context: TItem | undefined;
|
|
85
85
|
readonly input: TInput | undefined;
|
|
86
|
-
readonly error: TError | undefined;
|
|
86
|
+
readonly error: EffectFailure<TError> | undefined;
|
|
87
87
|
};
|
|
88
88
|
/**
|
|
89
|
-
* Actor logic produced by `
|
|
89
|
+
* Actor logic produced by `createEffectStreamLogic`. Its snapshot is an
|
|
90
90
|
* {@link EffectStreamSnapshot}, so the parent reads the latest item from the
|
|
91
91
|
* child's `context` rather than from events.
|
|
92
92
|
*/
|
|
@@ -95,9 +95,7 @@ export type EffectStreamActorLogic<TItem, TError, TInput, TRequirements> = Actor
|
|
|
95
95
|
} & EffectLogicBrand<TError, TRequirements>;
|
|
96
96
|
/**
|
|
97
97
|
* Creates actor logic that runs an Effect. The actor completes with the
|
|
98
|
-
* Effect's success value as its output and fails with the Effect
|
|
99
|
-
* squashed defect, or an `EffectInterruptedError` when the Effect interrupts
|
|
100
|
-
* itself. Stopping the actor interrupts the Effect without an error. Accepts
|
|
98
|
+
* Effect's success value as its output and fails with the original `Cause` wrapped in `EffectFailure`. Stopping the actor interrupts the Effect without an error. Accepts
|
|
101
99
|
* an Effect, a function of {@link EffectSourceArgs} returning one, or a config
|
|
102
100
|
* object with `id`, `schemas`, `validator` and `effect`. The logic must be run
|
|
103
101
|
* under `createEffectActor`, which provides the Effect services it declares.
|
|
@@ -105,29 +103,29 @@ export type EffectStreamActorLogic<TItem, TError, TInput, TRequirements> = Actor
|
|
|
105
103
|
* interruption. Use `withActorScope` to retain a resource until the owning
|
|
106
104
|
* Effect actor stops.
|
|
107
105
|
*/
|
|
108
|
-
export declare function
|
|
106
|
+
export declare function createEffectLogic<const TInputSchema extends EffectSchemaLike, const TOutputSchema extends EffectSchemaLike, TError = unknown, TRequirements = never>(config: Omit<EffectSourceConfig<SchemaOutput<TOutputSchema>, TError, SchemaOutput<TInputSchema>, TRequirements>, 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
109
107
|
input: TInputSchema;
|
|
110
108
|
output: TOutputSchema;
|
|
111
109
|
}>): EffectActorLogic<SchemaOutput<TOutputSchema>, TError, SchemaOutput<TInputSchema>, TRequirements>;
|
|
112
|
-
export declare function
|
|
110
|
+
export declare function createEffectLogic<const TInputSchema extends EffectSchemaLike, TEffect extends Effect.Effect<any, any, any>>(config: Omit<EffectSourceConfig<EffectSuccess<TEffect>, EffectError<TEffect>, SchemaOutput<TInputSchema>, EffectRequirements<TEffect>, {
|
|
113
111
|
input: TInputSchema;
|
|
114
112
|
}>, 'effect' | 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
115
113
|
input: TInputSchema;
|
|
116
114
|
output?: never;
|
|
117
115
|
}> & {
|
|
118
116
|
effect: TEffect | ((args: EffectSourceArgs<SchemaOutput<TInputSchema>>) => TEffect);
|
|
119
|
-
}): EffectActorLogic<EffectSuccess<TEffect>,
|
|
120
|
-
export declare function
|
|
117
|
+
}): EffectActorLogic<EffectSuccess<TEffect>, EffectError<TEffect>, SchemaOutput<TInputSchema>, EffectRequirements<TEffect>>;
|
|
118
|
+
export declare function createEffectLogic<const TOutputSchema extends EffectSchemaLike, TError = unknown, TInput = undefined, TRequirements = never>(config: Omit<EffectSourceConfig<SchemaOutput<TOutputSchema>, TError, TInput, TRequirements>, 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
121
119
|
input?: never;
|
|
122
120
|
output: TOutputSchema;
|
|
123
121
|
}>): EffectActorLogic<SchemaOutput<TOutputSchema>, TError, TInput, TRequirements>;
|
|
124
|
-
export declare function
|
|
122
|
+
export declare function createEffectLogic<TOutput, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectSourceConfig<TOutput, TError, TInput, TRequirements> & {
|
|
125
123
|
schemas?: undefined;
|
|
126
124
|
}): EffectActorLogic<TOutput, TError, TInput, TRequirements>;
|
|
127
|
-
export declare function
|
|
128
|
-
export declare function
|
|
125
|
+
export declare function createEffectLogic<TOutput, TError = unknown, TRequirements = never>(effect: Effect.Effect<TOutput, TError, TRequirements>): EffectActorLogic<TOutput, TError, undefined, TRequirements>;
|
|
126
|
+
export declare function createEffectLogic<TOutput, TError = unknown, TInput = undefined, TRequirements = never>(effect: (args: EffectSourceArgs<TInput>) => Effect.Effect<TOutput, TError, TRequirements>): EffectActorLogic<TOutput, TError, TInput, TRequirements>;
|
|
129
127
|
/**
|
|
130
|
-
* What `
|
|
128
|
+
* What `createEffectStreamLogic` and `createEffectEventStreamLogic` accept as their stream:
|
|
131
129
|
* either a Stream value, or a function of {@link EffectSourceArgs} returning
|
|
132
130
|
* one. The function form is called once per actor start.
|
|
133
131
|
*/
|
|
@@ -153,29 +151,29 @@ type StreamRequirements<T> = T extends Stream.Stream<any, any, infer R> ? R : ne
|
|
|
153
151
|
* `schemas`, `validator` and `stream`. Its resources close when the stream
|
|
154
152
|
* completes, fails or is interrupted.
|
|
155
153
|
*/
|
|
156
|
-
export declare function
|
|
154
|
+
export declare function createEffectStreamLogic<const TInputSchema extends EffectSchemaLike, TStream extends Stream.Stream<any, any, any>>(config: Omit<EffectStreamConfig<StreamItem<TStream>, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>, 'stream' | 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
157
155
|
input: TInputSchema;
|
|
158
156
|
}> & {
|
|
159
157
|
stream: TStream | ((args: EffectSourceArgs<SchemaOutput<TInputSchema>>) => TStream);
|
|
160
158
|
}): EffectStreamActorLogic<StreamItem<TStream>, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>;
|
|
161
|
-
export declare function
|
|
159
|
+
export declare function createEffectStreamLogic<TItem, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectStreamConfig<TItem, TError, TInput, TRequirements> & {
|
|
162
160
|
schemas?: undefined;
|
|
163
161
|
}): EffectStreamActorLogic<TItem, TError, TInput, TRequirements>;
|
|
164
|
-
export declare function
|
|
162
|
+
export declare function createEffectStreamLogic<TItem, TError = unknown, TInput = undefined, TRequirements = never>(stream: EffectStreamSource<TItem, TError, TInput, TRequirements>): EffectStreamActorLogic<TItem, TError, TInput, TRequirements>;
|
|
165
163
|
/**
|
|
166
164
|
* Creates actor logic that runs a Stream of events and relays each item to the
|
|
167
165
|
* parent machine as an event, the way `fromEventObservable` does. The actor
|
|
168
166
|
* has no output: it reaches `done` when the stream completes and `error` when
|
|
169
|
-
* it fails. Accepts the same forms as `
|
|
167
|
+
* it fails. Accepts the same forms as `createEffectStreamLogic`. Its resources close
|
|
170
168
|
* when the stream completes, fails or is interrupted.
|
|
171
169
|
*/
|
|
172
|
-
export declare function
|
|
170
|
+
export declare function createEffectEventStreamLogic<const TInputSchema extends EffectSchemaLike, TStream extends Stream.Stream<EventObject, any, any>>(config: Omit<EffectStreamConfig<StreamItem<TStream>, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>, 'stream' | 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
|
|
173
171
|
input: TInputSchema;
|
|
174
172
|
}> & {
|
|
175
173
|
stream: TStream | ((args: EffectSourceArgs<SchemaOutput<TInputSchema>>) => TStream);
|
|
176
174
|
}): EffectActorLogic<undefined, StreamError<TStream>, SchemaOutput<TInputSchema>, StreamRequirements<TStream>>;
|
|
177
|
-
export declare function
|
|
175
|
+
export declare function createEffectEventStreamLogic<TEvent extends EventObject, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectStreamConfig<TEvent, TError, TInput, TRequirements> & {
|
|
178
176
|
schemas?: undefined;
|
|
179
177
|
}): EffectActorLogic<undefined, TError, TInput, TRequirements>;
|
|
180
|
-
export declare function
|
|
178
|
+
export declare function createEffectEventStreamLogic<TEvent extends EventObject, TError = unknown, TInput = undefined, TRequirements = never>(stream: EffectStreamSource<TEvent, TError, TInput, TRequirements>): EffectActorLogic<undefined, TError, TInput, TRequirements>;
|
|
181
179
|
export {};
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { Cause } from 'effect';
|
|
2
2
|
import type { Snapshot } from 'xstate';
|
|
3
|
+
/** Preserves an Effect failure, defect or interruption through XState error routing. */
|
|
4
|
+
export declare class EffectFailure<E> extends Error {
|
|
5
|
+
#private;
|
|
6
|
+
readonly cause: Cause.Cause<E>;
|
|
7
|
+
readonly _tag = "EffectFailure";
|
|
8
|
+
static is(value: unknown): value is EffectFailure<unknown>;
|
|
9
|
+
constructor(cause: Cause.Cause<E>);
|
|
10
|
+
}
|
|
3
11
|
declare const EffectInterruptedError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
4
12
|
readonly _tag: "EffectInterruptedError";
|
|
5
13
|
} & Readonly<A>;
|
|
6
|
-
/**
|
|
7
|
-
* The failure an Effect-backed actor reports when its Effect was interrupted
|
|
8
|
-
* by something other than the actor being stopped, such as `Effect.interrupt`,
|
|
9
|
-
* losing an `Effect.race`, or an `Effect.timeout` that interrupts.
|
|
10
|
-
*/
|
|
14
|
+
/** @deprecated Inspect `EffectFailure.cause` for interruption instead. */
|
|
11
15
|
export declare class EffectInterruptedError extends EffectInterruptedError_base<{
|
|
12
16
|
readonly cause: Cause.Cause<never>;
|
|
13
17
|
}> {
|
|
@@ -19,7 +23,7 @@ declare const ActorStoppedError_base: new <A extends Record<string, any> = {}>(a
|
|
|
19
23
|
/**
|
|
20
24
|
* Reported by `waitFor` when the actor stops or errors before a snapshot
|
|
21
25
|
* matches, and by `join` when the actor stops without output. `join` reports
|
|
22
|
-
* an errored actor's
|
|
26
|
+
* an errored actor's outcome instead, preserving an Effect failure's Cause.
|
|
23
27
|
*/
|
|
24
28
|
export declare class ActorStoppedError extends ActorStoppedError_base<{
|
|
25
29
|
readonly actorId: string;
|
|
@@ -37,4 +41,17 @@ declare const NotReadyError_base: new <A extends Record<string, any> = {}>(args:
|
|
|
37
41
|
export declare class NotReadyError extends NotReadyError_base {
|
|
38
42
|
get message(): string;
|
|
39
43
|
}
|
|
44
|
+
export type UnwrapEffectFailure<E> = E extends EffectFailure<infer Failure> ? Failure : E;
|
|
45
|
+
declare const EventRejectedError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => Cause.YieldableError & {
|
|
46
|
+
readonly _tag: "EventRejectedError";
|
|
47
|
+
} & Readonly<A>;
|
|
48
|
+
/** An event rejected at the actor's delivery boundary. */
|
|
49
|
+
export declare class EventRejectedError extends EventRejectedError_base<{
|
|
50
|
+
readonly actorId: string;
|
|
51
|
+
readonly event: {
|
|
52
|
+
readonly type: string;
|
|
53
|
+
};
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
}> {
|
|
56
|
+
}
|
|
40
57
|
export {};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createEffectLogic, createEffectEventStreamLogic, createEffectStreamLogic, type EffectActorLogic, type EffectLogicBrand, type EffectSnapshot, type EffectSource, type EffectSourceArgs, type EffectStreamSource, type EffectStreamActorLogic, type EffectStreamSnapshot } from "./effectLogic.js";
|
|
2
2
|
export { createEffectActor, type EffectActorOptions } from "./createEffectActor.js";
|
|
3
3
|
export { EffectActor } from "./effectActor.js";
|
|
4
4
|
export { ActorScope, withActorScope } from "./actorScope.js";
|
|
5
5
|
export { deadLetters, emitted, inspect, join, send, snapshots, waitFor, type EmittedEventFrom, type SendableEventFrom, type WaitForOptions } from "./actor.js";
|
|
6
|
-
export { ActorStoppedError, EffectInterruptedError } from "./errors.js";
|
|
6
|
+
export { ActorStoppedError, EventRejectedError, EffectFailure, EffectInterruptedError } from "./errors.js";
|
|
7
7
|
export { setupEffect, type EffectAction, type EffectActionArgs, type EffectSetupReturn } from "./setupEffect.js";
|
|
8
8
|
export { type RequirementsFrom } from "./types.js";
|
|
9
9
|
export { taggedState, type StateTag, type TaggedState, type TaggedStateFrom } from "./state.js";
|
|
10
10
|
export { type ErrorFrom } from 'xstate';
|
|
11
|
-
export { type EffectSchema, type EffectSchemaLike, type EffectSetupSchemas, type EffectSetupStateSchema } from "./schema.js";
|
|
11
|
+
export { type EffectSchema, type EffectSchemaLike, type EffectSetupSchemas, type EffectSetupStateSchema, type ToStandardSetupSchemas, type ToStandardSetupStates } from "./schema.js";
|
|
12
|
+
export { createSnapshotCodec, SnapshotCodecError } from "./persistence.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect';
|
|
2
|
+
import type { AnyStateMachine, RestorablePersistedSnapshotFor, Snapshot } from 'xstate';
|
|
3
|
+
type SnapshotData<TMachine extends AnyStateMachine> = {
|
|
4
|
+
readonly status: Snapshot<unknown>['status'];
|
|
5
|
+
readonly context: unknown;
|
|
6
|
+
readonly value: unknown;
|
|
7
|
+
readonly children: Readonly<Record<string, unknown>>;
|
|
8
|
+
readonly machine: {
|
|
9
|
+
readonly id: TMachine['id'];
|
|
10
|
+
readonly version: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
declare const SnapshotCodecError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
14
|
+
readonly _tag: "SnapshotCodecError";
|
|
15
|
+
} & Readonly<A>;
|
|
16
|
+
/** A malformed transport, unsupported JSON value or missing persistence metadata. */
|
|
17
|
+
export declare class SnapshotCodecError extends SnapshotCodecError_base<{
|
|
18
|
+
readonly stage: 'encode' | 'decode';
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
}> {
|
|
22
|
+
get message(): string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Binds a full bidirectional snapshot schema to a versioned machine. The schema
|
|
26
|
+
* owns rich values and supported historical versions. Decoding never migrates;
|
|
27
|
+
* restore or `machineVersions` remains responsible for migration.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createSnapshotCodec<TMachine extends AnyStateMachine & {
|
|
30
|
+
readonly version: string;
|
|
31
|
+
}, P extends SnapshotData<TMachine>, Encoded, RD, RE>(machine: TMachine, schema: Schema.Codec<P, Encoded, RD, RE> & (unknown extends RE ? {
|
|
32
|
+
readonly 'A bidirectional codec is required': never;
|
|
33
|
+
} : unknown)): {
|
|
34
|
+
encode(snapshot: P | (Snapshot<unknown> & RestorablePersistedSnapshotFor<TMachine> & {
|
|
35
|
+
readonly context: P["context"];
|
|
36
|
+
})): Effect.Effect<string, SnapshotCodecError | Schema.SchemaError, RE>;
|
|
37
|
+
decode(json: string): Effect.Effect<P & Snapshot<unknown> & RestorablePersistedSnapshotFor<TMachine>, SnapshotCodecError | Schema.SchemaError, RD>;
|
|
38
|
+
};
|
|
39
|
+
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Schema } from 'effect';
|
|
2
2
|
import type { SetupSchemas, SetupStateSchema, SetupStateSchemas, StandardSchemaV1 } from 'xstate';
|
|
3
3
|
/**
|
|
4
|
-
* An Effect `Schema` accepted by this package. `setupEffect`, `
|
|
5
|
-
* `
|
|
4
|
+
* An Effect `Schema` accepted by this package. `setupEffect`, `createEffectLogic`,
|
|
5
|
+
* `createEffectStreamLogic` and `createEffectEventStreamLogic` convert it to a Standard
|
|
6
6
|
* Schema, so XState infers the decoded `Schema.Type` without an explicit call
|
|
7
7
|
* to `Schema.toStandardSchemaV1`.
|
|
8
8
|
*/
|