@xstate/effect 1.0.0-beta.0 → 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
- - Use `sendAndAwait` from `@xstate/effect/test` to acknowledge a specific event, including ignored events.
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
 
@@ -41,6 +41,9 @@ export declare class EffectActor<TLogic extends AnyActorLogic> implements ActorR
41
41
  private readonly _listeners;
42
42
  private _settled;
43
43
  private readonly _pending;
44
+ private _inStep;
45
+ private _deliveryDepth;
46
+ private _stopHeld;
44
47
  constructor(logic: TLogic, root: AnyActor, snapshot: SnapshotFrom<TLogic>, _mailbox: Queue.Queue<MailboxItem<EventFromLogic<TLogic>, SnapshotFrom<TLogic>>>, _stopExecution: () => void, _inspectors: Set<(event: InspectionEvent) => void>);
45
48
  getSnapshot(): SnapshotFrom<TLogic>;
46
49
  getPersistedSnapshot(): PersistedSnapshotOf<TLogic>;
@@ -48,13 +51,18 @@ export declare class EffectActor<TLogic extends AnyActorLogic> implements ActorR
48
51
  send(event: EventFromLogic<TLogic>): void;
49
52
  private _deliveryError;
50
53
  private _stoppedError;
51
- /** Stops the actor, its children and every Effect it hosts. */
54
+ /**
55
+ * Stops the actor, its children and every Effect it hosts. If a listener or
56
+ * an observer calls `stop()` while the actor delivers a macrostep to it, the
57
+ * actor stops when that macrostep ends. `createActor` does the same.
58
+ */
52
59
  stop(): this;
53
60
  subscribe(observer: Observer<SnapshotFrom<TLogic>>): Subscription;
54
61
  subscribe(nextListener?: (snapshot: SnapshotFrom<TLogic>) => void, errorListener?: (error: any) => void, completeListener?: () => void): Subscription;
55
62
  on<TType extends EmittedFrom<TLogic>['type'] | '*'>(type: TType, handler: (emitted: EmittedFrom<TLogic> & (TType extends '*' ? unknown : {
56
63
  type: TType;
57
64
  })) => void): Subscription;
