xstate 6.0.0-alpha.9 → 6.0.0-beta.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 +223 -78
- package/bin/agent.js +347 -0
- package/bin/xstate.js +52 -0
- package/dist/createActor-25900f76.cjs.js +7015 -0
- package/dist/createActor-551369be.development.esm.js +7175 -0
- package/dist/createActor-a49ae0ce.development.cjs.js +7289 -0
- package/dist/createActor-cbe3abac.esm.js +6902 -0
- package/dist/declarations/src/SimulatedClock.d.ts +2 -1
- package/dist/declarations/src/State.d.ts +66 -5
- package/dist/declarations/src/StateMachine.d.ts +103 -35
- package/dist/declarations/src/StateNode.d.ts +21 -16
- package/dist/declarations/src/actions.d.ts +11 -6
- package/dist/declarations/src/actors/callback.d.ts +11 -2
- package/dist/declarations/src/actors/index.d.ts +6 -5
- package/dist/declarations/src/actors/listener.d.ts +8 -3
- package/dist/declarations/src/actors/logic.d.ts +42 -52
- package/dist/declarations/src/actors/observable.d.ts +15 -2
- package/dist/declarations/src/actors/promise.d.ts +90 -22
- package/dist/declarations/src/actors/subscription.d.ts +9 -3
- package/dist/declarations/src/assert.d.ts +1 -0
- package/dist/declarations/src/base.types.d.ts +102 -0
- package/dist/declarations/src/createActor.d.ts +101 -27
- package/dist/declarations/src/createMachine.d.ts +38 -17
- package/dist/declarations/src/delay.d.ts +8 -0
- package/dist/declarations/src/durable/index.d.ts +231 -0
- package/dist/declarations/src/effectDescriptor.d.ts +95 -0
- package/dist/declarations/src/eventTypes.d.ts +9 -0
- package/dist/declarations/src/fsm/index.d.ts +2 -0
- package/dist/declarations/src/fsm.d.ts +169 -0
- package/dist/declarations/src/graph/adjacency.d.ts +6 -3
- package/dist/declarations/src/graph/graph.d.ts +6 -3
- package/dist/declarations/src/graph/index.d.ts +2 -3
- package/dist/declarations/src/graph/pathFromEvents.d.ts +5 -2
- package/dist/declarations/src/graph/shortestPaths.d.ts +5 -3
- package/dist/declarations/src/graph/simplePaths.d.ts +5 -3
- package/dist/declarations/src/graph/types.d.ts +113 -59
- package/dist/declarations/src/hotSwap.d.ts +18 -0
- package/dist/declarations/src/index.d.ts +28 -20
- package/dist/declarations/src/inspection.d.ts +24 -1
- package/dist/declarations/src/machineFromJSON.d.ts +171 -0
- package/dist/declarations/src/machineSchemas.d.ts +27 -0
- package/dist/declarations/src/machineVersion.types.d.ts +53 -0
- package/dist/declarations/src/machineVersions.d.ts +103 -0
- package/dist/declarations/src/mapState.d.ts +2 -0
- package/dist/declarations/src/persist/index.d.ts +36 -0
- package/dist/declarations/src/persistedSnapshotValidation.d.ts +18 -0
- package/dist/declarations/src/runtimeHelpers.d.ts +43 -0
- package/dist/declarations/src/schema.types.d.ts +31 -4
- package/dist/declarations/src/serialize.d.ts +39 -28
- package/dist/declarations/src/setup.d.ts +1074 -157
- package/dist/declarations/src/spawn.d.ts +6 -4
- package/dist/declarations/src/stateUtils.d.ts +57 -14
- package/dist/declarations/src/system.d.ts +178 -17
- package/dist/declarations/src/systemTransition.d.ts +108 -0
- package/dist/declarations/src/toPromise.d.ts +1 -0
- package/dist/declarations/src/transition.d.ts +48 -10
- package/dist/declarations/src/transitionActions.d.ts +22 -4
- package/dist/declarations/src/typeUtils.d.ts +51 -0
- package/dist/declarations/src/types.d.ts +835 -318
- package/dist/declarations/src/types.v6.d.ts +540 -106
- package/dist/declarations/src/utils.d.ts +38 -8
- package/dist/declarations/src/validation/index.d.ts +49 -0
- package/dist/declarations/src/validation.types.d.ts +29 -0
- package/dist/declarations/src/waitFor.d.ts +1 -0
- package/dist/effectDescriptor-2fa27c54.cjs.js +351 -0
- package/dist/effectDescriptor-321a8ce9.development.esm.js +343 -0
- package/dist/effectDescriptor-d8b0f749.esm.js +343 -0
- package/dist/effectDescriptor-f73da4ec.development.cjs.js +351 -0
- package/dist/machineSchemas-45c21bc2.esm.js +1292 -0
- package/dist/machineSchemas-6d96bef4.development.cjs.js +1379 -0
- package/dist/machineSchemas-88d01821.cjs.js +1300 -0
- package/dist/machineSchemas-b5736804.development.esm.js +1371 -0
- package/dist/schema.types-0ecd17b8.esm.js +71 -0
- package/dist/schema.types-15a68ddc.cjs.js +74 -0
- package/dist/schema.types-1c16f035.development.cjs.js +74 -0
- package/dist/schema.types-1cd4652e.development.esm.js +71 -0
- package/dist/validation-05f1d517.esm.js +8 -0
- package/dist/validation-4561b43c.cjs.js +10 -0
- package/dist/validation-e69470ad.development.esm.js +8 -0
- package/dist/validation-f2714a35.development.cjs.js +10 -0
- package/dist/xstate-actors.cjs.js +1068 -12
- package/dist/xstate-actors.cjs.mjs +2 -4
- package/dist/xstate-actors.development.cjs.js +1068 -12
- package/dist/xstate-actors.development.cjs.mjs +2 -4
- package/dist/xstate-actors.development.esm.js +1064 -1
- package/dist/xstate-actors.esm.js +1064 -1
- package/dist/xstate-actors.umd.min.js +1 -1
- package/dist/xstate-actors.umd.min.js.map +1 -1
- package/dist/xstate-durable.cjs.d.mts +2 -0
- package/dist/xstate-durable.cjs.d.ts +2 -0
- package/dist/xstate-durable.cjs.js +513 -0
- package/dist/xstate-durable.cjs.mjs +5 -0
- package/dist/xstate-durable.development.cjs.js +513 -0
- package/dist/xstate-durable.development.cjs.mjs +5 -0
- package/dist/xstate-durable.development.esm.js +507 -0
- package/dist/xstate-durable.esm.js +507 -0
- package/dist/xstate-durable.umd.min.js +2 -0
- package/dist/xstate-durable.umd.min.js.map +1 -0
- package/dist/xstate-fsm.cjs.d.mts +2 -0
- package/dist/xstate-fsm.cjs.d.ts +2 -0
- package/dist/xstate-fsm.cjs.js +111 -0
- package/dist/xstate-fsm.cjs.mjs +5 -0
- package/dist/xstate-fsm.development.cjs.js +111 -0
- package/dist/xstate-fsm.development.cjs.mjs +5 -0
- package/dist/xstate-fsm.development.esm.js +105 -0
- package/dist/xstate-fsm.esm.js +105 -0
- package/dist/xstate-fsm.umd.min.js +2 -0
- package/dist/xstate-fsm.umd.min.js.map +1 -0
- package/dist/xstate-graph.cjs.js +173 -457
- package/dist/xstate-graph.cjs.mjs +2 -5
- package/dist/xstate-graph.development.cjs.js +173 -457
- package/dist/xstate-graph.development.cjs.mjs +2 -5
- package/dist/xstate-graph.development.esm.js +171 -451
- package/dist/xstate-graph.esm.js +171 -451
- package/dist/xstate-graph.umd.min.js +1 -1
- package/dist/xstate-graph.umd.min.js.map +1 -1
- package/dist/xstate-persist.cjs.d.mts +2 -0
- package/dist/xstate-persist.cjs.d.ts +2 -0
- package/dist/xstate-persist.cjs.js +314 -0
- package/dist/xstate-persist.cjs.mjs +6 -0
- package/dist/xstate-persist.development.cjs.js +325 -0
- package/dist/xstate-persist.development.cjs.mjs +6 -0
- package/dist/xstate-persist.development.esm.js +319 -0
- package/dist/xstate-persist.esm.js +308 -0
- package/dist/xstate-persist.umd.min.js +2 -0
- package/dist/xstate-persist.umd.min.js.map +1 -0
- package/dist/xstate-validation.cjs.d.mts +2 -0
- package/dist/xstate-validation.cjs.d.ts +2 -0
- package/dist/xstate-validation.cjs.js +330 -0
- package/dist/xstate-validation.cjs.mjs +5 -0
- package/dist/xstate-validation.development.cjs.js +330 -0
- package/dist/xstate-validation.development.cjs.mjs +5 -0
- package/dist/xstate-validation.development.esm.js +324 -0
- package/dist/xstate-validation.esm.js +324 -0
- package/dist/xstate-validation.umd.min.js +2 -0
- package/dist/xstate-validation.umd.min.js.map +1 -0
- package/dist/xstate.cjs.js +2486 -1431
- package/dist/xstate.cjs.mjs +23 -13
- package/dist/xstate.development.cjs.js +2486 -1431
- package/dist/xstate.development.cjs.mjs +23 -13
- package/dist/xstate.development.esm.js +2445 -1399
- package/dist/xstate.esm.js +2445 -1399
- package/dist/xstate.umd.min.js +1 -1
- package/dist/xstate.umd.min.js.map +1 -1
- package/docs/actions.md +75 -0
- package/docs/actor-logic.md +250 -0
- package/docs/actors.md +64 -0
- package/docs/agents.md +132 -0
- package/docs/api-stability.json +4287 -0
- package/docs/async-logic.md +75 -0
- package/docs/async-requests.md +157 -0
- package/docs/backend-workflows.md +124 -0
- package/docs/cancellation.md +42 -0
- package/docs/cheatsheet.md +563 -0
- package/docs/choice-states.md +109 -0
- package/docs/choose-xstate.md +47 -0
- package/docs/configuration.md +161 -0
- package/docs/context.md +67 -0
- package/docs/create-actor.md +159 -0
- package/docs/data-and-effects.md +48 -0
- package/docs/delays.md +81 -0
- package/docs/durable-execution.md +520 -0
- package/docs/emitted-events.md +146 -0
- package/docs/errors.md +812 -0
- package/docs/examples.md +75 -0
- package/docs/faq.md +60 -0
- package/docs/final-states.md +149 -0
- package/docs/forms-and-wizards.md +166 -0
- package/docs/frameworks.md +69 -0
- package/docs/fsm.md +164 -0
- package/docs/glossary.md +68 -0
- package/docs/guards.md +137 -0
- package/docs/history-states.md +161 -0
- package/docs/how-xstate-runs-machines.md +86 -0
- package/docs/index.md +74 -0
- package/docs/input-output.md +69 -0
- package/docs/inspect-actor-systems.md +118 -0
- package/docs/inspection.md +75 -0
- package/docs/internal-events.md +156 -0
- package/docs/invoke.md +371 -0
- package/docs/lifecycle-and-errors.md +48 -0
- package/docs/listen-and-subscribe.md +126 -0
- package/docs/model-based-testing.md +321 -0
- package/docs/modeling.md +40 -0
- package/docs/parallel-states.md +182 -0
- package/docs/persist-and-restore-actors.md +120 -0
- package/docs/persistence.md +312 -0
- package/docs/quick-start.md +55 -0
- package/docs/retries-and-timeouts.md +122 -0
- package/docs/route-states.md +104 -0
- package/docs/scxml.md +92 -0
- package/docs/selectors.md +99 -0
- package/docs/serialization.md +127 -0
- package/docs/setup-and-provide.md +273 -0
- package/docs/snapshots.md +134 -0
- package/docs/spawn.md +212 -0
- package/docs/state-input.md +162 -0
- package/docs/statecharts.md +54 -0
- package/docs/states-events-transitions.md +82 -0
- package/docs/states.md +168 -0
- package/docs/system-transitions.md +172 -0
- package/docs/systems.md +166 -0
- package/docs/testing.md +152 -0
- package/docs/timeouts.md +125 -0
- package/docs/transition-functions.md +89 -0
- package/docs/transitions.md +187 -0
- package/docs/troubleshooting.md +56 -0
- package/docs/tutorial.md +481 -0
- package/docs/typescript.md +213 -0
- package/docs/typestates.md +112 -0
- package/docs/utilities.md +39 -0
- package/docs/why-state-machines.md +59 -0
- package/docs/xstate-v5-to-v6.md +1923 -0
- package/docs/your-first-machine.md +79 -0
- package/durable/package.json +8 -0
- package/fsm/package.json +8 -0
- package/package.json +72 -10
- package/persist/package.json +8 -0
- package/validation/package.json +8 -0
- package/actions/package.json +0 -8
- package/dev/package.json +0 -8
- package/dist/StateMachine-0aa98f54.development.esm.js +0 -746
- package/dist/StateMachine-aef700df.cjs.js +0 -732
- package/dist/StateMachine-d4931336.esm.js +0 -729
- package/dist/StateMachine-da872dde.development.cjs.js +0 -749
- package/dist/declarations/src/createMachineFromConfig.d.ts +0 -177
- package/dist/declarations/src/getNextSnapshot.d.ts +0 -37
- package/dist/declarations/src/graph/TestModel.d.ts +0 -70
- package/dist/declarations/src/graph/pathGenerators.d.ts +0 -4
- package/dist/index-219cb621.development.cjs.js +0 -4476
- package/dist/index-2f2fbd9b.development.esm.js +0 -4421
- package/dist/index-559ceab3.cjs.js +0 -4437
- package/dist/index-8828376f.esm.js +0 -4382
- package/guards/package.json +0 -8
- package/invoke/package.json +0 -4
package/dist/xstate-graph.cjs.js
CHANGED
|
@@ -2,384 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var dist_xstateActors = require('./
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
function simpleStringify(value) {
|
|
9
|
-
return JSON.stringify(value);
|
|
10
|
-
}
|
|
11
|
-
function formatPathTestResult(path, testPathResult, options) {
|
|
12
|
-
const resolvedOptions = {
|
|
13
|
-
formatColor: (_color, string) => string,
|
|
14
|
-
serializeState: simpleStringify,
|
|
15
|
-
serializeEvent: simpleStringify,
|
|
16
|
-
...options
|
|
17
|
-
};
|
|
18
|
-
const {
|
|
19
|
-
formatColor,
|
|
20
|
-
serializeState,
|
|
21
|
-
serializeEvent
|
|
22
|
-
} = resolvedOptions;
|
|
23
|
-
const {
|
|
24
|
-
state
|
|
25
|
-
} = path;
|
|
26
|
-
const targetStateString = serializeState(state, path.steps.length ? path.steps[path.steps.length - 1].event : undefined);
|
|
27
|
-
let errMessage = '';
|
|
28
|
-
let hasFailed = false;
|
|
29
|
-
errMessage += '\nPath:\n' + testPathResult.steps.map((s, i, steps) => {
|
|
30
|
-
const stateString = serializeState(s.step.state, i > 0 ? steps[i - 1].step.event : undefined);
|
|
31
|
-
const eventString = serializeEvent(s.step.event);
|
|
32
|
-
const stateResult = `\tState: ${hasFailed ? formatColor('gray', stateString) : s.state.error ? (hasFailed = true, formatColor('redBright', stateString)) : formatColor('greenBright', stateString)}`;
|
|
33
|
-
const eventResult = `\tEvent: ${hasFailed ? formatColor('gray', eventString) : s.event.error ? (hasFailed = true, formatColor('red', eventString)) : formatColor('green', eventString)}`;
|
|
34
|
-
return [stateResult, eventResult].join('\n');
|
|
35
|
-
}).concat(`\tState: ${hasFailed ? formatColor('gray', targetStateString) : testPathResult.state.error ? formatColor('red', targetStateString) : formatColor('green', targetStateString)}`).join('\n\n');
|
|
36
|
-
return errMessage;
|
|
37
|
-
}
|
|
38
|
-
function getDescription(snapshot) {
|
|
39
|
-
const contextString = !Object.keys(snapshot.context).length ? '' : `(${JSON.stringify(snapshot.context)})`;
|
|
40
|
-
const stateStrings = snapshot._nodes.filter(sn => sn.type === 'atomic' || sn.type === 'final').map(({
|
|
41
|
-
id,
|
|
42
|
-
path
|
|
43
|
-
}) => {
|
|
44
|
-
const meta = snapshot.getMeta()[id];
|
|
45
|
-
if (!meta) {
|
|
46
|
-
return `"${path.join('.')}"`;
|
|
47
|
-
}
|
|
48
|
-
const {
|
|
49
|
-
description
|
|
50
|
-
} = meta;
|
|
51
|
-
if (typeof description === 'function') {
|
|
52
|
-
return description(snapshot);
|
|
53
|
-
}
|
|
54
|
-
return description ? `"${description}"` : JSON.stringify(snapshot.value);
|
|
55
|
-
});
|
|
56
|
-
return `state${stateStrings.length === 1 ? '' : 's'} ` + stateStrings.join(', ') + ` ${contextString}`.trim();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Deduplicates your paths so that A -> B is not executed separately to A -> B
|
|
61
|
-
* -> C
|
|
62
|
-
*/
|
|
63
|
-
const deduplicatePaths = (paths, serializeEvent = simpleStringify) => {
|
|
64
|
-
/** Put all paths on the same level so we can dedup them */
|
|
65
|
-
const allPathsWithEventSequence = [];
|
|
66
|
-
paths.forEach(path => {
|
|
67
|
-
allPathsWithEventSequence.push({
|
|
68
|
-
path,
|
|
69
|
-
eventSequence: path.steps.map(step => serializeEvent(step.event))
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// Sort by path length, descending
|
|
74
|
-
allPathsWithEventSequence.sort((a, z) => z.path.steps.length - a.path.steps.length);
|
|
75
|
-
const superpathsWithEventSequence = [];
|
|
76
|
-
|
|
77
|
-
/** Filter out the paths that are subpaths of superpaths */
|
|
78
|
-
pathLoop: for (const pathWithEventSequence of allPathsWithEventSequence) {
|
|
79
|
-
// Check each existing superpath to see if the path is a subpath of it
|
|
80
|
-
superpathLoop: for (const superpathWithEventSequence of superpathsWithEventSequence) {
|
|
81
|
-
// eslint-disable-next-line @typescript-eslint/no-for-in-array
|
|
82
|
-
for (const i in pathWithEventSequence.eventSequence) {
|
|
83
|
-
// Check event sequence to determine if path is subpath, e.g.:
|
|
84
|
-
//
|
|
85
|
-
// This will short-circuit the check
|
|
86
|
-
// ['a', 'b', 'c', 'd'] (superpath)
|
|
87
|
-
// ['a', 'b', 'x'] (path)
|
|
88
|
-
//
|
|
89
|
-
// This will not short-circuit; path is subpath
|
|
90
|
-
// ['a', 'b', 'c', 'd'] (superpath)
|
|
91
|
-
// ['a', 'b', 'c'] (path)
|
|
92
|
-
if (pathWithEventSequence.eventSequence[i] !== superpathWithEventSequence.eventSequence[i]) {
|
|
93
|
-
// If the path is different from the superpath,
|
|
94
|
-
// continue to the next superpath
|
|
95
|
-
continue superpathLoop;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// If we reached here, path is subpath of superpath
|
|
100
|
-
// Continue & do not add path to superpaths
|
|
101
|
-
continue pathLoop;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// If we reached here, path is not a subpath of any existing superpaths
|
|
105
|
-
// So add it to the superpaths
|
|
106
|
-
superpathsWithEventSequence.push(pathWithEventSequence);
|
|
107
|
-
}
|
|
108
|
-
return superpathsWithEventSequence.map(path => path.path);
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
const createShortestPathsGen = () => (logic, defaultOptions) => {
|
|
112
|
-
const paths = getShortestPaths(logic, defaultOptions);
|
|
113
|
-
return paths;
|
|
114
|
-
};
|
|
115
|
-
const createSimplePathsGen = () => (logic, defaultOptions) => {
|
|
116
|
-
const paths = getSimplePaths(logic, defaultOptions);
|
|
117
|
-
return paths;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const validateState = _state => {
|
|
121
|
-
// TODO
|
|
122
|
-
};
|
|
123
|
-
const validateMachine = machine => {
|
|
124
|
-
validateState(machine.root);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Creates a test model that represents an abstract model of a system under test
|
|
129
|
-
* (SUT).
|
|
130
|
-
*
|
|
131
|
-
* The test model is used to generate test paths, which are used to verify that
|
|
132
|
-
* states in the model are reachable in the SUT.
|
|
133
|
-
*/
|
|
134
|
-
class TestModel {
|
|
135
|
-
getDefaultOptions() {
|
|
136
|
-
return {
|
|
137
|
-
serializeState: state => simpleStringify(state),
|
|
138
|
-
serializeEvent: event => simpleStringify(event),
|
|
139
|
-
// For non-state-machine test models, we cannot identify
|
|
140
|
-
// separate transitions, so just use event type
|
|
141
|
-
serializeTransition: (state, event) => `${simpleStringify(state)}|${event?.type}`,
|
|
142
|
-
events: [],
|
|
143
|
-
stateMatcher: (_, stateKey) => stateKey === '*',
|
|
144
|
-
logger: {
|
|
145
|
-
log: console.log.bind(console),
|
|
146
|
-
error: console.error.bind(console)
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
constructor(testLogic, options) {
|
|
151
|
-
this.testLogic = testLogic;
|
|
152
|
-
this.options = void 0;
|
|
153
|
-
this.defaultTraversalOptions = void 0;
|
|
154
|
-
this._toTestPath = statePath => {
|
|
155
|
-
function formatEvent(event) {
|
|
156
|
-
const {
|
|
157
|
-
type,
|
|
158
|
-
...other
|
|
159
|
-
} = event;
|
|
160
|
-
const propertyString = Object.keys(other).length ? ` (${JSON.stringify(other)})` : '';
|
|
161
|
-
return `${type}${propertyString}`;
|
|
162
|
-
}
|
|
163
|
-
const eventsString = statePath.steps.map(s => formatEvent(s.event)).join(' → ');
|
|
164
|
-
return {
|
|
165
|
-
...statePath,
|
|
166
|
-
test: params => this.testPath(statePath, params),
|
|
167
|
-
description: dist_xstateActors.isMachineSnapshot(statePath.state) ? `Reaches ${getDescription(statePath.state).trim()}: ${eventsString}` : JSON.stringify(statePath.state)
|
|
168
|
-
};
|
|
169
|
-
};
|
|
170
|
-
this.options = {
|
|
171
|
-
...this.getDefaultOptions(),
|
|
172
|
-
...options
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
getPaths(pathGenerator, options) {
|
|
176
|
-
const allowDuplicatePaths = options?.allowDuplicatePaths ?? false;
|
|
177
|
-
const paths = pathGenerator(this.testLogic, this._resolveOptions(options));
|
|
178
|
-
return (allowDuplicatePaths ? paths : deduplicatePaths(paths)).map(this._toTestPath);
|
|
179
|
-
}
|
|
180
|
-
getShortestPaths(options) {
|
|
181
|
-
return this.getPaths(createShortestPathsGen(), options);
|
|
182
|
-
}
|
|
183
|
-
getShortestPathsFrom(paths, options) {
|
|
184
|
-
const resultPaths = [];
|
|
185
|
-
for (const path of paths) {
|
|
186
|
-
const shortestPaths = this.getShortestPaths({
|
|
187
|
-
...options,
|
|
188
|
-
fromState: path.state
|
|
189
|
-
});
|
|
190
|
-
for (const shortestPath of shortestPaths) {
|
|
191
|
-
resultPaths.push(this._toTestPath(joinPaths(path, shortestPath)));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return resultPaths;
|
|
195
|
-
}
|
|
196
|
-
getSimplePaths(options) {
|
|
197
|
-
return this.getPaths(createSimplePathsGen(), options);
|
|
198
|
-
}
|
|
199
|
-
getSimplePathsFrom(paths, options) {
|
|
200
|
-
const resultPaths = [];
|
|
201
|
-
for (const path of paths) {
|
|
202
|
-
const shortestPaths = this.getSimplePaths({
|
|
203
|
-
...options,
|
|
204
|
-
fromState: path.state
|
|
205
|
-
});
|
|
206
|
-
for (const shortestPath of shortestPaths) {
|
|
207
|
-
resultPaths.push(this._toTestPath(joinPaths(path, shortestPath)));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return resultPaths;
|
|
211
|
-
}
|
|
212
|
-
getPathsFromEvents(events, options) {
|
|
213
|
-
const paths = getPathsFromEvents(this.testLogic, events, options);
|
|
214
|
-
return paths.map(this._toTestPath);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* An array of adjacencies, which are objects that represent each `state` with
|
|
219
|
-
* the `nextState` given the `event`.
|
|
220
|
-
*/
|
|
221
|
-
getAdjacencyMap() {
|
|
222
|
-
const adjMap = getAdjacencyMap(this.testLogic, this.options);
|
|
223
|
-
return adjMap;
|
|
224
|
-
}
|
|
225
|
-
async testPath(path, params, options) {
|
|
226
|
-
const testPathResult = {
|
|
227
|
-
steps: [],
|
|
228
|
-
state: {
|
|
229
|
-
error: null
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
try {
|
|
233
|
-
for (const step of path.steps) {
|
|
234
|
-
const testStepResult = {
|
|
235
|
-
step,
|
|
236
|
-
state: {
|
|
237
|
-
error: null
|
|
238
|
-
},
|
|
239
|
-
event: {
|
|
240
|
-
error: null
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
testPathResult.steps.push(testStepResult);
|
|
244
|
-
try {
|
|
245
|
-
await this.testTransition(params, step);
|
|
246
|
-
} catch (err) {
|
|
247
|
-
testStepResult.event.error = err;
|
|
248
|
-
throw err;
|
|
249
|
-
}
|
|
250
|
-
try {
|
|
251
|
-
await this.testState(params, step.state, options);
|
|
252
|
-
} catch (err) {
|
|
253
|
-
testStepResult.state.error = err;
|
|
254
|
-
throw err;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
} catch (err) {
|
|
258
|
-
// TODO: make option
|
|
259
|
-
err.message += formatPathTestResult(path, testPathResult, this.options);
|
|
260
|
-
throw err;
|
|
261
|
-
}
|
|
262
|
-
return testPathResult;
|
|
263
|
-
}
|
|
264
|
-
async testState(params, state, options) {
|
|
265
|
-
const resolvedOptions = this._resolveOptions(options);
|
|
266
|
-
const stateTestKeys = this._getStateTestKeys(params, state, resolvedOptions);
|
|
267
|
-
for (const stateTestKey of stateTestKeys) {
|
|
268
|
-
await params.states?.[stateTestKey](state);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
_getStateTestKeys(params, state, resolvedOptions) {
|
|
272
|
-
const states = params.states || {};
|
|
273
|
-
const stateTestKeys = Object.keys(states).filter(stateKey => {
|
|
274
|
-
return resolvedOptions.stateMatcher(state, stateKey);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// Fallthrough state tests
|
|
278
|
-
if (!stateTestKeys.length && '*' in states) {
|
|
279
|
-
stateTestKeys.push('*');
|
|
280
|
-
}
|
|
281
|
-
return stateTestKeys;
|
|
282
|
-
}
|
|
283
|
-
_getEventExec(params, step) {
|
|
284
|
-
const eventExec = params.events?.[step.event.type];
|
|
285
|
-
return eventExec;
|
|
286
|
-
}
|
|
287
|
-
async testTransition(params, step) {
|
|
288
|
-
const eventExec = this._getEventExec(params, step);
|
|
289
|
-
await eventExec?.(step);
|
|
290
|
-
}
|
|
291
|
-
_resolveOptions(options) {
|
|
292
|
-
return {
|
|
293
|
-
...this.defaultTraversalOptions,
|
|
294
|
-
...this.options,
|
|
295
|
-
...options
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
function stateValuesEqual(a, b) {
|
|
300
|
-
if (a === b) {
|
|
301
|
-
return true;
|
|
302
|
-
}
|
|
303
|
-
if (a === undefined || b === undefined) {
|
|
304
|
-
return false;
|
|
305
|
-
}
|
|
306
|
-
if (typeof a === 'string' || typeof b === 'string') {
|
|
307
|
-
return a === b;
|
|
308
|
-
}
|
|
309
|
-
const aKeys = Object.keys(a);
|
|
310
|
-
const bKeys = Object.keys(b);
|
|
311
|
-
return aKeys.length === bKeys.length && aKeys.every(key => stateValuesEqual(a[key], b[key]));
|
|
312
|
-
}
|
|
313
|
-
function serializeMachineTransition(snapshot, event, previousSnapshot, {
|
|
314
|
-
serializeEvent
|
|
315
|
-
}) {
|
|
316
|
-
// TODO: the stateValuesEqual check here is very likely not exactly correct
|
|
317
|
-
// but I'm not sure what the correct check is and what this is trying to do
|
|
318
|
-
if (!event || previousSnapshot && stateValuesEqual(previousSnapshot.value, snapshot.value)) {
|
|
319
|
-
return '';
|
|
320
|
-
}
|
|
321
|
-
const prevStateString = previousSnapshot ? ` from ${simpleStringify(previousSnapshot.value)}` : '';
|
|
322
|
-
return ` via ${serializeEvent(event)}${prevStateString}`;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Creates a test model that represents an abstract model of a system under test
|
|
327
|
-
* (SUT).
|
|
328
|
-
*
|
|
329
|
-
* The test model is used to generate test paths, which are used to verify that
|
|
330
|
-
* states in the `machine` are reachable in the SUT.
|
|
331
|
-
*
|
|
332
|
-
* @example
|
|
333
|
-
*
|
|
334
|
-
* ```js
|
|
335
|
-
* const toggleModel = createModel(toggleMachine).withEvents({
|
|
336
|
-
* TOGGLE: {
|
|
337
|
-
* exec: async (page) => {
|
|
338
|
-
* await page.click('input');
|
|
339
|
-
* }
|
|
340
|
-
* }
|
|
341
|
-
* });
|
|
342
|
-
* ```
|
|
343
|
-
*
|
|
344
|
-
* @param machine The state machine used to represent the abstract model.
|
|
345
|
-
* @param options Options for the created test model:
|
|
346
|
-
*
|
|
347
|
-
* - `events`: an object mapping string event types (e.g., `SUBMIT`) to an event
|
|
348
|
-
* test config (e.g., `{exec: () => {...}, cases: [...]}`)
|
|
349
|
-
*/
|
|
350
|
-
function createTestModel(machine, options) {
|
|
351
|
-
validateMachine(machine);
|
|
352
|
-
const serializeEvent = options?.serializeEvent ?? simpleStringify;
|
|
353
|
-
const serializeTransition = options?.serializeTransition ?? serializeMachineTransition;
|
|
354
|
-
const {
|
|
355
|
-
events: getEvents,
|
|
356
|
-
...otherOptions
|
|
357
|
-
} = options ?? {};
|
|
358
|
-
const testModel = new TestModel(machine, {
|
|
359
|
-
serializeState: (state, event, prevState) => {
|
|
360
|
-
// Only consider the `state` if `serializeTransition()` is opted out (empty string)
|
|
361
|
-
return `${serializeSnapshot(state)}${serializeTransition(state, event, prevState, {
|
|
362
|
-
serializeEvent
|
|
363
|
-
})}`;
|
|
364
|
-
},
|
|
365
|
-
stateMatcher: (state, key) => {
|
|
366
|
-
return key.startsWith('#') ? state._nodes.includes(machine.getStateNodeById(key)) : state.matches(key);
|
|
367
|
-
},
|
|
368
|
-
events: state => {
|
|
369
|
-
const events = typeof getEvents === 'function' ? getEvents(state) : getEvents ?? [];
|
|
370
|
-
return dist_xstateActors.getAllOwnEventDescriptors(state).flatMap(eventType => {
|
|
371
|
-
if (events.some(e => e.type === eventType)) {
|
|
372
|
-
return events.filter(e => e.type === eventType);
|
|
373
|
-
}
|
|
374
|
-
return [{
|
|
375
|
-
type: eventType
|
|
376
|
-
}]; // TODO: fix types
|
|
377
|
-
});
|
|
378
|
-
},
|
|
379
|
-
...otherOptions
|
|
380
|
-
});
|
|
381
|
-
return testModel;
|
|
382
|
-
}
|
|
5
|
+
var dist_xstateActors = require('./xstate-actors.cjs.js');
|
|
6
|
+
var createActor = require('./createActor-25900f76.cjs.js');
|
|
7
|
+
require('./validation-4561b43c.cjs.js');
|
|
383
8
|
|
|
384
9
|
function createMockActorScope() {
|
|
385
10
|
const emptyActor = dist_xstateActors.createEmptyActor();
|
|
@@ -400,15 +25,17 @@ function createMockActorScope() {
|
|
|
400
25
|
/**
|
|
401
26
|
* Returns all state nodes of the given `node`.
|
|
402
27
|
*
|
|
28
|
+
* @public
|
|
403
29
|
* @param stateNode State node to recursively get child state nodes from
|
|
30
|
+
* @public
|
|
404
31
|
*/
|
|
405
|
-
function
|
|
32
|
+
function getDescendantStateNodes(stateNode) {
|
|
406
33
|
const {
|
|
407
34
|
states
|
|
408
35
|
} = stateNode;
|
|
409
36
|
const nodes = Object.keys(states).reduce((accNodes, stateKey) => {
|
|
410
37
|
const childStateNode = states[stateKey];
|
|
411
|
-
const childStateNodes =
|
|
38
|
+
const childStateNodes = getDescendantStateNodes(childStateNode);
|
|
412
39
|
accNodes.push(childStateNode, ...childStateNodes);
|
|
413
40
|
return accNodes;
|
|
414
41
|
}, []);
|
|
@@ -423,55 +50,87 @@ function getChildren(stateNode) {
|
|
|
423
50
|
});
|
|
424
51
|
return children;
|
|
425
52
|
}
|
|
53
|
+
|
|
54
|
+
/** @public */
|
|
426
55
|
function serializeSnapshot(snapshot) {
|
|
427
56
|
const {
|
|
428
57
|
value,
|
|
429
58
|
context
|
|
430
59
|
} = snapshot;
|
|
60
|
+
const machineSnapshot = createActor.isMachineSnapshot(snapshot) ? snapshot : undefined;
|
|
61
|
+
const inputs = machineSnapshot?.getInputs();
|
|
62
|
+
const history = machineSnapshot?.historyValue;
|
|
431
63
|
return JSON.stringify({
|
|
432
64
|
value,
|
|
433
|
-
context: Object.keys(context ?? {}).length ? context : undefined
|
|
65
|
+
context: Object.keys(context ?? {}).length ? context : undefined,
|
|
66
|
+
// Equal values and root contexts can still make different decisions when
|
|
67
|
+
// their state inputs or remembered history differ. Empty records remain
|
|
68
|
+
// omitted to preserve the keys of machines that do not use these features.
|
|
69
|
+
stateInputs: inputs && Object.keys(inputs).length ? inputs : undefined,
|
|
70
|
+
historyValue: history && Object.keys(history).length ? Object.fromEntries(Object.entries(history).map(([id, nodes]) => [id, nodes.map(node => node.id)])) : undefined
|
|
434
71
|
});
|
|
435
72
|
}
|
|
436
73
|
function serializeEvent(event) {
|
|
437
74
|
return JSON.stringify(event);
|
|
438
75
|
}
|
|
76
|
+
function resolveEventCases(cases, state, defaults) {
|
|
77
|
+
const events = [];
|
|
78
|
+
const sources = cases;
|
|
79
|
+
const entries = Object.entries(sources).filter(([, source]) => source !== undefined);
|
|
80
|
+
const applicable = type => !defaults || defaults.some(event => createActor.matchesEventDescriptor(type, event.type));
|
|
81
|
+
for (const [type, source] of entries) {
|
|
82
|
+
if (!applicable(type)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const samples = typeof source === 'function' ? source(state) : source;
|
|
86
|
+
if (!Array.isArray(samples) || !samples.length) {
|
|
87
|
+
throw new Error(`Event cases for "${type}" must be a non-empty array`);
|
|
88
|
+
}
|
|
89
|
+
for (const payload of samples) {
|
|
90
|
+
const event = {
|
|
91
|
+
...payload,
|
|
92
|
+
type
|
|
93
|
+
};
|
|
94
|
+
if (!defaults || defaults.some(({
|
|
95
|
+
type: descriptor,
|
|
96
|
+
...pattern
|
|
97
|
+
}) => createActor.matchesEventDescriptor(type, descriptor) && createActor.matchesEvent(event, pattern))) {
|
|
98
|
+
events.push(event);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return [...events, ...(defaults?.filter(event => !entries.some(([type]) => type === event.type)) ?? [])];
|
|
103
|
+
}
|
|
439
104
|
function createDefaultMachineOptions(machine, options) {
|
|
440
|
-
const {
|
|
441
|
-
events: getEvents,
|
|
442
|
-
...otherOptions
|
|
443
|
-
} = options ?? {};
|
|
444
105
|
const traversalOptions = {
|
|
445
106
|
serializeState: serializeSnapshot,
|
|
446
107
|
serializeEvent,
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const matchingEvents = events.filter(ev => ev.type === type);
|
|
451
|
-
if (matchingEvents.length) {
|
|
452
|
-
return matchingEvents;
|
|
453
|
-
}
|
|
454
|
-
return [{
|
|
455
|
-
type
|
|
456
|
-
}];
|
|
457
|
-
});
|
|
458
|
-
},
|
|
459
|
-
fromState: machine.getInitialSnapshot(createMockActorScope(), options?.input),
|
|
460
|
-
...otherOptions
|
|
108
|
+
...options,
|
|
109
|
+
events: state => createActor.getAllOwnEvents(state),
|
|
110
|
+
fromState: options?.fromState ?? machine.getInitialSnapshot(createMockActorScope(), options?.input)
|
|
461
111
|
};
|
|
462
112
|
return traversalOptions;
|
|
463
113
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
serializeState: state => JSON.stringify(state),
|
|
467
|
-
serializeEvent
|
|
468
|
-
};
|
|
469
|
-
}
|
|
114
|
+
|
|
115
|
+
/** @public */
|
|
470
116
|
function toDirectedGraph(stateMachine) {
|
|
471
|
-
const stateNode = stateMachine
|
|
117
|
+
const stateNode = isMachineLogic(stateMachine) ? stateMachine.root : stateMachine; // TODO: accept only machines
|
|
472
118
|
|
|
473
|
-
const
|
|
474
|
-
|
|
119
|
+
const diagnostics = [];
|
|
120
|
+
const transitions = [...[...stateNode.transitions.values()].flat(), ...(stateNode.always ?? [])];
|
|
121
|
+
const edges = transitions.flatMap((t, transitionIndex) => {
|
|
122
|
+
if (t.to) {
|
|
123
|
+
diagnostics.push({
|
|
124
|
+
code: 'dynamic-target',
|
|
125
|
+
stateId: stateNode.id,
|
|
126
|
+
eventType: t.eventType,
|
|
127
|
+
transitionId: `${stateNode.id}:${transitionIndex}`,
|
|
128
|
+
message: 'Transition function destinations require execution or source analysis.'
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// A transition function computes its target when it runs, so there is
|
|
132
|
+
// no static edge to draw; a targetless transition stays in its state.
|
|
133
|
+
const targets = t.target ?? (t.to ? [] : [stateNode]);
|
|
475
134
|
return targets.map((target, targetIndex) => {
|
|
476
135
|
const edge = {
|
|
477
136
|
id: `${stateNode.id}:${transitionIndex}:${targetIndex}`,
|
|
@@ -498,11 +157,17 @@ function toDirectedGraph(stateMachine) {
|
|
|
498
157
|
return edge;
|
|
499
158
|
});
|
|
500
159
|
});
|
|
160
|
+
const children = getChildren(stateNode).map(toDirectedGraph);
|
|
161
|
+
diagnostics.push(...children.flatMap(child => child.analysis.diagnostics));
|
|
501
162
|
const graph = {
|
|
502
163
|
id: stateNode.id,
|
|
503
164
|
stateNode: stateNode,
|
|
504
|
-
children
|
|
165
|
+
children,
|
|
505
166
|
edges,
|
|
167
|
+
analysis: {
|
|
168
|
+
transitionTargetsComplete: diagnostics.length === 0,
|
|
169
|
+
diagnostics
|
|
170
|
+
},
|
|
506
171
|
toJSON: () => {
|
|
507
172
|
const {
|
|
508
173
|
id,
|
|
@@ -512,34 +177,44 @@ function toDirectedGraph(stateMachine) {
|
|
|
512
177
|
return {
|
|
513
178
|
id,
|
|
514
179
|
children,
|
|
515
|
-
edges: graphEdges
|
|
180
|
+
edges: graphEdges,
|
|
181
|
+
analysis: graph.analysis
|
|
516
182
|
};
|
|
517
183
|
}
|
|
518
184
|
};
|
|
519
185
|
return graph;
|
|
520
186
|
}
|
|
521
187
|
function isMachineLogic(logic) {
|
|
522
|
-
|
|
188
|
+
if (!logic || typeof logic !== 'object') {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
const machine = logic;
|
|
192
|
+
const root = machine.root;
|
|
193
|
+
return !!root && typeof root === 'object' && typeof root.id === 'string' && typeof root.states === 'object' && typeof root.transitions?.values === 'function' && typeof machine.getStateNodeById === 'function' && typeof machine.resolveState === 'function' && typeof machine.getTransitionData === 'function';
|
|
523
194
|
}
|
|
524
195
|
function resolveTraversalOptions(logic, traversalOptions, defaultOptions) {
|
|
525
196
|
const resolvedDefaultOptions = defaultOptions ?? (isMachineLogic(logic) ? createDefaultMachineOptions(logic, traversalOptions) : undefined);
|
|
526
197
|
const serializeState = traversalOptions?.serializeState ?? resolvedDefaultOptions?.serializeState ?? (state => JSON.stringify(state));
|
|
198
|
+
const eventSource = traversalOptions?.events ?? resolvedDefaultOptions?.events ?? [];
|
|
527
199
|
const traversalConfig = {
|
|
528
200
|
serializeState,
|
|
529
201
|
serializeEvent,
|
|
530
|
-
events: [],
|
|
531
202
|
filterEvents: undefined,
|
|
532
203
|
limit: Infinity,
|
|
533
|
-
fromState: undefined,
|
|
534
204
|
toState: undefined,
|
|
535
205
|
// Traversal should not continue past the `toState` predicate
|
|
536
206
|
// since the target state has already been reached at that point
|
|
537
207
|
stopWhen: traversalOptions?.toState,
|
|
538
208
|
...resolvedDefaultOptions,
|
|
539
|
-
...traversalOptions
|
|
209
|
+
...traversalOptions,
|
|
210
|
+
// Synthetic completion/delay events are supported by the machine runtime.
|
|
211
|
+
events: typeof eventSource === 'function' || Array.isArray(eventSource) ? eventSource : state => resolveEventCases(eventSource, state, createActor.isMachineSnapshot(state) ? createActor.getAllOwnEvents(state) : undefined),
|
|
212
|
+
fromState: traversalOptions?.fromState ?? resolvedDefaultOptions?.fromState
|
|
540
213
|
};
|
|
541
214
|
return traversalConfig;
|
|
542
215
|
}
|
|
216
|
+
|
|
217
|
+
/** @public */
|
|
543
218
|
function joinPaths(headPath, tailPath) {
|
|
544
219
|
const secondPathSource = tailPath.steps[0].state;
|
|
545
220
|
if (secondPathSource !== headPath.state) {
|
|
@@ -553,9 +228,18 @@ function joinPaths(headPath, tailPath) {
|
|
|
553
228
|
};
|
|
554
229
|
}
|
|
555
230
|
|
|
231
|
+
function createTraversalError(state, event, cause) {
|
|
232
|
+
return new Error(`Unable to evaluate graph transition from state ${state} with event ${event}. If the transition needs event payloads, provide representative events using the "events" option.`, {
|
|
233
|
+
cause
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
556
237
|
function resolveTransitionSnapshot(result) {
|
|
557
238
|
return Array.isArray(result) ? result[0] : result;
|
|
558
239
|
}
|
|
240
|
+
|
|
241
|
+
/** @public */
|
|
242
|
+
|
|
559
243
|
function getAdjacencyMap(logic, options) {
|
|
560
244
|
|
|
561
245
|
const {
|
|
@@ -573,21 +257,25 @@ function getAdjacencyMap(logic, options) {
|
|
|
573
257
|
const actorScope = createMockActorScope();
|
|
574
258
|
const fromState = customFromState ?? logic.getInitialSnapshot(actorScope,
|
|
575
259
|
// TODO: fix this
|
|
576
|
-
options
|
|
577
|
-
const adj =
|
|
260
|
+
options?.input);
|
|
261
|
+
const adj = Object.create(null);
|
|
578
262
|
let iterations = 0;
|
|
579
263
|
const queue = [{
|
|
580
264
|
nextState: fromState,
|
|
581
265
|
event: undefined,
|
|
582
266
|
prevState: undefined
|
|
583
267
|
}];
|
|
584
|
-
|
|
585
|
-
while (queue.length) {
|
|
268
|
+
for (let head = 0; head < queue.length; head++) {
|
|
586
269
|
const {
|
|
587
270
|
nextState: state,
|
|
588
271
|
event,
|
|
589
272
|
prevState
|
|
590
|
-
} = queue
|
|
273
|
+
} = queue[head];
|
|
274
|
+
// Release processed entries without shifting the remaining frontier.
|
|
275
|
+
if (head > 4096 && head * 2 > queue.length) {
|
|
276
|
+
queue.splice(0, head + 1);
|
|
277
|
+
head = -1;
|
|
278
|
+
}
|
|
591
279
|
if (iterations++ > limit) {
|
|
592
280
|
throw new Error('Traversal limit exceeded');
|
|
593
281
|
}
|
|
@@ -595,10 +283,9 @@ function getAdjacencyMap(logic, options) {
|
|
|
595
283
|
if (adj[serializedState]) {
|
|
596
284
|
continue;
|
|
597
285
|
}
|
|
598
|
-
stateMap.set(serializedState, state);
|
|
599
286
|
adj[serializedState] = {
|
|
600
287
|
state,
|
|
601
|
-
transitions:
|
|
288
|
+
transitions: Object.create(null)
|
|
602
289
|
};
|
|
603
290
|
if (stopWhen && stopWhen(state)) {
|
|
604
291
|
continue;
|
|
@@ -608,8 +295,14 @@ function getAdjacencyMap(logic, options) {
|
|
|
608
295
|
if (filterEvents && !filterEvents(state, nextEvent)) {
|
|
609
296
|
continue;
|
|
610
297
|
}
|
|
611
|
-
const
|
|
612
|
-
|
|
298
|
+
const serializedEvent = serializeEvent(nextEvent);
|
|
299
|
+
let nextSnapshot;
|
|
300
|
+
try {
|
|
301
|
+
nextSnapshot = resolveTransitionSnapshot(transition(state, nextEvent, actorScope));
|
|
302
|
+
} catch (cause) {
|
|
303
|
+
throw createTraversalError(serializedState, serializedEvent, cause);
|
|
304
|
+
}
|
|
305
|
+
adj[serializedState].transitions[serializedEvent] = {
|
|
613
306
|
event: nextEvent,
|
|
614
307
|
state: nextSnapshot
|
|
615
308
|
};
|
|
@@ -622,6 +315,8 @@ function getAdjacencyMap(logic, options) {
|
|
|
622
315
|
}
|
|
623
316
|
return adj;
|
|
624
317
|
}
|
|
318
|
+
|
|
319
|
+
/** @public */
|
|
625
320
|
function adjacencyMapToArray(adjMap) {
|
|
626
321
|
const adjList = [];
|
|
627
322
|
for (const adjValue of Object.values(adjMap)) {
|
|
@@ -643,7 +338,7 @@ function alterPath(path) {
|
|
|
643
338
|
steps = [{
|
|
644
339
|
state: path.state,
|
|
645
340
|
event: {
|
|
646
|
-
type:
|
|
341
|
+
type: createActor.XSTATE_INIT
|
|
647
342
|
}
|
|
648
343
|
}];
|
|
649
344
|
} else {
|
|
@@ -652,7 +347,7 @@ function alterPath(path) {
|
|
|
652
347
|
steps.push({
|
|
653
348
|
state: step.state,
|
|
654
349
|
event: i === 0 ? {
|
|
655
|
-
type:
|
|
350
|
+
type: createActor.XSTATE_INIT
|
|
656
351
|
} : path.steps[i - 1].event
|
|
657
352
|
});
|
|
658
353
|
}
|
|
@@ -667,46 +362,60 @@ function alterPath(path) {
|
|
|
667
362
|
};
|
|
668
363
|
}
|
|
669
364
|
|
|
670
|
-
|
|
671
|
-
return !!value && value instanceof StateMachine.StateMachine;
|
|
672
|
-
}
|
|
365
|
+
/** @public */
|
|
673
366
|
function getPathsFromEvents(logic, events, options) {
|
|
674
367
|
const resolvedOptions = resolveTraversalOptions(logic, {
|
|
675
368
|
events,
|
|
676
369
|
...options
|
|
677
|
-
}
|
|
370
|
+
});
|
|
678
371
|
const actorScope = createMockActorScope();
|
|
679
372
|
const fromState = resolvedOptions.fromState ?? logic.getInitialSnapshot(actorScope,
|
|
680
373
|
// TODO: fix this
|
|
681
374
|
options?.input);
|
|
682
375
|
const {
|
|
683
376
|
serializeState,
|
|
684
|
-
serializeEvent
|
|
377
|
+
serializeEvent,
|
|
378
|
+
filterEvents,
|
|
379
|
+
stopWhen,
|
|
380
|
+
limit
|
|
685
381
|
} = resolvedOptions;
|
|
686
|
-
const adjacency = getAdjacencyMap(logic, resolvedOptions);
|
|
687
|
-
const stateMap = new Map();
|
|
688
382
|
const steps = [];
|
|
689
|
-
const serializedFromState = serializeState(fromState, undefined, undefined);
|
|
690
|
-
stateMap.set(serializedFromState, fromState);
|
|
691
|
-
let stateSerial = serializedFromState;
|
|
692
383
|
let state = fromState;
|
|
384
|
+
let stateSerial = serializeState(state, undefined, undefined);
|
|
693
385
|
for (const event of events) {
|
|
386
|
+
if (steps.length >= limit) {
|
|
387
|
+
throw new Error('Traversal limit exceeded');
|
|
388
|
+
}
|
|
389
|
+
const eventSerial = serializeEvent(event);
|
|
390
|
+
let nextEvent = event;
|
|
391
|
+
if (options?.events !== undefined) {
|
|
392
|
+
const candidates = typeof options.events === 'function' ? options.events(state) : options.events;
|
|
393
|
+
nextEvent = undefined;
|
|
394
|
+
for (let index = candidates.length - 1; index >= 0; index--) {
|
|
395
|
+
const candidate = candidates[index];
|
|
396
|
+
if ((!filterEvents || filterEvents(state, candidate)) && serializeEvent(candidate) === eventSerial) {
|
|
397
|
+
nextEvent = candidate;
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
} else if (filterEvents && !filterEvents(state, event)) {
|
|
402
|
+
nextEvent = undefined;
|
|
403
|
+
}
|
|
404
|
+
if (!nextEvent || stopWhen?.(state)) {
|
|
405
|
+
throw new Error(`Invalid transition from ${stateSerial} with ${eventSerial}`);
|
|
406
|
+
}
|
|
694
407
|
steps.push({
|
|
695
|
-
state
|
|
408
|
+
state,
|
|
696
409
|
event
|
|
697
410
|
});
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
state
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
if (!nextState) {
|
|
704
|
-
throw new Error(`Invalid transition from ${stateSerial} with ${eventSerial}`);
|
|
411
|
+
let result;
|
|
412
|
+
try {
|
|
413
|
+
result = logic.transition(state, nextEvent, actorScope);
|
|
414
|
+
} catch (cause) {
|
|
415
|
+
throw createTraversalError(stateSerial, eventSerial, cause);
|
|
705
416
|
}
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
stateMap.set(nextStateSerial, nextState);
|
|
709
|
-
stateSerial = nextStateSerial;
|
|
417
|
+
const nextState = Array.isArray(result) ? result[0] : result;
|
|
418
|
+
stateSerial = serializeState(nextState, event, state);
|
|
710
419
|
state = nextState;
|
|
711
420
|
}
|
|
712
421
|
|
|
@@ -722,11 +431,16 @@ function getPathsFromEvents(logic, events, options) {
|
|
|
722
431
|
})];
|
|
723
432
|
}
|
|
724
433
|
|
|
434
|
+
/** @public */
|
|
435
|
+
|
|
725
436
|
function getShortestPaths(logic, options) {
|
|
726
437
|
const resolvedOptions = resolveTraversalOptions(logic, options);
|
|
727
438
|
const serializeState = resolvedOptions.serializeState;
|
|
728
439
|
const fromState = resolvedOptions.fromState ?? logic.getInitialSnapshot(createMockActorScope(), options?.input);
|
|
729
|
-
const adjacency = getAdjacencyMap(logic,
|
|
440
|
+
const adjacency = getAdjacencyMap(logic, {
|
|
441
|
+
...resolvedOptions,
|
|
442
|
+
fromState
|
|
443
|
+
});
|
|
730
444
|
|
|
731
445
|
// weight, state, event
|
|
732
446
|
const weightMap = new Map();
|
|
@@ -778,7 +492,7 @@ function getShortestPaths(logic, options) {
|
|
|
778
492
|
visited.add(serializedState);
|
|
779
493
|
unvisited.delete(serializedState);
|
|
780
494
|
}
|
|
781
|
-
const statePlanMap =
|
|
495
|
+
const statePlanMap = Object.create(null);
|
|
782
496
|
const paths = [];
|
|
783
497
|
weightMap.forEach(({
|
|
784
498
|
weight,
|
|
@@ -786,7 +500,7 @@ function getShortestPaths(logic, options) {
|
|
|
786
500
|
event: fromEvent
|
|
787
501
|
}, stateSerial) => {
|
|
788
502
|
const state = stateMap.get(stateSerial);
|
|
789
|
-
const steps =
|
|
503
|
+
const steps = fromState === undefined ? [] : statePlanMap[fromState].paths[0].steps.concat({
|
|
790
504
|
state: stateMap.get(fromState),
|
|
791
505
|
event: fromEvent
|
|
792
506
|
});
|
|
@@ -810,19 +524,24 @@ function getShortestPaths(logic, options) {
|
|
|
810
524
|
return paths.map(alterPath);
|
|
811
525
|
}
|
|
812
526
|
|
|
527
|
+
/** @public */
|
|
528
|
+
|
|
813
529
|
function getSimplePaths(logic, options) {
|
|
814
530
|
const resolvedOptions = resolveTraversalOptions(logic, options);
|
|
815
531
|
const actorScope = createMockActorScope();
|
|
816
532
|
const fromState = resolvedOptions.fromState ?? logic.getInitialSnapshot(actorScope, options?.input);
|
|
817
533
|
const serializeState = resolvedOptions.serializeState;
|
|
818
|
-
const adjacency = getAdjacencyMap(logic,
|
|
534
|
+
const adjacency = getAdjacencyMap(logic, {
|
|
535
|
+
...resolvedOptions,
|
|
536
|
+
fromState
|
|
537
|
+
});
|
|
819
538
|
const stateMap = new Map();
|
|
820
539
|
const visitCtx = {
|
|
821
540
|
vertices: new Set(),
|
|
822
541
|
edges: new Set()
|
|
823
542
|
};
|
|
824
543
|
const steps = [];
|
|
825
|
-
const pathMap =
|
|
544
|
+
const pathMap = Object.create(null);
|
|
826
545
|
function util(fromStateSerial, toStateSerial) {
|
|
827
546
|
const fromState = stateMap.get(fromStateSerial);
|
|
828
547
|
visitCtx.vertices.add(fromStateSerial);
|
|
@@ -880,16 +599,13 @@ function getSimplePaths(logic, options) {
|
|
|
880
599
|
return simplePaths.map(alterPath);
|
|
881
600
|
}
|
|
882
601
|
|
|
883
|
-
exports.
|
|
602
|
+
exports.getAllOwnEvents = createActor.getAllOwnEvents;
|
|
884
603
|
exports.adjacencyMapToArray = adjacencyMapToArray;
|
|
885
|
-
exports.createShortestPathsGen = createShortestPathsGen;
|
|
886
|
-
exports.createSimplePathsGen = createSimplePathsGen;
|
|
887
|
-
exports.createTestModel = createTestModel;
|
|
888
604
|
exports.getAdjacencyMap = getAdjacencyMap;
|
|
605
|
+
exports.getDescendantStateNodes = getDescendantStateNodes;
|
|
889
606
|
exports.getPathsFromEvents = getPathsFromEvents;
|
|
890
607
|
exports.getShortestPaths = getShortestPaths;
|
|
891
608
|
exports.getSimplePaths = getSimplePaths;
|
|
892
|
-
exports.getStateNodes = getStateNodes;
|
|
893
609
|
exports.joinPaths = joinPaths;
|
|
894
610
|
exports.serializeSnapshot = serializeSnapshot;
|
|
895
611
|
exports.toDirectedGraph = toDirectedGraph;
|