@statelyai/sdk 0.10.1 → 0.11.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/dist/sync.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createStatelyClient } from "./studio.mjs";
2
- import { d as upsertStatelyPragma, l as findStatelyPragmaAttachments, t as graphToXStateTS, u as getStatelyPragma } from "./graphToXStateTS-moihsH_U.mjs";
2
+ import { S as upsertStatelyPragma, _ as string, b as findStatelyPragmaAttachments, d as boolean, f as discriminatedUnion, g as record, h as object, l as _enum, m as number, p as literal, t as graphToXStateTS, u as array, v as union, x as getStatelyPragma, y as unknown } from "./graphToXStateTS-DV62vO_d.mjs";
3
3
  import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
4
4
  import * as ts$1 from "typescript";
5
5
  import ts from "typescript";
@@ -11,6 +11,8 @@ import { promisify } from "node:util";
11
11
  import * as fs$1 from "fs";
12
12
  import * as path$1 from "path";
13
13
  import { fileURLToPath, pathToFileURL } from "url";
14
+ import { createMachine, transition } from "xstate";
15
+ import { getShortestPaths, getSimplePaths } from "xstate/graph";
14
16
 
15
17
  //#region ../editor-sync/src/fs.ts
16
18
  function createTextDocument(fileName, text, uri = path$1.isAbsolute(fileName) ? pathToFileURL(fileName).toString() : fileName) {
@@ -107,11 +109,3095 @@ function getLineStarts(text) {
107
109
  return lineStarts;
108
110
  }
109
111
 
112
+ //#endregion
113
+ //#region ../graph-tools/dist/index.mjs
114
+ function summarizeGraphDiff(diff) {
115
+ return {
116
+ hasChanges: !isEmptyDiff(diff),
117
+ nodeChanges: diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length,
118
+ edgeChanges: diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length
119
+ };
120
+ }
121
+ function nodeDepth(graph, node) {
122
+ let depth = 0;
123
+ let parentId = node.parentId;
124
+ while (parentId) {
125
+ depth++;
126
+ parentId = graph.nodes.find((candidate) => candidate.id === parentId)?.parentId;
127
+ }
128
+ return depth;
129
+ }
130
+ function getStableNodeId(node, parentStableId, siblingIndex) {
131
+ if (node.parentId === null) return "root";
132
+ const key = node.data.key || node.id;
133
+ const suffix = siblingIndex === 0 ? key : `${key}_${siblingIndex + 1}`;
134
+ return parentStableId ? `${parentStableId}.${suffix}` : suffix;
135
+ }
136
+ function getStableEdgeId(edge, siblingIndex) {
137
+ const suffix = siblingIndex === 0 ? "" : `.${siblingIndex + 1}`;
138
+ return [
139
+ edge.sourceId,
140
+ edge.data.eventType,
141
+ edge.targetId,
142
+ edge.data.guard?.type ?? ""
143
+ ].join("->").concat(suffix);
144
+ }
145
+ function normalizeGraphForSemanticDiff(graph) {
146
+ const normalized = structuredClone(graph);
147
+ const nodeIdMap = /* @__PURE__ */ new Map();
148
+ const siblingKeys = /* @__PURE__ */ new Map();
149
+ const nodesByDepth = [...normalized.nodes].sort((a, b) => nodeDepth(normalized, a) - nodeDepth(normalized, b));
150
+ for (const node of nodesByDepth) {
151
+ const parentStableId = node.parentId ? nodeIdMap.get(node.parentId) ?? node.parentId : null;
152
+ const key = `${parentStableId ?? ""}:${node.data.key || node.id}`;
153
+ const siblingIndex = siblingKeys.get(key) ?? 0;
154
+ siblingKeys.set(key, siblingIndex + 1);
155
+ nodeIdMap.set(node.id, getStableNodeId(node, parentStableId, siblingIndex));
156
+ }
157
+ for (const node of normalized.nodes) {
158
+ node.id = nodeIdMap.get(node.id) ?? node.id;
159
+ if (node.parentId) node.parentId = nodeIdMap.get(node.parentId) ?? node.parentId;
160
+ if (node.initialNodeId) node.initialNodeId = nodeIdMap.get(node.initialNodeId) ?? node.initialNodeId;
161
+ if (node.data.initialId) node.data.initialId = nodeIdMap.get(node.data.initialId) ?? node.data.initialId;
162
+ }
163
+ const edgeSiblingKeys = /* @__PURE__ */ new Map();
164
+ for (const edge of normalized.edges) {
165
+ edge.sourceId = nodeIdMap.get(edge.sourceId) ?? edge.sourceId;
166
+ edge.targetId = nodeIdMap.get(edge.targetId) ?? edge.targetId;
167
+ const key = [
168
+ edge.sourceId,
169
+ edge.data.eventType,
170
+ edge.targetId,
171
+ edge.data.guard?.type ?? ""
172
+ ].join("->");
173
+ const siblingIndex = edgeSiblingKeys.get(key) ?? 0;
174
+ edgeSiblingKeys.set(key, siblingIndex + 1);
175
+ edge.id = getStableEdgeId(edge, siblingIndex);
176
+ }
177
+ if (normalized.initialNodeId) normalized.initialNodeId = nodeIdMap.get(normalized.initialNodeId) ?? normalized.initialNodeId;
178
+ return normalized;
179
+ }
180
+ function diffGraphsSemantically(baseGraph, compareGraph) {
181
+ const base = normalizeGraphForSemanticDiff(baseGraph);
182
+ const compare = normalizeGraphForSemanticDiff(compareGraph);
183
+ const diff = getDiff(base, compare);
184
+ return {
185
+ base,
186
+ compare,
187
+ diff,
188
+ summary: summarizeGraphDiff(diff)
189
+ };
190
+ }
191
+ function hasEntityId(graph, id) {
192
+ return graph.nodes.some((node) => node.id === id) || graph.edges.some((edge) => edge.id === id);
193
+ }
194
+ function uniqueId(graph, prefix, preferred) {
195
+ if (preferred && !hasEntityId(graph, preferred)) return preferred;
196
+ let index = 1;
197
+ let id = `${prefix}${index}`;
198
+ while (hasEntityId(graph, id)) {
199
+ index++;
200
+ id = `${prefix}${index}`;
201
+ }
202
+ return id;
203
+ }
204
+ function resolveLocation(location, resolve) {
205
+ return "nodeId" in location ? {
206
+ ...location,
207
+ nodeId: resolve(location.nodeId)
208
+ } : {
209
+ ...location,
210
+ edgeId: resolve(location.edgeId)
211
+ };
212
+ }
213
+ function getActionArray(graph, location) {
214
+ if ("nodeId" in location) {
215
+ const node = graph.nodes.find((candidate) => candidate.id === location.nodeId);
216
+ return node ? node.data[location.group] : null;
217
+ }
218
+ return graph.edges.find((candidate) => candidate.id === location.edgeId)?.data.actions ?? null;
219
+ }
220
+ function hasActionId(graph, id) {
221
+ for (const node of graph.nodes) {
222
+ if (node.data.entry.some((action) => action.id === id)) return true;
223
+ if (node.data.exit.some((action) => action.id === id)) return true;
224
+ }
225
+ for (const edge of graph.edges) if (edge.data.actions.some((action) => action.id === id)) return true;
226
+ return false;
227
+ }
228
+ function hasInvokeId(graph, id) {
229
+ return graph.nodes.some((node) => node.data.invokes.some((invoke) => invoke.id === id));
230
+ }
231
+ function createNodeInGraph(graph, opts) {
232
+ if (hasEntityId(graph, opts.id)) return;
233
+ const parentNode = graph.nodes.find((node) => node.id === opts.parentId);
234
+ if (!parentNode) return;
235
+ graph.nodes.push({
236
+ type: "node",
237
+ id: opts.id,
238
+ parentId: opts.parentId,
239
+ position: {
240
+ x: opts.x ?? 0,
241
+ y: opts.y ?? 0
242
+ },
243
+ x: opts.x ?? 0,
244
+ y: opts.y ?? 0,
245
+ width: 0,
246
+ height: 0,
247
+ dx: 0,
248
+ dy: 0,
249
+ label: opts.key,
250
+ data: {
251
+ key: opts.key,
252
+ type: opts.type ?? null,
253
+ history: false,
254
+ entry: [],
255
+ exit: [],
256
+ meta: null,
257
+ tags: [],
258
+ description: void 0,
259
+ initialId: null,
260
+ invokes: []
261
+ }
262
+ });
263
+ if (!parentNode.data.initialId) {
264
+ parentNode.data.initialId = opts.id;
265
+ parentNode.initialNodeId = opts.id;
266
+ }
267
+ }
268
+ function updateNodeInGraph(graph, nodeId, data) {
269
+ const node = graph.nodes.find((candidate) => candidate.id === nodeId);
270
+ if (!node) return;
271
+ if (data.key !== void 0) {
272
+ data.key = data.key.trim();
273
+ node.label = data.key;
274
+ }
275
+ if (data.initialId !== void 0) node.initialNodeId = data.initialId;
276
+ Object.assign(node.data, data);
277
+ }
278
+ function createEdgeInGraph(graph, opts) {
279
+ if (hasEntityId(graph, opts.id)) return;
280
+ const sourceNode = graph.nodes.find((node) => node.id === opts.sourceId);
281
+ if (!sourceNode) return;
282
+ graph.edges.push({
283
+ type: "edge",
284
+ id: opts.id,
285
+ sourceId: opts.sourceId,
286
+ targetId: opts.targetId,
287
+ label: opts.eventType,
288
+ position: {
289
+ x: sourceNode.position?.x ?? sourceNode.x ?? 0,
290
+ y: (sourceNode.position?.y ?? sourceNode.y ?? 0) + (sourceNode.height ?? 0) + 60
291
+ },
292
+ x: sourceNode.position?.x ?? sourceNode.x ?? 0,
293
+ y: (sourceNode.position?.y ?? sourceNode.y ?? 0) + (sourceNode.height ?? 0) + 60,
294
+ width: 0,
295
+ height: 0,
296
+ dx: 0,
297
+ dy: 0,
298
+ data: {
299
+ eventType: opts.eventType,
300
+ transitionType: opts.transitionType ?? "normal",
301
+ guard: null,
302
+ description: null,
303
+ actions: [],
304
+ meta: null
305
+ }
306
+ });
307
+ }
308
+ function updateEdgeInGraph(graph, edgeId, data) {
309
+ const edge = graph.edges.find((candidate) => candidate.id === edgeId);
310
+ if (!edge) return;
311
+ if (data.sourceId !== void 0) edge.sourceId = data.sourceId;
312
+ if (data.targetId !== void 0) edge.targetId = data.targetId;
313
+ if (data.eventType !== void 0) {
314
+ data.eventType = data.eventType.trim();
315
+ edge.label = data.eventType;
316
+ }
317
+ const { sourceId: _sourceId, targetId: _targetId, ...edgeData } = data;
318
+ Object.assign(edge.data, edgeData);
319
+ }
320
+ function deleteEdgeFromGraph(graph, edgeId) {
321
+ const index = graph.edges.findIndex((edge) => edge.id === edgeId);
322
+ if (index >= 0) graph.edges.splice(index, 1);
323
+ }
324
+ function deleteEntitiesFromGraph(graph, entityIds) {
325
+ const deletedNodeIds = /* @__PURE__ */ new Set();
326
+ const nodesToDelete = /* @__PURE__ */ new Set();
327
+ for (const id of entityIds) {
328
+ const node = graph.nodes.find((candidate) => candidate.id === id);
329
+ if (node && node.parentId !== null) {
330
+ nodesToDelete.add(id);
331
+ const collectDescendants = (parentId) => {
332
+ for (const child of graph.nodes) if (child.parentId === parentId && !nodesToDelete.has(child.id)) {
333
+ nodesToDelete.add(child.id);
334
+ collectDescendants(child.id);
335
+ }
336
+ };
337
+ collectDescendants(id);
338
+ }
339
+ }
340
+ for (const id of nodesToDelete) {
341
+ const index = graph.nodes.findIndex((node) => node.id === id);
342
+ if (index === -1) continue;
343
+ const nodeToDelete = graph.nodes[index];
344
+ const parentNode = graph.nodes.find((node) => node.id === nodeToDelete.parentId);
345
+ graph.nodes.splice(index, 1);
346
+ deletedNodeIds.add(id);
347
+ if (parentNode && parentNode.data.initialId === id) {
348
+ const childNodes = graph.nodes.filter((node) => node.parentId === parentNode.id);
349
+ parentNode.data.initialId = childNodes[0]?.id ?? null;
350
+ parentNode.initialNodeId = parentNode.data.initialId;
351
+ }
352
+ }
353
+ graph.edges = graph.edges.filter((edge) => !entityIds.has(edge.id) && !deletedNodeIds.has(edge.sourceId) && !deletedNodeIds.has(edge.targetId));
354
+ graph.annotations = graph.annotations.filter((annotation) => !entityIds.has(annotation.id));
355
+ }
356
+ function ensureImplementations(graph) {
357
+ graph.data.implementations ??= {
358
+ actions: [],
359
+ guards: [],
360
+ actors: [],
361
+ delays: [],
362
+ tags: []
363
+ };
364
+ return graph.data.implementations;
365
+ }
366
+ function implementationList(graph, sourceType) {
367
+ const implementations = ensureImplementations(graph);
368
+ if (sourceType === "action") return implementations.actions;
369
+ if (sourceType === "guard") return implementations.guards;
370
+ if (sourceType === "actor") return implementations.actors;
371
+ return implementations.delays;
372
+ }
373
+ function applyGraphPatches(graph, patches) {
374
+ const nextGraph = structuredClone(graph);
375
+ const idMap = /* @__PURE__ */ new Map();
376
+ const resolve = (id) => idMap.get(id) ?? id;
377
+ for (const patch of patches) switch (patch.op) {
378
+ case "createState": {
379
+ const realId = uniqueId(nextGraph, "node-", patch.id);
380
+ if (patch.id) idMap.set(patch.id, realId);
381
+ createNodeInGraph(nextGraph, {
382
+ id: realId,
383
+ parentId: resolve(patch.parentId),
384
+ key: patch.key,
385
+ x: patch.x,
386
+ y: patch.y,
387
+ type: patch.type
388
+ });
389
+ const node = nextGraph.nodes.find((candidate) => candidate.id === realId);
390
+ if (node) {
391
+ if (patch.color !== void 0) node.data.color = patch.color ?? void 0;
392
+ if (patch.initial) {
393
+ const parentNode = nextGraph.nodes.find((candidate) => candidate.id === node.parentId);
394
+ if (parentNode) {
395
+ parentNode.data.initialId = realId;
396
+ parentNode.initialNodeId = realId;
397
+ }
398
+ }
399
+ }
400
+ break;
401
+ }
402
+ case "updateState": {
403
+ const nodeData = {};
404
+ if (patch.key !== void 0) nodeData.key = patch.key;
405
+ if (patch.type !== void 0) nodeData.type = patch.type;
406
+ if (patch.stateDescription !== void 0) nodeData.description = patch.stateDescription;
407
+ if (patch.initialId !== void 0) nodeData.initialId = patch.initialId ? resolve(patch.initialId) : patch.initialId;
408
+ if (patch.color !== void 0) nodeData.color = patch.color ?? void 0;
409
+ if (patch.meta !== void 0) nodeData.meta = patch.meta;
410
+ if (patch.history !== void 0) nodeData.history = patch.history === null ? false : patch.history;
411
+ updateNodeInGraph(nextGraph, resolve(patch.stateId), nodeData);
412
+ break;
413
+ }
414
+ case "deleteState":
415
+ deleteEntitiesFromGraph(nextGraph, new Set([resolve(patch.stateId)]));
416
+ break;
417
+ case "createTransition": {
418
+ const realId = uniqueId(nextGraph, "edge-", patch.id);
419
+ if (patch.id) idMap.set(patch.id, realId);
420
+ createEdgeInGraph(nextGraph, {
421
+ id: realId,
422
+ sourceId: resolve(patch.sourceId),
423
+ targetId: resolve(patch.targetId),
424
+ eventType: patch.eventType,
425
+ transitionType: patch.transitionType
426
+ });
427
+ const edge = nextGraph.edges.find((candidate) => candidate.id === realId);
428
+ if (edge) {
429
+ if (patch.guard) edge.data.guard = patch.guard;
430
+ if (patch.color !== void 0) edge.data.color = patch.color ?? void 0;
431
+ if (patch.transitionDescription !== void 0) edge.data.description = patch.transitionDescription;
432
+ }
433
+ break;
434
+ }
435
+ case "updateTransition": {
436
+ const edgeData = {};
437
+ if (patch.sourceId !== void 0) edgeData.sourceId = resolve(patch.sourceId);
438
+ if (patch.targetId !== void 0) edgeData.targetId = resolve(patch.targetId);
439
+ if (patch.eventType !== void 0) edgeData.eventType = patch.eventType;
440
+ if (patch.transitionType !== void 0) edgeData.transitionType = patch.transitionType;
441
+ if (patch.transitionDescription !== void 0) edgeData.description = patch.transitionDescription;
442
+ if (patch.color !== void 0) edgeData.color = patch.color ?? void 0;
443
+ if (patch.meta !== void 0) edgeData.meta = patch.meta;
444
+ updateEdgeInGraph(nextGraph, resolve(patch.transitionId), edgeData);
445
+ break;
446
+ }
447
+ case "deleteTransition":
448
+ deleteEdgeFromGraph(nextGraph, resolve(patch.transitionId));
449
+ break;
450
+ case "createAction": {
451
+ const realId = patch.action.id ? uniqueId(nextGraph, "action-", patch.action.id) : `action-${Math.max(1, patches.indexOf(patch) + 1)}`;
452
+ if (patch.action.id) idMap.set(patch.action.id, realId);
453
+ if (!patch.action.type.trim()) break;
454
+ if (hasActionId(nextGraph, realId)) break;
455
+ const actions = getActionArray(nextGraph, resolveLocation(patch.location, resolve));
456
+ if (!actions) break;
457
+ const action = {
458
+ ...patch.action,
459
+ id: realId,
460
+ params: patch.action.params ?? {}
461
+ };
462
+ if (patch.index !== void 0) actions.splice(patch.index, 0, action);
463
+ else actions.push(action);
464
+ break;
465
+ }
466
+ case "updateAction": {
467
+ const action = getActionArray(nextGraph, resolveLocation(patch.location, resolve))?.find((candidate) => candidate.id === resolve(patch.actionId));
468
+ if (action) Object.assign(action, patch.data);
469
+ break;
470
+ }
471
+ case "deleteAction": {
472
+ const actions = getActionArray(nextGraph, resolveLocation(patch.location, resolve));
473
+ if (!actions) break;
474
+ const index = actions.findIndex((candidate) => candidate.id === resolve(patch.actionId));
475
+ if (index >= 0) actions.splice(index, 1);
476
+ break;
477
+ }
478
+ case "createInvoke": {
479
+ const realId = patch.invoke.id ? uniqueId(nextGraph, "invoke-", patch.invoke.id) : `invoke-${Math.max(1, patches.indexOf(patch) + 1)}`;
480
+ if (patch.invoke.id) idMap.set(patch.invoke.id, realId);
481
+ if (!patch.invoke.src.trim()) break;
482
+ if (hasInvokeId(nextGraph, realId)) break;
483
+ nextGraph.nodes.find((candidate) => candidate.id === resolve(patch.nodeId))?.data.invokes.push({
484
+ ...patch.invoke,
485
+ id: realId
486
+ });
487
+ break;
488
+ }
489
+ case "updateInvoke": {
490
+ const invoke = nextGraph.nodes.find((candidate) => candidate.id === resolve(patch.nodeId))?.data.invokes.find((candidate) => candidate.id === resolve(patch.invokeId));
491
+ if (invoke) Object.assign(invoke, patch.data);
492
+ break;
493
+ }
494
+ case "deleteInvoke": {
495
+ const node = nextGraph.nodes.find((candidate) => candidate.id === resolve(patch.nodeId));
496
+ if (!node) break;
497
+ const index = node.data.invokes.findIndex((candidate) => candidate.id === resolve(patch.invokeId));
498
+ if (index >= 0) node.data.invokes.splice(index, 1);
499
+ break;
500
+ }
501
+ case "createImplementation": {
502
+ const code = patch.implementation.code;
503
+ const base = {
504
+ id: patch.implementation.id,
505
+ name: patch.implementation.name ?? patch.implementation.id,
506
+ description: null,
507
+ code: code ? {
508
+ body: code,
509
+ lang: "ts",
510
+ deps: {}
511
+ } : null
512
+ };
513
+ implementationList(nextGraph, patch.sourceType).push(patch.sourceType === "actor" ? {
514
+ ...base,
515
+ inputSchema: null,
516
+ outputSchema: null
517
+ } : {
518
+ ...base,
519
+ paramsSchema: null
520
+ });
521
+ break;
522
+ }
523
+ case "updateImplementation": {
524
+ const item = implementationList(nextGraph, patch.sourceType).find((implementation) => implementation.id === resolve(patch.implementationId));
525
+ if (!item) break;
526
+ if (patch.data.name !== void 0) {
527
+ item.name = patch.data.name;
528
+ item.id = patch.data.name;
529
+ }
530
+ if (patch.data.code !== void 0) item.code = {
531
+ body: patch.data.code,
532
+ lang: "ts",
533
+ deps: {}
534
+ };
535
+ break;
536
+ }
537
+ case "deleteImplementation": {
538
+ const list = implementationList(nextGraph, patch.sourceType);
539
+ const index = list.findIndex((implementation) => implementation.id === resolve(patch.implementationId));
540
+ if (index >= 0) list.splice(index, 1);
541
+ break;
542
+ }
543
+ case "setGuard": {
544
+ if (!patch.guard.type.trim()) break;
545
+ const edge = nextGraph.edges.find((candidate) => candidate.id === resolve(patch.edgeId));
546
+ if (edge) edge.data.guard = patch.guard;
547
+ break;
548
+ }
549
+ case "deleteGuard": {
550
+ const edge = nextGraph.edges.find((candidate) => candidate.id === resolve(patch.edgeId));
551
+ if (edge) edge.data.guard = null;
552
+ break;
553
+ }
554
+ case "createTag": {
555
+ const node = nextGraph.nodes.find((candidate) => candidate.id === resolve(patch.nodeId));
556
+ if (node && !node.data.tags.some((tag) => tag.name === patch.tag.name)) node.data.tags.push(patch.tag);
557
+ break;
558
+ }
559
+ case "deleteTag": {
560
+ const node = nextGraph.nodes.find((candidate) => candidate.id === resolve(patch.nodeId));
561
+ if (!node) break;
562
+ const index = node.data.tags.findIndex((tag) => tag.name === patch.tagName);
563
+ if (index >= 0) node.data.tags.splice(index, 1);
564
+ break;
565
+ }
566
+ case "createAnnotation": {
567
+ const realId = uniqueId(nextGraph, "annotation-", patch.id);
568
+ if (patch.id) idMap.set(patch.id, realId);
569
+ nextGraph.annotations.push({
570
+ type: "node",
571
+ id: realId,
572
+ parentId: patch.parentId,
573
+ position: {
574
+ x: patch.x ?? 0,
575
+ y: patch.y ?? 0
576
+ },
577
+ x: patch.x ?? 0,
578
+ y: patch.y ?? 0,
579
+ dx: 0,
580
+ dy: 0,
581
+ width: 0,
582
+ height: 0,
583
+ content: patch.content,
584
+ data: { referenceIds: [] }
585
+ });
586
+ break;
587
+ }
588
+ case "updateAnnotation": {
589
+ const annotation = nextGraph.annotations.find((candidate) => candidate.id === resolve(patch.annotationId));
590
+ if (annotation && patch.data.content !== void 0) annotation.content = patch.data.content;
591
+ break;
592
+ }
593
+ case "deleteAnnotation":
594
+ deleteEntitiesFromGraph(nextGraph, new Set([resolve(patch.annotationId)]));
595
+ break;
596
+ case "setContext":
597
+ nextGraph.data.context = patch.context;
598
+ break;
599
+ case "updateSchemas":
600
+ nextGraph.data.schemas ??= {
601
+ context: null,
602
+ events: null,
603
+ input: null,
604
+ output: null
605
+ };
606
+ if (patch.schemas.context !== void 0) nextGraph.data.schemas.context = patch.schemas.context;
607
+ if (patch.schemas.events !== void 0) nextGraph.data.schemas.events = patch.schemas.events;
608
+ if (patch.schemas.input !== void 0) nextGraph.data.schemas.input = patch.schemas.input;
609
+ if (patch.schemas.output !== void 0) nextGraph.data.schemas.output = patch.schemas.output;
610
+ break;
611
+ }
612
+ return nextGraph;
613
+ }
614
+ function isAutoGeneratedId(id) {
615
+ return !!id && id.startsWith("$auto-");
616
+ }
617
+ function getAuthoredNodeId(node) {
618
+ const authored = node.data.nodeId ?? void 0;
619
+ return authored && !isAutoGeneratedId(authored) ? authored : void 0;
620
+ }
621
+ function getRootEmittedId(graph, node) {
622
+ if (graph.id === "root" && node.data.key && node.data.key !== "root") return getAuthoredNodeId(node) ?? node.data.key;
623
+ return getAuthoredNodeId(node) ?? (isAutoGeneratedId(graph.id) ? void 0 : graph.id);
624
+ }
625
+ function getDefaultPathId(graph, node) {
626
+ if (!node.parentId) return getRootEmittedId(graph, node) ?? "(machine)";
627
+ const parentNode = graph.nodes.find((candidate) => candidate.id === node.parentId);
628
+ if (!parentNode) return node.data.key;
629
+ return `${getResolvedNodeId(graph, parentNode)}.${node.data.key}`;
630
+ }
631
+ function getEmittedNodeId(graph, node) {
632
+ const authored = getAuthoredNodeId(node);
633
+ if (!node.parentId) return getRootEmittedId(graph, node);
634
+ if (!authored) return void 0;
635
+ return authored === getDefaultPathId(graph, node) ? void 0 : authored;
636
+ }
637
+ function getResolvedNodeId(graph, node) {
638
+ return getEmittedNodeId(graph, node) ?? getDefaultPathId(graph, node);
639
+ }
640
+ /**
641
+ * Serializes JavaScript values as JS source code (not JSON).
642
+ * - Unquoted keys for valid identifiers
643
+ * - Single-quoted strings
644
+ * - Omits undefined values
645
+ * - Supports RawCode for verbatim expressions
646
+ * - Supports inline expression directives for verbatim expressions
647
+ */
648
+ const VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
649
+ var RawCode = class {
650
+ constructor(code) {
651
+ this.code = code;
652
+ }
653
+ };
654
+ function serializeJS(value, indent = 0, step = 2) {
655
+ if (value instanceof RawCode) return value.code;
656
+ if (isInlineExpressionDirective(value)) return value.expr;
657
+ if (value === void 0) return "undefined";
658
+ if (value === null) return "null";
659
+ if (typeof value === "string") return `'${escapeString(value)}'`;
660
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
661
+ if (Array.isArray(value)) return serializeArray(value, indent, step);
662
+ if (typeof value === "object") return serializeObject(value, indent, step);
663
+ return String(value);
664
+ }
665
+ function escapeString(s) {
666
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
667
+ }
668
+ function serializeArray(arr, indent, step) {
669
+ const filtered = arr.filter((v) => v !== void 0);
670
+ if (filtered.length === 0) return "[]";
671
+ const inner = indent + step;
672
+ const pad = " ".repeat(inner);
673
+ const closePad = " ".repeat(indent);
674
+ return `[\n${filtered.map((v) => `${pad}${serializeJS(v, inner, step)}`).join(",\n")},\n${closePad}]`;
675
+ }
676
+ function serializeObject(obj, indent, step) {
677
+ const entries = Object.entries(obj).filter(([, v]) => v !== void 0);
678
+ if (entries.length === 0) return "{}";
679
+ const inner = indent + step;
680
+ const pad = " ".repeat(inner);
681
+ const closePad = " ".repeat(indent);
682
+ return `{\n${entries.map(([key, val]) => {
683
+ const k = VALID_IDENT.test(key) ? key : `'${escapeString(key)}'`;
684
+ const serialized = serializeJS(val, inner, step);
685
+ if (isRawExpression(val) && serialized.includes("\n")) return `${pad}${k}: ${indentRawCode(serialized, inner)}`;
686
+ return `${pad}${k}: ${serialized}`;
687
+ }).join(",\n")},\n${closePad}}`;
688
+ }
689
+ function isRawExpression(value) {
690
+ return value instanceof RawCode || isInlineExpressionDirective(value);
691
+ }
692
+ function isInlineExpressionDirective(value) {
693
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
694
+ const directive = value;
695
+ return directive["@type"] === "code" && typeof directive.expr === "string";
696
+ }
697
+ function indentRawCode(code, indent) {
698
+ const lines = code.split("\n");
699
+ if (lines.length <= 1) return code;
700
+ const pad = " ".repeat(indent);
701
+ return [lines[0], ...lines.slice(1).map((line) => line.trim() === "" ? "" : `${pad}${line}`)].join("\n");
702
+ }
703
+ /**
704
+ * Strips `export default` wrapper from an expression string.
705
+ * E.g. `"export default assign({ ... })"` → `"assign({ ... })"`
706
+ */
707
+ function stripExportDefault(code) {
708
+ const trimmed = code.trim();
709
+ if (trimmed.startsWith("export default ")) return trimmed.slice(15).replace(/;$/, "");
710
+ return trimmed;
711
+ }
712
+ /**
713
+ * Graph-native transition references. Transitions point at invoke/action/state
714
+ * blocks by graph id (`@statelyai.*`); the literal `xstate.*` event is produced
715
+ * here at codegen time. Literal `xstate.*` strings (pre-resolved, e.g. baked by
716
+ * an import) are passed through unchanged.
717
+ */
718
+ const STATELY_PREFIX = "@statelyai";
719
+ /** Legacy prefix accepted on read for graphs persisted before the rename. */
720
+ const LEGACY_STATELY_PREFIX = "@stately";
721
+ const STATELY_REF_PREFIX = {
722
+ invokeDone: "@statelyai.invoke.done",
723
+ invokeError: "@statelyai.invoke.error",
724
+ invokeSnapshot: "@statelyai.invoke.snapshot",
725
+ actionDone: "@statelyai.action.done",
726
+ actionError: "@statelyai.action.error",
727
+ stateDone: "@statelyai.state.done"
728
+ };
729
+ function parseStatelyRef(eventType) {
730
+ if (eventType.startsWith(`${LEGACY_STATELY_PREFIX}.`)) eventType = `${STATELY_PREFIX}${eventType.slice(8)}`;
731
+ if (!eventType.startsWith(`${STATELY_PREFIX}.`)) return null;
732
+ for (const kind of Object.keys(STATELY_REF_PREFIX)) {
733
+ const prefix = STATELY_REF_PREFIX[kind];
734
+ if (eventType === prefix) return {
735
+ kind,
736
+ blockId: ""
737
+ };
738
+ if (eventType.startsWith(`${prefix}.`)) return {
739
+ kind,
740
+ blockId: eventType.slice(prefix.length + 1)
741
+ };
742
+ }
743
+ return null;
744
+ }
745
+ /** Find an invoke block by its graph id, or by its emitted id (legacy literals). */
746
+ function findInvokeByActorId(node, actorId) {
747
+ const invokes = node.data.invokes ?? [];
748
+ return invokes.find((inv) => inv.id === actorId) ?? invokes.find((inv) => (inv.invocationId ?? inv.id) === actorId);
749
+ }
750
+ /** Resolve a spawn action block id to its spawned actor id (`params.id`). */
751
+ function resolveSpawnActorId$1(node, actionId) {
752
+ const spawnId = [...node.data.entry ?? [], ...node.data.exit ?? []].find((action) => action.id === actionId)?.params?.["id"];
753
+ return typeof spawnId === "string" && spawnId ? spawnId : actionId;
754
+ }
755
+ /**
756
+ * Resolve an edge's target into the string XState expects on the source state.
757
+ * Prefer a relative key path (resolved against the source's parent, or the root
758
+ * itself for root transitions) so output reads like a hand-written machine —
759
+ * e.g. a sibling's child becomes `Unlocked.Manual`. Fall back to an absolute
760
+ * `#id` reference only when the target lives outside the source's subtree.
761
+ */
762
+ function resolveTransitionTarget(graph, sourceNode, targetNode) {
763
+ const isRootSource = sourceNode.parentId == null;
764
+ const baseId = sourceNode.parentId ?? sourceNode.id;
765
+ const keys = [];
766
+ let current = targetNode;
767
+ while (current) {
768
+ keys.unshift(current.data.key);
769
+ if (current.parentId === baseId) {
770
+ if (isRootSource) return `.${keys.join(".")}`;
771
+ return keys.join(".");
772
+ }
773
+ const parentId = current.parentId;
774
+ current = graph.nodes.find((n) => n.id === parentId);
775
+ }
776
+ return `#${getAbsoluteTargetId(graph, targetNode)}`;
777
+ }
778
+ function getAbsoluteTargetId(graph, targetNode) {
779
+ const resolved = getResolvedNodeId(graph, targetNode);
780
+ if (!resolved.startsWith("root.")) return resolved;
781
+ const rootNode = graph.nodes.find((node) => node.parentId === null);
782
+ const rootKey = rootNode?.data.nodeId && !isAutoGeneratedId(rootNode.data.nodeId) ? rootNode.data.nodeId : rootNode?.data.key;
783
+ return rootKey && rootKey !== "root" ? `${rootKey}.${resolved.slice(5)}` : resolved;
784
+ }
785
+ /** Unwrap single-element arrays to a single value (XState single-or-array convention) */
786
+ function singleOrArray(arr) {
787
+ return arr.length === 1 ? arr[0] : arr;
788
+ }
789
+ /** Unwrap each value in a record of arrays */
790
+ function singleOrArrayRecord(record) {
791
+ const result = {};
792
+ for (const [key, arr] of Object.entries(record)) result[key] = singleOrArray(arr);
793
+ return result;
794
+ }
795
+ /**
796
+ * Remove empty arrays/objects/strings, unwrap single-element arrays.
797
+ * Matches studio's simplifyAttributes behavior.
798
+ */
799
+ function simplifyAttributes(obj) {
800
+ for (const key of Object.keys(obj)) {
801
+ const value = obj[key];
802
+ if (value === void 0 || value === null) delete obj[key];
803
+ else if (Array.isArray(value)) {
804
+ if (value.length === 0) delete obj[key];
805
+ else if (value.length === 1) obj[key] = value[0];
806
+ } else if (typeof value === "object" && Object.keys(value).length === 0) delete obj[key];
807
+ else if (typeof value === "string" && value === "") delete obj[key];
808
+ }
809
+ return obj;
810
+ }
811
+ /**
812
+ * Recursively apply simplifyAttributes to a config tree.
813
+ * Cleans up the config and all nested state nodes, transitions, and invokes.
814
+ */
815
+ function deepSimplify(config) {
816
+ for (const mapKey of ["on", "after"]) {
817
+ const map = config[mapKey];
818
+ if (map && typeof map === "object" && !Array.isArray(map)) {
819
+ for (const [, val] of Object.entries(map)) if (val && typeof val === "object" && !Array.isArray(val)) simplifyAttributes(val);
820
+ else if (Array.isArray(val)) {
821
+ for (const item of val) if (item && typeof item === "object") simplifyAttributes(item);
822
+ }
823
+ }
824
+ }
825
+ for (const key of ["always", "onDone"]) {
826
+ const val = config[key];
827
+ if (val && typeof val === "object" && !Array.isArray(val)) simplifyAttributes(val);
828
+ else if (Array.isArray(val)) {
829
+ for (const item of val) if (item && typeof item === "object") simplifyAttributes(item);
830
+ }
831
+ }
832
+ const invoke = config.invoke;
833
+ if (invoke && typeof invoke === "object" && !Array.isArray(invoke)) simplifyAttributes(invoke);
834
+ else if (Array.isArray(invoke)) {
835
+ for (const item of invoke) if (item && typeof item === "object") simplifyAttributes(item);
836
+ }
837
+ simplifyAttributes(config);
838
+ if (config.states && typeof config.states === "object") for (const stateConfig of Object.values(config.states)) deepSimplify(stateConfig);
839
+ return config;
840
+ }
841
+ function buildMeta(meta, color) {
842
+ if (isCodeExpression(meta)) return meta;
843
+ const result = {};
844
+ if (meta) {
845
+ for (const [k, v] of Object.entries(meta)) if (v !== void 0 && v !== null) result[k] = v;
846
+ }
847
+ if (color) result["@statelyai.color"] = color;
848
+ return Object.keys(result).length > 0 ? result : void 0;
849
+ }
850
+ function isCodeExpression(value) {
851
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && value["@type"] === "code" && typeof value.expr === "string");
852
+ }
853
+ /** Format event param — supports { type: string } (new) and plain string (legacy) */
854
+ function formatEventParam(event) {
855
+ if (event && typeof event === "object" && "type" in event) return formatInlineObject(event);
856
+ if (typeof event === "string") return `{ type: '${event}' }`;
857
+ return "undefined";
858
+ }
859
+ function formatStringParam(value) {
860
+ return typeof value === "string" ? `'${value}'` : "undefined";
861
+ }
862
+ function formatInlineObject(value) {
863
+ return `{ ${Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => `${formatObjectKey(key)}: ${serializeJS(item)}`).join(", ")} }`;
864
+ }
865
+ function formatObjectKey(key) {
866
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
867
+ }
868
+ const eventParamSchema = union([string(), object({ type: string() }).catchall(unknown())]);
869
+ const delayParamSchema = union([number(), string()]);
870
+ const builtInActionParamSchemas = {
871
+ "xstate.raise": object({
872
+ event: eventParamSchema.optional(),
873
+ id: string().optional(),
874
+ delay: delayParamSchema.optional()
875
+ }),
876
+ "xstate.sendTo": object({
877
+ to: string().optional(),
878
+ event: eventParamSchema.optional(),
879
+ id: string().optional(),
880
+ delay: delayParamSchema.optional()
881
+ }),
882
+ "xstate.cancel": object({ sendId: string().optional() }),
883
+ "xstate.emit": object({ event: eventParamSchema.optional() }),
884
+ "xstate.spawnChild": object({
885
+ src: string().optional(),
886
+ id: string().optional(),
887
+ systemId: string().optional()
888
+ }),
889
+ "xstate.stopChild": object({ actorRef: string().optional() }),
890
+ "xstate.log": object({ label: string().optional() }),
891
+ "xstate.assign": object({ assignment: record(string(), unknown()).optional() }).catchall(unknown())
892
+ };
893
+ const XSTATE_BUILT_IN_ACTIONS = {
894
+ "xstate.raise": {
895
+ package: "xstate",
896
+ version: "^5",
897
+ import: "raise",
898
+ importKind: "named",
899
+ paramsSchema: builtInActionParamSchemas["xstate.raise"],
900
+ emit(params) {
901
+ const eventStr = formatEventParam(params?.event);
902
+ const opts = [];
903
+ if (params?.id) opts.push(`id: '${params.id}'`);
904
+ if (params?.delay != null) opts.push(`delay: ${JSON.stringify(params.delay)}`);
905
+ return `raise(${eventStr}${opts.length > 0 ? `, { ${opts.join(", ")} }` : ""})`;
906
+ }
907
+ },
908
+ "xstate.sendTo": {
909
+ package: "xstate",
910
+ version: "^5",
911
+ import: "sendTo",
912
+ importKind: "named",
913
+ paramsSchema: builtInActionParamSchemas["xstate.sendTo"],
914
+ emit(params) {
915
+ const to = formatStringParam(params?.to);
916
+ const eventStr = formatEventParam(params?.event);
917
+ const opts = [];
918
+ if (params?.id) opts.push(`id: '${params.id}'`);
919
+ if (params?.delay != null) opts.push(`delay: ${JSON.stringify(params.delay)}`);
920
+ return `sendTo(${to}, ${eventStr}${opts.length > 0 ? `, { ${opts.join(", ")} }` : ""})`;
921
+ }
922
+ },
923
+ "xstate.cancel": {
924
+ package: "xstate",
925
+ version: "^5",
926
+ import: "cancel",
927
+ importKind: "named",
928
+ paramsSchema: builtInActionParamSchemas["xstate.cancel"],
929
+ emit(params) {
930
+ return `cancel(${formatStringParam(params?.sendId)})`;
931
+ }
932
+ },
933
+ "xstate.emit": {
934
+ package: "xstate",
935
+ version: "^5",
936
+ import: "emit",
937
+ importKind: "named",
938
+ paramsSchema: builtInActionParamSchemas["xstate.emit"],
939
+ emit(params) {
940
+ return `emit(${formatEventParam(params?.event)})`;
941
+ }
942
+ },
943
+ "xstate.spawnChild": {
944
+ package: "xstate",
945
+ version: "^5",
946
+ import: "spawnChild",
947
+ importKind: "named",
948
+ paramsSchema: builtInActionParamSchemas["xstate.spawnChild"],
949
+ emit(params) {
950
+ const src = formatStringParam(params?.src);
951
+ const opts = [];
952
+ if (params?.id) opts.push(`id: '${params.id}'`);
953
+ if (params?.systemId) opts.push(`systemId: '${params.systemId}'`);
954
+ return `spawnChild(${src}${opts.length > 0 ? `, { ${opts.join(", ")} }` : ""})`;
955
+ }
956
+ },
957
+ "xstate.stopChild": {
958
+ package: "xstate",
959
+ version: "^5",
960
+ import: "stopChild",
961
+ importKind: "named",
962
+ paramsSchema: builtInActionParamSchemas["xstate.stopChild"],
963
+ emit(params) {
964
+ return `stopChild(${formatStringParam(params?.actorRef)})`;
965
+ }
966
+ },
967
+ "xstate.log": {
968
+ package: "xstate",
969
+ version: "^5",
970
+ import: "log",
971
+ importKind: "named",
972
+ paramsSchema: builtInActionParamSchemas["xstate.log"],
973
+ emit(params) {
974
+ return `log(${(params?.label ? `'${params.label}'` : void 0) ?? ""})`;
975
+ }
976
+ },
977
+ "xstate.assign": {
978
+ package: "xstate",
979
+ version: "^5",
980
+ import: "assign",
981
+ importKind: "named",
982
+ paramsSchema: builtInActionParamSchemas["xstate.assign"],
983
+ emit(params) {
984
+ const assignments = params?.assignment && typeof params.assignment === "object" && !Array.isArray(params.assignment) ? params.assignment : params;
985
+ const entries = Object.entries(assignments ?? {}).filter(([k, v]) => k && v !== void 0);
986
+ if (entries.length === 0) return "assign({})";
987
+ return `assign({ ${entries.map(([k, v]) => `${JSON.stringify(k)}: ${formatAssignValue(v)}`).join(", ")} })`;
988
+ }
989
+ }
990
+ };
991
+ function isBuiltInActionType(type) {
992
+ return type in XSTATE_BUILT_IN_ACTIONS;
993
+ }
994
+ function emitBuiltInAction(type, params) {
995
+ return XSTATE_BUILT_IN_ACTIONS[type].emit(params);
996
+ }
997
+ function actionCodeExpression(expr, source) {
998
+ return {
999
+ "@type": "code",
1000
+ lang: "ts",
1001
+ expr,
1002
+ imports: [{
1003
+ package: source.package,
1004
+ version: source.version,
1005
+ import: source.import,
1006
+ importKind: source.importKind
1007
+ }]
1008
+ };
1009
+ }
1010
+ function inlineCodeExpression$1(expr) {
1011
+ return {
1012
+ "@type": "code",
1013
+ lang: "ts",
1014
+ expr: stripExportDefault(expr)
1015
+ };
1016
+ }
1017
+ /**
1018
+ * Serialize an edge guard for a machine config. Codegen emits inline guards as
1019
+ * a code-expression directive so the source round-trips. The simulation passes
1020
+ * `asTypeRef` to get a plain `{ type }` reference instead (it stubs guards by
1021
+ * type and can't evaluate the source) — see MachineConfigOptions.
1022
+ */
1023
+ function serializeGuard(guard, asTypeRef) {
1024
+ if (asTypeRef) return guard.params ? {
1025
+ type: guard.type,
1026
+ params: guard.params
1027
+ } : { type: guard.type };
1028
+ return guard.code ? inlineCodeExpression$1(guard.code) : guard;
1029
+ }
1030
+ /**
1031
+ * Converts a built-in xstate ActionItem to a code expression directive,
1032
+ * or returns a plain { type, params } object for user-defined actions.
1033
+ */
1034
+ function serializeActionItem(action) {
1035
+ const { type, params } = action;
1036
+ const exprCode = type === "xstate.expr" && typeof params?.code === "string" ? params.code : void 0;
1037
+ if (isBuiltInActionType(type)) {
1038
+ const builtInAction = XSTATE_BUILT_IN_ACTIONS[type];
1039
+ return actionCodeExpression(emitBuiltInAction(type, params), builtInAction);
1040
+ }
1041
+ if (exprCode) return inlineCodeExpression$1(exprCode);
1042
+ if (!params || Object.keys(params).length === 0) return { type };
1043
+ return {
1044
+ type,
1045
+ params
1046
+ };
1047
+ }
1048
+ function formatAssignValue(value) {
1049
+ if (typeof value !== "string") return JSON.stringify(value);
1050
+ const templateExpression = value.match(/^\{\{([\s\S]*)\}\}$/);
1051
+ if (templateExpression) return templateExpression[1].trim();
1052
+ return JSON.stringify(value);
1053
+ }
1054
+ /** Normalize tag to string — handles both `string` and `{ name: string }` */
1055
+ function tagToString(tag) {
1056
+ return typeof tag === "string" ? tag : tag.name;
1057
+ }
1058
+ function graphToMachineConfig(graph, options = {}) {
1059
+ const { showDescriptions = true, showMeta = true, inlineGuardsAsTypeRef = false } = options;
1060
+ const config = {};
1061
+ function getNodeConfig(node) {
1062
+ const nodeConfig = node.parentId ? {} : config;
1063
+ const resolvedNodeId = getEmittedNodeId(graph, node);
1064
+ const initialNode = node.data.initialId ? graph.nodes.find((n) => n.id === node.data.initialId) : null;
1065
+ const nodeType = [
1066
+ "final",
1067
+ "history",
1068
+ "parallel"
1069
+ ].includes(node.data.type) ? node.data.type : void 0;
1070
+ const tags = node.data.tags;
1071
+ Object.assign(nodeConfig, {
1072
+ id: resolvedNodeId,
1073
+ type: nodeType === "normal" ? void 0 : nodeType ?? void 0,
1074
+ initial: initialNode?.data.key,
1075
+ ...node.data.entry?.length ? { entry: node.data.entry.map(serializeActionItem) } : void 0,
1076
+ ...node.data.exit?.length ? { exit: node.data.exit.map(serializeActionItem) } : void 0,
1077
+ ...node.data.invokes?.length ? { invoke: node.data.invokes.map((inv) => ({
1078
+ src: inv.src,
1079
+ id: inv.invocationId ?? inv.id,
1080
+ ...inv.input ? { input: inv.input } : void 0,
1081
+ ...inv.output ? { output: inv.output } : void 0
1082
+ })) } : void 0,
1083
+ ...tags?.length ? { tags: tags.map(tagToString) } : void 0,
1084
+ ...showDescriptions && node.data.description ? { description: node.data.description } : void 0,
1085
+ ...showMeta && node.data.meta && Object.keys(node.data.meta).length > 0 ? { meta: node.data.meta } : void 0,
1086
+ ...node.data.history ? { history: node.data.history } : void 0
1087
+ });
1088
+ const childNodes = graph.nodes.filter((n) => n.parentId === node.id && !n.data.temp);
1089
+ const edges = graph.edges.filter((edge) => edge.sourceId === node.id && !edge.data.temp);
1090
+ if (edges.length > 0) {
1091
+ const on = {};
1092
+ const after = {};
1093
+ const alwaysArr = [];
1094
+ const onDone = {};
1095
+ const invokeMap = {};
1096
+ for (const edge of edges) {
1097
+ const targetNode = graph.nodes.find((n) => n.id === edge.targetId);
1098
+ const resolvedTarget = targetNode ? resolveTransitionTarget(graph, node, targetNode) : "";
1099
+ const type = edge.data.eventType ?? "";
1100
+ const transitionMeta = showMeta ? buildMeta(edge.data.meta, edge.data.color) : void 0;
1101
+ const transitionObject = {
1102
+ target: edge.data.transitionType === "targetless" ? void 0 : `${resolvedTarget}`,
1103
+ ...edge.data.transitionType === "reenter" ? { reenter: true } : void 0,
1104
+ guard: edge.data.guard ? serializeGuard(edge.data.guard, inlineGuardsAsTypeRef) : void 0,
1105
+ actions: edge.data.actions?.length ? edge.data.actions.map(serializeActionItem) : void 0,
1106
+ description: edge.data.description ?? void 0,
1107
+ ...transitionMeta ? { meta: transitionMeta } : void 0
1108
+ };
1109
+ const pushOn = (key) => {
1110
+ if (!on[key]) on[key] = [];
1111
+ on[key].push(transitionObject);
1112
+ };
1113
+ const nestInvoke = (blockId, slot) => {
1114
+ invokeMap[blockId] = {
1115
+ ...invokeMap[blockId],
1116
+ [slot]: (invokeMap[blockId]?.[slot] ?? []).concat(transitionObject)
1117
+ };
1118
+ };
1119
+ const ref = parseStatelyRef(type);
1120
+ if (ref) if (ref.kind === "invokeDone") nestInvoke(ref.blockId, "onDone");
1121
+ else if (ref.kind === "invokeError") nestInvoke(ref.blockId, "onError");
1122
+ else if (ref.kind === "invokeSnapshot") nestInvoke(ref.blockId, "onSnapshot");
1123
+ else if (ref.kind === "stateDone") {
1124
+ if (!onDone[type]) onDone[type] = [];
1125
+ onDone[type].push(transitionObject);
1126
+ } else pushOn(`xstate.${ref.kind === "actionDone" ? "done" : "error"}.actor.${resolveSpawnActorId$1(node, ref.blockId)}`);
1127
+ else if (type === "") alwaysArr.push(transitionObject);
1128
+ else if (type.startsWith("xstate.after.")) {
1129
+ const delayKey = type.slice(13).split(".")[0];
1130
+ if (!after[delayKey]) after[delayKey] = [];
1131
+ after[delayKey].push(transitionObject);
1132
+ } else if (type === "*") pushOn("*");
1133
+ else if (type.startsWith("xstate.done.state")) {
1134
+ if (!onDone[type]) onDone[type] = [];
1135
+ onDone[type].push(transitionObject);
1136
+ } else if (type.startsWith("xstate.done.actor.") || type.startsWith("xstate.error.actor.") || type.startsWith("xstate.snapshot.actor.")) {
1137
+ const verb = type.startsWith("xstate.done.actor.") ? "onDone" : type.startsWith("xstate.error.actor.") ? "onError" : "onSnapshot";
1138
+ const invoke = findInvokeByActorId(node, type.slice(type.lastIndexOf(".actor.") + 7));
1139
+ if (invoke) nestInvoke(invoke.id, verb);
1140
+ else pushOn(type);
1141
+ } else pushOn(type);
1142
+ }
1143
+ if (Object.keys(on).length > 0) nodeConfig.on = singleOrArrayRecord(on);
1144
+ if (Object.keys(after).length > 0) nodeConfig.after = singleOrArrayRecord(after);
1145
+ if (alwaysArr.length > 0) nodeConfig.always = singleOrArray(alwaysArr);
1146
+ if (Object.keys(onDone).length > 0) nodeConfig.onDone = singleOrArray(Object.values(onDone).flat());
1147
+ if (Object.keys(invokeMap).length > 0 && Array.isArray(nodeConfig.invoke)) nodeConfig.invoke = nodeConfig.invoke.map((inv, index) => {
1148
+ const mapped = invokeMap[node.data.invokes?.[index]?.id ?? inv.id];
1149
+ if (!mapped) return inv;
1150
+ return {
1151
+ ...inv,
1152
+ ...mapped.onDone ? { onDone: singleOrArray(mapped.onDone) } : void 0,
1153
+ ...mapped.onError ? { onError: singleOrArray(mapped.onError) } : void 0,
1154
+ ...mapped.onSnapshot ? { onSnapshot: singleOrArray(mapped.onSnapshot) } : void 0
1155
+ };
1156
+ });
1157
+ }
1158
+ if (Array.isArray(nodeConfig.invoke)) nodeConfig.invoke = nodeConfig.invoke.map((inv) => {
1159
+ if (inv.id && !isAutoGeneratedId(inv.id)) return inv;
1160
+ const { id: _id, ...rest } = inv;
1161
+ return rest;
1162
+ });
1163
+ if (childNodes.length > 0) {
1164
+ nodeConfig.states = {};
1165
+ for (const childState of childNodes) nodeConfig.states[childState.data.key] = getNodeConfig(childState);
1166
+ }
1167
+ return nodeConfig;
1168
+ }
1169
+ const rootNode = graph.nodes.find((node) => node.data.parentId === null || node.parentId == null);
1170
+ if (!rootNode) throw new Error("No root node found");
1171
+ getNodeConfig(rootNode);
1172
+ if (graph.data.context !== void 0 && graph.data.context !== null) config.context = graph.data.context;
1173
+ return deepSimplify(config);
1174
+ }
1175
+ const REF_PREFIX = {
1176
+ invokeDone: "@statelyai.invoke.done",
1177
+ invokeError: "@statelyai.invoke.error",
1178
+ invokeSnapshot: "@statelyai.invoke.snapshot",
1179
+ actionDone: "@statelyai.action.done",
1180
+ actionError: "@statelyai.action.error",
1181
+ stateDone: "@statelyai.state.done"
1182
+ };
1183
+ function parseRef(eventType) {
1184
+ if (eventType.startsWith("@stately.")) eventType = `@statelyai${eventType.slice(8)}`;
1185
+ if (!eventType.startsWith("@statelyai.")) return null;
1186
+ for (const kind of Object.keys(REF_PREFIX)) {
1187
+ const prefix = REF_PREFIX[kind];
1188
+ if (eventType === prefix) return {
1189
+ kind,
1190
+ blockId: ""
1191
+ };
1192
+ if (eventType.startsWith(`${prefix}.`)) return {
1193
+ kind,
1194
+ blockId: eventType.slice(prefix.length + 1)
1195
+ };
1196
+ }
1197
+ return null;
1198
+ }
1199
+ function getSourceNode(graph, edge) {
1200
+ return graph.nodes.find((node) => node.id === edge.sourceId);
1201
+ }
1202
+ function resolveSpawnActorId(sourceNode, actionId) {
1203
+ const actorId = [...sourceNode?.data.entry ?? [], ...sourceNode?.data.exit ?? []].find((action) => action.id === actionId)?.params?.["id"];
1204
+ return typeof actorId === "string" && actorId ? actorId : actionId;
1205
+ }
1206
+ function getTransitionRuntimeEventType(graph, edge) {
1207
+ const eventType = edge.data.eventType;
1208
+ const sourceNode = getSourceNode(graph, edge);
1209
+ if (eventType.startsWith("xstate.after.")) {
1210
+ const delayKey = eventType.slice(13).split(".")[0];
1211
+ return sourceNode ? `xstate.after.${delayKey}.${getResolvedNodeId(graph, sourceNode)}` : eventType;
1212
+ }
1213
+ const ref = parseRef(eventType);
1214
+ if (!ref) return eventType;
1215
+ if (ref.kind === "stateDone") {
1216
+ const node = graph.nodes.find((candidate) => candidate.id === ref.blockId) ?? sourceNode;
1217
+ return `xstate.done.state.${node ? getResolvedNodeId(graph, node) : ref.blockId}`;
1218
+ }
1219
+ if (ref.kind === "invokeDone" || ref.kind === "invokeError" || ref.kind === "invokeSnapshot") {
1220
+ const invokes = sourceNode?.data.invokes ?? [];
1221
+ const index = invokes.findIndex((invoke) => invoke.id === ref.blockId);
1222
+ const actorId = (index >= 0 ? invokes[index] : void 0)?.invocationId || (sourceNode && index >= 0 ? `${index}.${getResolvedNodeId(graph, sourceNode)}` : ref.blockId);
1223
+ return `xstate.${ref.kind === "invokeDone" ? "done" : ref.kind === "invokeError" ? "error" : "snapshot"}.actor.${actorId}`;
1224
+ }
1225
+ return `xstate.${ref.kind === "actionDone" ? "done" : "error"}.actor.${resolveSpawnActorId(sourceNode, ref.blockId)}`;
1226
+ }
1227
+ function transitionMatchesRuntimeEvent(graph, edge, eventType) {
1228
+ return getTransitionRuntimeEventType(graph, edge) === eventType;
1229
+ }
1230
+ /**
1231
+ * Map an XState state node (identified by its array of keys from the root) to
1232
+ * the corresponding graph node id. The runtime snapshot's `_nodes[].id` are
1233
+ * XState resolved ids (delimited paths), which do not match the graph's opaque
1234
+ * node ids; consumers (highlighting, edge lookup) need graph node ids.
1235
+ */
1236
+ function getGraphNodeIdForPath(graph, path) {
1237
+ const root = graph.nodes.find((n) => n.parentId === null);
1238
+ if (!root) return null;
1239
+ if (path.length === 0) return root.id;
1240
+ let parentId = root.id;
1241
+ let nodeId = null;
1242
+ for (const key of path) {
1243
+ const child = graph.nodes.find((n) => n.parentId === parentId && n.data.key === key);
1244
+ if (!child) return null;
1245
+ nodeId = child.id;
1246
+ parentId = child.id;
1247
+ }
1248
+ return nodeId;
1249
+ }
1250
+ function getStateIds(graph, snapshot) {
1251
+ const nodes = snapshot._nodes;
1252
+ if (!nodes) return [];
1253
+ return nodes.map((n) => getGraphNodeIdForPath(graph, n.path)).filter((id) => id !== null);
1254
+ }
1255
+ function getLeafStateIds(graph, snapshot) {
1256
+ const nodes = snapshot._nodes;
1257
+ if (!nodes) return [];
1258
+ const leaves = nodes.filter((n) => n.path.length > 0 && (n.type === "atomic" || n.type === "final"));
1259
+ return (leaves.length > 0 ? leaves : nodes).map((n) => getGraphNodeIdForPath(graph, n.path)).filter((id) => id !== null);
1260
+ }
1261
+ function getStatePath(snapshot) {
1262
+ const nodes = snapshot._nodes;
1263
+ if (!nodes?.length) return "(initial)";
1264
+ const withPath = nodes.filter((n) => n.path.length > 0);
1265
+ if (!withPath.length) return "(initial)";
1266
+ const leaves = withPath.filter((n) => n.type === "atomic" || n.type === "final");
1267
+ return (leaves.length > 0 ? leaves : withPath).map((n) => n.path.join(".")).join(", ");
1268
+ }
1269
+ function findEdgeId(graph, sourceStateIds, event) {
1270
+ const guardType = event["@xstate.guard"];
1271
+ for (const sourceId of sourceStateIds) {
1272
+ const edge = graph.edges.find((candidate) => candidate.sourceId === sourceId && transitionMatchesRuntimeEvent(graph, candidate, event.type) && (candidate.data.guard?.type ?? null) === (guardType ?? null));
1273
+ if (edge) return edge.id;
1274
+ }
1275
+ for (const sourceId of sourceStateIds) {
1276
+ const edge = graph.edges.find((candidate) => candidate.sourceId === sourceId && transitionMatchesRuntimeEvent(graph, candidate, event.type));
1277
+ if (edge) return edge.id;
1278
+ }
1279
+ return null;
1280
+ }
1281
+ function formatEventLabel(event) {
1282
+ if (event.type === "") return "always";
1283
+ if (event.type.startsWith("xstate.after.")) return `after ${event.type.slice(13).split(".")[0]}`;
1284
+ const guardType = event["@xstate.guard"];
1285
+ if (guardType) return `${event.type} [${guardType}]`;
1286
+ return event.type;
1287
+ }
1288
+ function formatEdgeLabel(graph, edgeId) {
1289
+ const edge = graph.edges.find((candidate) => candidate.id === edgeId);
1290
+ if (!edge) return null;
1291
+ const baseLabel = formatEventLabel({ type: edge.data.eventType });
1292
+ const guardType = edge.data.guard?.type;
1293
+ if (guardType) return `${baseLabel} [${guardType}]`;
1294
+ return baseLabel;
1295
+ }
1296
+ function getFallbackTransitionLabels(graph, finalStateIds) {
1297
+ return getFallbackEventlessEdges(graph, finalStateIds).map((edge) => formatEdgeLabel(graph, edge.id)).filter((label) => !!label);
1298
+ }
1299
+ function getFallbackEventlessEdges(graph, finalStateIds) {
1300
+ const finalLeafIdSet = new Set(finalStateIds);
1301
+ return graph.edges.filter((edge) => finalLeafIdSet.has(edge.targetId) && edge.data.eventType === "");
1302
+ }
1303
+ function getPathName(finalStatePath, finalStateIds, steps, graph) {
1304
+ let transitionLabels = steps.map((step) => {
1305
+ if (step.edgeId) return formatEdgeLabel(graph, step.edgeId);
1306
+ if (step.eventType && step.eventType !== "xstate.init") return formatEventLabel({ type: step.eventType });
1307
+ return null;
1308
+ }).filter((label) => !!label);
1309
+ if (transitionLabels.length === 0) transitionLabels = getFallbackTransitionLabels(graph, finalStateIds);
1310
+ if (transitionLabels.length === 0) return {
1311
+ name: finalStatePath,
1312
+ viaLabel: null
1313
+ };
1314
+ return {
1315
+ name: finalStatePath,
1316
+ viaLabel: `via ${transitionLabels.join(" -> ")}`
1317
+ };
1318
+ }
1319
+ function convertPath(path, graph, index) {
1320
+ const steps = [];
1321
+ const firstStep = path.steps[0];
1322
+ if (firstStep) {
1323
+ const allIds = getStateIds(graph, firstStep.state);
1324
+ steps.push({
1325
+ stateIds: getLeafStateIds(graph, firstStep.state),
1326
+ statePath: getStatePath(firstStep.state),
1327
+ eventType: firstStep.event.type,
1328
+ edgeId: findEdgeId(graph, allIds, firstStep.event)
1329
+ });
1330
+ }
1331
+ for (let i = 1; i < path.steps.length; i++) {
1332
+ const prevStep = path.steps[i - 1];
1333
+ const step = path.steps[i];
1334
+ const prevStateIds = getStateIds(graph, prevStep.state);
1335
+ steps.push({
1336
+ stateIds: getLeafStateIds(graph, step.state),
1337
+ statePath: getStatePath(step.state),
1338
+ eventType: step.event.type,
1339
+ edgeId: findEdgeId(graph, prevStateIds, step.event)
1340
+ });
1341
+ }
1342
+ const finalStatePath = getStatePath(path.state);
1343
+ const finalStateIds = getLeafStateIds(graph, path.state);
1344
+ const title = getPathName(finalStatePath, finalStateIds, steps, graph);
1345
+ return {
1346
+ id: `path-${index}`,
1347
+ name: title.name,
1348
+ viaLabel: title.viaLabel,
1349
+ steps,
1350
+ finalStatePath,
1351
+ finalStateIds
1352
+ };
1353
+ }
1354
+ function serializeMachineTransition(_state, event, prevState) {
1355
+ if (!event) return "";
1356
+ const prevStateString = prevState ? ` from ${JSON.stringify(prevState.value)}` : "";
1357
+ return ` via ${JSON.stringify(event)}${prevStateString}`;
1358
+ }
1359
+ function getGraphPathSimImplementations(graph, guardState) {
1360
+ const guards = {};
1361
+ for (const edge of graph.edges) if (edge.data.guard) {
1362
+ const guardType = edge.data.guard.type;
1363
+ if (guardState) guards[guardType] = () => guardState[guardType] ?? false;
1364
+ else guards[guardType] = ({ event }) => {
1365
+ if (event["@xstate.guard"] === guardType) return true;
1366
+ return false;
1367
+ };
1368
+ }
1369
+ return { guards };
1370
+ }
1371
+ function getTraversalEvents(state, graph) {
1372
+ const events = [];
1373
+ for (const edge of graph.edges) {
1374
+ const eventType = getTransitionRuntimeEventType(graph, edge);
1375
+ const event = { type: eventType };
1376
+ if (edge.data.guard?.type) event["@xstate.guard"] = edge.data.guard.type;
1377
+ if (eventType && !state.can(event)) continue;
1378
+ events.push(event);
1379
+ }
1380
+ const seen = /* @__PURE__ */ new Set();
1381
+ return events.filter((event) => {
1382
+ const key = JSON.stringify(event);
1383
+ if (seen.has(key)) return false;
1384
+ seen.add(key);
1385
+ return true;
1386
+ });
1387
+ }
1388
+ function getStepKey(step) {
1389
+ return JSON.stringify({
1390
+ stateIds: step.stateIds,
1391
+ statePath: step.statePath,
1392
+ eventType: step.eventType,
1393
+ edgeId: step.edgeId
1394
+ });
1395
+ }
1396
+ function getLeafGraphNodeIds(graph) {
1397
+ const activeNodes = graph.nodes.filter((node) => !node.data.temp);
1398
+ const parentIds = new Set(activeNodes.map((node) => node.parentId).filter(Boolean));
1399
+ return activeNodes.filter((node) => !parentIds.has(node.id)).map((node) => node.id);
1400
+ }
1401
+ function getCoverage(graph, paths) {
1402
+ const leafNodeIds = new Set(getLeafGraphNodeIds(graph));
1403
+ const coveredStateIds = /* @__PURE__ */ new Set();
1404
+ const coveredEdgeIds = /* @__PURE__ */ new Set();
1405
+ for (const path of paths) {
1406
+ let pathHasExplicitEdge = false;
1407
+ for (const step of path.steps) {
1408
+ for (const stateId of step.stateIds) if (leafNodeIds.has(stateId)) coveredStateIds.add(stateId);
1409
+ if (step.edgeId) {
1410
+ pathHasExplicitEdge = true;
1411
+ coveredEdgeIds.add(step.edgeId);
1412
+ }
1413
+ }
1414
+ for (const finalStateId of path.finalStateIds) if (leafNodeIds.has(finalStateId)) coveredStateIds.add(finalStateId);
1415
+ if (!pathHasExplicitEdge) for (const edge of getFallbackEventlessEdges(graph, path.finalStateIds)) coveredEdgeIds.add(edge.id);
1416
+ }
1417
+ return {
1418
+ states: {
1419
+ covered: coveredStateIds.size,
1420
+ total: leafNodeIds.size
1421
+ },
1422
+ transitions: {
1423
+ covered: coveredEdgeIds.size,
1424
+ total: graph.edges.filter((edge) => !edge.data.temp).length
1425
+ }
1426
+ };
1427
+ }
1428
+ function deduplicateGeneratedPaths(paths) {
1429
+ const sorted = [...paths].sort((a, b) => b.steps.length - a.steps.length);
1430
+ const unique = [];
1431
+ for (const path of sorted) if (!unique.some((existing) => {
1432
+ if (path.steps.length >= existing.steps.length) return false;
1433
+ return path.steps.every((step, index) => getStepKey(step) === getStepKey(existing.steps[index]));
1434
+ })) unique.push(path);
1435
+ return unique;
1436
+ }
1437
+ function generateGraphPathsData(graph, options) {
1438
+ const machineConfig = graphToMachineConfig(graph);
1439
+ const implementations = getGraphPathSimImplementations(graph);
1440
+ const machine = createMachine(machineConfig).provide(implementations);
1441
+ let converted = (options.strategy === "shortest" ? getShortestPaths : getSimplePaths)(machine, {
1442
+ events: (state) => getTraversalEvents(state, graph),
1443
+ serializeState: (state, event, prevState) => {
1444
+ const serializedState = JSON.stringify(state.value);
1445
+ if (!options.preferTransitionCoverage) return serializedState;
1446
+ return `${serializedState}|${serializeMachineTransition(state, event, prevState)}`;
1447
+ }
1448
+ }).map((path, index) => convertPath(path, graph, index));
1449
+ if (options.reduceDuplicates) converted = deduplicateGeneratedPaths(converted);
1450
+ if (options.limit) converted = converted.slice(0, options.limit);
1451
+ return {
1452
+ paths: converted,
1453
+ coverage: getCoverage(graph, converted)
1454
+ };
1455
+ }
1456
+ function toArray(value) {
1457
+ return Array.isArray(value) ? value : value === void 0 ? [] : [value];
1458
+ }
1459
+ function actionItems(value) {
1460
+ return toArray(value).map((item, index) => {
1461
+ if (typeof item === "string") return {
1462
+ id: item,
1463
+ type: item
1464
+ };
1465
+ if (item && typeof item === "object" && "type" in item) {
1466
+ const action = item;
1467
+ if (typeof action.type === "string") return {
1468
+ id: action.type || `action-${index}`,
1469
+ type: action.type,
1470
+ params: action.params
1471
+ };
1472
+ }
1473
+ return null;
1474
+ }).filter((item) => item !== null);
1475
+ }
1476
+ function invokeItems(value) {
1477
+ return toArray(value).map((item, index) => {
1478
+ if (typeof item === "string") return {
1479
+ id: item || `invoke-${index}`,
1480
+ src: item
1481
+ };
1482
+ if (!item || typeof item !== "object") return null;
1483
+ const invoke = item;
1484
+ if (typeof invoke.src !== "string") return null;
1485
+ return {
1486
+ id: typeof invoke.id === "string" ? invoke.id : invoke.src,
1487
+ src: invoke.src,
1488
+ ...invoke.input !== void 0 ? { input: invoke.input } : {},
1489
+ ...invoke.output !== void 0 ? { output: invoke.output } : {}
1490
+ };
1491
+ }).filter((item) => item !== null);
1492
+ }
1493
+ function transitionTargets(target) {
1494
+ if (typeof target === "string") return [target];
1495
+ if (Array.isArray(target)) return target.filter((item) => typeof item === "string");
1496
+ return [];
1497
+ }
1498
+ function targetToNodeId(sourceId, target, rootKey) {
1499
+ if (!target) return sourceId;
1500
+ let clean = target.startsWith("#") ? target.slice(1) : target;
1501
+ if (target.startsWith("#")) {
1502
+ const rootPrefix = `${rootKey}.`;
1503
+ if (clean === rootKey) return "root";
1504
+ if (clean.startsWith(rootPrefix)) clean = clean.slice(rootPrefix.length);
1505
+ return clean ? `root.${clean}` : "root";
1506
+ }
1507
+ if (clean.includes(".")) return `root.${clean}`;
1508
+ return `${sourceId.split(".").slice(0, -1).join(".") || "root"}.${clean}`;
1509
+ }
1510
+ function transitionObjects(value) {
1511
+ return toArray(value).map((item) => {
1512
+ if (typeof item === "string") return { target: item };
1513
+ if (item && typeof item === "object") return item;
1514
+ return {};
1515
+ });
1516
+ }
1517
+ function guardObject(value) {
1518
+ if (!value) return null;
1519
+ if (typeof value === "string") return { type: value };
1520
+ if (typeof value === "object" && "type" in value && typeof value.type === "string") {
1521
+ const guard = value;
1522
+ return {
1523
+ type: guard.type,
1524
+ params: guard.params
1525
+ };
1526
+ }
1527
+ return null;
1528
+ }
1529
+ function machineConfigToGraph(config) {
1530
+ createMachine(config);
1531
+ const rootKey = String(config.id ?? "machine");
1532
+ const graph = {
1533
+ id: "root",
1534
+ nodes: [],
1535
+ edges: [],
1536
+ data: {
1537
+ implementations: {
1538
+ actions: [],
1539
+ guards: [],
1540
+ actors: [],
1541
+ delays: []
1542
+ },
1543
+ schemas: config.schemas ?? null
1544
+ }
1545
+ };
1546
+ function visit(state, key, parentId) {
1547
+ const id = parentId ? `${parentId}.${key}` : "root";
1548
+ const initialId = state.initial ? `${id}.${state.initial}` : null;
1549
+ graph.nodes.push({
1550
+ id,
1551
+ parentId,
1552
+ data: {
1553
+ key,
1554
+ type: state.type === "parallel" || state.type === "final" || state.type === "history" ? state.type : null,
1555
+ initialId,
1556
+ history: state.history ?? false,
1557
+ entry: actionItems(state.entry),
1558
+ exit: actionItems(state.exit),
1559
+ invokes: invokeItems(state.invoke),
1560
+ tags: Array.isArray(state.tags) ? state.tags : typeof state.tags === "string" ? [state.tags] : [],
1561
+ description: state.description ?? null,
1562
+ meta: state.meta ?? null
1563
+ }
1564
+ });
1565
+ for (const [eventType, value] of Object.entries(state.on ?? {})) for (const [index, transition] of transitionObjects(value).entries()) {
1566
+ const target = transitionTargets(transition.target)[0];
1567
+ graph.edges.push({
1568
+ id: `${id}-${eventType}-${index}`,
1569
+ sourceId: id,
1570
+ targetId: targetToNodeId(id, target, rootKey),
1571
+ data: {
1572
+ eventType,
1573
+ transitionType: target ? transition.reenter ? "reenter" : "normal" : "targetless",
1574
+ guard: guardObject(transition.guard),
1575
+ actions: actionItems(transition.actions),
1576
+ description: typeof transition.description === "string" ? transition.description : null,
1577
+ meta: transition.meta && typeof transition.meta === "object" ? transition.meta : null
1578
+ }
1579
+ });
1580
+ }
1581
+ for (const [delay, value] of Object.entries(state.after ?? {})) for (const [index, transition] of transitionObjects(value).entries()) {
1582
+ const target = transitionTargets(transition.target)[0];
1583
+ graph.edges.push({
1584
+ id: `${id}-after-${delay}-${index}`,
1585
+ sourceId: id,
1586
+ targetId: targetToNodeId(id, target, rootKey),
1587
+ data: {
1588
+ eventType: `xstate.after.${delay}.${id}`,
1589
+ transitionType: target ? "normal" : "targetless",
1590
+ guard: guardObject(transition.guard),
1591
+ actions: actionItems(transition.actions),
1592
+ description: typeof transition.description === "string" ? transition.description : null,
1593
+ meta: null
1594
+ }
1595
+ });
1596
+ }
1597
+ for (const [index, transition] of transitionObjects(state.always).entries()) {
1598
+ const target = transitionTargets(transition.target)[0];
1599
+ graph.edges.push({
1600
+ id: `${id}-always-${index}`,
1601
+ sourceId: id,
1602
+ targetId: targetToNodeId(id, target, rootKey),
1603
+ data: {
1604
+ eventType: "",
1605
+ transitionType: target ? "normal" : "targetless",
1606
+ guard: guardObject(transition.guard),
1607
+ actions: actionItems(transition.actions),
1608
+ description: typeof transition.description === "string" ? transition.description : null,
1609
+ meta: null
1610
+ }
1611
+ });
1612
+ }
1613
+ for (const [childKey, child] of Object.entries(state.states ?? {})) visit(child, childKey, id);
1614
+ }
1615
+ visit(config, rootKey, null);
1616
+ return graph;
1617
+ }
1618
+ const XSTATE_V6_FN_TRANSITION_META = "@statelyai.xstate.v6.fnTransition";
1619
+ const XSTATE_V6_FN_SOURCE_META = "@statelyai.xstate.v6.fnSource";
1620
+ function sourceFile(source) {
1621
+ return ts.createSourceFile("machine.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1622
+ }
1623
+ function propName(name) {
1624
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
1625
+ return null;
1626
+ }
1627
+ function unwrapExpression$2(node) {
1628
+ if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node) || ts.isParenthesizedExpression(node)) return unwrapExpression$2(node.expression);
1629
+ return node;
1630
+ }
1631
+ function propertyContext(parent, key) {
1632
+ if (parent.transitionMap) return {
1633
+ v6: parent.v6,
1634
+ transitionValue: true
1635
+ };
1636
+ if (parent.transitionValue && key === "actions") return {
1637
+ v6: parent.v6,
1638
+ actionValue: true
1639
+ };
1640
+ if (key === "meta") return {
1641
+ v6: parent.v6,
1642
+ metaValue: true
1643
+ };
1644
+ if (parent.transitionValue && key === "to") return {
1645
+ v6: parent.v6,
1646
+ transitionValue: true
1647
+ };
1648
+ if (key === "entry" || key === "exit") return {
1649
+ v6: parent.v6,
1650
+ actionValue: true
1651
+ };
1652
+ if (key === "on" || key === "after") return {
1653
+ v6: parent.v6,
1654
+ transitionMap: true
1655
+ };
1656
+ if (key === "always" || key === "onDone" || key === "onError" || key === "onSnapshot" || key === "onTimeout") return {
1657
+ v6: parent.v6,
1658
+ transitionValue: true
1659
+ };
1660
+ return { v6: parent.v6 };
1661
+ }
1662
+ function fallbackFunctionTransition(fn) {
1663
+ return { meta: {
1664
+ [XSTATE_V6_FN_TRANSITION_META]: true,
1665
+ [XSTATE_V6_FN_SOURCE_META]: fn.getText()
1666
+ } };
1667
+ }
1668
+ function conditionText(cond) {
1669
+ return cond.negated ? `!(${cond.text})` : cond.text;
1670
+ }
1671
+ function simplifyConditions(conds) {
1672
+ const seen = /* @__PURE__ */ new Set();
1673
+ return conds.filter((cond) => {
1674
+ if (cond.negated) return false;
1675
+ const key = conditionText(cond);
1676
+ if (seen.has(key)) return false;
1677
+ seen.add(key);
1678
+ return true;
1679
+ });
1680
+ }
1681
+ function guardFromConditions(conds) {
1682
+ const simplified = simplifyConditions(conds);
1683
+ if (simplified.length === 0) return void 0;
1684
+ return { type: simplified.map(conditionText).join(" && ") };
1685
+ }
1686
+ function mergeActions(left, right) {
1687
+ return [...left ?? [], ...right];
1688
+ }
1689
+ function transitionFromValue(value, actions) {
1690
+ if (typeof value === "string") {
1691
+ if (actions.length === 0) return value;
1692
+ return {
1693
+ target: value,
1694
+ actions
1695
+ };
1696
+ }
1697
+ if (!value || typeof value !== "object" || Array.isArray(value)) return actions.length ? { actions } : null;
1698
+ const record = { ...value };
1699
+ const context = record.context;
1700
+ delete record.context;
1701
+ if (context !== void 0) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, [{
1702
+ type: "xstate.assign",
1703
+ params: { assignment: context }
1704
+ }]);
1705
+ if (actions.length) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, actions);
1706
+ return record;
1707
+ }
1708
+ function expressionBranches(expression, bindings, depth) {
1709
+ const node = unwrapExpression$2(expression);
1710
+ if (ts.isConditionalExpression(node)) {
1711
+ const cond = node.condition.getText();
1712
+ return [...expressionBranches(node.whenTrue, bindings, depth + 1).map((branch) => ({
1713
+ ...branch,
1714
+ conds: [{ text: cond }, ...branch.conds]
1715
+ })), ...expressionBranches(node.whenFalse, bindings, depth + 1).map((branch) => ({
1716
+ ...branch,
1717
+ conds: [{
1718
+ text: cond,
1719
+ negated: true
1720
+ }, ...branch.conds]
1721
+ }))];
1722
+ }
1723
+ if (ts.isBinaryExpression(node) && (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
1724
+ const cond = node.left.getText();
1725
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) return [...expressionBranches(node.right, bindings, depth + 1).map((branch) => ({
1726
+ ...branch,
1727
+ conds: [{ text: cond }, ...branch.conds]
1728
+ })), {
1729
+ conds: [{
1730
+ text: cond,
1731
+ negated: true
1732
+ }],
1733
+ actions: []
1734
+ }];
1735
+ return [{
1736
+ conds: [{ text: cond }],
1737
+ actions: [],
1738
+ result: node.left.getText()
1739
+ }, ...expressionBranches(node.right, bindings, depth + 1).map((branch) => ({
1740
+ ...branch,
1741
+ conds: [{
1742
+ text: cond,
1743
+ negated: true
1744
+ }, ...branch.conds]
1745
+ }))];
1746
+ }
1747
+ if (ts.isObjectLiteralExpression(node)) {
1748
+ const targetProperty = node.properties.find((property) => ts.isPropertyAssignment(property) && propName(property.name) === "target");
1749
+ const target = targetProperty ? unwrapExpression$2(targetProperty.initializer) : null;
1750
+ if (target && (ts.isConditionalExpression(target) || ts.isBinaryExpression(target) && (target.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || target.operatorToken.kind === ts.SyntaxKind.BarBarToken))) return expressionBranches(target, bindings, depth + 1).map((branch) => ({
1751
+ ...branch,
1752
+ result: objectLiteralToValue(node, bindings, {}, depth + 1, new Map([["target", branch.result]]))
1753
+ }));
1754
+ }
1755
+ return [{
1756
+ conds: [],
1757
+ actions: [],
1758
+ result: expressionToValue(node, bindings, {}, depth + 1)
1759
+ }];
1760
+ }
1761
+ function mergeBranchPrefix(prefix, branch) {
1762
+ return {
1763
+ conds: [...prefix.conds, ...branch.conds],
1764
+ actions: [...prefix.actions, ...branch.actions],
1765
+ result: branch.result,
1766
+ unknown: prefix.unknown || branch.unknown
1767
+ };
1768
+ }
1769
+ function expressionStatementAction(statement, enqName, bindings) {
1770
+ const expression = unwrapExpression$2(statement.expression);
1771
+ if (!ts.isCallExpression(expression)) return null;
1772
+ const callee = expression.expression;
1773
+ if (ts.isIdentifier(callee) && callee.text === enqName) return {
1774
+ type: "xstate.expr",
1775
+ params: {
1776
+ code: expression.getText(),
1777
+ lang: "ts"
1778
+ }
1779
+ };
1780
+ if (!ts.isPropertyAccessExpression(callee)) return null;
1781
+ if (!ts.isIdentifier(callee.expression) || callee.expression.text !== enqName) return null;
1782
+ const method = callee.name.text;
1783
+ const args = expression.arguments;
1784
+ const value = (index) => {
1785
+ const arg = args[index];
1786
+ return arg && ts.isExpression(arg) ? expressionToValue(arg, bindings, {}, 0) : void 0;
1787
+ };
1788
+ const spreadOpts = (index) => {
1789
+ const opts = value(index);
1790
+ return opts && typeof opts === "object" && !Array.isArray(opts) ? opts : {};
1791
+ };
1792
+ if (method === "emit") return {
1793
+ type: "xstate.emit",
1794
+ params: { event: value(0) }
1795
+ };
1796
+ if (method === "raise") return {
1797
+ type: "xstate.raise",
1798
+ params: {
1799
+ event: value(0),
1800
+ ...spreadOpts(1)
1801
+ }
1802
+ };
1803
+ if (method === "sendTo") return {
1804
+ type: "xstate.sendTo",
1805
+ params: {
1806
+ to: value(0),
1807
+ event: value(1),
1808
+ ...spreadOpts(2)
1809
+ }
1810
+ };
1811
+ if (method === "cancel") return {
1812
+ type: "xstate.cancel",
1813
+ params: { sendId: value(0) }
1814
+ };
1815
+ if (method === "log") return {
1816
+ type: "xstate.log",
1817
+ params: { label: value(0) }
1818
+ };
1819
+ if (method === "spawn") return {
1820
+ type: "xstate.spawnChild",
1821
+ params: {
1822
+ src: value(0),
1823
+ ...spreadOpts(1)
1824
+ }
1825
+ };
1826
+ if (method === "stop") return {
1827
+ type: "xstate.stopChild",
1828
+ params: { actorRef: value(0) }
1829
+ };
1830
+ return {
1831
+ type: "xstate.expr",
1832
+ params: {
1833
+ code: expression.getText(),
1834
+ lang: "ts"
1835
+ }
1836
+ };
1837
+ }
1838
+ function statementBranches(statements, bindings, enqName, prefixes = [{
1839
+ conds: [],
1840
+ actions: []
1841
+ }]) {
1842
+ let open = prefixes;
1843
+ const done = [];
1844
+ for (const statement of statements) {
1845
+ const nextOpen = [];
1846
+ for (const prefix of open) {
1847
+ if (ts.isReturnStatement(statement)) {
1848
+ if (!statement.expression) {
1849
+ done.push({
1850
+ ...prefix,
1851
+ result: void 0
1852
+ });
1853
+ continue;
1854
+ }
1855
+ for (const branch of expressionBranches(statement.expression, bindings, 0)) done.push(mergeBranchPrefix(prefix, branch));
1856
+ continue;
1857
+ }
1858
+ if (ts.isExpressionStatement(statement)) {
1859
+ const action = expressionStatementAction(statement, enqName, bindings);
1860
+ if (action) {
1861
+ nextOpen.push({
1862
+ ...prefix,
1863
+ actions: [...prefix.actions, action]
1864
+ });
1865
+ continue;
1866
+ }
1867
+ }
1868
+ if (ts.isIfStatement(statement)) {
1869
+ const cond = statement.expression.getText();
1870
+ const thenBranches = statementBranches(ts.isBlock(statement.thenStatement) ? statement.thenStatement.statements : [statement.thenStatement], bindings, enqName, [{
1871
+ ...prefix,
1872
+ conds: [...prefix.conds, { text: cond }]
1873
+ }]);
1874
+ const elseStatements = statement.elseStatement ? ts.isBlock(statement.elseStatement) ? statement.elseStatement.statements : [statement.elseStatement] : [];
1875
+ const elseBranches = elseStatements.length > 0 ? statementBranches(elseStatements, bindings, enqName, [{
1876
+ ...prefix,
1877
+ conds: [...prefix.conds, {
1878
+ text: cond,
1879
+ negated: true
1880
+ }]
1881
+ }]) : [{
1882
+ ...prefix,
1883
+ conds: [...prefix.conds, {
1884
+ text: cond,
1885
+ negated: true
1886
+ }]
1887
+ }];
1888
+ for (const branch of [...thenBranches, ...elseBranches]) if ("result" in branch) done.push(branch);
1889
+ else nextOpen.push(branch);
1890
+ continue;
1891
+ }
1892
+ if (ts.isSwitchStatement(statement)) {
1893
+ const expr = statement.expression.getText();
1894
+ const prior = [];
1895
+ for (const clause of statement.caseBlock.clauses) {
1896
+ const ownCond = ts.isCaseClause(clause) ? { text: `${expr} === ${clause.expression.getText()}` } : null;
1897
+ const conds = ownCond ? [...prior, ownCond] : prior.length ? [...prior] : [];
1898
+ const branches = statementBranches(clause.statements, bindings, enqName, [{
1899
+ ...prefix,
1900
+ conds: [...prefix.conds, ...conds]
1901
+ }]);
1902
+ for (const branch of branches) if ("result" in branch) done.push(branch);
1903
+ else nextOpen.push(branch);
1904
+ if (ownCond) prior.push({
1905
+ ...ownCond,
1906
+ negated: true
1907
+ });
1908
+ }
1909
+ continue;
1910
+ }
1911
+ nextOpen.push({
1912
+ ...prefix,
1913
+ unknown: true
1914
+ });
1915
+ }
1916
+ open = nextOpen;
1917
+ }
1918
+ return [...done, ...open];
1919
+ }
1920
+ function enqueueParamName(fn) {
1921
+ const param = fn.parameters[1];
1922
+ return param && ts.isIdentifier(param.name) ? param.name.text : "enq";
1923
+ }
1924
+ function normalizeFunctionTransition(fn, bindings, depth) {
1925
+ const transitions = (ts.isArrowFunction(fn) && !ts.isBlock(fn.body) ? expressionBranches(fn.body, bindings, depth + 1) : fn.body && ts.isBlock(fn.body) ? statementBranches(fn.body.statements, bindings, enqueueParamName(fn)) : []).filter((branch) => !branch.unknown).map((branch) => {
1926
+ const transition = transitionFromValue(branch.result, branch.actions);
1927
+ if (!transition) return null;
1928
+ if (typeof transition === "string") {
1929
+ const guard = guardFromConditions(branch.conds);
1930
+ return guard ? {
1931
+ target: transition,
1932
+ guard
1933
+ } : transition;
1934
+ }
1935
+ const guard = guardFromConditions(branch.conds);
1936
+ return guard ? {
1937
+ ...transition,
1938
+ guard
1939
+ } : transition;
1940
+ }).filter((transition) => Boolean(transition));
1941
+ if (transitions.length === 1) return transitions[0];
1942
+ if (transitions.length > 1) return transitions;
1943
+ return fallbackFunctionTransition(fn);
1944
+ }
1945
+ function inlineAction(fn) {
1946
+ return {
1947
+ type: "xstate.expr",
1948
+ params: {
1949
+ code: fn.getText(),
1950
+ lang: "ts"
1951
+ }
1952
+ };
1953
+ }
1954
+ function inlineCodeExpression(expr) {
1955
+ return {
1956
+ "@type": "code",
1957
+ lang: "ts",
1958
+ expr
1959
+ };
1960
+ }
1961
+ function normalizeFunctionAction(fn, bindings) {
1962
+ if (ts.isArrowFunction(fn) && !ts.isBlock(fn.body)) return inlineAction(fn);
1963
+ if (!fn.body || !ts.isBlock(fn.body)) return inlineAction(fn);
1964
+ const branches = statementBranches(fn.body.statements, bindings, enqueueParamName(fn));
1965
+ if (branches.length === 1 && !branches[0].unknown && branches[0].conds.length === 0 && branches[0].actions.length > 0 && !("result" in branches[0])) return branches[0].actions;
1966
+ return inlineAction(fn);
1967
+ }
1968
+ function objectLiteralToValue(node, bindings, context, depth, overrides = /* @__PURE__ */ new Map()) {
1969
+ const result = {};
1970
+ for (const property of node.properties) {
1971
+ if (ts.isSpreadAssignment(property)) {
1972
+ const spread = expressionToValue(property.expression, bindings, context, depth + 1);
1973
+ if (spread && typeof spread === "object" && !Array.isArray(spread)) Object.assign(result, spread);
1974
+ continue;
1975
+ }
1976
+ if (!ts.isPropertyAssignment(property) && !ts.isShorthandPropertyAssignment(property)) continue;
1977
+ const key = propName(property.name);
1978
+ if (!key) continue;
1979
+ if (overrides.has(key)) {
1980
+ result[key] = overrides.get(key);
1981
+ continue;
1982
+ }
1983
+ if (ts.isShorthandPropertyAssignment(property)) {
1984
+ result[key] = expressionToValue(property.name, bindings, propertyContext(context, key), depth + 1);
1985
+ continue;
1986
+ }
1987
+ result[key] = expressionToValue(property.initializer, bindings, propertyContext(context, key), depth + 1);
1988
+ }
1989
+ if (context.transitionValue && "to" in result) {
1990
+ const to = result.to;
1991
+ const { to: _to, ...rest } = result;
1992
+ if (typeof to === "string") return {
1993
+ ...rest,
1994
+ target: to
1995
+ };
1996
+ if (Array.isArray(to)) return to.map((item) => typeof item === "string" ? {
1997
+ ...rest,
1998
+ target: item
1999
+ } : item && typeof item === "object" ? {
2000
+ ...rest,
2001
+ ...item
2002
+ } : rest);
2003
+ if (to && typeof to === "object" && !Array.isArray(to)) return {
2004
+ ...rest,
2005
+ ...to
2006
+ };
2007
+ }
2008
+ return result;
2009
+ }
2010
+ function expressionToValue(expression, bindings, context = {}, depth = 0) {
2011
+ if (depth > 30) return void 0;
2012
+ const node = unwrapExpression$2(expression);
2013
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
2014
+ if (ts.isNumericLiteral(node)) return Number(node.text);
2015
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
2016
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
2017
+ if (node.kind === ts.SyntaxKind.NullKeyword) return null;
2018
+ if (ts.isIdentifier(node)) {
2019
+ if (node.text === "undefined") return void 0;
2020
+ const binding = bindings.get(node.text);
2021
+ return binding ? expressionToValue(binding, bindings, context, depth + 1) : context.metaValue ? inlineCodeExpression(node.getText()) : node.text;
2022
+ }
2023
+ if (ts.isObjectLiteralExpression(node)) return objectLiteralToValue(node, bindings, context, depth);
2024
+ if (ts.isArrayLiteralExpression(node)) {
2025
+ const values = node.elements.map((element) => expressionToValue(element, bindings, context, depth + 1));
2026
+ return context.actionValue ? values.flat() : values;
2027
+ }
2028
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
2029
+ if (context.metaValue) return inlineCodeExpression(node.getText());
2030
+ if (context.v6 && context.transitionValue) return normalizeFunctionTransition(node, bindings, depth);
2031
+ if (context.v6 && context.actionValue) return normalizeFunctionAction(node, bindings);
2032
+ return node.getText();
2033
+ }
2034
+ if (ts.isCallExpression(node)) {
2035
+ if (context.metaValue) return inlineCodeExpression(node.getText());
2036
+ if (context.actionValue) return inlineAction(node);
2037
+ const callee = node.expression.getText();
2038
+ const type = callee.includes(".") ? callee.split(".").pop() : callee;
2039
+ return { type: type === "assign" ? "xstate.assign" : type };
2040
+ }
2041
+ return context.metaValue ? inlineCodeExpression(node.getText()) : node.getText();
2042
+ }
2043
+ function collectBindings$1(file) {
2044
+ const bindings = /* @__PURE__ */ new Map();
2045
+ for (const statement of file.statements) {
2046
+ if (!ts.isVariableStatement(statement)) continue;
2047
+ for (const declaration of statement.declarationList.declarations) if (ts.isIdentifier(declaration.name) && declaration.initializer) bindings.set(declaration.name.text, declaration.initializer);
2048
+ }
2049
+ return bindings;
2050
+ }
2051
+ function isCreateMachineCall(node) {
2052
+ const expression = node.expression;
2053
+ if (ts.isIdentifier(expression)) return expression.text === "createMachine";
2054
+ if (ts.isPropertyAccessExpression(expression)) return expression.name.text === "createMachine";
2055
+ return false;
2056
+ }
2057
+ function hasV6Syntax(source) {
2058
+ const file = sourceFile(source);
2059
+ let found = false;
2060
+ function visit(node) {
2061
+ if (found) return;
2062
+ if (ts.isPropertyAssignment(node) && (ts.isIdentifier(node.name) || ts.isStringLiteral(node.name))) {
2063
+ const key = node.name.text;
2064
+ if (key === "triggers" || key === "internalEvents" || key === "timeout" || key === "onTimeout" || key === "choice" || key === "route") {
2065
+ found = true;
2066
+ return;
2067
+ }
2068
+ if ((key === "on" || key === "after" || key === "always" || key === "entry" || key === "exit" || key === "actions" && !ts.isObjectLiteralExpression(node.initializer)) && node.initializer.getText().includes("=>")) {
2069
+ found = true;
2070
+ return;
2071
+ }
2072
+ }
2073
+ ts.forEachChild(node, visit);
2074
+ }
2075
+ visit(file);
2076
+ return found;
2077
+ }
2078
+ function detectXStateSourceProfile(source) {
2079
+ return hasV6Syntax(source) ? "xstate-v6-alpha" : "xstate-v5";
2080
+ }
2081
+ function xstateSourceToConfig(source, options = {}) {
2082
+ const file = sourceFile(source);
2083
+ const bindings = collectBindings$1(file);
2084
+ let found = null;
2085
+ const profile = options.profile === "auto" || !options.profile ? detectXStateSourceProfile(source) : options.profile;
2086
+ function visit(node) {
2087
+ if (found || !ts.isCallExpression(node) || !isCreateMachineCall(node)) {
2088
+ ts.forEachChild(node, visit);
2089
+ return;
2090
+ }
2091
+ const arg = node.arguments[0];
2092
+ if (!arg) return;
2093
+ const value = expressionToValue(arg, bindings, { v6: profile === "xstate-v6-alpha" });
2094
+ if (value && typeof value === "object" && !Array.isArray(value)) found = value;
2095
+ }
2096
+ visit(file);
2097
+ if (!found) throw new Error("No createMachine(...) or setup(...).createMachine(...) call found.");
2098
+ return found;
2099
+ }
2100
+ function parseJsonLike(input) {
2101
+ return JSON.parse(input);
2102
+ }
2103
+ function parseSimpleYaml(input) {
2104
+ const lines = input.split(/\r?\n/).filter((line) => line.trim() && !line.trim().startsWith("#"));
2105
+ const root = {};
2106
+ const stack = [{
2107
+ indent: -1,
2108
+ value: root
2109
+ }];
2110
+ for (const line of lines) {
2111
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
2112
+ const trimmed = line.trim();
2113
+ const match = /^([^:]+):(.*)$/.exec(trimmed);
2114
+ if (!match) continue;
2115
+ const key = match[1].trim();
2116
+ const raw = match[2].trim();
2117
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) stack.pop();
2118
+ const parent = stack[stack.length - 1].value;
2119
+ if (!raw) {
2120
+ const child = {};
2121
+ parent[key] = child;
2122
+ stack.push({
2123
+ indent,
2124
+ value: child
2125
+ });
2126
+ } else if (raw === "true" || raw === "false") parent[key] = raw === "true";
2127
+ else if (/^-?\d+(?:\.\d+)?$/.test(raw)) parent[key] = Number(raw);
2128
+ else parent[key] = raw.replace(/^['"]|['"]$/g, "");
2129
+ }
2130
+ return root;
2131
+ }
2132
+ function parseMermaid(input) {
2133
+ const states = /* @__PURE__ */ new Set();
2134
+ const transitions = [];
2135
+ let initial;
2136
+ for (const line of input.split(/\r?\n/)) {
2137
+ const trimmed = line.trim();
2138
+ if (!trimmed || /^(stateDiagram|stateDiagram-v2|flowchart|graph)\b/.test(trimmed)) continue;
2139
+ const stateMatch = /^\[\*\]\s*-->\s*([A-Za-z0-9_.-]+)/.exec(trimmed);
2140
+ if (stateMatch?.[1]) {
2141
+ initial = stateMatch[1];
2142
+ states.add(initial);
2143
+ continue;
2144
+ }
2145
+ const transitionMatch = /^([A-Za-z0-9_.-]+)\s*(?:-->|--)\s*(?:\|([^|]+)\|)?\s*([A-Za-z0-9_.-]+)(?:\s*:\s*(.+))?/.exec(trimmed);
2146
+ if (!transitionMatch) continue;
2147
+ const source = transitionMatch[1];
2148
+ const target = transitionMatch[3];
2149
+ const event = transitionMatch[2] ?? transitionMatch[4];
2150
+ states.add(source);
2151
+ states.add(target);
2152
+ transitions.push({
2153
+ source,
2154
+ target,
2155
+ event: event?.trim()
2156
+ });
2157
+ initial ??= source;
2158
+ }
2159
+ const stateConfigs = {};
2160
+ for (const state of states) stateConfigs[state] = {};
2161
+ for (const transition of transitions) {
2162
+ const source = stateConfigs[transition.source];
2163
+ const on = source.on ??= {};
2164
+ on[transition.event || "NEXT"] = transition.target;
2165
+ }
2166
+ return {
2167
+ id: "machine",
2168
+ initial: initial ?? [...states][0] ?? "idle",
2169
+ states: Object.keys(stateConfigs).length ? stateConfigs : { idle: {} }
2170
+ };
2171
+ }
2172
+ function inferMachineFormat(machine, format) {
2173
+ if (format) return format;
2174
+ if (typeof machine !== "string") return "json";
2175
+ const trimmed = machine.trim();
2176
+ if (trimmed.startsWith("{")) return "json";
2177
+ if (/^(stateDiagram|flowchart|graph)\b/m.test(trimmed)) return "mermaid";
2178
+ if (/^<\?xml|^<scxml/m.test(trimmed)) return "scxml";
2179
+ if (/^\w+:/m.test(trimmed)) return "yaml";
2180
+ return "xstate";
2181
+ }
2182
+ function machineInputToConfig(input) {
2183
+ const format = inferMachineFormat(input.machine, input.format);
2184
+ if (typeof input.machine !== "string") {
2185
+ createMachine(input.machine);
2186
+ return input.machine;
2187
+ }
2188
+ if (format === "json") {
2189
+ const config = parseJsonLike(input.machine);
2190
+ createMachine(config);
2191
+ return config;
2192
+ }
2193
+ if (format === "yaml") {
2194
+ const config = parseSimpleYaml(input.machine);
2195
+ createMachine(config);
2196
+ return config;
2197
+ }
2198
+ if (format === "mermaid") {
2199
+ const config = parseMermaid(input.machine);
2200
+ createMachine(config);
2201
+ return config;
2202
+ }
2203
+ if (format === "xstate") {
2204
+ const config = xstateSourceToConfig(input.machine, { profile: input.profile ?? (input.xstateVersion === 6 ? "xstate-v6-alpha" : input.xstateVersion === 5 ? "xstate-v5" : "auto") });
2205
+ createMachine(config);
2206
+ return config;
2207
+ }
2208
+ throw new Error(`${format} source parsing is not supported by graph-tools v1 yet.`);
2209
+ }
2210
+ function machineInputToGraph(input) {
2211
+ return machineConfigToGraph(machineInputToConfig(input));
2212
+ }
2213
+ function graphToMermaid(graph) {
2214
+ const machine = graphToMachineConfig(graph);
2215
+ const lines = ["stateDiagram-v2"];
2216
+ if (machine.initial) lines.push(` [*] --> ${machine.initial}`);
2217
+ for (const [stateKey, state] of Object.entries(machine.states ?? {})) for (const [event, transition] of Object.entries(state.on ?? {})) {
2218
+ const target = typeof transition === "string" ? transition : Array.isArray(transition) ? void 0 : transition.target;
2219
+ if (target) lines.push(` ${stateKey} --> ${target} : ${event}`);
2220
+ }
2221
+ return lines.join("\n");
2222
+ }
2223
+ function toYaml(value, indent = 0) {
2224
+ const pad = " ".repeat(indent);
2225
+ if (!value || typeof value !== "object" || Array.isArray(value)) return `${JSON.stringify(value)}`;
2226
+ return Object.entries(value).map(([key, item]) => {
2227
+ if (item && typeof item === "object" && !Array.isArray(item)) return `${pad}${key}:\n${toYaml(item, indent + 2)}`;
2228
+ return `${pad}${key}: ${typeof item === "string" ? item : JSON.stringify(item)}`;
2229
+ }).join("\n");
2230
+ }
2231
+ function graphToFormat(graph, format) {
2232
+ const config = graphToMachineConfig(graph);
2233
+ if (format === "xstate") return `import { createMachine } from 'xstate';\n\nexport const machine = createMachine(${serializeJS(config, 2)});\n`;
2234
+ if (format === "json") return config;
2235
+ if (format === "yaml") return toYaml(config);
2236
+ if (format === "mermaid") return graphToMermaid(graph);
2237
+ if (format === "scxml") {
2238
+ const machine = config;
2239
+ return `<scxml version="1.0" initial="${machine.initial ?? ""}" name="${machine.id ?? "machine"}"></scxml>`;
2240
+ }
2241
+ return config;
2242
+ }
2243
+ const colorSchema = _enum([
2244
+ "green",
2245
+ "red",
2246
+ "purple",
2247
+ "blue",
2248
+ "orange",
2249
+ "yellow",
2250
+ "pink",
2251
+ "teal"
2252
+ ]);
2253
+ const stateTypeSchema = _enum([
2254
+ "normal",
2255
+ "parallel",
2256
+ "history",
2257
+ "final"
2258
+ ]).nullable();
2259
+ const actionLocationSchema = union([object({
2260
+ nodeId: string(),
2261
+ group: _enum(["entry", "exit"])
2262
+ }), object({
2263
+ edgeId: string(),
2264
+ group: literal("transition")
2265
+ })]);
2266
+ const createStatePatchSchema = object({
2267
+ op: literal("createState"),
2268
+ description: string().optional(),
2269
+ id: string().optional(),
2270
+ parentId: string(),
2271
+ key: string(),
2272
+ type: stateTypeSchema.optional(),
2273
+ x: number().optional(),
2274
+ y: number().optional(),
2275
+ color: colorSchema.nullable().optional(),
2276
+ initial: boolean().optional()
2277
+ });
2278
+ const updateStatePatchSchema = object({
2279
+ op: literal("updateState"),
2280
+ description: string().optional(),
2281
+ stateId: string(),
2282
+ key: string().optional(),
2283
+ type: stateTypeSchema.optional(),
2284
+ stateDescription: string().optional(),
2285
+ initialId: string().nullable().optional(),
2286
+ color: colorSchema.nullable().optional(),
2287
+ meta: record(string(), unknown()).nullable().optional(),
2288
+ history: _enum(["shallow", "deep"]).nullable().optional()
2289
+ });
2290
+ const deleteStatePatchSchema = object({
2291
+ op: literal("deleteState"),
2292
+ description: string().optional(),
2293
+ stateId: string()
2294
+ });
2295
+ const guardSchema = object({
2296
+ type: string(),
2297
+ code: string().optional(),
2298
+ params: record(string(), unknown()).optional()
2299
+ });
2300
+ const createTransitionPatchSchema = object({
2301
+ op: literal("createTransition"),
2302
+ description: string().optional(),
2303
+ id: string().optional(),
2304
+ sourceId: string(),
2305
+ targetId: string(),
2306
+ eventType: string(),
2307
+ transitionType: _enum([
2308
+ "normal",
2309
+ "targetless",
2310
+ "reenter"
2311
+ ]).optional(),
2312
+ guard: guardSchema.optional(),
2313
+ color: colorSchema.nullable().optional(),
2314
+ transitionDescription: string().nullable().optional()
2315
+ });
2316
+ const updateTransitionPatchSchema = object({
2317
+ op: literal("updateTransition"),
2318
+ description: string().optional(),
2319
+ transitionId: string(),
2320
+ sourceId: string().optional(),
2321
+ targetId: string().optional(),
2322
+ eventType: string().optional(),
2323
+ transitionType: _enum([
2324
+ "normal",
2325
+ "targetless",
2326
+ "reenter"
2327
+ ]).optional(),
2328
+ transitionDescription: string().nullable().optional(),
2329
+ color: colorSchema.nullable().optional(),
2330
+ meta: record(string(), unknown()).nullable().optional()
2331
+ });
2332
+ const deleteTransitionPatchSchema = object({
2333
+ op: literal("deleteTransition"),
2334
+ description: string().optional(),
2335
+ transitionId: string()
2336
+ });
2337
+ const createActionPatchSchema = object({
2338
+ op: literal("createAction"),
2339
+ description: string().optional(),
2340
+ location: actionLocationSchema,
2341
+ action: object({
2342
+ id: string().optional(),
2343
+ type: string(),
2344
+ params: record(string(), unknown()).optional()
2345
+ }),
2346
+ index: number().optional()
2347
+ });
2348
+ const updateActionPatchSchema = object({
2349
+ op: literal("updateAction"),
2350
+ description: string().optional(),
2351
+ location: actionLocationSchema,
2352
+ actionId: string(),
2353
+ data: object({
2354
+ type: string().optional(),
2355
+ params: record(string(), unknown()).optional()
2356
+ })
2357
+ });
2358
+ const deleteActionPatchSchema = object({
2359
+ op: literal("deleteAction"),
2360
+ description: string().optional(),
2361
+ location: actionLocationSchema,
2362
+ actionId: string()
2363
+ });
2364
+ const setGuardPatchSchema = object({
2365
+ op: literal("setGuard"),
2366
+ description: string().optional(),
2367
+ edgeId: string(),
2368
+ guard: guardSchema
2369
+ });
2370
+ const deleteGuardPatchSchema = object({
2371
+ op: literal("deleteGuard"),
2372
+ description: string().optional(),
2373
+ edgeId: string()
2374
+ });
2375
+ const machinePatchSchema = discriminatedUnion("op", [
2376
+ createStatePatchSchema,
2377
+ updateStatePatchSchema,
2378
+ deleteStatePatchSchema,
2379
+ createTransitionPatchSchema,
2380
+ updateTransitionPatchSchema,
2381
+ deleteTransitionPatchSchema,
2382
+ createActionPatchSchema,
2383
+ updateActionPatchSchema,
2384
+ deleteActionPatchSchema,
2385
+ setGuardPatchSchema,
2386
+ deleteGuardPatchSchema
2387
+ ]);
2388
+ const validationLevelSchema = _enum([
2389
+ "info",
2390
+ "warning",
2391
+ "error"
2392
+ ]);
2393
+ const validationIssueKindSchema = _enum([
2394
+ "correctness",
2395
+ "reachability",
2396
+ "optimization",
2397
+ "maintainability",
2398
+ "security"
2399
+ ]);
2400
+ const validationIssueCodeSchema = _enum([
2401
+ "parse_error",
2402
+ "unsupported_format",
2403
+ "empty_state_key",
2404
+ "invalid_state_key",
2405
+ "duplicate_state_key",
2406
+ "history_initial",
2407
+ "missing_initial",
2408
+ "invalid_initial",
2409
+ "unreachable_state",
2410
+ "final_state_invokes",
2411
+ "invalid_state_children",
2412
+ "empty_parallel",
2413
+ "single_child_compound",
2414
+ "noop_transient_state",
2415
+ "on_done_unreachable",
2416
+ "duplicate_transition",
2417
+ "invalid_delay",
2418
+ "repeated_guard",
2419
+ "transition_never_taken",
2420
+ "missing_guard",
2421
+ "redundant_transition",
2422
+ "missing_delay_implementation",
2423
+ "invalid_final_transition",
2424
+ "invalid_history_transition",
2425
+ "parallel_region_transition",
2426
+ "duplicate_source_name",
2427
+ "undefined_action",
2428
+ "undefined_guard",
2429
+ "undefined_actor"
2430
+ ]);
2431
+ const validationFixKindSchema = _enum([
2432
+ "patch",
2433
+ "rewrite",
2434
+ "advice",
2435
+ "proposedPatch"
2436
+ ]);
2437
+ const validationFixSourceSchema = _enum(["deterministic", "llm"]);
2438
+ const AUTO_NAME_PREFIXES = [
2439
+ ":invocation:",
2440
+ "inline:",
2441
+ "$auto-"
2442
+ ];
2443
+ const AUTO_NAME_SUBSTRINGS = [
2444
+ ":invocation[",
2445
+ "#transition[",
2446
+ "#actor["
2447
+ ];
2448
+ function isAutoGeneratedInlineName(name) {
2449
+ return AUTO_NAME_PREFIXES.some((prefix) => name.startsWith(prefix)) || AUTO_NAME_SUBSTRINGS.some((part) => name.includes(part));
2450
+ }
2451
+ function getImplementations(graph) {
2452
+ return graph.data.implementations ?? {
2453
+ actions: [],
2454
+ guards: [],
2455
+ actors: [],
2456
+ delays: []
2457
+ };
2458
+ }
2459
+ function issue(input) {
2460
+ const nodeIds = input.nodeIds ?? [];
2461
+ const edgeIds = input.edgeIds ?? [];
2462
+ return {
2463
+ code: input.code,
2464
+ kind: input.kind,
2465
+ level: input.level,
2466
+ message: input.message,
2467
+ target: {
2468
+ nodeIds,
2469
+ edgeIds
2470
+ },
2471
+ metadata: input.metadata,
2472
+ fixes: input.fixes,
2473
+ source: "deterministic",
2474
+ nodeIds,
2475
+ edgeIds
2476
+ };
2477
+ }
2478
+ function deleteTransitionFix(edge, title) {
2479
+ return {
2480
+ kind: "patch",
2481
+ source: "deterministic",
2482
+ title,
2483
+ patches: [{
2484
+ op: "deleteTransition",
2485
+ transitionId: edge.id
2486
+ }],
2487
+ confidence: .9
2488
+ };
2489
+ }
2490
+ function setInitialFix(node, child, title) {
2491
+ return {
2492
+ kind: "patch",
2493
+ source: "deterministic",
2494
+ title,
2495
+ patches: [{
2496
+ op: "updateState",
2497
+ stateId: node.id,
2498
+ initialId: child.id
2499
+ }],
2500
+ confidence: .85
2501
+ };
2502
+ }
2503
+ function findDuplicateNodes(graph, node, parent) {
2504
+ if (!parent) return [];
2505
+ return graph.nodes.filter((n) => n.parentId === parent.id && n.id !== node.id).filter((childNode) => childNode.data.key === node.data.key);
2506
+ }
2507
+ function findDuplicateEdges(graph, edge) {
2508
+ return graph.edges.filter((graphEdge) => graphEdge.id !== edge.id && !graphEdge.data.guard && !edge.data.guard && graphEdge.sourceId === edge.sourceId && graphEdge.data.eventType === edge.data.eventType);
2509
+ }
2510
+ function hasUnconditionalAlways(graph, sourceId) {
2511
+ return graph.edges.some((e) => e.sourceId === sourceId && e.data.eventType === "" && !e.data.guard && e.targetId !== e.sourceId);
2512
+ }
2513
+ function isTransitionPreempted(graph, edge) {
2514
+ if (edge.data.eventType === "") return false;
2515
+ return hasUnconditionalAlways(graph, edge.sourceId);
2516
+ }
2517
+ function computeReachableGraphNodes(graph) {
2518
+ const rootNode = graph.nodes.find((n) => n.parentId === null);
2519
+ if (!rootNode) return /* @__PURE__ */ new Set();
2520
+ const visited = /* @__PURE__ */ new Set();
2521
+ function getNodeInitialStates(node) {
2522
+ const children = graph.nodes.filter((n) => n.parentId === node.id);
2523
+ if (!children.length) return [];
2524
+ if (node.data.type === "parallel") return children;
2525
+ const initial = children.find((n) => node.data.initialId === n.id);
2526
+ return initial ? [initial] : [];
2527
+ }
2528
+ function dfs(node) {
2529
+ if (visited.has(node.id)) return;
2530
+ visited.add(node.id);
2531
+ getNodeInitialStates(node).forEach((n) => dfs(n));
2532
+ if (node.data.type === "parallel") graph.nodes.filter((n) => n.parentId === node.id).forEach((region) => {
2533
+ getNodeInitialStates(region).forEach((n) => dfs(n));
2534
+ });
2535
+ if (node.data.type === "history") {
2536
+ const parent = graph.nodes.find((n) => n.id === node.parentId);
2537
+ if (parent) if (parent.data.type === "parallel") graph.nodes.filter((n) => n.parentId === parent.id).forEach((region) => {
2538
+ getNodeInitialStates(region).forEach((n) => dfs(n));
2539
+ });
2540
+ else getNodeInitialStates(parent).forEach((n) => dfs(n));
2541
+ }
2542
+ graph.edges.filter((edge) => edge.sourceId === node.id && !isTransitionPreempted(graph, edge)).forEach((edge) => {
2543
+ const target = graph.nodes.find((n) => n.id === edge.targetId);
2544
+ if (target) dfs(target);
2545
+ });
2546
+ let ancestor = graph.nodes.find((n) => n.id === node.parentId);
2547
+ while (ancestor) {
2548
+ graph.edges.filter((edge) => edge.sourceId === ancestor.id && !isTransitionPreempted(graph, edge)).forEach((edge) => {
2549
+ const target = graph.nodes.find((n) => n.id === edge.targetId);
2550
+ if (target) dfs(target);
2551
+ });
2552
+ ancestor = graph.nodes.find((n) => n.id === ancestor.parentId);
2553
+ }
2554
+ }
2555
+ dfs(rootNode);
2556
+ const reachableWithParents = new Set(visited);
2557
+ for (const nodeId of visited) {
2558
+ let node = graph.nodes.find((n) => n.id === nodeId);
2559
+ while (node?.parentId) {
2560
+ reachableWithParents.add(node.parentId);
2561
+ node = graph.nodes.find((n) => n.id === node.parentId);
2562
+ }
2563
+ }
2564
+ return reachableWithParents;
2565
+ }
2566
+ function getNodeErrors(graph, node) {
2567
+ const errors = [];
2568
+ if (node.data.key.length === 0) errors.push({
2569
+ code: "empty_state_key",
2570
+ kind: "correctness",
2571
+ message: "State key cannot be empty"
2572
+ });
2573
+ const parent = graph.nodes.find((n) => n.id === node.parentId);
2574
+ const isInitial = parent?.data.initialId === node.id;
2575
+ const isHistoryNode = node.data.type === "history";
2576
+ if (isInitial && isHistoryNode) errors.push({
2577
+ code: "history_initial",
2578
+ kind: "correctness",
2579
+ message: "A history node cannot be the initial node. This will cause an infinite loop."
2580
+ });
2581
+ if (findDuplicateNodes(graph, node, parent).length > 0) errors.push({
2582
+ code: "duplicate_state_key",
2583
+ kind: "correctness",
2584
+ message: "A state with that name already exists"
2585
+ });
2586
+ if (node.data.key.includes("#")) errors.push({
2587
+ code: "invalid_state_key",
2588
+ kind: "correctness",
2589
+ message: "State key cannot contain \"#\"",
2590
+ metadata: { character: "#" }
2591
+ });
2592
+ const children = graph.nodes.filter((n) => n.parentId === node.id);
2593
+ if (children.length > 0 && node.data.type !== "parallel" && node.data.type !== "final" && node.data.type !== "history") {
2594
+ const isTargeted = graph.edges.some((edge) => edge.targetId === node.id);
2595
+ if (!node.data.initialId && isTargeted) errors.push({
2596
+ code: "missing_initial",
2597
+ kind: "correctness",
2598
+ message: "Compound state must have an initial state",
2599
+ metadata: {
2600
+ reason: "targeted_compound",
2601
+ childCount: children.length
2602
+ },
2603
+ fixes: children.length === 1 ? [setInitialFix(node, children[0], `Set ${children[0].data.key} as initial state`)] : void 0
2604
+ });
2605
+ else if (node.data.initialId) {
2606
+ if (!children.some((n) => n.id === node.data.initialId)) errors.push({
2607
+ code: "invalid_initial",
2608
+ kind: "correctness",
2609
+ message: "Initial state does not exist",
2610
+ metadata: {
2611
+ initialId: node.data.initialId,
2612
+ childCount: children.length
2613
+ },
2614
+ fixes: children.length === 1 ? [setInitialFix(node, children[0], `Set ${children[0].data.key} as initial state`)] : void 0
2615
+ });
2616
+ }
2617
+ }
2618
+ return errors.length > 0 ? errors : null;
2619
+ }
2620
+ function getNodeWarningsWithReachability(graph, node, reachableNodes) {
2621
+ const warnings = [];
2622
+ if (!reachableNodes.has(node.id) && node.data.type !== "history") warnings.push({
2623
+ code: "unreachable_state",
2624
+ kind: "reachability",
2625
+ message: "Unreachable state"
2626
+ });
2627
+ if (node.data.type === "final" && (node.data.invokes?.length ?? 0) > 0) warnings.push({
2628
+ code: "final_state_invokes",
2629
+ kind: "correctness",
2630
+ message: "Final state cannot have invocations"
2631
+ });
2632
+ if (node.data.type && ["final", "history"].includes(node.data.type) && graph.nodes.filter((n) => n.parentId === node.id).length > 0) warnings.push({
2633
+ code: "invalid_state_children",
2634
+ kind: "correctness",
2635
+ message: `${node.data.type.charAt(0).toUpperCase() + node.data.type.slice(1)} state cannot have child states`,
2636
+ metadata: { stateType: node.data.type }
2637
+ });
2638
+ if (node.data.type === "parallel" && graph.nodes.filter((n) => n.parentId === node.id).length === 0) warnings.push({
2639
+ code: "empty_parallel",
2640
+ kind: "correctness",
2641
+ message: "Parallel state should have child regions"
2642
+ });
2643
+ const children = graph.nodes.filter((n) => n.parentId === node.id);
2644
+ if (children.length === 1 && node.data.type !== "parallel" && node.data.type !== "final" && node.data.type !== "history") warnings.push({
2645
+ code: "single_child_compound",
2646
+ kind: "optimization",
2647
+ message: "State with single child can be simplified"
2648
+ });
2649
+ if (node.data.type !== "parallel" && node.data.type !== "final" && node.data.type !== "history" && children.length === 0 && (node.data.entry?.length ?? 0) === 0 && (node.data.exit?.length ?? 0) === 0 && (node.data.invokes?.length ?? 0) === 0) {
2650
+ const alwaysEdges = graph.edges.filter((e) => e.sourceId === node.id && e.data.eventType === "");
2651
+ const hasUnconditionalNoop = alwaysEdges.some((e) => !e.data.guard && e.targetId !== e.sourceId && (e.data.actions?.length ?? 0) === 0);
2652
+ const hasMeaningfulAlways = alwaysEdges.some((e) => !!e.data.guard || (e.data.actions?.length ?? 0) > 0);
2653
+ if (hasUnconditionalNoop && !hasMeaningfulAlways) warnings.push({
2654
+ code: "noop_transient_state",
2655
+ kind: "optimization",
2656
+ message: "State has no effect: an unconditional transition exits it immediately, and it has no actions or invocations"
2657
+ });
2658
+ }
2659
+ return warnings.length > 0 ? warnings : null;
2660
+ }
2661
+ function getOnDoneWarnings(graph, reachableNodes) {
2662
+ const results = [];
2663
+ for (const node of graph.nodes) {
2664
+ const doneEdges = graph.edges.filter((edge) => edge.sourceId === node.id && (edge.data.eventType.startsWith("@statelyai.state.done.") || edge.data.eventType.startsWith("xstate.done.state.")));
2665
+ if (doneEdges.length === 0) continue;
2666
+ const edgeIds = doneEdges.map((e) => e.id);
2667
+ if (node.data.type === "parallel") {
2668
+ const regions = graph.nodes.filter((n) => n.parentId === node.id);
2669
+ const regionsWithoutFinal = [];
2670
+ const regionsWithoutReachableFinal = [];
2671
+ for (const region of regions) {
2672
+ const finalChildren = graph.nodes.filter((n) => n.parentId === region.id).filter((child) => child.data.type === "final");
2673
+ if (finalChildren.length === 0) regionsWithoutFinal.push(region);
2674
+ else if (!finalChildren.some((child) => reachableNodes.has(child.id))) regionsWithoutReachableFinal.push(region);
2675
+ }
2676
+ if (regionsWithoutFinal.length > 0) results.push(issue({
2677
+ code: "on_done_unreachable",
2678
+ kind: "reachability",
2679
+ level: "warning",
2680
+ nodeIds: [node.id],
2681
+ edgeIds,
2682
+ message: "onDone transitions require each parallel region to have a final child state",
2683
+ metadata: { reason: "parallel_region_missing_final" }
2684
+ }));
2685
+ else if (regionsWithoutReachableFinal.length > 0) results.push(issue({
2686
+ code: "on_done_unreachable",
2687
+ kind: "reachability",
2688
+ level: "warning",
2689
+ nodeIds: [node.id],
2690
+ edgeIds,
2691
+ message: "onDone transitions will never trigger: a parallel region has no reachable final child state",
2692
+ metadata: { reason: "parallel_region_final_unreachable" }
2693
+ }));
2694
+ } else {
2695
+ const finalChildren = graph.nodes.filter((n) => n.parentId === node.id).filter((child) => child.data.type === "final");
2696
+ if (finalChildren.length === 0) results.push(issue({
2697
+ code: "on_done_unreachable",
2698
+ kind: "reachability",
2699
+ level: "warning",
2700
+ nodeIds: [node.id],
2701
+ edgeIds,
2702
+ message: "onDone transitions require a final child state to trigger",
2703
+ metadata: { reason: "missing_final_child" }
2704
+ }));
2705
+ else if (!finalChildren.some((child) => reachableNodes.has(child.id))) results.push(issue({
2706
+ code: "on_done_unreachable",
2707
+ kind: "reachability",
2708
+ level: "warning",
2709
+ nodeIds: [node.id],
2710
+ edgeIds,
2711
+ message: "onDone transitions will never trigger: no reachable final child state",
2712
+ metadata: { reason: "final_child_unreachable" }
2713
+ }));
2714
+ }
2715
+ }
2716
+ return results;
2717
+ }
2718
+ function getEdgeErrors(graph, edge) {
2719
+ const errors = [];
2720
+ const duplicates = findDuplicateEdges(graph, edge);
2721
+ if (duplicates.length > 0) errors.push({
2722
+ code: "duplicate_transition",
2723
+ kind: "correctness",
2724
+ message: "Two events with the same name and source state are not allowed",
2725
+ fixes: graph.edges.findIndex((e) => e.id === edge.id) > graph.edges.findIndex((e) => e.id === duplicates[0].id) ? [deleteTransitionFix(edge, "Delete duplicate transition")] : void 0
2726
+ });
2727
+ if (edge.data.eventType.startsWith("xstate.after.")) {
2728
+ const delayPart = edge.data.eventType.replace(/^xstate\.after\./, "").split(".")[0];
2729
+ if (delayPart !== void 0) {
2730
+ const numericValue = Number(delayPart);
2731
+ if (!Number.isNaN(numericValue) && numericValue < 0) errors.push({
2732
+ code: "invalid_delay",
2733
+ kind: "correctness",
2734
+ message: "Delay value cannot be negative",
2735
+ metadata: { delay: numericValue }
2736
+ });
2737
+ }
2738
+ }
2739
+ return errors.length > 0 ? errors : null;
2740
+ }
2741
+ function getEdgeWarnings(graph, edge) {
2742
+ const warnings = [];
2743
+ const group = graph.edges.filter((e) => e.sourceId === edge.sourceId && e.data.eventType === edge.data.eventType);
2744
+ if (edge.data.guard && group.length > 1) {
2745
+ const guardType = edge.data.guard.type;
2746
+ const withRepeatedGuard = group.filter((e) => e.data.guard?.type === guardType);
2747
+ if (withRepeatedGuard.length > 1) warnings.push({
2748
+ code: "repeated_guard",
2749
+ kind: "maintainability",
2750
+ message: `Found ${withRepeatedGuard.length} guards with the name \`${guardType}\``,
2751
+ metadata: {
2752
+ guardType,
2753
+ count: withRepeatedGuard.length
2754
+ }
2755
+ });
2756
+ }
2757
+ const edgeIndex = group.findIndex((e) => e.id === edge.id);
2758
+ if (edgeIndex > 0 && group.slice(0, edgeIndex).some((e) => !e.data.guard) || isTransitionPreempted(graph, edge)) warnings.push({
2759
+ code: "transition_never_taken",
2760
+ kind: "reachability",
2761
+ message: "This transition will never be taken",
2762
+ fixes: edgeIndex > 0 && group.slice(0, edgeIndex).some((e) => !e.data.guard) ? [deleteTransitionFix(edge, "Delete unreachable transition")] : void 0
2763
+ });
2764
+ if (group.length > 1 && !edge.data.guard && edgeIndex < group.length - 1) warnings.push({
2765
+ code: "missing_guard",
2766
+ kind: "correctness",
2767
+ message: "This transition is missing a guard, so transitions after it will never be taken"
2768
+ });
2769
+ if (edgeIndex > 0) {
2770
+ const prevEdge = group[edgeIndex - 1];
2771
+ const sameTarget = prevEdge.targetId === edge.targetId;
2772
+ const sameActions = JSON.stringify(prevEdge.data.actions) === JSON.stringify(edge.data.actions);
2773
+ if (sameTarget && sameActions) warnings.push({
2774
+ code: "redundant_transition",
2775
+ kind: "optimization",
2776
+ message: "Redundant transition: same target and actions as previous transition",
2777
+ fixes: [deleteTransitionFix(edge, "Delete redundant transition")]
2778
+ });
2779
+ }
2780
+ if (edge.data.eventType.startsWith("xstate.after.")) {
2781
+ const delayPart = edge.data.eventType.replace(/^xstate\.after\./, "").split(".")[0];
2782
+ if (delayPart !== void 0 && Number.isNaN(Number(delayPart))) {
2783
+ if (!getImplementations(graph).delays.find((d) => d.name === delayPart)?.code?.body?.trim()) warnings.push({
2784
+ code: "missing_delay_implementation",
2785
+ kind: "correctness",
2786
+ message: `Missing delay implementation for "${delayPart}"`,
2787
+ metadata: { delay: delayPart }
2788
+ });
2789
+ }
2790
+ }
2791
+ const sourceNode = graph.nodes.find((n) => n.id === edge.sourceId);
2792
+ if (sourceNode && sourceNode.data.type === "final") warnings.push({
2793
+ code: "invalid_final_transition",
2794
+ kind: "correctness",
2795
+ message: "Final state cannot transition to other states",
2796
+ fixes: [deleteTransitionFix(edge, "Delete transition from final state")]
2797
+ });
2798
+ if (sourceNode && sourceNode.data.type === "history") warnings.push({
2799
+ code: "invalid_history_transition",
2800
+ kind: "correctness",
2801
+ message: "History state cannot have transitions",
2802
+ fixes: [deleteTransitionFix(edge, "Delete transition from history state")]
2803
+ });
2804
+ const targetNode = graph.nodes.find((n) => n.id === edge.targetId);
2805
+ if (sourceNode && targetNode && sourceNode.parentId === targetNode.parentId && sourceNode.id !== targetNode.id) {
2806
+ const parentNode = graph.nodes.find((n) => n.id === sourceNode.parentId);
2807
+ if (parentNode && parentNode.data.type === "parallel") warnings.push({
2808
+ code: "parallel_region_transition",
2809
+ kind: "correctness",
2810
+ message: "Parallel regions should communicate via events, not transitions"
2811
+ });
2812
+ }
2813
+ return warnings.length > 0 ? warnings : null;
2814
+ }
2815
+ function getSourceWarnings(graph) {
2816
+ const results = [];
2817
+ const impls = getImplementations(graph);
2818
+ const groups = [
2819
+ {
2820
+ type: "action",
2821
+ items: impls.actions
2822
+ },
2823
+ {
2824
+ type: "guard",
2825
+ items: impls.guards
2826
+ },
2827
+ {
2828
+ type: "actor",
2829
+ items: impls.actors
2830
+ },
2831
+ {
2832
+ type: "delay",
2833
+ items: impls.delays
2834
+ }
2835
+ ];
2836
+ for (const group of groups) {
2837
+ const seen = /* @__PURE__ */ new Map();
2838
+ for (const item of group.items) {
2839
+ if (!item.name) continue;
2840
+ seen.set(item.name, (seen.get(item.name) ?? 0) + 1);
2841
+ }
2842
+ for (const [name, count] of seen) if (count > 1) results.push(issue({
2843
+ code: "duplicate_source_name",
2844
+ kind: "maintainability",
2845
+ level: "warning",
2846
+ message: `Duplicate ${group.type} source name "${name}" (${count} occurrences)`,
2847
+ metadata: {
2848
+ sourceType: group.type,
2849
+ name,
2850
+ count
2851
+ }
2852
+ }));
2853
+ }
2854
+ return results;
2855
+ }
2856
+ function getSourceRestrictionErrors(graph, sourceAllowed) {
2857
+ if (!sourceAllowed) return [];
2858
+ const results = [];
2859
+ const impls = getImplementations(graph);
2860
+ const actionIds = new Set(impls.actions.map((a) => a.id));
2861
+ const guardIds = new Set(impls.guards.map((g) => g.id));
2862
+ const actorIds = new Set(impls.actors.map((a) => a.id));
2863
+ const isInvalidActionType = (type) => {
2864
+ if (!type) return true;
2865
+ if (actionIds.has(type)) return false;
2866
+ if (type.startsWith("xstate.")) return false;
2867
+ if (isAutoGeneratedInlineName(type)) return false;
2868
+ return true;
2869
+ };
2870
+ const formatActionMsg = (type) => type ? `Action "${type}" is not a defined action implementation` : "Action is not selected";
2871
+ if (sourceAllowed.actions === "predefined") {
2872
+ for (const node of graph.nodes) {
2873
+ for (const action of node.data.entry ?? []) if (isInvalidActionType(action.type)) results.push(issue({
2874
+ code: "undefined_action",
2875
+ kind: "correctness",
2876
+ level: "error",
2877
+ nodeIds: [node.id],
2878
+ message: formatActionMsg(action.type)
2879
+ }));
2880
+ for (const action of node.data.exit ?? []) if (isInvalidActionType(action.type)) results.push(issue({
2881
+ code: "undefined_action",
2882
+ kind: "correctness",
2883
+ level: "error",
2884
+ nodeIds: [node.id],
2885
+ message: formatActionMsg(action.type)
2886
+ }));
2887
+ }
2888
+ for (const edge of graph.edges) for (const action of edge.data.actions ?? []) if (isInvalidActionType(action.type)) results.push(issue({
2889
+ code: "undefined_action",
2890
+ kind: "correctness",
2891
+ level: "error",
2892
+ edgeIds: [edge.id],
2893
+ message: formatActionMsg(action.type)
2894
+ }));
2895
+ }
2896
+ if (sourceAllowed.guards === "predefined") for (const edge of graph.edges) {
2897
+ const guard = edge.data.guard;
2898
+ if (!guard) continue;
2899
+ const type = guard.type;
2900
+ if (!type || !guardIds.has(type) && !type.startsWith("inline:") && !isAutoGeneratedInlineName(type)) results.push(issue({
2901
+ code: "undefined_guard",
2902
+ kind: "correctness",
2903
+ level: "error",
2904
+ edgeIds: [edge.id],
2905
+ message: type ? `Guard "${type}" is not a defined guard implementation` : "Guard is not selected"
2906
+ }));
2907
+ }
2908
+ if (sourceAllowed.actors === "predefined") for (const node of graph.nodes) for (const invoke of node.data.invokes ?? []) {
2909
+ const src = invoke.src;
2910
+ if (!src || !actorIds.has(src) && !isAutoGeneratedInlineName(src)) results.push(issue({
2911
+ code: "undefined_actor",
2912
+ kind: "correctness",
2913
+ level: "error",
2914
+ nodeIds: [node.id],
2915
+ message: src ? `Actor "${src}" is not a defined actor implementation` : "Actor is not selected"
2916
+ }));
2917
+ }
2918
+ return results;
2919
+ }
2920
+ function pushMessages(results, messages, level, nodeIds, edgeIds) {
2921
+ messages?.forEach((message) => {
2922
+ results.push(issue({
2923
+ ...message,
2924
+ level,
2925
+ nodeIds,
2926
+ edgeIds
2927
+ }));
2928
+ });
2929
+ }
2930
+ function validateGraph(graph, sourceAllowed) {
2931
+ const results = [];
2932
+ const nonTempNodes = graph.nodes.filter((n) => !n.data?.temp);
2933
+ const nonTempEdges = graph.edges.filter((e) => !e.data?.temp);
2934
+ const nonTempGraph = {
2935
+ ...graph,
2936
+ nodes: nonTempNodes,
2937
+ edges: nonTempEdges
2938
+ };
2939
+ const reachableNodes = computeReachableGraphNodes(nonTempGraph);
2940
+ for (const node of nonTempNodes) {
2941
+ pushMessages(results, getNodeErrors(nonTempGraph, node), "error", [node.id], []);
2942
+ pushMessages(results, getNodeWarningsWithReachability(nonTempGraph, node, reachableNodes), "warning", [node.id], []);
2943
+ }
2944
+ for (const edge of nonTempEdges) {
2945
+ pushMessages(results, getEdgeErrors(nonTempGraph, edge), "error", [], [edge.id]);
2946
+ pushMessages(results, getEdgeWarnings(nonTempGraph, edge), "warning", [], [edge.id]);
2947
+ }
2948
+ results.push(...getOnDoneWarnings(nonTempGraph, reachableNodes));
2949
+ results.push(...getSourceWarnings(nonTempGraph));
2950
+ results.push(...getSourceRestrictionErrors(nonTempGraph, sourceAllowed));
2951
+ return results;
2952
+ }
2953
+ function validationOk(issues) {
2954
+ return !issues.some((item) => item.level === "error");
2955
+ }
2956
+ const validationIssueTargetSchema = object({
2957
+ nodeIds: array(string()).optional(),
2958
+ edgeIds: array(string()).optional(),
2959
+ source: object({
2960
+ path: string().optional(),
2961
+ start: number().optional(),
2962
+ end: number().optional()
2963
+ }).optional()
2964
+ });
2965
+ const validationFixSchema = object({
2966
+ kind: validationFixKindSchema,
2967
+ source: validationFixSourceSchema,
2968
+ title: string(),
2969
+ patches: array(unknown()).optional(),
2970
+ output: unknown().optional(),
2971
+ diff: unknown().optional(),
2972
+ confidence: number().optional(),
2973
+ rationale: string().optional(),
2974
+ rank: number().optional(),
2975
+ postValidation: object({
2976
+ ok: boolean(),
2977
+ remainingIssueCodes: array(validationIssueCodeSchema)
2978
+ }).optional()
2979
+ });
2980
+ const validationIssueSchema = object({
2981
+ code: validationIssueCodeSchema,
2982
+ kind: validationIssueKindSchema,
2983
+ level: validationLevelSchema,
2984
+ message: string(),
2985
+ target: validationIssueTargetSchema,
2986
+ metadata: record(string(), unknown()).optional(),
2987
+ fixes: array(validationFixSchema).optional(),
2988
+ source: _enum(["deterministic", "llm"]).optional(),
2989
+ nodeIds: array(string()),
2990
+ edgeIds: array(string())
2991
+ });
2992
+ const machineFormatSchema = _enum([
2993
+ "xstate",
2994
+ "json",
2995
+ "yaml",
2996
+ "scxml",
2997
+ "mermaid"
2998
+ ]);
2999
+ const xstateSourceProfileSchema = _enum([
3000
+ "auto",
3001
+ "xstate-v5",
3002
+ "xstate-v6-alpha"
3003
+ ]);
3004
+ const machineInputSchema = object({
3005
+ machine: union([record(string(), unknown()), string()]),
3006
+ format: machineFormatSchema.optional(),
3007
+ profile: xstateSourceProfileSchema.optional(),
3008
+ xstateVersion: union([literal(5), literal(6)]).optional()
3009
+ });
3010
+ const graphSchema = record(string(), unknown());
3011
+ function asError(error) {
3012
+ return { message: error instanceof Error ? error.message : String(error) };
3013
+ }
3014
+ function simImplementations(graph, guardState) {
3015
+ const guards = {};
3016
+ for (const edge of graph.edges) if (edge.data.guard) {
3017
+ const guardType = edge.data.guard.type;
3018
+ guards[guardType] = ({ event }) => {
3019
+ if (event["@xstate.guard"] === guardType) return true;
3020
+ return guardState?.[guardType] ?? false;
3021
+ };
3022
+ }
3023
+ return { guards };
3024
+ }
3025
+ const operations = {
3026
+ validate_machine: {
3027
+ description: "Validate a machine without writing files.",
3028
+ input: machineInputSchema,
3029
+ output: object({
3030
+ ok: boolean(),
3031
+ issues: array(validationIssueSchema)
3032
+ }),
3033
+ async handler(input) {
3034
+ try {
3035
+ const issues = validateGraph(machineInputToGraph(input));
3036
+ return {
3037
+ ok: validationOk(issues),
3038
+ issues
3039
+ };
3040
+ } catch (error) {
3041
+ return {
3042
+ ok: false,
3043
+ issues: [{
3044
+ code: "parse_error",
3045
+ kind: "correctness",
3046
+ level: "error",
3047
+ message: asError(error).message,
3048
+ target: {
3049
+ nodeIds: [],
3050
+ edgeIds: []
3051
+ },
3052
+ source: "deterministic",
3053
+ nodeIds: [],
3054
+ edgeIds: []
3055
+ }]
3056
+ };
3057
+ }
3058
+ }
3059
+ },
3060
+ convert_to_graph: {
3061
+ description: "Convert a machine into an internal graph for patching.",
3062
+ input: machineInputSchema,
3063
+ output: object({ graph: graphSchema }),
3064
+ async handler(input) {
3065
+ return { graph: machineInputToGraph(input) };
3066
+ }
3067
+ },
3068
+ convert_format: {
3069
+ description: "Convert a machine or graph to a machine format.",
3070
+ input: object({
3071
+ machine: union([record(string(), unknown()), string()]).optional(),
3072
+ graph: graphSchema.optional(),
3073
+ format: machineFormatSchema.optional(),
3074
+ to: machineFormatSchema
3075
+ }).refine((value) => Boolean(value.machine) !== Boolean(value.graph), { message: "Provide exactly one of machine or graph." }),
3076
+ output: object({
3077
+ format: machineFormatSchema,
3078
+ output: union([record(string(), unknown()), string()])
3079
+ }),
3080
+ async handler(input) {
3081
+ const graph = input.graph ?? machineInputToGraph({
3082
+ machine: input.machine,
3083
+ format: input.format
3084
+ });
3085
+ return {
3086
+ format: input.to,
3087
+ output: graphToFormat(graph, input.to)
3088
+ };
3089
+ }
3090
+ },
3091
+ apply_patch: {
3092
+ description: "Apply semantic machine patches to a graph without writing files.",
3093
+ input: object({
3094
+ graph: graphSchema,
3095
+ patches: array(machinePatchSchema),
3096
+ outputFormat: machineFormatSchema.default("json")
3097
+ }),
3098
+ output: union([object({
3099
+ ok: literal(true),
3100
+ graph: graphSchema,
3101
+ machine: union([record(string(), unknown()), string()]),
3102
+ format: machineFormatSchema,
3103
+ diff: unknown(),
3104
+ patches: array(machinePatchSchema)
3105
+ }), object({
3106
+ ok: literal(false),
3107
+ reason: string(),
3108
+ errors: array(object({ message: string() }))
3109
+ })]),
3110
+ async handler(input) {
3111
+ try {
3112
+ const graph = applyGraphPatches(input.graph, input.patches);
3113
+ createMachine(graphToMachineConfig(graph));
3114
+ const diff = diffGraphsSemantically(input.graph, graph).diff;
3115
+ return {
3116
+ ok: true,
3117
+ graph,
3118
+ machine: graphToFormat(graph, input.outputFormat),
3119
+ format: input.outputFormat,
3120
+ diff,
3121
+ patches: input.patches
3122
+ };
3123
+ } catch (error) {
3124
+ return {
3125
+ ok: false,
3126
+ reason: "validation",
3127
+ errors: [asError(error)]
3128
+ };
3129
+ }
3130
+ }
3131
+ },
3132
+ diff_machines: {
3133
+ description: "Compute semantic diff between two machines.",
3134
+ input: object({
3135
+ left: machineInputSchema,
3136
+ right: machineInputSchema,
3137
+ includePatches: boolean().optional()
3138
+ }),
3139
+ output: object({
3140
+ diff: unknown(),
3141
+ patches: array(machinePatchSchema).optional()
3142
+ }),
3143
+ async handler(input) {
3144
+ return { diff: diffGraphsSemantically(machineInputToGraph(input.left), machineInputToGraph(input.right)).diff };
3145
+ }
3146
+ },
3147
+ generate_paths: {
3148
+ description: "Generate paths through a machine.",
3149
+ input: machineInputSchema.extend({
3150
+ targetPath: array(string()).optional(),
3151
+ kind: _enum(["shortest", "simple"]).default("shortest"),
3152
+ max: number().int().positive().optional()
3153
+ }),
3154
+ output: object({ paths: array(unknown()) }),
3155
+ async handler(input) {
3156
+ const result = generateGraphPathsData(machineInputToGraph(input), {
3157
+ strategy: input.kind,
3158
+ reduceDuplicates: false,
3159
+ preferTransitionCoverage: false,
3160
+ limit: input.max
3161
+ });
3162
+ return { paths: input.targetPath ? result.paths.filter((path) => path.finalStatePath === input.targetPath.join(".")) : result.paths };
3163
+ }
3164
+ },
3165
+ simulate: {
3166
+ description: "Simulate one event from a machine state.",
3167
+ input: machineInputSchema.extend({
3168
+ stateValue: unknown(),
3169
+ event: object({ type: string() }).passthrough(),
3170
+ guardState: record(string(), boolean()).optional()
3171
+ }),
3172
+ output: object({
3173
+ nextStateValue: unknown(),
3174
+ actions: array(unknown()),
3175
+ changed: boolean(),
3176
+ microsteps: array(unknown()).optional()
3177
+ }),
3178
+ async handler(input) {
3179
+ const graph = machineInputToGraph(input);
3180
+ const machine = createMachine(graphToMachineConfig(graph)).provide(simImplementations(graph, input.guardState));
3181
+ const snapshot = machine.resolveState({
3182
+ value: input.stateValue,
3183
+ context: {},
3184
+ status: "active"
3185
+ });
3186
+ const [nextSnapshot, actions] = transition(machine, snapshot, input.event);
3187
+ return {
3188
+ nextStateValue: nextSnapshot.value,
3189
+ actions,
3190
+ changed: JSON.stringify(nextSnapshot.value) !== JSON.stringify(snapshot.value)
3191
+ };
3192
+ }
3193
+ }
3194
+ };
3195
+
110
3196
  //#endregion
111
3197
  //#region ../editor-sync/src/internal/sourceLocations.ts
112
3198
  const sourceFileUris = /* @__PURE__ */ new WeakMap();
113
3199
  function collectXStateSourceLocations(sourceText, document, options = {}) {
114
- const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$1(document.fileName));
3200
+ const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2(document.fileName));
115
3201
  const uri = document.uri?.toString() ?? document.fileName;
