@xstate/effect 1.0.0-beta.1 → 1.0.0-beta.2

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
@@ -264,12 +264,13 @@ See [atoms and React](docs/atoms-and-react.md) for complete examples with import
264
264
 
265
265
  ## Testing and errors
266
266
 
267
- <!-- error types from src/errors.ts; timeout failure from src/actor.ts -->
267
+ <!-- error types and causeFromError from src/errors.ts; timeout failure from src/actor.ts -->
268
268
 
269
269
  - Use `sendAndAwait` from `@xstate/effect` to acknowledge one event after its transition and immediate effects. It does not wait for child replies, invoked work, background actions or future timers.
270
270
  - Use `TestClock` to drive deadlines without waiting for real time.
271
271
  - `ActorStoppedError` reports a stop before an awaited result.
272
272
  - `EffectFailure<E>` carries the original `Cause<E>` in task and stream error snapshots, including defects and self-interruption.
273
+ - Use `causeFromError(error)` to obtain the original Cause from `EffectFailure` or wrap an ordinary error. Use `Cause.findErrorOption` to inspect its typed failure.
273
274
  - Timed waits fail with Effect's `Cause.TimeoutError`.
274
275
  - `join` replays the original Cause and preserves a task's typed failure. Unexpected machine errors have type `unknown`.
275
276
 
@@ -53,6 +53,18 @@ declare const NotReadyError_base: new <A extends Record<string, any> = {}>(args:
53
53
  export declare class NotReadyError extends NotReadyError_base {
54
54
  get message(): string;
55
55
  }
56
+ /**
57
+ * Returns an EffectFailure's original Cause, or wraps an ordinary error with
58
+ * Cause.fail. Defects and interruption in an existing Cause are preserved.
59
+ *
60
+ * @experimental
61
+ */
62
+ export declare function causeFromError<E>(error: E): Cause.Cause<UnwrapEffectFailure<E>>;
63
+ /**
64
+ * Extracts the typed failure from EffectFailure while retaining ordinary errors.
65
+ *
66
+ * @experimental
67
+ */
56
68
  export type UnwrapEffectFailure<E> = E extends EffectFailure<infer Failure> ? Failure : E;
57
69
  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 & {
58
70
  readonly _tag: "EventRejectedError";
@@ -4,7 +4,7 @@ export { EffectActor } from "./effectActor.js";
4
4
  export { sendAndAwait } from "./sendAndAwait.js";
5
5
  export { ActorScope, withActorScope } from "./actorScope.js";
6
6
  export { deadLetters, emitted, inspect, join, send, snapshots, waitFor, type EmittedEventFrom, type SendableEventFrom, type WaitForOptions } from "./actor.js";
7
- export { ActorStoppedError, EventRejectedError, EffectFailure, EffectInterruptedError } from "./errors.js";
7
+ export { ActorStoppedError, EventRejectedError, EffectFailure, EffectInterruptedError, causeFromError, type UnwrapEffectFailure } from "./errors.js";
8
8
  export { setupEffect, type EffectAction, type EffectActionArgs, type EffectSetupReturn } from "./setupEffect.js";
9
9
  export { type RequirementsFrom } from "./types.js";
10
10
  export { taggedState, type StateTag, type TaggedState, type TaggedStateFrom } from "./state.js";
@@ -79,10 +79,22 @@ class NotReadyError extends Data.TaggedError('NotReadyError') {
79
79
  }
80
80
  }
81
81
 
82
- /** @internal */
82
+ /**
83
+ * Returns an EffectFailure's original Cause, or wraps an ordinary error with
84
+ * Cause.fail. Defects and interruption in an existing Cause are preserved.
85
+ *
86
+ * @experimental
87
+ */
83
88
  function causeFromError(error) {
84
89
  return error instanceof EffectFailure ? error.cause : Cause.fail(error);
85
90
  }
91
+
92
+ /**
93
+ * Extracts the typed failure from EffectFailure while retaining ordinary errors.
94
+ *
95
+ * @experimental
96
+ */
97
+
86
98
  /**
87
99
  * An event rejected at the actor's delivery boundary.
88
100
  *
@@ -576,15 +588,16 @@ function closeEffectHost(host) {
576
588
  discard: true
577
589
  }).pipe(Effect.andThen(Scope.close(host.scope, Exit.void))));
578
590
  }
591
+ const EFFECT_FORWARD = 'xstate.effect.forward';
579
592
 
580
- /**
581
- * Delivers a stream item to the parent as an event. This runs outside a
582
- * transition, so it uses the system relay like core's observable logic does.
583
- */
593
+ /** Queues a stream item for the source actor to forward in its transition. */
584
594
  function relayToParent(actor, event) {
585
595
  const actorWithParent = actor;
586
596
  if (actorWithParent._parent) {
587
- actor.system._relay(actorWithParent, actorWithParent._parent, event);
597
+ actor.system._relay(actorWithParent, actorWithParent, {
598
+ type: EFFECT_FORWARD,
599
+ event
600
+ });
588
601
  }
589
602
  }
590
603
 
@@ -987,4 +1000,4 @@ function taggedState(snapshot) {
987
1000
  };
988
1001
  }
