@statelyai/sdk 0.6.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sync.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CreateProjectInput, ProjectData, StudioClient, StudioMachineRecord, XStateVersion } from "./studio.mjs";
2
- import { b as DigraphConfig, i as StatelyGraph } from "./graph-DpBGHZwl.mjs";
2
+ import { b as DigraphConfig, i as StatelyGraph } from "./graph-zuNj3kfa.mjs";
3
3
  import { GraphDiff } from "@statelyai/graph";
4
4
 
5
5
  //#region src/sync.d.ts
@@ -52,8 +52,26 @@ interface PushSyncResult {
52
52
  machine: StudioMachineRecord<DigraphConfig>;
53
53
  outputPath?: string;
54
54
  }
55
+ interface PushLocalMachineLinksResult {
56
+ sourcePath: string;
57
+ project: ProjectData;
58
+ created: Array<{
59
+ machineIndex: number;
60
+ machine: StudioMachineRecord<DigraphConfig>;
61
+ }>;
62
+ updated: Array<{
63
+ machineIndex: number;
64
+ machine: StudioMachineRecord<DigraphConfig>;
65
+ }>;
66
+ skipped: Array<{
67
+ machineIndex: number;
68
+ reason: string;
69
+ }>;
70
+ outputPath?: string;
71
+ }
72
+ declare function pushLocalMachineLinks(options: PushSyncOptions): Promise<PushLocalMachineLinksResult>;
55
73
  declare function planSync(options: PlanSyncOptions): Promise<SyncPlan>;
56
74
  declare function pullSync(options: PlanSyncOptions): Promise<PullSyncResult>;
57
75
  declare function pushSync(options: PushSyncOptions): Promise<PushSyncResult>;
58
76
  //#endregion
59
- export { PlanSyncOptions, PullSyncResult, PushSyncOptions, PushSyncProjectOptions, PushSyncResult, ResolvedSyncInput, SyncInputFormat, SyncPlan, SyncPlanSummary, planSync, pullSync, pushSync };
77
+ export { PlanSyncOptions, PullSyncResult, PushLocalMachineLinksResult, PushSyncOptions, PushSyncProjectOptions, PushSyncResult, ResolvedSyncInput, SyncInputFormat, SyncPlan, SyncPlanSummary, planSync, pullSync, pushLocalMachineLinks, pushSync };
package/dist/sync.mjs CHANGED
@@ -1,377 +1,5 @@
1
- import { createStatelyClient } from "./studio.mjs";
2
- import { d as upsertStatelyPragma, t as graphToXStateTS } from "./graphToXStateTS-Gzh0ZqbN.mjs";
3
- import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
4
- import { getDiff, isEmptyDiff } from "@statelyai/graph";
5
- import fs from "node:fs/promises";
6
- import path from "node:path";
7
- import { execFile } from "node:child_process";
8
- import { promisify } from "node:util";
1
+ import "./studio.mjs";
2
+ import "./graphToXStateTS-Gzh0ZqbN.mjs";
3
+ import { i as pushSync, n as pullSync, r as pushLocalMachineLinks, t as planSync } from "./sync-DLkTmSyA.mjs";
9
4
 
