@statelyai/sdk 0.10.1 → 0.10.2

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-DOaLOySy.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,3078 @@ 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
+ const result = {};
843
+ if (meta) {
844
+ for (const [k, v] of Object.entries(meta)) if (v !== void 0 && v !== null) result[k] = v;
845
+ }
846
+ if (color) result["@statelyai.color"] = color;
847
+ return Object.keys(result).length > 0 ? result : void 0;
848
+ }
849
+ /** Format event param — supports { type: string } (new) and plain string (legacy) */
850
+ function formatEventParam(event) {
851
+ if (event && typeof event === "object" && "type" in event) return formatInlineObject(event);
852
+ if (typeof event === "string") return `{ type: '${event}' }`;
853
+ return "undefined";
854
+ }
855
+ function formatStringParam(value) {
856
+ return typeof value === "string" ? `'${value}'` : "undefined";
857
+ }
858
+ function formatInlineObject(value) {
859
+ return `{ ${Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => `${formatObjectKey(key)}: ${serializeJS(item)}`).join(", ")} }`;
860
+ }
861
+ function formatObjectKey(key) {
862
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
863
+ }
864
+ const eventParamSchema = union([string(), object({ type: string() }).catchall(unknown())]);
865
+ const delayParamSchema = union([number(), string()]);
866
+ const builtInActionParamSchemas = {
867
+ "xstate.raise": object({
868
+ event: eventParamSchema.optional(),
869
+ id: string().optional(),
870
+ delay: delayParamSchema.optional()
871
+ }),
872
+ "xstate.sendTo": object({
873
+ to: string().optional(),
874
+ event: eventParamSchema.optional(),
875
+ id: string().optional(),
876
+ delay: delayParamSchema.optional()
877
+ }),
878
+ "xstate.cancel": object({ sendId: string().optional() }),
879
+ "xstate.emit": object({ event: eventParamSchema.optional() }),
880
+ "xstate.spawnChild": object({
881
+ src: string().optional(),
882
+ id: string().optional(),
883
+ systemId: string().optional()
884
+ }),
885
+ "xstate.stopChild": object({ actorRef: string().optional() }),
886
+ "xstate.log": object({ label: string().optional() }),
887
+ "xstate.assign": object({ assignment: record(string(), unknown()).optional() }).catchall(unknown())
888
+ };
889
+ const XSTATE_BUILT_IN_ACTIONS = {
890
+ "xstate.raise": {
891
+ package: "xstate",
892
+ version: "^5",
893
+ import: "raise",
894
+ importKind: "named",
895
+ paramsSchema: builtInActionParamSchemas["xstate.raise"],
896
+ emit(params) {
897
+ const eventStr = formatEventParam(params?.event);
898
+ const opts = [];
899
+ if (params?.id) opts.push(`id: '${params.id}'`);
900
+ if (params?.delay != null) opts.push(`delay: ${JSON.stringify(params.delay)}`);
901
+ return `raise(${eventStr}${opts.length > 0 ? `, { ${opts.join(", ")} }` : ""})`;
902
+ }
903
+ },
904
+ "xstate.sendTo": {
905
+ package: "xstate",
906
+ version: "^5",
907
+ import: "sendTo",
908
+ importKind: "named",
909
+ paramsSchema: builtInActionParamSchemas["xstate.sendTo"],
910
+ emit(params) {
911
+ const to = formatStringParam(params?.to);
912
+ const eventStr = formatEventParam(params?.event);
913
+ const opts = [];
914
+ if (params?.id) opts.push(`id: '${params.id}'`);
915
+ if (params?.delay != null) opts.push(`delay: ${JSON.stringify(params.delay)}`);
916
+ return `sendTo(${to}, ${eventStr}${opts.length > 0 ? `, { ${opts.join(", ")} }` : ""})`;
917
+ }
918
+ },
919
+ "xstate.cancel": {
920
+ package: "xstate",
921
+ version: "^5",
922
+ import: "cancel",
923
+ importKind: "named",
924
+ paramsSchema: builtInActionParamSchemas["xstate.cancel"],
925
+ emit(params) {
926
+ return `cancel(${formatStringParam(params?.sendId)})`;
927
+ }
928
+ },
929
+ "xstate.emit": {
930
+ package: "xstate",
931
+ version: "^5",
932
+ import: "emit",
933
+ importKind: "named",
934
+ paramsSchema: builtInActionParamSchemas["xstate.emit"],
935
+ emit(params) {
936
+ return `emit(${formatEventParam(params?.event)})`;
937
+ }
938
+ },
939
+ "xstate.spawnChild": {
940
+ package: "xstate",
941
+ version: "^5",
942
+ import: "spawnChild",
943
+ importKind: "named",
944
+ paramsSchema: builtInActionParamSchemas["xstate.spawnChild"],
945
+ emit(params) {
946
+ const src = formatStringParam(params?.src);
947
+ const opts = [];
948
+ if (params?.id) opts.push(`id: '${params.id}'`);
949
+ if (params?.systemId) opts.push(`systemId: '${params.systemId}'`);
950
+ return `spawnChild(${src}${opts.length > 0 ? `, { ${opts.join(", ")} }` : ""})`;
951
+ }
952
+ },
953
+ "xstate.stopChild": {
954
+ package: "xstate",
955
+ version: "^5",
956
+ import: "stopChild",
957
+ importKind: "named",
958
+ paramsSchema: builtInActionParamSchemas["xstate.stopChild"],
959
+ emit(params) {
960
+ return `stopChild(${formatStringParam(params?.actorRef)})`;
961
+ }
962
+ },
963
+ "xstate.log": {
964
+ package: "xstate",
965
+ version: "^5",
966
+ import: "log",
967
+ importKind: "named",
968
+ paramsSchema: builtInActionParamSchemas["xstate.log"],
969
+ emit(params) {
970
+ return `log(${(params?.label ? `'${params.label}'` : void 0) ?? ""})`;
971
+ }
972
+ },
973
+ "xstate.assign": {
974
+ package: "xstate",
975
+ version: "^5",
976
+ import: "assign",
977
+ importKind: "named",
978
+ paramsSchema: builtInActionParamSchemas["xstate.assign"],
979
+ emit(params) {
980
+ const assignments = params?.assignment && typeof params.assignment === "object" && !Array.isArray(params.assignment) ? params.assignment : params;
981
+ const entries = Object.entries(assignments ?? {}).filter(([k, v]) => k && v !== void 0);
982
+ if (entries.length === 0) return "assign({})";
983
+ return `assign({ ${entries.map(([k, v]) => `${JSON.stringify(k)}: ${formatAssignValue(v)}`).join(", ")} })`;
984
+ }
985
+ }
986
+ };
987
+ function isBuiltInActionType(type) {
988
+ return type in XSTATE_BUILT_IN_ACTIONS;
989
+ }
990
+ function emitBuiltInAction(type, params) {
991
+ return XSTATE_BUILT_IN_ACTIONS[type].emit(params);
992
+ }
993
+ function actionCodeExpression(expr, source) {
994
+ return {
995
+ "@type": "code",
996
+ lang: "ts",
997
+ expr,
998
+ imports: [{
999
+ package: source.package,
1000
+ version: source.version,
1001
+ import: source.import,
1002
+ importKind: source.importKind
1003
+ }]
1004
+ };
1005
+ }
1006
+ function inlineCodeExpression(expr) {
1007
+ return {
1008
+ "@type": "code",
1009
+ lang: "ts",
1010
+ expr: stripExportDefault(expr)
1011
+ };
1012
+ }
1013
+ /**
1014
+ * Serialize an edge guard for a machine config. Codegen emits inline guards as
1015
+ * a code-expression directive so the source round-trips. The simulation passes
1016
+ * `asTypeRef` to get a plain `{ type }` reference instead (it stubs guards by
1017
+ * type and can't evaluate the source) — see MachineConfigOptions.
1018
+ */
1019
+ function serializeGuard(guard, asTypeRef) {
1020
+ if (asTypeRef) return guard.params ? {
1021
+ type: guard.type,
1022
+ params: guard.params
1023
+ } : { type: guard.type };
1024
+ return guard.code ? inlineCodeExpression(guard.code) : guard;
1025
+ }
1026
+ /**
1027
+ * Converts a built-in xstate ActionItem to a code expression directive,
1028
+ * or returns a plain { type, params } object for user-defined actions.
1029
+ */
1030
+ function serializeActionItem(action) {
1031
+ const { type, params } = action;
1032
+ const exprCode = type === "xstate.expr" && typeof params?.code === "string" ? params.code : void 0;
1033
+ if (isBuiltInActionType(type)) {
1034
+ const builtInAction = XSTATE_BUILT_IN_ACTIONS[type];
1035
+ return actionCodeExpression(emitBuiltInAction(type, params), builtInAction);
1036
+ }
1037
+ if (exprCode) return inlineCodeExpression(exprCode);
1038
+ if (!params || Object.keys(params).length === 0) return { type };
1039
+ return {
1040
+ type,
1041
+ params
1042
+ };
1043
+ }
1044
+ function formatAssignValue(value) {
1045
+ if (typeof value !== "string") return JSON.stringify(value);
1046
+ const templateExpression = value.match(/^\{\{([\s\S]*)\}\}$/);
1047
+ if (templateExpression) return templateExpression[1].trim();
1048
+ return JSON.stringify(value);
1049
+ }
1050
+ /** Normalize tag to string — handles both `string` and `{ name: string }` */
1051
+ function tagToString(tag) {
1052
+ return typeof tag === "string" ? tag : tag.name;
1053
+ }
1054
+ function graphToMachineConfig(graph, options = {}) {
1055
+ const { showDescriptions = true, showMeta = true, inlineGuardsAsTypeRef = false } = options;
1056
+ const config = {};
1057
+ function getNodeConfig(node) {
1058
+ const nodeConfig = node.parentId ? {} : config;
1059
+ const resolvedNodeId = getEmittedNodeId(graph, node);
1060
+ const initialNode = node.data.initialId ? graph.nodes.find((n) => n.id === node.data.initialId) : null;
1061
+ const nodeType = [
1062
+ "final",
1063
+ "history",
1064
+ "parallel"
1065
+ ].includes(node.data.type) ? node.data.type : void 0;
1066
+ const tags = node.data.tags;
1067
+ Object.assign(nodeConfig, {
1068
+ id: resolvedNodeId,
1069
+ type: nodeType === "normal" ? void 0 : nodeType ?? void 0,
1070
+ initial: initialNode?.data.key,
1071
+ ...node.data.entry?.length ? { entry: node.data.entry.map(serializeActionItem) } : void 0,
1072
+ ...node.data.exit?.length ? { exit: node.data.exit.map(serializeActionItem) } : void 0,
1073
+ ...node.data.invokes?.length ? { invoke: node.data.invokes.map((inv) => ({
1074
+ src: inv.src,
1075
+ id: inv.invocationId ?? inv.id,
1076
+ ...inv.input ? { input: inv.input } : void 0,
1077
+ ...inv.output ? { output: inv.output } : void 0
1078
+ })) } : void 0,
1079
+ ...tags?.length ? { tags: tags.map(tagToString) } : void 0,
1080
+ ...showDescriptions && node.data.description ? { description: node.data.description } : void 0,
1081
+ ...showMeta && node.data.meta && Object.keys(node.data.meta).length > 0 ? { meta: node.data.meta } : void 0,
1082
+ ...node.data.history ? { history: node.data.history } : void 0
1083
+ });
1084
+ const childNodes = graph.nodes.filter((n) => n.parentId === node.id && !n.data.temp);
1085
+ const edges = graph.edges.filter((edge) => edge.sourceId === node.id && !edge.data.temp);
1086
+ if (edges.length > 0) {
1087
+ const on = {};
1088
+ const after = {};
1089
+ const alwaysArr = [];
1090
+ const onDone = {};
1091
+ const invokeMap = {};
1092
+ for (const edge of edges) {
1093
+ const targetNode = graph.nodes.find((n) => n.id === edge.targetId);
1094
+ const resolvedTarget = targetNode ? resolveTransitionTarget(graph, node, targetNode) : "";
1095
+ const type = edge.data.eventType ?? "";
1096
+ const transitionMeta = showMeta ? buildMeta(edge.data.meta, edge.data.color) : void 0;
1097
+ const transitionObject = {
1098
+ target: edge.data.transitionType === "targetless" ? void 0 : `${resolvedTarget}`,
1099
+ ...edge.data.transitionType === "reenter" ? { reenter: true } : void 0,
1100
+ guard: edge.data.guard ? serializeGuard(edge.data.guard, inlineGuardsAsTypeRef) : void 0,
1101
+ actions: edge.data.actions?.length ? edge.data.actions.map(serializeActionItem) : void 0,
1102
+ description: edge.data.description ?? void 0,
1103
+ ...transitionMeta ? { meta: transitionMeta } : void 0
1104
+ };
1105
+ const pushOn = (key) => {
1106
+ if (!on[key]) on[key] = [];
1107
+ on[key].push(transitionObject);
1108
+ };
1109
+ const nestInvoke = (blockId, slot) => {
1110
+ invokeMap[blockId] = {
1111
+ ...invokeMap[blockId],
1112
+ [slot]: (invokeMap[blockId]?.[slot] ?? []).concat(transitionObject)
1113
+ };
1114
+ };
1115
+ const ref = parseStatelyRef(type);
1116
+ if (ref) if (ref.kind === "invokeDone") nestInvoke(ref.blockId, "onDone");
1117
+ else if (ref.kind === "invokeError") nestInvoke(ref.blockId, "onError");
1118
+ else if (ref.kind === "invokeSnapshot") nestInvoke(ref.blockId, "onSnapshot");
1119
+ else if (ref.kind === "stateDone") {
1120
+ if (!onDone[type]) onDone[type] = [];
1121
+ onDone[type].push(transitionObject);
1122
+ } else pushOn(`xstate.${ref.kind === "actionDone" ? "done" : "error"}.actor.${resolveSpawnActorId$1(node, ref.blockId)}`);
1123
+ else if (type === "") alwaysArr.push(transitionObject);
1124
+ else if (type.startsWith("xstate.after.")) {
1125
+ const delayKey = type.slice(13).split(".")[0];
1126
+ if (!after[delayKey]) after[delayKey] = [];
1127
+ after[delayKey].push(transitionObject);
1128
+ } else if (type === "*") pushOn("*");
1129
+ else if (type.startsWith("xstate.done.state")) {
1130
+ if (!onDone[type]) onDone[type] = [];
1131
+ onDone[type].push(transitionObject);
1132
+ } else if (type.startsWith("xstate.done.actor.") || type.startsWith("xstate.error.actor.") || type.startsWith("xstate.snapshot.actor.")) {
1133
+ const verb = type.startsWith("xstate.done.actor.") ? "onDone" : type.startsWith("xstate.error.actor.") ? "onError" : "onSnapshot";
1134
+ const invoke = findInvokeByActorId(node, type.slice(type.lastIndexOf(".actor.") + 7));
1135
+ if (invoke) nestInvoke(invoke.id, verb);
1136
+ else pushOn(type);
1137
+ } else pushOn(type);
1138
+ }
1139
+ if (Object.keys(on).length > 0) nodeConfig.on = singleOrArrayRecord(on);
1140
+ if (Object.keys(after).length > 0) nodeConfig.after = singleOrArrayRecord(after);
1141
+ if (alwaysArr.length > 0) nodeConfig.always = singleOrArray(alwaysArr);
1142
+ if (Object.keys(onDone).length > 0) nodeConfig.onDone = singleOrArray(Object.values(onDone).flat());
1143
+ if (Object.keys(invokeMap).length > 0 && Array.isArray(nodeConfig.invoke)) nodeConfig.invoke = nodeConfig.invoke.map((inv, index) => {
1144
+ const mapped = invokeMap[node.data.invokes?.[index]?.id ?? inv.id];
1145
+ if (!mapped) return inv;
1146
+ return {
1147
+ ...inv,
1148
+ ...mapped.onDone ? { onDone: singleOrArray(mapped.onDone) } : void 0,
1149
+ ...mapped.onError ? { onError: singleOrArray(mapped.onError) } : void 0,
1150
+ ...mapped.onSnapshot ? { onSnapshot: singleOrArray(mapped.onSnapshot) } : void 0
1151
+ };
1152
+ });
1153
+ }
1154
+ if (Array.isArray(nodeConfig.invoke)) nodeConfig.invoke = nodeConfig.invoke.map((inv) => {
1155
+ if (inv.id && !isAutoGeneratedId(inv.id)) return inv;
1156
+ const { id: _id, ...rest } = inv;
1157
+ return rest;
1158
+ });
1159
+ if (childNodes.length > 0) {
1160
+ nodeConfig.states = {};
1161
+ for (const childState of childNodes) nodeConfig.states[childState.data.key] = getNodeConfig(childState);
1162
+ }
1163
+ return nodeConfig;
1164
+ }
1165
+ const rootNode = graph.nodes.find((node) => node.data.parentId === null || node.parentId == null);
1166
+ if (!rootNode) throw new Error("No root node found");
1167
+ getNodeConfig(rootNode);
1168
+ if (graph.data.context !== void 0 && graph.data.context !== null) config.context = graph.data.context;
1169
+ return deepSimplify(config);
1170
+ }
1171
+ const REF_PREFIX = {
1172
+ invokeDone: "@statelyai.invoke.done",
1173
+ invokeError: "@statelyai.invoke.error",
1174
+ invokeSnapshot: "@statelyai.invoke.snapshot",
1175
+ actionDone: "@statelyai.action.done",
1176
+ actionError: "@statelyai.action.error",
1177
+ stateDone: "@statelyai.state.done"
1178
+ };
1179
+ function parseRef(eventType) {
1180
+ if (eventType.startsWith("@stately.")) eventType = `@statelyai${eventType.slice(8)}`;
1181
+ if (!eventType.startsWith("@statelyai.")) return null;
1182
+ for (const kind of Object.keys(REF_PREFIX)) {
1183
+ const prefix = REF_PREFIX[kind];
1184
+ if (eventType === prefix) return {
1185
+ kind,
1186
+ blockId: ""
1187
+ };
1188
+ if (eventType.startsWith(`${prefix}.`)) return {
1189
+ kind,
1190
+ blockId: eventType.slice(prefix.length + 1)
1191
+ };
1192
+ }
1193
+ return null;
1194
+ }
1195
+ function getSourceNode(graph, edge) {
1196
+ return graph.nodes.find((node) => node.id === edge.sourceId);
1197
+ }
1198
+ function resolveSpawnActorId(sourceNode, actionId) {
1199
+ const actorId = [...sourceNode?.data.entry ?? [], ...sourceNode?.data.exit ?? []].find((action) => action.id === actionId)?.params?.["id"];
1200
+ return typeof actorId === "string" && actorId ? actorId : actionId;
1201
+ }
1202
+ function getTransitionRuntimeEventType(graph, edge) {
1203
+ const eventType = edge.data.eventType;
1204
+ const sourceNode = getSourceNode(graph, edge);
1205
+ if (eventType.startsWith("xstate.after.")) {
1206
+ const delayKey = eventType.slice(13).split(".")[0];
1207
+ return sourceNode ? `xstate.after.${delayKey}.${getResolvedNodeId(graph, sourceNode)}` : eventType;
1208
+ }
1209
+ const ref = parseRef(eventType);
1210
+ if (!ref) return eventType;
1211
+ if (ref.kind === "stateDone") {
1212
+ const node = graph.nodes.find((candidate) => candidate.id === ref.blockId) ?? sourceNode;
1213
+ return `xstate.done.state.${node ? getResolvedNodeId(graph, node) : ref.blockId}`;
1214
+ }
1215
+ if (ref.kind === "invokeDone" || ref.kind === "invokeError" || ref.kind === "invokeSnapshot") {
1216
+ const invokes = sourceNode?.data.invokes ?? [];
1217
+ const index = invokes.findIndex((invoke) => invoke.id === ref.blockId);
1218
+ const actorId = (index >= 0 ? invokes[index] : void 0)?.invocationId || (sourceNode && index >= 0 ? `${index}.${getResolvedNodeId(graph, sourceNode)}` : ref.blockId);
1219
+ return `xstate.${ref.kind === "invokeDone" ? "done" : ref.kind === "invokeError" ? "error" : "snapshot"}.actor.${actorId}`;
1220
+ }
1221
+ return `xstate.${ref.kind === "actionDone" ? "done" : "error"}.actor.${resolveSpawnActorId(sourceNode, ref.blockId)}`;
1222
+ }
1223
+ function transitionMatchesRuntimeEvent(graph, edge, eventType) {
1224
+ return getTransitionRuntimeEventType(graph, edge) === eventType;
1225
+ }
1226
+ /**
1227
+ * Map an XState state node (identified by its array of keys from the root) to
1228
+ * the corresponding graph node id. The runtime snapshot's `_nodes[].id` are
1229
+ * XState resolved ids (delimited paths), which do not match the graph's opaque
1230
+ * node ids; consumers (highlighting, edge lookup) need graph node ids.
1231
+ */
1232
+ function getGraphNodeIdForPath(graph, path) {
1233
+ const root = graph.nodes.find((n) => n.parentId === null);
1234
+ if (!root) return null;
1235
+ if (path.length === 0) return root.id;
1236
+ let parentId = root.id;
1237
+ let nodeId = null;
1238
+ for (const key of path) {
1239
+ const child = graph.nodes.find((n) => n.parentId === parentId && n.data.key === key);
1240
+ if (!child) return null;
1241
+ nodeId = child.id;
1242
+ parentId = child.id;
1243
+ }
1244
+ return nodeId;
1245
+ }
1246
+ function getStateIds(graph, snapshot) {
1247
+ const nodes = snapshot._nodes;
1248
+ if (!nodes) return [];
1249
+ return nodes.map((n) => getGraphNodeIdForPath(graph, n.path)).filter((id) => id !== null);
1250
+ }
1251
+ function getLeafStateIds(graph, snapshot) {
1252
+ const nodes = snapshot._nodes;
1253
+ if (!nodes) return [];
1254
+ const leaves = nodes.filter((n) => n.path.length > 0 && (n.type === "atomic" || n.type === "final"));
1255
+ return (leaves.length > 0 ? leaves : nodes).map((n) => getGraphNodeIdForPath(graph, n.path)).filter((id) => id !== null);
1256
+ }
1257
+ function getStatePath(snapshot) {
1258
+ const nodes = snapshot._nodes;
1259
+ if (!nodes?.length) return "(initial)";
1260
+ const withPath = nodes.filter((n) => n.path.length > 0);
1261
+ if (!withPath.length) return "(initial)";
1262
+ const leaves = withPath.filter((n) => n.type === "atomic" || n.type === "final");
1263
+ return (leaves.length > 0 ? leaves : withPath).map((n) => n.path.join(".")).join(", ");
1264
+ }
1265
+ function findEdgeId(graph, sourceStateIds, event) {
1266
+ const guardType = event["@xstate.guard"];
1267
+ for (const sourceId of sourceStateIds) {
1268
+ const edge = graph.edges.find((candidate) => candidate.sourceId === sourceId && transitionMatchesRuntimeEvent(graph, candidate, event.type) && (candidate.data.guard?.type ?? null) === (guardType ?? null));
1269
+ if (edge) return edge.id;
1270
+ }
1271
+ for (const sourceId of sourceStateIds) {
1272
+ const edge = graph.edges.find((candidate) => candidate.sourceId === sourceId && transitionMatchesRuntimeEvent(graph, candidate, event.type));
1273
+ if (edge) return edge.id;
1274
+ }
1275
+ return null;
1276
+ }
1277
+ function formatEventLabel(event) {
1278
+ if (event.type === "") return "always";
1279
+ if (event.type.startsWith("xstate.after.")) return `after ${event.type.slice(13).split(".")[0]}`;
1280
+ const guardType = event["@xstate.guard"];
1281
+ if (guardType) return `${event.type} [${guardType}]`;
1282
+ return event.type;
1283
+ }
1284
+ function formatEdgeLabel(graph, edgeId) {
1285
+ const edge = graph.edges.find((candidate) => candidate.id === edgeId);
1286
+ if (!edge) return null;
1287
+ const baseLabel = formatEventLabel({ type: edge.data.eventType });
1288
+ const guardType = edge.data.guard?.type;
1289
+ if (guardType) return `${baseLabel} [${guardType}]`;
1290
+ return baseLabel;
1291
+ }
1292
+ function getFallbackTransitionLabels(graph, finalStateIds) {
1293
+ return getFallbackEventlessEdges(graph, finalStateIds).map((edge) => formatEdgeLabel(graph, edge.id)).filter((label) => !!label);
1294
+ }
1295
+ function getFallbackEventlessEdges(graph, finalStateIds) {
1296
+ const finalLeafIdSet = new Set(finalStateIds);
1297
+ return graph.edges.filter((edge) => finalLeafIdSet.has(edge.targetId) && edge.data.eventType === "");
1298
+ }
1299
+ function getPathName(finalStatePath, finalStateIds, steps, graph) {
1300
+ let transitionLabels = steps.map((step) => {
1301
+ if (step.edgeId) return formatEdgeLabel(graph, step.edgeId);
1302
+ if (step.eventType && step.eventType !== "xstate.init") return formatEventLabel({ type: step.eventType });
1303
+ return null;
1304
+ }).filter((label) => !!label);
1305
+ if (transitionLabels.length === 0) transitionLabels = getFallbackTransitionLabels(graph, finalStateIds);
1306
+ if (transitionLabels.length === 0) return {
1307
+ name: finalStatePath,
1308
+ viaLabel: null
1309
+ };
1310
+ return {
1311
+ name: finalStatePath,
1312
+ viaLabel: `via ${transitionLabels.join(" -> ")}`
1313
+ };
1314
+ }
1315
+ function convertPath(path, graph, index) {
1316
+ const steps = [];
1317
+ const firstStep = path.steps[0];
1318
+ if (firstStep) {
1319
+ const allIds = getStateIds(graph, firstStep.state);
1320
+ steps.push({
1321
+ stateIds: getLeafStateIds(graph, firstStep.state),
1322
+ statePath: getStatePath(firstStep.state),
1323
+ eventType: firstStep.event.type,
1324
+ edgeId: findEdgeId(graph, allIds, firstStep.event)
1325
+ });
1326
+ }
1327
+ for (let i = 1; i < path.steps.length; i++) {
1328
+ const prevStep = path.steps[i - 1];
1329
+ const step = path.steps[i];
1330
+ const prevStateIds = getStateIds(graph, prevStep.state);
1331
+ steps.push({
1332
+ stateIds: getLeafStateIds(graph, step.state),
1333
+ statePath: getStatePath(step.state),
1334
+ eventType: step.event.type,
1335
+ edgeId: findEdgeId(graph, prevStateIds, step.event)
1336
+ });
1337
+ }
1338
+ const finalStatePath = getStatePath(path.state);
1339
+ const finalStateIds = getLeafStateIds(graph, path.state);
1340
+ const title = getPathName(finalStatePath, finalStateIds, steps, graph);
1341
+ return {
1342
+ id: `path-${index}`,
1343
+ name: title.name,
1344
+ viaLabel: title.viaLabel,
1345
+ steps,
1346
+ finalStatePath,
1347
+ finalStateIds
1348
+ };
1349
+ }
1350
+ function serializeMachineTransition(_state, event, prevState) {
1351
+ if (!event) return "";
1352
+ const prevStateString = prevState ? ` from ${JSON.stringify(prevState.value)}` : "";
1353
+ return ` via ${JSON.stringify(event)}${prevStateString}`;
1354
+ }
1355
+ function getGraphPathSimImplementations(graph, guardState) {
1356
+ const guards = {};
1357
+ for (const edge of graph.edges) if (edge.data.guard) {
1358
+ const guardType = edge.data.guard.type;
1359
+ if (guardState) guards[guardType] = () => guardState[guardType] ?? false;
1360
+ else guards[guardType] = ({ event }) => {
1361
+ if (event["@xstate.guard"] === guardType) return true;
1362
+ return false;
1363
+ };
1364
+ }
1365
+ return { guards };
1366
+ }
1367
+ function getTraversalEvents(state, graph) {
1368
+ const events = [];
1369
+ for (const edge of graph.edges) {
1370
+ const eventType = getTransitionRuntimeEventType(graph, edge);
1371
+ const event = { type: eventType };
1372
+ if (edge.data.guard?.type) event["@xstate.guard"] = edge.data.guard.type;
1373
+ if (eventType && !state.can(event)) continue;
1374
+ events.push(event);
1375
+ }
1376
+ const seen = /* @__PURE__ */ new Set();
1377
+ return events.filter((event) => {
1378
+ const key = JSON.stringify(event);
1379
+ if (seen.has(key)) return false;
1380
+ seen.add(key);
1381
+ return true;
1382
+ });
1383
+ }
1384
+ function getStepKey(step) {
1385
+ return JSON.stringify({
1386
+ stateIds: step.stateIds,
1387
+ statePath: step.statePath,
1388
+ eventType: step.eventType,
1389
+ edgeId: step.edgeId
1390
+ });
1391
+ }
1392
+ function getLeafGraphNodeIds(graph) {
1393
+ const activeNodes = graph.nodes.filter((node) => !node.data.temp);
1394
+ const parentIds = new Set(activeNodes.map((node) => node.parentId).filter(Boolean));
1395
+ return activeNodes.filter((node) => !parentIds.has(node.id)).map((node) => node.id);
1396
+ }
1397
+ function getCoverage(graph, paths) {
1398
+ const leafNodeIds = new Set(getLeafGraphNodeIds(graph));
1399
+ const coveredStateIds = /* @__PURE__ */ new Set();
1400
+ const coveredEdgeIds = /* @__PURE__ */ new Set();
1401
+ for (const path of paths) {
1402
+ let pathHasExplicitEdge = false;
1403
+ for (const step of path.steps) {
1404
+ for (const stateId of step.stateIds) if (leafNodeIds.has(stateId)) coveredStateIds.add(stateId);
1405
+ if (step.edgeId) {
1406
+ pathHasExplicitEdge = true;
1407
+ coveredEdgeIds.add(step.edgeId);
1408
+ }
1409
+ }
1410
+ for (const finalStateId of path.finalStateIds) if (leafNodeIds.has(finalStateId)) coveredStateIds.add(finalStateId);
1411
+ if (!pathHasExplicitEdge) for (const edge of getFallbackEventlessEdges(graph, path.finalStateIds)) coveredEdgeIds.add(edge.id);
1412
+ }
1413
+ return {
1414
+ states: {
1415
+ covered: coveredStateIds.size,
1416
+ total: leafNodeIds.size
1417
+ },
1418
+ transitions: {
1419
+ covered: coveredEdgeIds.size,
1420
+ total: graph.edges.filter((edge) => !edge.data.temp).length
1421
+ }
1422
+ };
1423
+ }
1424
+ function deduplicateGeneratedPaths(paths) {
1425
+ const sorted = [...paths].sort((a, b) => b.steps.length - a.steps.length);
1426
+ const unique = [];
1427
+ for (const path of sorted) if (!unique.some((existing) => {
1428
+ if (path.steps.length >= existing.steps.length) return false;
1429
+ return path.steps.every((step, index) => getStepKey(step) === getStepKey(existing.steps[index]));
1430
+ })) unique.push(path);
1431
+ return unique;
1432
+ }
1433
+ function generateGraphPathsData(graph, options) {
1434
+ const machineConfig = graphToMachineConfig(graph);
1435
+ const implementations = getGraphPathSimImplementations(graph);
1436
+ const machine = createMachine(machineConfig).provide(implementations);
1437
+ let converted = (options.strategy === "shortest" ? getShortestPaths : getSimplePaths)(machine, {
1438
+ events: (state) => getTraversalEvents(state, graph),
1439
+ serializeState: (state, event, prevState) => {
1440
+ const serializedState = JSON.stringify(state.value);
1441
+ if (!options.preferTransitionCoverage) return serializedState;
1442
+ return `${serializedState}|${serializeMachineTransition(state, event, prevState)}`;
1443
+ }
1444
+ }).map((path, index) => convertPath(path, graph, index));
1445
+ if (options.reduceDuplicates) converted = deduplicateGeneratedPaths(converted);
1446
+ if (options.limit) converted = converted.slice(0, options.limit);
1447
+ return {
1448
+ paths: converted,
1449
+ coverage: getCoverage(graph, converted)
1450
+ };
1451
+ }
1452
+ function toArray(value) {
1453
+ return Array.isArray(value) ? value : value === void 0 ? [] : [value];
1454
+ }
1455
+ function actionItems(value) {
1456
+ return toArray(value).map((item, index) => {
1457
+ if (typeof item === "string") return {
1458
+ id: item,
1459
+ type: item
1460
+ };
1461
+ if (item && typeof item === "object" && "type" in item) {
1462
+ const action = item;
1463
+ if (typeof action.type === "string") return {
1464
+ id: action.type || `action-${index}`,
1465
+ type: action.type,
1466
+ params: action.params
1467
+ };
1468
+ }
1469
+ return null;
1470
+ }).filter((item) => item !== null);
1471
+ }
1472
+ function invokeItems(value) {
1473
+ return toArray(value).map((item, index) => {
1474
+ if (typeof item === "string") return {
1475
+ id: item || `invoke-${index}`,
1476
+ src: item
1477
+ };
1478
+ if (!item || typeof item !== "object") return null;
1479
+ const invoke = item;
1480
+ if (typeof invoke.src !== "string") return null;
1481
+ return {
1482
+ id: typeof invoke.id === "string" ? invoke.id : invoke.src,
1483
+ src: invoke.src,
1484
+ ...invoke.input !== void 0 ? { input: invoke.input } : {},
1485
+ ...invoke.output !== void 0 ? { output: invoke.output } : {}
1486
+ };
1487
+ }).filter((item) => item !== null);
1488
+ }
1489
+ function transitionTargets(target) {
1490
+ if (typeof target === "string") return [target];
1491
+ if (Array.isArray(target)) return target.filter((item) => typeof item === "string");
1492
+ return [];
1493
+ }
1494
+ function targetToNodeId(sourceId, target, rootKey) {
1495
+ if (!target) return sourceId;
1496
+ let clean = target.startsWith("#") ? target.slice(1) : target;
1497
+ if (target.startsWith("#")) {
1498
+ const rootPrefix = `${rootKey}.`;
1499
+ if (clean === rootKey) return "root";
1500
+ if (clean.startsWith(rootPrefix)) clean = clean.slice(rootPrefix.length);
1501
+ return clean ? `root.${clean}` : "root";
1502
+ }
1503
+ if (clean.includes(".")) return `root.${clean}`;
1504
+ return `${sourceId.split(".").slice(0, -1).join(".") || "root"}.${clean}`;
1505
+ }
1506
+ function transitionObjects(value) {
1507
+ return toArray(value).map((item) => {
1508
+ if (typeof item === "string") return { target: item };
1509
+ if (item && typeof item === "object") return item;
1510
+ return {};
1511
+ });
1512
+ }
1513
+ function guardObject(value) {
1514
+ if (!value) return null;
1515
+ if (typeof value === "string") return { type: value };
1516
+ if (typeof value === "object" && "type" in value && typeof value.type === "string") {
1517
+ const guard = value;
1518
+ return {
1519
+ type: guard.type,
1520
+ params: guard.params
1521
+ };
1522
+ }
1523
+ return null;
1524
+ }
1525
+ function machineConfigToGraph(config) {
1526
+ createMachine(config);
1527
+ const rootKey = String(config.id ?? "machine");
1528
+ const graph = {
1529
+ id: "root",
1530
+ nodes: [],
1531
+ edges: [],
1532
+ data: {
1533
+ implementations: {
1534
+ actions: [],
1535
+ guards: [],
1536
+ actors: [],
1537
+ delays: []
1538
+ },
1539
+ schemas: config.schemas ?? null
1540
+ }
1541
+ };
1542
+ function visit(state, key, parentId) {
1543
+ const id = parentId ? `${parentId}.${key}` : "root";
1544
+ const initialId = state.initial ? `${id}.${state.initial}` : null;
1545
+ graph.nodes.push({
1546
+ id,
1547
+ parentId,
1548
+ data: {
1549
+ key,
1550
+ type: state.type === "parallel" || state.type === "final" || state.type === "history" ? state.type : null,
1551
+ initialId,
1552
+ history: state.history ?? false,
1553
+ entry: actionItems(state.entry),
1554
+ exit: actionItems(state.exit),
1555
+ invokes: invokeItems(state.invoke),
1556
+ tags: Array.isArray(state.tags) ? state.tags : typeof state.tags === "string" ? [state.tags] : [],
1557
+ description: state.description ?? null,
1558
+ meta: state.meta ?? null
1559
+ }
1560
+ });
1561
+ for (const [eventType, value] of Object.entries(state.on ?? {})) for (const [index, transition] of transitionObjects(value).entries()) {
1562
+ const target = transitionTargets(transition.target)[0];
1563
+ graph.edges.push({
1564
+ id: `${id}-${eventType}-${index}`,
1565
+ sourceId: id,
1566
+ targetId: targetToNodeId(id, target, rootKey),
1567
+ data: {
1568
+ eventType,
1569
+ transitionType: target ? transition.reenter ? "reenter" : "normal" : "targetless",
1570
+ guard: guardObject(transition.guard),
1571
+ actions: actionItems(transition.actions),
1572
+ description: typeof transition.description === "string" ? transition.description : null,
1573
+ meta: transition.meta && typeof transition.meta === "object" ? transition.meta : null
1574
+ }
1575
+ });
1576
+ }
1577
+ for (const [delay, value] of Object.entries(state.after ?? {})) for (const [index, transition] of transitionObjects(value).entries()) {
1578
+ const target = transitionTargets(transition.target)[0];
1579
+ graph.edges.push({
1580
+ id: `${id}-after-${delay}-${index}`,
1581
+ sourceId: id,
1582
+ targetId: targetToNodeId(id, target, rootKey),
1583
+ data: {
1584
+ eventType: `xstate.after.${delay}.${id}`,
1585
+ transitionType: target ? "normal" : "targetless",
1586
+ guard: guardObject(transition.guard),
1587
+ actions: actionItems(transition.actions),
1588
+ description: typeof transition.description === "string" ? transition.description : null,
1589
+ meta: null
1590
+ }
1591
+ });
1592
+ }
1593
+ for (const [index, transition] of transitionObjects(state.always).entries()) {
1594
+ const target = transitionTargets(transition.target)[0];
1595
+ graph.edges.push({
1596
+ id: `${id}-always-${index}`,
1597
+ sourceId: id,
1598
+ targetId: targetToNodeId(id, target, rootKey),
1599
+ data: {
1600
+ eventType: "",
1601
+ transitionType: target ? "normal" : "targetless",
1602
+ guard: guardObject(transition.guard),
1603
+ actions: actionItems(transition.actions),
1604
+ description: typeof transition.description === "string" ? transition.description : null,
1605
+ meta: null
1606
+ }
1607
+ });
1608
+ }
1609
+ for (const [childKey, child] of Object.entries(state.states ?? {})) visit(child, childKey, id);
1610
+ }
1611
+ visit(config, rootKey, null);
1612
+ return graph;
1613
+ }
1614
+ const XSTATE_V6_FN_TRANSITION_META = "@statelyai.xstate.v6.fnTransition";
1615
+ const XSTATE_V6_FN_SOURCE_META = "@statelyai.xstate.v6.fnSource";
1616
+ function sourceFile(source) {
1617
+ return ts.createSourceFile("machine.ts", source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1618
+ }
1619
+ function propName(name) {
1620
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
1621
+ return null;
1622
+ }
1623
+ function unwrapExpression$2(node) {
1624
+ if (ts.isAsExpression(node) || ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node) || ts.isParenthesizedExpression(node)) return unwrapExpression$2(node.expression);
1625
+ return node;
1626
+ }
1627
+ function propertyContext(parent, key) {
1628
+ if (parent.transitionMap) return {
1629
+ v6: parent.v6,
1630
+ transitionValue: true
1631
+ };
1632
+ if (parent.transitionValue && key === "actions") return {
1633
+ v6: parent.v6,
1634
+ actionValue: true
1635
+ };
1636
+ if (parent.transitionValue && key === "to") return {
1637
+ v6: parent.v6,
1638
+ transitionValue: true
1639
+ };
1640
+ if (key === "entry" || key === "exit") return {
1641
+ v6: parent.v6,
1642
+ actionValue: true
1643
+ };
1644
+ if (key === "on" || key === "after") return {
1645
+ v6: parent.v6,
1646
+ transitionMap: true
1647
+ };
1648
+ if (key === "always" || key === "onDone" || key === "onError" || key === "onSnapshot" || key === "onTimeout") return {
1649
+ v6: parent.v6,
1650
+ transitionValue: true
1651
+ };
1652
+ return { v6: parent.v6 };
1653
+ }
1654
+ function fallbackFunctionTransition(fn) {
1655
+ return { meta: {
1656
+ [XSTATE_V6_FN_TRANSITION_META]: true,
1657
+ [XSTATE_V6_FN_SOURCE_META]: fn.getText()
1658
+ } };
1659
+ }
1660
+ function conditionText(cond) {
1661
+ return cond.negated ? `!(${cond.text})` : cond.text;
1662
+ }
1663
+ function simplifyConditions(conds) {
1664
+ const seen = /* @__PURE__ */ new Set();
1665
+ return conds.filter((cond) => {
1666
+ if (cond.negated) return false;
1667
+ const key = conditionText(cond);
1668
+ if (seen.has(key)) return false;
1669
+ seen.add(key);
1670
+ return true;
1671
+ });
1672
+ }
1673
+ function guardFromConditions(conds) {
1674
+ const simplified = simplifyConditions(conds);
1675
+ if (simplified.length === 0) return void 0;
1676
+ return { type: simplified.map(conditionText).join(" && ") };
1677
+ }
1678
+ function mergeActions(left, right) {
1679
+ return [...left ?? [], ...right];
1680
+ }
1681
+ function transitionFromValue(value, actions) {
1682
+ if (typeof value === "string") {
1683
+ if (actions.length === 0) return value;
1684
+ return {
1685
+ target: value,
1686
+ actions
1687
+ };
1688
+ }
1689
+ if (!value || typeof value !== "object" || Array.isArray(value)) return actions.length ? { actions } : null;
1690
+ const record = { ...value };
1691
+ const context = record.context;
1692
+ delete record.context;
1693
+ if (context !== void 0) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, [{
1694
+ type: "xstate.assign",
1695
+ params: { assignment: context }
1696
+ }]);
1697
+ if (actions.length) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, actions);
1698
+ return record;
1699
+ }
1700
+ function expressionBranches(expression, bindings, depth) {
1701
+ const node = unwrapExpression$2(expression);
1702
+ if (ts.isConditionalExpression(node)) {
1703
+ const cond = node.condition.getText();
1704
+ return [...expressionBranches(node.whenTrue, bindings, depth + 1).map((branch) => ({
1705
+ ...branch,
1706
+ conds: [{ text: cond }, ...branch.conds]
1707
+ })), ...expressionBranches(node.whenFalse, bindings, depth + 1).map((branch) => ({
1708
+ ...branch,
1709
+ conds: [{
1710
+ text: cond,
1711
+ negated: true
1712
+ }, ...branch.conds]
1713
+ }))];
1714
+ }
1715
+ if (ts.isBinaryExpression(node) && (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || node.operatorToken.kind === ts.SyntaxKind.BarBarToken)) {
1716
+ const cond = node.left.getText();
1717
+ if (node.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) return [...expressionBranches(node.right, bindings, depth + 1).map((branch) => ({
1718
+ ...branch,
1719
+ conds: [{ text: cond }, ...branch.conds]
1720
+ })), {
1721
+ conds: [{
1722
+ text: cond,
1723
+ negated: true
1724
+ }],
1725
+ actions: []
1726
+ }];
1727
+ return [{
1728
+ conds: [{ text: cond }],
1729
+ actions: [],
1730
+ result: node.left.getText()
1731
+ }, ...expressionBranches(node.right, bindings, depth + 1).map((branch) => ({
1732
+ ...branch,
1733
+ conds: [{
1734
+ text: cond,
1735
+ negated: true
1736
+ }, ...branch.conds]
1737
+ }))];
1738
+ }
1739
+ if (ts.isObjectLiteralExpression(node)) {
1740
+ const targetProperty = node.properties.find((property) => ts.isPropertyAssignment(property) && propName(property.name) === "target");
1741
+ const target = targetProperty ? unwrapExpression$2(targetProperty.initializer) : null;
1742
+ 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) => ({
1743
+ ...branch,
1744
+ result: objectLiteralToValue(node, bindings, {}, depth + 1, new Map([["target", branch.result]]))
1745
+ }));
1746
+ }
1747
+ return [{
1748
+ conds: [],
1749
+ actions: [],
1750
+ result: expressionToValue(node, bindings, {}, depth + 1)
1751
+ }];
1752
+ }
1753
+ function mergeBranchPrefix(prefix, branch) {
1754
+ return {
1755
+ conds: [...prefix.conds, ...branch.conds],
1756
+ actions: [...prefix.actions, ...branch.actions],
1757
+ result: branch.result,
1758
+ unknown: prefix.unknown || branch.unknown
1759
+ };
1760
+ }
1761
+ function expressionStatementAction(statement, enqName, bindings) {
1762
+ const expression = unwrapExpression$2(statement.expression);
1763
+ if (!ts.isCallExpression(expression)) return null;
1764
+ const callee = expression.expression;
1765
+ if (ts.isIdentifier(callee) && callee.text === enqName) return {
1766
+ type: "xstate.expr",
1767
+ params: {
1768
+ code: expression.getText(),
1769
+ lang: "ts"
1770
+ }
1771
+ };
1772
+ if (!ts.isPropertyAccessExpression(callee)) return null;
1773
+ if (!ts.isIdentifier(callee.expression) || callee.expression.text !== enqName) return null;
1774
+ const method = callee.name.text;
1775
+ const args = expression.arguments;
1776
+ const value = (index) => {
1777
+ const arg = args[index];
1778
+ return arg && ts.isExpression(arg) ? expressionToValue(arg, bindings, {}, 0) : void 0;
1779
+ };
1780
+ const spreadOpts = (index) => {
1781
+ const opts = value(index);
1782
+ return opts && typeof opts === "object" && !Array.isArray(opts) ? opts : {};
1783
+ };
1784
+ if (method === "emit") return {
1785
+ type: "xstate.emit",
1786
+ params: { event: value(0) }
1787
+ };
1788
+ if (method === "raise") return {
1789
+ type: "xstate.raise",
1790
+ params: {
1791
+ event: value(0),
1792
+ ...spreadOpts(1)
1793
+ }
1794
+ };
1795
+ if (method === "sendTo") return {
1796
+ type: "xstate.sendTo",
1797
+ params: {
1798
+ to: value(0),
1799
+ event: value(1),
1800
+ ...spreadOpts(2)
1801
+ }
1802
+ };
1803
+ if (method === "cancel") return {
1804
+ type: "xstate.cancel",
1805
+ params: { sendId: value(0) }
1806
+ };
1807
+ if (method === "log") return {
1808
+ type: "xstate.log",
1809
+ params: { label: value(0) }
1810
+ };
1811
+ if (method === "spawn") return {
1812
+ type: "xstate.spawnChild",
1813
+ params: {
1814
+ src: value(0),
1815
+ ...spreadOpts(1)
1816
+ }
1817
+ };
1818
+ if (method === "stop") return {
1819
+ type: "xstate.stopChild",
1820
+ params: { actorRef: value(0) }
1821
+ };
1822
+ return {
1823
+ type: "xstate.expr",
1824
+ params: {
1825
+ code: expression.getText(),
1826
+ lang: "ts"
1827
+ }
1828
+ };
1829
+ }
1830
+ function statementBranches(statements, bindings, enqName, prefixes = [{
1831
+ conds: [],
1832
+ actions: []
1833
+ }]) {
1834
+ let open = prefixes;
1835
+ const done = [];
1836
+ for (const statement of statements) {
1837
+ const nextOpen = [];
1838
+ for (const prefix of open) {
1839
+ if (ts.isReturnStatement(statement)) {
1840
+ if (!statement.expression) {
1841
+ done.push({
1842
+ ...prefix,
1843
+ result: void 0
1844
+ });
1845
+ continue;
1846
+ }
1847
+ for (const branch of expressionBranches(statement.expression, bindings, 0)) done.push(mergeBranchPrefix(prefix, branch));
1848
+ continue;
1849
+ }
1850
+ if (ts.isExpressionStatement(statement)) {
1851
+ const action = expressionStatementAction(statement, enqName, bindings);
1852
+ if (action) {
1853
+ nextOpen.push({
1854
+ ...prefix,
1855
+ actions: [...prefix.actions, action]
1856
+ });
1857
+ continue;
1858
+ }
1859
+ }
1860
+ if (ts.isIfStatement(statement)) {
1861
+ const cond = statement.expression.getText();
1862
+ const thenBranches = statementBranches(ts.isBlock(statement.thenStatement) ? statement.thenStatement.statements : [statement.thenStatement], bindings, enqName, [{
1863
+ ...prefix,
1864
+ conds: [...prefix.conds, { text: cond }]
1865
+ }]);
1866
+ const elseStatements = statement.elseStatement ? ts.isBlock(statement.elseStatement) ? statement.elseStatement.statements : [statement.elseStatement] : [];
1867
+ const elseBranches = elseStatements.length > 0 ? statementBranches(elseStatements, bindings, enqName, [{
1868
+ ...prefix,
1869
+ conds: [...prefix.conds, {
1870
+ text: cond,
1871
+ negated: true
1872
+ }]
1873
+ }]) : [{
1874
+ ...prefix,
1875
+ conds: [...prefix.conds, {
1876
+ text: cond,
1877
+ negated: true
1878
+ }]
1879
+ }];
1880
+ for (const branch of [...thenBranches, ...elseBranches]) if ("result" in branch) done.push(branch);
1881
+ else nextOpen.push(branch);
1882
+ continue;
1883
+ }
1884
+ if (ts.isSwitchStatement(statement)) {
1885
+ const expr = statement.expression.getText();
1886
+ const prior = [];
1887
+ for (const clause of statement.caseBlock.clauses) {
1888
+ const ownCond = ts.isCaseClause(clause) ? { text: `${expr} === ${clause.expression.getText()}` } : null;
1889
+ const conds = ownCond ? [...prior, ownCond] : prior.length ? [...prior] : [];
1890
+ const branches = statementBranches(clause.statements, bindings, enqName, [{
1891
+ ...prefix,
1892
+ conds: [...prefix.conds, ...conds]
1893
+ }]);
1894
+ for (const branch of branches) if ("result" in branch) done.push(branch);
1895
+ else nextOpen.push(branch);
1896
+ if (ownCond) prior.push({
1897
+ ...ownCond,
1898
+ negated: true
1899
+ });
1900
+ }
1901
+ continue;
1902
+ }
1903
+ nextOpen.push({
1904
+ ...prefix,
1905
+ unknown: true
1906
+ });
1907
+ }
1908
+ open = nextOpen;
1909
+ }
1910
+ return [...done, ...open];
1911
+ }
1912
+ function enqueueParamName(fn) {
1913
+ const param = fn.parameters[1];
1914
+ return param && ts.isIdentifier(param.name) ? param.name.text : "enq";
1915
+ }
1916
+ function normalizeFunctionTransition(fn, bindings, depth) {
1917
+ 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) => {
1918
+ const transition = transitionFromValue(branch.result, branch.actions);
1919
+ if (!transition) return null;
1920
+ if (typeof transition === "string") {
1921
+ const guard = guardFromConditions(branch.conds);
1922
+ return guard ? {
1923
+ target: transition,
1924
+ guard
1925
+ } : transition;
1926
+ }
1927
+ const guard = guardFromConditions(branch.conds);
1928
+ return guard ? {
1929
+ ...transition,
1930
+ guard
1931
+ } : transition;
1932
+ }).filter((transition) => Boolean(transition));
1933
+ if (transitions.length === 1) return transitions[0];
1934
+ if (transitions.length > 1) return transitions;
1935
+ return fallbackFunctionTransition(fn);
1936
+ }
1937
+ function inlineAction(fn) {
1938
+ return {
1939
+ type: "xstate.expr",
1940
+ params: {
1941
+ code: fn.getText(),
1942
+ lang: "ts"
1943
+ }
1944
+ };
1945
+ }
1946
+ function normalizeFunctionAction(fn, bindings) {
1947
+ if (ts.isArrowFunction(fn) && !ts.isBlock(fn.body)) return inlineAction(fn);
1948
+ if (!fn.body || !ts.isBlock(fn.body)) return inlineAction(fn);
1949
+ const branches = statementBranches(fn.body.statements, bindings, enqueueParamName(fn));
1950
+ 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;
1951
+ return inlineAction(fn);
1952
+ }
1953
+ function objectLiteralToValue(node, bindings, context, depth, overrides = /* @__PURE__ */ new Map()) {
1954
+ const result = {};
1955
+ for (const property of node.properties) {
1956
+ if (ts.isSpreadAssignment(property)) {
1957
+ const spread = expressionToValue(property.expression, bindings, context, depth + 1);
1958
+ if (spread && typeof spread === "object" && !Array.isArray(spread)) Object.assign(result, spread);
1959
+ continue;
1960
+ }
1961
+ if (!ts.isPropertyAssignment(property) && !ts.isShorthandPropertyAssignment(property)) continue;
1962
+ const key = propName(property.name);
1963
+ if (!key) continue;
1964
+ if (overrides.has(key)) {
1965
+ result[key] = overrides.get(key);
1966
+ continue;
1967
+ }
1968
+ if (ts.isShorthandPropertyAssignment(property)) {
1969
+ result[key] = expressionToValue(property.name, bindings, propertyContext(context, key), depth + 1);
1970
+ continue;
1971
+ }
1972
+ result[key] = expressionToValue(property.initializer, bindings, propertyContext(context, key), depth + 1);
1973
+ }
1974
+ if (context.transitionValue && "to" in result) {
1975
+ const to = result.to;
1976
+ const { to: _to, ...rest } = result;
1977
+ if (typeof to === "string") return {
1978
+ ...rest,
1979
+ target: to
1980
+ };
1981
+ if (Array.isArray(to)) return to.map((item) => typeof item === "string" ? {
1982
+ ...rest,
1983
+ target: item
1984
+ } : item && typeof item === "object" ? {
1985
+ ...rest,
1986
+ ...item
1987
+ } : rest);
1988
+ if (to && typeof to === "object" && !Array.isArray(to)) return {
1989
+ ...rest,
1990
+ ...to
1991
+ };
1992
+ }
1993
+ return result;
1994
+ }
1995
+ function expressionToValue(expression, bindings, context = {}, depth = 0) {
1996
+ if (depth > 30) return void 0;
1997
+ const node = unwrapExpression$2(expression);
1998
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
1999
+ if (ts.isNumericLiteral(node)) return Number(node.text);
2000
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
2001
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
2002
+ if (node.kind === ts.SyntaxKind.NullKeyword) return null;
2003
+ if (ts.isIdentifier(node)) {
2004
+ if (node.text === "undefined") return void 0;
2005
+ const binding = bindings.get(node.text);
2006
+ return binding ? expressionToValue(binding, bindings, context, depth + 1) : node.text;
2007
+ }
2008
+ if (ts.isObjectLiteralExpression(node)) return objectLiteralToValue(node, bindings, context, depth);
2009
+ if (ts.isArrayLiteralExpression(node)) {
2010
+ const values = node.elements.map((element) => expressionToValue(element, bindings, context, depth + 1));
2011
+ return context.actionValue ? values.flat() : values;
2012
+ }
2013
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
2014
+ if (context.v6 && context.transitionValue) return normalizeFunctionTransition(node, bindings, depth);
2015
+ if (context.v6 && context.actionValue) return normalizeFunctionAction(node, bindings);
2016
+ return node.getText();
2017
+ }
2018
+ if (ts.isCallExpression(node)) {
2019
+ if (context.actionValue) return inlineAction(node);
2020
+ const callee = node.expression.getText();
2021
+ const type = callee.includes(".") ? callee.split(".").pop() : callee;
2022
+ return { type: type === "assign" ? "xstate.assign" : type };
2023
+ }
2024
+ return node.getText();
2025
+ }
2026
+ function collectBindings$1(file) {
2027
+ const bindings = /* @__PURE__ */ new Map();
2028
+ for (const statement of file.statements) {
2029
+ if (!ts.isVariableStatement(statement)) continue;
2030
+ for (const declaration of statement.declarationList.declarations) if (ts.isIdentifier(declaration.name) && declaration.initializer) bindings.set(declaration.name.text, declaration.initializer);
2031
+ }
2032
+ return bindings;
2033
+ }
2034
+ function isCreateMachineCall(node) {
2035
+ const expression = node.expression;
2036
+ if (ts.isIdentifier(expression)) return expression.text === "createMachine";
2037
+ if (ts.isPropertyAccessExpression(expression)) return expression.name.text === "createMachine";
2038
+ return false;
2039
+ }
2040
+ function hasV6Syntax(source) {
2041
+ const file = sourceFile(source);
2042
+ let found = false;
2043
+ function visit(node) {
2044
+ if (found) return;
2045
+ if (ts.isPropertyAssignment(node) && (ts.isIdentifier(node.name) || ts.isStringLiteral(node.name))) {
2046
+ const key = node.name.text;
2047
+ if (key === "triggers" || key === "internalEvents" || key === "timeout" || key === "onTimeout" || key === "choice" || key === "route") {
2048
+ found = true;
2049
+ return;
2050
+ }
2051
+ if ((key === "on" || key === "after" || key === "always" || key === "entry" || key === "exit" || key === "actions" && !ts.isObjectLiteralExpression(node.initializer)) && node.initializer.getText().includes("=>")) {
2052
+ found = true;
2053
+ return;
2054
+ }
2055
+ }
2056
+ ts.forEachChild(node, visit);
2057
+ }
2058
+ visit(file);
2059
+ return found;
2060
+ }
2061
+ function detectXStateSourceProfile(source) {
2062
+ return hasV6Syntax(source) ? "xstate-v6-alpha" : "xstate-v5";
2063
+ }
2064
+ function xstateSourceToConfig(source, options = {}) {
2065
+ const file = sourceFile(source);
2066
+ const bindings = collectBindings$1(file);
2067
+ let found = null;
2068
+ const profile = options.profile === "auto" || !options.profile ? detectXStateSourceProfile(source) : options.profile;
2069
+ function visit(node) {
2070
+ if (found || !ts.isCallExpression(node) || !isCreateMachineCall(node)) {
2071
+ ts.forEachChild(node, visit);
2072
+ return;
2073
+ }
2074
+ const arg = node.arguments[0];
2075
+ if (!arg) return;
2076
+ const value = expressionToValue(arg, bindings, { v6: profile === "xstate-v6-alpha" });
2077
+ if (value && typeof value === "object" && !Array.isArray(value)) found = value;
2078
+ }
2079
+ visit(file);
2080
+ if (!found) throw new Error("No createMachine(...) or setup(...).createMachine(...) call found.");
2081
+ return found;
2082
+ }
2083
+ function parseJsonLike(input) {
2084
+ return JSON.parse(input);
2085
+ }
2086
+ function parseSimpleYaml(input) {
2087
+ const lines = input.split(/\r?\n/).filter((line) => line.trim() && !line.trim().startsWith("#"));
2088
+ const root = {};
2089
+ const stack = [{
2090
+ indent: -1,
2091
+ value: root
2092
+ }];
2093
+ for (const line of lines) {
2094
+ const indent = line.match(/^\s*/)?.[0].length ?? 0;
2095
+ const trimmed = line.trim();
2096
+ const match = /^([^:]+):(.*)$/.exec(trimmed);
2097
+ if (!match) continue;
2098
+ const key = match[1].trim();
2099
+ const raw = match[2].trim();
2100
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) stack.pop();
2101
+ const parent = stack[stack.length - 1].value;
2102
+ if (!raw) {
2103
+ const child = {};
2104
+ parent[key] = child;
2105
+ stack.push({
2106
+ indent,
2107
+ value: child
2108
+ });
2109
+ } else if (raw === "true" || raw === "false") parent[key] = raw === "true";
2110
+ else if (/^-?\d+(?:\.\d+)?$/.test(raw)) parent[key] = Number(raw);
2111
+ else parent[key] = raw.replace(/^['"]|['"]$/g, "");
2112
+ }
2113
+ return root;
2114
+ }
2115
+ function parseMermaid(input) {
2116
+ const states = /* @__PURE__ */ new Set();
2117
+ const transitions = [];
2118
+ let initial;
2119
+ for (const line of input.split(/\r?\n/)) {
2120
+ const trimmed = line.trim();
2121
+ if (!trimmed || /^(stateDiagram|stateDiagram-v2|flowchart|graph)\b/.test(trimmed)) continue;
2122
+ const stateMatch = /^\[\*\]\s*-->\s*([A-Za-z0-9_.-]+)/.exec(trimmed);
2123
+ if (stateMatch?.[1]) {
2124
+ initial = stateMatch[1];
2125
+ states.add(initial);
2126
+ continue;
2127
+ }
2128
+ const transitionMatch = /^([A-Za-z0-9_.-]+)\s*(?:-->|--)\s*(?:\|([^|]+)\|)?\s*([A-Za-z0-9_.-]+)(?:\s*:\s*(.+))?/.exec(trimmed);
2129
+ if (!transitionMatch) continue;
2130
+ const source = transitionMatch[1];
2131
+ const target = transitionMatch[3];
2132
+ const event = transitionMatch[2] ?? transitionMatch[4];
2133
+ states.add(source);
2134
+ states.add(target);
2135
+ transitions.push({
2136
+ source,
2137
+ target,
2138
+ event: event?.trim()
2139
+ });
2140
+ initial ??= source;
2141
+ }
2142
+ const stateConfigs = {};
2143
+ for (const state of states) stateConfigs[state] = {};
2144
+ for (const transition of transitions) {
2145
+ const source = stateConfigs[transition.source];
2146
+ const on = source.on ??= {};
2147
+ on[transition.event || "NEXT"] = transition.target;
2148
+ }
2149
+ return {
2150
+ id: "machine",
2151
+ initial: initial ?? [...states][0] ?? "idle",
2152
+ states: Object.keys(stateConfigs).length ? stateConfigs : { idle: {} }
2153
+ };
2154
+ }
2155
+ function inferMachineFormat(machine, format) {
2156
+ if (format) return format;
2157
+ if (typeof machine !== "string") return "json";
2158
+ const trimmed = machine.trim();
2159
+ if (trimmed.startsWith("{")) return "json";
2160
+ if (/^(stateDiagram|flowchart|graph)\b/m.test(trimmed)) return "mermaid";
2161
+ if (/^<\?xml|^<scxml/m.test(trimmed)) return "scxml";
2162
+ if (/^\w+:/m.test(trimmed)) return "yaml";
2163
+ return "xstate";
2164
+ }
2165
+ function machineInputToConfig(input) {
2166
+ const format = inferMachineFormat(input.machine, input.format);
2167
+ if (typeof input.machine !== "string") {
2168
+ createMachine(input.machine);
2169
+ return input.machine;
2170
+ }
2171
+ if (format === "json") {
2172
+ const config = parseJsonLike(input.machine);
2173
+ createMachine(config);
2174
+ return config;
2175
+ }
2176
+ if (format === "yaml") {
2177
+ const config = parseSimpleYaml(input.machine);
2178
+ createMachine(config);
2179
+ return config;
2180
+ }
2181
+ if (format === "mermaid") {
2182
+ const config = parseMermaid(input.machine);
2183
+ createMachine(config);
2184
+ return config;
2185
+ }
2186
+ if (format === "xstate") {
2187
+ const config = xstateSourceToConfig(input.machine, { profile: input.profile ?? (input.xstateVersion === 6 ? "xstate-v6-alpha" : input.xstateVersion === 5 ? "xstate-v5" : "auto") });
2188
+ createMachine(config);
2189
+ return config;
2190
+ }
2191
+ throw new Error(`${format} source parsing is not supported by graph-tools v1 yet.`);
2192
+ }
2193
+ function machineInputToGraph(input) {
2194
+ return machineConfigToGraph(machineInputToConfig(input));
2195
+ }
2196
+ function graphToMermaid(graph) {
2197
+ const machine = graphToMachineConfig(graph);
2198
+ const lines = ["stateDiagram-v2"];
2199
+ if (machine.initial) lines.push(` [*] --> ${machine.initial}`);
2200
+ for (const [stateKey, state] of Object.entries(machine.states ?? {})) for (const [event, transition] of Object.entries(state.on ?? {})) {
2201
+ const target = typeof transition === "string" ? transition : Array.isArray(transition) ? void 0 : transition.target;
2202
+ if (target) lines.push(` ${stateKey} --> ${target} : ${event}`);
2203
+ }
2204
+ return lines.join("\n");
2205
+ }
2206
+ function toYaml(value, indent = 0) {
2207
+ const pad = " ".repeat(indent);
2208
+ if (!value || typeof value !== "object" || Array.isArray(value)) return `${JSON.stringify(value)}`;
2209
+ return Object.entries(value).map(([key, item]) => {
2210
+ if (item && typeof item === "object" && !Array.isArray(item)) return `${pad}${key}:\n${toYaml(item, indent + 2)}`;
2211
+ return `${pad}${key}: ${typeof item === "string" ? item : JSON.stringify(item)}`;
2212
+ }).join("\n");
2213
+ }
2214
+ function graphToFormat(graph, format) {
2215
+ const config = graphToMachineConfig(graph);
2216
+ if (format === "xstate") return `import { createMachine } from 'xstate';\n\nexport const machine = createMachine(${serializeJS(config, 2)});\n`;
2217
+ if (format === "json") return config;
2218
+ if (format === "yaml") return toYaml(config);
2219
+ if (format === "mermaid") return graphToMermaid(graph);
2220
+ if (format === "scxml") {
2221
+ const machine = config;
2222
+ return `<scxml version="1.0" initial="${machine.initial ?? ""}" name="${machine.id ?? "machine"}"></scxml>`;
2223
+ }
2224
+ return config;
2225
+ }
2226
+ const colorSchema = _enum([
2227
+ "green",
2228
+ "red",
2229
+ "purple",
2230
+ "blue",
2231
+ "orange",
2232
+ "yellow",
2233
+ "pink",
2234
+ "teal"
2235
+ ]);
2236
+ const stateTypeSchema = _enum([
2237
+ "normal",
2238
+ "parallel",
2239
+ "history",
2240
+ "final"
2241
+ ]).nullable();
2242
+ const actionLocationSchema = union([object({
2243
+ nodeId: string(),
2244
+ group: _enum(["entry", "exit"])
2245
+ }), object({
2246
+ edgeId: string(),
2247
+ group: literal("transition")
2248
+ })]);
2249
+ const createStatePatchSchema = object({
2250
+ op: literal("createState"),
2251
+ description: string().optional(),
2252
+ id: string().optional(),
2253
+ parentId: string(),
2254
+ key: string(),
2255
+ type: stateTypeSchema.optional(),
2256
+ x: number().optional(),
2257
+ y: number().optional(),
2258
+ color: colorSchema.nullable().optional(),
2259
+ initial: boolean().optional()
2260
+ });
2261
+ const updateStatePatchSchema = object({
2262
+ op: literal("updateState"),
2263
+ description: string().optional(),
2264
+ stateId: string(),
2265
+ key: string().optional(),
2266
+ type: stateTypeSchema.optional(),
2267
+ stateDescription: string().optional(),
2268
+ initialId: string().nullable().optional(),
2269
+ color: colorSchema.nullable().optional(),
2270
+ meta: record(string(), unknown()).nullable().optional(),
2271
+ history: _enum(["shallow", "deep"]).nullable().optional()
2272
+ });
2273
+ const deleteStatePatchSchema = object({
2274
+ op: literal("deleteState"),
2275
+ description: string().optional(),
2276
+ stateId: string()
2277
+ });
2278
+ const guardSchema = object({
2279
+ type: string(),
2280
+ code: string().optional(),
2281
+ params: record(string(), unknown()).optional()
2282
+ });
2283
+ const createTransitionPatchSchema = object({
2284
+ op: literal("createTransition"),
2285
+ description: string().optional(),
2286
+ id: string().optional(),
2287
+ sourceId: string(),
2288
+ targetId: string(),
2289
+ eventType: string(),
2290
+ transitionType: _enum([
2291
+ "normal",
2292
+ "targetless",
2293
+ "reenter"
2294
+ ]).optional(),
2295
+ guard: guardSchema.optional(),
2296
+ color: colorSchema.nullable().optional(),
2297
+ transitionDescription: string().nullable().optional()
2298
+ });
2299
+ const updateTransitionPatchSchema = object({
2300
+ op: literal("updateTransition"),
2301
+ description: string().optional(),
2302
+ transitionId: string(),
2303
+ sourceId: string().optional(),
2304
+ targetId: string().optional(),
2305
+ eventType: string().optional(),
2306
+ transitionType: _enum([
2307
+ "normal",
2308
+ "targetless",
2309
+ "reenter"
2310
+ ]).optional(),
2311
+ transitionDescription: string().nullable().optional(),
2312
+ color: colorSchema.nullable().optional(),
2313
+ meta: record(string(), unknown()).nullable().optional()
2314
+ });
2315
+ const deleteTransitionPatchSchema = object({
2316
+ op: literal("deleteTransition"),
2317
+ description: string().optional(),
2318
+ transitionId: string()
2319
+ });
2320
+ const createActionPatchSchema = object({
2321
+ op: literal("createAction"),
2322
+ description: string().optional(),
2323
+ location: actionLocationSchema,
2324
+ action: object({
2325
+ id: string().optional(),
2326
+ type: string(),
2327
+ params: record(string(), unknown()).optional()
2328
+ }),
2329
+ index: number().optional()
2330
+ });
2331
+ const updateActionPatchSchema = object({
2332
+ op: literal("updateAction"),
2333
+ description: string().optional(),
2334
+ location: actionLocationSchema,
2335
+ actionId: string(),
2336
+ data: object({
2337
+ type: string().optional(),
2338
+ params: record(string(), unknown()).optional()
2339
+ })
2340
+ });
2341
+ const deleteActionPatchSchema = object({
2342
+ op: literal("deleteAction"),
2343
+ description: string().optional(),
2344
+ location: actionLocationSchema,
2345
+ actionId: string()
2346
+ });
2347
+ const setGuardPatchSchema = object({
2348
+ op: literal("setGuard"),
2349
+ description: string().optional(),
2350
+ edgeId: string(),
2351
+ guard: guardSchema
2352
+ });
2353
+ const deleteGuardPatchSchema = object({
2354
+ op: literal("deleteGuard"),
2355
+ description: string().optional(),
2356
+ edgeId: string()
2357
+ });
2358
+ const machinePatchSchema = discriminatedUnion("op", [
2359
+ createStatePatchSchema,
2360
+ updateStatePatchSchema,
2361
+ deleteStatePatchSchema,
2362
+ createTransitionPatchSchema,
2363
+ updateTransitionPatchSchema,
2364
+ deleteTransitionPatchSchema,
2365
+ createActionPatchSchema,
2366
+ updateActionPatchSchema,
2367
+ deleteActionPatchSchema,
2368
+ setGuardPatchSchema,
2369
+ deleteGuardPatchSchema
2370
+ ]);
2371
+ const validationLevelSchema = _enum([
2372
+ "info",
2373
+ "warning",
2374
+ "error"
2375
+ ]);
2376
+ const validationIssueKindSchema = _enum([
2377
+ "correctness",
2378
+ "reachability",
2379
+ "optimization",
2380
+ "maintainability",
2381
+ "security"
2382
+ ]);
2383
+ const validationIssueCodeSchema = _enum([
2384
+ "parse_error",
2385
+ "unsupported_format",
2386
+ "empty_state_key",
2387
+ "invalid_state_key",
2388
+ "duplicate_state_key",
2389
+ "history_initial",
2390
+ "missing_initial",
2391
+ "invalid_initial",
2392
+ "unreachable_state",
2393
+ "final_state_invokes",
2394
+ "invalid_state_children",
2395
+ "empty_parallel",
2396
+ "single_child_compound",
2397
+ "noop_transient_state",
2398
+ "on_done_unreachable",
2399
+ "duplicate_transition",
2400
+ "invalid_delay",
2401
+ "repeated_guard",
2402
+ "transition_never_taken",
2403
+ "missing_guard",
2404
+ "redundant_transition",
2405
+ "missing_delay_implementation",
2406
+ "invalid_final_transition",
2407
+ "invalid_history_transition",
2408
+ "parallel_region_transition",
2409
+ "duplicate_source_name",
2410
+ "undefined_action",
2411
+ "undefined_guard",
2412
+ "undefined_actor"
2413
+ ]);
2414
+ const validationFixKindSchema = _enum([
2415
+ "patch",
2416
+ "rewrite",
2417
+ "advice",
2418
+ "proposedPatch"
2419
+ ]);
2420
+ const validationFixSourceSchema = _enum(["deterministic", "llm"]);
2421
+ const AUTO_NAME_PREFIXES = [
2422
+ ":invocation:",
2423
+ "inline:",
2424
+ "$auto-"
2425
+ ];
2426
+ const AUTO_NAME_SUBSTRINGS = [
2427
+ ":invocation[",
2428
+ "#transition[",
2429
+ "#actor["
2430
+ ];
2431
+ function isAutoGeneratedInlineName(name) {
2432
+ return AUTO_NAME_PREFIXES.some((prefix) => name.startsWith(prefix)) || AUTO_NAME_SUBSTRINGS.some((part) => name.includes(part));
2433
+ }
2434
+ function getImplementations(graph) {
2435
+ return graph.data.implementations ?? {
2436
+ actions: [],
2437
+ guards: [],
2438
+ actors: [],
2439
+ delays: []
2440
+ };
2441
+ }
2442
+ function issue(input) {
2443
+ const nodeIds = input.nodeIds ?? [];
2444
+ const edgeIds = input.edgeIds ?? [];
2445
+ return {
2446
+ code: input.code,
2447
+ kind: input.kind,
2448
+ level: input.level,
2449
+ message: input.message,
2450
+ target: {
2451
+ nodeIds,
2452
+ edgeIds
2453
+ },
2454
+ metadata: input.metadata,
2455
+ fixes: input.fixes,
2456
+ source: "deterministic",
2457
+ nodeIds,
2458
+ edgeIds
2459
+ };
2460
+ }
2461
+ function deleteTransitionFix(edge, title) {
2462
+ return {
2463
+ kind: "patch",
2464
+ source: "deterministic",
2465
+ title,
2466
+ patches: [{
2467
+ op: "deleteTransition",
2468
+ transitionId: edge.id
2469
+ }],
2470
+ confidence: .9
2471
+ };
2472
+ }
2473
+ function setInitialFix(node, child, title) {
2474
+ return {
2475
+ kind: "patch",
2476
+ source: "deterministic",
2477
+ title,
2478
+ patches: [{
2479
+ op: "updateState",
2480
+ stateId: node.id,
2481
+ initialId: child.id
2482
+ }],
2483
+ confidence: .85
2484
+ };
2485
+ }
2486
+ function findDuplicateNodes(graph, node, parent) {
2487
+ if (!parent) return [];
2488
+ return graph.nodes.filter((n) => n.parentId === parent.id && n.id !== node.id).filter((childNode) => childNode.data.key === node.data.key);
2489
+ }
2490
+ function findDuplicateEdges(graph, edge) {
2491
+ 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);
2492
+ }
2493
+ function hasUnconditionalAlways(graph, sourceId) {
2494
+ return graph.edges.some((e) => e.sourceId === sourceId && e.data.eventType === "" && !e.data.guard && e.targetId !== e.sourceId);
2495
+ }
2496
+ function isTransitionPreempted(graph, edge) {
2497
+ if (edge.data.eventType === "") return false;
2498
+ return hasUnconditionalAlways(graph, edge.sourceId);
2499
+ }
2500
+ function computeReachableGraphNodes(graph) {
2501
+ const rootNode = graph.nodes.find((n) => n.parentId === null);
2502
+ if (!rootNode) return /* @__PURE__ */ new Set();
2503
+ const visited = /* @__PURE__ */ new Set();
2504
+ function getNodeInitialStates(node) {
2505
+ const children = graph.nodes.filter((n) => n.parentId === node.id);
2506
+ if (!children.length) return [];
2507
+ if (node.data.type === "parallel") return children;
2508
+ const initial = children.find((n) => node.data.initialId === n.id);
2509
+ return initial ? [initial] : [];
2510
+ }
2511
+ function dfs(node) {
2512
+ if (visited.has(node.id)) return;
2513
+ visited.add(node.id);
2514
+ getNodeInitialStates(node).forEach((n) => dfs(n));
2515
+ if (node.data.type === "parallel") graph.nodes.filter((n) => n.parentId === node.id).forEach((region) => {
2516
+ getNodeInitialStates(region).forEach((n) => dfs(n));
2517
+ });
2518
+ if (node.data.type === "history") {
2519
+ const parent = graph.nodes.find((n) => n.id === node.parentId);
2520
+ if (parent) if (parent.data.type === "parallel") graph.nodes.filter((n) => n.parentId === parent.id).forEach((region) => {
2521
+ getNodeInitialStates(region).forEach((n) => dfs(n));
2522
+ });
2523
+ else getNodeInitialStates(parent).forEach((n) => dfs(n));
2524
+ }
2525
+ graph.edges.filter((edge) => edge.sourceId === node.id && !isTransitionPreempted(graph, edge)).forEach((edge) => {
2526
+ const target = graph.nodes.find((n) => n.id === edge.targetId);
2527
+ if (target) dfs(target);
2528
+ });
2529
+ let ancestor = graph.nodes.find((n) => n.id === node.parentId);
2530
+ while (ancestor) {
2531
+ graph.edges.filter((edge) => edge.sourceId === ancestor.id && !isTransitionPreempted(graph, edge)).forEach((edge) => {
2532
+ const target = graph.nodes.find((n) => n.id === edge.targetId);
2533
+ if (target) dfs(target);
2534
+ });
2535
+ ancestor = graph.nodes.find((n) => n.id === ancestor.parentId);
2536
+ }
2537
+ }
2538
+ dfs(rootNode);
2539
+ const reachableWithParents = new Set(visited);
2540
+ for (const nodeId of visited) {
2541
+ let node = graph.nodes.find((n) => n.id === nodeId);
2542
+ while (node?.parentId) {
2543
+ reachableWithParents.add(node.parentId);
2544
+ node = graph.nodes.find((n) => n.id === node.parentId);
2545
+ }
2546
+ }
2547
+ return reachableWithParents;
2548
+ }
2549
+ function getNodeErrors(graph, node) {
2550
+ const errors = [];
2551
+ if (node.data.key.length === 0) errors.push({
2552
+ code: "empty_state_key",
2553
+ kind: "correctness",
2554
+ message: "State key cannot be empty"
2555
+ });
2556
+ const parent = graph.nodes.find((n) => n.id === node.parentId);
2557
+ const isInitial = parent?.data.initialId === node.id;
2558
+ const isHistoryNode = node.data.type === "history";
2559
+ if (isInitial && isHistoryNode) errors.push({
2560
+ code: "history_initial",
2561
+ kind: "correctness",
2562
+ message: "A history node cannot be the initial node. This will cause an infinite loop."
2563
+ });
2564
+ if (findDuplicateNodes(graph, node, parent).length > 0) errors.push({
2565
+ code: "duplicate_state_key",
2566
+ kind: "correctness",
2567
+ message: "A state with that name already exists"
2568
+ });
2569
+ if (node.data.key.includes("#")) errors.push({
2570
+ code: "invalid_state_key",
2571
+ kind: "correctness",
2572
+ message: "State key cannot contain \"#\"",
2573
+ metadata: { character: "#" }
2574
+ });
2575
+ const children = graph.nodes.filter((n) => n.parentId === node.id);
2576
+ if (children.length > 0 && node.data.type !== "parallel" && node.data.type !== "final" && node.data.type !== "history") {
2577
+ const isTargeted = graph.edges.some((edge) => edge.targetId === node.id);
2578
+ if (!node.data.initialId && isTargeted) errors.push({
2579
+ code: "missing_initial",
2580
+ kind: "correctness",
2581
+ message: "Compound state must have an initial state",
2582
+ metadata: {
2583
+ reason: "targeted_compound",
2584
+ childCount: children.length
2585
+ },
2586
+ fixes: children.length === 1 ? [setInitialFix(node, children[0], `Set ${children[0].data.key} as initial state`)] : void 0
2587
+ });
2588
+ else if (node.data.initialId) {
2589
+ if (!children.some((n) => n.id === node.data.initialId)) errors.push({
2590
+ code: "invalid_initial",
2591
+ kind: "correctness",
2592
+ message: "Initial state does not exist",
2593
+ metadata: {
2594
+ initialId: node.data.initialId,
2595
+ childCount: children.length
2596
+ },
2597
+ fixes: children.length === 1 ? [setInitialFix(node, children[0], `Set ${children[0].data.key} as initial state`)] : void 0
2598
+ });
2599
+ }
2600
+ }
2601
+ return errors.length > 0 ? errors : null;
2602
+ }
2603
+ function getNodeWarningsWithReachability(graph, node, reachableNodes) {
2604
+ const warnings = [];
2605
+ if (!reachableNodes.has(node.id) && node.data.type !== "history") warnings.push({
2606
+ code: "unreachable_state",
2607
+ kind: "reachability",
2608
+ message: "Unreachable state"
2609
+ });
2610
+ if (node.data.type === "final" && (node.data.invokes?.length ?? 0) > 0) warnings.push({
2611
+ code: "final_state_invokes",
2612
+ kind: "correctness",
2613
+ message: "Final state cannot have invocations"
2614
+ });
2615
+ if (node.data.type && ["final", "history"].includes(node.data.type) && graph.nodes.filter((n) => n.parentId === node.id).length > 0) warnings.push({
2616
+ code: "invalid_state_children",
2617
+ kind: "correctness",
2618
+ message: `${node.data.type.charAt(0).toUpperCase() + node.data.type.slice(1)} state cannot have child states`,
2619
+ metadata: { stateType: node.data.type }
2620
+ });
2621
+ if (node.data.type === "parallel" && graph.nodes.filter((n) => n.parentId === node.id).length === 0) warnings.push({
2622
+ code: "empty_parallel",
2623
+ kind: "correctness",
2624
+ message: "Parallel state should have child regions"
2625
+ });
2626
+ const children = graph.nodes.filter((n) => n.parentId === node.id);
2627
+ if (children.length === 1 && node.data.type !== "parallel" && node.data.type !== "final" && node.data.type !== "history") warnings.push({
2628
+ code: "single_child_compound",
2629
+ kind: "optimization",
2630
+ message: "State with single child can be simplified"
2631
+ });
2632
+ 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) {
2633
+ const alwaysEdges = graph.edges.filter((e) => e.sourceId === node.id && e.data.eventType === "");
2634
+ const hasUnconditionalNoop = alwaysEdges.some((e) => !e.data.guard && e.targetId !== e.sourceId && (e.data.actions?.length ?? 0) === 0);
2635
+ const hasMeaningfulAlways = alwaysEdges.some((e) => !!e.data.guard || (e.data.actions?.length ?? 0) > 0);
2636
+ if (hasUnconditionalNoop && !hasMeaningfulAlways) warnings.push({
2637
+ code: "noop_transient_state",
2638
+ kind: "optimization",
2639
+ message: "State has no effect: an unconditional transition exits it immediately, and it has no actions or invocations"
2640
+ });
2641
+ }
2642
+ return warnings.length > 0 ? warnings : null;
2643
+ }
2644
+ function getOnDoneWarnings(graph, reachableNodes) {
2645
+ const results = [];
2646
+ for (const node of graph.nodes) {
2647
+ const doneEdges = graph.edges.filter((edge) => edge.sourceId === node.id && (edge.data.eventType.startsWith("@statelyai.state.done.") || edge.data.eventType.startsWith("xstate.done.state.")));
2648
+ if (doneEdges.length === 0) continue;
2649
+ const edgeIds = doneEdges.map((e) => e.id);
2650
+ if (node.data.type === "parallel") {
2651
+ const regions = graph.nodes.filter((n) => n.parentId === node.id);
2652
+ const regionsWithoutFinal = [];
2653
+ const regionsWithoutReachableFinal = [];
2654
+ for (const region of regions) {
2655
+ const finalChildren = graph.nodes.filter((n) => n.parentId === region.id).filter((child) => child.data.type === "final");
2656
+ if (finalChildren.length === 0) regionsWithoutFinal.push(region);
2657
+ else if (!finalChildren.some((child) => reachableNodes.has(child.id))) regionsWithoutReachableFinal.push(region);
2658
+ }
2659
+ if (regionsWithoutFinal.length > 0) results.push(issue({
2660
+ code: "on_done_unreachable",
2661
+ kind: "reachability",
2662
+ level: "warning",
2663
+ nodeIds: [node.id],
2664
+ edgeIds,
2665
+ message: "onDone transitions require each parallel region to have a final child state",
2666
+ metadata: { reason: "parallel_region_missing_final" }
2667
+ }));
2668
+ else if (regionsWithoutReachableFinal.length > 0) results.push(issue({
2669
+ code: "on_done_unreachable",
2670
+ kind: "reachability",
2671
+ level: "warning",
2672
+ nodeIds: [node.id],
2673
+ edgeIds,
2674
+ message: "onDone transitions will never trigger: a parallel region has no reachable final child state",
2675
+ metadata: { reason: "parallel_region_final_unreachable" }
2676
+ }));
2677
+ } else {
2678
+ const finalChildren = graph.nodes.filter((n) => n.parentId === node.id).filter((child) => child.data.type === "final");
2679
+ if (finalChildren.length === 0) results.push(issue({
2680
+ code: "on_done_unreachable",
2681
+ kind: "reachability",
2682
+ level: "warning",
2683
+ nodeIds: [node.id],
2684
+ edgeIds,
2685
+ message: "onDone transitions require a final child state to trigger",
2686
+ metadata: { reason: "missing_final_child" }
2687
+ }));
2688
+ else if (!finalChildren.some((child) => reachableNodes.has(child.id))) results.push(issue({
2689
+ code: "on_done_unreachable",
2690
+ kind: "reachability",
2691
+ level: "warning",
2692
+ nodeIds: [node.id],
2693
+ edgeIds,
2694
+ message: "onDone transitions will never trigger: no reachable final child state",
2695
+ metadata: { reason: "final_child_unreachable" }
2696
+ }));
2697
+ }
2698
+ }
2699
+ return results;
2700
+ }
2701
+ function getEdgeErrors(graph, edge) {
2702
+ const errors = [];
2703
+ const duplicates = findDuplicateEdges(graph, edge);
2704
+ if (duplicates.length > 0) errors.push({
2705
+ code: "duplicate_transition",
2706
+ kind: "correctness",
2707
+ message: "Two events with the same name and source state are not allowed",
2708
+ 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
2709
+ });
2710
+ if (edge.data.eventType.startsWith("xstate.after.")) {
2711
+ const delayPart = edge.data.eventType.replace(/^xstate\.after\./, "").split(".")[0];
2712
+ if (delayPart !== void 0) {
2713
+ const numericValue = Number(delayPart);
2714
+ if (!Number.isNaN(numericValue) && numericValue < 0) errors.push({
2715
+ code: "invalid_delay",
2716
+ kind: "correctness",
2717
+ message: "Delay value cannot be negative",
2718
+ metadata: { delay: numericValue }
2719
+ });
2720
+ }
2721
+ }
2722
+ return errors.length > 0 ? errors : null;
2723
+ }
2724
+ function getEdgeWarnings(graph, edge) {
2725
+ const warnings = [];
2726
+ const group = graph.edges.filter((e) => e.sourceId === edge.sourceId && e.data.eventType === edge.data.eventType);
2727
+ if (edge.data.guard && group.length > 1) {
2728
+ const guardType = edge.data.guard.type;
2729
+ const withRepeatedGuard = group.filter((e) => e.data.guard?.type === guardType);
2730
+ if (withRepeatedGuard.length > 1) warnings.push({
2731
+ code: "repeated_guard",
2732
+ kind: "maintainability",
2733
+ message: `Found ${withRepeatedGuard.length} guards with the name \`${guardType}\``,
2734
+ metadata: {
2735
+ guardType,
2736
+ count: withRepeatedGuard.length
2737
+ }
2738
+ });
2739
+ }
2740
+ const edgeIndex = group.findIndex((e) => e.id === edge.id);
2741
+ if (edgeIndex > 0 && group.slice(0, edgeIndex).some((e) => !e.data.guard) || isTransitionPreempted(graph, edge)) warnings.push({
2742
+ code: "transition_never_taken",
2743
+ kind: "reachability",
2744
+ message: "This transition will never be taken",
2745
+ fixes: edgeIndex > 0 && group.slice(0, edgeIndex).some((e) => !e.data.guard) ? [deleteTransitionFix(edge, "Delete unreachable transition")] : void 0
2746
+ });
2747
+ if (group.length > 1 && !edge.data.guard && edgeIndex < group.length - 1) warnings.push({
2748
+ code: "missing_guard",
2749
+ kind: "correctness",
2750
+ message: "This transition is missing a guard, so transitions after it will never be taken"
2751
+ });
2752
+ if (edgeIndex > 0) {
2753
+ const prevEdge = group[edgeIndex - 1];
2754
+ const sameTarget = prevEdge.targetId === edge.targetId;
2755
+ const sameActions = JSON.stringify(prevEdge.data.actions) === JSON.stringify(edge.data.actions);
2756
+ if (sameTarget && sameActions) warnings.push({
2757
+ code: "redundant_transition",
2758
+ kind: "optimization",
2759
+ message: "Redundant transition: same target and actions as previous transition",
2760
+ fixes: [deleteTransitionFix(edge, "Delete redundant transition")]
2761
+ });
2762
+ }
2763
+ if (edge.data.eventType.startsWith("xstate.after.")) {
2764
+ const delayPart = edge.data.eventType.replace(/^xstate\.after\./, "").split(".")[0];
2765
+ if (delayPart !== void 0 && Number.isNaN(Number(delayPart))) {
2766
+ if (!getImplementations(graph).delays.find((d) => d.name === delayPart)?.code?.body?.trim()) warnings.push({
2767
+ code: "missing_delay_implementation",
2768
+ kind: "correctness",
2769
+ message: `Missing delay implementation for "${delayPart}"`,
2770
+ metadata: { delay: delayPart }
2771
+ });
2772
+ }
2773
+ }
2774
+ const sourceNode = graph.nodes.find((n) => n.id === edge.sourceId);
2775
+ if (sourceNode && sourceNode.data.type === "final") warnings.push({
2776
+ code: "invalid_final_transition",
2777
+ kind: "correctness",
2778
+ message: "Final state cannot transition to other states",
2779
+ fixes: [deleteTransitionFix(edge, "Delete transition from final state")]
2780
+ });
2781
+ if (sourceNode && sourceNode.data.type === "history") warnings.push({
2782
+ code: "invalid_history_transition",
2783
+ kind: "correctness",
2784
+ message: "History state cannot have transitions",
2785
+ fixes: [deleteTransitionFix(edge, "Delete transition from history state")]
2786
+ });
2787
+ const targetNode = graph.nodes.find((n) => n.id === edge.targetId);
2788
+ if (sourceNode && targetNode && sourceNode.parentId === targetNode.parentId && sourceNode.id !== targetNode.id) {
2789
+ const parentNode = graph.nodes.find((n) => n.id === sourceNode.parentId);
2790
+ if (parentNode && parentNode.data.type === "parallel") warnings.push({
2791
+ code: "parallel_region_transition",
2792
+ kind: "correctness",
2793
+ message: "Parallel regions should communicate via events, not transitions"
2794
+ });
2795
+ }
2796
+ return warnings.length > 0 ? warnings : null;
2797
+ }
2798
+ function getSourceWarnings(graph) {
2799
+ const results = [];
2800
+ const impls = getImplementations(graph);
2801
+ const groups = [
2802
+ {
2803
+ type: "action",
2804
+ items: impls.actions
2805
+ },
2806
+ {
2807
+ type: "guard",
2808
+ items: impls.guards
2809
+ },
2810
+ {
2811
+ type: "actor",
2812
+ items: impls.actors
2813
+ },
2814
+ {
2815
+ type: "delay",
2816
+ items: impls.delays
2817
+ }
2818
+ ];
2819
+ for (const group of groups) {
2820
+ const seen = /* @__PURE__ */ new Map();
2821
+ for (const item of group.items) {
2822
+ if (!item.name) continue;
2823
+ seen.set(item.name, (seen.get(item.name) ?? 0) + 1);
2824
+ }
2825
+ for (const [name, count] of seen) if (count > 1) results.push(issue({
2826
+ code: "duplicate_source_name",
2827
+ kind: "maintainability",
2828
+ level: "warning",
2829
+ message: `Duplicate ${group.type} source name "${name}" (${count} occurrences)`,
2830
+ metadata: {
2831
+ sourceType: group.type,
2832
+ name,
2833
+ count
2834
+ }
2835
+ }));
2836
+ }
2837
+ return results;
2838
+ }
2839
+ function getSourceRestrictionErrors(graph, sourceAllowed) {
2840
+ if (!sourceAllowed) return [];
2841
+ const results = [];
2842
+ const impls = getImplementations(graph);
2843
+ const actionIds = new Set(impls.actions.map((a) => a.id));
2844
+ const guardIds = new Set(impls.guards.map((g) => g.id));
2845
+ const actorIds = new Set(impls.actors.map((a) => a.id));
2846
+ const isInvalidActionType = (type) => {
2847
+ if (!type) return true;
2848
+ if (actionIds.has(type)) return false;
2849
+ if (type.startsWith("xstate.")) return false;
2850
+ if (isAutoGeneratedInlineName(type)) return false;
2851
+ return true;
2852
+ };
2853
+ const formatActionMsg = (type) => type ? `Action "${type}" is not a defined action implementation` : "Action is not selected";
2854
+ if (sourceAllowed.actions === "predefined") {
2855
+ for (const node of graph.nodes) {
2856
+ for (const action of node.data.entry ?? []) if (isInvalidActionType(action.type)) results.push(issue({
2857
+ code: "undefined_action",
2858
+ kind: "correctness",
2859
+ level: "error",
2860
+ nodeIds: [node.id],
2861
+ message: formatActionMsg(action.type)
2862
+ }));
2863
+ for (const action of node.data.exit ?? []) if (isInvalidActionType(action.type)) results.push(issue({
2864
+ code: "undefined_action",
2865
+ kind: "correctness",
2866
+ level: "error",
2867
+ nodeIds: [node.id],
2868
+ message: formatActionMsg(action.type)
2869
+ }));
2870
+ }
2871
+ for (const edge of graph.edges) for (const action of edge.data.actions ?? []) if (isInvalidActionType(action.type)) results.push(issue({
2872
+ code: "undefined_action",
2873
+ kind: "correctness",
2874
+ level: "error",
2875
+ edgeIds: [edge.id],
2876
+ message: formatActionMsg(action.type)
2877
+ }));
2878
+ }
2879
+ if (sourceAllowed.guards === "predefined") for (const edge of graph.edges) {
2880
+ const guard = edge.data.guard;
2881
+ if (!guard) continue;
2882
+ const type = guard.type;
2883
+ if (!type || !guardIds.has(type) && !type.startsWith("inline:") && !isAutoGeneratedInlineName(type)) results.push(issue({
2884
+ code: "undefined_guard",
2885
+ kind: "correctness",
2886
+ level: "error",
2887
+ edgeIds: [edge.id],
2888
+ message: type ? `Guard "${type}" is not a defined guard implementation` : "Guard is not selected"
2889
+ }));
2890
+ }
2891
+ if (sourceAllowed.actors === "predefined") for (const node of graph.nodes) for (const invoke of node.data.invokes ?? []) {
2892
+ const src = invoke.src;
2893
+ if (!src || !actorIds.has(src) && !isAutoGeneratedInlineName(src)) results.push(issue({
2894
+ code: "undefined_actor",
2895
+ kind: "correctness",
2896
+ level: "error",
2897
+ nodeIds: [node.id],
2898
+ message: src ? `Actor "${src}" is not a defined actor implementation` : "Actor is not selected"
2899
+ }));
2900
+ }
2901
+ return results;
2902
+ }
2903
+ function pushMessages(results, messages, level, nodeIds, edgeIds) {
2904
+ messages?.forEach((message) => {
2905
+ results.push(issue({
2906
+ ...message,
2907
+ level,
2908
+ nodeIds,
2909
+ edgeIds
2910
+ }));
2911
+ });
2912
+ }
2913
+ function validateGraph(graph, sourceAllowed) {
2914
+ const results = [];
2915
+ const nonTempNodes = graph.nodes.filter((n) => !n.data?.temp);
2916
+ const nonTempEdges = graph.edges.filter((e) => !e.data?.temp);
2917
+ const nonTempGraph = {
2918
+ ...graph,
2919
+ nodes: nonTempNodes,
2920
+ edges: nonTempEdges
2921
+ };
2922
+ const reachableNodes = computeReachableGraphNodes(nonTempGraph);
2923
+ for (const node of nonTempNodes) {
2924
+ pushMessages(results, getNodeErrors(nonTempGraph, node), "error", [node.id], []);
2925
+ pushMessages(results, getNodeWarningsWithReachability(nonTempGraph, node, reachableNodes), "warning", [node.id], []);
2926
+ }
2927
+ for (const edge of nonTempEdges) {
2928
+ pushMessages(results, getEdgeErrors(nonTempGraph, edge), "error", [], [edge.id]);
2929
+ pushMessages(results, getEdgeWarnings(nonTempGraph, edge), "warning", [], [edge.id]);
2930
+ }
2931
+ results.push(...getOnDoneWarnings(nonTempGraph, reachableNodes));
2932
+ results.push(...getSourceWarnings(nonTempGraph));
2933
+ results.push(...getSourceRestrictionErrors(nonTempGraph, sourceAllowed));
2934
+ return results;
2935
+ }
2936
+ function validationOk(issues) {
2937
+ return !issues.some((item) => item.level === "error");
2938
+ }
2939
+ const validationIssueTargetSchema = object({
2940
+ nodeIds: array(string()).optional(),
2941
+ edgeIds: array(string()).optional(),
2942
+ source: object({
2943
+ path: string().optional(),
2944
+ start: number().optional(),
2945
+ end: number().optional()
2946
+ }).optional()
2947
+ });
2948
+ const validationFixSchema = object({
2949
+ kind: validationFixKindSchema,
2950
+ source: validationFixSourceSchema,
2951
+ title: string(),
2952
+ patches: array(unknown()).optional(),
2953
+ output: unknown().optional(),
2954
+ diff: unknown().optional(),
2955
+ confidence: number().optional(),
2956
+ rationale: string().optional(),
2957
+ rank: number().optional(),
2958
+ postValidation: object({
2959
+ ok: boolean(),
2960
+ remainingIssueCodes: array(validationIssueCodeSchema)
2961
+ }).optional()
2962
+ });
2963
+ const validationIssueSchema = object({
2964
+ code: validationIssueCodeSchema,
2965
+ kind: validationIssueKindSchema,
2966
+ level: validationLevelSchema,
2967
+ message: string(),
2968
+ target: validationIssueTargetSchema,
2969
+ metadata: record(string(), unknown()).optional(),
2970
+ fixes: array(validationFixSchema).optional(),
2971
+ source: _enum(["deterministic", "llm"]).optional(),
2972
+ nodeIds: array(string()),
2973
+ edgeIds: array(string())
2974
+ });
2975
+ const machineFormatSchema = _enum([
2976
+ "xstate",
2977
+ "json",
2978
+ "yaml",
2979
+ "scxml",
2980
+ "mermaid"
2981
+ ]);
2982
+ const xstateSourceProfileSchema = _enum([
2983
+ "auto",
2984
+ "xstate-v5",
2985
+ "xstate-v6-alpha"
2986
+ ]);
2987
+ const machineInputSchema = object({
2988
+ machine: union([record(string(), unknown()), string()]),
2989
+ format: machineFormatSchema.optional(),
2990
+ profile: xstateSourceProfileSchema.optional(),
2991
+ xstateVersion: union([literal(5), literal(6)]).optional()
2992
+ });
2993
+ const graphSchema = record(string(), unknown());
2994
+ function asError(error) {
2995
+ return { message: error instanceof Error ? error.message : String(error) };
2996
+ }
2997
+ function simImplementations(graph, guardState) {
2998
+ const guards = {};
2999
+ for (const edge of graph.edges) if (edge.data.guard) {
3000
+ const guardType = edge.data.guard.type;
3001
+ guards[guardType] = ({ event }) => {
3002
+ if (event["@xstate.guard"] === guardType) return true;
3003
+ return guardState?.[guardType] ?? false;
3004
+ };
3005
+ }
3006
+ return { guards };
3007
+ }
3008
+ const operations = {
3009
+ validate_machine: {
3010
+ description: "Validate a machine without writing files.",
3011
+ input: machineInputSchema,
3012
+ output: object({
3013
+ ok: boolean(),
3014
+ issues: array(validationIssueSchema)
3015
+ }),
3016
+ async handler(input) {
3017
+ try {
3018
+ const issues = validateGraph(machineInputToGraph(input));
3019
+ return {
3020
+ ok: validationOk(issues),
3021
+ issues
3022
+ };
3023
+ } catch (error) {
3024
+ return {
3025
+ ok: false,
3026
+ issues: [{
3027
+ code: "parse_error",
3028
+ kind: "correctness",
3029
+ level: "error",
3030
+ message: asError(error).message,
3031
+ target: {
3032
+ nodeIds: [],
3033
+ edgeIds: []
3034
+ },
3035
+ source: "deterministic",
3036
+ nodeIds: [],
3037
+ edgeIds: []
3038
+ }]
3039
+ };
3040
+ }
3041
+ }
3042
+ },
3043
+ convert_to_graph: {
3044
+ description: "Convert a machine into an internal graph for patching.",
3045
+ input: machineInputSchema,
3046
+ output: object({ graph: graphSchema }),
3047
+ async handler(input) {
3048
+ return { graph: machineInputToGraph(input) };
3049
+ }
3050
+ },
3051
+ convert_format: {
3052
+ description: "Convert a machine or graph to a machine format.",
3053
+ input: object({
3054
+ machine: union([record(string(), unknown()), string()]).optional(),
3055
+ graph: graphSchema.optional(),
3056
+ format: machineFormatSchema.optional(),
3057
+ to: machineFormatSchema
3058
+ }).refine((value) => Boolean(value.machine) !== Boolean(value.graph), { message: "Provide exactly one of machine or graph." }),
3059
+ output: object({
3060
+ format: machineFormatSchema,
3061
+ output: union([record(string(), unknown()), string()])
3062
+ }),
3063
+ async handler(input) {
3064
+ const graph = input.graph ?? machineInputToGraph({
3065
+ machine: input.machine,
3066
+ format: input.format
3067
+ });
3068
+ return {
3069
+ format: input.to,
3070
+ output: graphToFormat(graph, input.to)
3071
+ };
3072
+ }
3073
+ },
3074
+ apply_patch: {
3075
+ description: "Apply semantic machine patches to a graph without writing files.",
3076
+ input: object({
3077
+ graph: graphSchema,
3078
+ patches: array(machinePatchSchema),
3079
+ outputFormat: machineFormatSchema.default("json")
3080
+ }),
3081
+ output: union([object({
3082
+ ok: literal(true),
3083
+ graph: graphSchema,
3084
+ machine: union([record(string(), unknown()), string()]),
3085
+ format: machineFormatSchema,
3086
+ diff: unknown(),
3087
+ patches: array(machinePatchSchema)
3088
+ }), object({
3089
+ ok: literal(false),
3090
+ reason: string(),
3091
+ errors: array(object({ message: string() }))
3092
+ })]),
3093
+ async handler(input) {
3094
+ try {
3095
+ const graph = applyGraphPatches(input.graph, input.patches);
3096
+ createMachine(graphToMachineConfig(graph));
3097
+ const diff = diffGraphsSemantically(input.graph, graph).diff;
3098
+ return {
3099
+ ok: true,
3100
+ graph,
3101
+ machine: graphToFormat(graph, input.outputFormat),
3102
+ format: input.outputFormat,
3103
+ diff,
3104
+ patches: input.patches
3105
+ };
3106
+ } catch (error) {
3107
+ return {
3108
+ ok: false,
3109
+ reason: "validation",
3110
+ errors: [asError(error)]
3111
+ };
3112
+ }
3113
+ }
3114
+ },
3115
+ diff_machines: {
3116
+ description: "Compute semantic diff between two machines.",
3117
+ input: object({
3118
+ left: machineInputSchema,
3119
+ right: machineInputSchema,
3120
+ includePatches: boolean().optional()
3121
+ }),
3122
+ output: object({
3123
+ diff: unknown(),
3124
+ patches: array(machinePatchSchema).optional()
3125
+ }),
3126
+ async handler(input) {
3127
+ return { diff: diffGraphsSemantically(machineInputToGraph(input.left), machineInputToGraph(input.right)).diff };
3128
+ }
3129
+ },
3130
+ generate_paths: {
3131
+ description: "Generate paths through a machine.",
3132
+ input: machineInputSchema.extend({
3133
+ targetPath: array(string()).optional(),
3134
+ kind: _enum(["shortest", "simple"]).default("shortest"),
3135
+ max: number().int().positive().optional()
3136
+ }),
3137
+ output: object({ paths: array(unknown()) }),
3138
+ async handler(input) {
3139
+ const result = generateGraphPathsData(machineInputToGraph(input), {
3140
+ strategy: input.kind,
3141
+ reduceDuplicates: false,
3142
+ preferTransitionCoverage: false,
3143
+ limit: input.max
3144
+ });
3145
+ return { paths: input.targetPath ? result.paths.filter((path) => path.finalStatePath === input.targetPath.join(".")) : result.paths };
3146
+ }
3147
+ },
3148
+ simulate: {
3149
+ description: "Simulate one event from a machine state.",
3150
+ input: machineInputSchema.extend({
3151
+ stateValue: unknown(),
3152
+ event: object({ type: string() }).passthrough(),
3153
+ guardState: record(string(), boolean()).optional()
3154
+ }),
3155
+ output: object({
3156
+ nextStateValue: unknown(),
3157
+ actions: array(unknown()),
3158
+ changed: boolean(),
3159
+ microsteps: array(unknown()).optional()
3160
+ }),
3161
+ async handler(input) {
3162
+ const graph = machineInputToGraph(input);
3163
+ const machine = createMachine(graphToMachineConfig(graph)).provide(simImplementations(graph, input.guardState));
3164
+ const snapshot = machine.resolveState({
3165
+ value: input.stateValue,
3166
+ context: {},
3167
+ status: "active"
3168
+ });
3169
+ const [nextSnapshot, actions] = transition(machine, snapshot, input.event);
3170
+ return {
3171
+ nextStateValue: nextSnapshot.value,
3172
+ actions,
3173
+ changed: JSON.stringify(nextSnapshot.value) !== JSON.stringify(snapshot.value)
3174
+ };
3175
+ }
3176
+ }
3177
+ };
3178
+
110
3179
  //#endregion
