xstate 6.0.0-alpha.57 → 6.0.0-alpha.59
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/dist/{createActor-3dc6cdb7.development.esm.js → createActor-224ece29.development.esm.js} +124 -46
- package/dist/{createActor-c7c704c2.esm.js → createActor-5236aa33.esm.js} +124 -46
- package/dist/{createActor-2213c48b.development.cjs.js → createActor-72b67075.development.cjs.js} +124 -46
- package/dist/{createActor-c4723a2b.cjs.js → createActor-7d8aebcc.cjs.js} +124 -46
- package/dist/declarations/src/StateMachine.d.ts +10 -0
- package/dist/declarations/src/actors/index.d.ts +1 -1
- package/dist/declarations/src/base.types.d.ts +87 -0
- package/dist/declarations/src/fsm.d.ts +1 -3
- package/dist/declarations/src/graph/graph.d.ts +0 -3
- package/dist/declarations/src/index.d.ts +0 -5
- package/dist/declarations/src/setup.d.ts +29 -37
- package/dist/declarations/src/system.d.ts +1 -8
- package/dist/declarations/src/types.d.ts +28 -8
- package/dist/declarations/src/types.v6.d.ts +39 -36
- package/dist/{effectDescriptor-3121155c.cjs.js → effectDescriptor-3f1dfc32.cjs.js} +1 -1
- package/dist/{effectDescriptor-c0976206.development.cjs.js → effectDescriptor-48919b13.development.cjs.js} +1 -1
- package/dist/{effectDescriptor-95202c94.development.esm.js → effectDescriptor-7740e170.development.esm.js} +1 -1
- package/dist/{effectDescriptor-9cb5c006.esm.js → effectDescriptor-e04e6848.esm.js} +1 -1
- package/dist/{types-802dc45c.cjs.js → types-34ac08ad.cjs.js} +19 -5
- package/dist/{types-33de9b3d.development.esm.js → types-7df72520.development.esm.js} +19 -5
- package/dist/{types-ec6c763a.development.cjs.js → types-80c6a857.development.cjs.js} +19 -5
- package/dist/{types-69f39448.esm.js → types-b0313706.esm.js} +19 -5
- package/dist/xstate-actors.cjs.js +16 -5
- package/dist/xstate-actors.development.cjs.js +16 -5
- package/dist/xstate-actors.development.esm.js +17 -6
- package/dist/xstate-actors.esm.js +17 -6
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-durable.cjs.js +2 -2
- package/dist/xstate-durable.development.cjs.js +2 -2
- package/dist/xstate-durable.development.esm.js +2 -2
- package/dist/xstate-durable.esm.js +2 -2
- package/dist/xstate-durable.umd.min.js +1 -1
- package/dist/xstate-durable.umd.min.js.map +1 -1
- package/dist/xstate-fsm.umd.min.js.map +1 -1
- package/dist/xstate-graph.cjs.js +56 -42
- package/dist/xstate-graph.development.cjs.js +56 -42
- package/dist/xstate-graph.development.esm.js +56 -42
- package/dist/xstate-graph.esm.js +56 -42
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate-scxml.cjs.js +72 -55
- package/dist/xstate-scxml.development.cjs.js +72 -55
- package/dist/xstate-scxml.development.esm.js +72 -55
- package/dist/xstate-scxml.esm.js +72 -55
- package/dist/xstate-scxml.umd.min.js +1 -1
- package/dist/xstate-scxml.umd.min.js.map +1 -1
- package/dist/xstate-validation.cjs.js +1 -1
- package/dist/xstate-validation.development.cjs.js +1 -1
- package/dist/xstate-validation.development.esm.js +1 -1
- package/dist/xstate-validation.esm.js +1 -1
- package/dist/xstate-validation.umd.min.js.map +1 -1
- package/dist/xstate.cjs.js +32 -34
- package/dist/xstate.development.cjs.js +32 -34
- package/dist/xstate.development.esm.js +34 -36
- package/dist/xstate.esm.js +34 -36
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/dist/declarations/src/inspectionAmbient.d.ts +0 -1
package/dist/{createActor-3dc6cdb7.development.esm.js → createActor-224ece29.development.esm.js}
RENAMED
|
@@ -2669,6 +2669,7 @@ function computeExitSet(transitions, stateNodeSet, snapshot, resolveTransition)
|
|
|
2669
2669
|
return [...statesToExit];
|
|
2670
2670
|
}
|
|
2671
2671
|
function initialMicrostep(root, preInitialState, actorScope, initEvent, internalQueue) {
|
|
2672
|
+
root.machine._microstepHooks?.begin(actorScope.self);
|
|
2672
2673
|
const initialStateNodes = [...getInitialStateNodes(root)];
|
|
2673
2674
|
const statesForDefaultEntry = new Set();
|
|
2674
2675
|
const collectDefaultEntryStates = stateNode => {
|
|
@@ -2697,6 +2698,10 @@ function initialMicrostep(root, preInitialState, actorScope, initEvent, internal
|
|
|
2697
2698
|
/** https://www.w3.org/TR/scxml/#microstepProcedure */
|
|
2698
2699
|
function microstep(transitions, currentSnapshot, actorScope, event, isInitial, internalQueue, selectionResults) {
|
|
2699
2700
|
const executableActions = [];
|
|
2701
|
+
const internalEvents = currentSnapshot.machine._microstepHooks?.drain(actorScope.self);
|
|
2702
|
+
if (internalEvents?.length) {
|
|
2703
|
+
internalQueue.push(...internalEvents);
|
|
2704
|
+
}
|
|
2700
2705
|
if (!transitions.length) {
|
|
2701
2706
|
return [currentSnapshot, executableActions];
|
|
2702
2707
|
}
|
|
@@ -3411,6 +3416,11 @@ function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps
|
|
|
3411
3416
|
// eventless transitions should always be selected after selecting *regular* transitions
|
|
3412
3417
|
// by assigning `undefined` to `previousState` we ensure that `shouldSelectEventlessTransitions` gets always computed to true in such a case
|
|
3413
3418
|
const previousState = enabledTransitions.length ? nextSnapshot : undefined;
|
|
3419
|
+
// Failed eventless guards can raise events even when no transition wins.
|
|
3420
|
+
const raisedEvents = nextSnapshot.machine._microstepHooks?.drain(actorScope.self);
|
|
3421
|
+
if (raisedEvents?.length) {
|
|
3422
|
+
internalQueue.push(...raisedEvents);
|
|
3423
|
+
}
|
|
3414
3424
|
if (!enabledTransitions.length) {
|
|
3415
3425
|
if (!internalQueue.length) {
|
|
3416
3426
|
break;
|
|
@@ -3525,6 +3535,7 @@ function cancelTimers(snapshot, event, actorScope) {
|
|
|
3525
3535
|
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
3526
3536
|
}
|
|
3527
3537
|
function selectEventlessTransitions(snapshot, event, actorScope) {
|
|
3538
|
+
snapshot.machine._microstepHooks?.begin(actorScope.self);
|
|
3528
3539
|
const enabledTransitionSet = new Set();
|
|
3529
3540
|
const atomicStates = snapshot.nodes.filter(isAtomicStateNode);
|
|
3530
3541
|
for (const atomicStateNode of atomicStates) {
|
|
@@ -4066,7 +4077,17 @@ function mapValues(collection, iteratee) {
|
|
|
4066
4077
|
const collectionKeys = Object.keys(collection);
|
|
4067
4078
|
for (let i = 0; i < collectionKeys.length; i++) {
|
|
4068
4079
|
const key = collectionKeys[i];
|
|
4069
|
-
|
|
4080
|
+
const value = iteratee(collection[key], key, collection, i);
|
|
4081
|
+
if (key === '__proto__') {
|
|
4082
|
+
Object.defineProperty(result, key, {
|
|
4083
|
+
value,
|
|
4084
|
+
enumerable: true,
|
|
4085
|
+
configurable: true,
|
|
4086
|
+
writable: true
|
|
4087
|
+
});
|
|
4088
|
+
} else {
|
|
4089
|
+
result[key] = value;
|
|
4090
|
+
}
|
|
4070
4091
|
}
|
|
4071
4092
|
return result;
|
|
4072
4093
|
}
|
|
@@ -4233,23 +4254,14 @@ function matchesEventDescriptor(eventType, descriptor) {
|
|
|
4233
4254
|
}
|
|
4234
4255
|
|
|
4235
4256
|
function getStepEffect(actor, key) {
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
function waitForStep(actor, key) {
|
|
4239
|
-
return new Promise((resolve, reject) => {
|
|
4240
|
-
const subscription = actor.subscribe(snapshot => {
|
|
4241
|
-
const effect = snapshot.effects?.[key];
|
|
4242
|
-
if (effect?.status === 'done') {
|
|
4243
|
-
subscription.unsubscribe();
|
|
4244
|
-
resolve(effect.output);
|
|
4245
|
-
} else if (effect?.status === 'error') {
|
|
4246
|
-
subscription.unsubscribe();
|
|
4247
|
-
reject(effect.error);
|
|
4248
|
-
}
|
|
4249
|
-
});
|
|
4250
|
-
});
|
|
4257
|
+
const effects = actor.getSnapshot().effects;
|
|
4258
|
+
return effects && Object.prototype.hasOwnProperty.call(effects, key) ? effects[key] : undefined;
|
|
4251
4259
|
}
|
|
4252
4260
|
|
|
4261
|
+
// Live promises belong to an actor incarnation, unlike its persisted journal.
|
|
4262
|
+
// A restored active record has no promise here and must be retried locally.
|
|
4263
|
+
const pendingSteps = new WeakMap();
|
|
4264
|
+
|
|
4253
4265
|
/**
|
|
4254
4266
|
* Runs one keyed step of an async actor with the built-in journal: the
|
|
4255
4267
|
* result is memoized in the actor's own snapshot (`effects[key]`), so a
|
|
@@ -4267,29 +4279,66 @@ async function runStep(actor, key, exec, sendSelf = event => void actor.system.s
|
|
|
4267
4279
|
if (effect?.status === 'error') {
|
|
4268
4280
|
throw effect.error;
|
|
4269
4281
|
}
|
|
4270
|
-
|
|
4271
|
-
|
|
4282
|
+
const running = pendingSteps.get(actor)?.get(key);
|
|
4283
|
+
if (running) {
|
|
4284
|
+
return running;
|
|
4272
4285
|
}
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
key
|
|
4276
|
-
});
|
|
4277
|
-
try {
|
|
4278
|
-
const output = await exec();
|
|
4279
|
-
sendSelf({
|
|
4280
|
-
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
4281
|
-
key,
|
|
4282
|
-
output
|
|
4283
|
-
});
|
|
4284
|
-
return output;
|
|
4285
|
-
} catch (error) {
|
|
4286
|
-
sendSelf({
|
|
4287
|
-
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
4288
|
-
key,
|
|
4289
|
-
error
|
|
4290
|
-
});
|
|
4291
|
-
throw error;
|
|
4286
|
+
if (actor.getSnapshot().status !== 'active') {
|
|
4287
|
+
throw new Error(`Actor terminated before step "${key}" completed`);
|
|
4292
4288
|
}
|
|
4289
|
+
let resolve;
|
|
4290
|
+
let reject;
|
|
4291
|
+
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
4292
|
+
resolve = resolvePromise;
|
|
4293
|
+
reject = rejectPromise;
|
|
4294
|
+
});
|
|
4295
|
+
let steps = pendingSteps.get(actor);
|
|
4296
|
+
if (!steps) {
|
|
4297
|
+
steps = new Map();
|
|
4298
|
+
pendingSteps.set(actor, steps);
|
|
4299
|
+
}
|
|
4300
|
+
steps.set(key, promise);
|
|
4301
|
+
// oxlint-disable-next-line prefer-const
|
|
4302
|
+
let subscription;
|
|
4303
|
+
const cleanup = () => {
|
|
4304
|
+
subscription?.unsubscribe();
|
|
4305
|
+
steps.delete(key);
|
|
4306
|
+
if (!steps.size) {
|
|
4307
|
+
pendingSteps.delete(actor);
|
|
4308
|
+
}
|
|
4309
|
+
};
|
|
4310
|
+
void promise.then(cleanup, cleanup);
|
|
4311
|
+
subscription = actor.subscribe({
|
|
4312
|
+
error: reject,
|
|
4313
|
+
complete: () => reject(new Error(`Actor terminated before step "${key}" completed`))
|
|
4314
|
+
});
|
|
4315
|
+
void (async () => {
|
|
4316
|
+
try {
|
|
4317
|
+
sendSelf({
|
|
4318
|
+
type: XSTATE_LOGIC_EFFECT_START,
|
|
4319
|
+
key
|
|
4320
|
+
});
|
|
4321
|
+
const output = await exec();
|
|
4322
|
+
if (actor.getSnapshot().status === 'active') {
|
|
4323
|
+
sendSelf({
|
|
4324
|
+
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
4325
|
+
key,
|
|
4326
|
+
output
|
|
4327
|
+
});
|
|
4328
|
+
resolve(output);
|
|
4329
|
+
}
|
|
4330
|
+
} catch (error) {
|
|
4331
|
+
if (actor.getSnapshot().status === 'active') {
|
|
4332
|
+
sendSelf({
|
|
4333
|
+
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
4334
|
+
key,
|
|
4335
|
+
error
|
|
4336
|
+
});
|
|
4337
|
+
}
|
|
4338
|
+
reject(error);
|
|
4339
|
+
}
|
|
4340
|
+
})().catch(reject);
|
|
4341
|
+
return promise;
|
|
4293
4342
|
}
|
|
4294
4343
|
|
|
4295
4344
|
/**
|
|
@@ -4931,6 +4980,8 @@ class RuntimeSystem {
|
|
|
4931
4980
|
}
|
|
4932
4981
|
}
|
|
4933
4982
|
}
|
|
4983
|
+
|
|
4984
|
+
/** @internal */
|
|
4934
4985
|
function createRuntimeSystem(rootActor, options) {
|
|
4935
4986
|
return new RuntimeSystem(rootActor, options);
|
|
4936
4987
|
}
|
|
@@ -5283,12 +5334,12 @@ class Actor {
|
|
|
5283
5334
|
}
|
|
5284
5335
|
|
|
5285
5336
|
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
5286
|
-
_recoverOrError(err, snapshot) {
|
|
5337
|
+
_recoverOrError(err, snapshot, previousSnapshot) {
|
|
5287
5338
|
if (this._tryHandleExecutionError(err, snapshot)) {
|
|
5288
5339
|
return true;
|
|
5289
5340
|
}
|
|
5290
5341
|
this._setErrorSnapshot(err);
|
|
5291
|
-
this._error(err);
|
|
5342
|
+
this._error(err, previousSnapshot);
|
|
5292
5343
|
return false;
|
|
5293
5344
|
}
|
|
5294
5345
|
_tryHandleExecutionError(err, snapshot = this._snapshot) {
|
|
@@ -5621,6 +5672,7 @@ class Actor {
|
|
|
5621
5672
|
return this;
|
|
5622
5673
|
}
|
|
5623
5674
|
_process(event) {
|
|
5675
|
+
const previousSnapshot = this._snapshot;
|
|
5624
5676
|
let nextState;
|
|
5625
5677
|
let caughtError;
|
|
5626
5678
|
try {
|
|
@@ -5651,7 +5703,7 @@ class Actor {
|
|
|
5651
5703
|
executeExecutableEffects(effects, this._actorScope);
|
|
5652
5704
|
this.update(snapshot, event);
|
|
5653
5705
|
} catch (err) {
|
|
5654
|
-
if (!this._recoverOrError(err, snapshot)) {
|
|
5706
|
+
if (!this._recoverOrError(err, snapshot, previousSnapshot)) {
|
|
5655
5707
|
this._inspectTransition(this._snapshot, event);
|
|
5656
5708
|
}
|
|
5657
5709
|
return;
|
|
@@ -5737,8 +5789,39 @@ class Actor {
|
|
|
5737
5789
|
}
|
|
5738
5790
|
});
|
|
5739
5791
|
}
|
|
5740
|
-
_error(err) {
|
|
5792
|
+
_error(err, previousSnapshot) {
|
|
5741
5793
|
this._stopProcedure();
|
|
5794
|
+
// Transition calculation or effect execution may fail before child stop
|
|
5795
|
+
// effects run. Keep the previous snapshot's children when calculation has
|
|
5796
|
+
// already removed them, and include children owned by non-machine logic
|
|
5797
|
+
// from the runtime registry.
|
|
5798
|
+
const children = new Set();
|
|
5799
|
+
for (const snapshot of [this._snapshot, previousSnapshot]) {
|
|
5800
|
+
const snapshotChildren = snapshot?.children;
|
|
5801
|
+
for (const child of Object.values(snapshotChildren ?? {})) {
|
|
5802
|
+
if (child?._parent === this && typeof child.getSnapshot === 'function') {
|
|
5803
|
+
children.add(child);
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
for (const child of this.system._peekChildren?.()?.values() ?? []) {
|
|
5808
|
+
if (child._parent === this) {
|
|
5809
|
+
children.add(child);
|
|
5810
|
+
}
|
|
5811
|
+
}
|
|
5812
|
+
for (const child of children) {
|
|
5813
|
+
try {
|
|
5814
|
+
if (child.getSnapshot().status !== 'active') {
|
|
5815
|
+
continue;
|
|
5816
|
+
}
|
|
5817
|
+
const result = this.system.stopActor(child);
|
|
5818
|
+
if (result) {
|
|
5819
|
+
void Promise.resolve(result).catch(reportUnhandledError);
|
|
5820
|
+
}
|
|
5821
|
+
} catch (error) {
|
|
5822
|
+
reportUnhandledError(error);
|
|
5823
|
+
}
|
|
5824
|
+
}
|
|
5742
5825
|
if (!this.observers?.size) {
|
|
5743
5826
|
if (!this._parent) {
|
|
5744
5827
|
this._reportUnlessObserved(err);
|
|
@@ -5765,11 +5848,6 @@ class Actor {
|
|
|
5765
5848
|
this.system._relay(this, this._parent, createErrorActorEvent(this.id, err, this.sessionId));
|
|
5766
5849
|
}
|
|
5767
5850
|
}
|
|
5768
|
-
// TODO: atm children don't belong entirely to the actor so
|
|
5769
|
-
// in a way - it's not even super aware of them
|
|
5770
|
-
// so we can't stop them from here but we really should!
|
|
5771
|
-
// right now, they are being stopped within the machine's transition
|
|
5772
|
-
// but that could throw and leave us with "orphaned" active actors
|
|
5773
5851
|
_stopProcedure() {
|
|
5774
5852
|
if (this._processingStatus !== ProcessingStatus.Running) {
|
|
5775
5853
|
// Actor already stopped; do nothing
|
|
@@ -2651,6 +2651,7 @@ function computeExitSet(transitions, stateNodeSet, snapshot, resolveTransition)
|
|
|
2651
2651
|
return [...statesToExit];
|
|
2652
2652
|
}
|
|
2653
2653
|
function initialMicrostep(root, preInitialState, actorScope, initEvent, internalQueue) {
|
|
2654
|
+
root.machine._microstepHooks?.begin(actorScope.self);
|
|
2654
2655
|
const initialStateNodes = [...getInitialStateNodes(root)];
|
|
2655
2656
|
const statesForDefaultEntry = new Set();
|
|
2656
2657
|
const collectDefaultEntryStates = stateNode => {
|
|
@@ -2679,6 +2680,10 @@ function initialMicrostep(root, preInitialState, actorScope, initEvent, internal
|
|
|
2679
2680
|
/** https://www.w3.org/TR/scxml/#microstepProcedure */
|
|
2680
2681
|
function microstep(transitions, currentSnapshot, actorScope, event, isInitial, internalQueue, selectionResults) {
|
|
2681
2682
|
const executableActions = [];
|
|
2683
|
+
const internalEvents = currentSnapshot.machine._microstepHooks?.drain(actorScope.self);
|
|
2684
|
+
if (internalEvents?.length) {
|
|
2685
|
+
internalQueue.push(...internalEvents);
|
|
2686
|
+
}
|
|
2682
2687
|
if (!transitions.length) {
|
|
2683
2688
|
return [currentSnapshot, executableActions];
|
|
2684
2689
|
}
|
|
@@ -3393,6 +3398,11 @@ function macrostep(snapshot, event, actorScope, internalQueue, initialMicrosteps
|
|
|
3393
3398
|
// eventless transitions should always be selected after selecting *regular* transitions
|
|
3394
3399
|
// by assigning `undefined` to `previousState` we ensure that `shouldSelectEventlessTransitions` gets always computed to true in such a case
|
|
3395
3400
|
const previousState = enabledTransitions.length ? nextSnapshot : undefined;
|
|
3401
|
+
// Failed eventless guards can raise events even when no transition wins.
|
|
3402
|
+
const raisedEvents = nextSnapshot.machine._microstepHooks?.drain(actorScope.self);
|
|
3403
|
+
if (raisedEvents?.length) {
|
|
3404
|
+
internalQueue.push(...raisedEvents);
|
|
3405
|
+
}
|
|
3396
3406
|
if (!enabledTransitions.length) {
|
|
3397
3407
|
if (!internalQueue.length) {
|
|
3398
3408
|
break;
|
|
@@ -3507,6 +3517,7 @@ function cancelTimers(snapshot, event, actorScope) {
|
|
|
3507
3517
|
return resolveActionsWithContext(snapshot, event, actorScope, actions);
|
|
3508
3518
|
}
|
|
3509
3519
|
function selectEventlessTransitions(snapshot, event, actorScope) {
|
|
3520
|
+
snapshot.machine._microstepHooks?.begin(actorScope.self);
|
|
3510
3521
|
const enabledTransitionSet = new Set();
|
|
3511
3522
|
const atomicStates = snapshot.nodes.filter(isAtomicStateNode);
|
|
3512
3523
|
for (const atomicStateNode of atomicStates) {
|
|
@@ -4045,7 +4056,17 @@ function mapValues(collection, iteratee) {
|
|
|
4045
4056
|
const collectionKeys = Object.keys(collection);
|
|
4046
4057
|
for (let i = 0; i < collectionKeys.length; i++) {
|
|
4047
4058
|
const key = collectionKeys[i];
|
|
4048
|
-
|
|
4059
|
+
const value = iteratee(collection[key], key, collection, i);
|
|
4060
|
+
if (key === '__proto__') {
|
|
4061
|
+
Object.defineProperty(result, key, {
|
|
4062
|
+
value,
|
|
4063
|
+
enumerable: true,
|
|
4064
|
+
configurable: true,
|
|
4065
|
+
writable: true
|
|
4066
|
+
});
|
|
4067
|
+
} else {
|
|
4068
|
+
result[key] = value;
|
|
4069
|
+
}
|
|
4049
4070
|
}
|
|
4050
4071
|
return result;
|
|
4051
4072
|
}
|
|
@@ -4203,23 +4224,14 @@ function matchesEventDescriptor(eventType, descriptor) {
|
|
|
4203
4224
|
}
|
|
4204
4225
|
|
|
4205
4226
|
function getStepEffect(actor, key) {
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
function waitForStep(actor, key) {
|
|
4209
|
-
return new Promise((resolve, reject) => {
|
|
4210
|
-
const subscription = actor.subscribe(snapshot => {
|
|
4211
|
-
const effect = snapshot.effects?.[key];
|
|
4212
|
-
if (effect?.status === 'done') {
|
|
4213
|
-
subscription.unsubscribe();
|
|
4214
|
-
resolve(effect.output);
|
|
4215
|
-
} else if (effect?.status === 'error') {
|
|
4216
|
-
subscription.unsubscribe();
|
|
4217
|
-
reject(effect.error);
|
|
4218
|
-
}
|
|
4219
|
-
});
|
|
4220
|
-
});
|
|
4227
|
+
const effects = actor.getSnapshot().effects;
|
|
4228
|
+
return effects && Object.prototype.hasOwnProperty.call(effects, key) ? effects[key] : undefined;
|
|
4221
4229
|
}
|
|
4222
4230
|
|
|
4231
|
+
// Live promises belong to an actor incarnation, unlike its persisted journal.
|
|
4232
|
+
// A restored active record has no promise here and must be retried locally.
|
|
4233
|
+
const pendingSteps = new WeakMap();
|
|
4234
|
+
|
|
4223
4235
|
/**
|
|
4224
4236
|
* Runs one keyed step of an async actor with the built-in journal: the
|
|
4225
4237
|
* result is memoized in the actor's own snapshot (`effects[key]`), so a
|
|
@@ -4237,29 +4249,66 @@ async function runStep(actor, key, exec, sendSelf = event => void actor.system.s
|
|
|
4237
4249
|
if (effect?.status === 'error') {
|
|
4238
4250
|
throw effect.error;
|
|
4239
4251
|
}
|
|
4240
|
-
|
|
4241
|
-
|
|
4252
|
+
const running = pendingSteps.get(actor)?.get(key);
|
|
4253
|
+
if (running) {
|
|
4254
|
+
return running;
|
|
4242
4255
|
}
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
key
|
|
4246
|
-
});
|
|
4247
|
-
try {
|
|
4248
|
-
const output = await exec();
|
|
4249
|
-
sendSelf({
|
|
4250
|
-
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
4251
|
-
key,
|
|
4252
|
-
output
|
|
4253
|
-
});
|
|
4254
|
-
return output;
|
|
4255
|
-
} catch (error) {
|
|
4256
|
-
sendSelf({
|
|
4257
|
-
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
4258
|
-
key,
|
|
4259
|
-
error
|
|
4260
|
-
});
|
|
4261
|
-
throw error;
|
|
4256
|
+
if (actor.getSnapshot().status !== 'active') {
|
|
4257
|
+
throw new Error(`Actor terminated before step "${key}" completed`);
|
|
4262
4258
|
}
|
|
4259
|
+
let resolve;
|
|
4260
|
+
let reject;
|
|
4261
|
+
const promise = new Promise((resolvePromise, rejectPromise) => {
|
|
4262
|
+
resolve = resolvePromise;
|
|
4263
|
+
reject = rejectPromise;
|
|
4264
|
+
});
|
|
4265
|
+
let steps = pendingSteps.get(actor);
|
|
4266
|
+
if (!steps) {
|
|
4267
|
+
steps = new Map();
|
|
4268
|
+
pendingSteps.set(actor, steps);
|
|
4269
|
+
}
|
|
4270
|
+
steps.set(key, promise);
|
|
4271
|
+
// oxlint-disable-next-line prefer-const
|
|
4272
|
+
let subscription;
|
|
4273
|
+
const cleanup = () => {
|
|
4274
|
+
subscription?.unsubscribe();
|
|
4275
|
+
steps.delete(key);
|
|
4276
|
+
if (!steps.size) {
|
|
4277
|
+
pendingSteps.delete(actor);
|
|
4278
|
+
}
|
|
4279
|
+
};
|
|
4280
|
+
void promise.then(cleanup, cleanup);
|
|
4281
|
+
subscription = actor.subscribe({
|
|
4282
|
+
error: reject,
|
|
4283
|
+
complete: () => reject(new Error(`Actor terminated before step "${key}" completed`))
|
|
4284
|
+
});
|
|
4285
|
+
void (async () => {
|
|
4286
|
+
try {
|
|
4287
|
+
sendSelf({
|
|
4288
|
+
type: XSTATE_LOGIC_EFFECT_START,
|
|
4289
|
+
key
|
|
4290
|
+
});
|
|
4291
|
+
const output = await exec();
|
|
4292
|
+
if (actor.getSnapshot().status === 'active') {
|
|
4293
|
+
sendSelf({
|
|
4294
|
+
type: XSTATE_LOGIC_EFFECT_RESOLVE,
|
|
4295
|
+
key,
|
|
4296
|
+
output
|
|
4297
|
+
});
|
|
4298
|
+
resolve(output);
|
|
4299
|
+
}
|
|
4300
|
+
} catch (error) {
|
|
4301
|
+
if (actor.getSnapshot().status === 'active') {
|
|
4302
|
+
sendSelf({
|
|
4303
|
+
type: XSTATE_LOGIC_EFFECT_REJECT,
|
|
4304
|
+
key,
|
|
4305
|
+
error
|
|
4306
|
+
});
|
|
4307
|
+
}
|
|
4308
|
+
reject(error);
|
|
4309
|
+
}
|
|
4310
|
+
})().catch(reject);
|
|
4311
|
+
return promise;
|
|
4263
4312
|
}
|
|
4264
4313
|
|
|
4265
4314
|
/**
|
|
@@ -4898,6 +4947,8 @@ class RuntimeSystem {
|
|
|
4898
4947
|
}
|
|
4899
4948
|
}
|
|
4900
4949
|
}
|
|
4950
|
+
|
|
4951
|
+
/** @internal */
|
|
4901
4952
|
function createRuntimeSystem(rootActor, options) {
|
|
4902
4953
|
return new RuntimeSystem(rootActor, options);
|
|
4903
4954
|
}
|
|
@@ -5250,12 +5301,12 @@ class Actor {
|
|
|
5250
5301
|
}
|
|
5251
5302
|
|
|
5252
5303
|
/** Recover via the logic's error event if possible; otherwise error out. */
|
|
5253
|
-
_recoverOrError(err, snapshot) {
|
|
5304
|
+
_recoverOrError(err, snapshot, previousSnapshot) {
|
|
5254
5305
|
if (this._tryHandleExecutionError(err, snapshot)) {
|
|
5255
5306
|
return true;
|
|
5256
5307
|
}
|
|
5257
5308
|
this._setErrorSnapshot(err);
|
|
5258
|
-
this._error(err);
|
|
5309
|
+
this._error(err, previousSnapshot);
|
|
5259
5310
|
return false;
|
|
5260
5311
|
}
|
|
5261
5312
|
_tryHandleExecutionError(err, snapshot = this._snapshot) {
|
|
@@ -5588,6 +5639,7 @@ class Actor {
|
|
|
5588
5639
|
return this;
|
|
5589
5640
|
}
|
|
5590
5641
|
_process(event) {
|
|
5642
|
+
const previousSnapshot = this._snapshot;
|
|
5591
5643
|
let nextState;
|
|
5592
5644
|
let caughtError;
|
|
5593
5645
|
try {
|
|
@@ -5618,7 +5670,7 @@ class Actor {
|
|
|
5618
5670
|
executeExecutableEffects(effects, this._actorScope);
|
|
5619
5671
|
this.update(snapshot, event);
|
|
5620
5672
|
} catch (err) {
|
|
5621
|
-
if (!this._recoverOrError(err, snapshot)) {
|
|
5673
|
+
if (!this._recoverOrError(err, snapshot, previousSnapshot)) {
|
|
5622
5674
|
this._inspectTransition(this._snapshot, event);
|
|
5623
5675
|
}
|
|
5624
5676
|
return;
|
|
@@ -5704,8 +5756,39 @@ class Actor {
|
|
|
5704
5756
|
}
|
|
5705
5757
|
});
|
|
5706
5758
|
}
|
|
5707
|
-
_error(err) {
|
|
5759
|
+
_error(err, previousSnapshot) {
|
|
5708
5760
|
this._stopProcedure();
|
|
5761
|
+
// Transition calculation or effect execution may fail before child stop
|
|
5762
|
+
// effects run. Keep the previous snapshot's children when calculation has
|
|
5763
|
+
// already removed them, and include children owned by non-machine logic
|
|
5764
|
+
// from the runtime registry.
|
|
5765
|
+
const children = new Set();
|
|
5766
|
+
for (const snapshot of [this._snapshot, previousSnapshot]) {
|
|
5767
|
+
const snapshotChildren = snapshot?.children;
|
|
5768
|
+
for (const child of Object.values(snapshotChildren ?? {})) {
|
|
5769
|
+
if (child?._parent === this && typeof child.getSnapshot === 'function') {
|
|
5770
|
+
children.add(child);
|
|
5771
|
+
}
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
for (const child of this.system._peekChildren?.()?.values() ?? []) {
|
|
5775
|
+
if (child._parent === this) {
|
|
5776
|
+
children.add(child);
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
for (const child of children) {
|
|
5780
|
+
try {
|
|
5781
|
+
if (child.getSnapshot().status !== 'active') {
|
|
5782
|
+
continue;
|
|
5783
|
+
}
|
|
5784
|
+
const result = this.system.stopActor(child);
|
|
5785
|
+
if (result) {
|
|
5786
|
+
void Promise.resolve(result).catch(reportUnhandledError);
|
|
5787
|
+
}
|
|
5788
|
+
} catch (error) {
|
|
5789
|
+
reportUnhandledError(error);
|
|
5790
|
+
}
|
|
5791
|
+
}
|
|
5709
5792
|
if (!this.observers?.size) {
|
|
5710
5793
|
if (!this._parent) {
|
|
5711
5794
|
this._reportUnlessObserved(err);
|
|
@@ -5732,11 +5815,6 @@ class Actor {
|
|
|
5732
5815
|
this.system._relay(this, this._parent, createErrorActorEvent(this.id, err, this.sessionId));
|
|
5733
5816
|
}
|
|
5734
5817
|
}
|
|
5735
|
-
// TODO: atm children don't belong entirely to the actor so
|
|
5736
|
-
// in a way - it's not even super aware of them
|
|
5737
|
-
// so we can't stop them from here but we really should!
|
|
5738
|
-
// right now, they are being stopped within the machine's transition
|
|
5739
|
-
// but that could throw and leave us with "orphaned" active actors
|
|
5740
5818
|
_stopProcedure() {
|
|
5741
5819
|
if (this._processingStatus !== ProcessingStatus.Running) {
|
|
5742
5820
|
// Actor already stopped; do nothing
|