@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.
- package/README.md +30 -2
- package/dist/auth.d.mts +1 -1
- package/dist/{editorHostSession-DFjM-RoT.d.mts → editorHostSession-CWAuoMtC.d.mts} +9 -3
- package/dist/{editorHostSession-CwGeXJ6g.mjs → editorHostSession-shrjfosC.mjs} +6 -3
- package/dist/editorHostSession.d.mts +1 -1
- package/dist/editorHostSession.mjs +1 -1
- package/dist/embed.d.mts +6 -3
- package/dist/embed.mjs +23 -1
- package/dist/embedPanel-Bpbq4sfJ.d.mts +13 -0
- package/dist/{graph-DoUReqxz.d.mts → graph-BwHnhzpC.d.mts} +48 -6
- package/dist/graph.d.mts +1 -1
- package/dist/graph.mjs +36 -4
- package/dist/{index-DKRB-u93.d.mts → index-D2I1qPsf.d.mts} +19 -3
- package/dist/index.d.mts +8 -7
- package/dist/index.mjs +6 -6
- package/dist/{inspect-Br_vS_lK.d.mts → inspect-D61HS-t7.d.mts} +2 -5
- package/dist/{inspect-BA3EPIz9.mjs → inspect-_E2lVbQA.mjs} +2 -4
- package/dist/inspect.d.mts +1 -1
- package/dist/inspect.mjs +2 -2
- package/dist/patchTypes.d.mts +1 -1
- package/dist/protocol.d.mts +46 -15
- package/dist/{statelyPragma-CWxhEIcZ.mjs → statelyPragma-DD7wx7-b.mjs} +1198 -31
- package/dist/{studio-B6P177N3.d.mts → studio-B9-1Ynrx.d.mts} +4 -2
- package/dist/{studio-iNSHtNmP.mjs → studio-CNkxMZyz.mjs} +4 -1
- package/dist/studio.d.mts +1 -1
- package/dist/studio.mjs +1 -1
- package/dist/sync.d.mts +12 -4
- package/dist/sync.mjs +2920 -262
- package/dist/{version-DKSLBwJ9.mjs → version-7SUCFMqb.mjs} +34 -8
- package/package.json +3 -3
- package/schemas/xstate.schema.json +41 -5
- /package/dist/{auth-B0ZTKri2.d.mts → auth-BPyCCHYo.d.mts} +0 -0
- /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.
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
4165
|
-
if (!ts.isPropertyAccessExpression(node.expression)) return
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
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
|
-
|
|
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
|
|
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(...)
|
|
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
|
|
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
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
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
|
-
|
|
4278
|
-
|
|
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,
|
|
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 };
|