@systemfsoftware/xstate 0.0.0-stage → 6.0.0-beta.65
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/CHANGELOG.md +57 -0
- package/LICENSE +22 -0
- package/README.md +640 -2
- package/dist/actors-LxMBvgxO.mjs +472 -0
- package/dist/actors.d.ts +3 -0
- package/dist/actors.mjs +3 -0
- package/dist/base.types-DF8ZPiXj.d.ts +210 -0
- package/dist/createActor-BGHd7i8Q.mjs +4851 -0
- package/dist/durable.d.ts +211 -0
- package/dist/durable.mjs +283 -0
- package/dist/effectDescriptor-EvNuy6U3.d.ts +91 -0
- package/dist/fsm.d.ts +139 -0
- package/dist/fsm.mjs +60 -0
- package/dist/graph.d.ts +160 -0
- package/dist/graph.mjs +667 -0
- package/dist/index-CKUyiMZC.d.ts +1832 -0
- package/dist/index-DDWoKIF5.d.ts +512 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +7 -0
- package/dist/schema.types-tsIbgQd-.mjs +46 -0
- package/dist/src-zLWug7SF.mjs +2641 -0
- package/dist/transition-ZMXyPI-x.mjs +258 -0
- package/dist/types-B2EAwOk-.d.ts +3511 -0
- package/dist/utils-CXBxxp3Z.mjs +146 -0
- package/dist/validation.d.ts +49 -0
- package/dist/validation.mjs +216 -0
- package/package.json +83 -3
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { t as EffectDescriptor } from "./effectDescriptor-EvNuy6U3.js";
|
|
2
|
+
import { Ar as ActorSystemRuntime, Br as InspectionEvent, C as AnyActor, D as AnyEventObject, Jt as OutputFrom, Ot as InputFrom, X as CustomExecutableActionObject, _n as SnapshotFrom, gt as EventFromLogic, hn as Snapshot, w as AnyActorLogic, yt as ExecutableActionObjectFromLogic } from "./types-B2EAwOk-.js";
|
|
3
|
+
/** @experimental */
|
|
4
|
+
export interface DurableEffectMetadata {
|
|
5
|
+
/** Stable within one durable execution. */
|
|
6
|
+
id: string;
|
|
7
|
+
transitionIndex: number;
|
|
8
|
+
effectIndex: number;
|
|
9
|
+
}
|
|
10
|
+
/** @experimental */
|
|
11
|
+
export interface DurableWaitMetadata {
|
|
12
|
+
/** Stable within one durable execution. */
|
|
13
|
+
id: string;
|
|
14
|
+
transitionIndex: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* An event addressed to a durable execution's root actor, captured while
|
|
18
|
+
* effects settled. `source` is the live reference of the sending actor when
|
|
19
|
+
* one initiated the send.
|
|
20
|
+
*
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
export interface DurableRootEvent<TEvent> {
|
|
24
|
+
event: TEvent;
|
|
25
|
+
source: AnyActor | undefined;
|
|
26
|
+
}
|
|
27
|
+
/** @experimental */
|
|
28
|
+
export interface DurableEffect<TEffect> extends DurableEffectMetadata {
|
|
29
|
+
effect: TEffect;
|
|
30
|
+
/**
|
|
31
|
+
* Serializable view of the effect: actor references replaced by logical
|
|
32
|
+
* addresses and actor sources by source keys, for journaling and
|
|
33
|
+
* deduplication. Payload fields are only as serializable as their values.
|
|
34
|
+
*/
|
|
35
|
+
descriptor: EffectDescriptor;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A durable host adapter: the runtime operations that execute this
|
|
39
|
+
* execution's effects, plus the durable loop hooks.
|
|
40
|
+
*
|
|
41
|
+
* The runtime operations (`sendEvent`, `scheduleTimer`, …) are installed on
|
|
42
|
+
* the actor system of every snapshot the execution produces, including
|
|
43
|
+
* children created during transitions and actors rehydrated from restored
|
|
44
|
+
* snapshots. Operations initiated by live child actors (parent sends,
|
|
45
|
+
* timers, terminations) route to them without any per-actor wiring; omitted
|
|
46
|
+
* operations keep their default local behavior.
|
|
47
|
+
*
|
|
48
|
+
* Events addressed to this execution's root actor do not reach `sendEvent`
|
|
49
|
+
* during `executeEffects`: the execution captures and retains them, and the
|
|
50
|
+
* execution's `waitForEvent()` hands them out before deferring to this
|
|
51
|
+
* adapter. While the loop is parked, a root-addressed event reaches
|
|
52
|
+
* `enqueueRootEvent`, or the broader `sendEvent` override when implemented,
|
|
53
|
+
* so the host can place it in its own mailbox.
|
|
54
|
+
*
|
|
55
|
+
* @experimental
|
|
56
|
+
*/
|
|
57
|
+
export interface DurableExecutionAdapter<TLogic extends AnyActorLogic> extends Partial<ActorSystemRuntime> {
|
|
58
|
+
/**
|
|
59
|
+
* Absolute clock in milliseconds, defaulting to Date.now. Checkpoints and
|
|
60
|
+
* resumed executions must use the same time origin. Replay hosts should
|
|
61
|
+
* return the recorded scheduling time rather than the replay's wall clock.
|
|
62
|
+
*/
|
|
63
|
+
now?(): number;
|
|
64
|
+
/**
|
|
65
|
+
* Accepts a timer synchronously, or fulfills its promise when registration
|
|
66
|
+
* accepts the timer and its delay begins. For a new timer, that acceptance
|
|
67
|
+
* time is read through now() and persisted. Retried/restored timers retain
|
|
68
|
+
* their original deadline; hosts must honor it during registration.
|
|
69
|
+
*/
|
|
70
|
+
scheduleTimer?: ActorSystemRuntime['scheduleTimer'];
|
|
71
|
+
/**
|
|
72
|
+
* Enqueues an event addressed to this execution's root while the durable
|
|
73
|
+
* loop is parked in `waitForEvent()`. Use this when the host only owns the
|
|
74
|
+
* root mailbox; implementing `sendEvent` instead takes ownership of delivery
|
|
75
|
+
* for every target and must use `deliverEvent` for co-located actors.
|
|
76
|
+
*/
|
|
77
|
+
enqueueRootEvent?(source: AnyActor | undefined, event: AnyEventObject): void | PromiseLike<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Executes a custom action as a durable host step or activity. The host
|
|
80
|
+
* should identify the action by `type` and memoize or deduplicate it using
|
|
81
|
+
* `metadata.id`.
|
|
82
|
+
*/
|
|
83
|
+
executeAction(action: CustomExecutableActionObject, metadata: DurableEffectMetadata, runtime: Partial<ActorSystemRuntime>): void | PromiseLike<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Creates the runtime used by one effect, for hosts that key operations by
|
|
86
|
+
* effect ID. Overrides the adapter's runtime operations
|
|
87
|
+
* operation-by-operation; operations it omits keep their behavior. The
|
|
88
|
+
* complete effect is provided for hosts that need serializable actor
|
|
89
|
+
* source, input, event or target data beyond the runtime method arguments.
|
|
90
|
+
*/
|
|
91
|
+
runtime?(metadata: DurableEffectMetadata, effect: ExecutableActionObjectFromLogic<TLogic>): Partial<ActorSystemRuntime>;
|
|
92
|
+
/**
|
|
93
|
+
* Waits durably for the next event addressed to this execution. Typed to
|
|
94
|
+
* also accept plain event objects: an adapter written against
|
|
95
|
+
* `AnyActorLogic` (a generic host library) cannot produce
|
|
96
|
+
* `EventFromLogic<TLogic>`, and the events a host relays are runtime data
|
|
97
|
+
* anyway — the execution narrows at its own boundary.
|
|
98
|
+
*/
|
|
99
|
+
waitForEvent(metadata: DurableWaitMetadata): EventFromLogic<TLogic> | AnyEventObject | PromiseLike<EventFromLogic<TLogic> | AnyEventObject>;
|
|
100
|
+
/** Index assigned to the first transition. Defaults to `0`. */
|
|
101
|
+
transitionIndex?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Pins this execution's actor identity. Session ids become
|
|
104
|
+
* `<executionId>:<n>`, a deterministic function of actor-creation order, so
|
|
105
|
+
* a replay re-creates the same session ids and journaled completion events
|
|
106
|
+
* (which carry the producing incarnation's `sessionId`) still match the
|
|
107
|
+
* children the replay re-creates. Without it, session ids embed a random
|
|
108
|
+
* per-process system id and journaled internal events go stale across
|
|
109
|
+
* replays. Uniqueness across executions is the host's responsibility.
|
|
110
|
+
*/
|
|
111
|
+
executionId?: string;
|
|
112
|
+
}
|
|
113
|
+
/** @experimental */
|
|
114
|
+
export declare class DurableExecutionCancelledError extends Error {
|
|
115
|
+
constructor();
|
|
116
|
+
}
|
|
117
|
+
/** @experimental */
|
|
118
|
+
export declare class DurableExecutionResumeError extends Error {
|
|
119
|
+
constructor();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The snapshot type a durable execution hands back: the logic's own snapshot,
|
|
123
|
+
* intersected with the base `Snapshot` union so the `status`/`output`/`error`
|
|
124
|
+
* discriminant stays visible even when `TLogic` is an unresolved type
|
|
125
|
+
* parameter (a generic host library), where `SnapshotFrom` alone is opaque.
|
|
126
|
+
*
|
|
127
|
+
* @experimental
|
|
128
|
+
*/
|
|
129
|
+
export type DurableSnapshot<TLogic extends AnyActorLogic> = SnapshotFrom<TLogic> & Snapshot<OutputFrom<TLogic>>;
|
|
130
|
+
/** @experimental */
|
|
131
|
+
export interface DurableExecution<TLogic extends AnyActorLogic> {
|
|
132
|
+
/**
|
|
133
|
+
* The logical address of this execution's root actor: the logic's own name,
|
|
134
|
+
* or `x:0` for anonymous logic. Known before any transition runs, so hosts
|
|
135
|
+
* can label mailboxes and wire messages without a snapshot.
|
|
136
|
+
*/
|
|
137
|
+
readonly rootAddress: string;
|
|
138
|
+
/** The machine's id, for pinning a journal to the logic that produced it. */
|
|
139
|
+
readonly machineId: string;
|
|
140
|
+
/**
|
|
141
|
+
* The machine's declared `version`. Persist it with the execution and
|
|
142
|
+
* reject a worker whose machine version differs: a changed machine
|
|
143
|
+
* reorders effect ids, and memoized results silently misalign.
|
|
144
|
+
*/
|
|
145
|
+
readonly machineVersion: string | undefined;
|
|
146
|
+
/** Index assigned to the next transition. Persist this with checkpoints. */
|
|
147
|
+
readonly nextTransitionIndex: number;
|
|
148
|
+
/**
|
|
149
|
+
* Restores a checkpoint on a fresh execution. Returns effects that start
|
|
150
|
+
* active embedded children and re-arm pending root timers; execute them
|
|
151
|
+
* with `executeEffects` before waiting or processing the next event.
|
|
152
|
+
* Does not send a machine event, replay entry actions, or advance the
|
|
153
|
+
* transition index. Restoration effect IDs use `restore:<index>:<effect>`.
|
|
154
|
+
*/
|
|
155
|
+
restore(persistedSnapshot: Snapshot<unknown>): [snapshot: DurableSnapshot<TLogic>, effects: DurableEffect<ExecutableActionObjectFromLogic<TLogic>>[]];
|
|
156
|
+
initialTransition(...[input]: undefined extends InputFrom<TLogic> ? [input?: InputFrom<TLogic>] : [input: InputFrom<TLogic>]): [snapshot: DurableSnapshot<TLogic>, effects: DurableEffect<ExecutableActionObjectFromLogic<TLogic>>[]];
|
|
157
|
+
transition(snapshot: SnapshotFrom<TLogic>, event: EventFromLogic<TLogic>): [snapshot: DurableSnapshot<TLogic>, effects: DurableEffect<ExecutableActionObjectFromLogic<TLogic>>[]];
|
|
158
|
+
/**
|
|
159
|
+
* Returns the actor reference behind a snapshot produced by this execution,
|
|
160
|
+
* for addressing and inspection. `undefined` for snapshots this execution
|
|
161
|
+
* has not seen (for example a freshly deserialized checkpoint that has not
|
|
162
|
+
* been passed through `restore()` or `transition()` yet).
|
|
163
|
+
*
|
|
164
|
+
* With an `address`, returns the live actor at that logical address in the
|
|
165
|
+
* snapshot's actor tree instead — the root itself, one of its transitive
|
|
166
|
+
* children, or `undefined` when no live actor has that address (for
|
|
167
|
+
* example a timer firing for an actor that already completed).
|
|
168
|
+
*/
|
|
169
|
+
getActorRef(snapshot: SnapshotFrom<TLogic>, address?: string): AnyActor | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* Executes effects and resolves only when every runtime operation they
|
|
172
|
+
* transitively initiated — including operations from live child actors
|
|
173
|
+
* reacting to delivered events — has been accepted by the runtime; a
|
|
174
|
+
* failed operation rejects it. Operations are handed to the runtime one at
|
|
175
|
+
* a time, in initiation order.
|
|
176
|
+
*
|
|
177
|
+
* Events addressed to this execution's root actor that were produced along
|
|
178
|
+
* the way are retained, in order, and handed out by `waitForEvent()`
|
|
179
|
+
* before it defers to the adapter — so the drive loop is just
|
|
180
|
+
* `transition(snapshot, await waitForEvent())` + `executeEffects`.
|
|
181
|
+
*/
|
|
182
|
+
executeEffects(effects: readonly DurableEffect<ExecutableActionObjectFromLogic<TLogic>>[]): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Resolves with the next event addressed to this execution's root actor:
|
|
185
|
+
* an event a prior `executeEffects` batch captured (an invoked actor
|
|
186
|
+
* completing, a child reporting up), or — once none are queued — whatever
|
|
187
|
+
* the adapter's durable wait produces.
|
|
188
|
+
*/
|
|
189
|
+
waitForEvent(): Promise<EventFromLogic<TLogic>>;
|
|
190
|
+
run(...[input]: undefined extends InputFrom<TLogic> ? [input?: InputFrom<TLogic>] : [input: InputFrom<TLogic>]): Promise<OutputFrom<TLogic>>;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Creates a host-neutral durable execution around pure actor transitions.
|
|
194
|
+
*
|
|
195
|
+
* The returned transition methods only calculate snapshots and tag their
|
|
196
|
+
* effects. Persistence, retries, messaging, timers and child execution remain
|
|
197
|
+
* responsibilities of the adapter.
|
|
198
|
+
*
|
|
199
|
+
* @experimental
|
|
200
|
+
*/
|
|
201
|
+
export interface DurableExecutionOptions {
|
|
202
|
+
/**
|
|
203
|
+
* Observes the execution's inspection events — actor lifecycle, event
|
|
204
|
+
* routing, transitions — across the whole live actor tree. The host-side
|
|
205
|
+
* home for operation logs and instrumentation, so adapters stay pure
|
|
206
|
+
* physics.
|
|
207
|
+
*/
|
|
208
|
+
inspect?: (inspectionEvent: InspectionEvent) => void;
|
|
209
|
+
}
|
|
210
|
+
/** @experimental */
|
|
211
|
+
export declare function createDurable<TLogic extends AnyActorLogic>(logic: TLogic, adapter: DurableExecutionAdapter<TLogic>, options?: DurableExecutionOptions): DurableExecution<TLogic>;
|
package/dist/durable.mjs
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { i as initialTransition, o as transition, s as getEffectDescriptor } from "./transition-ZMXyPI-x.mjs";
|
|
2
|
+
import { K as setInertActorScopeSnapshot, O as createStartEffect, Q as recordTimerStart, U as attachSnapshotActorRef, W as createInertActorScope, Z as getTimerStart, bt as withSystemInspector, ct as encodeAddressSegment, ft as withExecutionIdentity, ht as deliverEvent, k as createTimerEffect, pt as getSnapshotActorRef, rt as isRemoteActorRef, st as RUNTIME_OPERATIONS, ut as getRootActorId } from "./createActor-BGHd7i8Q.mjs";
|
|
3
|
+
// src/durable/index.ts
|
|
4
|
+
/** @experimental */
|
|
5
|
+
var DurableExecutionCancelledError = class extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super("Durable execution was stopped");
|
|
8
|
+
this.name = "DurableExecutionCancelledError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/** @experimental */
|
|
12
|
+
var DurableExecutionResumeError = class extends Error {
|
|
13
|
+
constructor() {
|
|
14
|
+
super("run() can only start a fresh durable execution; resume checkpoints with restore() and the explicit transition loop");
|
|
15
|
+
this.name = "DurableExecutionResumeError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
/** @experimental */
|
|
19
|
+
function createDurable(logic, adapter, options) {
|
|
20
|
+
let nextTransitionIndex = adapter.transitionIndex ?? 0;
|
|
21
|
+
const startingTransitionIndex = nextTransitionIndex;
|
|
22
|
+
let restored = false;
|
|
23
|
+
const now = () => adapter.now?.() ?? Date.now();
|
|
24
|
+
const observedRoots = /* @__PURE__ */ new WeakSet();
|
|
25
|
+
let lastTransitionIndex = nextTransitionIndex === 0 ? void 0 : nextTransitionIndex - 1;
|
|
26
|
+
const restoredTimerDeadlines = /* @__PURE__ */ new WeakMap();
|
|
27
|
+
if (!Number.isSafeInteger(nextTransitionIndex) || nextTransitionIndex < 0) throw new RangeError("transitionIndex must be a non-negative safe integer");
|
|
28
|
+
const tagEffects = (effects, restoring = false) => {
|
|
29
|
+
const transitionIndex = restoring ? nextTransitionIndex : nextTransitionIndex++;
|
|
30
|
+
lastTransitionIndex = restoring ? lastTransitionIndex ?? transitionIndex : transitionIndex;
|
|
31
|
+
return effects.map((effect, effectIndex) => {
|
|
32
|
+
const tagged = {
|
|
33
|
+
id: `${restoring ? "restore:" : ""}${transitionIndex}:${effectIndex}`,
|
|
34
|
+
transitionIndex,
|
|
35
|
+
effectIndex,
|
|
36
|
+
effect
|
|
37
|
+
};
|
|
38
|
+
let descriptor;
|
|
39
|
+
Object.defineProperty(tagged, "descriptor", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: () => descriptor ??= getEffectDescriptor(effect)
|
|
42
|
+
});
|
|
43
|
+
return tagged;
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const rootAddress = encodeAddressSegment(getRootActorId(logic));
|
|
47
|
+
const machineId = logic.id ?? rootAddress;
|
|
48
|
+
const machineVersion = logic.version;
|
|
49
|
+
const executionIdentity = adapter.executionId ? {
|
|
50
|
+
systemId: adapter.executionId,
|
|
51
|
+
nextSessionId: 0
|
|
52
|
+
} : void 0;
|
|
53
|
+
const systemRuntime = {};
|
|
54
|
+
for (const operation of [
|
|
55
|
+
...RUNTIME_OPERATIONS,
|
|
56
|
+
"sendEvent",
|
|
57
|
+
"runStep",
|
|
58
|
+
"runLogic"
|
|
59
|
+
]) {
|
|
60
|
+
const impl = adapter[operation];
|
|
61
|
+
if (impl) systemRuntime[operation] = impl.bind(adapter);
|
|
62
|
+
}
|
|
63
|
+
const hasSystemRuntime = Object.keys(systemRuntime).length > 0 || !!adapter.enqueueRootEvent;
|
|
64
|
+
let currentBatch;
|
|
65
|
+
const pendingRootEvents = [];
|
|
66
|
+
let operationTail = Promise.resolve();
|
|
67
|
+
const track = (batch, operation) => {
|
|
68
|
+
batch.pending.add(operation);
|
|
69
|
+
operation.catch((error) => {
|
|
70
|
+
batch.failures.push(error);
|
|
71
|
+
}).then(() => batch.pending.delete(operation));
|
|
72
|
+
return operation;
|
|
73
|
+
};
|
|
74
|
+
const handOff = (batch, run) => {
|
|
75
|
+
const operation = operationTail.then(run).then(() => void 0);
|
|
76
|
+
operationTail = operation.catch(() => {});
|
|
77
|
+
return track(batch, operation);
|
|
78
|
+
};
|
|
79
|
+
const dispatch = (run) => currentBatch ? handOff(currentBatch, run) : run();
|
|
80
|
+
const drain = async (batch) => {
|
|
81
|
+
while (batch.pending.size) await Promise.allSettled([...batch.pending]);
|
|
82
|
+
};
|
|
83
|
+
const settle = async (batch) => {
|
|
84
|
+
await drain(batch);
|
|
85
|
+
if (batch.failures.length) throw batch.failures[0];
|
|
86
|
+
};
|
|
87
|
+
function wrapRuntime(runtime, localDeliveryFallback, fallbackToActorSystem = false, timerDeadline) {
|
|
88
|
+
const wrapped = {};
|
|
89
|
+
for (const operation of RUNTIME_OPERATIONS) {
|
|
90
|
+
const impl = runtime[operation] ?? (fallbackToActorSystem ? (...args) => {
|
|
91
|
+
return (operation === "spawnActor" ? args[1] : args[0]).system[operation](...args);
|
|
92
|
+
} : void 0);
|
|
93
|
+
if (impl) wrapped[operation] = (...args) => dispatch(() => {
|
|
94
|
+
if (operation === "scheduleTimer" && timerDeadline) args[2] = Math.min(timerDeadline.delay, Math.max(0, timerDeadline.deadline - now()));
|
|
95
|
+
if (operation === "scheduleTimer") {
|
|
96
|
+
const timer = args[0].getSnapshot()?.timers?.[args[1]];
|
|
97
|
+
if (timer) {
|
|
98
|
+
const previousStart = getTimerStart(timer);
|
|
99
|
+
if (previousStart !== void 0 && !timerDeadline) args[2] = Math.min(timer.delay, Math.max(0, previousStart + timer.delay - now()));
|
|
100
|
+
const result = impl(...args);
|
|
101
|
+
if (result) return Promise.resolve(result).then(() => {
|
|
102
|
+
recordTimerStart(timer, previousStart ?? now());
|
|
103
|
+
});
|
|
104
|
+
recordTimerStart(timer, previousStart ?? now());
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return impl(...args);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (runtime.runStep) wrapped.runStep = runtime.runStep;
|
|
112
|
+
if (runtime.runLogic) wrapped.runLogic = runtime.runLogic;
|
|
113
|
+
wrapped.sendEvent = (source, target, event) => {
|
|
114
|
+
if (!currentBatch && target.address === rootAddress && !runtime.sendEvent && adapter.enqueueRootEvent) return dispatch(() => adapter.enqueueRootEvent(source, event));
|
|
115
|
+
if (!currentBatch && target.address === rootAddress && !runtime.sendEvent) throw new Error(`A root-addressed event ("${event.type}") was produced while the durable loop was parked, but the adapter has no enqueueRootEvent or sendEvent to receive it. Implement enqueueRootEvent to place root-addressed events in the host's mailbox.`);
|
|
116
|
+
if (currentBatch && target.address === rootAddress) {
|
|
117
|
+
currentBatch.rootEvents.push({
|
|
118
|
+
event,
|
|
119
|
+
source
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const impl = runtime.sendEvent;
|
|
124
|
+
if (impl) return dispatch(() => impl(source, target, event));
|
|
125
|
+
if (localDeliveryFallback) return dispatch(() => deliverEvent(source, target, event));
|
|
126
|
+
throw new TypeError(`The durable runtime does not support the sendEvent operation (target '${target.address}')`);
|
|
127
|
+
};
|
|
128
|
+
return wrapped;
|
|
129
|
+
}
|
|
130
|
+
const wrappedSystemRuntime = hasSystemRuntime ? wrapRuntime(systemRuntime, true) : void 0;
|
|
131
|
+
function findByAddress(actor, address) {
|
|
132
|
+
if (actor.address === address) return actor;
|
|
133
|
+
if (!address.startsWith(`${actor.address}/`)) return;
|
|
134
|
+
const children = actor.getSnapshot().children;
|
|
135
|
+
for (const child of Object.values(children ?? {})) {
|
|
136
|
+
const found = child && findByAddress(child, address);
|
|
137
|
+
if (found) return found;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const inspect = options?.inspect;
|
|
141
|
+
function wireInspection(system) {
|
|
142
|
+
if (inspect && !system._hasInspectionObservers?.()) system.inspect(inspect);
|
|
143
|
+
}
|
|
144
|
+
function installSystemRuntime(snapshot, recursive = false) {
|
|
145
|
+
if (snapshot.status === "error") {
|
|
146
|
+
const root = getSnapshotActorRef(snapshot)?.actor;
|
|
147
|
+
if (root && !observedRoots.has(root)) {
|
|
148
|
+
root.subscribe({ error() {} });
|
|
149
|
+
observedRoots.add(root);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (wrappedSystemRuntime || inspect) {
|
|
153
|
+
const ref = getSnapshotActorRef(snapshot)?.actor;
|
|
154
|
+
const systems = /* @__PURE__ */ new Set();
|
|
155
|
+
const visit = (actor) => {
|
|
156
|
+
if (!systems.has(actor.system)) {
|
|
157
|
+
systems.add(actor.system);
|
|
158
|
+
if (wrappedSystemRuntime) actor.system.runtime = wrappedSystemRuntime;
|
|
159
|
+
wireInspection(actor.system);
|
|
160
|
+
}
|
|
161
|
+
if (recursive) {
|
|
162
|
+
const children = actor.getSnapshot().children;
|
|
163
|
+
for (const child of Object.values(children ?? {})) if (child) visit(child);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
if (ref) visit(ref);
|
|
167
|
+
if (!recursive || !ref) {
|
|
168
|
+
const children = snapshot.children;
|
|
169
|
+
for (const child of Object.values(children ?? {})) if (child) visit(child);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return snapshot;
|
|
173
|
+
}
|
|
174
|
+
const execution = {
|
|
175
|
+
rootAddress,
|
|
176
|
+
machineId,
|
|
177
|
+
machineVersion,
|
|
178
|
+
get nextTransitionIndex() {
|
|
179
|
+
return nextTransitionIndex;
|
|
180
|
+
},
|
|
181
|
+
restore(persistedSnapshot) {
|
|
182
|
+
if (restored || nextTransitionIndex !== startingTransitionIndex || currentBatch) throw new Error("restore() requires a fresh durable execution");
|
|
183
|
+
const { snapshot, scope } = withSystemInspector(inspect, () => withExecutionIdentity(executionIdentity, () => {
|
|
184
|
+
const scope = createInertActorScope(logic);
|
|
185
|
+
const snapshot = logic.restoreSnapshot ? logic.restoreSnapshot(persistedSnapshot, scope) : persistedSnapshot;
|
|
186
|
+
setInertActorScopeSnapshot(scope, snapshot, false);
|
|
187
|
+
return {
|
|
188
|
+
snapshot: installSystemRuntime(attachSnapshotActorRef(scope, snapshot), true),
|
|
189
|
+
scope
|
|
190
|
+
};
|
|
191
|
+
}));
|
|
192
|
+
const effects = [];
|
|
193
|
+
if (snapshot.status === "active") {
|
|
194
|
+
const { children, timers } = snapshot;
|
|
195
|
+
for (const child of Object.values(children ?? {})) if (child && !isRemoteActorRef(child) && child.getSnapshot().status === "active") effects.push(createStartEffect(child));
|
|
196
|
+
for (const timer of Object.values(timers ?? {})) {
|
|
197
|
+
const effect = createTimerEffect(scope, timer, timer.delay);
|
|
198
|
+
if (timer.startedAt !== void 0) restoredTimerDeadlines.set(effect, {
|
|
199
|
+
deadline: timer.startedAt + timer.delay,
|
|
200
|
+
delay: timer.delay
|
|
201
|
+
});
|
|
202
|
+
effects.push(effect);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
restored = true;
|
|
206
|
+
return [snapshot, tagEffects(effects, true)];
|
|
207
|
+
},
|
|
208
|
+
initialTransition(...args) {
|
|
209
|
+
if (restored) throw new Error("initialTransition() cannot initialize a restored execution");
|
|
210
|
+
const [snapshot, effects] = withSystemInspector(inspect, () => withExecutionIdentity(executionIdentity, () => initialTransition(logic, ...args)));
|
|
211
|
+
return [installSystemRuntime(snapshot), tagEffects(effects)];
|
|
212
|
+
},
|
|
213
|
+
transition(snapshot, event) {
|
|
214
|
+
const [nextSnapshot, effects] = withSystemInspector(inspect, () => withExecutionIdentity(executionIdentity, () => transition(logic, snapshot, event)));
|
|
215
|
+
return [installSystemRuntime(nextSnapshot), tagEffects(effects)];
|
|
216
|
+
},
|
|
217
|
+
getActorRef(snapshot, address) {
|
|
218
|
+
const root = getSnapshotActorRef(snapshot)?.actor;
|
|
219
|
+
if (!root || address === void 0) return root;
|
|
220
|
+
return findByAddress(root, address);
|
|
221
|
+
},
|
|
222
|
+
async executeEffects(effects) {
|
|
223
|
+
if (currentBatch) throw new Error("executeEffects calls must not overlap: await the previous call before starting the next batch.");
|
|
224
|
+
const batch = {
|
|
225
|
+
rootEvents: [],
|
|
226
|
+
failures: [],
|
|
227
|
+
pending: /* @__PURE__ */ new Set()
|
|
228
|
+
};
|
|
229
|
+
currentBatch = batch;
|
|
230
|
+
const fallbackRuntime = hasSystemRuntime ? void 0 : {};
|
|
231
|
+
try {
|
|
232
|
+
for (const tagged of effects) {
|
|
233
|
+
const { effect } = tagged;
|
|
234
|
+
const metadata = {
|
|
235
|
+
id: tagged.id,
|
|
236
|
+
transitionIndex: tagged.transitionIndex,
|
|
237
|
+
effectIndex: tagged.effectIndex
|
|
238
|
+
};
|
|
239
|
+
const perEffectRuntime = adapter.runtime?.(metadata, effect);
|
|
240
|
+
const timerDeadline = restoredTimerDeadlines.get(effect);
|
|
241
|
+
const runtime = perEffectRuntime || timerDeadline ? wrapRuntime({
|
|
242
|
+
...systemRuntime,
|
|
243
|
+
...perEffectRuntime
|
|
244
|
+
}, hasSystemRuntime, !!perEffectRuntime || hasSystemRuntime, timerDeadline) : fallbackRuntime;
|
|
245
|
+
if (effect.kind === "action") await adapter.executeAction(effect, metadata, runtime ?? wrappedSystemRuntime ?? {});
|
|
246
|
+
else await effect.exec(runtime);
|
|
247
|
+
}
|
|
248
|
+
await settle(batch);
|
|
249
|
+
pendingRootEvents.push(...batch.rootEvents);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
await drain(batch);
|
|
252
|
+
throw error;
|
|
253
|
+
} finally {
|
|
254
|
+
currentBatch = void 0;
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
async waitForEvent() {
|
|
258
|
+
if (lastTransitionIndex === void 0) throw new Error("Cannot wait for an event before the first transition or restore");
|
|
259
|
+
const queued = pendingRootEvents.shift();
|
|
260
|
+
if (queued) return queued.event;
|
|
261
|
+
return adapter.waitForEvent({
|
|
262
|
+
id: restored && nextTransitionIndex === 0 ? "event:restore:0" : `event:${lastTransitionIndex}`,
|
|
263
|
+
transitionIndex: lastTransitionIndex
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
async run(...args) {
|
|
267
|
+
if (restored || startingTransitionIndex !== 0 || nextTransitionIndex !== startingTransitionIndex) throw new DurableExecutionResumeError();
|
|
268
|
+
let [snapshot, effects] = execution.initialTransition(...args);
|
|
269
|
+
await execution.executeEffects(effects);
|
|
270
|
+
while (snapshot.status === "active") {
|
|
271
|
+
const event = await execution.waitForEvent();
|
|
272
|
+
[snapshot, effects] = execution.transition(snapshot, event);
|
|
273
|
+
await execution.executeEffects(effects);
|
|
274
|
+
}
|
|
275
|
+
const terminalSnapshot = snapshot;
|
|
276
|
+
if (terminalSnapshot.status === "done") return terminalSnapshot.output;
|
|
277
|
+
if (terminalSnapshot.status === "error") throw terminalSnapshot.error;
|
|
278
|
+
throw new DurableExecutionCancelledError();
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
return execution;
|
|
282
|
+
}
|
|
283
|
+
export { DurableExecutionCancelledError, DurableExecutionResumeError, createDurable };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { vt as ExecutableActionObject } from "./types-B2EAwOk-.js";
|
|
2
|
+
/**
|
|
3
|
+
* A serializable view of an executable effect. Actor references are replaced
|
|
4
|
+
* by their logical addresses and actor sources by their registered source
|
|
5
|
+
* keys, so a durable host can journal, deduplicate, and route effects without
|
|
6
|
+
* holding live actor references. Payload fields (`event`, `input`, `params`,
|
|
7
|
+
* `output`, `error`) pass through by reference and are only as serializable
|
|
8
|
+
* as their values.
|
|
9
|
+
*
|
|
10
|
+
* @experimental
|
|
11
|
+
*/
|
|
12
|
+
type EffectDescriptor = {
|
|
13
|
+
kind: 'builtin';
|
|
14
|
+
type: '@xstate.spawn';
|
|
15
|
+
source: string | undefined;
|
|
16
|
+
actor: string;
|
|
17
|
+
id: string;
|
|
18
|
+
src: string;
|
|
19
|
+
input: unknown;
|
|
20
|
+
} | {
|
|
21
|
+
kind: 'builtin';
|
|
22
|
+
type: '@xstate.start';
|
|
23
|
+
source: string | undefined;
|
|
24
|
+
actor: string;
|
|
25
|
+
id: string;
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'builtin';
|
|
28
|
+
type: '@xstate.raise';
|
|
29
|
+
source: string;
|
|
30
|
+
event: unknown;
|
|
31
|
+
id: string | undefined;
|
|
32
|
+
delay: number | undefined;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'builtin';
|
|
35
|
+
type: '@xstate.sendTo';
|
|
36
|
+
source: string;
|
|
37
|
+
target: string;
|
|
38
|
+
/** The target's incarnation token, when the target is a remote handle that carries one. */
|
|
39
|
+
incarnation: string | undefined;
|
|
40
|
+
event: unknown;
|
|
41
|
+
id: string | undefined;
|
|
42
|
+
delay: number | undefined;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'builtin';
|
|
45
|
+
type: '@xstate.cancel';
|
|
46
|
+
source: string;
|
|
47
|
+
id: string;
|
|
48
|
+
} | {
|
|
49
|
+
kind: 'builtin';
|
|
50
|
+
type: '@xstate.stop';
|
|
51
|
+
source: string;
|
|
52
|
+
actor: string;
|
|
53
|
+
id: string;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'builtin';
|
|
56
|
+
type: '@xstate.terminate';
|
|
57
|
+
source: string;
|
|
58
|
+
actor: string;
|
|
59
|
+
id: string;
|
|
60
|
+
status: 'done' | 'error';
|
|
61
|
+
output: unknown;
|
|
62
|
+
error: unknown;
|
|
63
|
+
} | {
|
|
64
|
+
kind: 'builtin';
|
|
65
|
+
type: '@xstate.deadLetter';
|
|
66
|
+
source: string | undefined;
|
|
67
|
+
target: string | undefined;
|
|
68
|
+
event: unknown;
|
|
69
|
+
reason: string;
|
|
70
|
+
issues: unknown;
|
|
71
|
+
error: unknown;
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'emit';
|
|
74
|
+
type: string;
|
|
75
|
+
source: string;
|
|
76
|
+
event: unknown;
|
|
77
|
+
} | {
|
|
78
|
+
kind: 'action';
|
|
79
|
+
type: string;
|
|
80
|
+
params: unknown;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Returns the serializable descriptor for an executable effect: the same
|
|
84
|
+
* discriminants (`kind`, `type`) with actor references replaced by logical
|
|
85
|
+
* addresses and actor sources by source keys. Payload fields pass through by
|
|
86
|
+
* reference.
|
|
87
|
+
*
|
|
88
|
+
* @experimental
|
|
89
|
+
*/
|
|
90
|
+
declare function getEffectDescriptor(effect: ExecutableActionObject): EffectDescriptor;
|
|
91
|
+
export { getEffectDescriptor as n, EffectDescriptor as t };
|