10
- //#region src/sync.ts
11
- const execFileAsync = promisify(execFile);
12
- function isUrl(value) {
13
- try {
14
- const url = new URL(value);
15
- return url.protocol === "http:" || url.protocol === "https:";
16
- } catch {
17
- return false;
18
- }
19
- }
20
- async function fileExists(filePath) {
21
- try {
22
- await fs.access(filePath);
23
- return true;
24
- } catch {
25
- return false;
26
- }
27
- }
28
- function parseGitHubRemote(remoteUrl) {
29
- const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
30
- if (httpsMatch) {
31
- const [, owner, repo] = httpsMatch;
32
- if (!owner || !repo) return null;
33
- return {
34
- url: `https://github.com/${owner}/${repo}`,
35
- owner,
36
- repo
37
- };
38
- }
39
- const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
40
- if (sshMatch) {
41
- const [, owner, repo] = sshMatch;
42
- if (!owner || !repo) return null;
43
- return {
44
- url: `https://github.com/${owner}/${repo}`,
45
- owner,
46
- repo
47
- };
48
- }
49
- return null;
50
- }
51
- async function inferConnectedRepo(filePath, cwd) {
52
- const workingDir = cwd ?? path.dirname(filePath);
53
- try {
54
- const [{ stdout: repoRootStdout }, { stdout: remoteStdout }, { stdout: branchStdout }, { stdout: treeShaStdout }] = await Promise.all([
55
- execFileAsync("git", ["rev-parse", "--show-toplevel"], { cwd: workingDir }),
56
- execFileAsync("git", [
57
- "remote",
58
- "get-url",
59
- "origin"
60
- ], { cwd: workingDir }),
61
- execFileAsync("git", ["branch", "--show-current"], { cwd: workingDir }),
62
- execFileAsync("git", ["rev-parse", "HEAD"], { cwd: workingDir })
63
- ]);
64
- const repoRoot = repoRootStdout.trim();
65
- const remote = parseGitHubRemote(remoteStdout.trim());
66
- const branch = branchStdout.trim();
67
- const treeSha = treeShaStdout.trim();
68
- if (!remote || !branch || !treeSha) return;
69
- const relativePath = path.relative(repoRoot, filePath).replace(/\\/g, "/");
70
- const relativeDir = path.dirname(relativePath).replace(/\\/g, "/");
71
- const selectedPaths = relativePath && relativePath !== "." ? [relativePath] : [];
72
- return {
73
- ...remote,
74
- branch,
75
- treeSha,
76
- autoSync: false,
77
- pathForNewFiles: relativeDir && relativeDir !== "." ? relativeDir : "src/stately-studio",
78
- selectedPaths
79
- };
80
- } catch {
81
- return;
82
- }
83
- }
84
- function normalizeActions(value) {
85
- if (value == null) return [];
86
- return (Array.isArray(value) ? value : [value]).map((item) => {
87
- if (typeof item === "string") return { type: item };
88
- if (typeof item === "object" && item !== null && "type" in item) {
89
- const type = item.type;
90
- const params = "params" in item && typeof item.params === "object" && item.params ? item.params : void 0;
91
- return {
92
- type,
93
- ...params ? { params } : {}
94
- };
95
- }
96
- return { type: String(item) };
97
- });
98
- }
99
- function normalizeTags(value) {
100
- if (!value) return [];
101
- if (Array.isArray(value)) return value.map((tag) => String(tag));
102
- return [String(value)];
103
- }
104
- function normalizeInvoke(value) {
105
- if (!value) return [];
106
- return (Array.isArray(value) ? value : [value]).map((invoke, index) => ({
107
- src: invoke.src,
108
- id: invoke.id ?? `invoke[${index}]`,
109
- ...invoke.input ? { input: invoke.input } : {}
110
- }));
111
- }
112
- function resolveTargetId(sourceParentPath, target) {
113
- if (!target) return;
114
- if (target.startsWith("#")) {
115
- const stripped = target.slice(1);
116
- const machineIndex = stripped.indexOf(".");
117
- if (machineIndex >= 0) return `(machine).${stripped.slice(machineIndex + 1)}`;
118
- return `(machine).${stripped}`;
119
- }
120
- if (target.startsWith("(machine)")) return target;
121
- if (target.includes(".")) return `(machine).${target}`;
122
- return `${sourceParentPath}.${target}`;
123
- }
124
- function appendTransitionEdges(edges, sourceId, sourceParentPath, eventType, transition, edgeCounts) {
125
- const transitions = Array.isArray(transition) ? transition : [transition];
126
- for (const item of transitions) {
127
- const normalized = typeof item === "string" ? { target: item } : item;
128
- const targetId = resolveTargetId(sourceParentPath, Array.isArray(normalized.target) ? normalized.target[0] : normalized.target);
129
- const groupKey = `${sourceId}:${eventType}`;
130
- const index = edgeCounts.get(groupKey) ?? 0;
131
- edgeCounts.set(groupKey, index + 1);
132
- edges.push({
133
- type: "edge",
134
- id: `${sourceId}#${eventType}[${index}]`,
135
- sourceId,
136
- targetId: targetId ?? sourceId,
137
- label: eventType,
138
- data: {
139
- eventType,
140
- transitionType: normalized.reenter === true ? "reenter" : normalized.internal === true || !targetId ? "targetless" : "normal",
141
- ...normalized.guard ? { guard: typeof normalized.guard === "string" ? { type: normalized.guard } : {
142
- type: normalized.guard.type,
143
- ...normalized.guard.params ? { params: normalized.guard.params } : {}
144
- } } : {},
145
- actions: normalizeActions(normalized.actions),
146
- ...normalized.description ? { description: normalized.description } : {}
147
- }
148
- });
149
- }
150
- }
151
- function fromXStateConfig(config) {
152
- const nodes = [];
153
- const edges = [];
154
- const edgeCounts = /* @__PURE__ */ new Map();
155
- function visitNode(key, nodeConfig, parentId, parentPath) {
156
- const nodeId = parentPath ? `${parentPath}.${key}` : "(machine)";
157
- const currentPath = parentPath ? nodeId : "(machine)";
158
- const initialId = nodeConfig.initial ? `${currentPath}.${nodeConfig.initial}` : void 0;
159
- nodes.push({
160
- type: "node",
161
- id: nodeId,
162
- parentId,
163
- label: key,
164
- ...initialId ? { initialNodeId: initialId } : {},
165
- data: {
166
- key,
167
- ...nodeConfig.type ? { type: nodeConfig.type } : {},
168
- ...nodeConfig.history ? { history: nodeConfig.history } : {},
169
- ...initialId ? { initialId } : {},
170
- entry: normalizeActions(nodeConfig.entry),
171
- exit: normalizeActions(nodeConfig.exit),
172
- invokes: normalizeInvoke(nodeConfig.invoke),
173
- tags: normalizeTags(nodeConfig.tags),
174
- ...nodeConfig.description ? { description: nodeConfig.description } : {}
175
- }
176
- });
177
- for (const [eventType, transition] of Object.entries(nodeConfig.on ?? {})) appendTransitionEdges(edges, nodeId, parentPath ?? "(machine)", eventType, transition, edgeCounts);
178
- if (nodeConfig.always) appendTransitionEdges(edges, nodeId, parentPath ?? "(machine)", "", nodeConfig.always, edgeCounts);
179
- for (const [childKey, childConfig] of Object.entries(nodeConfig.states ?? {})) visitNode(childKey, childConfig, nodeId, currentPath);
180
- }
181
- visitNode("(machine)", config, null, null);
182
- return {
183
- id: config.id ?? "machine",
184
- nodes,
185
- edges,
186
- data: {}
187
- };
188
- }
189
- async function resolveLocalFile(locator, options) {
190
- const filePath = path.resolve(options.cwd ?? process.cwd(), locator);
191
- const contents = await fs.readFile(filePath, "utf8");
192
- const extension = path.extname(filePath).toLowerCase();
193
- if (extension === ".ts" || extension === ".tsx" || extension === ".js" || extension === ".jsx") {
194
- const config = (await (options.client ?? createStatelyClient({
195
- apiKey: options.apiKey,
196
- baseUrl: options.baseUrl,
197
- fetch: options.fetch
198
- })).code.extractMachines(contents)).machines[0]?.config;
199
- if (!config) throw new Error(`No machines extracted from ${filePath}`);
200
- return {
201
- kind: "local-file",
202
- locator: filePath,
203
- format: "xstate",
204
- graph: fromXStateConfig(config)
205
- };
206
- }
207
- const parsed = JSON.parse(contents);
208
- if ("rootNode" in parsed && "edges" in parsed) return {
209
- kind: "local-file",
210
- locator: filePath,
211
- format: "digraph",
212
- graph: fromStudioMachine(parsed)
213
- };
214
- if ("nodes" in parsed && "edges" in parsed) return {
215
- kind: "local-file",
216
- locator: filePath,
217
- format: "graph",
218
- graph: parsed
219
- };
220
- if ("states" in parsed) return {
221
- kind: "local-file",
222
- locator: filePath,
223
- format: "xstate",
224
- graph: fromXStateConfig(parsed)
225
- };
226
- throw new Error(`Unsupported local sync input: ${filePath}`);
227
- }
228
- function inferWritableTargetFormat(filePath) {
229
- if (filePath.endsWith(".ts") || filePath.endsWith(".tsx") || filePath.endsWith(".js") || filePath.endsWith(".jsx")) return "xstate";
230
- if (filePath.endsWith(".digraph.json")) return "digraph";
231
- if (filePath.endsWith(".graph.json")) return "graph";
232
- return null;
233
- }
234
- function serializeGraph(graph, format, options = {}) {
235
- switch (format) {
236
- case "digraph": return `${JSON.stringify(toStudioMachine(graph), null, 2)}\n`;
237
- case "graph": return `${JSON.stringify(graph, null, 2)}\n`;
238
- case "xstate": {
239
- const source = graphToXStateTS(graph);
240
- if (!options.remoteMachineId) return source;
241
- return upsertStatelyPragma(source, options.remoteMachineId, options.targetPath ? { fileName: options.targetPath } : {});
242
- }
243
- default: {
244
- const exhaustive = format;
245
- throw new Error(`Unsupported sync output format: ${exhaustive}`);
246
- }
247
- }
248
- }
249
- async function resolveRemoteMachine(machineId, baseUrl, kind, locator, options) {
250
- return {
251
- kind,
252
- locator,
253
- format: "digraph",
254
- graph: fromStudioMachine(await (options.client ?? createStatelyClient({
255
- apiKey: options.apiKey,
256
- baseUrl,
257
- fetch: options.fetch
258
- })).machines.get(machineId)),
259
- remoteMachineId: machineId
260
- };
261
- }
262
- async function resolveSyncInput(locator, options) {
263
- if (await fileExists(path.resolve(options.cwd ?? process.cwd(), locator))) return resolveLocalFile(locator, options);
264
- if (isUrl(locator)) {
265
- const url = new URL(locator);
266
- const machineId = url.pathname.split("/").filter(Boolean).at(-1);
267
- if (!machineId) throw new Error(`Could not resolve machine ID from URL: ${locator}`);
268
- return resolveRemoteMachine(machineId, url.origin, "studio-url", locator, options);
269
- }
270
- return resolveRemoteMachine(locator, options.baseUrl, "studio-machine-id", locator, options);
271
- }
272
- function summarizeDiff(diff) {
273
- const nodeChanges = diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length;
274
- const edgeChanges = diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length;
275
- return {
276
- hasChanges: !isEmptyDiff(diff),
277
- nodeChanges,
278
- edgeChanges
279
- };
280
- }
281
- async function planSync(options) {
282
- const source = await resolveSyncInput(options.source, options);
283
- const target = await resolveSyncInput(options.target, options);
284
- const diff = getDiff(source.graph, target.graph);
285
- return {
286
- source,
287
- target,
288
- diff,
289
- summary: summarizeDiff(diff),
290
- warnings: []
291
- };
292
- }
293
- async function pullSync(options) {
294
- const source = await resolveSyncInput(options.source, options);
295
- const outputPath = path.resolve(options.cwd ?? process.cwd(), options.target);
296
- const fallbackFormat = inferWritableTargetFormat(outputPath);
297
- let targetFormat = fallbackFormat;
298
- if (await fileExists(outputPath)) try {
299
- targetFormat = (await resolveLocalFile(options.target, options)).format;
300
- } catch (error) {
301
- if (!fallbackFormat) throw error;
302
- }
303
- if (!targetFormat) throw new Error(`Could not infer a writable target format from ${outputPath}. Use an existing digraph/graph file or a .digraph.json/.graph.json target.`);
304
- const serialized = serializeGraph(source.graph, targetFormat, {
305
- remoteMachineId: source.remoteMachineId,
306
- targetPath: outputPath
307
- });
308
- await fs.writeFile(outputPath, serialized, "utf8");
309
- return {
310
- source,
311
- target: {
312
- kind: "local-file",
313
- locator: outputPath,
314
- format: targetFormat,
315
- graph: source.graph
316
- },
317
- outputPath
318
- };
319
- }
320
- function inferDefaultProjectName(sourcePath, repo) {
321
- if (repo?.repo) return repo.repo;
322
- const parentDir = path.basename(path.dirname(sourcePath));
323
- if (parentDir && parentDir !== ".") return parentDir;
324
- return path.basename(sourcePath, path.extname(sourcePath));
325
- }
326
- async function resolvePushProject(client, sourcePath, options) {
327
- const explicitProject = options.project;
328
- if (explicitProject?.projectVersionId) return client.projects.get(explicitProject.projectVersionId);
329
- if (explicitProject?.projectId) return client.projects.get(explicitProject.projectId);
330
- const inferredRepo = explicitProject?.repo ?? await inferConnectedRepo(sourcePath, options.cwd);
331
- const projectInput = {
332
- name: explicitProject?.name ?? inferDefaultProjectName(sourcePath, inferredRepo),
333
- visibility: explicitProject?.visibility ?? "Private",
334
- ...explicitProject?.description ? { description: explicitProject.description } : {},
335
- ...explicitProject?.keywords ? { keywords: explicitProject.keywords } : {},
336
- ...inferredRepo ? { repo: inferredRepo } : {}
337
- };
338
- return client.projects.ensure(projectInput);
339
- }
340
- async function pushSync(options) {
341
- const client = options.client ?? createStatelyClient({
342
- apiKey: options.apiKey,
343
- baseUrl: options.baseUrl,
344
- fetch: options.fetch
345
- });
346
- const source = await resolveSyncInput(options.source, {
347
- ...options,
348
- target: options.source
349
- });
350
- if (source.kind !== "local-file") throw new Error("pushSync currently requires a local source file.");
351
- const sourcePath = source.locator;
352
- const project = await resolvePushProject(client, sourcePath, options);
353
- if (!project.projectVersionId) throw new Error("Resolved project is missing projectVersionId.");
354
- const machine = await client.machines.create({
355
- projectVersionId: project.projectVersionId,
356
- definition: toStudioMachine(source.graph),
357
- xstateVersion: options.xstateVersion ?? 5
358
- });
359
- let outputPath;
360
- if (source.format === "xstate") {
361
- const contents = await fs.readFile(sourcePath, "utf8");
362
- const nextContents = upsertStatelyPragma(contents, machine.id, { fileName: sourcePath });
363
- if (nextContents !== contents) {
364
- await fs.writeFile(sourcePath, nextContents, "utf8");
365
- outputPath = sourcePath;
366
- }
367
- }
368
- return {
369
- source,
370
- project,
371
- machine,
372
- ...outputPath ? { outputPath } : {}
373
- };
374
- }
375
-
376
- //#endregion
377
- export { planSync, pullSync, pushSync };
5
+ export { planSync, pullSync, pushLocalMachineLinks, pushSync };
@@ -1,7 +1,7 @@
1
1
  //#region src/clientUtils.ts
