@statelyai/sdk 0.10.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sync.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createStatelyClient } from "./studio.mjs";
2
- import { S as upsertStatelyPragma, _ as string, b as findStatelyPragmaAttachments, d as boolean, f as discriminatedUnion, g as record, h as object, l as _enum, m as number, p as literal, t as graphToXStateTS, u as array, v as union, x as getStatelyPragma, y as unknown } from "./graphToXStateTS-DOaLOySy.mjs";
2
+ import { S as upsertStatelyPragma, _ as string, b as findStatelyPragmaAttachments, d as boolean, f as discriminatedUnion, g as record, h as object, l as _enum, m as number, p as literal, t as graphToXStateTS, u as array, v as union, x as getStatelyPragma, y as unknown } from "./graphToXStateTS-D12GTMBp.mjs";
3
3
  import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
4
4
  import * as ts$1 from "typescript";
5
5
  import ts from "typescript";
@@ -111,9 +111,346 @@ function getLineStarts(text) {
111
111
 
112
112
  //#endregion
113
113
  //#region ../graph-tools/dist/index.mjs
114
+ /**
115
+ * Converts a graph to an adjacency list representation.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * import { createGraph, toAdjacencyList } from '@statelyai/graph';
120
+ *
121
+ * const graph = createGraph({
122
+ * nodes: { a: {}, b: {}, c: {} },
123
+ * edges: [{ source: 'a', target: 'b' }, { source: 'a', target: 'c' }],
124
+ * });
125
+ *
126
+ * toAdjacencyList(graph);
127
+ * // { a: ['b', 'c'], b: [], c: [] }
128
+ * ```
129
+ */
130
+ function toAdjacencyList(graph) {
131
+ const adj = {};
132
+ for (const node of graph.nodes) adj[node.id] = [];
133
+ for (const edge of graph.edges) {
134
+ adj[edge.sourceId]?.push(edge.targetId);
135
+ if (graph.mode !== "directed") adj[edge.targetId]?.push(edge.sourceId);
136
+ }
137
+ return adj;
138
+ }
139
+ /**
140
+ * Parses an adjacency list into a graph.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { fromAdjacencyList } from '@statelyai/graph';
145
+ *
146
+ * const graph = fromAdjacencyList({
147
+ * a: ['b', 'c'],
148
+ * b: ['c'],
149
+ * c: [],
150
+ * });
151
+ *
152
+ * graph.nodes; // [{id: 'a', ...}, {id: 'b', ...}, {id: 'c', ...}]
153
+ * graph.edges; // [{sourceId: 'a', targetId: 'b', ...}, ...]
154
+ * ```
155
+ */
156
+ function fromAdjacencyList(adj, options) {
157
+ const directed = options?.directed ?? true;
158
+ const seen = /* @__PURE__ */ new Set();
159
+ const nodes = Object.keys(adj).map((id) => ({
160
+ type: "node",
161
+ id,
162
+ parentId: null,
163
+ initialNodeId: null,
164
+ label: "",
165
+ data: void 0
166
+ }));
167
+ const edges = [];
168
+ let edgeIdx = 0;
169
+ for (const [sourceId, targets] of Object.entries(adj)) for (const targetId of targets) {
170
+ const key = directed ? `${sourceId}->${targetId}` : [sourceId, targetId].sort().join("<->");
171
+ if (!seen.has(key)) {
172
+ seen.add(key);
173
+ edges.push({
174
+ type: "edge",
175
+ id: `e${edgeIdx++}`,
176
+ sourceId,
177
+ targetId,
178
+ label: "",
179
+ data: void 0
180
+ });
181
+ }
182
+ }
183
+ return {
184
+ id: options?.id ?? "",
185
+ mode: directed ? "directed" : "undirected",
186
+ initialNodeId: null,
187
+ nodes,
188
+ edges,
189
+ data: void 0
190
+ };
191
+ }
192
+ /**
193
+ * Converts a graph to an edge list of `[source, target]` tuples.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * import { createGraph, toEdgeList } from '@statelyai/graph';
198
+ *
199
+ * const graph = createGraph({
200
+ * nodes: { a: {}, b: {}, c: {} },
201
+ * edges: [{ source: 'a', target: 'b' }, { source: 'b', target: 'c' }],
202
+ * });
203
+ *
204
+ * toEdgeList(graph);
205
+ * // [['a', 'b'], ['b', 'c']]
206
+ * ```
207
+ */
208
+ function toEdgeList(graph) {
209
+ return graph.edges.map((e) => [e.sourceId, e.targetId]);
210
+ }
211
+ /**
212
+ * Parses an edge list of `[source, target]` tuples into a graph.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * import { fromEdgeList } from '@statelyai/graph';
217
+ *
218
+ * const graph = fromEdgeList([
219
+ * ['a', 'b'],
220
+ * ['b', 'c'],
221
+ * ]);
222
+ *
223
+ * graph.nodes; // [{id: 'a', ...}, {id: 'b', ...}, {id: 'c', ...}]
224
+ * graph.edges; // [{sourceId: 'a', targetId: 'b', ...}, ...]
225
+ * ```
226
+ */
227
+ function fromEdgeList(edges, options) {
228
+ const directed = options?.directed ?? true;
229
+ const nodeIds = /* @__PURE__ */ new Set();
230
+ for (const [source, target] of edges) {
231
+ nodeIds.add(source);
232
+ nodeIds.add(target);
233
+ }
234
+ const nodes = [...nodeIds].map((id) => ({
235
+ type: "node",
236
+ id,
237
+ parentId: null,
238
+ initialNodeId: null,
239
+ label: "",
240
+ data: void 0
241
+ }));
242
+ const edgeObjects = edges.map(([sourceId, targetId], i) => ({
243
+ type: "edge",
244
+ id: `e${i}`,
245
+ sourceId,
246
+ targetId,
247
+ label: "",
248
+ data: void 0
249
+ }));
250
+ return {
251
+ id: options?.id ?? "",
252
+ mode: directed ? "directed" : "undirected",
253
+ initialNodeId: null,
254
+ nodes,
255
+ edges: edgeObjects,
256
+ data: void 0
257
+ };
258
+ }
259
+ /**
260
+ * Create a `GraphFormatConverter` from a pair of `to`/`from` functions.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * import { createFormatConverter } from '@statelyai/graph';
265
+ *
266
+ * const yamlConverter = createFormatConverter(
267
+ * (graph) => toYAML(graph),
268
+ * (yaml) => fromYAML(yaml),
269
+ * );
270
+ *
271
+ * const yaml = yamlConverter.to(graph);
272
+ * const graph = yamlConverter.from(yaml);
273
+ * ```
274
+ */
275
+ function createFormatConverter$1(to, from) {
276
+ return {
277
+ to,
278
+ from
279
+ };
280
+ }
281
+ createFormatConverter$1(toAdjacencyList, fromAdjacencyList);
282
+ createFormatConverter$1(toEdgeList, fromEdgeList);
283
+ const LAYOUT_KEYS = {
284
+ node: [
285
+ "x",
286
+ "y",
287
+ "width",
288
+ "height",
289
+ "style",
290
+ "color",
291
+ "shape"
292
+ ],
293
+ edge: [
294
+ "x",
295
+ "y",
296
+ "width",
297
+ "height",
298
+ "style",
299
+ "color"
300
+ ]
301
+ };
302
+ new Set(LAYOUT_KEYS.node), new Set(LAYOUT_KEYS.edge);
303
+ function nodeToConfig$1(node) {
304
+ const config = { id: node.id };
305
+ if (node.parentId) config.parentId = node.parentId;
306
+ if (node.initialNodeId) config.initialNodeId = node.initialNodeId;
307
+ if (node.label !== "") config.label = node.label;
308
+ if (node.data !== void 0) config.data = node.data;
309
+ if (node.x !== void 0) config.x = node.x;
310
+ if (node.y !== void 0) config.y = node.y;
311
+ if (node.width !== void 0) config.width = node.width;
312
+ if (node.height !== void 0) config.height = node.height;
313
+ if (node.shape !== void 0) config.shape = node.shape;
314
+ if (node.color !== void 0) config.color = node.color;
315
+ if (node.style !== void 0) config.style = node.style;
316
+ return config;
317
+ }
318
+ function edgeToConfig$1(edge) {
319
+ const config = {
320
+ id: edge.id,
321
+ sourceId: edge.sourceId,
322
+ targetId: edge.targetId
323
+ };
324
+ if (edge.label !== "") config.label = edge.label;
325
+ if (edge.data !== void 0) config.data = edge.data;
326
+ if (edge.x !== void 0) config.x = edge.x;
327
+ if (edge.y !== void 0) config.y = edge.y;
328
+ if (edge.width !== void 0) config.width = edge.width;
329
+ if (edge.height !== void 0) config.height = edge.height;
330
+ if (edge.color !== void 0) config.color = edge.color;
331
+ if (edge.style !== void 0) config.style = edge.style;
332
+ return config;
333
+ }
334
+ /** Shallow-compare two values, returning true if they differ. */
335
+ function differs(a, b) {
336
+ if (a === b) return false;
337
+ if (a == null || b == null) return a !== b;
338
+ if (typeof a === "object" && typeof b === "object") return JSON.stringify(a) !== JSON.stringify(b);
339
+ return true;
340
+ }
341
+ const NODE_COMPARE_KEYS = [
342
+ "parentId",
343
+ "initialNodeId",
344
+ "label",
345
+ "data",
346
+ "x",
347
+ "y",
348
+ "width",
349
+ "height",
350
+ "shape",
351
+ "color",
352
+ "style"
353
+ ];
354
+ const EDGE_COMPARE_KEYS = [
355
+ "sourceId",
356
+ "targetId",
357
+ "label",
358
+ "data",
359
+ "x",
360
+ "y",
361
+ "width",
362
+ "height",
363
+ "color",
364
+ "style"
365
+ ];
366
+ /**
367
+ * Compute a structured diff from graph `a` to graph `b` by matching IDs.
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * import { createGraph, getDiff } from '@statelyai/graph';
372
+ *
373
+ * const a = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
374
+ * const b = createGraph({ nodes: [{ id: 'n1', label: 'hello' }, { id: 'n2' }], edges: [] });
375
+ *
376
+ * const diff = getDiff(a, b);
377
+ * // diff.nodes.added → [{ id: 'n2' }]
378
+ * // diff.nodes.updated → [{ id: 'n1', old: { label: '' }, new: { label: 'hello' } }]
379
+ * ```
380
+ */
381
+ function getDiff$1(a, b) {
382
+ const aNodeMap = new Map(a.nodes.map((n) => [n.id, n]));
383
+ const bNodeMap = new Map(b.nodes.map((n) => [n.id, n]));
384
+ const aEdgeMap = new Map(a.edges.map((e) => [e.id, e]));
385
+ const bEdgeMap = new Map(b.edges.map((e) => [e.id, e]));
386
+ const diff = {
387
+ nodes: {
388
+ added: [],
389
+ removed: [],
390
+ updated: []
391
+ },
392
+ edges: {
393
+ added: [],
394
+ removed: [],
395
+ updated: []
396
+ }
397
+ };
398
+ for (const [id, nodeB] of bNodeMap) {
399
+ const nodeA = aNodeMap.get(id);
400
+ if (!nodeA) diff.nodes.added.push(nodeToConfig$1(nodeB));
401
+ else {
402
+ const oldPartial = {};
403
+ const newPartial = {};
404
+ for (const key of NODE_COMPARE_KEYS) if (differs(nodeA[key], nodeB[key])) {
405
+ oldPartial[key] = nodeA[key];
406
+ newPartial[key] = nodeB[key];
407
+ }
408
+ if (Object.keys(oldPartial).length > 0) diff.nodes.updated.push({
409
+ id,
410
+ old: oldPartial,
411
+ new: newPartial
412
+ });
413
+ }
414
+ }
415
+ for (const [id, nodeA] of aNodeMap) if (!bNodeMap.has(id)) diff.nodes.removed.push(nodeToConfig$1(nodeA));
416
+ for (const [id, edgeB] of bEdgeMap) {
417
+ const edgeA = aEdgeMap.get(id);
418
+ if (!edgeA) diff.edges.added.push(edgeToConfig$1(edgeB));
419
+ else {
420
+ const oldPartial = {};
421
+ const newPartial = {};
422
+ for (const key of EDGE_COMPARE_KEYS) if (differs(edgeA[key], edgeB[key])) {
423
+ oldPartial[key] = edgeA[key];
424
+ newPartial[key] = edgeB[key];
425
+ }
426
+ if (Object.keys(oldPartial).length > 0) diff.edges.updated.push({
427
+ id,
428
+ old: oldPartial,
429
+ new: newPartial
430
+ });
431
+ }
432
+ }
433
+ for (const [id, edgeA] of aEdgeMap) if (!bEdgeMap.has(id)) diff.edges.removed.push(edgeToConfig$1(edgeA));
434
+ return diff;
435
+ }
436
+ /**
437
+ * Check if a diff has zero changes.
438
+ *
439
+ * @example
440
+ * ```ts
441
+ * import { createGraph, getDiff, isEmptyDiff } from '@statelyai/graph';
442
+ *
443
+ * const g = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
444
+ * const diff = getDiff(g, g);
445
+ * isEmptyDiff(diff); // true
446
+ * ```
447
+ */
448
+ function isEmptyDiff$1(diff) {
449
+ 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;
450
+ }
114
451
  function summarizeGraphDiff(diff) {
115
452
  return {
116
- hasChanges: !isEmptyDiff(diff),
453
+ hasChanges: !isEmptyDiff$1(diff),
117
454
  nodeChanges: diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length,
118
455
  edgeChanges: diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length
119
456
  };
@@ -180,7 +517,7 @@ function normalizeGraphForSemanticDiff(graph) {
180
517
  function diffGraphsSemantically(baseGraph, compareGraph) {
181
518
  const base = normalizeGraphForSemanticDiff(baseGraph);
182
519
  const compare = normalizeGraphForSemanticDiff(compareGraph);
183
- const diff = getDiff(base, compare);
520
+ const diff = getDiff$1(base, compare);
184
521
  return {
185
522
  base,
186
523
  compare,
@@ -839,6 +1176,7 @@ function deepSimplify(config) {
839
1176
  return config;
840
1177
  }
841
1178
  function buildMeta(meta, color) {
1179
+ if (isCodeExpression(meta)) return meta;
842
1180
  const result = {};
843
1181
  if (meta) {
844
1182
  for (const [k, v] of Object.entries(meta)) if (v !== void 0 && v !== null) result[k] = v;
@@ -846,6 +1184,9 @@ function buildMeta(meta, color) {
846
1184
  if (color) result["@statelyai.color"] = color;
847
1185
  return Object.keys(result).length > 0 ? result : void 0;
848
1186
  }
1187
+ function isCodeExpression(value) {
1188
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && value["@type"] === "code" && typeof value.expr === "string");
1189
+ }
849
1190
  /** Format event param — supports { type: string } (new) and plain string (legacy) */
850
1191
  function formatEventParam(event) {
851
1192
  if (event && typeof event === "object" && "type" in event) return formatInlineObject(event);
@@ -864,6 +1205,10 @@ function formatObjectKey(key) {
864
1205
  const eventParamSchema = union([string(), object({ type: string() }).catchall(unknown())]);
865
1206
  const delayParamSchema = union([number(), string()]);
866
1207
  const builtInActionParamSchemas = {
1208
+ "xstate.enqueue": object({
1209
+ code: string().optional(),
1210
+ lang: string().optional()
1211
+ }),
867
1212
  "xstate.raise": object({
868
1213
  event: eventParamSchema.optional(),
869
1214
  id: string().optional(),
@@ -887,6 +1232,16 @@ const builtInActionParamSchemas = {
887
1232
  "xstate.assign": object({ assignment: record(string(), unknown()).optional() }).catchall(unknown())
888
1233
  };
889
1234
  const XSTATE_BUILT_IN_ACTIONS = {
1235
+ "xstate.enqueue": {
1236
+ package: "xstate",
1237
+ version: "^6",
1238
+ import: "enqueue",
1239
+ importKind: "named",
1240
+ paramsSchema: builtInActionParamSchemas["xstate.enqueue"],
1241
+ emit(params) {
1242
+ return typeof params?.code === "string" ? stripExportDefault(params.code) : "";
1243
+ }
1244
+ },
890
1245
  "xstate.raise": {
891
1246
  package: "xstate",
892
1247
  version: "^5",
@@ -1003,7 +1358,7 @@ function actionCodeExpression(expr, source) {
1003
1358
  }]
1004
1359
  };
1005
1360
  }
1006
- function inlineCodeExpression(expr) {
1361
+ function inlineCodeExpression$1(expr) {
1007
1362
  return {
1008
1363
  "@type": "code",
1009
1364
  lang: "ts",
@@ -1021,7 +1376,7 @@ function serializeGuard(guard, asTypeRef) {
1021
1376
  type: guard.type,
1022
1377
  params: guard.params
1023
1378
  } : { type: guard.type };
1024
- return guard.code ? inlineCodeExpression(guard.code) : guard;
1379
+ return guard.code ? inlineCodeExpression$1(guard.code) : guard;
1025
1380
  }
1026
1381
  /**
1027
1382
  * Converts a built-in xstate ActionItem to a code expression directive,
@@ -1029,12 +1384,13 @@ function serializeGuard(guard, asTypeRef) {
1029
1384
  */
1030
1385
  function serializeActionItem(action) {
1031
1386
  const { type, params } = action;
1387
+ if (type === "xstate.enqueue" && typeof params?.code === "string") return inlineCodeExpression$1(params.code);
1032
1388
  const exprCode = type === "xstate.expr" && typeof params?.code === "string" ? params.code : void 0;
1033
1389
  if (isBuiltInActionType(type)) {
1034
1390
  const builtInAction = XSTATE_BUILT_IN_ACTIONS[type];
1035
1391
  return actionCodeExpression(emitBuiltInAction(type, params), builtInAction);
1036
1392
  }
1037
- if (exprCode) return inlineCodeExpression(exprCode);
1393
+ if (exprCode) return inlineCodeExpression$1(exprCode);
1038
1394
  if (!params || Object.keys(params).length === 0) return { type };
1039
1395
  return {
1040
1396
  type,
@@ -1061,7 +1417,8 @@ function graphToMachineConfig(graph, options = {}) {
1061
1417
  const nodeType = [
1062
1418
  "final",
1063
1419
  "history",
1064
- "parallel"
1420
+ "parallel",
1421
+ "choice"
1065
1422
  ].includes(node.data.type) ? node.data.type : void 0;
1066
1423
  const tags = node.data.tags;
1067
1424
  Object.assign(nodeConfig, {
@@ -1076,6 +1433,7 @@ function graphToMachineConfig(graph, options = {}) {
1076
1433
  ...inv.input ? { input: inv.input } : void 0,
1077
1434
  ...inv.output ? { output: inv.output } : void 0
1078
1435
  })) } : void 0,
1436
+ ...node.data.output !== void 0 ? { output: node.data.output } : void 0,
1079
1437
  ...tags?.length ? { tags: tags.map(tagToString) } : void 0,
1080
1438
  ...showDescriptions && node.data.description ? { description: node.data.description } : void 0,
1081
1439
  ...showMeta && node.data.meta && Object.keys(node.data.meta).length > 0 ? { meta: node.data.meta } : void 0,
@@ -1087,6 +1445,7 @@ function graphToMachineConfig(graph, options = {}) {
1087
1445
  const on = {};
1088
1446
  const after = {};
1089
1447
  const alwaysArr = [];
1448
+ const choiceArr = [];
1090
1449
  const onDone = {};
1091
1450
  const invokeMap = {};
1092
1451
  for (const edge of edges) {
@@ -1098,6 +1457,7 @@ function graphToMachineConfig(graph, options = {}) {
1098
1457
  target: edge.data.transitionType === "targetless" ? void 0 : `${resolvedTarget}`,
1099
1458
  ...edge.data.transitionType === "reenter" ? { reenter: true } : void 0,
1100
1459
  guard: edge.data.guard ? serializeGuard(edge.data.guard, inlineGuardsAsTypeRef) : void 0,
1460
+ ...edge.data.context !== void 0 ? { context: edge.data.context } : void 0,
1101
1461
  actions: edge.data.actions?.length ? edge.data.actions.map(serializeActionItem) : void 0,
1102
1462
  description: edge.data.description ?? void 0,
1103
1463
  ...transitionMeta ? { meta: transitionMeta } : void 0
@@ -1120,6 +1480,7 @@ function graphToMachineConfig(graph, options = {}) {
1120
1480
  if (!onDone[type]) onDone[type] = [];
1121
1481
  onDone[type].push(transitionObject);
1122
1482
  } else pushOn(`xstate.${ref.kind === "actionDone" ? "done" : "error"}.actor.${resolveSpawnActorId$1(node, ref.blockId)}`);
1483
+ else if (type === "" && node.data.type === "choice") choiceArr.push(transitionObject);
1123
1484
  else if (type === "") alwaysArr.push(transitionObject);
1124
1485
  else if (type.startsWith("xstate.after.")) {
1125
1486
  const delayKey = type.slice(13).split(".")[0];
@@ -1139,6 +1500,7 @@ function graphToMachineConfig(graph, options = {}) {
1139
1500
  if (Object.keys(on).length > 0) nodeConfig.on = singleOrArrayRecord(on);
1140
1501
  if (Object.keys(after).length > 0) nodeConfig.after = singleOrArrayRecord(after);
1141
1502
  if (alwaysArr.length > 0) nodeConfig.always = singleOrArray(alwaysArr);
1503
+ if (choiceArr.length > 0) nodeConfig.choice = singleOrArray(choiceArr);
1142
1504
  if (Object.keys(onDone).length > 0) nodeConfig.onDone = singleOrArray(Object.values(onDone).flat());
1143
1505
  if (Object.keys(invokeMap).length > 0 && Array.isArray(nodeConfig.invoke)) nodeConfig.invoke = nodeConfig.invoke.map((inv, index) => {
1144
1506
  const mapped = invokeMap[node.data.invokes?.[index]?.id ?? inv.id];
@@ -1517,6 +1879,7 @@ function guardObject(value) {
1517
1879
  const guard = value;
1518
1880
  return {
1519
1881
  type: guard.type,
1882
+ code: guard.code,
1520
1883
  params: guard.params
1521
1884
  };
1522
1885
  }
@@ -1547,9 +1910,10 @@ function machineConfigToGraph(config) {
1547
1910
  parentId,
1548
1911
  data: {
1549
1912
  key,
1550
- type: state.type === "parallel" || state.type === "final" || state.type === "history" ? state.type : null,
1913
+ type: state.type === "parallel" || state.type === "final" || state.type === "history" || state.type === "choice" ? state.type : null,
1551
1914
  initialId,
1552
1915
  history: state.history ?? false,
1916
+ ...state.output !== void 0 ? { output: state.output } : {},
1553
1917
  entry: actionItems(state.entry),
1554
1918
  exit: actionItems(state.exit),
1555
1919
  invokes: invokeItems(state.invoke),
@@ -1568,6 +1932,7 @@ function machineConfigToGraph(config) {
1568
1932
  eventType,
1569
1933
  transitionType: target ? transition.reenter ? "reenter" : "normal" : "targetless",
1570
1934
  guard: guardObject(transition.guard),
1935
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1571
1936
  actions: actionItems(transition.actions),
1572
1937
  description: typeof transition.description === "string" ? transition.description : null,
1573
1938
  meta: transition.meta && typeof transition.meta === "object" ? transition.meta : null
@@ -1584,6 +1949,7 @@ function machineConfigToGraph(config) {
1584
1949
  eventType: `xstate.after.${delay}.${id}`,
1585
1950
  transitionType: target ? "normal" : "targetless",
1586
1951
  guard: guardObject(transition.guard),
1952
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1587
1953
  actions: actionItems(transition.actions),
1588
1954
  description: typeof transition.description === "string" ? transition.description : null,
1589
1955
  meta: null
@@ -1600,12 +1966,30 @@ function machineConfigToGraph(config) {
1600
1966
  eventType: "",
1601
1967
  transitionType: target ? "normal" : "targetless",
1602
1968
  guard: guardObject(transition.guard),
1969
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1603
1970
  actions: actionItems(transition.actions),
1604
1971
  description: typeof transition.description === "string" ? transition.description : null,
1605
1972
  meta: null
1606
1973
  }
1607
1974
  });
1608
1975
  }
1976
+ for (const [index, transition] of transitionObjects(state.choice).entries()) {
1977
+ const target = transitionTargets(transition.target)[0];
1978
+ graph.edges.push({
1979
+ id: `${id}-choice-${index}`,
1980
+ sourceId: id,
1981
+ targetId: targetToNodeId(id, target, rootKey),
1982
+ data: {
1983
+ eventType: "",
1984
+ transitionType: target ? "normal" : "targetless",
1985
+ guard: guardObject(transition.guard),
1986
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1987
+ actions: actionItems(transition.actions),
1988
+ description: typeof transition.description === "string" ? transition.description : null,
1989
+ meta: transition.meta && typeof transition.meta === "object" ? transition.meta : null
1990
+ }
1991
+ });
1992
+ }
1609
1993
  for (const [childKey, child] of Object.entries(state.states ?? {})) visit(child, childKey, id);
1610
1994
  }
1611
1995
  visit(config, rootKey, null);
@@ -1633,6 +2017,19 @@ function propertyContext(parent, key) {
1633
2017
  v6: parent.v6,
1634
2018
  actionValue: true
1635
2019
  };
2020
+ if (parent.transitionValue && key === "context") return {
2021
+ v6: parent.v6,
2022
+ assignmentMap: true,
2023
+ transitionContext: true
2024
+ };
2025
+ if (parent.assignmentMap) return {
2026
+ v6: parent.v6,
2027
+ assignmentValue: true
2028
+ };
2029
+ if (key === "meta") return {
2030
+ v6: parent.v6,
2031
+ metaValue: true
2032
+ };
1636
2033
  if (parent.transitionValue && key === "to") return {
1637
2034
  v6: parent.v6,
1638
2035
  transitionValue: true
@@ -1641,21 +2038,28 @@ function propertyContext(parent, key) {
1641
2038
  v6: parent.v6,
1642
2039
  actionValue: true
1643
2040
  };
2041
+ if (key === "input" || key === "output") return {
2042
+ v6: parent.v6,
2043
+ ioValue: true
2044
+ };
1644
2045
  if (key === "on" || key === "after") return {
1645
2046
  v6: parent.v6,
1646
2047
  transitionMap: true
1647
2048
  };
1648
- if (key === "always" || key === "onDone" || key === "onError" || key === "onSnapshot" || key === "onTimeout") return {
2049
+ if (key === "always" || key === "choice" || key === "onDone" || key === "onError" || key === "onSnapshot" || key === "onTimeout") return {
1649
2050
  v6: parent.v6,
1650
2051
  transitionValue: true
1651
2052
  };
1652
2053
  return { v6: parent.v6 };
1653
2054
  }
1654
- function fallbackFunctionTransition(fn) {
1655
- return { meta: {
1656
- [XSTATE_V6_FN_TRANSITION_META]: true,
1657
- [XSTATE_V6_FN_SOURCE_META]: fn.getText()
1658
- } };
2055
+ function fallbackFunctionTransition(fn, actions = []) {
2056
+ return {
2057
+ ...actions.length ? { actions } : void 0,
2058
+ meta: {
2059
+ [XSTATE_V6_FN_TRANSITION_META]: true,
2060
+ [XSTATE_V6_FN_SOURCE_META]: fn.getText()
2061
+ }
2062
+ };
1659
2063
  }
1660
2064
  function conditionText(cond) {
1661
2065
  return cond.negated ? `!(${cond.text})` : cond.text;
@@ -1670,10 +2074,28 @@ function simplifyConditions(conds) {
1670
2074
  return true;
1671
2075
  });
1672
2076
  }
1673
- function guardFromConditions(conds) {
2077
+ function hashString(value) {
2078
+ let hash = 5381;
2079
+ for (let i = 0; i < value.length; i++) hash = hash * 33 ^ value.charCodeAt(i);
2080
+ return (hash >>> 0).toString(36);
2081
+ }
2082
+ function inlineGuardCode(params, condition) {
2083
+ return `${params} => ${condition}`;
2084
+ }
2085
+ function guardFromConditions(conds, params) {
1674
2086
  const simplified = simplifyConditions(conds);
1675
2087
  if (simplified.length === 0) return void 0;
1676
- return { type: simplified.map(conditionText).join(" && ") };
2088
+ const code = inlineGuardCode(params, simplified.map(conditionText).join(" && "));
2089
+ return {
2090
+ type: `inline:${hashString(code)}`,
2091
+ code
2092
+ };
2093
+ }
2094
+ function wrapAssignExpression(expr) {
2095
+ return `{{ ${expr.trim()} }}`;
2096
+ }
2097
+ function hasSpreadAssignment(node) {
2098
+ return node.properties.some((property) => ts.isSpreadAssignment(property));
1677
2099
  }
1678
2100
  function mergeActions(left, right) {
1679
2101
  return [...left ?? [], ...right];
@@ -1688,12 +2110,6 @@ function transitionFromValue(value, actions) {
1688
2110
  }
1689
2111
  if (!value || typeof value !== "object" || Array.isArray(value)) return actions.length ? { actions } : null;
1690
2112
  const record = { ...value };
1691
- const context = record.context;
1692
- delete record.context;
1693
- if (context !== void 0) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, [{
1694
- type: "xstate.assign",
1695
- params: { assignment: context }
1696
- }]);
1697
2113
  if (actions.length) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, actions);
1698
2114
  return record;
1699
2115
  }
@@ -1741,13 +2157,19 @@ function expressionBranches(expression, bindings, depth) {
1741
2157
  const target = targetProperty ? unwrapExpression$2(targetProperty.initializer) : null;
1742
2158
  if (target && (ts.isConditionalExpression(target) || ts.isBinaryExpression(target) && (target.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken || target.operatorToken.kind === ts.SyntaxKind.BarBarToken))) return expressionBranches(target, bindings, depth + 1).map((branch) => ({
1743
2159
  ...branch,
1744
- result: objectLiteralToValue(node, bindings, {}, depth + 1, new Map([["target", branch.result]]))
2160
+ result: objectLiteralToValue(node, bindings, {
2161
+ v6: true,
2162
+ transitionValue: true
2163
+ }, depth + 1, new Map([["target", branch.result]]))
1745
2164
  }));
1746
2165
  }
1747
2166
  return [{
1748
2167
  conds: [],
1749
2168
  actions: [],
1750
- result: expressionToValue(node, bindings, {}, depth + 1)
2169
+ result: expressionToValue(node, bindings, {
2170
+ v6: true,
2171
+ transitionValue: true
2172
+ }, depth + 1)
1751
2173
  }];
1752
2174
  }
1753
2175
  function mergeBranchPrefix(prefix, branch) {
@@ -1833,9 +2255,15 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1833
2255
  }]) {
1834
2256
  let open = prefixes;
1835
2257
  const done = [];
2258
+ const scopedBindings = new Map(bindings);
1836
2259
  for (const statement of statements) {
1837
2260
  const nextOpen = [];
1838
2261
  for (const prefix of open) {
2262
+ if (ts.isVariableStatement(statement)) {
2263
+ for (const declaration of statement.declarationList.declarations) if (ts.isIdentifier(declaration.name) && declaration.initializer) scopedBindings.set(declaration.name.text, declaration.initializer);
2264
+ nextOpen.push(prefix);
2265
+ continue;
2266
+ }
1839
2267
  if (ts.isReturnStatement(statement)) {
1840
2268
  if (!statement.expression) {
1841
2269
  done.push({
@@ -1844,11 +2272,11 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1844
2272
  });
1845
2273
  continue;
1846
2274
  }
1847
- for (const branch of expressionBranches(statement.expression, bindings, 0)) done.push(mergeBranchPrefix(prefix, branch));
2275
+ for (const branch of expressionBranches(statement.expression, scopedBindings, 0)) done.push(mergeBranchPrefix(prefix, branch));
1848
2276
  continue;
1849
2277
  }
1850
2278
  if (ts.isExpressionStatement(statement)) {
1851
- const action = expressionStatementAction(statement, enqName, bindings);
2279
+ const action = expressionStatementAction(statement, enqName, scopedBindings);
1852
2280
  if (action) {
1853
2281
  nextOpen.push({
1854
2282
  ...prefix,
@@ -1859,12 +2287,12 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1859
2287
  }
1860
2288
  if (ts.isIfStatement(statement)) {
1861
2289
  const cond = statement.expression.getText();
1862
- const thenBranches = statementBranches(ts.isBlock(statement.thenStatement) ? statement.thenStatement.statements : [statement.thenStatement], bindings, enqName, [{
2290
+ const thenBranches = statementBranches(ts.isBlock(statement.thenStatement) ? statement.thenStatement.statements : [statement.thenStatement], scopedBindings, enqName, [{
1863
2291
  ...prefix,
1864
2292
  conds: [...prefix.conds, { text: cond }]
1865
2293
  }]);
1866
2294
  const elseStatements = statement.elseStatement ? ts.isBlock(statement.elseStatement) ? statement.elseStatement.statements : [statement.elseStatement] : [];
1867
- const elseBranches = elseStatements.length > 0 ? statementBranches(elseStatements, bindings, enqName, [{
2295
+ const elseBranches = elseStatements.length > 0 ? statementBranches(elseStatements, scopedBindings, enqName, [{
1868
2296
  ...prefix,
1869
2297
  conds: [...prefix.conds, {
1870
2298
  text: cond,
@@ -1887,7 +2315,7 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1887
2315
  for (const clause of statement.caseBlock.clauses) {
1888
2316
  const ownCond = ts.isCaseClause(clause) ? { text: `${expr} === ${clause.expression.getText()}` } : null;
1889
2317
  const conds = ownCond ? [...prior, ownCond] : prior.length ? [...prior] : [];
1890
- const branches = statementBranches(clause.statements, bindings, enqName, [{
2318
+ const branches = statementBranches(clause.statements, scopedBindings, enqName, [{
1891
2319
  ...prefix,
1892
2320
  conds: [...prefix.conds, ...conds]
1893
2321
  }]);
@@ -1913,18 +2341,24 @@ function enqueueParamName(fn) {
1913
2341
  const param = fn.parameters[1];
1914
2342
  return param && ts.isIdentifier(param.name) ? param.name.text : "enq";
1915
2343
  }
2344
+ function guardParams(fn) {
2345
+ const param = fn.parameters[0];
2346
+ if (!param) return "()";
2347
+ return `(${param.getText()})`;
2348
+ }
1916
2349
  function normalizeFunctionTransition(fn, bindings, depth) {
2350
+ const preservedActions = fn.body && ts.isBlock(fn.body) && fn.body.statements.some((statement) => ts.isExpressionStatement(statement)) ? [enqueueAction(fn)] : [];
1917
2351
  const transitions = (ts.isArrowFunction(fn) && !ts.isBlock(fn.body) ? expressionBranches(fn.body, bindings, depth + 1) : fn.body && ts.isBlock(fn.body) ? statementBranches(fn.body.statements, bindings, enqueueParamName(fn)) : []).filter((branch) => !branch.unknown).map((branch) => {
1918
- const transition = transitionFromValue(branch.result, branch.actions);
2352
+ const transition = transitionFromValue(branch.result, branch.actions.length ? preservedActions : []);
1919
2353
  if (!transition) return null;
1920
2354
  if (typeof transition === "string") {
1921
- const guard = guardFromConditions(branch.conds);
2355
+ const guard = guardFromConditions(branch.conds, guardParams(fn));
1922
2356
  return guard ? {
1923
2357
  target: transition,
1924
2358
  guard
1925
2359
  } : transition;
1926
2360
  }
1927
- const guard = guardFromConditions(branch.conds);
2361
+ const guard = guardFromConditions(branch.conds, guardParams(fn));
1928
2362
  return guard ? {
1929
2363
  ...transition,
1930
2364
  guard
@@ -1932,7 +2366,7 @@ function normalizeFunctionTransition(fn, bindings, depth) {
1932
2366
  }).filter((transition) => Boolean(transition));
1933
2367
  if (transitions.length === 1) return transitions[0];
1934
2368
  if (transitions.length > 1) return transitions;
1935
- return fallbackFunctionTransition(fn);
2369
+ return fallbackFunctionTransition(fn, preservedActions);
1936
2370
  }
1937
2371
  function inlineAction(fn) {
1938
2372
  return {
@@ -1943,12 +2377,24 @@ function inlineAction(fn) {
1943
2377
  }
1944
2378
  };
1945
2379
  }
1946
- function normalizeFunctionAction(fn, bindings) {
1947
- if (ts.isArrowFunction(fn) && !ts.isBlock(fn.body)) return inlineAction(fn);
1948
- if (!fn.body || !ts.isBlock(fn.body)) return inlineAction(fn);
1949
- const branches = statementBranches(fn.body.statements, bindings, enqueueParamName(fn));
1950
- if (branches.length === 1 && !branches[0].unknown && branches[0].conds.length === 0 && branches[0].actions.length > 0 && !("result" in branches[0])) return branches[0].actions;
1951
- return inlineAction(fn);
2380
+ function enqueueAction(fn) {
2381
+ return {
2382
+ type: "xstate.enqueue",
2383
+ params: {
2384
+ code: fn.getText(),
2385
+ lang: "ts"
2386
+ }
2387
+ };
2388
+ }
2389
+ function inlineCodeExpression(expr) {
2390
+ return {
2391
+ "@type": "code",
2392
+ lang: "ts",
2393
+ expr
2394
+ };
2395
+ }
2396
+ function normalizeFunctionAction(fn, _bindings) {
2397
+ return enqueueAction(fn);
1952
2398
  }
1953
2399
  function objectLiteralToValue(node, bindings, context, depth, overrides = /* @__PURE__ */ new Map()) {
1954
2400
  const result = {};
@@ -1995,15 +2441,22 @@ function objectLiteralToValue(node, bindings, context, depth, overrides = /* @__
1995
2441
  function expressionToValue(expression, bindings, context = {}, depth = 0) {
1996
2442
  if (depth > 30) return void 0;
1997
2443
  const node = unwrapExpression$2(expression);
1998
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
1999
- if (ts.isNumericLiteral(node)) return Number(node.text);
2000
- if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
2001
- if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
2002
- if (node.kind === ts.SyntaxKind.NullKeyword) return null;
2444
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return context.ioValue ? inlineCodeExpression(JSON.stringify(node.text)) : node.text;
2445
+ if (ts.isNumericLiteral(node)) return context.ioValue ? inlineCodeExpression(node.text) : Number(node.text);
2446
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return context.ioValue ? inlineCodeExpression("true") : true;
2447
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return context.ioValue ? inlineCodeExpression("false") : false;
2448
+ if (node.kind === ts.SyntaxKind.NullKeyword) return context.ioValue ? inlineCodeExpression("null") : null;
2449
+ if (context.assignmentValue) {
2450
+ if (ts.isIdentifier(node) && node.text === "undefined") return void 0;
2451
+ return wrapAssignExpression(node.getText());
2452
+ }
2453
+ if (context.transitionContext && !ts.isObjectLiteralExpression(node)) return inlineCodeExpression(node.getText());
2454
+ if (context.ioValue && !ts.isObjectLiteralExpression(node)) return inlineCodeExpression(node.getText());
2455
+ if (context.transitionContext && ts.isObjectLiteralExpression(node) && hasSpreadAssignment(node)) return inlineCodeExpression(node.getText());
2003
2456
  if (ts.isIdentifier(node)) {
2004
2457
  if (node.text === "undefined") return void 0;
2005
2458
  const binding = bindings.get(node.text);
2006
- return binding ? expressionToValue(binding, bindings, context, depth + 1) : node.text;
2459
+ return binding ? expressionToValue(binding, bindings, context, depth + 1) : context.metaValue ? inlineCodeExpression(node.getText()) : node.text;
2007
2460
  }
2008
2461
  if (ts.isObjectLiteralExpression(node)) return objectLiteralToValue(node, bindings, context, depth);
2009
2462
  if (ts.isArrayLiteralExpression(node)) {
@@ -2011,17 +2464,19 @@ function expressionToValue(expression, bindings, context = {}, depth = 0) {
2011
2464
  return context.actionValue ? values.flat() : values;
2012
2465
  }
2013
2466
  if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
2467
+ if (context.metaValue) return inlineCodeExpression(node.getText());
2014
2468
  if (context.v6 && context.transitionValue) return normalizeFunctionTransition(node, bindings, depth);
2015
2469
  if (context.v6 && context.actionValue) return normalizeFunctionAction(node, bindings);
2016
2470
  return node.getText();
2017
2471
  }
2018
2472
  if (ts.isCallExpression(node)) {
2473
+ if (context.metaValue) return inlineCodeExpression(node.getText());
2019
2474
  if (context.actionValue) return inlineAction(node);
2020
2475
  const callee = node.expression.getText();
2021
2476
  const type = callee.includes(".") ? callee.split(".").pop() : callee;
2022
2477
  return { type: type === "assign" ? "xstate.assign" : type };
2023
2478
  }
2024
- return node.getText();
2479
+ return context.metaValue ? inlineCodeExpression(node.getText()) : node.getText();
2025
2480
  }
2026
2481
  function collectBindings$1(file) {
2027
2482
  const bindings = /* @__PURE__ */ new Map();
@@ -2048,7 +2503,7 @@ function hasV6Syntax(source) {
2048
2503
  found = true;
2049
2504
  return;
2050
2505
  }
2051
- if ((key === "on" || key === "after" || key === "always" || key === "entry" || key === "exit" || key === "actions" && !ts.isObjectLiteralExpression(node.initializer)) && node.initializer.getText().includes("=>")) {
2506
+ if ((key === "on" || key === "after" || key === "always" || key === "choice" || key === "onDone" || key === "onError" || key === "onSnapshot" || key === "onTimeout" || key === "entry" || key === "exit" || key === "actions" && !ts.isObjectLiteralExpression(node.initializer)) && node.initializer.getText().includes("=>")) {
2052
2507
  found = true;
2053
2508
  return;
2054
2509
  }
@@ -2402,6 +2857,7 @@ const validationIssueCodeSchema = _enum([
2402
2857
  "transition_never_taken",
2403
2858
  "missing_guard",
2404
2859
  "redundant_transition",
2860
+ "eventless_cycle",
2405
2861
  "missing_delay_implementation",
2406
2862
  "invalid_final_transition",
2407
2863
  "invalid_history_transition",
@@ -2698,6 +3154,51 @@ function getOnDoneWarnings(graph, reachableNodes) {
2698
3154
  }
2699
3155
  return results;
2700
3156
  }
3157
+ function getEventlessCycleErrors(graph) {
3158
+ const eventlessEdges = graph.edges.filter((edge) => edge.data.eventType === "" && !edge.data.guard);
3159
+ const bySource = /* @__PURE__ */ new Map();
3160
+ for (const edge of eventlessEdges) {
3161
+ const edges = bySource.get(edge.sourceId) ?? [];
3162
+ edges.push(edge);
3163
+ bySource.set(edge.sourceId, edges);
3164
+ }
3165
+ const results = [];
3166
+ const reported = /* @__PURE__ */ new Set();
3167
+ for (const startEdge of eventlessEdges) {
3168
+ const stack = [{
3169
+ nodeId: startEdge.targetId,
3170
+ path: [startEdge]
3171
+ }];
3172
+ const seen = /* @__PURE__ */ new Set();
3173
+ while (stack.length > 0) {
3174
+ const current = stack.pop();
3175
+ if (current.nodeId === startEdge.sourceId) {
3176
+ const edgeIds = current.path.map((edge) => edge.id);
3177
+ const key = [...edgeIds].sort().join("|");
3178
+ if (!reported.has(key)) {
3179
+ reported.add(key);
3180
+ results.push(issue({
3181
+ code: "eventless_cycle",
3182
+ kind: "correctness",
3183
+ level: "error",
3184
+ edgeIds,
3185
+ nodeIds: Array.from(new Set(current.path.flatMap((edge) => [edge.sourceId, edge.targetId]))),
3186
+ message: "Unguarded eventless transitions form an infinite transition loop",
3187
+ metadata: { edgeIds }
3188
+ }));
3189
+ }
3190
+ continue;
3191
+ }
3192
+ if (seen.has(current.nodeId)) continue;
3193
+ seen.add(current.nodeId);
3194
+ for (const nextEdge of bySource.get(current.nodeId) ?? []) stack.push({
3195
+ nodeId: nextEdge.targetId,
3196
+ path: [...current.path, nextEdge]
3197
+ });
3198
+ }
3199
+ }
3200
+ return results;
3201
+ }
2701
3202
  function getEdgeErrors(graph, edge) {
2702
3203
  const errors = [];
2703
3204
  const duplicates = findDuplicateEdges(graph, edge);
@@ -2929,6 +3430,7 @@ function validateGraph(graph, sourceAllowed) {
2929
3430
  pushMessages(results, getEdgeWarnings(nonTempGraph, edge), "warning", [], [edge.id]);
2930
3431
  }
2931
3432
  results.push(...getOnDoneWarnings(nonTempGraph, reachableNodes));
3433
+ results.push(...getEventlessCycleErrors(nonTempGraph));
2932
3434
  results.push(...getSourceWarnings(nonTempGraph));
2933
3435
  results.push(...getSourceRestrictionErrors(nonTempGraph, sourceAllowed));
2934
3436
  return results;