65
+ private _deliver;
58
66
  /**
59
67
  * Observes the inspection events of this actor and its children: every
60
68
  * actor creation and transition of the execution.
@@ -62,7 +70,7 @@ export declare class EffectActor<TLogic extends AnyActorLogic> implements ActorR
62
70
  inspect(observer: Observer<InspectionEvent> | ((event: InspectionEvent) => void)): Subscription;
63
71
  [symbolObservable](): this;
64
72
  toJSON(): {
65
- $actor: string;
73
+ '@actor': string;
66
74
  };
67
75
  }
68
76
  export {};
@@ -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";
@@ -1,9 +1,10 @@
1
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
+ export { sendAndAwait } from "./sendAndAwait.js";
4
5
  export { ActorScope, withActorScope } from "./actorScope.js";
5
6
  export { deadLetters, emitted, inspect, join, send, snapshots, waitFor, type EmittedEventFrom, type SendableEventFrom, type WaitForOptions } from "./actor.js";
6
- export { ActorStoppedError, EventRejectedError, EffectFailure, EffectInterruptedError } from "./errors.js";
7
+ export { ActorStoppedError, EventRejectedError, EffectFailure, EffectInterruptedError, causeFromError, type UnwrapEffectFailure } from "./errors.js";
7
8
  export { setupEffect, type EffectAction, type EffectActionArgs, type EffectSetupReturn } from "./setupEffect.js";
8
9
  export { type RequirementsFrom } from "./types.js";
9
10
  export { taggedState, type StateTag, type TaggedState, type TaggedStateFrom } from "./state.js";
@@ -0,0 +1,14 @@
1
+ import type { Effect } from 'effect';
2
+ import type { AnyActorLogic, EventFromLogic, SnapshotFrom } from 'xstate';
3
+ import type { EffectActor } from "./effectActor.js";
4
+ import type { ActorStoppedError, EventRejectedError } from "./errors.js";
5
+ /**
6
+ * Sends one event and acknowledges its transition and immediate effects.
7
+ * Ignored events are acknowledged too, as are events whose actions fail; the
8
+ * failure is processed afterwards. Child replies, invoked work, background
9
+ * action bodies and future timers are not awaited. Interrupting the waiter
10
+ * detaches its receipt without retracting the event.
11
+ *
12
+ * @experimental
13
+ */
14
+ export declare function sendAndAwait<TLogic extends AnyActorLogic>(actor: EffectActor<TLogic>, event: EventFromLogic<TLogic>): Effect.Effect<SnapshotFrom<TLogic>, ActorStoppedError | EventRejectedError>;
@@ -1,13 +1 @@
1
- import type { Effect } from 'effect';
2
- import type { AnyActorLogic, EventFromLogic, SnapshotFrom } from 'xstate';
3
- import type { EffectActor } from "./effectActor.js";
4
- import type { ActorStoppedError, EventRejectedError } from "./errors.js";
5
- /**
6
- * Sends one mailbox item and awaits its macrostep publication and immediate
7
- * effect scheduling. Ignored events are acknowledged too. Does not await
8
- * invoked work, background actions or future timers. Interrupting the waiter
9
- * detaches its receipt without retracting the event.
10
- *
11
- * @experimental
12
- */
13
- export declare function sendAndAwait<TLogic extends AnyActorLogic>(actor: EffectActor<TLogic>, event: EventFromLogic<TLogic>): Effect.Effect<SnapshotFrom<TLogic>, ActorStoppedError | EventRejectedError>;
1
+ export { sendAndAwait } from "./sendAndAwait.js";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Sends one event and acknowledges its transition and immediate effects.
3
+ * Ignored events are acknowledged too, as are events whose actions fail; the
4
+ * failure is processed afterwards. Child replies, invoked work, background
5
+ * action bodies and future timers are not awaited. Interrupting the waiter
6
+ * detaches its receipt without retracting the event.
7
+ *
8
+ * @experimental
9
+ */
10
+ function sendAndAwait(actor, event) {
11
+ return actor._sendAndAwait(event);
12
+ }
13
+
14
+ export { sendAndAwait as s };
@@ -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
  *