2
2
  const jsonResultFormats = new Set([
3
3
  "digraph",
4
- "json",
4
+ "xstate-json",
5
5
  "xgraph"
6
6
  ]);
7
7
  function createRequestId() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statelyai/sdk",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "statelyai": "./dist/cli.mjs"
@@ -54,11 +54,14 @@
54
54
  "import": "./dist/assetStorage.mjs"
55
55
  },
56
56
  "./statelyai.schema.json": "./schemas/statelyai.schema.json",
57
- "./schemas/statelyai.schema.json": "./schemas/statelyai.schema.json"
57
+ "./schemas/statelyai.schema.json": "./schemas/statelyai.schema.json",
58
+ "./xstate-json.schema.json": "./schemas/xstate-json.schema.json",
59
+ "./schemas/xstate-json.schema.json": "./schemas/xstate-json.schema.json"
58
60
  },
59
61
  "dependencies": {
60
62
  "@oclif/core": "^4.10.3",
61
63
  "@statelyai/graph": "^0.9.0",
64
+ "esbuild": "^0.27.0",
62
65
  "typescript": "^5.9.3",
63
66
  "xstate": "^5.0.0"
64
67
  },
@@ -54,7 +54,7 @@
54
54
  "enum": [
55
55
  "auto",
56
56
  "xstate",
57
- "json",
57
+ "xstate-json",
58
58
  "xgraph",
59
59
  "digraph",
60
60
  "mermaid",
@@ -0,0 +1,214 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://stately.ai/schemas/xstate-json.json",
4
+ "title": "XState JSON machine config",
5
+ "description": "Strict JSON subset of an XState machine config that can be passed to createMachine(config). Shorthand string forms are normalized to object and array forms.",
6
+ "$ref": "#/$defs/machineConfig",
7
+ "$defs": {
8
+ "jsonValue": {
9
+ "description": "Any JSON value.",
10
+ "oneOf": [
11
+ { "type": "null" },
12
+ { "type": "boolean" },
13
+ { "type": "number" },
14
+ { "type": "string" },
15
+ {
16
+ "type": "array",
17
+ "items": { "$ref": "#/$defs/jsonValue" }
18
+ },
19
+ {
20
+ "type": "object",
21
+ "additionalProperties": { "$ref": "#/$defs/jsonValue" }
22
+ }
23
+ ]
24
+ },
25
+ "jsonObject": {
26
+ "type": "object",
27
+ "additionalProperties": { "$ref": "#/$defs/jsonValue" }
28
+ },
29
+ "parameterizedObject": {
30
+ "type": "object",
31
+ "additionalProperties": false,
32
+ "required": ["type"],
33
+ "properties": {
34
+ "type": { "type": "string", "minLength": 1 },
35
+ "params": { "$ref": "#/$defs/jsonValue" }
36
+ }
37
+ },
38
+ "actionObject": {
39
+ "$ref": "#/$defs/parameterizedObject"
40
+ },
41
+ "guardObject": {
42
+ "$ref": "#/$defs/parameterizedObject"
43
+ },
44
+ "transitionObject": {
45
+ "type": "object",
46
+ "additionalProperties": false,
47
+ "properties": {
48
+ "target": {
49
+ "type": "array",
50
+ "items": { "type": "string", "minLength": 1 }
51
+ },
52
+ "guard": { "$ref": "#/$defs/guardObject" },
53
+ "actions": {
54
+ "type": "array",
55
+ "items": { "$ref": "#/$defs/actionObject" }
56
+ },
57
+ "reenter": { "type": "boolean" },
58
+ "meta": { "$ref": "#/$defs/jsonObject" },
59
+ "description": { "type": "string" }
60
+ }
61
+ },
62
+ "transitionArray": {
63
+ "type": "array",
64
+ "items": { "$ref": "#/$defs/transitionObject" }
65
+ },
66
+ "transitionsByEvent": {
67
+ "type": "object",
68
+ "additionalProperties": { "$ref": "#/$defs/transitionArray" }
69
+ },
70
+ "invokeObject": {
71
+ "type": "object",
72
+ "additionalProperties": false,
73
+ "required": ["src"],
74
+ "properties": {
75
+ "id": { "type": "string" },
76
+ "systemId": { "type": "string" },
77
+ "src": { "type": "string", "minLength": 1 },
78
+ "input": { "$ref": "#/$defs/jsonValue" },
79
+ "onDone": { "$ref": "#/$defs/transitionArray" },
80
+ "onError": { "$ref": "#/$defs/transitionArray" },
81
+ "onSnapshot": { "$ref": "#/$defs/transitionArray" }
82
+ }
83
+ },
84
+ "stateNodeConfig": {
85
+ "type": "object",
86
+ "additionalProperties": false,
87
+ "properties": {
88
+ "id": { "type": "string" },
89
+ "type": {
90
+ "enum": ["atomic", "compound", "parallel", "final", "history"]
91
+ },
92
+ "history": {
93
+ "anyOf": [
94
+ { "enum": ["shallow", "deep"] },
95
+ { "type": "boolean" }
96
+ ]
97
+ },
98
+ "states": {
99
+ "type": "object",
100
+ "additionalProperties": { "$ref": "#/$defs/stateNodeConfig" }
101
+ },
102
+ "invoke": {
103
+ "type": "array",
104
+ "items": { "$ref": "#/$defs/invokeObject" }
105
+ },
106
+ "on": { "$ref": "#/$defs/transitionsByEvent" },
107
+ "entry": {
108
+ "type": "array",
109
+ "items": { "$ref": "#/$defs/actionObject" }
110
+ },
111
+ "exit": {
112
+ "type": "array",
113
+ "items": { "$ref": "#/$defs/actionObject" }
114
+ },
115
+ "onDone": { "$ref": "#/$defs/transitionArray" },
116
+ "after": { "$ref": "#/$defs/transitionsByEvent" },
117
+ "always": { "$ref": "#/$defs/transitionArray" },
118
+ "meta": { "$ref": "#/$defs/jsonObject" },
119
+ "output": { "$ref": "#/$defs/jsonValue" },
120
+ "order": { "type": "number" },
121
+ "tags": {
122
+ "type": "array",
123
+ "items": { "type": "string" }
124
+ },
125
+ "description": { "type": "string" },
126
+ "target": { "type": "string" },
127
+ "initial": { "type": "string" }
128
+ }
129
+ },
130
+ "machineConfig": {
131
+ "type": "object",
132
+ "additionalProperties": false,
133
+ "properties": {
134
+ "id": { "type": "string" },
135
+ "type": {
136
+ "enum": ["atomic", "compound", "parallel", "final", "history"]
137
+ },
138
+ "history": {
139
+ "anyOf": [
140
+ { "enum": ["shallow", "deep"] },
141
+ { "type": "boolean" }
142
+ ]
143
+ },
144
+ "states": {
145
+ "type": "object",
146
+ "additionalProperties": { "$ref": "#/$defs/stateNodeConfig" }
147
+ },
148
+ "invoke": {
149
+ "type": "array",
150
+ "items": { "$ref": "#/$defs/invokeObject" }
151
+ },
152
+ "on": { "$ref": "#/$defs/transitionsByEvent" },
153
+ "entry": {
154
+ "type": "array",
155
+ "items": { "$ref": "#/$defs/actionObject" }
156
+ },
157
+ "exit": {
158
+ "type": "array",
159
+ "items": { "$ref": "#/$defs/actionObject" }
160
+ },
161
+ "onDone": { "$ref": "#/$defs/transitionArray" },
162
+ "after": { "$ref": "#/$defs/transitionsByEvent" },
163
+ "always": { "$ref": "#/$defs/transitionArray" },
164
+ "meta": { "$ref": "#/$defs/jsonObject" },
165
+ "output": { "$ref": "#/$defs/jsonValue" },
166
+ "order": { "type": "number" },
167
+ "tags": {
168
+ "type": "array",
169
+ "items": { "type": "string" }
170
+ },
171
+ "description": { "type": "string" },
172
+ "target": { "type": "string" },
173
+ "initial": { "type": "string" },
174
+ "schemas": { "$ref": "#/$defs/jsonValue" },
175
+ "version": { "type": "string" },
176
+ "context": { "$ref": "#/$defs/jsonObject" },
177
+ "options": {
178
+ "type": "object",
179
+ "additionalProperties": false,
180
+ "properties": {
181
+ "maxIterations": { "type": "number" }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ },
187
+ "examples": [
188
+ {
189
+ "id": "trafficLight",
190
+ "initial": "green",
191
+ "states": {
192
+ "green": {
193
+ "tags": ["go"],
194
+ "on": {
195
+ "TIMER": [
196
+ {
197
+ "target": ["yellow"],
198
+ "actions": [{ "type": "trackChange" }]
199
+ }
200
+ ]
201
+ }
202
+ },
203
+ "yellow": {
204
+ "on": {
205
+ "TIMER": [{ "target": ["red"] }]
206
+ }
207
+ },
208
+ "red": {
209
+ "type": "final"
210
+ }
211
+ }
212
+ }
213
+ ]
214
+ }