116
3202
  sourceFileUris.set(sourceFile, uri);
117
3203
  const bindings = collectBindings(sourceFile, uri, document.fileName, options);
@@ -119,13 +3205,18 @@ function collectXStateSourceLocations(sourceText, document, options = {}) {
119
3205
  const root = findMachineConfigObjectLiteral(sourceFile, bindings);
120
3206
  if (!root) return;
121
3207
  const states = [];
122
- collectStateLocations(root, [], states, bindings, staticValues, root);
3208
+ const statesObjectLocations = [];
3209
+ collectStateLocations(root, [], states, statesObjectLocations, bindings, staticValues, root);
3210
+ const rootProfile = detectXStateSourceProfile(sourceText);
3211
+ const referencedStateSymbols = new Set([...states, ...statesObjectLocations].map((state) => state.symbol).filter((symbol) => typeof symbol === "string"));
123
3212
  return {
124
3213
  version: 1,
3214
+ profile: rootProfile === "xstate-v6-alpha" || [...referencedStateSymbols].some((symbol) => detectXStateSourceProfile(bindings.get(symbol)?.sourceFile.text ?? "") === "xstate-v6-alpha") ? "xstate-v6-alpha" : rootProfile,
125
3215
  root: {
126
3216
  uri,
127
- range: toSourceRange(sourceFile, root)
3217
+ range: toSourceRange$1(sourceFile, root)
128
3218
  },
3219
+ statesObject: statesObjectLocations[0],
129
3220
  states,
130
3221
  staticValues: collectStaticValueReferences(sourceFile, uri, root, staticValues)
131
3222
  };
@@ -143,7 +3234,7 @@ function collectStaticValueBindings(sourceFile) {
143
3234
  kind: "const",
144
3235
  symbol: declaration.name.text,
145
3236
  value,
146
- valueRange: toSourceRange(sourceFile, declaration.initializer)
3237
+ valueRange: toSourceRange$1(sourceFile, declaration.initializer)
147
3238
  });
