effect-machine 0.9.0 → 0.10.0
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 +76 -16
- package/dist/actor.d.ts +55 -89
- package/dist/actor.js +135 -30
- package/dist/cluster/entity-machine.js +5 -3
- package/dist/errors.d.ts +12 -1
- package/dist/errors.js +8 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/internal/transition.d.ts +26 -2
- package/dist/internal/transition.js +37 -8
- package/dist/internal/utils.d.ts +7 -2
- package/dist/machine.d.ts +66 -3
- package/dist/machine.js +65 -0
- package/dist/persistence/persistent-actor.js +52 -16
- package/dist/testing.js +57 -3
- package/package.json +9 -8
- package/{dist-v3 → v3/dist}/actor.d.ts +65 -78
- package/{dist-v3 → v3/dist}/actor.js +173 -35
- package/{dist-v3 → v3/dist}/cluster/entity-machine.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/entity-machine.js +8 -6
- package/{dist-v3 → v3/dist}/cluster/to-entity.d.ts +1 -1
- package/{dist-v3 → v3/dist}/cluster/to-entity.js +1 -1
- package/v3/dist/errors.d.ts +76 -0
- package/{dist-v3 → v3/dist}/errors.js +9 -2
- package/v3/dist/index.d.ts +13 -0
- package/v3/dist/index.js +13 -0
- package/{dist-v3 → v3/dist}/inspection.d.ts +53 -8
- package/v3/dist/inspection.js +156 -0
- package/{dist-v3 → v3/dist}/internal/brands.d.ts +1 -1
- package/{dist-v3 → v3/dist}/internal/inspection.d.ts +1 -1
- package/v3/dist/internal/inspection.js +20 -0
- package/{dist-v3 → v3/dist}/internal/transition.d.ts +35 -11
- package/{dist-v3 → v3/dist}/internal/transition.js +47 -15
- package/{dist-v3 → v3/dist}/internal/utils.d.ts +9 -4
- package/{dist-v3 → v3/dist}/internal/utils.js +1 -1
- package/{dist-v3 → v3/dist}/machine.d.ts +86 -9
- package/{dist-v3 → v3/dist}/machine.js +128 -2
- package/{dist-v3 → v3/dist}/persistence/adapter.d.ts +18 -5
- package/{dist-v3 → v3/dist}/persistence/adapter.js +1 -1
- package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.d.ts +1 -1
- package/{dist-v3 → v3/dist}/persistence/adapters/in-memory.js +1 -1
- package/{dist-v3 → v3/dist}/persistence/persistent-actor.d.ts +7 -6
- package/{dist-v3 → v3/dist}/persistence/persistent-actor.js +58 -19
- package/{dist-v3 → v3/dist}/persistence/persistent-machine.d.ts +1 -1
- package/{dist-v3 → v3/dist}/persistence/persistent-machine.js +1 -1
- package/{dist-v3 → v3/dist}/schema.d.ts +1 -1
- package/{dist-v3 → v3/dist}/schema.js +5 -2
- package/{dist-v3 → v3/dist}/slot.d.ts +4 -3
- package/{dist-v3 → v3/dist}/slot.js +1 -1
- package/{dist-v3 → v3/dist}/testing.d.ts +14 -8
- package/{dist-v3 → v3/dist}/testing.js +60 -6
- package/dist-v3/errors.d.ts +0 -27
- package/dist-v3/index.d.ts +0 -12
- package/dist-v3/index.js +0 -13
- package/dist-v3/inspection.js +0 -48
- package/dist-v3/internal/inspection.js +0 -13
- /package/{dist-v3 → v3/dist}/_virtual/_rolldown/runtime.js +0 -0
- /package/{dist-v3 → v3/dist}/cluster/index.d.ts +0 -0
- /package/{dist-v3 → v3/dist}/cluster/index.js +0 -0
- /package/{dist-v3 → v3/dist}/internal/brands.js +0 -0
- /package/{dist-v3 → v3/dist}/persistence/index.d.ts +0 -0
- /package/{dist-v3 → v3/dist}/persistence/index.js +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { EffectHandlers, EffectSlot, EffectSlots, EffectsDef, EffectsSchema, GuardHandlers, GuardSlot, GuardSlots, GuardsDef, GuardsSchema, MachineContext, Slot } from "./slot.js";
|
|
2
2
|
import { Event, MachineEventSchema, MachineStateSchema, State } from "./schema.js";
|
|
3
3
|
import { PersistenceConfig, PersistentMachine, isPersistentMachine } from "./persistence/persistent-machine.js";
|
|
4
|
-
import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
|
|
4
|
+
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
|
|
5
5
|
import { ProcessEventResult } from "./internal/transition.js";
|
|
6
6
|
import { PersistentActorRef, createPersistentActor, restorePersistentActor } from "./persistence/persistent-actor.js";
|
|
7
7
|
import { ActorMetadata, PersistedEvent, PersistenceAdapter, PersistenceAdapterTag, PersistenceError, RestoreFailure, RestoreResult, Snapshot, VersionConflictError } from "./persistence/adapter.js";
|
|
8
8
|
import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
|
|
9
9
|
import { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, ProvideHandlers, SpawnEffect, StateHandlerContext, TaskOptions, Transition, machine_d_exports } from "./machine.js";
|
|
10
|
-
import { ActorRef, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
|
|
10
|
+
import { ActorRef, ActorRefSync, ActorSystem, Default, SystemEvent, SystemEventListener } from "./actor.js";
|
|
11
11
|
import { SimulationResult, TestHarness, TestHarnessOptions, assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
12
12
|
import { AnyInspectionEvent, EffectEvent, ErrorEvent, EventReceivedEvent, InspectionEvent, Inspector, InspectorHandler, SpawnEvent, StopEvent, TaskEvent, TracingInspectorOptions, TransitionEvent, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
13
|
-
export { type ActorMetadata, type ActorRef, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type BuiltMachine, DuplicateActorError, type EffectEvent, type EffectSlots, type EffectsDef, type EffectsSchema, type ErrorEvent, Event, type EventReceivedEvent, type GuardHandlers, type GuardSlot, type GuardSlots, type GuardsDef, type GuardsSchema, type HandlerContext, InMemoryPersistenceAdapter, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, machine_d_exports as Machine, type MachineContext, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, MissingSchemaError, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, type ProcessEventResult, type ProvideHandlers, ProvisionValidationError, type RestoreFailure, type RestoreResult, type SimulationResult, Slot, type EffectHandlers as SlotEffectHandlers, type EffectSlot as SlotEffectSlot, SlotProvisionError, type Snapshot, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TracingInspectorOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
|
|
13
|
+
export { type ActorMetadata, type ActorRef, type ActorRefSync, ActorStoppedError, type ActorSystem, Default as ActorSystemDefault, ActorSystem as ActorSystemService, type AnyInspectionEvent, AssertionError, type BackgroundEffect, type BuiltMachine, DuplicateActorError, type EffectEvent, type EffectSlots, type EffectsDef, type EffectsSchema, type ErrorEvent, Event, type EventReceivedEvent, type GuardHandlers, type GuardSlot, type GuardSlots, type GuardsDef, type GuardsSchema, type HandlerContext, InMemoryPersistenceAdapter, type InspectionEvent, type Inspector, type InspectorHandler, Inspector as InspectorService, InvalidSchemaError, machine_d_exports as Machine, type MachineContext, type MachineEventSchema, type MachineRef, type MachineStateSchema, type Machine as MachineType, type MakeConfig, MissingMatchHandlerError, MissingSchemaError, NoReplyError, type PersistOptions, type PersistedEvent, type PersistenceAdapter, PersistenceAdapterTag, type PersistenceConfig, PersistenceError, type PersistentActorRef, type PersistentMachine, type ProcessEventResult, type ProvideHandlers, ProvisionValidationError, type RestoreFailure, type RestoreResult, type SimulationResult, Slot, type EffectHandlers as SlotEffectHandlers, type EffectSlot as SlotEffectSlot, SlotProvisionError, type Snapshot, type SpawnEffect, type SpawnEvent, State, type StateHandlerContext, type StopEvent, type SystemEvent, type SystemEventListener, type TaskEvent, type TaskOptions, type TestHarness, type TestHarnessOptions, type TracingInspectorOptions, type Transition, type TransitionEvent, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Inspector, collectingInspector, combineInspectors, consoleInspector, makeInspector, makeInspectorEffect, tracingInspector } from "./inspection.js";
|
|
2
|
-
import { AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
|
|
2
|
+
import { ActorStoppedError, AssertionError, DuplicateActorError, InvalidSchemaError, MissingMatchHandlerError, MissingSchemaError, NoReplyError, ProvisionValidationError, SlotProvisionError, UnprovidedSlotsError } from "./errors.js";
|
|
3
3
|
import { isPersistentMachine } from "./persistence/persistent-machine.js";
|
|
4
4
|
import { Slot } from "./slot.js";
|
|
5
5
|
import { machine_exports } from "./machine.js";
|
|
@@ -10,4 +10,4 @@ import { Event, State } from "./schema.js";
|
|
|
10
10
|
import { assertNeverReaches, assertPath, assertReaches, createTestHarness, simulate } from "./testing.js";
|
|
11
11
|
import { InMemoryPersistenceAdapter, makeInMemoryPersistenceAdapter } from "./persistence/adapters/in-memory.js";
|
|
12
12
|
import "./persistence/index.js";
|
|
13
|
-
export { Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
|
|
13
|
+
export { ActorStoppedError, Default as ActorSystemDefault, ActorSystem as ActorSystemService, AssertionError, DuplicateActorError, Event, InMemoryPersistenceAdapter, Inspector as InspectorService, InvalidSchemaError, machine_exports as Machine, MissingMatchHandlerError, MissingSchemaError, NoReplyError, PersistenceAdapterTag, PersistenceError, ProvisionValidationError, Slot, SlotProvisionError, State, UnprovidedSlotsError, VersionConflictError, assertNeverReaches, assertPath, assertReaches, collectingInspector, combineInspectors, consoleInspector, createPersistentActor, createTestHarness, isPersistentMachine, makeInMemoryPersistenceAdapter, makeInspector, makeInspectorEffect, restorePersistentActor, simulate, tracingInspector };
|
|
@@ -29,7 +29,11 @@ declare const runTransitionHandler: <S extends {
|
|
|
29
29
|
readonly _tag: string;
|
|
30
30
|
}, E extends {
|
|
31
31
|
readonly _tag: string;
|
|
32
|
-
}, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<
|
|
32
|
+
}, R, GD extends GuardsDef, EFD extends EffectsDef>(machine: Machine<S, E, R, Record<string, never>, Record<string, never>, GD, EFD>, transition: Transition<S, E, GD, EFD, R>, state: S, event: E, self: MachineRef<E>, system: ActorSystem, actorId: string) => Effect.Effect<{
|
|
33
|
+
newState: S;
|
|
34
|
+
hasReply: boolean;
|
|
35
|
+
reply: unknown;
|
|
36
|
+
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
33
37
|
/**
|
|
34
38
|
* Execute a transition for a given state and event.
|
|
35
39
|
* Handles transition resolution, handler invocation, and guard/effect slot creation.
|
|
@@ -49,6 +53,8 @@ declare const executeTransition: <S extends {
|
|
|
49
53
|
newState: S;
|
|
50
54
|
transitioned: boolean;
|
|
51
55
|
reenter: boolean;
|
|
56
|
+
hasReply: boolean;
|
|
57
|
+
reply: unknown;
|
|
52
58
|
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>>>;
|
|
53
59
|
/**
|
|
54
60
|
* Optional hooks for event processing inspection/tracing.
|
|
@@ -84,7 +90,22 @@ interface ProcessEventResult<S> {
|
|
|
84
90
|
readonly lifecycleRan: boolean;
|
|
85
91
|
/** Whether new state is final */
|
|
86
92
|
readonly isFinal: boolean;
|
|
93
|
+
/** Whether the handler provided a reply (structural, not value-based) */
|
|
94
|
+
readonly hasReply: boolean;
|
|
95
|
+
/** Domain reply value from handler (used by ask). Only meaningful when hasReply is true. */
|
|
96
|
+
readonly reply?: unknown;
|
|
97
|
+
/** Whether the event was postponed (buffered for retry after next state change) */
|
|
98
|
+
readonly postponed: boolean;
|
|
87
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if an event should be postponed in the current state.
|
|
102
|
+
* @internal
|
|
103
|
+
*/
|
|
104
|
+
declare const shouldPostpone: <S extends {
|
|
105
|
+
readonly _tag: string;
|
|
106
|
+
}, E extends {
|
|
107
|
+
readonly _tag: string;
|
|
108
|
+
}, R>(machine: Machine<S, E, R, any, any, any, any>, stateTag: string, eventTag: string) => boolean;
|
|
88
109
|
/**
|
|
89
110
|
* Process a single event through the machine.
|
|
90
111
|
*
|
|
@@ -109,6 +130,9 @@ declare const processEventCore: <S extends {
|
|
|
109
130
|
transitioned: boolean;
|
|
110
131
|
lifecycleRan: boolean;
|
|
111
132
|
isFinal: boolean;
|
|
133
|
+
hasReply: boolean;
|
|
134
|
+
reply: unknown;
|
|
135
|
+
postponed: boolean;
|
|
112
136
|
}, never, Exclude<R, MachineContext<S, E, MachineRef<E>>> | Exclude<Exclude<R, MachineContext<S, E, MachineRef<E>>>, Scope.Scope>>;
|
|
113
137
|
/**
|
|
114
138
|
* Run spawn effects for a state (forked into state scope, auto-cancelled on state exit).
|
|
@@ -157,4 +181,4 @@ declare const findSpawnEffects: <S extends {
|
|
|
157
181
|
readonly _tag: string;
|
|
158
182
|
}, R, GD extends GuardsDef = Record<string, never>, EFD extends EffectsDef = Record<string, never>>(machine: Machine<S, E, R, any, any, GD, EFD>, stateTag: string) => ReadonlyArray<SpawnEffect<S, E, EFD, R>>;
|
|
159
183
|
//#endregion
|
|
160
|
-
export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler };
|
|
184
|
+
export { ProcessEventError, ProcessEventHooks, ProcessEventResult, TransitionExecutionResult, executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
|
|
@@ -37,8 +37,18 @@ const runTransitionHandler = Effect.fn("effect-machine.runTransitionHandler")(fu
|
|
|
37
37
|
guards,
|
|
38
38
|
effects
|
|
39
39
|
};
|
|
40
|
-
const
|
|
41
|
-
|
|
40
|
+
const raw = transition.handler(handlerCtx);
|
|
41
|
+
const resolved = isEffect(raw) ? yield* raw.pipe(Effect.provideService(machine.Context, ctx)) : raw;
|
|
42
|
+
if (resolved !== null && typeof resolved === "object" && "state" in resolved && "reply" in resolved && !("_tag" in resolved)) return {
|
|
43
|
+
newState: resolved.state,
|
|
44
|
+
hasReply: true,
|
|
45
|
+
reply: resolved.reply
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
newState: resolved,
|
|
49
|
+
hasReply: false,
|
|
50
|
+
reply: void 0
|
|
51
|
+
};
|
|
42
52
|
});
|
|
43
53
|
/**
|
|
44
54
|
* Execute a transition for a given state and event.
|
|
@@ -56,15 +66,28 @@ const executeTransition = Effect.fn("effect-machine.executeTransition")(function
|
|
|
56
66
|
if (transition === void 0) return {
|
|
57
67
|
newState: currentState,
|
|
58
68
|
transitioned: false,
|
|
59
|
-
reenter: false
|
|
69
|
+
reenter: false,
|
|
70
|
+
hasReply: false,
|
|
71
|
+
reply: void 0
|
|
60
72
|
};
|
|
73
|
+
const { newState, hasReply, reply } = yield* runTransitionHandler(machine, transition, currentState, event, self, system, actorId);
|
|
61
74
|
return {
|
|
62
|
-
newState
|
|
75
|
+
newState,
|
|
63
76
|
transitioned: true,
|
|
64
|
-
reenter: transition.reenter === true
|
|
77
|
+
reenter: transition.reenter === true,
|
|
78
|
+
hasReply,
|
|
79
|
+
reply
|
|
65
80
|
};
|
|
66
81
|
});
|
|
67
82
|
/**
|
|
83
|
+
* Check if an event should be postponed in the current state.
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
const shouldPostpone = (machine, stateTag, eventTag) => {
|
|
87
|
+
for (const rule of machine.postponeRules) if (rule.stateTag === stateTag && rule.eventTag === eventTag) return true;
|
|
88
|
+
return false;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
68
91
|
* Process a single event through the machine.
|
|
69
92
|
*
|
|
70
93
|
* Handles:
|
|
@@ -93,7 +116,10 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
|
|
|
93
116
|
previousState: currentState,
|
|
94
117
|
transitioned: false,
|
|
95
118
|
lifecycleRan: false,
|
|
96
|
-
isFinal: false
|
|
119
|
+
isFinal: false,
|
|
120
|
+
hasReply: false,
|
|
121
|
+
reply: void 0,
|
|
122
|
+
postponed: false
|
|
97
123
|
};
|
|
98
124
|
const newState = result.newState;
|
|
99
125
|
const runLifecycle = newState._tag !== currentState._tag || result.reenter;
|
|
@@ -109,7 +135,10 @@ const processEventCore = Effect.fn("effect-machine.processEventCore")(function*
|
|
|
109
135
|
previousState: currentState,
|
|
110
136
|
transitioned: true,
|
|
111
137
|
lifecycleRan: runLifecycle,
|
|
112
|
-
isFinal: machine.finalStates.has(newState._tag)
|
|
138
|
+
isFinal: machine.finalStates.has(newState._tag),
|
|
139
|
+
hasReply: result.hasReply,
|
|
140
|
+
reply: result.reply,
|
|
141
|
+
postponed: false
|
|
113
142
|
};
|
|
114
143
|
});
|
|
115
144
|
/**
|
|
@@ -236,4 +265,4 @@ const findSpawnEffects = (machine, stateTag) => {
|
|
|
236
265
|
return getIndex(machine).spawn.get(stateTag) ?? [];
|
|
237
266
|
};
|
|
238
267
|
//#endregion
|
|
239
|
-
export { executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler };
|
|
268
|
+
export { executeTransition, findSpawnEffects, findTransitions, invalidateIndex, processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone };
|
package/dist/internal/utils.d.ts
CHANGED
|
@@ -26,7 +26,12 @@ type TaggedConstructor<T extends {
|
|
|
26
26
|
/**
|
|
27
27
|
* Transition handler result - either a new state or Effect producing one
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
/** Reply tuple returned from transition handlers for ask support */
|
|
30
|
+
interface TransitionReply<State> {
|
|
31
|
+
readonly state: State;
|
|
32
|
+
readonly reply: unknown;
|
|
33
|
+
}
|
|
34
|
+
type TransitionResult<State, R> = State | TransitionReply<State> | Effect.Effect<State | TransitionReply<State>, never, R>;
|
|
30
35
|
/**
|
|
31
36
|
* Internal event tags used for lifecycle effect contexts.
|
|
32
37
|
* Prefixed with $ to distinguish from user events.
|
|
@@ -57,4 +62,4 @@ declare const isEffect: (value: unknown) => value is Effect.Effect<unknown, unkn
|
|
|
57
62
|
*/
|
|
58
63
|
declare const stubSystem: ActorSystem;
|
|
59
64
|
//#endregion
|
|
60
|
-
export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionResult, getTag, isEffect, stubSystem };
|
|
65
|
+
export { ArgsOf, INTERNAL_ENTER_EVENT, INTERNAL_INIT_EVENT, InstanceOf, TagOf, TaggedConstructor, TransitionReply, TransitionResult, getTag, isEffect, stubSystem };
|
package/dist/machine.d.ts
CHANGED
|
@@ -6,17 +6,19 @@ import { PersistenceConfig, PersistentMachine } from "./persistence/persistent-m
|
|
|
6
6
|
import { DuplicateActorError } from "./errors.js";
|
|
7
7
|
import { findTransitions } from "./internal/transition.js";
|
|
8
8
|
import { ActorRef, ActorSystem } from "./actor.js";
|
|
9
|
-
import { Cause, Effect, Schedule, Schema, Scope, ServiceMap } from "effect";
|
|
9
|
+
import { Cause, Duration, Effect, Schedule, Schema, Scope, ServiceMap } from "effect";
|
|
10
10
|
|
|
11
11
|
//#region src/machine.d.ts
|
|
12
12
|
declare namespace machine_d_exports {
|
|
13
|
-
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, Transition, TransitionHandler, findTransitions, make, spawn };
|
|
13
|
+
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, PersistenceConfig, PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, make, spawn };
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Self reference for sending events back to the machine
|
|
17
17
|
*/
|
|
18
18
|
interface MachineRef<Event> {
|
|
19
19
|
readonly send: (event: Event) => Effect.Effect<void>;
|
|
20
|
+
/** Fire-and-forget alias for send (OTP gen_server:cast). */
|
|
21
|
+
readonly cast: (event: Event) => Effect.Effect<void>;
|
|
20
22
|
readonly spawn: <S2 extends {
|
|
21
23
|
readonly _tag: string;
|
|
22
24
|
}, E2 extends {
|
|
@@ -86,6 +88,18 @@ interface TaskOptions<State, Event, ED extends EffectsDef, A, E1, ES, EF> {
|
|
|
86
88
|
readonly onFailure?: (cause: Cause.Cause<E1>, ctx: StateHandlerContext<State, Event, ED>) => EF;
|
|
87
89
|
readonly name?: string;
|
|
88
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Configuration for `.timeout()` — gen_statem-style state timeouts.
|
|
93
|
+
*
|
|
94
|
+
* Entering the state starts a timer. Leaving cancels it.
|
|
95
|
+
* `.reenter()` restarts the timer with fresh state values.
|
|
96
|
+
*/
|
|
97
|
+
interface TimeoutConfig<State, Event> {
|
|
98
|
+
/** Duration before firing. Static or derived from current state. */
|
|
99
|
+
readonly duration: Duration.Input | ((state: State) => Duration.Input);
|
|
100
|
+
/** Event to send when the timer fires. Static or derived from current state. */
|
|
101
|
+
readonly event: Event | ((state: State) => Event);
|
|
102
|
+
}
|
|
89
103
|
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
90
104
|
type IsUnknown<T> = unknown extends T ? ([T] extends [unknown] ? true : false) : false;
|
|
91
105
|
type NormalizeR<T> = IsAny<T> extends true ? T : IsUnknown<T> extends true ? never : T;
|
|
@@ -152,6 +166,11 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
152
166
|
/** @internal */
|
|
153
167
|
readonly _finalStates: Set<string>;
|
|
154
168
|
/** @internal */
|
|
169
|
+
readonly _postponeRules: Array<{
|
|
170
|
+
readonly stateTag: string;
|
|
171
|
+
readonly eventTag: string;
|
|
172
|
+
}>;
|
|
173
|
+
/** @internal */
|
|
155
174
|
readonly _guardsSchema?: GuardsSchema<GD>;
|
|
156
175
|
/** @internal */
|
|
157
176
|
readonly _effectsSchema?: EffectsSchema<EFD>;
|
|
@@ -175,6 +194,10 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
175
194
|
get spawnEffects(): ReadonlyArray<SpawnEffect<State, Event, EFD, R>>;
|
|
176
195
|
get backgroundEffects(): ReadonlyArray<BackgroundEffect<State, Event, EFD, R>>;
|
|
177
196
|
get finalStates(): ReadonlySet<string>;
|
|
197
|
+
get postponeRules(): ReadonlyArray<{
|
|
198
|
+
readonly stateTag: string;
|
|
199
|
+
readonly eventTag: string;
|
|
200
|
+
}>;
|
|
178
201
|
get guardsSchema(): GuardsSchema<GD> | undefined;
|
|
179
202
|
get effectsSchema(): EffectsSchema<EFD> | undefined;
|
|
180
203
|
/** @internal */
|
|
@@ -230,6 +253,28 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
230
253
|
* Interrupts do not emit failure events.
|
|
231
254
|
*/
|
|
232
255
|
task<NS extends VariantsUnion<_SD> & BrandedState, A, E1, ES extends VariantsUnion<_ED> & BrandedEvent, EF extends VariantsUnion<_ED> & BrandedEvent>(state: TaggedOrConstructor<NS>, run: (ctx: StateHandlerContext<NS, VariantsUnion<_ED> & BrandedEvent, EFD>) => Effect.Effect<A, E1, Scope.Scope>, options: TaskOptions<NS, VariantsUnion<_ED> & BrandedEvent, EFD, A, E1, ES, EF>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
256
|
+
/**
|
|
257
|
+
* State timeout — gen_statem's `state_timeout`.
|
|
258
|
+
*
|
|
259
|
+
* Entering the state starts a timer. Leaving cancels it (via state scope).
|
|
260
|
+
* `.reenter()` restarts the timer with fresh state values.
|
|
261
|
+
* Compiles to `.task()` internally — preserves `@machine.task` inspection events.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* machine
|
|
266
|
+
* .timeout(State.Loading, {
|
|
267
|
+
* duration: Duration.seconds(30),
|
|
268
|
+
* event: Event.Timeout,
|
|
269
|
+
* })
|
|
270
|
+
* // Dynamic duration from state
|
|
271
|
+
* .timeout(State.Retrying, {
|
|
272
|
+
* duration: (state) => Duration.seconds(state.backoff),
|
|
273
|
+
* event: Event.GiveUp,
|
|
274
|
+
* })
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
timeout<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, config: TimeoutConfig<NS, VariantsUnion<_ED> & BrandedEvent>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
233
278
|
/**
|
|
234
279
|
* Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
|
|
235
280
|
* Use effect slots defined via `Slot.Effects` for the actual work.
|
|
@@ -251,6 +296,24 @@ declare class Machine<State, Event, R = never, _SD extends Record<string, Schema
|
|
|
251
296
|
* ```
|
|
252
297
|
*/
|
|
253
298
|
background(handler: StateEffectHandler<State, Event, EFD, Scope.Scope>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
299
|
+
/**
|
|
300
|
+
* Postpone events — gen_statem's event postpone.
|
|
301
|
+
*
|
|
302
|
+
* When a matching event arrives in the given state, it is buffered instead of
|
|
303
|
+
* processed. After the next state transition (tag change), all buffered events
|
|
304
|
+
* are drained through the loop in FIFO order.
|
|
305
|
+
*
|
|
306
|
+
* Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
|
|
307
|
+
* with `ActorStoppedError` on stop/interrupt/final-state.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```ts
|
|
311
|
+
* machine
|
|
312
|
+
* .postpone(State.Connecting, Event.Data) // single event
|
|
313
|
+
* .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
postpone<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>, events: TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent> | ReadonlyArray<TaggedOrConstructor<VariantsUnion<_ED> & BrandedEvent>>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
254
317
|
final<NS extends VariantsUnion<_SD> & BrandedState>(state: TaggedOrConstructor<NS>): Machine<State, Event, R, _SD, _ED, GD, EFD>;
|
|
255
318
|
/**
|
|
256
319
|
* Finalize the machine. Returns a `BuiltMachine` — the only type accepted by `Machine.spawn`.
|
|
@@ -288,4 +351,4 @@ declare const spawn: {
|
|
|
288
351
|
}, R>(machine: BuiltMachine<S, E, R>, id: string): Effect.Effect<ActorRef<S, E>, never, R>;
|
|
289
352
|
};
|
|
290
353
|
//#endregion
|
|
291
|
-
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
|
|
354
|
+
export { BackgroundEffect, BuiltMachine, HandlerContext, Machine, MachineRef, MakeConfig, PersistOptions, type PersistenceConfig, type PersistentMachine, ProvideHandlers, SlotContext, SpawnEffect, StateEffectHandler, StateHandlerContext, TaskOptions, TimeoutConfig, Transition, TransitionHandler, findTransitions, machine_d_exports, make, spawn };
|
package/dist/machine.js
CHANGED
|
@@ -64,6 +64,7 @@ var Machine = class Machine {
|
|
|
64
64
|
/** @internal */ _spawnEffects;
|
|
65
65
|
/** @internal */ _backgroundEffects;
|
|
66
66
|
/** @internal */ _finalStates;
|
|
67
|
+
/** @internal */ _postponeRules;
|
|
67
68
|
/** @internal */ _guardsSchema;
|
|
68
69
|
/** @internal */ _effectsSchema;
|
|
69
70
|
/** @internal */ _guardHandlers;
|
|
@@ -88,6 +89,9 @@ var Machine = class Machine {
|
|
|
88
89
|
get finalStates() {
|
|
89
90
|
return this._finalStates;
|
|
90
91
|
}
|
|
92
|
+
get postponeRules() {
|
|
93
|
+
return this._postponeRules;
|
|
94
|
+
}
|
|
91
95
|
get guardsSchema() {
|
|
92
96
|
return this._guardsSchema;
|
|
93
97
|
}
|
|
@@ -101,6 +105,7 @@ var Machine = class Machine {
|
|
|
101
105
|
this._spawnEffects = [];
|
|
102
106
|
this._backgroundEffects = [];
|
|
103
107
|
this._finalStates = /* @__PURE__ */ new Set();
|
|
108
|
+
this._postponeRules = [];
|
|
104
109
|
this._guardsSchema = guardsSchema;
|
|
105
110
|
this._effectsSchema = effectsSchema;
|
|
106
111
|
this._guardHandlers = /* @__PURE__ */ new Map();
|
|
@@ -259,6 +264,36 @@ var Machine = class Machine {
|
|
|
259
264
|
return this.spawn(state, handler);
|
|
260
265
|
}
|
|
261
266
|
/**
|
|
267
|
+
* State timeout — gen_statem's `state_timeout`.
|
|
268
|
+
*
|
|
269
|
+
* Entering the state starts a timer. Leaving cancels it (via state scope).
|
|
270
|
+
* `.reenter()` restarts the timer with fresh state values.
|
|
271
|
+
* Compiles to `.task()` internally — preserves `@machine.task` inspection events.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* machine
|
|
276
|
+
* .timeout(State.Loading, {
|
|
277
|
+
* duration: Duration.seconds(30),
|
|
278
|
+
* event: Event.Timeout,
|
|
279
|
+
* })
|
|
280
|
+
* // Dynamic duration from state
|
|
281
|
+
* .timeout(State.Retrying, {
|
|
282
|
+
* duration: (state) => Duration.seconds(state.backoff),
|
|
283
|
+
* event: Event.GiveUp,
|
|
284
|
+
* })
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
timeout(state, config) {
|
|
288
|
+
const stateTag = getTag(state);
|
|
289
|
+
const resolveDuration = typeof config.duration === "function" ? config.duration : () => config.duration;
|
|
290
|
+
const resolveEvent = typeof config.event === "function" ? config.event : () => config.event;
|
|
291
|
+
return this.task(state, (ctx) => Effect.sleep(resolveDuration(ctx.state)), {
|
|
292
|
+
onSuccess: (_, ctx) => resolveEvent(ctx.state),
|
|
293
|
+
name: `$timeout:${stateTag}`
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
262
297
|
* Machine-lifetime effect that is forked on actor spawn and runs until the actor stops.
|
|
263
298
|
* Use effect slots defined via `Slot.Effects` for the actual work.
|
|
264
299
|
*
|
|
@@ -282,6 +317,35 @@ var Machine = class Machine {
|
|
|
282
317
|
this._backgroundEffects.push({ handler });
|
|
283
318
|
return this;
|
|
284
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Postpone events — gen_statem's event postpone.
|
|
322
|
+
*
|
|
323
|
+
* When a matching event arrives in the given state, it is buffered instead of
|
|
324
|
+
* processed. After the next state transition (tag change), all buffered events
|
|
325
|
+
* are drained through the loop in FIFO order.
|
|
326
|
+
*
|
|
327
|
+
* Reply-bearing events (from `call`/`ask`) in the postpone buffer are settled
|
|
328
|
+
* with `ActorStoppedError` on stop/interrupt/final-state.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* machine
|
|
333
|
+
* .postpone(State.Connecting, Event.Data) // single event
|
|
334
|
+
* .postpone(State.Connecting, [Event.Data, Event.Cmd]) // multiple events
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
postpone(state, events) {
|
|
338
|
+
const stateTag = getTag(state);
|
|
339
|
+
const eventList = Array.isArray(events) ? events : [events];
|
|
340
|
+
for (const ev of eventList) {
|
|
341
|
+
const eventTag = getTag(ev);
|
|
342
|
+
this._postponeRules.push({
|
|
343
|
+
stateTag,
|
|
344
|
+
eventTag
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
285
349
|
final(state) {
|
|
286
350
|
const stateTag = getTag(state);
|
|
287
351
|
this._finalStates.add(stateTag);
|
|
@@ -313,6 +377,7 @@ var Machine = class Machine {
|
|
|
313
377
|
result._finalStates = new Set(this._finalStates);
|
|
314
378
|
result._spawnEffects = [...this._spawnEffects];
|
|
315
379
|
result._backgroundEffects = [...this._backgroundEffects];
|
|
380
|
+
result._postponeRules = [...this._postponeRules];
|
|
316
381
|
const anyHandlers = handlers;
|
|
317
382
|
if (this._guardsSchema !== void 0) for (const name of Object.keys(this._guardsSchema.definitions)) result._guardHandlers.set(name, anyHandlers[name]);
|
|
318
383
|
if (this._effectsSchema !== void 0) for (const name of Object.keys(this._effectsSchema.definitions)) result._effectHandlers.set(name, anyHandlers[name]);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Inspector } from "../inspection.js";
|
|
2
2
|
import { INTERNAL_INIT_EVENT, stubSystem } from "../internal/utils.js";
|
|
3
|
+
import { NoReplyError } from "../errors.js";
|
|
3
4
|
import { emitWithTimestamp } from "../internal/inspection.js";
|
|
4
|
-
import { processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler } from "../internal/transition.js";
|
|
5
|
+
import { processEventCore, resolveTransition, runSpawnEffects, runTransitionHandler, shouldPostpone } from "../internal/transition.js";
|
|
5
6
|
import { PersistenceAdapterTag } from "./adapter.js";
|
|
6
|
-
import { ActorSystem, buildActorRefCore, notifyListeners } from "../actor.js";
|
|
7
|
+
import { ActorSystem, buildActorRefCore, notifyListeners, settlePendingReplies } from "../actor.js";
|
|
7
8
|
import { Cause, Clock, Deferred, Effect, Exit, Fiber, Option, Queue, Ref, Schedule, Scope, SubscriptionRef } from "effect";
|
|
8
9
|
//#region src/persistence/persistent-actor.ts
|
|
9
10
|
/** Get current time in milliseconds using Effect Clock */
|
|
@@ -19,7 +20,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
|
|
|
19
20
|
for (const persistedEvent of events) {
|
|
20
21
|
if (stopVersion !== void 0 && persistedEvent.version > stopVersion) break;
|
|
21
22
|
const transition = resolveTransition(machine, state, persistedEvent.event);
|
|
22
|
-
if (transition !== void 0) state = yield* runTransitionHandler(machine, transition, state, persistedEvent.event, self, stubSystem, "restore");
|
|
23
|
+
if (transition !== void 0) state = (yield* runTransitionHandler(machine, transition, state, persistedEvent.event, self, stubSystem, "restore")).newState;
|
|
23
24
|
version = persistedEvent.version;
|
|
24
25
|
}
|
|
25
26
|
return {
|
|
@@ -30,7 +31,7 @@ const replayEvents = Effect.fn("effect-machine.persistentActor.replayEvents")(fu
|
|
|
30
31
|
/**
|
|
31
32
|
* Build PersistentActorRef with all methods
|
|
32
33
|
*/
|
|
33
|
-
const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system, childrenMap) => {
|
|
34
|
+
const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, stop, adapter, system, childrenMap, pendingReplies) => {
|
|
34
35
|
const { machine, persistence } = persistentMachine;
|
|
35
36
|
const typedMachine = machine;
|
|
36
37
|
const persist = Effect.gen(function* () {
|
|
@@ -44,8 +45,10 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
|
|
|
44
45
|
const version = Ref.get(versionRef).pipe(Effect.withSpan("effect-machine.persistentActor.version"));
|
|
45
46
|
const replayTo = Effect.fn("effect-machine.persistentActor.replayTo")(function* (targetVersion) {
|
|
46
47
|
if (targetVersion <= (yield* Ref.get(versionRef))) {
|
|
48
|
+
const dummySend = Effect.fn("effect-machine.persistentActor.replay.send")((_event) => Effect.void);
|
|
47
49
|
const dummySelf = {
|
|
48
|
-
send:
|
|
50
|
+
send: dummySend,
|
|
51
|
+
cast: dummySend,
|
|
49
52
|
spawn: () => Effect.die("spawn not supported in replay")
|
|
50
53
|
};
|
|
51
54
|
const maybeSnapshot = yield* adapter.loadSnapshot(id, persistence.stateSchema);
|
|
@@ -70,7 +73,7 @@ const buildPersistentActorRef = (id, persistentMachine, stateRef, versionRef, ev
|
|
|
70
73
|
}
|
|
71
74
|
});
|
|
72
75
|
return {
|
|
73
|
-
...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap),
|
|
76
|
+
...buildActorRefCore(id, typedMachine, stateRef, eventQueue, stoppedRef, listeners, stop, system, childrenMap, pendingReplies),
|
|
74
77
|
persist,
|
|
75
78
|
version,
|
|
76
79
|
replayTo
|
|
@@ -92,11 +95,16 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
92
95
|
const eventQueue = yield* Queue.unbounded();
|
|
93
96
|
const stoppedRef = yield* Ref.make(false);
|
|
94
97
|
const childrenMap = /* @__PURE__ */ new Map();
|
|
98
|
+
const selfSend = Effect.fn("effect-machine.persistentActor.self.send")(function* (event) {
|
|
99
|
+
if (yield* Ref.get(stoppedRef)) return;
|
|
100
|
+
yield* Queue.offer(eventQueue, {
|
|
101
|
+
_tag: "send",
|
|
102
|
+
event
|
|
103
|
+
});
|
|
104
|
+
});
|
|
95
105
|
const self = {
|
|
96
|
-
send:
|
|
97
|
-
|
|
98
|
-
yield* Queue.offer(eventQueue, { event });
|
|
99
|
-
}),
|
|
106
|
+
send: selfSend,
|
|
107
|
+
cast: selfSend,
|
|
100
108
|
spawn: (childId, childMachine) => Effect.gen(function* () {
|
|
101
109
|
const child = yield* system.spawn(childId, childMachine).pipe(Effect.provideService(ActorSystem, system));
|
|
102
110
|
childrenMap.set(childId, child);
|
|
@@ -177,9 +185,10 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
177
185
|
finalState: resolvedInitial,
|
|
178
186
|
timestamp
|
|
179
187
|
}));
|
|
180
|
-
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap);
|
|
188
|
+
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Ref.set(stoppedRef, true).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap, /* @__PURE__ */ new Set());
|
|
181
189
|
}
|
|
182
|
-
const
|
|
190
|
+
const pendingReplies = /* @__PURE__ */ new Set();
|
|
191
|
+
const loopFiber = yield* Effect.forkDetach(persistentEventLoop(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system, pendingReplies));
|
|
183
192
|
return buildPersistentActorRef(id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, listeners, Effect.gen(function* () {
|
|
184
193
|
const finalState = yield* SubscriptionRef.get(stateRef);
|
|
185
194
|
yield* emitWithTimestamp(inspector, (timestamp) => ({
|
|
@@ -190,16 +199,17 @@ const createPersistentActor = Effect.fn("effect-machine.persistentActor.spawn")(
|
|
|
190
199
|
}));
|
|
191
200
|
yield* Ref.set(stoppedRef, true);
|
|
192
201
|
yield* Fiber.interrupt(loopFiber);
|
|
202
|
+
yield* settlePendingReplies(pendingReplies, id);
|
|
193
203
|
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
194
204
|
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
195
205
|
yield* Fiber.interrupt(snapshotFiber);
|
|
196
206
|
yield* Fiber.interrupt(persistenceFiber);
|
|
197
|
-
}).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap);
|
|
207
|
+
}).pipe(Effect.withSpan("effect-machine.persistentActor.stop"), Effect.asVoid), adapter, system, childrenMap, pendingReplies);
|
|
198
208
|
});
|
|
199
209
|
/**
|
|
200
210
|
* Main event loop for persistent actor
|
|
201
211
|
*/
|
|
202
|
-
const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop")(function* (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system) {
|
|
212
|
+
const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop")(function* (id, persistentMachine, stateRef, versionRef, eventQueue, stoppedRef, self, listeners, adapter, createdAt, stateScopeRef, backgroundFibers, snapshotQueue, snapshotEnabledRef, persistenceQueue, snapshotFiber, persistenceFiber, inspector, system, pendingReplies) {
|
|
203
213
|
const { machine, persistence } = persistentMachine;
|
|
204
214
|
const typedMachine = machine;
|
|
205
215
|
const hooks = inspector === void 0 ? void 0 : {
|
|
@@ -228,10 +238,28 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
|
|
|
228
238
|
timestamp
|
|
229
239
|
}))
|
|
230
240
|
};
|
|
241
|
+
const postponed = [];
|
|
242
|
+
const pendingDrain = [];
|
|
243
|
+
const hasPostponeRules = machine.postponeRules.length > 0;
|
|
231
244
|
while (true) {
|
|
232
|
-
const
|
|
245
|
+
const queued = pendingDrain.length > 0 ? pendingDrain.shift() : yield* Queue.take(eventQueue);
|
|
246
|
+
const event = queued.event;
|
|
233
247
|
const currentState = yield* SubscriptionRef.get(stateRef);
|
|
234
248
|
const currentVersion = yield* Ref.get(versionRef);
|
|
249
|
+
if (hasPostponeRules && shouldPostpone(typedMachine, currentState._tag, event._tag)) {
|
|
250
|
+
postponed.push(queued);
|
|
251
|
+
if (queued._tag === "call") yield* Deferred.succeed(queued.reply, {
|
|
252
|
+
newState: currentState,
|
|
253
|
+
previousState: currentState,
|
|
254
|
+
transitioned: false,
|
|
255
|
+
lifecycleRan: false,
|
|
256
|
+
isFinal: false,
|
|
257
|
+
hasReply: false,
|
|
258
|
+
reply: void 0,
|
|
259
|
+
postponed: true
|
|
260
|
+
});
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
235
263
|
yield* emitWithTimestamp(inspector, (timestamp) => ({
|
|
236
264
|
type: "@machine.event",
|
|
237
265
|
actorId: id,
|
|
@@ -240,7 +268,12 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
|
|
|
240
268
|
timestamp
|
|
241
269
|
}));
|
|
242
270
|
const result = yield* processEventCore(typedMachine, currentState, event, self, stateScopeRef, system, id, hooks);
|
|
243
|
-
if (
|
|
271
|
+
if (queued._tag === "call") yield* Deferred.succeed(queued.reply, result);
|
|
272
|
+
else if (queued._tag === "ask") if (result.hasReply) yield* Deferred.succeed(queued.reply, result.reply);
|
|
273
|
+
else yield* Deferred.fail(queued.reply, new NoReplyError({
|
|
274
|
+
actorId: id,
|
|
275
|
+
eventTag: event._tag
|
|
276
|
+
}));
|
|
244
277
|
if (!result.transitioned) continue;
|
|
245
278
|
const newVersion = currentVersion + 1;
|
|
246
279
|
yield* Ref.set(versionRef, newVersion);
|
|
@@ -268,12 +301,15 @@ const persistentEventLoop = Effect.fn("effect-machine.persistentActor.eventLoop"
|
|
|
268
301
|
timestamp
|
|
269
302
|
}));
|
|
270
303
|
yield* Ref.set(stoppedRef, true);
|
|
304
|
+
postponed.length = 0;
|
|
305
|
+
yield* settlePendingReplies(pendingReplies, id);
|
|
271
306
|
yield* Scope.close(stateScopeRef.current, Exit.void);
|
|
272
307
|
yield* Effect.all(backgroundFibers.map(Fiber.interrupt), { concurrency: "unbounded" });
|
|
273
308
|
yield* Fiber.interrupt(snapshotFiber);
|
|
274
309
|
yield* Fiber.interrupt(persistenceFiber);
|
|
275
310
|
return;
|
|
276
311
|
}
|
|
312
|
+
if (result.lifecycleRan && postponed.length > 0) pendingDrain.push(...postponed.splice(0));
|
|
277
313
|
}
|
|
278
314
|
});
|
|
279
315
|
/**
|