111
3180
  //#region ../editor-sync/src/internal/sourceLocations.ts
112
3181
  const sourceFileUris = /* @__PURE__ */ new WeakMap();
113
3182
  function collectXStateSourceLocations(sourceText, document, options = {}) {
114
- const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$1(document.fileName));
3183
+ const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2(document.fileName));
115
3184
  const uri = document.uri?.toString() ?? document.fileName;
116
3185
  sourceFileUris.set(sourceFile, uri);
117
3186
  const bindings = collectBindings(sourceFile, uri, document.fileName, options);
@@ -119,13 +3188,18 @@ function collectXStateSourceLocations(sourceText, document, options = {}) {
119
3188
  const root = findMachineConfigObjectLiteral(sourceFile, bindings);
120
3189
  if (!root) return;
121
3190
  const states = [];
122
- collectStateLocations(root, [], states, bindings, staticValues, root);
3191
+ const statesObjectLocations = [];
3192
+ collectStateLocations(root, [], states, statesObjectLocations, bindings, staticValues, root);
3193
+ const rootProfile = detectXStateSourceProfile(sourceText);
3194
+ const referencedStateSymbols = new Set([...states, ...statesObjectLocations].map((state) => state.symbol).filter((symbol) => typeof symbol === "string"));
123
3195
  return {
124
3196
  version: 1,
3197
+ profile: rootProfile === "xstate-v6-alpha" || [...referencedStateSymbols].some((symbol) => detectXStateSourceProfile(bindings.get(symbol)?.sourceFile.text ?? "") === "xstate-v6-alpha") ? "xstate-v6-alpha" : rootProfile,
125
3198
  root: {
126
3199
  uri,
127
- range: toSourceRange(sourceFile, root)
3200
+ range: toSourceRange$1(sourceFile, root)
128
3201
  },
3202
+ statesObject: statesObjectLocations[0],
129
3203
  states,
130
3204
  staticValues: collectStaticValueReferences(sourceFile, uri, root, staticValues)
131
3205
  };
@@ -143,7 +3217,7 @@ function collectStaticValueBindings(sourceFile) {
143
3217
  kind: "const",
144
3218
  symbol: declaration.name.text,
145
3219
  value,
146
- valueRange: toSourceRange(sourceFile, declaration.initializer)
3220
+ valueRange: toSourceRange$1(sourceFile, declaration.initializer)
147
3221
  });