148
3239
  }
149
3240
  continue;
@@ -158,15 +3249,18 @@ function collectStaticValueBindings(sourceFile) {
158
3249
  kind: "enumMember",
159
3250
  symbol,
160
3251
  value,
161
- valueRange: toSourceRange(sourceFile, member.initializer)
3252
+ valueRange: toSourceRange$1(sourceFile, member.initializer)
162
3253
  });
163
3254
  }
164
3255
  }
165
3256
  return bindings;
166
3257
  }
167
- function collectBindings(sourceFile, uri, fileName, options) {
3258
+ function collectBindings(sourceFile, uri, fileName, options, cache = /* @__PURE__ */ new Map()) {
3259
+ const cached = cache.get(fileName);
3260
+ if (cached) return cached;
168
3261
  const bindings = collectTopLevelBindings(sourceFile, uri);
169
- addImportedBindings(bindings, sourceFile, uri, fileName, options);
3262
+ cache.set(fileName, bindings);
3263
+ addImportedBindings(bindings, sourceFile, uri, fileName, options, cache);
170
3264
  return bindings;
171
3265
  }
172
3266
  function collectTopLevelBindings(sourceFile, uri) {
@@ -198,7 +3292,7 @@ function collectTopLevelBindings(sourceFile, uri) {
198
3292
  }
199
3293
  return bindings;
200
3294
  }
201
- function addImportedBindings(bindings, sourceFile, uri, fileName, options) {
3295
+ function addImportedBindings(bindings, sourceFile, uri, fileName, options, cache) {
202
3296
  const resolver = options.resolver;
203
3297
  if (!resolver) return;
204
3298
  for (const statement of sourceFile.statements) {
@@ -210,9 +3304,9 @@ function addImportedBindings(bindings, sourceFile, uri, fileName, options) {
210
3304
  uri
211
3305
  }, statement.moduleSpecifier.text);
212
3306
  if (!importedDocument) continue;
213
- const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$1(importedDocument.fileName));
3307
+ const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(importedDocument.fileName));
214
3308
  sourceFileUris.set(importedSourceFile, importedDocument.uri);
215
- const importedBindings = collectBindings(importedSourceFile, importedDocument.uri, importedDocument.fileName, options);
3309
+ const importedBindings = collectBindings(importedSourceFile, importedDocument.uri, importedDocument.fileName, options, cache);
216
3310
  for (const element of namedBindings.elements) {
217
3311
  const importedName = element.propertyName?.text ?? element.name.text;
218
3312
  const binding = importedBindings.get(importedName);
@@ -235,11 +3329,20 @@ function findMachineConfigObjectLiteral(sourceFile, bindings) {
235
3329
  visit(sourceFile);
236
3330
  return found;
237
3331
  }
238
- function collectStateLocations(stateObject, parentPath, locations, bindings, staticValues, root) {
239
- const statesObject = getObjectLiteralProperty(stateObject, "states");
3332
+ function collectStateLocations(stateObject, parentPath, locations, statesObjectLocations, bindings, staticValues, root) {
3333
+ const statesObject = resolveObjectLiteralProperty(stateObject, "states", bindings);
240
3334
  if (!statesObject) return;
241
- for (const property of statesObject.properties) {
242
- const resolved = resolveStateProperty(property, bindings, staticValues, root);
3335
+ statesObjectLocations.push({
3336
+ uri: statesObject.uri,
3337
+ path: parentPath,
3338
+ kind: statesObject.kind,
3339
+ symbol: statesObject.symbol,
3340
+ keyRange: statesObject.keyRange,
3341
+ referenceRange: statesObject.referenceRange,
3342
+ range: toSourceRange$1(statesObject.sourceFile, statesObject.object)
3343
+ });
3344
+ for (const property of statesObject.object.properties) {
3345
+ const resolved = resolveStateProperty(property, statesObject.bindings, staticValues, root);
243
3346
  if (!resolved) continue;
244
3347
  const path = [...parentPath, resolved.key];
245
3348
  locations.push({
@@ -251,9 +3354,9 @@ function collectStateLocations(stateObject, parentPath, locations, bindings, sta
251
3354
  referenceRange: resolved.referenceRange,
252
3355
  keySource: resolved.keySource,
253
3356
  keyReplacementText: resolved.keyReplacementText,
254
- range: toSourceRange(resolved.sourceFile, resolved.stateObject)
3357
+ range: toSourceRange$1(resolved.sourceFile, resolved.stateObject)
255
3358
  });
256
- collectStateLocations(resolved.stateObject, path, locations, resolved.bindings, staticValues, root);
3359
+ collectStateLocations(resolved.stateObject, path, locations, statesObjectLocations, resolved.bindings, staticValues, root);
257
3360
  }
258
3361
  }
259
3362
  function resolveStateProperty(property, bindings, staticValues, root) {
@@ -264,8 +3367,8 @@ function resolveStateProperty(property, bindings, staticValues, root) {
264
3367
  key: property.name.text,
265
3368
  kind: binding.kind,
266
3369
  symbol: property.name.text,
267
- keyRange: toSourceRange(property.getSourceFile(), property.name),
268
- referenceRange: toSourceRange(property.getSourceFile(), property.name),
3370
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3371
+ referenceRange: toSourceRange$1(property.getSourceFile(), property.name),
269
3372
  stateObject: binding.node,
270
3373
  sourceFile: binding.sourceFile,
271
3374
  uri: binding.uri,
@@ -279,7 +3382,7 @@ function resolveStateProperty(property, bindings, staticValues, root) {
279
3382
  if (inlineObject) return {
280
3383
  key: resolvedKey.key,
281
3384
  kind: "inline",
282
- keyRange: toSourceRange(property.getSourceFile(), property.name),
3385
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
283
3386
  keySource: resolvedKey.keySource,
284
3387
  keyReplacementText: resolvedKey.keyReplacementText,
285
3388
  stateObject: inlineObject,
@@ -294,8 +3397,8 @@ function resolveStateProperty(property, bindings, staticValues, root) {
294
3397
  key: resolvedKey.key,
295
3398
  kind: binding.kind,
296
3399
  symbol: property.initializer.text,
297
- keyRange: toSourceRange(property.getSourceFile(), property.name),
298
- referenceRange: toSourceRange(property.getSourceFile(), property.initializer),
3400
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3401
+ referenceRange: toSourceRange$1(property.getSourceFile(), property.initializer),
299
3402
  keySource: resolvedKey.keySource,
300
3403
  keyReplacementText: resolvedKey.keyReplacementText,
301
3404
  stateObject: binding.node,
@@ -327,11 +3430,34 @@ function resolveIdentifierObject(expression, bindings) {
327
3430
  const binding = bindings.get(unwrapped.text);
328
3431
  return binding?.kind === "objectReference" ? binding.node : null;
329
3432
  }
330
- function getObjectLiteralProperty(objectLiteral, propertyName) {
3433
+ function resolveObjectLiteralProperty(objectLiteral, propertyName, bindings) {
331
3434
  for (const property of objectLiteral.properties) {
332
3435
  if (!ts$1.isPropertyAssignment(property)) continue;
333
3436
  if (getPropertyNameText$1(property.name) !== propertyName) continue;
334
- return unwrapObjectLiteralExpression(property.initializer);
3437
+ const inlineObject = unwrapObjectLiteralExpression(property.initializer);
3438
+ if (inlineObject) return {
3439
+ object: inlineObject,
3440
+ kind: "inline",
3441
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3442
+ sourceFile: property.getSourceFile(),
3443
+ uri: getSourceFileUri(property.getSourceFile()),
3444
+ bindings
3445
+ };
3446
+ if (ts$1.isIdentifier(property.initializer)) {
3447
+ const binding = bindings.get(property.initializer.text);
3448
+ if (binding?.kind !== "objectReference") return null;
3449
+ return {
3450
+ object: binding.node,
3451
+ kind: binding.kind,
3452
+ symbol: property.initializer.text,
3453
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3454
+ referenceRange: toSourceRange$1(property.getSourceFile(), property.initializer),
3455
+ sourceFile: binding.sourceFile,
3456
+ uri: binding.uri,
3457
+ bindings: binding.bindings
3458
+ };
3459
+ }
3460
+ return null;
335
3461
  }
336
3462
  return null;
337
3463
  }
@@ -383,7 +3509,7 @@ function collectStaticValueReferences(sourceFile, uri, root, staticValues) {
383
3509
  const binding = getStaticValueBindingForExpression(node.expression, staticValues);
384
3510
  if (binding && !isStateKeyComputedPropertyName(node)) references.push({
385
3511
  uri,
386
- range: toSourceRange(sourceFile, node),
3512
+ range: toSourceRange$1(sourceFile, node),
387
3513
  value: binding.value
388
3514
  });
389
3515
  return;
@@ -396,7 +3522,7 @@ function collectStaticValueReferences(sourceFile, uri, root, staticValues) {
396
3522
  if (binding) {
397
3523
  references.push({
398
3524
  uri,
399
- range: toSourceRange(sourceFile, node),
3525
+ range: toSourceRange$1(sourceFile, node),
400
3526
  value: binding.value
401
3527
  });
402
3528
  return;
@@ -478,7 +3604,7 @@ function unwrapExpression$1(expression) {
478
3604
  return current;
479
3605
  }
480
3606
  }
481
- function toSourceRange(sourceFile, node) {
3607
+ function toSourceRange$1(sourceFile, node) {
482
3608
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
483
3609
  const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
484
3610
  return {
@@ -488,7 +3614,7 @@ function toSourceRange(sourceFile, node) {
488
3614
  endChar: end.character
489
3615
  };
490
3616
  }
491
- function getScriptKind$1(fileName) {
3617
+ function getScriptKind$2(fileName) {
492
3618
  if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
493
3619
  if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
494
3620
  if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
@@ -512,7 +3638,7 @@ function parseSourceToMachine(text, document, options = {}) {
512
3638
  range: location.range,
513
3639
  newText: JSON.stringify(location.value)
514
3640
  }));
515
- const stateReplacements = sourceLocations.states.filter((location) => location.path.length === 1 && location.keyRange).flatMap((location) => {
3641
+ const stateReplacements = sourceLocations.states.filter((location) => location.path.length === 1 && location.keyRange && !sourceLocations.statesObject?.referenceRange).flatMap((location) => {
516
3642
  const keyRange = location.keyRange;
517
3643
  const keyReplacement = location.keyReplacementText ? [{
518
3644
  range: keyRange,
@@ -530,7 +3656,93 @@ function parseSourceToMachine(text, document, options = {}) {
530
3656
  newText: `${location.path[0]}: ${stateText}`
531
3657
  }];
532
3658
  });
533
- return applySourceRangeReplacements(text, [...staticValueReplacements, ...stateReplacements].filter(removeOverlappingRanges).sort(compareSourceRangesDescending));
3659
+ const statesObjectReplacements = sourceLocations.statesObject?.referenceRange && sourceLocations.statesObject.kind !== "inline" ? (() => {
3660
+ const statesText = readHydratedStatesObject(sourceLocations.statesObject.uri, sourceLocations.statesObject.range, sourceLocations.states, options.resolver);
3661
+ return statesText ? [{
3662
+ range: sourceLocations.statesObject.referenceRange,
3663
+ newText: statesText
3664
+ }] : [];
3665
+ })() : [];
3666
+ const importReplacements = collectHydratedImportReplacements(text, document, new Set([...sourceLocations.states.filter((location) => location.path.length === 1 && location.referenceRange).map((location) => location.symbol).filter((symbol) => typeof symbol === "string"), ...sourceLocations.statesObject?.symbol ? [sourceLocations.statesObject.symbol] : []]));
3667
+ const hydratedText = applySourceRangeReplacements(text, [
3668
+ ...staticValueReplacements,
3669
+ ...stateReplacements,
3670
+ ...statesObjectReplacements,
3671
+ ...importReplacements
3672
+ ].filter(removeOverlappingRanges).sort(compareSourceRangesDescending));
3673
+ if (sourceLocations.profile === "xstate-v6-alpha") return `import { createMachine } from "xstate";
3674
+
3675
+ export const machine = createMachine(${serializeJS(xstateSourceToConfig(hydratedText, { profile: "xstate-v6-alpha" }), 0)});
3676
+ `;
3677
+ return hydratedText;
3678
+ }
3679
+ function readHydratedStatesObject(uri, range, states, resolver) {
3680
+ if (!resolver) return null;
3681
+ const document = resolver.read(uri);
3682
+ if (!document) return null;
3683
+ const replacements = states.filter((state) => state.path.length === 1 && state.referenceRange).flatMap((state) => {
3684
+ const stateText = readSourceRange(state.uri, state.range, resolver);
3685
+ if (!stateText || !state.referenceRange) return [];
3686
+ const isShorthand = state.keyRange && areSourceRangesEqual(state.referenceRange, state.keyRange);
3687
+ return [{
3688
+ range: state.referenceRange,
3689
+ newText: isShorthand ? `${state.path[0]}: ${stateText}` : stateText
3690
+ }];
3691
+ });
3692
+ return applySourceRangeReplacementsToSlice(document.text, range, replacements);
3693
+ }
3694
+ function applySourceRangeReplacementsToSlice(text, range, replacements) {
3695
+ const sliceStart = offsetAtRangePosition(text, range.startLine, range.startChar);
3696
+ const sliceEnd = offsetAtRangePosition(text, range.endLine, range.endChar);
3697
+ let updated = text.slice(sliceStart, sliceEnd);
3698
+ for (const replacement of replacements.sort(compareSourceRangesDescending)) {
3699
+ const start = offsetAtRangePosition(text, replacement.range.startLine, replacement.range.startChar);
3700
+ const end = offsetAtRangePosition(text, replacement.range.endLine, replacement.range.endChar);
3701
+ if (start < sliceStart || end > sliceEnd) continue;
3702
+ const localStart = start - sliceStart;
3703
+ const localEnd = end - sliceStart;
3704
+ updated = `${updated.slice(0, localStart)}${replacement.newText}${updated.slice(localEnd)}`;
3705
+ }
3706
+ return updated;
3707
+ }
3708
+ function collectHydratedImportReplacements(text, document, hydratedSymbols) {
3709
+ if (hydratedSymbols.size === 0) return [];
3710
+ const sourceFile = ts$1.createSourceFile(document.fileName, text, ts$1.ScriptTarget.Latest, true, getScriptKind$1(document.fileName));
3711
+ const replacements = [];
3712
+ for (const statement of sourceFile.statements) {
3713
+ if (!ts$1.isImportDeclaration(statement) || !ts$1.isStringLiteral(statement.moduleSpecifier)) continue;
3714
+ if (!statement.moduleSpecifier.text.startsWith(".")) continue;
3715
+ const namedBindings = statement.importClause?.namedBindings;
3716
+ if (!namedBindings || !ts$1.isNamedImports(namedBindings)) continue;
3717
+ const importedNames = namedBindings.elements.map((element) => element.name.text);
3718
+ if (importedNames.length === 0 || !importedNames.every((name) => hydratedSymbols.has(name))) continue;
3719
+ replacements.push({
3720
+ range: toSourceRange(sourceFile, statement, { includeTrailingNewline: true }),
3721
+ newText: ""
3722
+ });
3723
+ }
3724
+ return replacements;
3725
+ }
3726
+ function toSourceRange(sourceFile, node, options = {}) {
3727
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3728
+ let endOffset = node.getEnd();
3729
+ if (options.includeTrailingNewline) {
3730
+ const match = sourceFile.text.slice(endOffset).match(/^\r?\n/);
3731
+ if (match) endOffset += match[0].length;
3732
+ }
3733
+ const end = sourceFile.getLineAndCharacterOfPosition(endOffset);
3734
+ return {
3735
+ startLine: start.line,
3736
+ startChar: start.character,
3737
+ endLine: end.line,
3738
+ endChar: end.character
3739
+ };
3740
+ }
3741
+ function getScriptKind$1(fileName) {
3742
+ if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
3743
+ if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
3744
+ if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
3745
+ return ts$1.ScriptKind.TS;
534
3746
  }
535
3747
  function removeOverlappingRanges(replacement, index, replacements) {
536
3748
  return !replacements.some((candidate, candidateIndex) => {