@statelyai/sdk 0.11.0 → 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-DV62vO_d.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,
@@ -868,6 +1205,10 @@ function formatObjectKey(key) {
868
1205
  const eventParamSchema = union([string(), object({ type: string() }).catchall(unknown())]);
869
1206
  const delayParamSchema = union([number(), string()]);
870
1207
  const builtInActionParamSchemas = {
1208
+ "xstate.enqueue": object({
1209
+ code: string().optional(),
1210
+ lang: string().optional()
1211
+ }),
871
1212
  "xstate.raise": object({
872
1213
  event: eventParamSchema.optional(),
873
1214
  id: string().optional(),
@@ -891,6 +1232,16 @@ const builtInActionParamSchemas = {
891
1232
  "xstate.assign": object({ assignment: record(string(), unknown()).optional() }).catchall(unknown())
892
1233
  };
893
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
+ },
894
1245
  "xstate.raise": {
895
1246
  package: "xstate",
896
1247
  version: "^5",
@@ -1033,6 +1384,7 @@ function serializeGuard(guard, asTypeRef) {
1033
1384
  */
1034
1385
  function serializeActionItem(action) {
1035
1386
  const { type, params } = action;
1387
+ if (type === "xstate.enqueue" && typeof params?.code === "string") return inlineCodeExpression$1(params.code);
1036
1388
  const exprCode = type === "xstate.expr" && typeof params?.code === "string" ? params.code : void 0;
1037
1389
  if (isBuiltInActionType(type)) {
1038
1390
  const builtInAction = XSTATE_BUILT_IN_ACTIONS[type];
@@ -1065,7 +1417,8 @@ function graphToMachineConfig(graph, options = {}) {
1065
1417
  const nodeType = [
1066
1418
  "final",
1067
1419
  "history",
1068
- "parallel"
1420
+ "parallel",
1421
+ "choice"
1069
1422
  ].includes(node.data.type) ? node.data.type : void 0;
1070
1423
  const tags = node.data.tags;
1071
1424
  Object.assign(nodeConfig, {
@@ -1080,6 +1433,7 @@ function graphToMachineConfig(graph, options = {}) {
1080
1433
  ...inv.input ? { input: inv.input } : void 0,
1081
1434
  ...inv.output ? { output: inv.output } : void 0
1082
1435
  })) } : void 0,
1436
+ ...node.data.output !== void 0 ? { output: node.data.output } : void 0,
1083
1437
  ...tags?.length ? { tags: tags.map(tagToString) } : void 0,
1084
1438
  ...showDescriptions && node.data.description ? { description: node.data.description } : void 0,
1085
1439
  ...showMeta && node.data.meta && Object.keys(node.data.meta).length > 0 ? { meta: node.data.meta } : void 0,
@@ -1091,6 +1445,7 @@ function graphToMachineConfig(graph, options = {}) {
1091
1445
  const on = {};
1092
1446
  const after = {};
1093
1447
  const alwaysArr = [];
1448
+ const choiceArr = [];
1094
1449
  const onDone = {};
1095
1450
  const invokeMap = {};
1096
1451
  for (const edge of edges) {
@@ -1102,6 +1457,7 @@ function graphToMachineConfig(graph, options = {}) {
1102
1457
  target: edge.data.transitionType === "targetless" ? void 0 : `${resolvedTarget}`,
1103
1458
  ...edge.data.transitionType === "reenter" ? { reenter: true } : void 0,
1104
1459
  guard: edge.data.guard ? serializeGuard(edge.data.guard, inlineGuardsAsTypeRef) : void 0,
1460
+ ...edge.data.context !== void 0 ? { context: edge.data.context } : void 0,
1105
1461
  actions: edge.data.actions?.length ? edge.data.actions.map(serializeActionItem) : void 0,
1106
1462
  description: edge.data.description ?? void 0,
1107
1463
  ...transitionMeta ? { meta: transitionMeta } : void 0
@@ -1124,6 +1480,7 @@ function graphToMachineConfig(graph, options = {}) {
1124
1480
  if (!onDone[type]) onDone[type] = [];
1125
1481
  onDone[type].push(transitionObject);
1126
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);
1127
1484
  else if (type === "") alwaysArr.push(transitionObject);
1128
1485
  else if (type.startsWith("xstate.after.")) {
1129
1486
  const delayKey = type.slice(13).split(".")[0];
@@ -1143,6 +1500,7 @@ function graphToMachineConfig(graph, options = {}) {
1143
1500
  if (Object.keys(on).length > 0) nodeConfig.on = singleOrArrayRecord(on);
1144
1501
  if (Object.keys(after).length > 0) nodeConfig.after = singleOrArrayRecord(after);
1145
1502
  if (alwaysArr.length > 0) nodeConfig.always = singleOrArray(alwaysArr);
1503
+ if (choiceArr.length > 0) nodeConfig.choice = singleOrArray(choiceArr);
1146
1504
  if (Object.keys(onDone).length > 0) nodeConfig.onDone = singleOrArray(Object.values(onDone).flat());
1147
1505
  if (Object.keys(invokeMap).length > 0 && Array.isArray(nodeConfig.invoke)) nodeConfig.invoke = nodeConfig.invoke.map((inv, index) => {
1148
1506
  const mapped = invokeMap[node.data.invokes?.[index]?.id ?? inv.id];
@@ -1521,6 +1879,7 @@ function guardObject(value) {
1521
1879
  const guard = value;
1522
1880
  return {
1523
1881
  type: guard.type,
1882
+ code: guard.code,
1524
1883
  params: guard.params
1525
1884
  };
1526
1885
  }
@@ -1551,9 +1910,10 @@ function machineConfigToGraph(config) {
1551
1910
  parentId,
1552
1911
  data: {
1553
1912
  key,
1554
- 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,
1555
1914
  initialId,
1556
1915
  history: state.history ?? false,
1916
+ ...state.output !== void 0 ? { output: state.output } : {},
1557
1917
  entry: actionItems(state.entry),
1558
1918
  exit: actionItems(state.exit),
1559
1919
  invokes: invokeItems(state.invoke),
@@ -1572,6 +1932,7 @@ function machineConfigToGraph(config) {
1572
1932
  eventType,
1573
1933
  transitionType: target ? transition.reenter ? "reenter" : "normal" : "targetless",
1574
1934
  guard: guardObject(transition.guard),
1935
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1575
1936
  actions: actionItems(transition.actions),
1576
1937
  description: typeof transition.description === "string" ? transition.description : null,
1577
1938
  meta: transition.meta && typeof transition.meta === "object" ? transition.meta : null
@@ -1588,6 +1949,7 @@ function machineConfigToGraph(config) {
1588
1949
  eventType: `xstate.after.${delay}.${id}`,
1589
1950
  transitionType: target ? "normal" : "targetless",
1590
1951
  guard: guardObject(transition.guard),
1952
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1591
1953
  actions: actionItems(transition.actions),
1592
1954
  description: typeof transition.description === "string" ? transition.description : null,
1593
1955
  meta: null
@@ -1604,12 +1966,30 @@ function machineConfigToGraph(config) {
1604
1966
  eventType: "",
1605
1967
  transitionType: target ? "normal" : "targetless",
1606
1968
  guard: guardObject(transition.guard),
1969
+ ...transition.context !== void 0 ? { context: transition.context } : {},
1607
1970
  actions: actionItems(transition.actions),
1608
1971
  description: typeof transition.description === "string" ? transition.description : null,
1609
1972
  meta: null
1610
1973
  }
1611
1974
  });
1612
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
+ }
1613
1993
  for (const [childKey, child] of Object.entries(state.states ?? {})) visit(child, childKey, id);
1614
1994
  }
1615
1995
  visit(config, rootKey, null);
@@ -1637,6 +2017,15 @@ function propertyContext(parent, key) {
1637
2017
  v6: parent.v6,
1638
2018
  actionValue: true
1639
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
+ };
1640
2029
  if (key === "meta") return {
1641
2030
  v6: parent.v6,
1642
2031
  metaValue: true
@@ -1649,21 +2038,28 @@ function propertyContext(parent, key) {
1649
2038
  v6: parent.v6,
1650
2039
  actionValue: true
1651
2040
  };
2041
+ if (key === "input" || key === "output") return {
2042
+ v6: parent.v6,
2043
+ ioValue: true
2044
+ };
1652
2045
  if (key === "on" || key === "after") return {
1653
2046
  v6: parent.v6,
1654
2047
  transitionMap: true
1655
2048
  };
1656
- 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 {
1657
2050
  v6: parent.v6,
1658
2051
  transitionValue: true
1659
2052
  };
1660
2053
  return { v6: parent.v6 };
1661
2054
  }
1662
- function fallbackFunctionTransition(fn) {
1663
- return { meta: {
1664
- [XSTATE_V6_FN_TRANSITION_META]: true,
1665
- [XSTATE_V6_FN_SOURCE_META]: fn.getText()
1666
- } };
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
+ };
1667
2063
  }
1668
2064
  function conditionText(cond) {
1669
2065
  return cond.negated ? `!(${cond.text})` : cond.text;
@@ -1678,10 +2074,28 @@ function simplifyConditions(conds) {
1678
2074
  return true;
1679
2075
  });
1680
2076
  }
1681
- 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) {
1682
2086
  const simplified = simplifyConditions(conds);
1683
2087
  if (simplified.length === 0) return void 0;
1684
- 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));
1685
2099
  }
1686
2100
  function mergeActions(left, right) {
1687
2101
  return [...left ?? [], ...right];
@@ -1696,12 +2110,6 @@ function transitionFromValue(value, actions) {
1696
2110
  }
1697
2111
  if (!value || typeof value !== "object" || Array.isArray(value)) return actions.length ? { actions } : null;
1698
2112
  const record = { ...value };
1699
- const context = record.context;
1700
- delete record.context;
1701
- if (context !== void 0) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, [{
1702
- type: "xstate.assign",
1703
- params: { assignment: context }
1704
- }]);
1705
2113
  if (actions.length) record.actions = mergeActions(Array.isArray(record.actions) ? record.actions : record.actions ? [record.actions] : void 0, actions);
1706
2114
  return record;
1707
2115
  }
@@ -1749,13 +2157,19 @@ function expressionBranches(expression, bindings, depth) {
1749
2157
  const target = targetProperty ? unwrapExpression$2(targetProperty.initializer) : null;
1750
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) => ({
1751
2159
  ...branch,
1752
- 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]]))
1753
2164
  }));
1754
2165
  }
1755
2166
  return [{
1756
2167
  conds: [],
1757
2168
  actions: [],
1758
- result: expressionToValue(node, bindings, {}, depth + 1)
2169
+ result: expressionToValue(node, bindings, {
2170
+ v6: true,
2171
+ transitionValue: true
2172
+ }, depth + 1)
1759
2173
  }];
1760
2174
  }
1761
2175
  function mergeBranchPrefix(prefix, branch) {
@@ -1841,9 +2255,15 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1841
2255
  }]) {
1842
2256
  let open = prefixes;
1843
2257
  const done = [];
2258
+ const scopedBindings = new Map(bindings);
1844
2259
  for (const statement of statements) {
1845
2260
  const nextOpen = [];
1846
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
+ }
1847
2267
  if (ts.isReturnStatement(statement)) {
1848
2268
  if (!statement.expression) {
1849
2269
  done.push({
@@ -1852,11 +2272,11 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1852
2272
  });
1853
2273
  continue;
1854
2274
  }
1855
- 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));
1856
2276
  continue;
