@statelyai/sdk 0.26.2 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +30 -2
  2. package/dist/auth.d.mts +1 -1
  3. package/dist/{editorHostSession-DFjM-RoT.d.mts → editorHostSession-CWAuoMtC.d.mts} +9 -3
  4. package/dist/{editorHostSession-CwGeXJ6g.mjs → editorHostSession-shrjfosC.mjs} +6 -3
  5. package/dist/editorHostSession.d.mts +1 -1
  6. package/dist/editorHostSession.mjs +1 -1
  7. package/dist/embed.d.mts +6 -3
  8. package/dist/embed.mjs +23 -1
  9. package/dist/embedPanel-Bpbq4sfJ.d.mts +13 -0
  10. package/dist/{graph-DoUReqxz.d.mts → graph-BwHnhzpC.d.mts} +48 -6
  11. package/dist/graph.d.mts +1 -1
  12. package/dist/graph.mjs +36 -4
  13. package/dist/{index-DKRB-u93.d.mts → index-D2I1qPsf.d.mts} +19 -3
  14. package/dist/index.d.mts +8 -7
  15. package/dist/index.mjs +6 -6
  16. package/dist/{inspect-Br_vS_lK.d.mts → inspect-D61HS-t7.d.mts} +2 -5
  17. package/dist/{inspect-BA3EPIz9.mjs → inspect-_E2lVbQA.mjs} +2 -4
  18. package/dist/inspect.d.mts +1 -1
  19. package/dist/inspect.mjs +2 -2
  20. package/dist/patchTypes.d.mts +1 -1
  21. package/dist/protocol.d.mts +46 -15
  22. package/dist/{statelyPragma-CWxhEIcZ.mjs → statelyPragma-DD7wx7-b.mjs} +1198 -31
  23. package/dist/{studio-B6P177N3.d.mts → studio-B9-1Ynrx.d.mts} +4 -2
  24. package/dist/{studio-iNSHtNmP.mjs → studio-CNkxMZyz.mjs} +4 -1
  25. package/dist/studio.d.mts +1 -1
  26. package/dist/studio.mjs +1 -1
  27. package/dist/sync.d.mts +12 -4
  28. package/dist/sync.mjs +2920 -262
  29. package/dist/{version-DKSLBwJ9.mjs → version-7SUCFMqb.mjs} +34 -8
  30. package/package.json +3 -3
  31. package/schemas/xstate.schema.json +41 -5
  32. /package/dist/{auth-B0ZTKri2.d.mts → auth-BPyCCHYo.d.mts} +0 -0
  33. /package/dist/{editor-sync-types-C-zWaaVk.d.mts → editor-sync-types-CTKArS8n.d.mts} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { a as discriminatedUnion, c as object, d as union, f as unknown, i as custom, l as record, n as array, o as literal, r as boolean, s as number, t as _enum, u as string } from "./openapi-BTDjYWXd.mjs";
2
2
  import ts from "typescript";
3
+ import "xstate";
3
4
  import "xstate/graph";
4
5
 
5
6
  //#region ../../node_modules/.pnpm/js-yaml@4.2.0/node_modules/js-yaml/dist/js-yaml.mjs
@@ -2333,6 +2334,57 @@ var index_vite_proxy_tmp_default = import_js_yaml.default;
2333
2334
 
2334
2335
  //#endregion
2335
2336
  //#region ../graph-tools/dist/index.mjs