148
3222
  }
149
3223
  continue;
@@ -158,15 +3232,18 @@ function collectStaticValueBindings(sourceFile) {
158
3232
  kind: "enumMember",
159
3233
  symbol,
160
3234
  value,
161
- valueRange: toSourceRange(sourceFile, member.initializer)
3235
+ valueRange: toSourceRange$1(sourceFile, member.initializer)
162
3236
  });
163
3237
  }
164
3238
  }
165
3239
  return bindings;
166
3240
  }
167
- function collectBindings(sourceFile, uri, fileName, options) {
3241
+ function collectBindings(sourceFile, uri, fileName, options, cache = /* @__PURE__ */ new Map()) {
3242
+ const cached = cache.get(fileName);
3243
+ if (cached) return cached;
168
3244
  const bindings = collectTopLevelBindings(sourceFile, uri);
169
- addImportedBindings(bindings, sourceFile, uri, fileName, options);
3245
+ cache.set(fileName, bindings);
3246
+ addImportedBindings(bindings, sourceFile, uri, fileName, options, cache);
170
3247
  return bindings;
171
3248
  }
172
3249
  function collectTopLevelBindings(sourceFile, uri) {
@@ -198,7 +3275,7 @@ function collectTopLevelBindings(sourceFile, uri) {
198
3275
  }
199
3276
  return bindings;
200
3277
  }
201
- function addImportedBindings(bindings, sourceFile, uri, fileName, options) {
3278
+ function addImportedBindings(bindings, sourceFile, uri, fileName, options, cache) {
202
3279
  const resolver = options.resolver;
203
3280
  if (!resolver) return;
204
3281
  for (const statement of sourceFile.statements) {
@@ -210,9 +3287,9 @@ function addImportedBindings(bindings, sourceFile, uri, fileName, options) {
210
3287
  uri
211
3288
  }, statement.moduleSpecifier.text);
212
3289
  if (!importedDocument) continue;
213
- const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$1(importedDocument.fileName));
3290
+ const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(importedDocument.fileName));
214
3291
  sourceFileUris.set(importedSourceFile, importedDocument.uri);
215
- const importedBindings = collectBindings(importedSourceFile, importedDocument.uri, importedDocument.fileName, options);
3292
+ const importedBindings = collectBindings(importedSourceFile, importedDocument.uri, importedDocument.fileName, options, cache);
216
3293
  for (const element of namedBindings.elements) {
217
3294
  const importedName = element.propertyName?.text ?? element.name.text;
218
3295
  const binding = importedBindings.get(importedName);
@@ -235,11 +3312,20 @@ function findMachineConfigObjectLiteral(sourceFile, bindings) {
235
3312
  visit(sourceFile);
236
3313
  return found;
237
3314
  }
238
- function collectStateLocations(stateObject, parentPath, locations, bindings, staticValues, root) {
239
- const statesObject = getObjectLiteralProperty(stateObject, "states");
3315
+ function collectStateLocations(stateObject, parentPath, locations, statesObjectLocations, bindings, staticValues, root) {
3316
+ const statesObject = resolveObjectLiteralProperty(stateObject, "states", bindings);
240
3317
  if (!statesObject) return;
241
- for (const property of statesObject.properties) {
242
- const resolved = resolveStateProperty(property, bindings, staticValues, root);
3318
+ statesObjectLocations.push({
3319
+ uri: statesObject.uri,
3320
+ path: parentPath,
3321
+ kind: statesObject.kind,
3322
+ symbol: statesObject.symbol,
3323
+ keyRange: statesObject.keyRange,
3324
+ referenceRange: statesObject.referenceRange,
3325
+ range: toSourceRange$1(statesObject.sourceFile, statesObject.object)
3326
+ });
3327
+ for (const property of statesObject.object.properties) {
3328
+ const resolved = resolveStateProperty(property, statesObject.bindings, staticValues, root);
243
3329
  if (!resolved) continue;
244
3330
  const path = [...parentPath, resolved.key];
245
3331
  locations.push({
@@ -251,9 +3337,9 @@ function collectStateLocations(stateObject, parentPath, locations, bindings, sta
251
3337
  referenceRange: resolved.referenceRange,
252
3338
  keySource: resolved.keySource,
253
3339
  keyReplacementText: resolved.keyReplacementText,
254
- range: toSourceRange(resolved.sourceFile, resolved.stateObject)
3340
+ range: toSourceRange$1(resolved.sourceFile, resolved.stateObject)
255
3341
  });
256
- collectStateLocations(resolved.stateObject, path, locations, resolved.bindings, staticValues, root);
3342
+ collectStateLocations(resolved.stateObject, path, locations, statesObjectLocations, resolved.bindings, staticValues, root);
257
3343
  }
258
3344
  }
259
3345
  function resolveStateProperty(property, bindings, staticValues, root) {
@@ -264,8 +3350,8 @@ function resolveStateProperty(property, bindings, staticValues, root) {
264
3350
  key: property.name.text,
265
3351
  kind: binding.kind,
266
3352
  symbol: property.name.text,
267
- keyRange: toSourceRange(property.getSourceFile(), property.name),
268
- referenceRange: toSourceRange(property.getSourceFile(), property.name),
3353
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3354
+ referenceRange: toSourceRange$1(property.getSourceFile(), property.name),
269
3355
  stateObject: binding.node,
270
3356
  sourceFile: binding.sourceFile,
271
3357
  uri: binding.uri,
@@ -279,7 +3365,7 @@ function resolveStateProperty(property, bindings, staticValues, root) {
279
3365
  if (inlineObject) return {
280
3366
  key: resolvedKey.key,
281
3367
  kind: "inline",
282
- keyRange: toSourceRange(property.getSourceFile(), property.name),
3368
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
283
3369
  keySource: resolvedKey.keySource,
284
3370
  keyReplacementText: resolvedKey.keyReplacementText,
285
3371
  stateObject: inlineObject,
@@ -294,8 +3380,8 @@ function resolveStateProperty(property, bindings, staticValues, root) {
294
3380
  key: resolvedKey.key,
295
3381
  kind: binding.kind,
296
3382
  symbol: property.initializer.text,
297
- keyRange: toSourceRange(property.getSourceFile(), property.name),
298
- referenceRange: toSourceRange(property.getSourceFile(), property.initializer),
3383
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3384
+ referenceRange: toSourceRange$1(property.getSourceFile(), property.initializer),
299
3385
  keySource: resolvedKey.keySource,
300
3386
  keyReplacementText: resolvedKey.keyReplacementText,
301
3387
  stateObject: binding.node,
@@ -327,11 +3413,34 @@ function resolveIdentifierObject(expression, bindings) {
327
3413
  const binding = bindings.get(unwrapped.text);
328
3414
  return binding?.kind === "objectReference" ? binding.node : null;
329
3415
  }
330
- function getObjectLiteralProperty(objectLiteral, propertyName) {
3416
+ function resolveObjectLiteralProperty(objectLiteral, propertyName, bindings) {
331
3417
  for (const property of objectLiteral.properties) {
332
3418
  if (!ts$1.isPropertyAssignment(property)) continue;
333
3419
  if (getPropertyNameText$1(property.name) !== propertyName) continue;
334
- return unwrapObjectLiteralExpression(property.initializer);
3420
+ const inlineObject = unwrapObjectLiteralExpression(property.initializer);
3421
+ if (inlineObject) return {
3422
+ object: inlineObject,
3423
+ kind: "inline",
3424
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3425
+ sourceFile: property.getSourceFile(),
3426
+ uri: getSourceFileUri(property.getSourceFile()),
3427
+ bindings
3428
+ };
3429
+ if (ts$1.isIdentifier(property.initializer)) {
3430
+ const binding = bindings.get(property.initializer.text);
3431
+ if (binding?.kind !== "objectReference") return null;
3432
+ return {
3433
+ object: binding.node,
3434
+ kind: binding.kind,
3435
+ symbol: property.initializer.text,
3436
+ keyRange: toSourceRange$1(property.getSourceFile(), property.name),
3437
+ referenceRange: toSourceRange$1(property.getSourceFile(), property.initializer),
3438
+ sourceFile: binding.sourceFile,
3439
+ uri: binding.uri,
3440
+ bindings: binding.bindings
3441
+ };
3442
+ }
3443
+ return null;
335
3444
  }
336
3445
  return null;
337
3446
  }
@@ -383,7 +3492,7 @@ function collectStaticValueReferences(sourceFile, uri, root, staticValues) {
383
3492
  const binding = getStaticValueBindingForExpression(node.expression, staticValues);
384
3493
  if (binding && !isStateKeyComputedPropertyName(node)) references.push({
385
3494
  uri,
386
- range: toSourceRange(sourceFile, node),
3495
+ range: toSourceRange$1(sourceFile, node),
387
3496
  value: binding.value
388
3497
  });
389
3498
  return;
@@ -396,7 +3505,7 @@ function collectStaticValueReferences(sourceFile, uri, root, staticValues) {
396
3505
  if (binding) {
397
3506
  references.push({
398
3507
  uri,
399
- range: toSourceRange(sourceFile, node),
3508
+ range: toSourceRange$1(sourceFile, node),
400
3509
  value: binding.value
401
3510
  });
402
3511
  return;
@@ -478,7 +3587,7 @@ function unwrapExpression$1(expression) {
478
3587
  return current;
479
3588
  }
480
3589
  }
481
- function toSourceRange(sourceFile, node) {
3590
+ function toSourceRange$1(sourceFile, node) {
482
3591
  const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
483
3592
  const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
484
3593
  return {
@@ -488,7 +3597,7 @@ function toSourceRange(sourceFile, node) {
488
3597
  endChar: end.character
489
3598
  };
490
3599
  }
491
- function getScriptKind$1(fileName) {
3600
+ function getScriptKind$2(fileName) {
492
3601
  if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
493
3602
  if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
494
3603
  if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
@@ -512,7 +3621,7 @@ function parseSourceToMachine(text, document, options = {}) {
512
3621
  range: location.range,
513
3622
  newText: JSON.stringify(location.value)
514
3623
  }));
515
- const stateReplacements = sourceLocations.states.filter((location) => location.path.length === 1 && location.keyRange).flatMap((location) => {
3624
+ const stateReplacements = sourceLocations.states.filter((location) => location.path.length === 1 && location.keyRange && !sourceLocations.statesObject?.referenceRange).flatMap((location) => {
516
3625
  const keyRange = location.keyRange;
517
3626
  const keyReplacement = location.keyReplacementText ? [{
518
3627
  range: keyRange,
@@ -530,7 +3639,93 @@ function parseSourceToMachine(text, document, options = {}) {
530
3639
  newText: `${location.path[0]}: ${stateText}`
531
3640
  }];
532
3641
  });
533
- return applySourceRangeReplacements(text, [...staticValueReplacements, ...stateReplacements].filter(removeOverlappingRanges).sort(compareSourceRangesDescending));
3642
+ const statesObjectReplacements = sourceLocations.statesObject?.referenceRange && sourceLocations.statesObject.kind !== "inline" ? (() => {
3643
+ const statesText = readHydratedStatesObject(sourceLocations.statesObject.uri, sourceLocations.statesObject.range, sourceLocations.states, options.resolver);
3644
+ return statesText ? [{
3645
+ range: sourceLocations.statesObject.referenceRange,
3646
+ newText: statesText
3647
+ }] : [];
3648
+ })() : [];
3649
+ 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] : []]));
3650
+ const hydratedText = applySourceRangeReplacements(text, [
3651
+ ...staticValueReplacements,
3652
+ ...stateReplacements,
3653
+ ...statesObjectReplacements,
3654
+ ...importReplacements
3655
+ ].filter(removeOverlappingRanges).sort(compareSourceRangesDescending));
3656
+ if (sourceLocations.profile === "xstate-v6-alpha") return `import { createMachine } from "xstate";
3657
+
3658
+ export const machine = createMachine(${serializeJS(xstateSourceToConfig(hydratedText, { profile: "xstate-v6-alpha" }), 0)});
3659
+ `;
3660
+ return hydratedText;
3661
+ }
3662
+ function readHydratedStatesObject(uri, range, states, resolver) {
3663
+ if (!resolver) return null;
3664
+ const document = resolver.read(uri);
3665
+ if (!document) return null;
3666
+ const replacements = states.filter((state) => state.path.length === 1 && state.referenceRange).flatMap((state) => {
3667
+ const stateText = readSourceRange(state.uri, state.range, resolver);
3668
+ if (!stateText || !state.referenceRange) return [];
3669
+ const isShorthand = state.keyRange && areSourceRangesEqual(state.referenceRange, state.keyRange);
3670
+ return [{
3671
+ range: state.referenceRange,
3672
+ newText: isShorthand ? `${state.path[0]}: ${stateText}` : stateText
3673
+ }];
3674
+ });
3675
+ return applySourceRangeReplacementsToSlice(document.text, range, replacements);
3676
+ }
3677
+ function applySourceRangeReplacementsToSlice(text, range, replacements) {
3678
+ const sliceStart = offsetAtRangePosition(text, range.startLine, range.startChar);
3679
+ const sliceEnd = offsetAtRangePosition(text, range.endLine, range.endChar);
3680
+ let updated = text.slice(sliceStart, sliceEnd);
3681
+ for (const replacement of replacements.sort(compareSourceRangesDescending)) {
3682
+ const start = offsetAtRangePosition(text, replacement.range.startLine, replacement.range.startChar);
3683
+ const end = offsetAtRangePosition(text, replacement.range.endLine, replacement.range.endChar);
3684
+ if (start < sliceStart || end > sliceEnd) continue;
3685
+ const localStart = start - sliceStart;
3686
+ const localEnd = end - sliceStart;
3687
+ updated = `${updated.slice(0, localStart)}${replacement.newText}${updated.slice(localEnd)}`;
3688
+ }
3689
+ return updated;
3690
+ }
3691
+ function collectHydratedImportReplacements(text, document, hydratedSymbols) {
3692
+ if (hydratedSymbols.size === 0) return [];
3693
+ const sourceFile = ts$1.createSourceFile(document.fileName, text, ts$1.ScriptTarget.Latest, true, getScriptKind$1(document.fileName));
3694
+ const replacements = [];
3695
+ for (const statement of sourceFile.statements) {
3696
+ if (!ts$1.isImportDeclaration(statement) || !ts$1.isStringLiteral(statement.moduleSpecifier)) continue;
3697
+ if (!statement.moduleSpecifier.text.startsWith(".")) continue;
3698
+ const namedBindings = statement.importClause?.namedBindings;
3699
+ if (!namedBindings || !ts$1.isNamedImports(namedBindings)) continue;
3700
+ const importedNames = namedBindings.elements.map((element) => element.name.text);
3701
+ if (importedNames.length === 0 || !importedNames.every((name) => hydratedSymbols.has(name))) continue;
3702
+ replacements.push({
3703
+ range: toSourceRange(sourceFile, statement, { includeTrailingNewline: true }),
3704
+ newText: ""
3705
+ });
3706
+ }
3707
+ return replacements;
3708
+ }
3709
+ function toSourceRange(sourceFile, node, options = {}) {
3710
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
3711
+ let endOffset = node.getEnd();
3712
+ if (options.includeTrailingNewline) {
3713
+ const match = sourceFile.text.slice(endOffset).match(/^\r?\n/);
3714
+ if (match) endOffset += match[0].length;
3715
+ }
3716
+ const end = sourceFile.getLineAndCharacterOfPosition(endOffset);
3717
+ return {
3718
+ startLine: start.line,
3719
+ startChar: start.character,
3720
+ endLine: end.line,
3721
+ endChar: end.character
3722
+ };
3723
+ }
3724
+ function getScriptKind$1(fileName) {
3725
+ if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
3726
+ if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
3727
+ if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
3728
+ return ts$1.ScriptKind.TS;
534
3729
  }
535
3730
  function removeOverlappingRanges(replacement, index, replacements) {
536
3731
  return !replacements.some((candidate, candidateIndex) => {