xstate 6.0.0-alpha.8 → 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 +107 -32
- 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 -19
- 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 +1086 -147
- 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 +547 -104
- 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 -1430
- package/dist/xstate.cjs.mjs +23 -13
- package/dist/xstate.development.cjs.js +2486 -1430
- package/dist/xstate.development.cjs.mjs +23 -13
- package/dist/xstate.development.esm.js +2445 -1398
- package/dist/xstate.esm.js +2445 -1398
- 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/bin/agent.js
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
// Data-only agent operations. No application module loading, code evaluation or effect
|
|
2
|
+
// execution is needed to inspect and explore a JSON machine definition.
|
|
3
|
+
const { readFileSync } = require('node:fs');
|
|
4
|
+
const { join } = require('node:path');
|
|
5
|
+
const { createHash } = require('node:crypto');
|
|
6
|
+
const core = require('../dist/xstate.cjs.js');
|
|
7
|
+
const graph = require('../dist/xstate-graph.cjs.js');
|
|
8
|
+
const { version } = require('../package.json');
|
|
9
|
+
|
|
10
|
+
function fail(code, message, details) {
|
|
11
|
+
const error = new Error(message);
|
|
12
|
+
error.code = code;
|
|
13
|
+
error.details = details;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readJSON(file) {
|
|
18
|
+
if (!file) fail('MISSING_ARGUMENT', 'Expected a JSON file path.');
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
fail('INVALID_JSON', `Cannot read JSON from ${file}: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function expressions(value, path = '') {
|
|
27
|
+
if (!value || typeof value !== 'object') return [];
|
|
28
|
+
if (Object.hasOwn(value, '@code')) return [path];
|
|
29
|
+
return Object.entries(value).flatMap(([key, entry]) =>
|
|
30
|
+
expressions(
|
|
31
|
+
entry,
|
|
32
|
+
`${path}/${key.replaceAll('~', '~0').replaceAll('/', '~1')}`
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function loadMachine(file) {
|
|
38
|
+
const definition = readJSON(file);
|
|
39
|
+
if (
|
|
40
|
+
!definition ||
|
|
41
|
+
typeof definition !== 'object' ||
|
|
42
|
+
Array.isArray(definition)
|
|
43
|
+
) {
|
|
44
|
+
fail('INVALID_MACHINE', 'Expected a machine definition object.');
|
|
45
|
+
}
|
|
46
|
+
const paths = expressions(definition);
|
|
47
|
+
if (paths.length) {
|
|
48
|
+
fail(
|
|
49
|
+
'EVALUATOR_REQUIRED',
|
|
50
|
+
'The data-only CLI does not evaluate code expressions. Use the library with explicit runtime sources.',
|
|
51
|
+
{ paths }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return {
|
|
56
|
+
definition,
|
|
57
|
+
machine: core.machineFromJSON(definition),
|
|
58
|
+
hash: createHash('sha256')
|
|
59
|
+
.update(JSON.stringify(definition))
|
|
60
|
+
.digest('hex')
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
fail('INVALID_MACHINE', error.message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function view(snapshot) {
|
|
68
|
+
return {
|
|
69
|
+
value: snapshot.value,
|
|
70
|
+
context: snapshot.context,
|
|
71
|
+
stateInputs: snapshot.getInputs(),
|
|
72
|
+
status: snapshot.status,
|
|
73
|
+
output: snapshot.output,
|
|
74
|
+
error:
|
|
75
|
+
snapshot.error instanceof Error
|
|
76
|
+
? { name: snapshot.error.name, message: snapshot.error.message }
|
|
77
|
+
: snapshot.error
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function effectView(effect) {
|
|
82
|
+
return {
|
|
83
|
+
type: effect.type,
|
|
84
|
+
kind: effect.kind,
|
|
85
|
+
params: effect.params,
|
|
86
|
+
event: effect.event
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function eventList(value) {
|
|
91
|
+
const events = Array.isArray(value) ? value : value?.events;
|
|
92
|
+
if (
|
|
93
|
+
!Array.isArray(events) ||
|
|
94
|
+
events.some((event) => !event || typeof event.type !== 'string')
|
|
95
|
+
) {
|
|
96
|
+
fail(
|
|
97
|
+
'INVALID_EVENTS',
|
|
98
|
+
'Expected an array of event objects with string type fields.'
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return events;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function replay(machine, events, input) {
|
|
105
|
+
let [snapshot, effects] = core.initialTransition(machine, input);
|
|
106
|
+
const trace = [
|
|
107
|
+
{ event: null, snapshot: view(snapshot), effects: effects.map(effectView) }
|
|
108
|
+
];
|
|
109
|
+
const snapshots = [snapshot];
|
|
110
|
+
for (const event of events) {
|
|
111
|
+
[snapshot, effects] = core.transition(machine, snapshot, event);
|
|
112
|
+
snapshots.push(snapshot);
|
|
113
|
+
trace.push({
|
|
114
|
+
event,
|
|
115
|
+
snapshot: view(snapshot),
|
|
116
|
+
effects: effects.map(effectView)
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { trace, snapshots };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function assertions(machine, options) {
|
|
123
|
+
const states = new Set([
|
|
124
|
+
machine.root.id,
|
|
125
|
+
...graph.getDescendantStateNodes(machine.root).map((node) => node.id)
|
|
126
|
+
]);
|
|
127
|
+
for (const kind of ['never', 'reachable']) {
|
|
128
|
+
if (options[kind] === undefined) continue;
|
|
129
|
+
if (
|
|
130
|
+
!Array.isArray(options[kind]) ||
|
|
131
|
+
options[kind].some(
|
|
132
|
+
(id) =>
|
|
133
|
+
typeof id !== 'string' ||
|
|
134
|
+
!id.startsWith('#') ||
|
|
135
|
+
!states.has(id.slice(1))
|
|
136
|
+
)
|
|
137
|
+
) {
|
|
138
|
+
fail(
|
|
139
|
+
'INVALID_ASSERTION',
|
|
140
|
+
`${kind} must list existing state IDs prefixed with #.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function violation(snapshots, never = []) {
|
|
147
|
+
for (const snapshot of snapshots) {
|
|
148
|
+
for (const state of never) {
|
|
149
|
+
if (snapshot.matches(state)) return { state, snapshot: view(snapshot) };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function difference(before, after, path = '') {
|
|
155
|
+
if (JSON.stringify(before) === JSON.stringify(after)) return [];
|
|
156
|
+
if (
|
|
157
|
+
before &&
|
|
158
|
+
after &&
|
|
159
|
+
typeof before === 'object' &&
|
|
160
|
+
typeof after === 'object' &&
|
|
161
|
+
!Array.isArray(before) &&
|
|
162
|
+
!Array.isArray(after)
|
|
163
|
+
) {
|
|
164
|
+
return [...new Set([...Object.keys(before), ...Object.keys(after)])]
|
|
165
|
+
.sort()
|
|
166
|
+
.flatMap((key) => {
|
|
167
|
+
const pointer = `${path}/${key.replaceAll('~', '~0').replaceAll('/', '~1')}`;
|
|
168
|
+
if (!Object.hasOwn(before, key))
|
|
169
|
+
return [{ op: 'add', path: pointer, after: after[key] }];
|
|
170
|
+
if (!Object.hasOwn(after, key))
|
|
171
|
+
return [{ op: 'remove', path: pointer, before: before[key] }];
|
|
172
|
+
return difference(before[key], after[key], pointer);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return [{ op: 'replace', path, before, after }];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function run(command, rawArgs) {
|
|
179
|
+
const args = rawArgs.filter((arg) => arg !== '--json');
|
|
180
|
+
if (command === 'docs') {
|
|
181
|
+
return {
|
|
182
|
+
index: join(__dirname, '../docs/index.md'),
|
|
183
|
+
agentGuide: join(__dirname, '../docs/agents.md'),
|
|
184
|
+
stability: readJSON(join(__dirname, '../docs/api-stability.json'))
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (command === 'diff') {
|
|
188
|
+
const changes = difference(readJSON(args[0]), readJSON(args[1]));
|
|
189
|
+
return {
|
|
190
|
+
changed: changes.length > 0,
|
|
191
|
+
changes,
|
|
192
|
+
compatibility: 'notChecked'
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const { machine, definition, hash } = loadMachine(args[0]);
|
|
196
|
+
if (command === 'validate')
|
|
197
|
+
return {
|
|
198
|
+
valid: true,
|
|
199
|
+
scope: 'definition',
|
|
200
|
+
runtimePayloadValidation: false
|
|
201
|
+
};
|
|
202
|
+
if (command === 'inspect') {
|
|
203
|
+
return {
|
|
204
|
+
definition,
|
|
205
|
+
graph: graph.toDirectedGraph(machine),
|
|
206
|
+
serializationDiagnostics:
|
|
207
|
+
core.getMachineSerializationDiagnostics(machine),
|
|
208
|
+
runtimePayloadValidation: false
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (command === 'simulate') {
|
|
212
|
+
const request = readJSON(args[1]);
|
|
213
|
+
return {
|
|
214
|
+
effectsExecuted: false,
|
|
215
|
+
...omitSnapshots(replay(machine, eventList(request), request.input))
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (command === 'replay') {
|
|
219
|
+
const fixture = readJSON(args[1]);
|
|
220
|
+
if (
|
|
221
|
+
fixture.schemaVersion !== 1 ||
|
|
222
|
+
fixture.machineHash !== hash ||
|
|
223
|
+
fixture.xstateVersion !== version
|
|
224
|
+
) {
|
|
225
|
+
fail(
|
|
226
|
+
'FIXTURE_MISMATCH',
|
|
227
|
+
'The fixture must match this definition, XState version and fixture format.'
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
assertions(machine, fixture);
|
|
231
|
+
const result = replay(machine, eventList(fixture), fixture.input);
|
|
232
|
+
const failure = violation(result.snapshots, fixture.never);
|
|
233
|
+
if (failure)
|
|
234
|
+
fail('PROPERTY_VIOLATION', 'A forbidden stable state was reached.', {
|
|
235
|
+
...failure,
|
|
236
|
+
trace: result.trace,
|
|
237
|
+
fixture
|
|
238
|
+
});
|
|
239
|
+
return { effectsExecuted: false, trace: result.trace };
|
|
240
|
+
}
|
|
241
|
+
if (command === 'test') {
|
|
242
|
+
const options = readJSON(args[1]);
|
|
243
|
+
assertions(machine, options);
|
|
244
|
+
if (!(options.never?.length || options.reachable?.length)) {
|
|
245
|
+
fail(
|
|
246
|
+
'MISSING_ASSERTION',
|
|
247
|
+
'Supply at least one never or reachable state assertion.'
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
if (
|
|
251
|
+
options.limit !== undefined &&
|
|
252
|
+
(!Number.isSafeInteger(options.limit) || options.limit <= 0)
|
|
253
|
+
) {
|
|
254
|
+
fail('INVALID_LIMIT', 'limit must be a positive safe integer.');
|
|
255
|
+
}
|
|
256
|
+
const paths = graph.getShortestPaths(machine, {
|
|
257
|
+
events: options.events,
|
|
258
|
+
input: options.input,
|
|
259
|
+
limit: options.limit ?? 10000
|
|
260
|
+
});
|
|
261
|
+
const visited = paths.map((path) => path.state);
|
|
262
|
+
const errored = visited.find((snapshot) => snapshot.status === 'error');
|
|
263
|
+
if (errored)
|
|
264
|
+
fail(
|
|
265
|
+
'MODEL_EXECUTION_FAILED',
|
|
266
|
+
'Model exploration entered an error snapshot.',
|
|
267
|
+
{ snapshot: view(errored) }
|
|
268
|
+
);
|
|
269
|
+
for (const path of paths) {
|
|
270
|
+
const failure = violation(
|
|
271
|
+
path.steps.map((step) => step.state),
|
|
272
|
+
options.never
|
|
273
|
+
);
|
|
274
|
+
if (failure) {
|
|
275
|
+
const fixture = {
|
|
276
|
+
schemaVersion: 1,
|
|
277
|
+
xstateVersion: version,
|
|
278
|
+
machineHash: hash,
|
|
279
|
+
events: path.steps.slice(1).map((step) => step.event),
|
|
280
|
+
input: options.input,
|
|
281
|
+
never: options.never
|
|
282
|
+
};
|
|
283
|
+
fail('PROPERTY_VIOLATION', 'A forbidden stable state was reached.', {
|
|
284
|
+
...failure,
|
|
285
|
+
fixture
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const missing = (options.reachable ?? []).filter(
|
|
290
|
+
(state) => !visited.some((snapshot) => snapshot.matches(state))
|
|
291
|
+
);
|
|
292
|
+
if (missing.length)
|
|
293
|
+
fail(
|
|
294
|
+
'REACHABILITY_NOT_WITNESSED',
|
|
295
|
+
'No supplied event representative reached these states.',
|
|
296
|
+
{ states: missing }
|
|
297
|
+
);
|
|
298
|
+
return {
|
|
299
|
+
passed: true,
|
|
300
|
+
scope: 'stableSnapshots',
|
|
301
|
+
effectsExecuted: false,
|
|
302
|
+
proof: false,
|
|
303
|
+
representativeEvents: options.events ?? null,
|
|
304
|
+
eventMode: Array.isArray(options.events)
|
|
305
|
+
? 'replaceDefaults'
|
|
306
|
+
: 'augmentDefaults',
|
|
307
|
+
limit: options.limit ?? 10000,
|
|
308
|
+
paths: paths.length,
|
|
309
|
+
states: visited.map(view)
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
fail('UNKNOWN_COMMAND', `Unknown agent command: ${command}`);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function omitSnapshots({ trace }) {
|
|
316
|
+
return { trace };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
module.exports = (command, args) => {
|
|
320
|
+
try {
|
|
321
|
+
const result = run(command, args);
|
|
322
|
+
console.log(
|
|
323
|
+
JSON.stringify({
|
|
324
|
+
schemaVersion: 1,
|
|
325
|
+
xstateVersion: version,
|
|
326
|
+
command,
|
|
327
|
+
ok: true,
|
|
328
|
+
result
|
|
329
|
+
})
|
|
330
|
+
);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.log(
|
|
333
|
+
JSON.stringify({
|
|
334
|
+
schemaVersion: 1,
|
|
335
|
+
xstateVersion: version,
|
|
336
|
+
command,
|
|
337
|
+
ok: false,
|
|
338
|
+
error: {
|
|
339
|
+
code: error.code ?? 'OPERATION_FAILED',
|
|
340
|
+
message: error.message,
|
|
341
|
+
details: error.details
|
|
342
|
+
}
|
|
343
|
+
})
|
|
344
|
+
);
|
|
345
|
+
process.exitCode = 1;
|
|
346
|
+
}
|
|
347
|
+
};
|
package/bin/xstate.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Lightweight `xstate` CLI. Heavy commands are delegated to dedicated
|
|
3
|
+
// packages fetched on demand so the core package stays dependency-free.
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
switch (command) {
|
|
9
|
+
case 'docs':
|
|
10
|
+
case 'inspect':
|
|
11
|
+
case 'validate':
|
|
12
|
+
case 'simulate':
|
|
13
|
+
case 'test':
|
|
14
|
+
case 'replay':
|
|
15
|
+
case 'diff':
|
|
16
|
+
require('./agent.js')(command, rest);
|
|
17
|
+
break;
|
|
18
|
+
case 'migrate': {
|
|
19
|
+
// Delegates to @xstate/codemod (ts-morph based v5 → v6 codemods).
|
|
20
|
+
const result = spawnSync(
|
|
21
|
+
process.platform === 'win32' ? 'npx.cmd' : 'npx',
|
|
22
|
+
['-y', '@xstate/codemod', 'migrate', ...rest],
|
|
23
|
+
{ stdio: 'inherit' }
|
|
24
|
+
);
|
|
25
|
+
process.exit(result.status ?? 1);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
case '--help':
|
|
29
|
+
case 'help':
|
|
30
|
+
case undefined:
|
|
31
|
+
console.log(
|
|
32
|
+
[
|
|
33
|
+
'Usage: xstate <command>',
|
|
34
|
+
'',
|
|
35
|
+
'Commands:',
|
|
36
|
+
' docs Installed docs and export stability (JSON)',
|
|
37
|
+
' inspect <machine.json> Definition and graph completeness (JSON)',
|
|
38
|
+
' validate <machine.json> Validate a data-only definition (JSON)',
|
|
39
|
+
' simulate <machine> <events> Calculate transitions without effects (JSON)',
|
|
40
|
+
' test <machine> <options> Check sampled stable-state properties (JSON)',
|
|
41
|
+
' replay <machine> <fixture> Replay a matching counterexample (JSON)',
|
|
42
|
+
' diff <before> <after> Structural definition changes (JSON)',
|
|
43
|
+
' migrate [globs...] [--dry] Migrate source files to the current XState version',
|
|
44
|
+
' (delegates to @xstate/codemod)'
|
|
45
|
+
].join('\n')
|
|
46
|
+
);
|
|
47
|
+
process.exit(command ? 0 : 1);
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
console.error(`Unknown command: ${command}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|