2337
+ /**
2338
+ * Convert a resolved {@link GraphNode} back into a {@link NodeConfig}.
2339
+ *
2340
+ * Faithful and complete: round-tripping through `createGraphNode` yields a
2341
+ * deep-equal node. Optional fields are only included when present; ports are
2342
+ * deep-copied so the config does not share port objects with the source node.
2343
+ */
2344
+ function toNodeConfig(node) {
2345
+ const config = { id: node.id };
2346
+ if (node.parentId != null) config.parentId = node.parentId;
2347
+ if (node.initialNodeId != null) config.initialNodeId = node.initialNodeId;
2348
+ if (node.label != null) config.label = node.label;
2349
+ if (node.data != null) config.data = node.data;
2350
+ if (node.ports !== void 0) config.ports = node.ports.map((p) => ({ ...p }));
2351
+ if (node.x !== void 0) config.x = node.x;
2352
+ if (node.y !== void 0) config.y = node.y;
2353
+ if (node.width !== void 0) config.width = node.width;
2354
+ if (node.height !== void 0) config.height = node.height;
2355
+ if (node.shape !== void 0) config.shape = node.shape;
2356
+ if (node.color !== void 0) config.color = node.color;
2357
+ if (node.style !== void 0) config.style = node.style;
2358
+ return config;
2359
+ }
2360
+ /**
2361
+ * Convert a resolved {@link GraphEdge} back into an {@link EdgeConfig}.
2362
+ *
2363
+ * Faithful and complete: round-tripping through `createGraphEdge` yields a
2364
+ * deep-equal edge. Optional fields are only included when present.
2365
+ */
2366
+ function toEdgeConfig(edge) {
2367
+ const config = {
2368
+ id: edge.id,
2369
+ sourceId: edge.sourceId,
2370
+ targetId: edge.targetId
2371
+ };
2372
+ if (edge.label != null) config.label = edge.label;
2373
+ if (edge.data != null) config.data = edge.data;
2374
+ if (edge.weight !== void 0) config.weight = edge.weight;
2375
+ if (edge.mode !== void 0) config.mode = edge.mode;
2376
+ if (edge.sourcePort !== void 0) config.sourcePort = edge.sourcePort;
2377
+ if (edge.targetPort !== void 0) config.targetPort = edge.targetPort;
2378
+ if (edge.points !== void 0) config.points = edge.points.map((p) => ({ ...p }));
2379
+ if (edge.routing !== void 0) config.routing = edge.routing;
2380
+ if (edge.x !== void 0) config.x = edge.x;
2381
+ if (edge.y !== void 0) config.y = edge.y;
2382
+ if (edge.width !== void 0) config.width = edge.width;
2383
+ if (edge.height !== void 0) config.height = edge.height;
2384
+ if (edge.color !== void 0) config.color = edge.color;
2385
+ if (edge.style !== void 0) config.style = edge.style;
2386
+ return config;
2387
+ }
2336
2388
  const LAYOUT_KEYS = {
2337
2389
  node: [
2338
2390
  "x",
@@ -2355,6 +2407,872 @@ const LAYOUT_KEYS = {
2355
2407
  ]
2356
2408
  };
2357
2409
  new Set(LAYOUT_KEYS.node), new Set(LAYOUT_KEYS.edge);
2410
+ /** Shallow-compare two values, returning true if they differ. */
2411
+ function differs(a, b) {
2412
+ if (a === b) return false;
2413
+ if (a == null || b == null) return a !== b;
2414
+ if (typeof a === "object" && typeof b === "object") return JSON.stringify(a) !== JSON.stringify(b);
2415
+ return true;
2416
+ }
2417
+ const NODE_COMPARE_KEYS = [
2418
+ "parentId",
2419
+ "initialNodeId",
2420
+ "label",
2421
+ "data",
2422
+ "ports",
2423
+ "x",
2424
+ "y",
2425
+ "width",
2426
+ "height",
2427
+ "shape",
2428
+ "color",
2429
+ "style"
2430
+ ];
2431
+ const EDGE_COMPARE_KEYS = [
2432
+ "sourceId",
2433
+ "targetId",
2434
+ "label",
2435
+ "data",
2436
+ "weight",
2437
+ "mode",
2438
+ "sourcePort",
2439
+ "targetPort",
2440
+ "points",
2441
+ "routing",
2442
+ "x",
2443
+ "y",
2444
+ "width",
2445
+ "height",
2446
+ "color",
2447
+ "style"
2448
+ ];
2449
+ /**
2450
+ * Compute a structured diff from graph `a` to graph `b` by matching IDs.
2451
+ *
2452
+ * @example
2453
+ * ```ts
2454
+ * import { createGraph, getDiff } from '@statelyai/graph';
2455
+ *
2456
+ * const a = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
2457
+ * const b = createGraph({ nodes: [{ id: 'n1', label: 'hello' }, { id: 'n2' }], edges: [] });
2458
+ *
2459
+ * const diff = getDiff(a, b);
2460
+ * // diff.nodes.added → [{ id: 'n2' }]
2461
+ * // diff.nodes.updated → [{ id: 'n1', old: { label: '' }, new: { label: 'hello' } }]
2462
+ * ```
2463
+ */
2464
+ function getDiff(a, b) {
2465
+ const aNodeMap = new Map(a.nodes.map((n) => [n.id, n]));
2466
+ const bNodeMap = new Map(b.nodes.map((n) => [n.id, n]));
2467
+ const aEdgeMap = new Map(a.edges.map((e) => [e.id, e]));
2468
+ const bEdgeMap = new Map(b.edges.map((e) => [e.id, e]));
2469
+ const diff = {
2470
+ nodes: {
2471
+ added: [],
2472
+ removed: [],
2473
+ updated: []
2474
+ },
2475
+ edges: {
2476
+ added: [],
2477
+ removed: [],
2478
+ updated: []
2479
+ }
2480
+ };
2481
+ for (const [id, nodeB] of bNodeMap) {
2482
+ const nodeA = aNodeMap.get(id);
2483
+ if (!nodeA) diff.nodes.added.push(toNodeConfig(nodeB));
2484
+ else {
2485
+ const oldPartial = {};
2486
+ const newPartial = {};
2487
+ for (const key of NODE_COMPARE_KEYS) {
2488
+ const oldValue = nodeA[key] ?? null;
2489
+ const newValue = nodeB[key] ?? null;
2490
+ if (differs(oldValue, newValue)) {
2491
+ oldPartial[key] = oldValue;
2492
+ newPartial[key] = newValue;
2493
+ }
2494
+ }
2495
+ if (Object.keys(oldPartial).length > 0) diff.nodes.updated.push({
2496
+ id,
2497
+ old: oldPartial,
2498
+ new: newPartial
2499
+ });
2500
+ }
2501
+ }
2502
+ for (const [id, nodeA] of aNodeMap) if (!bNodeMap.has(id)) diff.nodes.removed.push(toNodeConfig(nodeA));
2503
+ for (const [id, edgeB] of bEdgeMap) {
2504
+ const edgeA = aEdgeMap.get(id);
2505
+ if (!edgeA) diff.edges.added.push(toEdgeConfig(edgeB));
2506
+ else {
2507
+ const oldPartial = {};
2508
+ const newPartial = {};
2509
+ for (const key of EDGE_COMPARE_KEYS) {
2510
+ const oldValue = edgeA[key] ?? null;
2511
+ const newValue = edgeB[key] ?? null;
2512
+ if (differs(oldValue, newValue)) {
2513
+ oldPartial[key] = oldValue;
2514
+ newPartial[key] = newValue;
2515
+ }
2516
+ }
2517
+ if (Object.keys(oldPartial).length > 0) diff.edges.updated.push({
2518
+ id,
2519
+ old: oldPartial,
2520
+ new: newPartial
2521
+ });
2522
+ }
2523
+ }
2524
+ for (const [id, edgeA] of aEdgeMap) if (!bEdgeMap.has(id)) diff.edges.removed.push(toEdgeConfig(edgeA));
2525
+ return diff;
2526
+ }
2527
+ /**
2528
+ * Check if a diff has zero changes.
2529
+ *
2530
+ * @example
2531
+ * ```ts
2532
+ * import { createGraph, getDiff, isEmptyDiff } from '@statelyai/graph';
2533
+ *
2534
+ * const g = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
2535
+ * const diff = getDiff(g, g);
2536
+ * isEmptyDiff(diff); // true
2537
+ * ```
2538
+ */
2539
+ function isEmptyDiff(diff) {
2540
+ return diff.nodes.added.length === 0 && diff.nodes.removed.length === 0 && diff.nodes.updated.length === 0 && diff.edges.added.length === 0 && diff.edges.removed.length === 0 && diff.edges.updated.length === 0;
2541
+ }
2542
+ function summarizeGraphDiff(diff) {
2543
+ return {
2544
+ hasChanges: !isEmptyDiff(diff),
2545
+ nodeChanges: diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length,
2546
+ edgeChanges: diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length
2547
+ };
2548
+ }
2549
+ function nodeDepth$1(graph, node) {
2550
+ let depth = 0;
2551
+ let parentId = node.parentId;
2552
+ while (parentId) {
2553
+ depth++;
2554
+ parentId = graph.nodes.find((candidate) => candidate.id === parentId)?.parentId;
2555
+ }
2556
+ return depth;
2557
+ }
2558
+ function nodeKey(node) {
2559
+ return node.data?.key || node.id;
2560
+ }
2561
+ function edgeSignature(edge) {
2562
+ return [
2563
+ edge.sourceId,
2564
+ edge.data?.eventType,
2565
+ edge.targetId,
2566
+ edge.data?.guard?.type ?? ""
2567
+ ].join("->");
2568
+ }
2569
+ function getStableNodeId(node, parentStableId, siblingIndex, preferredIds) {
2570
+ const preferredId = preferredIds?.get(node.id);
2571
+ if (preferredId !== void 0) return preferredId;
2572
+ if (node.parentId === null) return "root";
2573
+ const key = nodeKey(node);
2574
+ const suffix = siblingIndex === 0 ? key : `${key}_${siblingIndex + 1}`;
2575
+ return parentStableId ? `${parentStableId}.${suffix}` : suffix;
2576
+ }
2577
+ function getStableEdgeId(edge, siblingIndex, preferredIds) {
2578
+ const preferredId = preferredIds?.get(edge.id);
2579
+ if (preferredId !== void 0) return preferredId;
2580
+ const suffix = siblingIndex === 0 ? "" : `.${siblingIndex + 1}`;
2581
+ return edgeSignature(edge).concat(suffix);
2582
+ }
2583
+ function removePresentationFields(entity, kind) {
2584
+ const mutableEntity = entity;
2585
+ for (const key of LAYOUT_KEYS[kind]) delete mutableEntity[key];
2586
+ const data = mutableEntity.data;
2587
+ if (data && typeof data === "object" && !Array.isArray(data)) delete data.color;
2588
+ }
2589
+ /**
2590
+ * Rewrites entity ids to structural ("semantic") ids so two graphs that model
2591
+ * the same machine compare equal regardless of how their ids were minted.
2592
+ *
2593
+ * Invariant: node and edge array order is preserved exactly. Callers
2594
+ * (`alignCompareIds` in the editor's /v1/diff route) rely on
2595
+ * `normalized.nodes[i]` describing `graph.nodes[i]` to translate a semantic id
2596
+ * back to the caller's original id. Never sort, filter, or reorder the arrays
2597
+ * on the returned graph; sorting for traversal must be done on a copy, as the
2598
+ * depth ordering below does.
2599
+ *
2600
+ * Presentation-only fields defined by `LAYOUT_KEYS`, plus entity data colors,
2601
+ * are removed. When preferred ids are supplied, matching raw ids retain the
2602
+ * base graph's semantic identity so a stable-id rename is reported as an
2603
+ * update.
2604
+ *
2605
+ * Entity `data` is treated as optional throughout: sparse graphs (a node with
2606
+ * `data: {}`, an edge with `data: null`) normalize instead of throwing.
2607
+ */
2608
+ function normalizeGraphForSemanticDiff(graph, preferredIds) {
2609
+ const normalized = structuredClone(graph);
2610
+ const nodeIdMap = /* @__PURE__ */ new Map();
2611
+ const siblingKeys = /* @__PURE__ */ new Map();
2612
+ const reservedNodeIds = new Set(preferredIds?.nodes.values());
2613
+ const claimedNodeIds = /* @__PURE__ */ new Set();
2614
+ const nodesByDepth = [...normalized.nodes].sort((a, b) => nodeDepth$1(normalized, a) - nodeDepth$1(normalized, b));
2615
+ for (const node of nodesByDepth) {
2616
+ const parentStableId = node.parentId ? nodeIdMap.get(node.parentId) ?? node.parentId : null;
2617
+ const key = `${parentStableId ?? ""}:${nodeKey(node)}`;
2618
+ const siblingIndex = siblingKeys.get(key) ?? 0;
2619
+ siblingKeys.set(key, siblingIndex + 1);
2620
+ const proposedId = getStableNodeId(node, parentStableId, siblingIndex, preferredIds?.nodes);
2621
+ let stableId = proposedId;
2622
+ let collisionIndex = 2;
2623
+ while (!preferredIds?.nodes.has(node.id) && (reservedNodeIds.has(stableId) || claimedNodeIds.has(stableId))) stableId = `${proposedId}:unmatched:${collisionIndex++}`;
2624
+ claimedNodeIds.add(stableId);
2625
+ nodeIdMap.set(node.id, stableId);
2626
+ }
2627
+ for (const node of normalized.nodes) {
2628
+ removePresentationFields(node, "node");
2629
+ node.id = nodeIdMap.get(node.id) ?? node.id;
2630
+ if (node.parentId) node.parentId = nodeIdMap.get(node.parentId) ?? node.parentId;
2631
+ if (node.initialNodeId) node.initialNodeId = nodeIdMap.get(node.initialNodeId) ?? node.initialNodeId;
2632
+ if (node.data?.initialId) node.data.initialId = nodeIdMap.get(node.data.initialId) ?? node.data.initialId;
2633
+ }
2634
+ const edgeSiblingKeys = /* @__PURE__ */ new Map();
2635
+ const reservedEdgeIds = new Set(preferredIds?.edges.values());
2636
+ const claimedEdgeIds = /* @__PURE__ */ new Set();
2637
+ for (const edge of normalized.edges) {
2638
+ removePresentationFields(edge, "edge");
2639
+ edge.sourceId = nodeIdMap.get(edge.sourceId) ?? edge.sourceId;
2640
+ edge.targetId = nodeIdMap.get(edge.targetId) ?? edge.targetId;
2641
+ const key = edgeSignature(edge);
2642
+ const siblingIndex = edgeSiblingKeys.get(key) ?? 0;
2643
+ edgeSiblingKeys.set(key, siblingIndex + 1);
2644
+ const proposedId = getStableEdgeId(edge, siblingIndex, preferredIds?.edges);
2645
+ let stableId = proposedId;
2646
+ let collisionIndex = 2;
2647
+ while (!preferredIds?.edges.has(edge.id) && (reservedEdgeIds.has(stableId) || claimedEdgeIds.has(stableId))) stableId = `${proposedId}:unmatched:${collisionIndex++}`;
2648
+ claimedEdgeIds.add(stableId);
2649
+ edge.id = stableId;
2650
+ }
2651
+ if (normalized.initialNodeId) normalized.initialNodeId = nodeIdMap.get(normalized.initialNodeId) ?? normalized.initialNodeId;
2652
+ return normalized;
2653
+ }
2654
+ function diffGraphsSemantically(baseGraph, compareGraph) {
2655
+ const base = normalizeGraphForSemanticDiff(baseGraph);
2656
+ const compareNodeIds = new Set(compareGraph.nodes.map((node) => node.id));
2657
+ const compareEdgeIds = new Set(compareGraph.edges.map((edge) => edge.id));
2658
+ const compare = normalizeGraphForSemanticDiff(compareGraph, {
2659
+ nodes: new Map(baseGraph.nodes.map((node, index) => [node.id, base.nodes[index].id]).filter(([id]) => compareNodeIds.has(id))),
2660
+ edges: new Map(baseGraph.edges.map((edge, index) => [edge.id, base.edges[index].id]).filter(([id]) => compareEdgeIds.has(id)))
2661
+ });
2662
+ const diff = getDiff(base, compare);
2663
+ return {
2664
+ base,
2665
+ compare,
2666
+ diff,
2667
+ summary: summarizeGraphDiff(diff)
2668
+ };
2669
+ }
2670
+ function stableStringify(value) {
2671
+ return JSON.stringify(value, (_key, item) => {
2672
+ if (item && typeof item === "object" && !Array.isArray(item)) return Object.fromEntries(Object.entries(item).sort(([left], [right]) => left.localeCompare(right)));
2673
+ return item;
2674
+ });
2675
+ }
2676
+ function isEqual(a, b) {
2677
+ return stableStringify(a) === stableStringify(b);
2678
+ }
2679
+ function toStateColor(value) {
2680
+ return value ?? null;
2681
+ }
2682
+ function toTransitionColor(value) {
2683
+ return value ?? null;
2684
+ }
2685
+ function nodeDepth(graph, nodeId) {
2686
+ const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
2687
+ let depth = 0;
2688
+ let current = nodeById.get(nodeId);
2689
+ while (current?.parentId) {
2690
+ depth += 1;
2691
+ current = nodeById.get(current.parentId);
2692
+ }
2693
+ return depth;
2694
+ }
2695
+ function isRootNode(node) {
2696
+ return node.parentId == null;
2697
+ }
2698
+ function buildStateUpdatePatch(prevNode, nextNode) {
2699
+ const patch = {
2700
+ op: "updateState",
2701
+ stateId: nextNode.id
2702
+ };
2703
+ let changed = false;
2704
+ if (!prevNode || prevNode.data.key !== nextNode.data.key) {
2705
+ patch.key = nextNode.data.key;
2706
+ changed = true;
2707
+ }
2708
+ if (!prevNode || prevNode.data.type !== nextNode.data.type) {
2709
+ patch.type = nextNode.data.type ?? null;
2710
+ changed = true;
2711
+ }
2712
+ if (!prevNode || prevNode.data.description !== nextNode.data.description) {
2713
+ patch.stateDescription = nextNode.data.description ?? null;
2714
+ changed = true;
2715
+ }
2716
+ if (!prevNode || prevNode.data.initialId !== nextNode.data.initialId) {
2717
+ patch.initialId = nextNode.data.initialId ?? null;
2718
+ changed = true;
2719
+ }
2720
+ if (!prevNode || !isEqual(prevNode.data.initialInput, nextNode.data.initialInput)) {
2721
+ patch.initialInput = nextNode.data.initialInput ?? null;
2722
+ changed = true;
2723
+ }
2724
+ if (!prevNode || !isEqual(prevNode.data.output, nextNode.data.output)) {
2725
+ patch.output = nextNode.data.output ?? null;
2726
+ changed = true;
2727
+ }
2728
+ if (!prevNode || !isEqual(prevNode.data.timeout, nextNode.data.timeout)) {
2729
+ patch.timeout = nextNode.data.timeout ?? null;
2730
+ changed = true;
2731
+ }
2732
+ if (!prevNode || !isEqual(prevNode.data.route, nextNode.data.route)) {
2733
+ patch.route = nextNode.data.route ?? null;
2734
+ changed = true;
2735
+ }
2736
+ if (!prevNode || prevNode.data.color !== nextNode.data.color) {
2737
+ patch.color = toStateColor(nextNode.data.color);
2738
+ changed = true;
2739
+ }
2740
+ if (!prevNode || !isEqual(prevNode.data.meta, nextNode.data.meta)) {
2741
+ patch.meta = nextNode.data.meta ?? null;
2742
+ changed = true;
2743
+ }
2744
+ if (!prevNode || prevNode.data.history !== nextNode.data.history) {
2745
+ patch.history = nextNode.data.history === true ? "shallow" : nextNode.data.history === false ? null : nextNode.data.history ?? null;
2746
+ changed = true;
2747
+ }
2748
+ return changed ? patch : null;
2749
+ }
2750
+ function buildTransitionUpdatePatch(prevEdge, nextEdge) {
2751
+ const patch = {
2752
+ op: "updateTransition",
2753
+ transitionId: nextEdge.id
2754
+ };
2755
+ let changed = false;
2756
+ if (!prevEdge || prevEdge.sourceId !== nextEdge.sourceId) {
2757
+ patch.sourceId = nextEdge.sourceId;
2758
+ changed = true;
2759
+ }
2760
+ if (!prevEdge || prevEdge.targetId !== nextEdge.targetId) {
2761
+ patch.targetId = nextEdge.targetId;
2762
+ changed = true;
2763
+ }
2764
+ if (!prevEdge || prevEdge.data.eventType !== nextEdge.data.eventType) {
2765
+ patch.eventType = nextEdge.data.eventType;
2766
+ changed = true;
2767
+ }
2768
+ if (!prevEdge || !isEqual(prevEdge.data.matches, nextEdge.data.matches)) {
2769
+ patch.matches = nextEdge.data.matches ?? null;
2770
+ changed = true;
2771
+ }
2772
+ if (!prevEdge || prevEdge.data.transitionType !== nextEdge.data.transitionType) {
2773
+ patch.transitionType = nextEdge.data.transitionType ?? "normal";
2774
+ changed = true;
2775
+ }
2776
+ if (!prevEdge || prevEdge.data.description !== nextEdge.data.description) {
2777
+ patch.transitionDescription = nextEdge.data.description ?? null;
2778
+ changed = true;
2779
+ }
2780
+ if (!prevEdge || !isEqual(prevEdge.data.input, nextEdge.data.input)) {
2781
+ patch.input = nextEdge.data.input ?? null;
2782
+ changed = true;
2783
+ }
2784
+ if (!prevEdge || prevEdge.data.color !== nextEdge.data.color) {
2785
+ patch.color = toTransitionColor(nextEdge.data.color);
2786
+ changed = true;
2787
+ }
2788
+ if (!prevEdge || !isEqual(prevEdge.data.meta, nextEdge.data.meta)) {
2789
+ patch.meta = nextEdge.data.meta ?? null;
2790
+ changed = true;
2791
+ }
2792
+ return changed ? patch : null;
2793
+ }
2794
+ function diffTags(patches, nodeId, prevTags, nextTags) {
2795
+ const getTagName = (tag) => typeof tag === "string" ? tag : tag.name;
2796
+ const prev = new Set(prevTags.map(getTagName));
2797
+ const next = new Set(nextTags.map(getTagName));
2798
+ for (const tag of next) if (!prev.has(tag)) patches.push({
2799
+ op: "createTag",
2800
+ nodeId,
2801
+ tag: { name: tag }
2802
+ });
2803
+ for (const tag of prev) if (!next.has(tag)) patches.push({
2804
+ op: "deleteTag",
2805
+ nodeId,
2806
+ tagName: tag
2807
+ });
2808
+ }
2809
+ function requiresAppendOnlyListRebuild(prevIds, nextIds) {
2810
+ const prevIdSet = new Set(prevIds);
2811
+ const nextIdSet = new Set(nextIds);
2812
+ const retainedPrevIds = prevIds.filter((id) => nextIdSet.has(id));
2813
+ const retainedNextIds = nextIds.filter((id) => prevIdSet.has(id));
2814
+ let sawNewId = false;
2815
+ for (const id of nextIds) if (prevIdSet.has(id)) {
2816
+ if (sawNewId) return true;
2817
+ } else sawNewId = true;
2818
+ return !isEqual(retainedPrevIds, retainedNextIds);
2819
+ }
2820
+ function appendCreateInvokePatch(patches, nodeId, invoke) {
2821
+ patches.push({
2822
+ op: "createInvoke",
2823
+ nodeId,
2824
+ invoke: {
2825
+ id: invoke.id,
2826
+ ...invoke.invocationId ? { invocationId: invoke.invocationId } : {},
2827
+ src: invoke.src,
2828
+ ...invoke.srcExpression ? { srcExpression: invoke.srcExpression } : {},
2829
+ ...invoke.code ? { code: invoke.code } : {},
2830
+ ...invoke.registryKey ? { registryKey: invoke.registryKey } : {},
2831
+ ...invoke.timeout !== void 0 ? { timeout: invoke.timeout } : {},
2832
+ ...invoke.input ? { input: invoke.input } : {},
2833
+ ...invoke.output ? { output: invoke.output } : {}
2834
+ }
2835
+ });
2836
+ }
2837
+ function diffInvokes(patches, nodeId, prevInvokes, nextInvokes) {
2838
+ if (requiresAppendOnlyListRebuild(prevInvokes.map((invoke) => invoke.id), nextInvokes.map((invoke) => invoke.id))) {
2839
+ for (const invoke of prevInvokes) patches.push({
2840
+ op: "deleteInvoke",
2841
+ nodeId,
2842
+ invokeId: invoke.id
2843
+ });
2844
+ for (const invoke of nextInvokes) appendCreateInvokePatch(patches, nodeId, invoke);
2845
+ return;
2846
+ }
2847
+ const prevById = new Map(prevInvokes.map((invoke) => [invoke.id, invoke]));
2848
+ const nextById = new Map(nextInvokes.map((invoke) => [invoke.id, invoke]));
2849
+ for (const invoke of nextInvokes) {
2850
+ const previous = prevById.get(invoke.id);
2851
+ if (!previous) {
2852
+ appendCreateInvokePatch(patches, nodeId, invoke);
2853
+ continue;
2854
+ }
2855
+ const data = {};
2856
+ if (previous.invocationId !== invoke.invocationId) data.invocationId = invoke.invocationId ?? null;
2857
+ if (previous.src !== invoke.src) data.src = invoke.src;
2858
+ if (previous.code !== invoke.code) data.code = invoke.code ?? null;
2859
+ if (!isEqual(previous.srcExpression, invoke.srcExpression)) data.srcExpression = invoke.srcExpression ?? null;
2860
+ if (previous.registryKey !== invoke.registryKey) data.registryKey = invoke.registryKey ?? null;
2861
+ if (!isEqual(previous.timeout, invoke.timeout)) data.timeout = invoke.timeout ?? null;
2862
+ if (!isEqual(previous.input, invoke.input)) data.input = invoke.input ?? null;
2863
+ if (!isEqual(previous.output, invoke.output)) data.output = invoke.output ?? null;
2864
+ if (Object.keys(data).length > 0) patches.push({
2865
+ op: "updateInvoke",
2866
+ nodeId,
2867
+ invokeId: invoke.id,
2868
+ data
2869
+ });
2870
+ }
2871
+ for (const invoke of prevInvokes) if (!nextById.has(invoke.id)) patches.push({
2872
+ op: "deleteInvoke",
2873
+ nodeId,
2874
+ invokeId: invoke.id
2875
+ });
2876
+ }
2877
+ function diffActions(patches, location, prevActions, nextActions) {
2878
+ const actionId = (action, index) => action.id ?? `${index}:${action.type}`;
2879
+ const prevIds = prevActions.map(actionId);
2880
+ const nextIds = nextActions.map(actionId);
2881
+ if (requiresAppendOnlyListRebuild(prevIds, nextIds)) {
2882
+ prevActions.forEach((action, index) => {
2883
+ patches.push({
2884
+ op: "deleteAction",
2885
+ location,
2886
+ actionId: actionId(action, index)
2887
+ });
2888
+ });
2889
+ nextActions.forEach((action, index) => {
2890
+ patches.push({
2891
+ op: "createAction",
2892
+ location,
2893
+ index,
2894
+ action: {
2895
+ id: action.id,
2896
+ type: action.type,
2897
+ params: action.params
2898
+ }
2899
+ });
2900
+ });
2901
+ return;
2902
+ }
2903
+ const prevById = new Map(prevActions.map((action, index) => [actionId(action, index), action]));
2904
+ const nextIdSet = new Set(nextIds);
2905
+ prevActions.forEach((action, index) => {
2906
+ const id = actionId(action, index);
2907
+ if (!nextIdSet.has(id)) patches.push({
2908
+ op: "deleteAction",
2909
+ location,
2910
+ actionId: id
2911
+ });
2912
+ });
2913
+ nextActions.forEach((action, index) => {
2914
+ const id = actionId(action, index);
2915
+ const previous = prevById.get(id);
2916
+ if (!previous) {
2917
+ patches.push({
2918
+ op: "createAction",
2919
+ location,
2920
+ index,
2921
+ action: {
2922
+ id: action.id,
2923
+ type: action.type,
2924
+ params: action.params
2925
+ }
2926
+ });
2927
+ return;
2928
+ }
2929
+ const data = {};
2930
+ if (previous.type !== action.type) data.type = action.type;
2931
+ if (!isEqual(previous.params, action.params)) data.params = action.params ?? {};
2932
+ if (Object.keys(data).length > 0) patches.push({
2933
+ op: "updateAction",
2934
+ location,
2935
+ actionId: id,
2936
+ data
2937
+ });
2938
+ });
2939
+ }
2940
+ function implementationList$1(graph, sourceType) {
2941
+ const implementations = graph.data?.implementations;
2942
+ if (!implementations) return [];
2943
+ switch (sourceType) {
2944
+ case "action": return implementations.actions ?? [];
2945
+ case "guard": return implementations.guards ?? [];
2946
+ case "actor": return implementations.actors ?? [];
2947
+ case "delay": return implementations.delays ?? [];
2948
+ }
2949
+ }
2950
+ function collectUnsupportedChanges(prevGraph, nextGraph) {
2951
+ const unsupportedChanges = [];
2952
+ const prevNodeById = new Map(prevGraph.nodes.map((node) => [node.id, node]));
2953
+ for (const node of nextGraph.nodes) {
2954
+ const previous = prevNodeById.get(node.id);
2955
+ if (previous && (previous.parentId ?? null) !== (node.parentId ?? null)) unsupportedChanges.push(`State "${node.id}" moved to a different parent.`);
2956
+ }
2957
+ for (const sourceType of [
2958
+ "action",
2959
+ "guard",
2960
+ "actor",
2961
+ "delay"
2962
+ ]) {
2963
+ const previousById = new Map(implementationList$1(prevGraph, sourceType).map((implementation) => [implementation.id, implementation]));
2964
+ for (const implementation of implementationList$1(nextGraph, sourceType)) {
2965
+ const previous = previousById.get(implementation.id);
2966
+ const unsupportedMetadata = {
2967
+ description: implementation.description,
2968
+ lang: implementation.code?.lang,
2969
+ deps: implementation.code?.deps,
2970
+ ..."paramsSchema" in implementation ? { paramsSchema: implementation.paramsSchema } : {},
2971
+ ..."inputSchema" in implementation ? { inputSchema: implementation.inputSchema } : {},
2972
+ ..."outputSchema" in implementation ? { outputSchema: implementation.outputSchema } : {}
2973
+ };
2974
+ const previousUnsupportedMetadata = previous ? {
2975
+ description: previous.description,
2976
+ lang: previous.code?.lang,
2977
+ deps: previous.code?.deps,
2978
+ ..."paramsSchema" in previous ? { paramsSchema: previous.paramsSchema } : {},
2979
+ ..."inputSchema" in previous ? { inputSchema: previous.inputSchema } : {},
2980
+ ..."outputSchema" in previous ? { outputSchema: previous.outputSchema } : {}
2981
+ } : {};
2982
+ if (previous ? !isEqual(previousUnsupportedMetadata, unsupportedMetadata) : Object.values(unsupportedMetadata).some((value) => value !== void 0 && value !== null)) unsupportedChanges.push(`${sourceType} implementation "${implementation.id}" has metadata that source patches cannot represent.`);
2983
+ }
2984
+ }
2985
+ return unsupportedChanges;
2986
+ }
2987
+ function diffImplementations(patches, sourceType, prevGraph, nextGraph) {
2988
+ const prevById = new Map(implementationList$1(prevGraph, sourceType).map((implementation) => [implementation.id, implementation]));
2989
+ const nextById = new Map(implementationList$1(nextGraph, sourceType).map((implementation) => [implementation.id, implementation]));
2990
+ for (const implementation of nextById.values()) {
2991
+ const previous = prevById.get(implementation.id);
2992
+ const code = implementation.code?.body;
2993
+ if (!previous) {
2994
+ patches.push({
2995
+ op: "createImplementation",
2996
+ sourceType,
2997
+ implementation: {
2998
+ id: implementation.id,
2999
+ name: implementation.name,
3000
+ ...code !== void 0 ? { code } : void 0
3001
+ }
3002
+ });
3003
+ continue;
3004
+ }
3005
+ const data = {};
3006
+ if (previous.name !== implementation.name) data.name = implementation.name;
3007
+ if (previous.code?.body !== implementation.code?.body) data.code = code ?? null;
3008
+ if (Object.keys(data).length > 0) patches.push({
3009
+ op: "updateImplementation",
3010
+ sourceType,
3011
+ implementationId: implementation.id,
3012
+ data
3013
+ });
3014
+ }
3015
+ for (const implementation of prevById.values()) if (!nextById.has(implementation.id)) patches.push({
3016
+ op: "deleteImplementation",
3017
+ sourceType,
3018
+ implementationId: implementation.id
3019
+ });
3020
+ }
3021
+ function appendCreateTransitionPatches(patches, edge) {
3022
+ patches.push({
3023
+ op: "createTransition",
3024
+ id: edge.id,
3025
+ sourceId: edge.sourceId,
3026
+ targetId: edge.targetId,
3027
+ eventType: edge.data.eventType,
3028
+ ...edge.data.matches ? { matches: edge.data.matches } : {},
3029
+ transitionType: edge.data.transitionType,
3030
+ ...edge.data.guard ? { guard: edge.data.guard } : {},
3031
+ ...edge.data.color ? { color: toTransitionColor(edge.data.color) } : {},
3032
+ ...edge.data.description ? { transitionDescription: edge.data.description } : {},
3033
+ ...edge.data.input !== void 0 ? { input: edge.data.input } : {}
3034
+ });
3035
+ if (edge.data.meta) patches.push({
3036
+ op: "updateTransition",
3037
+ transitionId: edge.id,
3038
+ meta: edge.data.meta
3039
+ });
3040
+ diffActions(patches, {
3041
+ edgeId: edge.id,
3042
+ group: "transition"
3043
+ }, [], edge.data.actions ?? []);
3044
+ }
3045
+ /** Transitions are ordered relative to their (source state, event type) peers. */
3046
+ function transitionPriorityGroup(edge) {
3047
+ return `${edge.sourceId}\0${edge.data?.eventType ?? ""}`;
3048
+ }
3049
+ function groupEdgeIdsByPriority(edges) {
3050
+ const groups = /* @__PURE__ */ new Map();
3051
+ for (const edge of edges) {
3052
+ const key = transitionPriorityGroup(edge);
3053
+ const ids = groups.get(key);
3054
+ if (ids) ids.push(edge.id);
3055
+ else groups.set(key, [edge.id]);
3056
+ }
3057
+ return groups;
3058
+ }
3059
+ /**
3060
+ * Returns the ids of transitions that must be deleted and recreated because
3061
+ * their priority group cannot be reached by appends and deletions alone.
3062
+ *
3063
+ * An edge is rebuilt when either the group it came from or the group it moves
3064
+ * to needs a rebuild, so a rebuilt group is always torn down and recreated as
3065
+ * a whole.
3066
+ */
3067
+ function collectRebuiltEdgeIds(prevGraph, nextGraph) {
3068
+ const prevGroups = groupEdgeIdsByPriority(prevGraph.edges);
3069
+ const nextGroups = groupEdgeIdsByPriority(nextGraph.edges);
3070
+ const rebuiltGroups = /* @__PURE__ */ new Set();
3071
+ for (const key of new Set([...prevGroups.keys(), ...nextGroups.keys()])) if (requiresAppendOnlyListRebuild(prevGroups.get(key) ?? [], nextGroups.get(key) ?? [])) rebuiltGroups.add(key);
3072
+ const rebuiltEdgeIds = /* @__PURE__ */ new Set();
3073
+ for (const edge of [...prevGraph.edges, ...nextGraph.edges]) if (rebuiltGroups.has(transitionPriorityGroup(edge))) rebuiltEdgeIds.add(edge.id);
3074
+ return rebuiltEdgeIds;
3075
+ }
3076
+ function graphChangeToPatchesResult(prevGraph, nextGraph) {
3077
+ const patches = [];
3078
+ const unsupportedChanges = collectUnsupportedChanges(prevGraph, nextGraph);
3079
+ const prevNodeById = new Map(prevGraph.nodes.map((node) => [node.id, node]));
3080
+ const nextNodeById = new Map(nextGraph.nodes.map((node) => [node.id, node]));
3081
+ const prevEdgeById = new Map(prevGraph.edges.map((edge) => [edge.id, edge]));
3082
+ const nextEdgeById = new Map(nextGraph.edges.map((edge) => [edge.id, edge]));
3083
+ const createdNodes = nextGraph.nodes.filter((node) => !isRootNode(node) && !prevNodeById.has(node.id)).sort((a, b) => nodeDepth(nextGraph, a.id) - nodeDepth(nextGraph, b.id));
3084
+ for (const node of createdNodes) {
3085
+ const x = node.position?.x ?? node.x;
3086
+ const y = node.position?.y ?? node.y;
3087
+ patches.push({
3088
+ op: "createState",
3089
+ id: node.id,
3090
+ parentId: node.parentId,
3091
+ key: node.data.key,
3092
+ type: node.data.type,
3093
+ ...x !== void 0 ? { x } : {},
3094
+ ...y !== void 0 ? { y } : {},
3095
+ color: toStateColor(node.data.color),
3096
+ initial: nextNodeById.get(node.parentId)?.data.initialId === node.id
3097
+ });
3098
+ const stateUpdate = buildStateUpdatePatch(void 0, node);
3099
+ if (stateUpdate) patches.push(stateUpdate);
3100
+ diffTags(patches, node.id, [], node.data.tags ?? []);
3101
+ diffInvokes(patches, node.id, [], node.data.invokes ?? []);
3102
+ diffActions(patches, {
3103
+ nodeId: node.id,
3104
+ group: "entry"
3105
+ }, [], node.data.entry ?? []);
3106
+ diffActions(patches, {
3107
+ nodeId: node.id,
3108
+ group: "exit"
3109
+ }, [], node.data.exit ?? []);
3110
+ }
3111
+ for (const node of prevGraph.nodes.filter((node) => !isRootNode(node) && !nextNodeById.has(node.id)).sort((a, b) => nodeDepth(prevGraph, b.id) - nodeDepth(prevGraph, a.id))) patches.push({
3112
+ op: "deleteState",
3113
+ stateId: node.id
3114
+ });
3115
+ for (const node of nextGraph.nodes) {
3116
+ const previous = prevNodeById.get(node.id);
3117
+ if (!previous) continue;
3118
+ const stateUpdate = buildStateUpdatePatch(previous, node);
3119
+ if (stateUpdate) patches.push(stateUpdate);
3120
+ diffTags(patches, node.id, previous.data.tags ?? [], node.data.tags ?? []);
3121
+ diffInvokes(patches, node.id, previous.data.invokes ?? [], node.data.invokes ?? []);
3122
+ diffActions(patches, {
3123
+ nodeId: node.id,
3124
+ group: "entry"
3125
+ }, previous.data.entry ?? [], node.data.entry ?? []);
3126
+ diffActions(patches, {
3127
+ nodeId: node.id,
3128
+ group: "exit"
3129
+ }, previous.data.exit ?? [], node.data.exit ?? []);
3130
+ }
3131
+ const rebuiltEdgeIds = collectRebuiltEdgeIds(prevGraph, nextGraph);
3132
+ const isRebuilt = (edge) => rebuiltEdgeIds.has(edge.id);
3133
+ for (const edge of prevGraph.edges) if (isRebuilt(edge)) patches.push({
3134
+ op: "deleteTransition",
3135
+ transitionId: edge.id
3136
+ });
3137
+ for (const edge of nextGraph.edges) if (isRebuilt(edge)) appendCreateTransitionPatches(patches, edge);
3138
+ for (const edge of nextGraph.edges) {
3139
+ if (isRebuilt(edge)) continue;
3140
+ const previous = prevEdgeById.get(edge.id);
3141
+ if (!previous) {
3142
+ appendCreateTransitionPatches(patches, edge);
3143
+ continue;
3144
+ }
3145
+ const transitionUpdate = buildTransitionUpdatePatch(previous, edge);
3146
+ if (transitionUpdate) patches.push(transitionUpdate);
3147
+ if (!previous.data.guard && edge.data.guard) patches.push({
3148
+ op: "setGuard",
3149
+ edgeId: edge.id,
3150
+ guard: edge.data.guard
3151
+ });
3152
+ else if (previous.data.guard && !edge.data.guard) patches.push({
3153
+ op: "deleteGuard",
3154
+ edgeId: edge.id
3155
+ });
3156
+ else if (!isEqual(previous.data.guard, edge.data.guard) && edge.data.guard) patches.push({
3157
+ op: "setGuard",
3158
+ edgeId: edge.id,
3159
+ guard: edge.data.guard
3160
+ });
3161
+ diffActions(patches, {
3162
+ edgeId: edge.id,
3163
+ group: "transition"
3164
+ }, previous.data.actions ?? [], edge.data.actions ?? []);
3165
+ }
3166
+ for (const edge of prevGraph.edges) if (!isRebuilt(edge) && !nextEdgeById.has(edge.id)) patches.push({
3167
+ op: "deleteTransition",
3168
+ transitionId: edge.id
3169
+ });
3170
+ if (!isEqual(prevGraph.data.context, nextGraph.data.context)) patches.push({
3171
+ op: "setContext",
3172
+ context: nextGraph.data.context ?? null
3173
+ });
3174
+ if (!isEqual(prevGraph.data.schemas, nextGraph.data.schemas)) patches.push({
3175
+ op: "updateSchemas",
3176
+ schemas: {
3177
+ context: nextGraph.data.schemas?.context ?? null,
3178
+ events: nextGraph.data.schemas?.events ?? null,
3179
+ input: nextGraph.data.schemas?.input ?? null,
3180
+ output: nextGraph.data.schemas?.output ?? null
3181
+ }
3182
+ });
3183
+ diffImplementations(patches, "action", prevGraph, nextGraph);
3184
+ diffImplementations(patches, "guard", prevGraph, nextGraph);
3185
+ diffImplementations(patches, "actor", prevGraph, nextGraph);
3186
+ diffImplementations(patches, "delay", prevGraph, nextGraph);
3187
+ return {
3188
+ patches,
3189
+ unsupportedChanges
3190
+ };
3191
+ }
3192
+ const UNMATCHED_ID_PREFIX = "__diff_unmatched_";
3193
+ function createIdAllocator(reserved) {
3194
+ const taken = new Set(reserved);
3195
+ let counter = 0;
3196
+ return {
3197
+ claim(preferredId) {
3198
+ if (!taken.has(preferredId)) {
3199
+ taken.add(preferredId);
3200
+ return preferredId;
3201
+ }
3202
+ let candidate = `${UNMATCHED_ID_PREFIX}${counter++}`;
3203
+ while (taken.has(candidate)) candidate = `${UNMATCHED_ID_PREFIX}${counter++}`;
3204
+ taken.add(candidate);
3205
+ return candidate;
3206
+ },
3207
+ reserve(id) {
3208
+ taken.add(id);
3209
+ },
3210
+ isTaken(id) {
3211
+ return taken.has(id);
3212
+ }
3213
+ };
3214
+ }
3215
+ function assignAlignedIds(compareEntities, baseIds, baseIdBySemanticId) {
3216
+ const baseIdSet = new Set(baseIds);
3217
+ const assigned = /* @__PURE__ */ new Map();
3218
+ const allocator = createIdAllocator([]);
3219
+ for (const entity of compareEntities) if (baseIdSet.has(entity.id)) {
3220
+ allocator.reserve(entity.id);
3221
+ assigned.set(entity.id, entity.id);
3222
+ }
3223
+ for (const entity of compareEntities) {
3224
+ if (assigned.has(entity.id)) continue;
3225
+ const matchedBaseId = baseIdBySemanticId.get(entity.semanticId);
3226
+ if (matchedBaseId !== void 0 && !allocator.isTaken(matchedBaseId)) {
3227
+ allocator.reserve(matchedBaseId);
3228
+ assigned.set(entity.id, matchedBaseId);
3229
+ }
3230
+ }
3231
+ for (const entity of compareEntities) {
3232
+ if (assigned.has(entity.id)) continue;
3233
+ assigned.set(entity.id, allocator.claim(entity.id));
3234
+ }
3235
+ return assigned;
3236
+ }
3237
+ /**
3238
+ * Rewrites compare entity ids to the corresponding base ids.
3239
+ *
3240
+ * Shared raw ids win, then semantic ids, then collision-free compare ids.
3241
+ * Normalization must preserve entity array order so semantic ids can be mapped
3242
+ * back to the caller's original graph.
3243
+ */
3244
+ function alignCompareGraphIds(base, compare, semanticBase, semanticCompare) {
3245
+ const baseNodeIds = new Map(semanticBase.nodes.map((node, index) => [node.id, base.nodes[index].id]));
3246
+ const baseEdgeIds = new Map(semanticBase.edges.map((edge, index) => [edge.id, base.edges[index].id]));
3247
+ const compareNodeIds = assignAlignedIds(compare.nodes.map((node, index) => ({
3248
+ id: node.id,
3249
+ semanticId: semanticCompare.nodes[index].id
3250
+ })), base.nodes.map((node) => node.id), baseNodeIds);
3251
+ const compareEdgeIds = assignAlignedIds(compare.edges.map((edge, index) => ({
3252
+ id: edge.id,
3253
+ semanticId: semanticCompare.edges[index].id
3254
+ })), base.edges.map((edge) => edge.id), baseEdgeIds);
3255
+ const mapNodeId = (id) => compareNodeIds.get(id) ?? id;
3256
+ const aligned = structuredClone(compare);
3257
+ if (aligned.initialNodeId) aligned.initialNodeId = mapNodeId(aligned.initialNodeId);
3258
+ for (const node of aligned.nodes) {
3259
+ if (node.parentId) node.parentId = mapNodeId(node.parentId);
3260
+ if (node.initialNodeId) node.initialNodeId = mapNodeId(node.initialNodeId);
3261
+ if (node.data?.initialId) node.data.initialId = mapNodeId(node.data.initialId);
3262
+ node.id = mapNodeId(node.id);
3263
+ }
3264
+ for (const edge of aligned.edges) {
3265
+ edge.sourceId = mapNodeId(edge.sourceId);
3266
+ edge.targetId = mapNodeId(edge.targetId);
3267
+ edge.id = compareEdgeIds.get(edge.id) ?? edge.id;
3268
+ }
3269
+ for (const annotation of aligned.annotations ?? []) {
3270
+ const annotationData = annotation.data;
3271
+ if (!annotationData?.referenceIds) continue;
3272
+ annotationData.referenceIds = annotationData.referenceIds.map((id) => annotation.type === "edge" ? compareEdgeIds.get(id) ?? id : mapNodeId(id));
3273
+ }
3274
+ return aligned;
3275
+ }
2358
3276
  function isAutoGeneratedId(id) {
2359
3277
  return !!id && id.startsWith("$auto-");
2360
3278
  }
@@ -2471,7 +3389,7 @@ function stripExportDefault(code) {
2471
3389
  return trimmed;
2472
3390
  }
2473
3391
  const XSTATE_V5_PROFILE = "@statelyai/xstate@5.30.0";
2474
- const XSTATE_V6_PROFILE = "@statelyai/xstate@6.0.0-alpha.47";
3392
+ const XSTATE_V6_PROFILE = "@statelyai/xstate@6.0.0-alpha.51";
2475
3393
  const LEGACY_MACHINE_PROFILES = ["xstate-v5", "xstate-v6-alpha"];
2476
3394
  function normalizeMachineProfile(profile) {
2477
3395
  return profile === "xstate-v5" ? XSTATE_V5_PROFILE : profile === "xstate-v6-alpha" ? XSTATE_V6_PROFILE : profile;
@@ -2617,7 +3535,7 @@ function deepSimplify(config) {
2617
3535
  return config;
2618
3536
  }
2619
3537
  function buildMeta(meta, color) {
2620
- if (isCodeExpression$1(meta)) return meta;
3538
+ if (isCodeExpression$2(meta)) return meta;
2621
3539
  const result = {};
2622
3540
  if (meta) {
2623
3541
  for (const [k, v] of Object.entries(meta)) if (v !== void 0 && v !== null) result[k] = v;
@@ -2625,7 +3543,7 @@ function buildMeta(meta, color) {
2625
3543
  if (color) result["@statelyai.color"] = color;
2626
3544
  return Object.keys(result).length > 0 ? result : void 0;
2627
3545
  }
2628
- function isCodeExpression$1(value) {
3546
+ function isCodeExpression$2(value) {
2629
3547
  return Boolean(value && typeof value === "object" && !Array.isArray(value) && value["@type"] === "code" && typeof value.expr === "string");
2630
3548
  }
2631
3549
  /** Format event param — supports { type: string } (new) and plain string (legacy) */
@@ -2636,7 +3554,7 @@ function formatEventParam(event) {
2636
3554
  }
2637
3555
  function formatStringParam(value) {
2638
3556
  if (typeof value === "string") return `'${value}'`;
2639
- return isCodeExpression$1(value) ? serializeJS(value) : "undefined";
3557
+ return isCodeExpression$2(value) ? serializeJS(value) : "undefined";
2640
3558
  }
2641
3559
  function formatInlineObject(value) {
2642
3560
  return `{ ${Object.entries(value).filter(([, item]) => item !== void 0).map(([key, item]) => `${formatObjectKey(key)}: ${serializeJS(item)}`).join(", ")} }`;
@@ -2885,6 +3803,7 @@ function graphToMachineConfig(graph, options = {}) {
2885
3803
  ...inv.output !== void 0 ? { output: inv.output } : void 0
2886
3804
  })) } : void 0,
2887
3805
  ...node.data.output !== void 0 ? { output: node.data.output } : void 0,
3806
+ ...node.data.schemas ? { schemas: node.data.schemas } : void 0,
2888
3807
  ...tags?.length ? { tags: tags.map(tagToString) } : void 0,
2889
3808
  ...showDescriptions && node.data.description ? { description: node.data.description } : void 0,
2890
3809
  ...showMeta && node.data.meta && Object.keys(node.data.meta).length > 0 ? { meta: node.data.meta } : void 0,
@@ -3004,6 +3923,8 @@ function graphToMachineConfig(graph, options = {}) {
3004
3923
  if (!rootNode) throw new Error("No root node found");
3005
3924
  getNodeConfig(rootNode);
3006
3925
  if (graph.data.context !== void 0 && graph.data.context !== null) config.context = graph.data.context;
3926
+ if (graph.data.version) config.version = graph.data.version;
3927
+ if (graph.data.options?.maxIterations !== void 0) config.options = { maxIterations: graph.data.options.maxIterations };
3007
3928
  return deepSimplify(config);
3008
3929
  }
3009
3930
  const INLINE_EXPRESSION_DIRECTIVE_FIELDS = [
@@ -4159,16 +5080,27 @@ function isCreateMachineCall(node) {
4159
5080
  return false;
4160
5081
  }
4161
5082
  function isMachineFactoryName(name) {
4162
- return name === "createMachine" || name === "createMachineFromConfig";
4163
- }
4164
- function setupCallForCreateMachine(node, bindings) {
4165
- if (!ts.isPropertyAccessExpression(node.expression)) return null;
4166
- let receiver = unwrapExpression(node.expression.expression);
4167
- if (ts.isIdentifier(receiver)) {
4168
- const binding = bindings.get(receiver.text);
4169
- if (binding && !ts.isFunctionDeclaration(binding)) receiver = unwrapExpression(binding);
4170
- }
4171
- return ts.isCallExpression(receiver) && ts.isIdentifier(receiver.expression) && receiver.expression.text === "setup" ? receiver : null;
5083
+ return name === "createMachine" || name === "createMachineFromConfig" || name === "createFSM";
5084
+ }
5085
+ function setupConfigExpressionsForCreateMachine(node, bindings) {
5086
+ if (!ts.isPropertyAccessExpression(node.expression)) return [];
5087
+ const findSetupConfigs = (value, seen = /* @__PURE__ */ new Set()) => {
5088
+ const receiver = unwrapExpression(value);
5089
+ if (ts.isIdentifier(receiver)) {
5090
+ if (seen.has(receiver.text)) return [];
5091
+ seen.add(receiver.text);
5092
+ const binding = bindings.get(receiver.text);
5093
+ return binding && !ts.isFunctionDeclaration(binding) ? findSetupConfigs(binding, seen) : [];
5094
+ }
5095
+ if (!ts.isCallExpression(receiver)) return [];
5096
+ if (ts.isIdentifier(receiver.expression) && receiver.expression.text === "setup") return receiver.arguments[0] ? [receiver.arguments[0]] : [];
5097
+ if (ts.isPropertyAccessExpression(receiver.expression) && receiver.expression.name.text === "extend") {
5098
+ const base = findSetupConfigs(receiver.expression.expression, seen);
5099
+ return receiver.arguments[0] ? [...base, receiver.arguments[0]] : base;
5100
+ }
5101
+ return [];
5102
+ };
5103
+ return findSetupConfigs(node.expression.expression);
4172
5104
  }
4173
5105
  function hasV6Syntax(source) {
4174
5106
  const file = sourceFile(source);
@@ -4220,14 +5152,14 @@ function hasV6Syntax(source) {
4220
5152
  }
4221
5153
  function visitMachineCalls(node) {
4222
5154
  if (ts.isCallExpression(node) && isCreateMachineCall(node)) {
4223
- if ((ts.isIdentifier(node.expression) ? node.expression.text : ts.isPropertyAccessExpression(node.expression) ? node.expression.name.text : void 0) === "createMachineFromConfig") {
5155
+ const factoryName = ts.isIdentifier(node.expression) ? node.expression.text : ts.isPropertyAccessExpression(node.expression) ? node.expression.name.text : void 0;
5156
+ if (factoryName === "createMachineFromConfig" || factoryName === "createFSM") {
4224
5157
  found = true;
4225
5158
  return;
4226
5159
  }
4227
5160
  const config = node.arguments[0];
4228
5161
  if (config) visitSyntax(config);
4229
- const setupConfig = setupCallForCreateMachine(node, bindings)?.arguments[0];
4230
- if (setupConfig) visitSyntax(setupConfig);
5162
+ for (const setupConfig of setupConfigExpressionsForCreateMachine(node, bindings)) visitSyntax(setupConfig);
4231
5163
  return;
4232
5164
  }
4233
5165
  ts.forEachChild(node, visitMachineCalls);
@@ -4240,24 +5172,93 @@ function detectXStateSourceProfile(source) {
4240
5172
  }
4241
5173
  function xstateSourceToConfig(source, options = {}) {
4242
5174
  const [config] = extractXStateMachineConfigs(source, options);
4243
- if (!config) throw new Error("No createMachine(...) or setup(...).createMachine(...) call found.");
5175
+ if (!config) throw new Error("No createMachine(...), createFSM(...), or setup factory call found.");
4244
5176
  return config;
4245
5177
  }
4246
5178
  /**
4247
5179
  * Extract every XState machine config from source without executing code.
4248
5180
  * XState 5/6 syntax is inferred structurally unless a profile is supplied.
4249
5181
  */
4250
- function extractXStateMachineConfigs(source, options = {}) {
5182
+ function extractXStateMachineConfigEntries(source, options = {}) {
4251
5183
  const file = sourceFile(source);
4252
5184
  const bindings = collectBindings(file);
4253
5185
  const imports = collectSourceImports(file);
4254
5186
  const found = [];
5187
+ let machineIndex = 0;
4255
5188
  const profile = options.profile === "auto" || !options.profile ? detectXStateSourceProfile(source) : options.profile;
5189
+ const isRecordValue = (value) => !!value && typeof value === "object" && !Array.isArray(value);
5190
+ const mergeSetupStateSchemas = (machineStates, setupStates) => {
5191
+ if (!isRecordValue(machineStates) || !isRecordValue(setupStates)) return machineStates;
5192
+ return Object.fromEntries(Object.entries(machineStates).map(([key, machineState]) => {
5193
+ if (!isRecordValue(machineState)) return [key, machineState];
5194
+ const contract = isRecordValue(setupStates[key]) ? setupStates[key] : void 0;
5195
+ if (!contract) return [key, machineState];
5196
+ const contractSchemas = isRecordValue(contract.schemas) ? contract.schemas : {};
5197
+ const stateSchemas = isRecordValue(machineState.schemas) ? machineState.schemas : {};
5198
+ const schemas = Object.fromEntries([
5199
+ "input",
5200
+ "context",
5201
+ "output"
5202
+ ].flatMap((schemaKey) => {
5203
+ const schema = stateSchemas[schemaKey] ?? contractSchemas[schemaKey];
5204
+ return schema === void 0 ? [] : [[schemaKey, schema]];
5205
+ }));
5206
+ return [key, {
5207
+ ...machineState,
5208
+ ...Object.keys(schemas).length ? { schemas } : {},
5209
+ states: mergeSetupStateSchemas(machineState.states, contract.states)
5210
+ }];
5211
+ }));
5212
+ };
5213
+ const mergeSetupRecords = (base, extension) => {
5214
+ const mergeMap = (left, right) => {
5215
+ if (!isRecordValue(left) && !isRecordValue(right)) return void 0;
5216
+ const merged = {
5217
+ ...isRecordValue(left) ? left : {},
5218
+ ...isRecordValue(right) ? right : {}
5219
+ };
5220
+ return Object.keys(merged).length ? merged : void 0;
5221
+ };
5222
+ const schemas = mergeMap(base.schemas, extension.schemas);
5223
+ if (isRecordValue(schemas)) for (const key of [
5224
+ "events",
5225
+ "internalEvents",
5226
+ "actions",
5227
+ "guards",
5228
+ "emitted",
5229
+ "children"
5230
+ ]) {
5231
+ const merged = mergeMap(isRecordValue(base.schemas) ? base.schemas[key] : void 0, isRecordValue(extension.schemas) ? extension.schemas[key] : void 0);
5232
+ if (merged !== void 0) schemas[key] = merged;
5233
+ else delete schemas[key];
5234
+ }
5235
+ const result = {
5236
+ ...base,
5237
+ ...extension
5238
+ };
5239
+ if (schemas !== void 0) result.schemas = schemas;
5240
+ else delete result.schemas;
5241
+ for (const key of [
5242
+ "states",
5243
+ "actions",
5244
+ "actors",
5245
+ "actorSources",
5246
+ "guards",
5247
+ "delays"
5248
+ ]) {
5249
+ const merged = mergeMap(base[key], extension[key]);
5250
+ if (merged !== void 0) result[key] = merged;
5251
+ else delete result[key];
5252
+ }
5253
+ return result;
5254
+ };
4256
5255
  function visit(node) {
4257
5256
  if (!ts.isCallExpression(node) || !isCreateMachineCall(node)) {
4258
5257
  ts.forEachChild(node, visit);
4259
5258
  return;
4260
5259
  }
5260
+ const sourceMachineIndex = machineIndex;
5261
+ machineIndex += 1;
4261
5262
  const arg = node.arguments[0];
4262
5263
  if (!arg) return;
4263
5264
  const value = expressionToValue(arg, bindings, {
@@ -4266,22 +5267,32 @@ function extractXStateMachineConfigs(source, options = {}) {
4266
5267
  imports
4267
5268
  });
4268
5269
  if (value && typeof value === "object" && !Array.isArray(value)) {
4269
- const setupArg = setupCallForCreateMachine(node, bindings)?.arguments[0];
4270
- const setupValue = setupArg ? expressionToValue(setupArg, bindings, {
4271
- v6: profile === "xstate-v6-alpha",
4272
- setupValue: true,
4273
- imports
4274
- }) : null;
4275
- const setupRecord = setupValue && typeof setupValue === "object" && !Array.isArray(setupValue) ? Object.fromEntries(Object.entries(setupValue).filter(([, item]) => !(item && typeof item === "object" && !Array.isArray(item) && Object.keys(item).length === 0))) : {};
5270
+ const { states: setupStates, ...setupWithoutStates } = setupConfigExpressionsForCreateMachine(node, bindings).reduce((merged, setupArg) => {
5271
+ const setupValue = expressionToValue(setupArg, bindings, {
5272
+ v6: profile === "xstate-v6-alpha",
5273
+ setupValue: true,
5274
+ imports
5275
+ });
5276
+ if (!isRecordValue(setupValue)) return merged;
5277
+ return mergeSetupRecords(merged, setupValue);
5278
+ }, {});
5279
+ const machineValue = value;
4276
5280
  found.push({
4277
- ...setupRecord,
4278
- ...value
5281
+ machineIndex: sourceMachineIndex,
5282
+ config: {
5283
+ ...setupWithoutStates,
5284
+ ...machineValue,
5285
+ states: mergeSetupStateSchemas(machineValue.states, setupStates)
5286
+ }
4279
5287
  });
4280
5288
  }
4281
5289
  }
4282
5290
  visit(file);
4283
5291
  return found;
4284
5292
  }
5293
+ function extractXStateMachineConfigs(source, options = {}) {
5294
+ return extractXStateMachineConfigEntries(source, options).map(({ config }) => config);
5295
+ }
4285
5296
  function jsonSchemaToTSType(schema) {
4286
5297
  if (!schema) return "unknown";
4287
5298
  if (schema.enum) return schema.enum.map((v) => typeof v === "string" ? `'${v}'` : String(v)).join(" | ");
@@ -4490,6 +5501,7 @@ function graphToXStateTS(graph, options = {}) {
4490
5501
  const impls = filterInlineImplementations(graph.data.implementations);
4491
5502
  const supportsTypeSyntax = targetLanguage === "typescript";
4492
5503
  const hasSchemas = !!(schemas && (schemas.context || schemas.events || schemas.input || schemas.output));
5504
+ const hasStateSchemas = graph.nodes.some((node) => node.data.schemas);
4493
5505
  const hasActions = !!impls?.actions.length;
4494
5506
  const hasGuards = !!impls?.guards.length;
4495
5507
  const hasActors = !!impls?.actors.length;
@@ -4508,10 +5520,11 @@ function graphToXStateTS(graph, options = {}) {
4508
5520
  implementationImports,
4509
5521
  codeImports,
4510
5522
  xstateVersion,
4511
- usesTypes: xstateVersion === 6 && supportsTypeSyntax && hasSchemas && xstateV6SchemaFormat === "types",
4512
- usesZod: xstateVersion === 6 && hasSchemas && xstateV6SchemaFormat === "zod"
5523
+ usesTypes: xstateVersion === 6 && supportsTypeSyntax && (hasSchemas || hasStateSchemas) && xstateV6SchemaFormat === "types",
5524
+ usesZod: xstateVersion === 6 && (hasSchemas || hasStateSchemas) && xstateV6SchemaFormat === "zod"
4513
5525
  });
4514
5526
  const machineConfig = xstateVersion === 6 ? toV6MachineConfig(graphToMachineConfig(graph, configOptions)) : toV5MachineConfig(graphToMachineConfig(graph, configOptions));
5527
+ if (xstateVersion === 6) stripStateSchemas(machineConfig);
4515
5528
  let machineExpr;
4516
5529
  if (hasSetup) {
4517
5530
  const setupObj = buildSetupObject(schemas, impls, {
@@ -4519,6 +5532,13 @@ function graphToXStateTS(graph, options = {}) {
4519
5532
  xstateVersion,
4520
5533
  xstateV6SchemaFormat
4521
5534
  });
5535
+ if (xstateVersion === 6) {
5536
+ const states = buildV6SetupStates(graph, {
5537
+ includeTypeSyntax: supportsTypeSyntax,
5538
+ xstateV6SchemaFormat
5539
+ });
5540
+ if (states) setupObj.states = states;
5541
+ }
4522
5542
  const configStr = serializeJS(machineConfig, 2);
4523
5543
  machineExpr = `setup(${serializeJS(setupObj, 0)}).createMachine(${configStr})`;
4524
5544
  } else machineExpr = `createMachine(${serializeJS(machineConfig, 0)})`;
@@ -4535,6 +5555,51 @@ function graphToXStateTS(graph, options = {}) {
4535
5555
  }
4536
5556
  return lines.join("\n") + "\n";
4537
5557
  }
5558
+ function stripStateSchemas(config) {
5559
+ delete config.schemas;
5560
+ const states = config.states;
5561
+ if (!states || typeof states !== "object" || Array.isArray(states)) return;
5562
+ for (const state of Object.values(states)) if (state && typeof state === "object" && !Array.isArray(state)) stripStateSchemas(state);
5563
+ }
5564
+ function buildV6SetupStates(graph, options) {
5565
+ const root = graph.nodes.find((node) => node.parentId == null);
5566
+ if (!root) return void 0;
5567
+ const buildState = (nodeId) => {
5568
+ const node = graph.nodes.find((candidate) => candidate.id === nodeId);
5569
+ if (!node) return void 0;
5570
+ const result = {};
5571
+ const schemas = buildV6StateSchemasBlock(node.data.schemas, options);
5572
+ if (schemas) result.schemas = schemas;
5573
+ const childStates = Object.fromEntries(graph.nodes.filter((candidate) => candidate.parentId === node.id).flatMap((child) => {
5574
+ const contract = buildState(child.id);
5575
+ return contract ? [[child.data.key, contract]] : [];
5576
+ }));
5577
+ if (Object.keys(childStates).length) result.states = childStates;
5578
+ return Object.keys(result).length ? result : void 0;
5579
+ };
5580
+ const states = Object.fromEntries(graph.nodes.filter((node) => node.parentId === root.id).flatMap((node) => {
5581
+ const contract = buildState(node.id);
5582
+ return contract ? [[node.data.key, contract]] : [];
5583
+ }));
5584
+ return Object.keys(states).length ? states : void 0;
5585
+ }
5586
+ function buildV6StateSchemasBlock(schemas, options) {
5587
+ if (!schemas) return void 0;
5588
+ const result = {};
5589
+ const contextSchema = schemas.context;
5590
+ if (options.xstateV6SchemaFormat === "zod") {
5591
+ const context = contextSchemaToZod(contextSchema);
5592
+ if (context) result.context = raw(context);
5593
+ if (schemas.input) result.input = raw(jsonSchemaToZod(schemas.input));
5594
+ if (schemas.output) result.output = raw(jsonSchemaToZod(schemas.output));
5595
+ } else if (options.includeTypeSyntax) {
5596
+ const context = contextSchemaToTSType(contextSchema);
5597
+ if (context) result.context = raw(`types<${context}>()`);
5598
+ if (schemas.input) result.input = raw(`types<${jsonSchemaToTSType(schemas.input)}>()`);
5599
+ if (schemas.output) result.output = raw(`types<${jsonSchemaToTSType(schemas.output)}>()`);
5600
+ }
5601
+ return Object.keys(result).length ? result : void 0;
5602
+ }
4538
5603
  function filterInlineImplementations(impls) {
4539
5604
  if (!impls) return impls;
4540
5605
  return {
@@ -5282,6 +6347,108 @@ function isCompleteFunctionExpression(code) {
5282
6347
  if (!expression) return false;
5283
6348
  return ts.isFunctionExpression(expression) || ts.isArrowFunction(expression);
5284
6349
  }
6350
+ function getXStateFSMCompatibilityIssues(graph) {
6351
+ const root = graph.nodes.find((node) => node.parentId == null);
6352
+ if (!root) return ["missing root state"];
6353
+ const states = graph.nodes.filter((node) => node.parentId === root.id);
6354
+ const stateIds = new Set(states.map((node) => node.id));
6355
+ const issues = /* @__PURE__ */ new Set();
6356
+ if (!root.data.initialId || !stateIds.has(root.data.initialId)) issues.add("missing initial state");
6357
+ if (graph.nodes.some((node) => node.parentId !== null && node.parentId !== root.id)) issues.add("nested states");
6358
+ if (states.some((node) => node.data.type && node.data.type !== "normal")) issues.add("non-atomic state types");
6359
+ if (graph.nodes.some((node) => node.data.entry?.length || node.data.exit?.length || node.data.invokes?.length || node.data.timeout !== void 0 || node.data.route !== void 0 || node.data.output !== void 0 || node.data.initialInput !== void 0)) issues.add("state effects, invokes, timeouts, routes, or outputs");
6360
+ if (graph.nodes.some((node) => node.data.schemas?.input || node.data.schemas?.output) || graph.data.schemas?.input || graph.data.schemas?.output) issues.add("input or output schemas");
6361
+ if (graph.data.version || graph.data.options) issues.add("machine version or runtime options");
6362
+ const eventCounts = /* @__PURE__ */ new Map();
6363
+ for (const edge of graph.edges.filter((edge) => stateIds.has(edge.sourceId))) {
6364
+ const key = `${edge.sourceId}:${edge.data.eventType}`;
6365
+ eventCounts.set(key, (eventCounts.get(key) ?? 0) + 1);
6366
+ if (!edge.data.eventType) issues.add("eventless transitions");
6367
+ if (!stateIds.has(edge.targetId) && edge.data.transitionType !== "targetless") issues.add("transitions outside the flat state set");
6368
+ if (edge.data.guard || edge.data.actions?.length || edge.data.matches || edge.data.input !== void 0 || edge.data.transitionType === "reenter") issues.add("guards, actions, matching, transition input, or reentry");
6369
+ if (isCodeExpression$1(edge.data.context)) issues.add("dynamic transition context");
6370
+ }
6371
+ if ([...eventCounts.values()].some((count) => count > 1)) issues.add("multiple transitions for one event");
6372
+ return [...issues];
6373
+ }
6374
+ function graphToXStateFSM(graph, options = {}) {
6375
+ const root = graph.nodes.find((node) => node.parentId == null);
6376
+ if (!root) throw new Error("No root node found");
6377
+ const states = graph.nodes.filter((node) => node.parentId === root.id);
6378
+ const stateById = new Map(states.map((node) => [node.id, node]));
6379
+ const initial = root.data.initialId ? stateById.get(root.data.initialId)?.data.key : void 0;
6380
+ if (!initial) throw new Error("Cannot export XState FSM without an initial state.");
6381
+ const schemaFormat = options.xstateV6SchemaFormat ?? "types";
6382
+ const setupConfig = buildSetupConfig(graph, states, schemaFormat);
6383
+ const config = {
6384
+ id: root.data.nodeId || graph.id,
6385
+ initial,
6386
+ context: graph.data.context ?? void 0,
6387
+ states: Object.fromEntries(states.map((state) => [state.data.key, buildState(graph, state, stateById)]))
6388
+ };
6389
+ const imports = [setupConfig ? "setup" : "createFSM"];
6390
+ if (schemaFormat === "types" && setupConfig) imports.push("types");
6391
+ const lines = [`import { ${imports.join(", ")} } from 'xstate/fsm';`];
6392
+ if (schemaFormat === "zod" && setupConfig) lines.push(`import { z } from 'zod';`);
6393
+ const expression = setupConfig ? `setup(${serializeJS(setupConfig, 0)}).createFSM(${serializeJS(config, 0)})` : `createFSM(${serializeJS(config, 0)})`;
6394
+ lines.push("", `const machine = ${expression};`);
6395
+ if ((options.exportStyle ?? "named") === "named") lines.push("", "export { machine };");
6396
+ else if (options.exportStyle === "default") lines.push("", "export default machine;");
6397
+ return `${lines.join("\n")}\n`;
6398
+ }
6399
+ function buildState(graph, state, stateById) {
6400
+ const on = {};
6401
+ for (const edge of graph.edges.filter((candidate) => candidate.sourceId === state.id)) {
6402
+ const eventType = edge.data.eventType;
6403
+ if (!eventType || on[eventType] !== void 0) continue;
6404
+ const transition = buildTransition$1(edge, stateById);
6405
+ if (transition !== void 0) on[eventType] = transition;
6406
+ }
6407
+ return Object.keys(on).length ? { on } : {};
6408
+ }
6409
+ function buildTransition$1(edge, stateById) {
6410
+ const target = edge.data.transitionType === "targetless" ? void 0 : stateById.get(edge.targetId)?.data.key;
6411
+ const context = isCodeExpression$1(edge.data.context) ? void 0 : edge.data.context;
6412
+ if (context !== void 0) return {
6413
+ target,
6414
+ context
6415
+ };
6416
+ return target;
6417
+ }
6418
+ function buildSetupConfig(graph, states, format) {
6419
+ const schemas = {};
6420
+ const rootContext = graph.data.schemas?.context;
6421
+ const rootEvents = graph.data.schemas?.events;
6422
+ if (format === "types") {
6423
+ const context = contextSchemaToTSType(rootContext);
6424
+ if (context) schemas.context = raw(`types<${context}>()`);
6425
+ if (rootEvents && Object.keys(rootEvents).length) schemas.events = Object.fromEntries(Object.entries(rootEvents).map(([eventType, schema]) => [eventType, raw(`types<${eventPayloadType(schema)}>()`)]));
6426
+ } else {
6427
+ const context = contextSchemaToZod(rootContext);
6428
+ if (context) schemas.context = raw(context);
6429
+ if (rootEvents && Object.keys(rootEvents).length) schemas.events = Object.fromEntries(Object.entries(rootEvents).map(([eventType, schema]) => [eventType, raw(eventSchemaToZod(schema))]));
6430
+ }
6431
+ const stateContracts = Object.fromEntries(states.flatMap((state) => {
6432
+ const context = format === "types" ? contextSchemaToTSType(state.data.schemas?.context) : contextSchemaToZod(state.data.schemas?.context);
6433
+ return context ? [[state.data.key, { schemas: { context: raw(format === "types" ? `types<${context}>()` : context) } }]] : [];
6434
+ }));
6435
+ const setup = {};
6436
+ if (Object.keys(schemas).length) setup.schemas = schemas;
6437
+ if (Object.keys(stateContracts).length) setup.states = stateContracts;
6438
+ return Object.keys(setup).length ? setup : void 0;
6439
+ }
6440
+ function eventPayloadType(schema) {
6441
+ const properties = Object.fromEntries(Object.entries(schema.properties ?? {}).filter(([key]) => key !== "type"));
6442
+ const required = schema.required?.filter((key) => key !== "type");
6443
+ return jsonSchemaToTSType({
6444
+ ...schema,
6445
+ properties,
6446
+ required
6447
+ });
6448
+ }
6449
+ function isCodeExpression$1(value) {
6450
+ return !!value && typeof value === "object" && !Array.isArray(value) && value["@type"] === "code";
6451
+ }
5285
6452
  const colorSchema = _enum([
5286
6453
  "green",
5287
6454
  "red",
@@ -5663,4 +6830,4 @@ function getScriptKind(fileName) {
5663
6830
  }
5664
6831
 
5665
6832
  //#endregion
5666
- export { RawCode as a, eventsSchemaToTSType as c, jsonSchemaToTSType as d, normalizeMachineProfile as f, xstateSourceToConfig as h, INLINE_EXPRESSION_DIRECTIVE_FIELDS as i, graphToMachineConfig as l, serializeJS as m, getStatelyPragma as n, contextSchemaToTSType as o, raw as p, upsertStatelyPragma as r, detectXStateSourceProfile as s, findStatelyPragmaAttachments as t, graphToXStateTS as u };
6833
+ export { normalizeMachineProfile as _, RawCode as a, xstateSourceToConfig as b, detectXStateSourceProfile as c, getXStateFSMCompatibilityIssues as d, graphChangeToPatchesResult as f, jsonSchemaToTSType as g, graphToXStateTS as h, INLINE_EXPRESSION_DIRECTIVE_FIELDS as i, diffGraphsSemantically as l, graphToXStateFSM as m, getStatelyPragma as n, alignCompareGraphIds as o, graphToMachineConfig as p, upsertStatelyPragma as r, contextSchemaToTSType as s, findStatelyPragmaAttachments as t, eventsSchemaToTSType as u, raw as v, serializeJS as y };