xstate 5.0.0-beta.20 → 5.0.0-beta.21
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/actions/dist/xstate-actions.cjs.js +1 -1
- package/actions/dist/xstate-actions.development.cjs.js +1 -1
- package/actions/dist/xstate-actions.development.esm.js +1 -1
- package/actions/dist/xstate-actions.esm.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js +1 -1
- package/actions/dist/xstate-actions.umd.min.js.map +1 -1
- package/actors/dist/xstate-actors.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.cjs.js +1 -1
- package/actors/dist/xstate-actors.development.esm.js +1 -1
- package/actors/dist/xstate-actors.esm.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js +1 -1
- package/actors/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/{actions-b299d008.development.esm.js → actions-49f0501e.development.esm.js} +136 -63
- package/dist/{actions-a8a9433c.esm.js → actions-5039c951.esm.js} +134 -64
- package/dist/{actions-d1c41ed3.development.cjs.js → actions-a95d2e66.development.cjs.js} +136 -62
- package/dist/{actions-069d9805.cjs.js → actions-c619a105.cjs.js} +134 -63
- package/dist/declarations/src/Machine.d.ts +2 -2
- package/dist/declarations/src/State.d.ts +4 -7
- package/dist/declarations/src/StateMachine.d.ts +7 -6
- package/dist/declarations/src/StateNode.d.ts +3 -3
- package/dist/declarations/src/actions/send.d.ts +1 -1
- package/dist/declarations/src/actions/stop.d.ts +1 -1
- package/dist/declarations/src/actions.d.ts +2 -2
- package/dist/declarations/src/actors/callback.d.ts +4 -4
- package/dist/declarations/src/actors/observable.d.ts +7 -4
- package/dist/declarations/src/actors/promise.d.ts +4 -4
- package/dist/declarations/src/types.d.ts +41 -29
- package/dist/declarations/src/utils.d.ts +2 -2
- package/dist/xstate.cjs.js +11 -10
- package/dist/xstate.development.cjs.js +11 -10
- package/dist/xstate.development.esm.js +12 -11
- package/dist/xstate.esm.js +12 -11
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/guards/dist/xstate-guards.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.cjs.js +1 -1
- package/guards/dist/xstate-guards.development.esm.js +1 -1
- package/guards/dist/xstate-guards.esm.js +1 -1
- package/guards/dist/xstate-guards.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
var dev_dist_xstateDev = require('../dev/dist/xstate-dev.development.cjs.js');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* `T | unknown` reduces to `unknown` and that can be problematic when it comes to contextual typing.
|
|
7
|
+
* It especially is a problem when the union has a function member, like here:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* declare function test(cbOrVal: ((arg: number) => unknown) | unknown): void;
|
|
11
|
+
* test((arg) => {}) // oops, implicit any
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* This type can be used to avoid this problem. This union represents the same value space as `unknown`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
5
17
|
// https://github.com/microsoft/TypeScript/issues/23182#issuecomment-379091887
|
|
6
18
|
|
|
7
19
|
/**
|
|
@@ -422,27 +434,18 @@ function toArray(value) {
|
|
|
422
434
|
}
|
|
423
435
|
return toArrayStrict(value);
|
|
424
436
|
}
|
|
425
|
-
function mapContext(mapper, context, event) {
|
|
437
|
+
function mapContext(mapper, context, event, self) {
|
|
426
438
|
if (typeof mapper === 'function') {
|
|
427
439
|
return mapper({
|
|
428
440
|
context,
|
|
429
|
-
event
|
|
441
|
+
event,
|
|
442
|
+
self
|
|
430
443
|
});
|
|
431
444
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
context,
|
|
435
|
-
event
|
|
436
|
-
};
|
|
437
|
-
for (const key of Object.keys(mapper)) {
|
|
438
|
-
const subMapper = mapper[key];
|
|
439
|
-
if (typeof subMapper === 'function') {
|
|
440
|
-
result[key] = subMapper(args);
|
|
441
|
-
} else {
|
|
442
|
-
result[key] = subMapper;
|
|
443
|
-
}
|
|
445
|
+
if (typeof mapper === 'object' && Object.values(mapper).some(val => typeof val === 'function')) {
|
|
446
|
+
console.warn(`Dynamically mapping values to individual properties is deprecated. Use a single function that returns the mapped object instead.\nFound object containing properties whose values are possibly mapping functions: ${Object.entries(mapper).filter(([key, value]) => typeof value === 'function').map(([key, value]) => `\n - ${key}: ${value.toString().replace(/\n\s*/g, '')}`).join('')}`);
|
|
444
447
|
}
|
|
445
|
-
return
|
|
448
|
+
return mapper;
|
|
446
449
|
}
|
|
447
450
|
function isPromiseLike(value) {
|
|
448
451
|
if (value instanceof Promise) {
|
|
@@ -494,13 +497,12 @@ function toInvokeConfig(invocable, id) {
|
|
|
494
497
|
};
|
|
495
498
|
}
|
|
496
499
|
function toObserver(nextHandler, errorHandler, completionHandler) {
|
|
497
|
-
const noop = () => {};
|
|
498
500
|
const isObserver = typeof nextHandler === 'object';
|
|
499
|
-
const self = isObserver ? nextHandler :
|
|
501
|
+
const self = isObserver ? nextHandler : undefined;
|
|
500
502
|
return {
|
|
501
|
-
next: (
|
|
502
|
-
error: (
|
|
503
|
-
complete: (
|
|
503
|
+
next: (isObserver ? nextHandler.next : nextHandler)?.bind(self),
|
|
504
|
+
error: (isObserver ? nextHandler.error : errorHandler)?.bind(self),
|
|
505
|
+
complete: (isObserver ? nextHandler.complete : completionHandler)?.bind(self)
|
|
504
506
|
};
|
|
505
507
|
}
|
|
506
508
|
function createInvokeId(stateNodeId, index) {
|
|
@@ -514,7 +516,7 @@ function resolveReferencedActor(referenced) {
|
|
|
514
516
|
}
|
|
515
517
|
|
|
516
518
|
function fromCallback(invokeCallback) {
|
|
517
|
-
|
|
519
|
+
return {
|
|
518
520
|
config: invokeCallback,
|
|
519
521
|
start: (_state, {
|
|
520
522
|
self
|
|
@@ -580,19 +582,20 @@ function fromCallback(invokeCallback) {
|
|
|
580
582
|
},
|
|
581
583
|
getSnapshot: () => undefined,
|
|
582
584
|
getPersistedState: ({
|
|
583
|
-
input
|
|
584
|
-
|
|
585
|
+
input,
|
|
586
|
+
canceled
|
|
587
|
+
}) => ({
|
|
588
|
+
input,
|
|
589
|
+
canceled
|
|
590
|
+
})
|
|
585
591
|
};
|
|
586
|
-
return logic;
|
|
587
592
|
}
|
|
588
593
|
|
|
589
594
|
function fromObservable(observableCreator) {
|
|
590
595
|
const nextEventType = '$$xstate.next';
|
|
591
596
|
const errorEventType = '$$xstate.error';
|
|
592
597
|
const completeEventType = '$$xstate.complete';
|
|
593
|
-
|
|
594
|
-
// TODO: add event types
|
|
595
|
-
const logic = {
|
|
598
|
+
return {
|
|
596
599
|
config: observableCreator,
|
|
597
600
|
transition: (state, event, {
|
|
598
601
|
self,
|
|
@@ -622,6 +625,7 @@ function fromObservable(observableCreator) {
|
|
|
622
625
|
status: 'error',
|
|
623
626
|
input: undefined,
|
|
624
627
|
data: event.data,
|
|
628
|
+
// TODO: if we keep this as `data` we should reflect this in the type
|
|
625
629
|
subscription: undefined
|
|
626
630
|
};
|
|
627
631
|
case completeEventType:
|
|
@@ -699,7 +703,6 @@ function fromObservable(observableCreator) {
|
|
|
699
703
|
subscription: undefined
|
|
700
704
|
})
|
|
701
705
|
};
|
|
702
|
-
return logic;
|
|
703
706
|
}
|
|
704
707
|
|
|
705
708
|
/**
|
|
@@ -716,7 +719,7 @@ function fromEventObservable(lazyObservable) {
|
|
|
716
719
|
const completeEventType = '$$xstate.complete';
|
|
717
720
|
|
|
718
721
|
// TODO: event types
|
|
719
|
-
|
|
722
|
+
return {
|
|
720
723
|
config: lazyObservable,
|
|
721
724
|
transition: (state, event) => {
|
|
722
725
|
if (state.status !== 'active') {
|
|
@@ -729,6 +732,7 @@ function fromEventObservable(lazyObservable) {
|
|
|
729
732
|
status: 'error',
|
|
730
733
|
input: undefined,
|
|
731
734
|
data: event.data,
|
|
735
|
+
// TODO: if we keep this as `data` we should reflect this in the type
|
|
732
736
|
subscription: undefined
|
|
733
737
|
};
|
|
734
738
|
case completeEventType:
|
|
@@ -803,7 +807,6 @@ function fromEventObservable(lazyObservable) {
|
|
|
803
807
|
subscription: undefined
|
|
804
808
|
})
|
|
805
809
|
};
|
|
806
|
-
return logic;
|
|
807
810
|
}
|
|
808
811
|
|
|
809
812
|
const resolveEventType = '$$xstate.resolve';
|
|
@@ -831,6 +834,7 @@ promiseCreator) {
|
|
|
831
834
|
...state,
|
|
832
835
|
status: 'error',
|
|
833
836
|
data: event.data,
|
|
837
|
+
// TODO: if we keep this as `data` we should reflect this in the type
|
|
834
838
|
input: undefined
|
|
835
839
|
};
|
|
836
840
|
case stopSignalType:
|
|
@@ -940,6 +944,19 @@ function createEmptyActor() {
|
|
|
940
944
|
return interpret(emptyLogic);
|
|
941
945
|
}
|
|
942
946
|
|
|
947
|
+
/**
|
|
948
|
+
* This function makes sure that unhandled errors are thrown in a separate macrotask.
|
|
949
|
+
* It allows those errors to be detected by global error handlers and reported to bug tracking services
|
|
950
|
+
* without interrupting our own stack of execution.
|
|
951
|
+
*
|
|
952
|
+
* @param err error to be thrown
|
|
953
|
+
*/
|
|
954
|
+
function reportUnhandledError(err) {
|
|
955
|
+
setTimeout(() => {
|
|
956
|
+
throw err;
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
|
|
943
960
|
function createSystem() {
|
|
944
961
|
let sessionIdCounter = 0;
|
|
945
962
|
const children = new Map();
|
|
@@ -1108,29 +1125,38 @@ class Interpreter {
|
|
|
1108
1125
|
deferredFn();
|
|
1109
1126
|
}
|
|
1110
1127
|
for (const observer of this.observers) {
|
|
1111
|
-
|
|
1128
|
+
// TODO: should observers be notified in case of the error?
|
|
1129
|
+
try {
|
|
1130
|
+
observer.next?.(snapshot);
|
|
1131
|
+
} catch (err) {
|
|
1132
|
+
reportUnhandledError(err);
|
|
1133
|
+
}
|
|
1112
1134
|
}
|
|
1113
1135
|
const status = this.logic.getStatus?.(state);
|
|
1114
1136
|
switch (status?.status) {
|
|
1115
1137
|
case 'done':
|
|
1116
1138
|
this._stopProcedure();
|
|
1139
|
+
this._complete();
|
|
1117
1140
|
this._doneEvent = doneInvoke(this.id, status.data);
|
|
1118
1141
|
this._parent?.send(this._doneEvent);
|
|
1119
|
-
this._complete();
|
|
1120
1142
|
break;
|
|
1121
1143
|
case 'error':
|
|
1122
1144
|
this._stopProcedure();
|
|
1123
|
-
this._parent?.send(error(this.id, status.data));
|
|
1124
1145
|
this._error(status.data);
|
|
1146
|
+
this._parent?.send(error(this.id, status.data));
|
|
1125
1147
|
break;
|
|
1126
1148
|
}
|
|
1127
1149
|
}
|
|
1128
1150
|
subscribe(nextListenerOrObserver, errorListener, completeListener) {
|
|
1129
1151
|
const observer = toObserver(nextListenerOrObserver, errorListener, completeListener);
|
|
1130
|
-
this.
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1152
|
+
if (this.status !== ActorStatus.Stopped) {
|
|
1153
|
+
this.observers.add(observer);
|
|
1154
|
+
} else {
|
|
1155
|
+
try {
|
|
1156
|
+
observer.complete?.();
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
reportUnhandledError(err);
|
|
1159
|
+
}
|
|
1134
1160
|
}
|
|
1135
1161
|
return {
|
|
1136
1162
|
unsubscribe: () => {
|
|
@@ -1152,8 +1178,26 @@ class Interpreter {
|
|
|
1152
1178
|
this.system._set(this._systemId, this);
|
|
1153
1179
|
}
|
|
1154
1180
|
this.status = ActorStatus.Running;
|
|
1181
|
+
const status = this.logic.getStatus?.(this._state);
|
|
1182
|
+
switch (status?.status) {
|
|
1183
|
+
case 'done':
|
|
1184
|
+
// a state machine can be "done" upon intialization (it could reach a final state using initial microsteps)
|
|
1185
|
+
// we still need to complete observers, flush deferreds etc
|
|
1186
|
+
this.update(this._state);
|
|
1187
|
+
// fallthrough
|
|
1188
|
+
case 'error':
|
|
1189
|
+
// TODO: rethink cleanup of observers, mailbox, etc
|
|
1190
|
+
return this;
|
|
1191
|
+
}
|
|
1155
1192
|
if (this.logic.start) {
|
|
1156
|
-
|
|
1193
|
+
try {
|
|
1194
|
+
this.logic.start(this._state, this._actorContext);
|
|
1195
|
+
} catch (err) {
|
|
1196
|
+
this._stopProcedure();
|
|
1197
|
+
this._error(err);
|
|
1198
|
+
this._parent?.send(error(this.id, err));
|
|
1199
|
+
return this;
|
|
1200
|
+
}
|
|
1157
1201
|
}
|
|
1158
1202
|
|
|
1159
1203
|
// TODO: this notifies all subscribers but usually this is redundant
|
|
@@ -1167,23 +1211,30 @@ class Interpreter {
|
|
|
1167
1211
|
return this;
|
|
1168
1212
|
}
|
|
1169
1213
|
_process(event) {
|
|
1214
|
+
// TODO: reexamine what happens when an action (or a guard or smth) throws
|
|
1215
|
+
let nextState;
|
|
1216
|
+
let caughtError;
|
|
1170
1217
|
try {
|
|
1171
|
-
|
|
1172
|
-
this.update(nextState);
|
|
1173
|
-
if (event.type === stopSignalType) {
|
|
1174
|
-
this._stopProcedure();
|
|
1175
|
-
this._complete();
|
|
1176
|
-
}
|
|
1218
|
+
nextState = this.logic.transition(this._state, event, this._actorContext);
|
|
1177
1219
|
} catch (err) {
|
|
1178
|
-
//
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
}
|
|
1220
|
+
// we wrap it in a box so we can rethrow it later even if falsy value gets caught here
|
|
1221
|
+
caughtError = {
|
|
1222
|
+
err
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
if (caughtError) {
|
|
1226
|
+
const {
|
|
1227
|
+
err
|
|
1228
|
+
} = caughtError;
|
|
1229
|
+
this._stopProcedure();
|
|
1230
|
+
this._error(err);
|
|
1231
|
+
this._parent?.send(error(this.id, err));
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
this.update(nextState);
|
|
1235
|
+
if (event.type === stopSignalType) {
|
|
1236
|
+
this._stopProcedure();
|
|
1237
|
+
this._complete();
|
|
1187
1238
|
}
|
|
1188
1239
|
}
|
|
1189
1240
|
_stop() {
|
|
@@ -1212,15 +1263,35 @@ class Interpreter {
|
|
|
1212
1263
|
}
|
|
1213
1264
|
_complete() {
|
|
1214
1265
|
for (const observer of this.observers) {
|
|
1215
|
-
|
|
1266
|
+
try {
|
|
1267
|
+
observer.complete?.();
|
|
1268
|
+
} catch (err) {
|
|
1269
|
+
reportUnhandledError(err);
|
|
1270
|
+
}
|
|
1216
1271
|
}
|
|
1217
1272
|
this.observers.clear();
|
|
1218
1273
|
}
|
|
1219
|
-
_error(
|
|
1274
|
+
_error(err) {
|
|
1275
|
+
if (!this.observers.size) {
|
|
1276
|
+
if (!this._parent) {
|
|
1277
|
+
reportUnhandledError(err);
|
|
1278
|
+
}
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
let reportError = false;
|
|
1220
1282
|
for (const observer of this.observers) {
|
|
1221
|
-
observer.error
|
|
1283
|
+
const errorListener = observer.error;
|
|
1284
|
+
reportError ||= !errorListener;
|
|
1285
|
+
try {
|
|
1286
|
+
errorListener?.(err);
|
|
1287
|
+
} catch (err2) {
|
|
1288
|
+
reportUnhandledError(err2);
|
|
1289
|
+
}
|
|
1222
1290
|
}
|
|
1223
1291
|
this.observers.clear();
|
|
1292
|
+
if (reportError) {
|
|
1293
|
+
reportUnhandledError(err);
|
|
1294
|
+
}
|
|
1224
1295
|
}
|
|
1225
1296
|
_stopProcedure() {
|
|
1226
1297
|
if (this.status !== ActorStatus.Running) {
|
|
@@ -1553,10 +1624,10 @@ function toGuardDefinition(guardConfig, getPredicate) {
|
|
|
1553
1624
|
}
|
|
1554
1625
|
}
|
|
1555
1626
|
|
|
1556
|
-
function getOutput(configuration, context, event) {
|
|
1627
|
+
function getOutput(configuration, context, event, self) {
|
|
1557
1628
|
const machine = configuration[0].machine;
|
|
1558
1629
|
const finalChildStateNode = configuration.find(stateNode => stateNode.type === 'final' && stateNode.parent === machine.root);
|
|
1559
|
-
return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event) : undefined;
|
|
1630
|
+
return finalChildStateNode && finalChildStateNode.output ? mapContext(finalChildStateNode.output, context, event, self) : undefined;
|
|
1560
1631
|
}
|
|
1561
1632
|
const isAtomicStateNode = stateNode => stateNode.type === 'atomic' || stateNode.type === 'final';
|
|
1562
1633
|
function getChildren(stateNode) {
|
|
@@ -2183,7 +2254,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2183
2254
|
actions.push(...filteredTransitions.flatMap(t => t.actions));
|
|
2184
2255
|
|
|
2185
2256
|
// Enter states
|
|
2186
|
-
enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial);
|
|
2257
|
+
enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorCtx);
|
|
2187
2258
|
const nextConfiguration = [...mutConfiguration];
|
|
2188
2259
|
const done = isInFinalState(nextConfiguration);
|
|
2189
2260
|
if (done) {
|
|
@@ -2192,7 +2263,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2192
2263
|
}
|
|
2193
2264
|
try {
|
|
2194
2265
|
const nextState = resolveActionsAndContext(actions, event, currentState, actorCtx);
|
|
2195
|
-
const output = done ? getOutput(nextConfiguration, nextState.context, event) : undefined;
|
|
2266
|
+
const output = done ? getOutput(nextConfiguration, nextState.context, event, actorCtx.self) : undefined;
|
|
2196
2267
|
internalQueue.push(...nextState._internalQueue);
|
|
2197
2268
|
return cloneState(currentState, {
|
|
2198
2269
|
configuration: nextConfiguration,
|
|
@@ -2209,7 +2280,7 @@ function microstepProcedure(transitions, currentState, mutConfiguration, event,
|
|
|
2209
2280
|
throw e;
|
|
2210
2281
|
}
|
|
2211
2282
|
}
|
|
2212
|
-
function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial) {
|
|
2283
|
+
function enterStates(event, filteredTransitions, mutConfiguration, actions, internalQueue, currentState, historyValue, isInitial, actorContext) {
|
|
2213
2284
|
const statesToEnter = new Set();
|
|
2214
2285
|
const statesForDefaultEntry = new Set();
|
|
2215
2286
|
computeEntrySet(filteredTransitions, historyValue, statesForDefaultEntry, statesToEnter);
|
|
@@ -2237,7 +2308,7 @@ function enterStates(event, filteredTransitions, mutConfiguration, actions, inte
|
|
|
2237
2308
|
if (!parent.parent) {
|
|
2238
2309
|
continue;
|
|
2239
2310
|
}
|
|
2240
|
-
internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event) : undefined));
|
|
2311
|
+
internalQueue.push(done(parent.id, stateNodeToEnter.output ? mapContext(stateNodeToEnter.output, currentState.context, event, actorContext.self) : undefined));
|
|
2241
2312
|
if (parent.parent) {
|
|
2242
2313
|
const grandparent = parent.parent;
|
|
2243
2314
|
if (grandparent.type === 'parallel') {
|
|
@@ -2560,6 +2631,7 @@ class State {
|
|
|
2560
2631
|
this.value = void 0;
|
|
2561
2632
|
this.done = void 0;
|
|
2562
2633
|
this.output = void 0;
|
|
2634
|
+
this.error = void 0;
|
|
2563
2635
|
this.context = void 0;
|
|
2564
2636
|
this.historyValue = {};
|
|
2565
2637
|
this._internalQueue = void 0;
|
|
@@ -2576,6 +2648,7 @@ class State {
|
|
|
2576
2648
|
this.tags = new Set(flatten(this.configuration.map(sn => sn.tags)));
|
|
2577
2649
|
this.done = config.done ?? false;
|
|
2578
2650
|
this.output = config.output;
|
|
2651
|
+
this.error = config.error;
|
|
2579
2652
|
}
|
|
2580
2653
|
|
|
2581
2654
|
/**
|
|
@@ -3058,6 +3131,7 @@ exports.and = and;
|
|
|
3058
3131
|
exports.assign = assign;
|
|
3059
3132
|
exports.cancel = cancel;
|
|
3060
3133
|
exports.choose = choose;
|
|
3134
|
+
exports.cloneState = cloneState;
|
|
3061
3135
|
exports.constantPrefixes = constantPrefixes;
|
|
3062
3136
|
exports.createEmptyActor = createEmptyActor;
|
|
3063
3137
|
exports.createInitEvent = createInitEvent;
|