1857
2277
  }
1858
2278
  if (ts.isExpressionStatement(statement)) {
1859
- const action = expressionStatementAction(statement, enqName, bindings);
2279
+ const action = expressionStatementAction(statement, enqName, scopedBindings);
1860
2280
  if (action) {
1861
2281
  nextOpen.push({
1862
2282
  ...prefix,
@@ -1867,12 +2287,12 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1867
2287
  }
1868
2288
  if (ts.isIfStatement(statement)) {
1869
2289
  const cond = statement.expression.getText();
1870
- 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, [{
1871
2291
  ...prefix,
1872
2292
  conds: [...prefix.conds, { text: cond }]
1873
2293
  }]);
1874
2294
  const elseStatements = statement.elseStatement ? ts.isBlock(statement.elseStatement) ? statement.elseStatement.statements : [statement.elseStatement] : [];
1875
- const elseBranches = elseStatements.length > 0 ? statementBranches(elseStatements, bindings, enqName, [{
2295
+ const elseBranches = elseStatements.length > 0 ? statementBranches(elseStatements, scopedBindings, enqName, [{
1876
2296
  ...prefix,
1877
2297
  conds: [...prefix.conds, {
1878
2298
  text: cond,
@@ -1895,7 +2315,7 @@ function statementBranches(statements, bindings, enqName, prefixes = [{
1895
2315
  for (const clause of statement.caseBlock.clauses) {
1896
2316
  const ownCond = ts.isCaseClause(clause) ? { text: `${expr} === ${clause.expression.getText()}` } : null;
1897
2317
  const conds = ownCond ? [...prior, ownCond] : prior.length ? [...prior] : [];
1898
- const branches = statementBranches(clause.statements, bindings, enqName, [{
2318
+ const branches = statementBranches(clause.statements, scopedBindings, enqName, [{
1899
2319
  ...prefix,
1900
2320
  conds: [...prefix.conds, ...conds]
1901
2321
  }]);
@@ -1921,18 +2341,24 @@ function enqueueParamName(fn) {
1921
2341
  const param = fn.parameters[1];
1922
2342
  return param && ts.isIdentifier(param.name) ? param.name.text : "enq";
1923
2343
  }
2344
+ function guardParams(fn) {
2345
+ const param = fn.parameters[0];
2346
+ if (!param) return "()";
2347
+ return `(${param.getText()})`;
2348
+ }
1924
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)] : [];
1925
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) => {
1926
- const transition = transitionFromValue(branch.result, branch.actions);
2352
+ const transition = transitionFromValue(branch.result, branch.actions.length ? preservedActions : []);
1927
2353
  if (!transition) return null;
1928
2354
  if (typeof transition === "string") {
1929
- const guard = guardFromConditions(branch.conds);
2355
+ const guard = guardFromConditions(branch.conds, guardParams(fn));
1930
2356
  return guard ? {
1931
2357
  target: transition,
1932
2358
  guard
1933
2359
  } : transition;
1934
2360
  }
1935
- const guard = guardFromConditions(branch.conds);
2361
+ const guard = guardFromConditions(branch.conds, guardParams(fn));
1936
2362
  return guard ? {
1937
2363
  ...transition,
1938
2364
  guard
@@ -1940,7 +2366,7 @@ function normalizeFunctionTransition(fn, bindings, depth) {
1940
2366
  }).filter((transition) => Boolean(transition));
1941
2367
  if (transitions.length === 1) return transitions[0];
1942
2368
  if (transitions.length > 1) return transitions;
1943
- return fallbackFunctionTransition(fn);
2369
+ return fallbackFunctionTransition(fn, preservedActions);
1944
2370
  }
1945
2371
  function inlineAction(fn) {
1946
2372
  return {
@@ -1951,6 +2377,15 @@ function inlineAction(fn) {
1951
2377
  }
1952
2378
  };
1953
2379
  }
2380
+ function enqueueAction(fn) {
2381
+ return {
2382
+ type: "xstate.enqueue",
2383
+ params: {
2384
+ code: fn.getText(),
2385
+ lang: "ts"
2386
+ }
2387
+ };
2388
+ }
1954
2389
  function inlineCodeExpression(expr) {
1955
2390
  return {
1956
2391
  "@type": "code",
@@ -1958,12 +2393,8 @@ function inlineCodeExpression(expr) {
1958
2393
  expr
1959
2394
  };
1960
2395
  }
1961
- function normalizeFunctionAction(fn, bindings) {
1962
- if (ts.isArrowFunction(fn) && !ts.isBlock(fn.body)) return inlineAction(fn);
1963
- if (!fn.body || !ts.isBlock(fn.body)) return inlineAction(fn);
1964
- const branches = statementBranches(fn.body.statements, bindings, enqueueParamName(fn));
1965
- if (branches.length === 1 && !branches[0].unknown && branches[0].conds.length === 0 && branches[0].actions.length > 0 && !("result" in branches[0])) return branches[0].actions;
1966
- return inlineAction(fn);
2396
+ function normalizeFunctionAction(fn, _bindings) {
2397
+ return enqueueAction(fn);
1967
2398
  }
1968
2399
  function objectLiteralToValue(node, bindings, context, depth, overrides = /* @__PURE__ */ new Map()) {
1969
2400
  const result = {};
@@ -2010,11 +2441,18 @@ function objectLiteralToValue(node, bindings, context, depth, overrides = /* @__
2010
2441
  function expressionToValue(expression, bindings, context = {}, depth = 0) {
2011
2442
  if (depth > 30) return void 0;
2012
2443
  const node = unwrapExpression$2(expression);
2013
- if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
2014
- if (ts.isNumericLiteral(node)) return Number(node.text);
2015
- if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
2016
- if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
2017
- if (node.kind === ts.SyntaxKind.NullKeyword) return null;
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());
2018
2456
  if (ts.isIdentifier(node)) {
2019
2457
  if (node.text === "undefined") return void 0;
2020
2458
  const binding = bindings.get(node.text);
@@ -2065,7 +2503,7 @@ function hasV6Syntax(source) {
2065
2503
  found = true;
2066
2504
  return;
2067
2505
  }
2068
- 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("=>")) {
2069
2507
  found = true;
2070
2508
  return;
2071
2509
  }
@@ -2419,6 +2857,7 @@ const validationIssueCodeSchema = _enum([
2419
2857
  "transition_never_taken",
2420
2858
  "missing_guard",
2421
2859
  "redundant_transition",
2860
+ "eventless_cycle",
2422
2861
  "missing_delay_implementation",
2423
2862
  "invalid_final_transition",
2424
2863
  "invalid_history_transition",
@@ -2715,6 +3154,51 @@ function getOnDoneWarnings(graph, reachableNodes) {
2715
3154
  }
2716
3155
  return results;
2717
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
+ }
2718
3202
  function getEdgeErrors(graph, edge) {
2719
3203
  const errors = [];
2720
3204
  const duplicates = findDuplicateEdges(graph, edge);
@@ -2946,6 +3430,7 @@ function validateGraph(graph, sourceAllowed) {
2946
3430
  pushMessages(results, getEdgeWarnings(nonTempGraph, edge), "warning", [], [edge.id]);
2947
3431
  }
2948
3432
  results.push(...getOnDoneWarnings(nonTempGraph, reachableNodes));
3433
+ results.push(...getEventlessCycleErrors(nonTempGraph));
2949
3434
  results.push(...getSourceWarnings(nonTempGraph));
2950
3435
  results.push(...getSourceRestrictionErrors(nonTempGraph, sourceAllowed));
2951
3436
  return results;