@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 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 and peer dependencies from package.json -->
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@alpha xstate@alpha effect@^4
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
- fromEffect,
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 = fromEffect({
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
- Runnable copies live in [examples/effect-workflows](../../examples/effect-workflows). Their tests verify the docs use the same code.
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
- <!-- fromEffect, fromEffectStream, fromEffectEventStream from src/fromEffect.ts -->
194
+ <!-- createEffectLogic, createEffectStreamLogic, createEffectEventStreamLogic from src/effectLogic.ts -->
187
195
 
188
196
  | Function | Behavior |
189
197
  | --- | --- |
190
- | `fromEffect` | Run a task; its success value is the actor's output. |
191
- | `fromEffectStream` | Expose the latest stream item as snapshot context. |
192
- | `fromEffectEventStream` | Relay stream items to the parent as events. |
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 { createEffectActor, fromEffectStream, snapshots } from '@xstate/effect';
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 = fromEffectStream(Stream.make(0, 25, 60, 100));
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 `fromEffect` logic when the machine needs to wait for a result.
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`, `select` and `state`. Its `Atom.runtime` must provide the logic's services. Pass `{ input }` when the logic requires input; missing or mismatched input is a type error.
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
- - `EffectInterruptedError` reports self-interruption inside Effect logic.
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/fromEffect.ts -->
280
+ <!-- span names and attributes from src/internal.ts and src/effectLogic.ts -->
264
281
 
265
- Hosted work uses spans named `fromEffect`, `fromEffectStream`, `fromEffectEventStream` and `action.<name>`. Each carries `xstate.actor.id` and `xstate.actor.address`. Provide a `Tracer` to record and export them.
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, fails with `snapshot.error` when it errors, and fails with
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> | ActorStoppedError>;
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 { AnyActorLogic, ErrorFrom, EventFromLogic, SnapshotFrom } from 'xstate';
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
- export interface ActorAtoms<TLogic extends AnyActorLogic, ER = never> {
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 type { ActorRef, AnyActor, AnyActorLogic, EmittedFrom, EventFromLogic, InspectionEvent, Observer, Snapshot, SnapshotFrom, Subscription } from 'xstate';
3
- /** A mailbox item that reports a failed fire-and-forget action. */
4
- export interface ActionFailure {
5
- readonly [actionFailure]: true;
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 declare const actionFailure: unique symbol;
9
- export declare function isActionFailure(value: unknown): value is ActionFailure;
10
- export type MailboxItem<TEvent> = TEvent | ActionFailure;
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
- constructor(logic: TLogic, root: AnyActor, snapshot: SnapshotFrom<TLogic>, _mailbox: Queue.Queue<MailboxItem<EventFromLogic<TLogic>>>, _stopExecution: () => void, _inspectors: Set<(event: InspectionEvent) => void>);
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(): Snapshot<unknown>;
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
- xstate$$type: number;
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 `fromEffect`,
8
- * `fromEffectStream` and `fromEffectEventStream`. It carries the logic's
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 `fromEffect` actor. The actor holds no context of its own: it
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 Effect's failure, defect or
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 `fromEffect` accepts as its Effect: either an Effect value, or a
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 EffectFailure<T> = T extends Effect.Effect<any, infer E, any> ? E : never;
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 `fromEffect` and `fromEffectEventStream`. It is
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 `fromEffectStream` actor. `context` holds the most recent
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 `fromEffectStream`. Its snapshot is an
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's error, its
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 fromEffect<const TInputSchema extends EffectSchemaLike, const TOutputSchema extends EffectSchemaLike, TError = unknown, TRequirements = never>(config: Omit<EffectSourceConfig<SchemaOutput<TOutputSchema>, TError, SchemaOutput<TInputSchema>, TRequirements>, 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
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 fromEffect<const TInputSchema extends EffectSchemaLike, TEffect extends Effect.Effect<any, any, any>>(config: Omit<EffectSourceConfig<EffectSuccess<TEffect>, EffectFailure<TEffect>, SchemaOutput<TInputSchema>, EffectRequirements<TEffect>, {
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>, EffectFailure<TEffect>, SchemaOutput<TInputSchema>, EffectRequirements<TEffect>>;
120
- export declare function fromEffect<const TOutputSchema extends EffectSchemaLike, TError = unknown, TInput = undefined, TRequirements = never>(config: Omit<EffectSourceConfig<SchemaOutput<TOutputSchema>, TError, TInput, TRequirements>, 'schemas' | 'validator'> & EffectSchemaValidationConfig<{
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 fromEffect<TOutput, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectSourceConfig<TOutput, TError, TInput, TRequirements> & {
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 fromEffect<TOutput, TError = unknown, TRequirements = never>(effect: Effect.Effect<TOutput, TError, TRequirements>): EffectActorLogic<TOutput, TError, undefined, TRequirements>;
128
- export declare function fromEffect<TOutput, TError = unknown, TInput = undefined, TRequirements = never>(effect: (args: EffectSourceArgs<TInput>) => Effect.Effect<TOutput, TError, TRequirements>): EffectActorLogic<TOutput, TError, TInput, TRequirements>;
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 `fromEffectStream` and `fromEffectEventStream` accept as their stream:
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 fromEffectStream<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<{
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 fromEffectStream<TItem, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectStreamConfig<TItem, TError, TInput, TRequirements> & {
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 fromEffectStream<TItem, TError = unknown, TInput = undefined, TRequirements = never>(stream: EffectStreamSource<TItem, TError, TInput, TRequirements>): EffectStreamActorLogic<TItem, TError, TInput, TRequirements>;
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 `fromEffectStream`. Its resources close
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 fromEffectEventStream<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<{
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 fromEffectEventStream<TEvent extends EventObject, TError = unknown, TInput = undefined, TRequirements = never>(config: EffectStreamConfig<TEvent, TError, TInput, TRequirements> & {
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 fromEffectEventStream<TEvent extends EventObject, TError = unknown, TInput = undefined, TRequirements = never>(stream: EffectStreamSource<TEvent, TError, TInput, TRequirements>): EffectActorLogic<undefined, TError, TInput, TRequirements>;
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 own `snapshot.error` instead.
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 { fromEffect, fromEffectEventStream, fromEffectStream, type EffectActorLogic, type EffectLogicBrand, type EffectSnapshot, type EffectSource, type EffectSourceArgs, type EffectStreamSource, type EffectStreamActorLogic, type EffectStreamSnapshot } from "./fromEffect.js";
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,2 @@
1
+ declare const _default: import("@oxlint/plugins").Plugin;
2
+ export default _default;
@@ -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`, `fromEffect`,
5
- * `fromEffectStream` and `fromEffectEventStream` convert it to a Standard
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
  */