989
1002
 
990
- export { ActorStoppedError as A, EffectFailure as E, NotReadyError as N, causeFromError as a, runHostedEffect as b, createEffectActor as c, EffectActor as d, ActorScope as e, EventRejectedError as f, EffectInterruptedError as g, relayToParent as r, startHostedEffect as s, taggedState as t, withActorScope as w };
1003
+ export { ActorStoppedError as A, EFFECT_FORWARD as E, NotReadyError as N, causeFromError as a, EffectFailure as b, createEffectActor as c, runHostedEffect as d, EffectActor as e, ActorScope as f, EventRejectedError as g, EffectInterruptedError as h, relayToParent as r, startHostedEffect as s, taggedState as t, withActorScope as w };
@@ -1,8 +1,8 @@
1
1
  import { Cause } from 'effect';
2
2
  import { Atom, AsyncResult } from 'effect/reactivity';
3
3
  import { StateMachine } from 'xstate';
4
- import { c as createEffectActor, a as causeFromError, N as NotReadyError, t as taggedState } from './state-83513b45.js';
5
- export { N as NotReadyError } from './state-83513b45.js';
4
+ import { c as createEffectActor, a as causeFromError, N as NotReadyError, t as taggedState } from './state-54c1cda0.js';
5
+ export { N as NotReadyError } from './state-54c1cda0.js';
6
6
  import 'xstate/durable';
7
7
 
8
8
  /**
@@ -1,7 +1,7 @@
1
1
  import { Schema, Effect, Stream, Cause, Exit, Queue, Data } from 'effect';
2
2
  import { createStepLogic, setup } from 'xstate';
3
- import { s as startHostedEffect, r as relayToParent, E as EffectFailure, A as ActorStoppedError, a as causeFromError, b as runHostedEffect } from './state-83513b45.js';
4
- export { e as ActorScope, A as ActorStoppedError, d as EffectActor, E as EffectFailure, g as EffectInterruptedError, f as EventRejectedError, c as createEffectActor, t as taggedState, w as withActorScope } from './state-83513b45.js';
3
+ import { s as startHostedEffect, E as EFFECT_FORWARD, r as relayToParent, b as EffectFailure, A as ActorStoppedError, a as causeFromError, d as runHostedEffect } from './state-54c1cda0.js';
4
+ export { f as ActorScope, A as ActorStoppedError, e as EffectActor, b as EffectFailure, h as EffectInterruptedError, g as EventRejectedError, a as causeFromError, c as createEffectActor, t as taggedState, w as withActorScope } from './state-54c1cda0.js';
5
5
  export { s as sendAndAwait } from './sendAndAwait-bf1d4117.js';
6
6
  import { dual } from 'effect/Function';
7
7
  import 'xstate/durable';
@@ -390,7 +390,9 @@ function createEffectEventStreamLogic(sourceOrConfig) {
390
390
  const stream = config.stream;
391
391
  const logic = createStepLogic({
392
392
  id: config.id,
393
- validator: config.validator,
393
+ validator: config.validator && {
394
+ check: request => request.kind === 'event' && request.event.type === EFFECT_FORWARD ? undefined : config.validator.check(request)
395
+ },
394
396
  schemas: toLogicSchemas(config.schemas),
395
397
  context: undefined,
396
398
  run: ({
@@ -399,6 +401,10 @@ function createEffectEventStreamLogic(sourceOrConfig) {
399
401
  self,
400
402
  system
401
403
  }, enq) => {
404
+ if (event.type === EFFECT_FORWARD) {
405
+ enq.sendBack(event.event);
406
+ return;
407
+ }
402
408
  if (event.type === EFFECT_COMPLETE) {
403
409
  return {
404
410
  status: 'done',
@@ -434,6 +440,9 @@ function createEffectEventStreamLogic(sourceOrConfig) {
434
440
  }));
435
441
  }
436
442
  });
443
+ Object.assign(logic, {
444
+ isInternalEventType: eventType => eventType === EFFECT_FORWARD
445
+ });
437
446
  return brandLogic(logic, undefined, undefined);
438
447
  }
439
448
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xstate/effect",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "type": "module",
5
5
  "description": "Effect integration for XState",
6
6
  "keywords": [
@@ -33,14 +33,14 @@
33
33
  "peerDependencies": {
34
34
  "@oxlint/plugins": "^1.76.0",
35
35
  "effect": "^4.0.0",
36
- "xstate": "^6.0.0-beta.2"
36
+ "xstate": "^6.0.0-beta.3"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@oxlint/plugins": "^1.76.0",
40
40
  "effect": "4.0.6",
41
41
  "fast-check": "4.10.2",
42
42
  "@xstate/test": "^2.0.0-beta.1",
43
- "xstate": "^6.0.0-beta.2"
43
+ "xstate": "^6.0.0-beta.3"
44
44
  },
45
45
  "preconstruct": {
46
46
  "entrypoints": [