@@ -90,6 +102,8 @@ function causeFromError(error) {
90
102
  */
91
103
  class EventRejectedError extends Data.TaggedError('EventRejectedError') {}
92
104
 
105
+ /** @internal */
106
+
93
107
  const symbolObservable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();
94
108
 
95
109
  /**
@@ -142,6 +156,9 @@ class EffectActor {
142
156
  this._listeners = new Map();
143
157
  this._settled = false;
144
158
  this._pending = new Set();
159
+ this._inStep = false;
160
+ this._deliveryDepth = 0;
161
+ this._stopHeld = false;
145
162
  this._root = root;
146
163
  this.id = root.id;
147
164
  this.address = root.address;
@@ -203,11 +220,6 @@ class EffectActor {
203
220
  }
204
221
  return undefined;
205
222
  }
206
-
207
- /** @internal */
208
- _rejectReceipts() {
209
- for (const receipt of this._pending) this._completeReceipt(receipt, Exit.fail(this._stoppedError()));
210
- }
211
223
  _stoppedError() {
212
224
  return new ActorStoppedError({
213
225
  actorId: this.id,
@@ -223,8 +235,16 @@ class EffectActor {
223
235
  resume?.(exit);
224
236
  }
225
237
 
226
- /** Stops the actor, its children and every Effect it hosts. */
238
+ /**
239
+ * Stops the actor, its children and every Effect it hosts. If a listener or
240
+ * an observer calls `stop()` while the actor delivers a macrostep to it, the
241
+ * actor stops when that macrostep ends. `createActor` does the same.
242
+ */
227
243
  stop() {
244
+ if (this._inStep && this._deliveryDepth > 0) {
245
+ this._stopHeld = true;
246
+ return this;
247
+ }
228
248
  this._stopExecution();
229
249
  return this;
230
250
  }
@@ -265,9 +285,20 @@ class EffectActor {
265
285
 
266
286
  /** @internal Delivers an emitted event to `on` listeners. */
267
287
  _emit(event) {
288
+ if (this._settled) return;
268
289
  const listeners = [...(this._listeners.get(event.type) ?? []), ...(this._listeners.get('*') ?? [])];
269
- for (const listener of listeners) {
270
- safeCall(listener, event);
290
+ this._deliver(() => {
291
+ for (const listener of listeners) {
292
+ safeCall(listener, event);
293
+ }
294
+ });
295
+ }
296
+ _deliver(notify) {
297
+ this._deliveryDepth++;
298
+ try {
299
+ notify();
300
+ } finally {
301
+ this._deliveryDepth--;
271
302
  }
272
303
  }
273
304
 
@@ -289,48 +320,66 @@ class EffectActor {
289
320
  }
290
321
  toJSON() {
291
322
  return {
292
- $actor: this.address
323
+ '@actor': this.address
293
324
  };
294
325
  }
295
326
 
296
- /** @internal Publishes a snapshot produced by the execution loop. */
297
- _publish(snapshot, receipt) {
298
- if (this._settled) return;
299
- this._snapshot = snapshot;
300
- const status = snapshot.status;
301
- if (status === 'active') {
302
- for (const observer of this._observers) {
303
- safeCall(observer.next, snapshot);
327
+ /** @internal */
328
+ _runStep(step) {
329
+ return Effect.gen({
330
+ self: this
331
+ }, function* () {
332
+ if (this._settled) return;
333
+ this._snapshot = step.snapshot;
334
+ this._inStep = true;
335
+ yield* step.effects;
336
+ if (this._settled) return;
337
+ if (this._snapshot.status !== 'active') {
338
+ this._settle(this._snapshot, step.kind === 'transition' ? step.receipt : undefined);
339
+ } else if (step.kind === 'transition') {
340
+ this._deliver(() => {
341
+ for (const observer of this._observers) {
342
+ safeCall(observer.next, this._snapshot);
343
+ }
344
+ });
304
345
  }
305
- return;
306
- }
307
- this._settle(snapshot, receipt);
346
+ }).pipe(Effect.onExit(exit => Effect.sync(() => {
347
+ this._inStep = false;
348
+ if (this._stopHeld) {
349
+ this._stopExecution();
350
+ } else if (step.kind === 'transition' && Exit.isSuccess(exit)) {
351
+ this._completeReceipt(step.receipt, step.rejection ? Exit.fail(step.rejection) : Exit.succeed(step.snapshot));
352
+ }
353
+ })));
308
354
  }
309
355
 
310
- /** @internal Marks the actor stopped and notifies observers. */
356
+ /** @internal Settles the actor and notifies its observers. */
311
357
  _settle(snapshot, processing) {
312
358
  if (this._settled) {
313
359
  return;
314
360
  }
315
361
  this._snapshot = snapshot;
316
362
  this._settled = true;
317
- for (const receipt of this._pending) {
318
- if (receipt !== processing) this._completeReceipt(receipt, Exit.fail(this._stoppedError()));
319
- }
320
363
  const observers = [...this._observers];
321
364
  this._observers.clear();
322
365
  const status = snapshot.status;
323
- if (status === 'done') {
324
- for (const observer of observers) {
325
- safeCall(observer.next, snapshot);
366
+ this._deliver(() => {
367
+ if (status === 'done') {
368
+ for (const observer of observers) {
369
+ safeCall(observer.next, snapshot);
370
+ }
326
371
  }
327
- }
328
- for (const observer of observers) {
329
- if (status === 'error') {
330
- safeCall(observer.error, snapshot.error);
331
- } else {
332
- safeCall(observer.complete);
372
+ for (const observer of observers) {
373
+ if (status === 'error') {
374
+ safeCall(observer.error, snapshot.error);
375
+ } else {
376
+ safeCall(observer.complete);
377
+ }
333
378
  }
379
+ });
380
+ this._completeReceipt(processing, Exit.succeed(snapshot));
381
+ for (const receipt of this._pending) {
382
+ this._completeReceipt(receipt, Exit.fail(this._stoppedError()));
334
383
  }
335
384
  }
336
385
 
@@ -539,15 +588,16 @@ function closeEffectHost(host) {
539
588
  discard: true
540
589
  }).pipe(Effect.andThen(Scope.close(host.scope, Exit.void))));
541
590
  }
591
+ const EFFECT_FORWARD = 'xstate.effect.forward';
542
592
 
543
- /**
544
- * Delivers a stream item to the parent as an event. This runs outside a
545
- * transition, so it uses the system relay like core's observable logic does.
546
- */
593
+ /** Queues a stream item for the source actor to forward in its transition. */
547
594
  function relayToParent(actor, event) {
548
595
  const actorWithParent = actor;
549
596
  if (actorWithParent._parent) {
550
- actor.system._relay(actorWithParent, actorWithParent._parent, event);
597
+ actor.system._relay(actorWithParent, actorWithParent, {
598
+ type: EFFECT_FORWARD,
599
+ event
600
+ });
551
601
  }
552
602
  }
553
603
 
@@ -761,12 +811,12 @@ function createEffectActor(logic, ...[options]) {
761
811
  }
762
812
  runFork(Queue.shutdown(mailbox));
763
813
  if (!actor._isSettled) {
764
- actor._settle({
765
- ...actor.getSnapshot(),
814
+ const current = actor.getSnapshot();
815
+ actor._settle(current.status === 'active' ? {
816
+ ...current,
766
817
  status: 'stopped'
767
- });
818
+ } : current);
768
819
  }
769
- actor._rejectReceipts();
770
820
  closeEffectHost(host);
771
821
  };
772
822
 
@@ -805,7 +855,11 @@ function createEffectActor(logic, ...[options]) {
805
855
  });
806
856
  }));
807
857
  const loop = Effect.gen(function* () {
808
- yield* executeEffects(effects);
858
+ yield* actor._runStep({
859
+ kind: 'start',
860
+ snapshot,
861
+ effects: executeEffects(effects)
862
+ });
809
863
  while (snapshot.status === 'active' && !stopped) {
810
864
  delivered = undefined;
811
865
  const received = yield* Effect.promise(() => durable.waitForEvent().catch(() => undefined));
@@ -820,6 +874,11 @@ function createEffectActor(logic, ...[options]) {
820
874
  const errorEvent = logic.getExecutionErrorEvent?.(snapshot, item.error);
821
875
  if (!errorEvent) {
822
876
  snapshot = errorSnapshot(snapshot, item.error);
877
+ yield* actor._runStep({
878
+ kind: 'transition',
879
+ snapshot,
880
+ effects: Effect.void
881
+ });
823
882
  break;
824
883
  }
825
884
  event = errorEvent;
@@ -830,22 +889,28 @@ function createEffectActor(logic, ...[options]) {
830
889
  [snapshot, effects] = durable.transition(snapshot, event);
831
890
  } catch (error) {
832
891
  snapshot = errorSnapshot(snapshot, error);
833
- actor._publish(snapshot, receipt);
834
- actor._completeReceipt(receipt, Exit.succeed(snapshot));
892
+ yield* actor._runStep({
893
+ kind: 'transition',
894
+ snapshot,
895
+ effects: Effect.void,
896
+ receipt
897
+ });
835
898
  break;
836
899
  }
837
- actor._publish(snapshot, receipt);
838
- if (stopped) break;
839
900
  const rejectedReason = effects.length === 1 && effects[0].effect.type === '@xstate.deadLetter' && 'reason' in effects[0].effect && effects[0].effect.reason === 'invalidEvent' ? effects[0].effect.reason : undefined;
840
- yield* executeEffects(effects);
841
- actor._completeReceipt(receipt, rejectedReason !== undefined ? Exit.fail(new EventRejectedError({
842
- actorId: actor.id,
843
- event,
844
- reason: rejectedReason
845
- })) : Exit.succeed(snapshot));
901
+ yield* actor._runStep({
902
+ kind: 'transition',
903
+ snapshot,
904
+ effects: executeEffects(effects),
905
+ receipt,
906
+ rejection: rejectedReason !== undefined ? new EventRejectedError({
907
+ actorId: actor.id,
908
+ event,
909
+ reason: rejectedReason
910
+ }) : undefined
911
+ });
846
912
  }
847
913
  if (!stopped) {
848
- actor._publish(snapshot);
849
914
  if (snapshot.status !== 'active') {
850
915
  stopChildren(snapshot);
851
916
  closeEffectHost(host);
@@ -855,13 +920,14 @@ function createEffectActor(logic, ...[options]) {
855
920
  yield* Effect.forkIn(loop, actorScope);
856
921
  return {
857
922
  actor,
858
- host
923
+ host,
924
+ stopNow: stop
859
925
  };
860
926
  }), ({
861
- actor,
862
- host
927
+ host,
928
+ stopNow
863
929
  }) => Effect.gen(function* () {
864
- actor.stop();
930
+ stopNow();
865
931
  if (host.closing) {
866
932
  yield* Fiber.join(host.closing);
867
933
  } else {
@@ -934,4 +1000,4 @@ function taggedState(snapshot) {
934
1000
  };
935
1001
  }
936
1002
 
937
- 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-a2154dce.js';
5
- export { N as NotReadyError } from './state-a2154dce.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,13 +1 @@
1
- /**
2
- * Sends one mailbox item and awaits its macrostep publication and immediate
3
- * effect scheduling. Ignored events are acknowledged too. Does not await
4
- * invoked work, background actions or future timers. Interrupting the waiter
5
- * detaches its receipt without retracting the event.
6
- *
7
- * @experimental
8
- */
9
- function sendAndAwait(actor, event) {
10
- return actor._sendAndAwait(event);
11
- }
12
-
13
- export { sendAndAwait };
1
+ export { s as sendAndAwait } from './sendAndAwait-bf1d4117.js';
@@ -1,7 +1,8 @@
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-a2154dce.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-a2154dce.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
+ export { s as sendAndAwait } from './sendAndAwait-bf1d4117.js';
5
6
  import { dual } from 'effect/Function';
6
7
  import 'xstate/durable';
7
8
 
@@ -389,7 +390,9 @@ function createEffectEventStreamLogic(sourceOrConfig) {
389
390
  const stream = config.stream;
390
391
  const logic = createStepLogic({
391
392
  id: config.id,
392
- validator: config.validator,
393
+ validator: config.validator && {
394
+ check: request => request.kind === 'event' && request.event.type === EFFECT_FORWARD ? undefined : config.validator.check(request)
395
+ },
393
396
  schemas: toLogicSchemas(config.schemas),
394
397
  context: undefined,
395
398
  run: ({
@@ -398,6 +401,10 @@ function createEffectEventStreamLogic(sourceOrConfig) {
398
401
  self,
399
402
  system
400
403
  }, enq) => {
404
+ if (event.type === EFFECT_FORWARD) {
405
+ enq.sendBack(event.event);
406
+ return;
407
+ }
401
408
  if (event.type === EFFECT_COMPLETE) {
402
409
  return {
403
410
  status: 'done',
@@ -433,6 +440,9 @@ function createEffectEventStreamLogic(sourceOrConfig) {
433
440
  }));
434
441
  }
435
442
  });
443
+ Object.assign(logic, {
444
+ isInternalEventType: eventType => eventType === EFFECT_FORWARD
445
+ });
436
446
  return brandLogic(logic, undefined, undefined);
437
447
  }
438
448
 
@@ -748,6 +758,12 @@ function wrapActions(actions) {
748
758
  }
749
759
  return Effect.isEffect(result) ? runHostedEffect(args.self, result, `action.${key}`) : result;
750
760
  };
761
+ // Built-in effect descriptors use these reserved prefixes.
762
+ if (!key.startsWith('xstate.') && !key.startsWith('@xstate.')) {
763
+ Object.defineProperty(wrapped[key], 'name', {
764
+ value: key
765
+ });
766
+ }
751
767
  }
752
768
  return wrapped;
753
769
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xstate/effect",
3
- "version": "1.0.0-beta.0",
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.0"
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
- "@xstate/test": "^2.0.0-beta.0",
43
- "xstate": "^6.0.0-beta.0"
42
+ "@xstate/test": "^2.0.0-beta.1",
43
+ "xstate": "^6.0.0-beta.3"
44
44
  },
45
45
  "preconstruct": {
46
46
  "entrypoints": [