statelyai 0.12.8 → 0.14.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.
@@ -2,30 +2,193 @@ import { Args, Command, Flags, flush, handle, run } from "@oclif/core";
2
2
  import fsPromises from "node:fs/promises";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
5
+ import { changeSet, createFileStore, createLoop, parseModelGraph, proposalOutline, verdictLines, verify } from "@statelyai/mcp/loop";
5
6
  import { z, z as z$1 } from "zod";
6
7
  import { execFile, execFileSync, spawn } from "node:child_process";
7
8
  import { promisify } from "node:util";
8
9
  import * as crypto from "node:crypto";
10
+ import { randomUUID } from "node:crypto";
11
+ import { StudioApiError, buildAuthorizeUrl, createPkcePair, createStatelyClient, discoverAuthorizationServer, discoverProtectedResource, exchangeCodeForToken, refreshToken, registerOAuthClient } from "@statelyai/sdk";
12
+ import * as ts$1 from "typescript";
13
+ import ts from "typescript";
9
14
  import fs, { promises, statSync, watch } from "node:fs";
10
15
  import { createInterface } from "node:readline/promises";
11
16
  import { Writable } from "node:stream";
12
17
  import { fileURLToPath, pathToFileURL } from "node:url";
13
18
  import * as http from "node:http";
14
- import * as ts$1 from "typescript";
15
- import ts from "typescript";
16
19
  import * as fs$1 from "fs";
17
20
  import * as path$2 from "path";
18
21
  import { fileURLToPath as fileURLToPath$1, pathToFileURL as pathToFileURL$1 } from "url";
19
22
  import { fromStudioMachine, toStudioMachine } from "@statelyai/sdk/graph";
20
23
  import { EDITOR_PROTOCOL_VERSION } from "@statelyai/sdk/editor-host-protocol";
21
- import { createStatelyClient, createStatelyClient as createStatelyClient$1$1 } from "@statelyai/sdk/studio";
24
+ import { createStatelyClient as createStatelyClient$1, createStatelyClient as createStatelyClient$1$1 } from "@statelyai/sdk/studio";
22
25
  import { WebSocket, WebSocketServer } from "ws";
23
26
  import os from "node:os";
24
- import { StudioApiError, buildAuthorizeUrl, createPkcePair, createStatelyClient as createStatelyClient$1, discoverAuthorizationServer, discoverProtectedResource, exchangeCodeForToken, refreshToken, registerOAuthClient } from "@statelyai/sdk";
25
27
 
26
28
  //#region \0rolldown/runtime.js
27
29
  var __commonJSMin$1 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
28
30
 
31
+ //#endregion
32
+ //#region src/loopCommands.ts
33
+ /** An XGraph file, or a `.statelyai` head or version file (its `graph`). */
34
+ async function readGraphFile(file) {
35
+ const value = JSON.parse(await fsPromises.readFile(file, "utf8"));
36
+ return parseModelGraph(Array.isArray(value.nodes) ? value : value.graph);
37
+ }
38
+ /** `statelyai changes`: change lines and the proposed flow outline, base -> proposed. */
39
+ async function changesReport(baseFile, proposedFile, json = false) {
40
+ const [base, proposed] = await Promise.all([readGraphFile(baseFile), readGraphFile(proposedFile)]);
41
+ const { comparison, changes } = await changeSet(base, proposed);
42
+ if (json) return {
43
+ text: JSON.stringify({
44
+ changes,
45
+ comparison
46
+ }, null, 2),
47
+ exitCode: 0
48
+ };
49
+ return {
50
+ text: [
51
+ ...changes.length ? changes.map((line) => `- ${line}`) : ["No behavior changes."],
52
+ "",
53
+ proposalOutline({
54
+ name: path.basename(proposedFile),
55
+ proposed,
56
+ comparison,
57
+ changes
58
+ })
59
+ ].join("\n"),
60
+ exitCode: 0
61
+ };
62
+ }
63
+ /** `statelyai verify <proposal> <regenerated>`: behavior verdict; 1 on drift, 2 when unavailable. */
64
+ async function verifyReport(proposalFile, regeneratedFile, json = false) {
65
+ const [proposal, regenerated] = await Promise.all([readGraphFile(proposalFile), readGraphFile(regeneratedFile)]);
66
+ const verdict = await verify(path.basename(proposalFile), proposal, regenerated);
67
+ return {
68
+ text: json ? JSON.stringify(verdict, null, 2) : verdictLines(verdict).join("\n"),
69
+ exitCode: verdict.status === "matches" ? 0 : verdict.status === "drifted" ? 1 : 2
70
+ };
71
+ }
72
+ const localLoop = (root) => createLoop({ store: createFileStore(root) });
73
+ /** `statelyai brief`: the Apply brief for a pending proposal in `.statelyai/`. */
74
+ async function briefReport(root, machineId, versionId, json = false) {
75
+ const loop = localLoop(root);
76
+ const brief = await loop.brief(machineId, versionId);
77
+ if (json) return {
78
+ text: JSON.stringify(brief, null, 2),
79
+ exitCode: 0
80
+ };
81
+ const proposal = await loop.getProposal(machineId, versionId);
82
+ return {
83
+ text: [
84
+ proposalOutline({
85
+ name: `${brief.name} (${machineId}, ${versionId}, from ${brief.origin === "prompt" ? "a prompt" : "the editor"})`,
86
+ proposed: proposal.proposed,
87
+ comparison: proposal.comparison,
88
+ changes: proposal.changes,
89
+ evidence: proposal.evidence
90
+ }),
91
+ "",
92
+ brief.text
93
+ ].join("\n"),
94
+ exitCode: 0
95
+ };
96
+ }
97
+ /** `statelyai pending`: proposals in `.statelyai/` whose baseline is still the head. */
98
+ async function pendingReport(root, json = false) {
99
+ const { proposals } = await localLoop(root).listPendingProposals({});
100
+ if (json) return {
101
+ text: JSON.stringify({ proposals }, null, 2),
102
+ exitCode: 0
103
+ };
104
+ if (!proposals.length) return {
105
+ text: "No pending proposals in .statelyai/.",
106
+ exitCode: 0
107
+ };
108
+ return {
109
+ text: proposals.flatMap((proposal) => [
110
+ `- ${JSON.stringify(proposal.name)} for ${JSON.stringify(proposal.machineName)} (from ${proposal.origin === "prompt" ? "a prompt" : "the editor"})`,
111
+ ` statelyai brief ${proposal.machineId} --proposal ${proposal.versionId}`,
112
+ ...proposal.changes.map((line) => ` - ${line}`)
113
+ ]).join("\n"),
114
+ exitCode: 0
115
+ };
116
+ }
117
+ const jsonFlag = Flags.boolean({
118
+ description: "Print machine-readable JSON",
119
+ default: false
120
+ });
121
+ const rootFlag = Flags.string({ description: "Repository root containing .statelyai/ (defaults to the current directory)" });
122
+ var ReportCommand = class extends Command {
123
+ static enableJsonFlag = false;
124
+ async print(report) {
125
+ let result;
126
+ try {
127
+ result = await report;
128
+ } catch (error) {
129
+ this.error(error instanceof Error ? error.message : String(error), { exit: 2 });
130
+ }
131
+ this.log(result.text);
132
+ if (result.exitCode) this.exit(result.exitCode);
133
+ }
134
+ };
135
+ var ChangesCommand = class ChangesCommand extends ReportCommand {
136
+ static summary = "Print the behavior changes between two machine graphs.";
137
+ static description = "Compares XGraph JSON files (or .statelyai head and version files) offline, then prints change lines and an outline of the proposed flow.";
138
+ static args = {
139
+ base: Args.string({
140
+ required: true,
141
+ description: "Base XGraph JSON file"
142
+ }),
143
+ proposed: Args.string({
144
+ required: true,
145
+ description: "Proposed XGraph JSON file"
146
+ })
147
+ };
148
+ static flags = {
149
+ help: Flags.help({ char: "h" }),
150
+ json: jsonFlag
151
+ };
152
+ async run() {
153
+ const { args, flags } = await this.parse(ChangesCommand);
154
+ await this.print(changesReport(args.base, args.proposed, flags.json));
155
+ }
156
+ };
157
+ var BriefCommand = class BriefCommand extends ReportCommand {
158
+ static summary = "Print the implementation brief for a pending local proposal.";
159
+ static description = "Reads .statelyai/ and prints the proposal outline and the Apply message an agent implements. Fails when the model changed since the proposal.";
160
+ static args = { machine: Args.string({
161
+ required: true,
162
+ description: "Machine id (its .statelyai/machines file name)"
163
+ }) };
164
+ static flags = {
165
+ help: Flags.help({ char: "h" }),
166
+ proposal: Flags.string({
167
+ required: true,
168
+ description: "Proposal version id, for example v0002"
169
+ }),
170
+ root: rootFlag,
171
+ json: jsonFlag
172
+ };
173
+ async run() {
174
+ const { args, flags } = await this.parse(BriefCommand);
175
+ await this.print(briefReport(flags.root ?? process.cwd(), args.machine, flags.proposal, flags.json));
176
+ }
177
+ };
178
+ var PendingCommand = class PendingCommand extends ReportCommand {
179
+ static summary = "List pending proposals in .statelyai/.";
180
+ static description = "Lists proposals whose baseline still matches the machine head, newest first.";
181
+ static flags = {
182
+ help: Flags.help({ char: "h" }),
183
+ root: rootFlag,
184
+ json: jsonFlag
185
+ };
186
+ async run() {
187
+ const { flags } = await this.parse(PendingCommand);
188
+ await this.print(pendingReport(flags.root ?? process.cwd(), flags.json));
189
+ }
190
+ };
191
+
29
192
  //#endregion
30
193
  //#region ../api-contract/dist/verification.mjs
31
194
  const count = z.number().int().nonnegative();
@@ -679,7 +842,11 @@ async function verifyProject(options = {}) {
679
842
  var VerifyCommand = class VerifyCommand extends Command {
680
843
  static enableJsonFlag = false;
681
844
  static summary = "Validate a verification report, print its verdict, and export JSON or JUnit.";
682
- static description = "Saved-report processing works offline. Live execution requires a configured verification adapter.";
845
+ static description = "Saved-report processing works offline. Live execution requires a configured verification adapter. With two graph files, compares a proposal with its regeneration instead (exit 1 on drift).";
846
+ static args = {
847
+ proposal: Args.string({ description: "Proposal XGraph JSON file (or .statelyai version file) to compare" }),
848
+ regenerated: Args.string({ description: "Regenerated XGraph JSON file (or .statelyai head file)" })
849
+ };
683
850
  static flags = {
684
851
  model: Flags.string({ description: "Model file or registry machine ID (live execution)" }),
685
852
  bindings: Flags.string({ description: "Bindings file (live execution)" }),
@@ -698,7 +865,19 @@ var VerifyCommand = class VerifyCommand extends Command {
698
865
  })
699
866
  };
700
867
  async run() {
701
- const { flags } = await this.parse(VerifyCommand);
868
+ const { args, flags } = await this.parse(VerifyCommand);
869
+ if (args.proposal || args.regenerated) {
870
+ if (!args.proposal || !args.regenerated) this.error("Pass both the proposal and the regenerated graph files.", { exit: 2 });
871
+ let report;
872
+ try {
873
+ report = await verifyReport(args.proposal, args.regenerated);
874
+ } catch (error) {
875
+ this.error(error instanceof Error ? error.message : String(error), { exit: 2 });
876
+ }
877
+ this.log(report.text);
878
+ if (report.exitCode) this.exit(report.exitCode);
879
+ return;
880
+ }
702
881
  let result;
703
882
  try {
704
883
  result = await verifyProject({
@@ -719,6 +898,236 @@ var VerifyCommand = class VerifyCommand extends Command {
719
898
  }
720
899
  };
721
900
 
901
+ //#endregion
902
+ //#region ../editor-sync/dist/statelyPragma-D46lczJR.mjs
903
+ function findStatelyPragmaAttachments(sourceText, fileName = "machine.ts") {
904
+ const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$4(fileName));
905
+ const attachments = [];
906
+ const visit = (node) => {
907
+ if (ts.isCallExpression(node) && isCreateMachineExpression$3(node.expression)) {
908
+ const attachNode = findAttachNode(node);
909
+ if (attachNode) attachments.push({
910
+ machineStart: getMachineExpressionStart(node.expression, sourceFile),
911
+ attachStart: attachNode.getStart(sourceFile),
912
+ pragma: findAttachedStatelyPragma(sourceText, attachNode)
913
+ });
914
+ }
915
+ ts.forEachChild(node, visit);
916
+ };
917
+ visit(sourceFile);
918
+ return attachments;
919
+ }
920
+ function getStatelyPragma(sourceText, fileName = "machine.ts", machineIndex = 0) {
921
+ return findStatelyPragmaAttachments(sourceText, fileName)[machineIndex]?.pragma;
922
+ }
923
+ function upsertStatelyPragma(sourceText, id, options = {}) {
924
+ const attachment = findStatelyPragmaAttachments(sourceText, options.fileName ?? "machine.ts")[options.machineIndex ?? 0];
925
+ if (!attachment) return sourceText;
926
+ const lineEnding = options.lineEnding ?? detectLineEnding(sourceText);
927
+ const canonicalComment = `// @statelyai id=${id}`;
928
+ if (attachment.pragma) return sourceText.slice(0, attachment.pragma.start) + canonicalComment + sourceText.slice(attachment.pragma.end);
929
+ const insertText = `${getIndentationAtOffset(sourceText, attachment.attachStart)}${canonicalComment}${lineEnding}`;
930
+ return sourceText.slice(0, attachment.attachStart) + insertText + sourceText.slice(attachment.attachStart);
931
+ }
932
+ function findAttachedStatelyPragma(sourceText, node) {
933
+ const comments = ts.getLeadingCommentRanges(sourceText, node.getFullStart()) ?? [];
934
+ for (let index = comments.length - 1; index >= 0; index -= 1) {
935
+ const comment = comments[index];
936
+ const parsed = parseStatelyPragma(sourceText, comment.pos, comment.end);
937
+ if (parsed) return parsed;
938
+ }
939
+ }
940
+ function parseStatelyPragma(sourceText, start, end) {
941
+ const match = normalizeCommentContent(sourceText.slice(start, end)).match(/^@statelyai(?:\s+(.*))?$/s);
942
+ if (!match) return;
943
+ const fields = parseFields(match[1] ?? "");
944
+ return {
945
+ tag: "@statelyai",
946
+ id: fields.id,
947
+ fields,
948
+ start,
949
+ end
950
+ };
951
+ }
952
+ function normalizeCommentContent(rawComment) {
953
+ if (rawComment.startsWith("//")) return rawComment.slice(2).trim();
954
+ if (rawComment.startsWith("/*")) return rawComment.slice(2, rawComment.endsWith("*/") ? -2 : void 0).split(/\r?\n/).map((line) => line.replace(/^\s*\*\s?/, "")).join(" ").trim();
955
+ return rawComment.trim();
956
+ }
957
+ function parseFields(input) {
958
+ const fields = {};
959
+ for (const token of input.split(/\s+/)) {
960
+ if (!token) continue;
961
+ const equalsIndex = token.indexOf("=");
962
+ if (equalsIndex <= 0) continue;
963
+ const key = token.slice(0, equalsIndex);
964
+ const value = token.slice(equalsIndex + 1);
965
+ if (!key || !value) continue;
966
+ fields[key] = value;
967
+ }
968
+ return fields;
969
+ }
970
+ function findAttachNode(node) {
971
+ let current = node;
972
+ while (current?.parent) {
973
+ if (ts.isVariableStatement(current.parent) || ts.isExpressionStatement(current.parent) || ts.isExportAssignment(current.parent)) return current.parent;
974
+ current = current.parent;
975
+ }
976
+ return current;
977
+ }
978
+ function isCreateMachineExpression$3(expression) {
979
+ const isMachineFactoryName = (name) => name === "createMachine" || name === "createMachineFromConfig";
980
+ return ts.isIdentifier(expression) && isMachineFactoryName(expression.text) || ts.isPropertyAccessExpression(expression) && isMachineFactoryName(expression.name.text);
981
+ }
982
+ function getMachineExpressionStart(expression, sourceFile) {
983
+ if (ts.isPropertyAccessExpression(expression)) return expression.name.getStart(sourceFile);
984
+ return expression.getStart(sourceFile);
985
+ }
986
+ function getIndentationAtOffset(sourceText, offset) {
987
+ const lineStart = sourceText.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
988
+ return sourceText.slice(lineStart, offset).match(/^[ \t]*/)?.[0] ?? "";
989
+ }
990
+ function detectLineEnding(sourceText) {
991
+ return sourceText.includes("\r\n") ? "\r\n" : "\n";
992
+ }
993
+ function getScriptKind$4(fileName) {
994
+ if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
995
+ if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
996
+ if (fileName.endsWith(".js")) return ts.ScriptKind.JS;
997
+ return ts.ScriptKind.TS;
998
+ }
999
+
1000
+ //#endregion
1001
+ //#region src/link.ts
1002
+ function resourceId(locator, kind) {
1003
+ let id = locator.trim();
1004
+ if (/^https?:\/\//.test(id)) {
1005
+ const url = new URL(id);
1006
+ id = kind === "machine" ? url.searchParams.get("machineId") ?? url.pathname.match(/\/machines?\/([^/]+)/)?.[1] ?? "" : url.pathname.match(/\/(?:projects?|editor)\/([^/]+)/)?.[1] ?? "";
1007
+ id = decodeURIComponent(id);
1008
+ }
1009
+ if (!/^[\w-]+$/.test(id)) throw new Error(`Pass a ${kind} ID or a URL containing a ${kind} ID.`);
1010
+ return id;
1011
+ }
1012
+ /** Association only: no definitions are uploaded, downloaded, or executed. */
1013
+ async function linkProject(options) {
1014
+ if (options.projectId && options.create) throw new Error("Choose --project or --create, not both.");
1015
+ if (Boolean(options.file) !== Boolean(options.machineId)) throw new Error("Machine association requires a file and --machine <id|url>.");
1016
+ if (options.create && options.machineId) throw new Error("Link an existing machine to its existing project with --project; --create creates an empty project.");
1017
+ if (options.visibility && !options.create) throw new Error("--visibility requires --create.");
1018
+ if (options.name && !options.create) throw new Error("--name requires --create.");
1019
+ const cwd = path.resolve(options.cwd ?? process.cwd());
1020
+ const configPath = path.resolve(cwd, options.configPath ?? STATELY_CONFIG_FILE);
1021
+ const existing = await readStatelyProjectConfig({
1022
+ cwd,
1023
+ configPath,
1024
+ rewriteIfNeeded: false
1025
+ }).catch((error) => {
1026
+ if (error.code === "ENOENT") return void 0;
1027
+ throw error;
1028
+ });
1029
+ if (existing?.rewriteStatus.reason === "multiple-legacy-sources") throw new Error("Update the legacy sources configuration manually before linking; no files were changed.");
1030
+ const studioUrl = options.baseUrl ?? existing?.config.studioUrl ?? "https://stately.ai";
1031
+ const requestedId = options.projectId ? resourceId(options.projectId, "project") : void 0;
1032
+ const sameDeployment = !existing?.config.studioUrl || resolveApiBaseUrl(studioUrl) === resolveApiBaseUrl(existing.config.studioUrl);
1033
+ if (existing?.config.projectId && (!sameDeployment || options.create || requestedId && requestedId !== existing.config.projectId) && !options.force) throw new Error(`Already linked to project ${existing.config.projectId}. Use --force to change the project association; existing machine IDs are retained.`);
1034
+ const machineId = options.machineId ? resourceId(options.machineId, "machine") : void 0;
1035
+ const filePath = options.file ? path.resolve(cwd, options.file) : void 0;
1036
+ const source = filePath ? await fsPromises.readFile(filePath, "utf8") : void 0;
1037
+ const machineIndex = options.machineIndex ?? 0;
1038
+ if (filePath && source !== void 0) {
1039
+ if (!/\.[cm]?[jt]sx?$/.test(filePath)) throw new Error("Machine association supports JavaScript and TypeScript source files.");
1040
+ const attachments = findStatelyPragmaAttachments(source, filePath);
1041
+ if (attachments.length > 1 && options.machineIndex === void 0) throw new Error("This file contains multiple machines. Select one with --machine-index (zero-based).");
1042
+ const attachment = attachments[machineIndex];
1043
+ if (!attachment || !Number.isInteger(machineIndex) || machineIndex < 0) throw new Error("No machine at the selected index.");
1044
+ if (attachment.pragma?.id && attachment.pragma.id !== machineId && !options.force) throw new Error(`Machine already linked to ${attachment.pragma.id}. Use --force to replace its association.`);
1045
+ } else if (options.machineIndex !== void 0) throw new Error("--machine-index requires a file and --machine.");
1046
+ const client = options.client ?? createStatelyClient({
1047
+ apiKey: options.apiKey,
1048
+ baseUrl: resolveApiBaseUrl(studioUrl)
1049
+ });
1050
+ let project;
1051
+ if (options.create) project = await client.projects.create({
1052
+ name: options.name ?? path.basename(cwd),
1053
+ visibility: options.visibility ?? "Private"
1054
+ });
1055
+ else {
1056
+ const projectId = requestedId ?? existing?.config.projectId;
1057
+ if (!projectId) throw new Error("Choose --project <id|url> or --create to connect this workspace.");
1058
+ project = await client.projects.get(projectId);
1059
+ }
1060
+ if (machineId) {
1061
+ if (!project.machines.some((machine) => machine.machineId === machineId)) throw new Error(`Machine ${machineId} is not in project ${project.projectId}. No files were changed.`);
1062
+ await client.machines.get(machineId);
1063
+ }
1064
+ if (filePath && await fsPromises.readFile(filePath, "utf8") !== source) throw new Error("Source changed while linking. Retry; no files were changed.");
1065
+ const config = {
1066
+ ...existing?.config ?? createStatelyProjectConfig({}),
1067
+ projectId: project.projectId,
1068
+ studioUrl
1069
+ };
1070
+ const writes = [];
1071
+ const stage = async (destination, write) => {
1072
+ const suffix = randomUUID();
1073
+ const entry = {
1074
+ path: destination,
1075
+ staged: `${destination}.${suffix}.tmp`,
1076
+ backup: `${destination}.${suffix}.bak`,
1077
+ existed: false,
1078
+ committed: false
1079
+ };
1080
+ writes.push(entry);
1081
+ try {
1082
+ await fsPromises.copyFile(destination, entry.backup);
1083
+ entry.existed = true;
1084
+ } catch (error) {
1085
+ if (error.code !== "ENOENT") throw error;
1086
+ }
1087
+ await write(entry.staged);
1088
+ if (entry.existed) await fsPromises.chmod(entry.staged, (await fsPromises.stat(destination)).mode);
1089
+ };
1090
+ let rollbackFailed = false;
1091
+ try {
1092
+ await stage(configPath, (temporary) => writeStatelyProjectConfig(temporary, config));
1093
+ if (filePath && source !== void 0 && machineId) {
1094
+ const updated = upsertStatelyPragma(source, machineId, {
1095
+ fileName: filePath,
1096
+ machineIndex
1097
+ });
1098
+ if (updated !== source) await stage(filePath, (temporary) => fsPromises.writeFile(temporary, updated, "utf8"));
1099
+ }
1100
+ if (filePath && await fsPromises.readFile(filePath, "utf8") !== source) throw new Error("Source changed while linking. Retry; no files were changed.");
1101
+ for (const entry of writes) {
1102
+ await fsPromises.rename(entry.staged, entry.path);
1103
+ entry.committed = true;
1104
+ }
1105
+ } catch (error) {
1106
+ const rollbackErrors = [];
1107
+ for (const entry of [...writes].reverse()) {
1108
+ if (!entry.committed) continue;
1109
+ try {
1110
+ if (entry.existed) await fsPromises.rename(entry.backup, entry.path);
1111
+ else await fsPromises.unlink(entry.path);
1112
+ } catch (rollbackError) {
1113
+ rollbackFailed = true;
1114
+ rollbackErrors.push(rollbackError);
1115
+ }
1116
+ }
1117
+ if (rollbackFailed) throw new AggregateError([error, ...rollbackErrors], "Link failed and could not restore every file. Original files are retained in .bak files beside their destinations.");
1118
+ throw error;
1119
+ } finally {
1120
+ await Promise.all(writes.flatMap((entry) => rollbackFailed ? [entry.staged] : [entry.staged, entry.backup]).map((temporary) => fsPromises.rm(temporary, { force: true })));
1121
+ }
1122
+ return {
1123
+ project,
1124
+ config,
1125
+ configPath,
1126
+ machineId,
1127
+ filePath
1128
+ };
1129
+ }
1130
+
722
1131
  //#endregion
723
1132
  //#region ../editor-sync/dist/fs-Dw5c-Wnp.mjs
724
1133
  function comparePosition(left, right) {
@@ -928,131 +1337,467 @@ function offsetAt(lineStarts, line, character) {
928
1337
  }
929
1338
 
930
1339
  //#endregion
931
- //#region ../../node_modules/.pnpm/xstate@5.32.1/node_modules/xstate/dist/xstate-dev.esm.js
932
- function getGlobal() {
933
- if (typeof globalThis !== "undefined") return globalThis;
934
- if (typeof self !== "undefined") return self;
935
- if (typeof window !== "undefined") return window;
936
- if (typeof global !== "undefined") return global;
937
- }
938
- function getDevTools() {
939
- const w = getGlobal();
940
- if (w.__xstate__) return w.__xstate__;
1340
+ //#region ../graph-tools/dist/semanticGraphDiffModel-1Yw70u6x.mjs
1341
+ /**
1342
+ * Convert a resolved {@link GraphNode} back into a {@link NodeConfig}.
1343
+ *
1344
+ * Faithful and complete: round-tripping through `createGraphNode` yields a
1345
+ * deep-equal node. Optional fields are only included when present; ports are
1346
+ * deep-copied so the config does not share port objects with the source node.
1347
+ */
1348
+ function toNodeConfig(node) {
1349
+ const config = { id: node.id };
1350
+ if (node.parentId != null) config.parentId = node.parentId;
1351
+ if (node.initialNodeId != null) config.initialNodeId = node.initialNodeId;
1352
+ if (node.label != null) config.label = node.label;
1353
+ if (node.data != null) config.data = node.data;
1354
+ if (node.ports !== void 0) config.ports = node.ports.map((p) => ({ ...p }));
1355
+ if (node.x !== void 0) config.x = node.x;
1356
+ if (node.y !== void 0) config.y = node.y;
1357
+ if (node.width !== void 0) config.width = node.width;
1358
+ if (node.height !== void 0) config.height = node.height;
1359
+ if (node.shape !== void 0) config.shape = node.shape;
1360
+ if (node.color !== void 0) config.color = node.color;
1361
+ if (node.style !== void 0) config.style = node.style;
1362
+ return config;
941
1363
  }
942
- const devToolsAdapter = (service) => {
943
- if (typeof window === "undefined") return;
944
- const devTools = getDevTools();
945
- if (devTools) devTools.register(service);
946
- };
947
-
948
- //#endregion
949
- //#region ../../node_modules/.pnpm/xstate@5.32.1/node_modules/xstate/dist/raise-e974d1c9.esm.js
950
- var Mailbox = class {
951
- constructor(_process) {
952
- this._process = _process;
953
- this._active = false;
954
- this._current = null;
955
- this._last = null;
956
- }
957
- start() {
958
- this._active = true;
959
- this.flush();
960
- }
961
- clear() {
962
- if (this._current) {
963
- this._current.next = null;
964
- this._last = this._current;
965
- }
966
- }
967
- enqueue(event) {
968
- const enqueued = {
969
- value: event,
970
- next: null
971
- };
972
- if (this._current) {
973
- this._last.next = enqueued;
974
- this._last = enqueued;
975
- return;
976
- }
977
- this._current = enqueued;
978
- this._last = enqueued;
979
- if (this._active) this.flush();
980
- }
981
- flush() {
982
- while (this._current) {
983
- const consumed = this._current;
984
- this._process(consumed.value);
985
- this._current = consumed.next;
986
- }
987
- this._last = null;
988
- }
989
- };
990
- const STATE_DELIMITER = ".";
991
- const TARGETLESS_KEY = "";
992
- const NULL_EVENT = "";
993
- const STATE_IDENTIFIER = "#";
994
- const WILDCARD = "*";
995
- const XSTATE_INIT = "xstate.init";
996
- const XSTATE_STOP = "xstate.stop";
997
1364
  /**
998
- * Returns an event that represents an implicit event that is sent after the
999
- * specified `delay`.
1365
+ * Convert a resolved {@link GraphEdge} back into an {@link EdgeConfig}.
1000
1366
  *
1001
- * @param delayRef The delay in milliseconds
1002
- * @param id The state node ID where this event is handled
1367
+ * Faithful and complete: round-tripping through `createGraphEdge` yields a
1368
+ * deep-equal edge. Optional fields are only included when present.
1003
1369
  */
1004
- function createAfterEvent(delayRef, id) {
1005
- return { type: `xstate.after.${delayRef}.${id}` };
1370
+ function toEdgeConfig(edge) {
1371
+ const config = {
1372
+ id: edge.id,
1373
+ sourceId: edge.sourceId,
1374
+ targetId: edge.targetId
1375
+ };
1376
+ if (edge.label != null) config.label = edge.label;
1377
+ if (edge.data != null) config.data = edge.data;
1378
+ if (edge.weight !== void 0) config.weight = edge.weight;
1379
+ if (edge.mode !== void 0) config.mode = edge.mode;
1380
+ if (edge.sourcePort !== void 0) config.sourcePort = edge.sourcePort;
1381
+ if (edge.targetPort !== void 0) config.targetPort = edge.targetPort;
1382
+ if (edge.points !== void 0) config.points = edge.points.map((p) => ({ ...p }));
1383
+ if (edge.routing !== void 0) config.routing = edge.routing;
1384
+ if (edge.x !== void 0) config.x = edge.x;
1385
+ if (edge.y !== void 0) config.y = edge.y;
1386
+ if (edge.width !== void 0) config.width = edge.width;
1387
+ if (edge.height !== void 0) config.height = edge.height;
1388
+ if (edge.color !== void 0) config.color = edge.color;
1389
+ if (edge.style !== void 0) config.style = edge.style;
1390
+ return config;
1391
+ }
1392
+ const LAYOUT_KEYS$1 = {
1393
+ node: [
1394
+ "x",
1395
+ "y",
1396
+ "width",
1397
+ "height",
1398
+ "style",
1399
+ "color",
1400
+ "shape"
1401
+ ],
1402
+ edge: [
1403
+ "x",
1404
+ "y",
1405
+ "width",
1406
+ "height",
1407
+ "points",
1408
+ "routing",
1409
+ "style",
1410
+ "color"
1411
+ ]
1412
+ };
1413
+ new Set(LAYOUT_KEYS$1.node), new Set(LAYOUT_KEYS$1.edge);
1414
+ /** Shallow-compare two values, returning true if they differ. */
1415
+ function differs(a, b) {
1416
+ if (a === b) return false;
1417
+ if (a == null || b == null) return a !== b;
1418
+ if (typeof a === "object" && typeof b === "object") return JSON.stringify(a) !== JSON.stringify(b);
1419
+ return true;
1006
1420
  }
1421
+ const NODE_COMPARE_KEYS = [
1422
+ "parentId",
1423
+ "initialNodeId",
1424
+ "label",
1425
+ "data",
1426
+ "ports",
1427
+ "x",
1428
+ "y",
1429
+ "width",
1430
+ "height",
1431
+ "shape",
1432
+ "color",
1433
+ "style"
1434
+ ];
1435
+ const EDGE_COMPARE_KEYS = [
1436
+ "sourceId",
1437
+ "targetId",
1438
+ "label",
1439
+ "data",
1440
+ "weight",
1441
+ "mode",
1442
+ "sourcePort",
1443
+ "targetPort",
1444
+ "points",
1445
+ "routing",
1446
+ "x",
1447
+ "y",
1448
+ "width",
1449
+ "height",
1450
+ "color",
1451
+ "style"
1452
+ ];
1007
1453
  /**
1008
- * Returns an event that represents that a final state node has been reached in
1009
- * the parent state node.
1454
+ * Compute a structured diff from graph `a` to graph `b` by matching IDs.
1010
1455
  *
1011
- * @param id The final state node's parent state node `id`
1012
- * @param output The data to pass into the event
1456
+ * @example
1457
+ * ```ts
1458
+ * import { createGraph, getDiff } from '@statelyai/graph';
1459
+ *
1460
+ * const a = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
1461
+ * const b = createGraph({ nodes: [{ id: 'n1', label: 'hello' }, { id: 'n2' }], edges: [] });
1462
+ *
1463
+ * const diff = getDiff(a, b);
1464
+ * // diff.nodes.added → [{ id: 'n2' }]
1465
+ * // diff.nodes.updated → [{ id: 'n1', old: { label: '' }, new: { label: 'hello' } }]
1466
+ * ```
1013
1467
  */
1014
- function createDoneStateEvent(id, output) {
1015
- return {
1016
- type: `xstate.done.state.${id}`,
1017
- output
1468
+ function getDiff(a, b) {
1469
+ const aNodeMap = new Map(a.nodes.map((n) => [n.id, n]));
1470
+ const bNodeMap = new Map(b.nodes.map((n) => [n.id, n]));
1471
+ const aEdgeMap = new Map(a.edges.map((e) => [e.id, e]));
1472
+ const bEdgeMap = new Map(b.edges.map((e) => [e.id, e]));
1473
+ const diff = {
1474
+ nodes: {
1475
+ added: [],
1476
+ removed: [],
1477
+ updated: []
1478
+ },
1479
+ edges: {
1480
+ added: [],
1481
+ removed: [],
1482
+ updated: []
1483
+ }
1018
1484
  };
1485
+ for (const [id, nodeB] of bNodeMap) {
1486
+ const nodeA = aNodeMap.get(id);
1487
+ if (!nodeA) diff.nodes.added.push(toNodeConfig(nodeB));
1488
+ else {
1489
+ const oldPartial = {};
1490
+ const newPartial = {};
1491
+ for (const key of NODE_COMPARE_KEYS) {
1492
+ const oldValue = nodeA[key] ?? null;
1493
+ const newValue = nodeB[key] ?? null;
1494
+ if (differs(oldValue, newValue)) {
1495
+ oldPartial[key] = oldValue;
1496
+ newPartial[key] = newValue;
1497
+ }
1498
+ }
1499
+ if (Object.keys(oldPartial).length > 0) diff.nodes.updated.push({
1500
+ id,
1501
+ old: oldPartial,
1502
+ new: newPartial
1503
+ });
1504
+ }
1505
+ }
1506
+ for (const [id, nodeA] of aNodeMap) if (!bNodeMap.has(id)) diff.nodes.removed.push(toNodeConfig(nodeA));
1507
+ for (const [id, edgeB] of bEdgeMap) {
1508
+ const edgeA = aEdgeMap.get(id);
1509
+ if (!edgeA) diff.edges.added.push(toEdgeConfig(edgeB));
1510
+ else {
1511
+ const oldPartial = {};
1512
+ const newPartial = {};
1513
+ for (const key of EDGE_COMPARE_KEYS) {
1514
+ const oldValue = edgeA[key] ?? null;
1515
+ const newValue = edgeB[key] ?? null;
1516
+ if (differs(oldValue, newValue)) {
1517
+ oldPartial[key] = oldValue;
1518
+ newPartial[key] = newValue;
1519
+ }
1520
+ }
1521
+ if (Object.keys(oldPartial).length > 0) diff.edges.updated.push({
1522
+ id,
1523
+ old: oldPartial,
1524
+ new: newPartial
1525
+ });
1526
+ }
1527
+ }
1528
+ for (const [id, edgeA] of aEdgeMap) if (!bEdgeMap.has(id)) diff.edges.removed.push(toEdgeConfig(edgeA));
1529
+ return diff;
1019
1530
  }
1020
1531
  /**
1021
- * Returns an event that represents that an invoked service has terminated.
1532
+ * Check if a diff has zero changes.
1022
1533
  *
1023
- * An invoked service is terminated when it has reached a top-level final state
1024
- * node, but not when it is canceled.
1534
+ * @example
1535
+ * ```ts
1536
+ * import { createGraph, getDiff, isEmptyDiff } from '@statelyai/graph';
1025
1537
  *
1026
- * @param invokeId The invoked service ID
1027
- * @param output The data to pass into the event
1538
+ * const g = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
1539
+ * const diff = getDiff(g, g);
1540
+ * isEmptyDiff(diff); // true
1541
+ * ```
1028
1542
  */
1029
- function createDoneActorEvent(invokeId, output) {
1030
- return {
1031
- type: `xstate.done.actor.${invokeId}`,
1032
- output,
1033
- actorId: invokeId
1034
- };
1543
+ function isEmptyDiff$1(diff) {
1544
+ 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;
1035
1545
  }
1036
- function createErrorActorEvent(id, error) {
1546
+ function summarizeGraphDiff(diff) {
1037
1547
  return {
1038
- type: `xstate.error.actor.${id}`,
1039
- error,
1040
- actorId: id
1548
+ hasChanges: !isEmptyDiff$1(diff),
1549
+ nodeChanges: diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length,
1550
+ edgeChanges: diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length
1041
1551
  };
1042
1552
  }
1043
- function createInitEvent(input) {
1044
- return {
1045
- type: XSTATE_INIT,
1046
- input
1047
- };
1553
+ function nodeDepth$1(graph, node) {
1554
+ let depth = 0;
1555
+ let parentId = node.parentId;
1556
+ while (parentId) {
1557
+ depth++;
1558
+ parentId = graph.nodes.find((candidate) => candidate.id === parentId)?.parentId;
1559
+ }
1560
+ return depth;
1048
1561
  }
1049
- /**
1050
- * This function makes sure that unhandled errors are thrown in a separate
1051
- * macrotask. It allows those errors to be detected by global error handlers and
1052
- * reported to bug tracking services without interrupting our own stack of
1053
- * execution.
1054
- *
1055
- * @param err Error to be thrown
1562
+ function nodeKey(node) {
1563
+ return node.data?.key || node.id;
1564
+ }
1565
+ function edgeSignature(edge) {
1566
+ return [
1567
+ edge.sourceId,
1568
+ edge.data?.eventType,
1569
+ edge.targetId,
1570
+ edge.data?.guard?.type ?? ""
1571
+ ].join("->");
1572
+ }
1573
+ function getStableNodeId(node, parentStableId, siblingIndex, preferredIds) {
1574
+ const preferredId = preferredIds?.get(node.id);
1575
+ if (preferredId !== void 0) return preferredId;
1576
+ if (node.parentId === null) return "root";
1577
+ const key = nodeKey(node);
1578
+ const suffix = siblingIndex === 0 ? key : `${key}_${siblingIndex + 1}`;
1579
+ return parentStableId ? `${parentStableId}.${suffix}` : suffix;
1580
+ }
1581
+ function getStableEdgeId(edge, siblingIndex, preferredIds) {
1582
+ const preferredId = preferredIds?.get(edge.id);
1583
+ if (preferredId !== void 0) return preferredId;
1584
+ const suffix = siblingIndex === 0 ? "" : `.${siblingIndex + 1}`;
1585
+ return edgeSignature(edge).concat(suffix);
1586
+ }
1587
+ function removePresentationFields(entity, kind) {
1588
+ const mutableEntity = entity;
1589
+ for (const key of LAYOUT_KEYS$1[kind]) delete mutableEntity[key];
1590
+ const data = mutableEntity.data;
1591
+ if (data && typeof data === "object" && !Array.isArray(data)) delete data.color;
1592
+ }
1593
+ /**
1594
+ * Rewrites entity ids to structural ("semantic") ids so two graphs that model
1595
+ * the same machine compare equal regardless of how their ids were minted.
1596
+ *
1597
+ * Invariant: node and edge array order is preserved exactly. Callers
1598
+ * (`alignCompareIds` in the editor's /v1/diff route) rely on
1599
+ * `normalized.nodes[i]` describing `graph.nodes[i]` to translate a semantic id
1600
+ * back to the caller's original id. Never sort, filter, or reorder the arrays
1601
+ * on the returned graph; sorting for traversal must be done on a copy, as the
1602
+ * depth ordering below does.
1603
+ *
1604
+ * Presentation-only fields defined by `LAYOUT_KEYS`, plus entity data colors,
1605
+ * are removed. When preferred ids are supplied, matching raw ids retain the
1606
+ * base graph's semantic identity so a stable-id rename is reported as an
1607
+ * update.
1608
+ *
1609
+ * Entity `data` is treated as optional throughout: sparse graphs (a node with
1610
+ * `data: {}`, an edge with `data: null`) normalize instead of throwing.
1611
+ */
1612
+ function normalizeGraphForSemanticDiff(graph, preferredIds) {
1613
+ const normalized = structuredClone(graph);
1614
+ const nodeIdMap = /* @__PURE__ */ new Map();
1615
+ const siblingKeys = /* @__PURE__ */ new Map();
1616
+ const reservedNodeIds = new Set(preferredIds?.nodes.values());
1617
+ const claimedNodeIds = /* @__PURE__ */ new Set();
1618
+ const nodesByDepth = [...normalized.nodes].sort((a, b) => nodeDepth$1(normalized, a) - nodeDepth$1(normalized, b));
1619
+ for (const node of nodesByDepth) {
1620
+ const parentStableId = node.parentId ? nodeIdMap.get(node.parentId) ?? node.parentId : null;
1621
+ const key = `${parentStableId ?? ""}:${nodeKey(node)}`;
1622
+ const siblingIndex = siblingKeys.get(key) ?? 0;
1623
+ siblingKeys.set(key, siblingIndex + 1);
1624
+ const proposedId = getStableNodeId(node, parentStableId, siblingIndex, preferredIds?.nodes);
1625
+ let stableId = proposedId;
1626
+ let collisionIndex = 2;
1627
+ while (!preferredIds?.nodes.has(node.id) && (reservedNodeIds.has(stableId) || claimedNodeIds.has(stableId))) stableId = `${proposedId}:unmatched:${collisionIndex++}`;
1628
+ claimedNodeIds.add(stableId);
1629
+ nodeIdMap.set(node.id, stableId);
1630
+ }
1631
+ for (const node of normalized.nodes) {
1632
+ removePresentationFields(node, "node");
1633
+ node.id = nodeIdMap.get(node.id) ?? node.id;
1634
+ if (node.parentId) node.parentId = nodeIdMap.get(node.parentId) ?? node.parentId;
1635
+ if (node.initialNodeId) node.initialNodeId = nodeIdMap.get(node.initialNodeId) ?? node.initialNodeId;
1636
+ if (node.data?.initialId) node.data.initialId = nodeIdMap.get(node.data.initialId) ?? node.data.initialId;
1637
+ }
1638
+ const edgeSiblingKeys = /* @__PURE__ */ new Map();
1639
+ const reservedEdgeIds = new Set(preferredIds?.edges.values());
1640
+ const claimedEdgeIds = /* @__PURE__ */ new Set();
1641
+ for (const edge of normalized.edges) {
1642
+ removePresentationFields(edge, "edge");
1643
+ edge.sourceId = nodeIdMap.get(edge.sourceId) ?? edge.sourceId;
1644
+ edge.targetId = nodeIdMap.get(edge.targetId) ?? edge.targetId;
1645
+ const key = edgeSignature(edge);
1646
+ const siblingIndex = edgeSiblingKeys.get(key) ?? 0;
1647
+ edgeSiblingKeys.set(key, siblingIndex + 1);
1648
+ const proposedId = getStableEdgeId(edge, siblingIndex, preferredIds?.edges);
1649
+ let stableId = proposedId;
1650
+ let collisionIndex = 2;
1651
+ while (!preferredIds?.edges.has(edge.id) && (reservedEdgeIds.has(stableId) || claimedEdgeIds.has(stableId))) stableId = `${proposedId}:unmatched:${collisionIndex++}`;
1652
+ claimedEdgeIds.add(stableId);
1653
+ edge.id = stableId;
1654
+ }
1655
+ if (normalized.initialNodeId) normalized.initialNodeId = nodeIdMap.get(normalized.initialNodeId) ?? normalized.initialNodeId;
1656
+ return normalized;
1657
+ }
1658
+ function diffGraphsSemantically(baseGraph, compareGraph) {
1659
+ const base = normalizeGraphForSemanticDiff(baseGraph);
1660
+ const compareNodeIds = new Set(compareGraph.nodes.map((node) => node.id));
1661
+ const compareEdgeIds = new Set(compareGraph.edges.map((edge) => edge.id));
1662
+ const compare = normalizeGraphForSemanticDiff(compareGraph, {
1663
+ nodes: new Map(baseGraph.nodes.map((node, index) => [node.id, base.nodes[index].id]).filter(([id]) => compareNodeIds.has(id))),
1664
+ edges: new Map(baseGraph.edges.map((edge, index) => [edge.id, base.edges[index].id]).filter(([id]) => compareEdgeIds.has(id)))
1665
+ });
1666
+ const diff = getDiff(base, compare);
1667
+ return {
1668
+ base,
1669
+ compare,
1670
+ diff,
1671
+ summary: summarizeGraphDiff(diff)
1672
+ };
1673
+ }
1674
+
1675
+ //#endregion
1676
+ //#region ../../node_modules/.pnpm/xstate@5.32.1/node_modules/xstate/dist/xstate-dev.esm.js
1677
+ function getGlobal() {
1678
+ if (typeof globalThis !== "undefined") return globalThis;
1679
+ if (typeof self !== "undefined") return self;
1680
+ if (typeof window !== "undefined") return window;
1681
+ if (typeof global !== "undefined") return global;
1682
+ }
1683
+ function getDevTools() {
1684
+ const w = getGlobal();
1685
+ if (w.__xstate__) return w.__xstate__;
1686
+ }
1687
+ const devToolsAdapter = (service) => {
1688
+ if (typeof window === "undefined") return;
1689
+ const devTools = getDevTools();
1690
+ if (devTools) devTools.register(service);
1691
+ };
1692
+
1693
+ //#endregion
1694
+ //#region ../../node_modules/.pnpm/xstate@5.32.1/node_modules/xstate/dist/raise-e974d1c9.esm.js
1695
+ var Mailbox = class {
1696
+ constructor(_process) {
1697
+ this._process = _process;
1698
+ this._active = false;
1699
+ this._current = null;
1700
+ this._last = null;
1701
+ }
1702
+ start() {
1703
+ this._active = true;
1704
+ this.flush();
1705
+ }
1706
+ clear() {
1707
+ if (this._current) {
1708
+ this._current.next = null;
1709
+ this._last = this._current;
1710
+ }
1711
+ }
1712
+ enqueue(event) {
1713
+ const enqueued = {
1714
+ value: event,
1715
+ next: null
1716
+ };
1717
+ if (this._current) {
1718
+ this._last.next = enqueued;
1719
+ this._last = enqueued;
1720
+ return;
1721
+ }
1722
+ this._current = enqueued;
1723
+ this._last = enqueued;
1724
+ if (this._active) this.flush();
1725
+ }
1726
+ flush() {
1727
+ while (this._current) {
1728
+ const consumed = this._current;
1729
+ this._process(consumed.value);
1730
+ this._current = consumed.next;
1731
+ }
1732
+ this._last = null;
1733
+ }
1734
+ };
1735
+ const STATE_DELIMITER = ".";
1736
+ const TARGETLESS_KEY = "";
1737
+ const NULL_EVENT = "";
1738
+ const STATE_IDENTIFIER = "#";
1739
+ const WILDCARD = "*";
1740
+ const XSTATE_INIT = "xstate.init";
1741
+ const XSTATE_STOP = "xstate.stop";
1742
+ /**
1743
+ * Returns an event that represents an implicit event that is sent after the
1744
+ * specified `delay`.
1745
+ *
1746
+ * @param delayRef The delay in milliseconds
1747
+ * @param id The state node ID where this event is handled
1748
+ */
1749
+ function createAfterEvent(delayRef, id) {
1750
+ return { type: `xstate.after.${delayRef}.${id}` };
1751
+ }
1752
+ /**
1753
+ * Returns an event that represents that a final state node has been reached in
1754
+ * the parent state node.
1755
+ *
1756
+ * @param id The final state node's parent state node `id`
1757
+ * @param output The data to pass into the event
1758
+ */
1759
+ function createDoneStateEvent(id, output) {
1760
+ return {
1761
+ type: `xstate.done.state.${id}`,
1762
+ output
1763
+ };
1764
+ }
1765
+ /**
1766
+ * Returns an event that represents that an invoked service has terminated.
1767
+ *
1768
+ * An invoked service is terminated when it has reached a top-level final state
1769
+ * node, but not when it is canceled.
1770
+ *
1771
+ * @param invokeId The invoked service ID
1772
+ * @param output The data to pass into the event
1773
+ */
1774
+ function createDoneActorEvent(invokeId, output) {
1775
+ return {
1776
+ type: `xstate.done.actor.${invokeId}`,
1777
+ output,
1778
+ actorId: invokeId
1779
+ };
1780
+ }
1781
+ function createErrorActorEvent(id, error) {
1782
+ return {
1783
+ type: `xstate.error.actor.${id}`,
1784
+ error,
1785
+ actorId: id
1786
+ };
1787
+ }
1788
+ function createInitEvent(input) {
1789
+ return {
1790
+ type: XSTATE_INIT,
1791
+ input
1792
+ };
1793
+ }
1794
+ /**
1795
+ * This function makes sure that unhandled errors are thrown in a separate
1796
+ * macrotask. It allows those errors to be detected by global error handlers and
1797
+ * reported to bug tracking services without interrupting our own stack of
1798
+ * execution.
1799
+ *
1800
+ * @param err Error to be thrown
1056
1801
  */
1057
1802
  function reportUnhandledError(err) {
1058
1803
  setTimeout(() => {
@@ -5831,373 +6576,40 @@ var import_js_yaml = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((ex
5831
6576
  throw new Error("Function yaml." + from + " is removed in js-yaml 4. Use yaml." + to + " instead, which is now safe by default.");
5832
6577
  };
5833
6578
  }
5834
- module.exports.Type = require_type();
5835
- module.exports.Schema = require_schema();
5836
- module.exports.FAILSAFE_SCHEMA = require_failsafe();
5837
- module.exports.JSON_SCHEMA = require_json();
5838
- module.exports.CORE_SCHEMA = require_core();
5839
- module.exports.DEFAULT_SCHEMA = require_default();
5840
- module.exports.load = loader.load;
5841
- module.exports.loadAll = loader.loadAll;
5842
- module.exports.dump = dumper.dump;
5843
- module.exports.YAMLException = require_exception();
5844
- module.exports.types = {
5845
- binary: require_binary(),
5846
- float: require_float(),
5847
- map: require_map(),
5848
- null: require_null(),
5849
- pairs: require_pairs(),
5850
- set: require_set(),
5851
- timestamp: require_timestamp(),
5852
- bool: require_bool(),
5853
- int: require_int(),
5854
- merge: require_merge(),
5855
- omap: require_omap(),
5856
- seq: require_seq(),
5857
- str: require_str()
5858
- };
5859
- module.exports.safeLoad = renamed("safeLoad", "load");
5860
- module.exports.safeLoadAll = renamed("safeLoadAll", "loadAll");
5861
- module.exports.safeDump = renamed("safeDump", "dump");
5862
- })))(), 1);
5863
- var { Type, Schema, FAILSAFE_SCHEMA, JSON_SCHEMA, CORE_SCHEMA, DEFAULT_SCHEMA, load, loadAll, dump, YAMLException, types, safeLoad, safeLoadAll, safeDump } = import_js_yaml.default;
5864
- var index_vite_proxy_tmp_default = import_js_yaml.default;
5865
-
5866
- //#endregion
5867
- //#region ../graph-tools/dist/index.mjs
5868
- /**
5869
- * Convert a resolved {@link GraphNode} back into a {@link NodeConfig}.
5870
- *
5871
- * Faithful and complete: round-tripping through `createGraphNode` yields a
5872
- * deep-equal node. Optional fields are only included when present; ports are
5873
- * deep-copied so the config does not share port objects with the source node.
5874
- */
5875
- function toNodeConfig(node) {
5876
- const config = { id: node.id };
5877
- if (node.parentId != null) config.parentId = node.parentId;
5878
- if (node.initialNodeId != null) config.initialNodeId = node.initialNodeId;
5879
- if (node.label != null) config.label = node.label;
5880
- if (node.data != null) config.data = node.data;
5881
- if (node.ports !== void 0) config.ports = node.ports.map((p) => ({ ...p }));
5882
- if (node.x !== void 0) config.x = node.x;
5883
- if (node.y !== void 0) config.y = node.y;
5884
- if (node.width !== void 0) config.width = node.width;
5885
- if (node.height !== void 0) config.height = node.height;
5886
- if (node.shape !== void 0) config.shape = node.shape;
5887
- if (node.color !== void 0) config.color = node.color;
5888
- if (node.style !== void 0) config.style = node.style;
5889
- return config;
5890
- }
5891
- /**
5892
- * Convert a resolved {@link GraphEdge} back into an {@link EdgeConfig}.
5893
- *
5894
- * Faithful and complete: round-tripping through `createGraphEdge` yields a
5895
- * deep-equal edge. Optional fields are only included when present.
5896
- */
5897
- function toEdgeConfig(edge) {
5898
- const config = {
5899
- id: edge.id,
5900
- sourceId: edge.sourceId,
5901
- targetId: edge.targetId
5902
- };
5903
- if (edge.label != null) config.label = edge.label;
5904
- if (edge.data != null) config.data = edge.data;
5905
- if (edge.weight !== void 0) config.weight = edge.weight;
5906
- if (edge.mode !== void 0) config.mode = edge.mode;
5907
- if (edge.sourcePort !== void 0) config.sourcePort = edge.sourcePort;
5908
- if (edge.targetPort !== void 0) config.targetPort = edge.targetPort;
5909
- if (edge.points !== void 0) config.points = edge.points.map((p) => ({ ...p }));
5910
- if (edge.routing !== void 0) config.routing = edge.routing;
5911
- if (edge.x !== void 0) config.x = edge.x;
5912
- if (edge.y !== void 0) config.y = edge.y;
5913
- if (edge.width !== void 0) config.width = edge.width;
5914
- if (edge.height !== void 0) config.height = edge.height;
5915
- if (edge.color !== void 0) config.color = edge.color;
5916
- if (edge.style !== void 0) config.style = edge.style;
5917
- return config;
5918
- }
5919
- const LAYOUT_KEYS$1 = {
5920
- node: [
5921
- "x",
5922
- "y",
5923
- "width",
5924
- "height",
5925
- "style",
5926
- "color",
5927
- "shape"
5928
- ],
5929
- edge: [
5930
- "x",
5931
- "y",
5932
- "width",
5933
- "height",
5934
- "points",
5935
- "routing",
5936
- "style",
5937
- "color"
5938
- ]
5939
- };
5940
- new Set(LAYOUT_KEYS$1.node), new Set(LAYOUT_KEYS$1.edge);
5941
- /** Shallow-compare two values, returning true if they differ. */
5942
- function differs(a, b) {
5943
- if (a === b) return false;
5944
- if (a == null || b == null) return a !== b;
5945
- if (typeof a === "object" && typeof b === "object") return JSON.stringify(a) !== JSON.stringify(b);
5946
- return true;
5947
- }
5948
- const NODE_COMPARE_KEYS = [
5949
- "parentId",
5950
- "initialNodeId",
5951
- "label",
5952
- "data",
5953
- "ports",
5954
- "x",
5955
- "y",
5956
- "width",
5957
- "height",
5958
- "shape",
5959
- "color",
5960
- "style"
5961
- ];
5962
- const EDGE_COMPARE_KEYS = [
5963
- "sourceId",
5964
- "targetId",
5965
- "label",
5966
- "data",
5967
- "weight",
5968
- "mode",
5969
- "sourcePort",
5970
- "targetPort",
5971
- "points",
5972
- "routing",
5973
- "x",
5974
- "y",
5975
- "width",
5976
- "height",
5977
- "color",
5978
- "style"
5979
- ];
5980
- /**
5981
- * Compute a structured diff from graph `a` to graph `b` by matching IDs.
5982
- *
5983
- * @example
5984
- * ```ts
5985
- * import { createGraph, getDiff } from '@statelyai/graph';
5986
- *
5987
- * const a = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
5988
- * const b = createGraph({ nodes: [{ id: 'n1', label: 'hello' }, { id: 'n2' }], edges: [] });
5989
- *
5990
- * const diff = getDiff(a, b);
5991
- * // diff.nodes.added → [{ id: 'n2' }]
5992
- * // diff.nodes.updated → [{ id: 'n1', old: { label: '' }, new: { label: 'hello' } }]
5993
- * ```
5994
- */
5995
- function getDiff(a, b) {
5996
- const aNodeMap = new Map(a.nodes.map((n) => [n.id, n]));
5997
- const bNodeMap = new Map(b.nodes.map((n) => [n.id, n]));
5998
- const aEdgeMap = new Map(a.edges.map((e) => [e.id, e]));
5999
- const bEdgeMap = new Map(b.edges.map((e) => [e.id, e]));
6000
- const diff = {
6001
- nodes: {
6002
- added: [],
6003
- removed: [],
6004
- updated: []
6005
- },
6006
- edges: {
6007
- added: [],
6008
- removed: [],
6009
- updated: []
6010
- }
6011
- };
6012
- for (const [id, nodeB] of bNodeMap) {
6013
- const nodeA = aNodeMap.get(id);
6014
- if (!nodeA) diff.nodes.added.push(toNodeConfig(nodeB));
6015
- else {
6016
- const oldPartial = {};
6017
- const newPartial = {};
6018
- for (const key of NODE_COMPARE_KEYS) {
6019
- const oldValue = nodeA[key] ?? null;
6020
- const newValue = nodeB[key] ?? null;
6021
- if (differs(oldValue, newValue)) {
6022
- oldPartial[key] = oldValue;
6023
- newPartial[key] = newValue;
6024
- }
6025
- }
6026
- if (Object.keys(oldPartial).length > 0) diff.nodes.updated.push({
6027
- id,
6028
- old: oldPartial,
6029
- new: newPartial
6030
- });
6031
- }
6032
- }
6033
- for (const [id, nodeA] of aNodeMap) if (!bNodeMap.has(id)) diff.nodes.removed.push(toNodeConfig(nodeA));
6034
- for (const [id, edgeB] of bEdgeMap) {
6035
- const edgeA = aEdgeMap.get(id);
6036
- if (!edgeA) diff.edges.added.push(toEdgeConfig(edgeB));
6037
- else {
6038
- const oldPartial = {};
6039
- const newPartial = {};
6040
- for (const key of EDGE_COMPARE_KEYS) {
6041
- const oldValue = edgeA[key] ?? null;
6042
- const newValue = edgeB[key] ?? null;
6043
- if (differs(oldValue, newValue)) {
6044
- oldPartial[key] = oldValue;
6045
- newPartial[key] = newValue;
6046
- }
6047
- }
6048
- if (Object.keys(oldPartial).length > 0) diff.edges.updated.push({
6049
- id,
6050
- old: oldPartial,
6051
- new: newPartial
6052
- });
6053
- }
6054
- }
6055
- for (const [id, edgeA] of aEdgeMap) if (!bEdgeMap.has(id)) diff.edges.removed.push(toEdgeConfig(edgeA));
6056
- return diff;
6057
- }
6058
- /**
6059
- * Check if a diff has zero changes.
6060
- *
6061
- * @example
6062
- * ```ts
6063
- * import { createGraph, getDiff, isEmptyDiff } from '@statelyai/graph';
6064
- *
6065
- * const g = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
6066
- * const diff = getDiff(g, g);
6067
- * isEmptyDiff(diff); // true
6068
- * ```
6069
- */
6070
- function isEmptyDiff$1(diff) {
6071
- 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;
6072
- }
6073
- function summarizeGraphDiff(diff) {
6074
- return {
6075
- hasChanges: !isEmptyDiff$1(diff),
6076
- nodeChanges: diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length,
6077
- edgeChanges: diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length
6078
- };
6079
- }
6080
- function nodeDepth$1(graph, node) {
6081
- let depth = 0;
6082
- let parentId = node.parentId;
6083
- while (parentId) {
6084
- depth++;
6085
- parentId = graph.nodes.find((candidate) => candidate.id === parentId)?.parentId;
6086
- }
6087
- return depth;
6088
- }
6089
- function nodeKey(node) {
6090
- return node.data?.key || node.id;
6091
- }
6092
- function edgeSignature(edge) {
6093
- return [
6094
- edge.sourceId,
6095
- edge.data?.eventType,
6096
- edge.targetId,
6097
- edge.data?.guard?.type ?? ""
6098
- ].join("->");
6099
- }
6100
- function getStableNodeId(node, parentStableId, siblingIndex, preferredIds) {
6101
- const preferredId = preferredIds?.get(node.id);
6102
- if (preferredId !== void 0) return preferredId;
6103
- if (node.parentId === null) return "root";
6104
- const key = nodeKey(node);
6105
- const suffix = siblingIndex === 0 ? key : `${key}_${siblingIndex + 1}`;
6106
- return parentStableId ? `${parentStableId}.${suffix}` : suffix;
6107
- }
6108
- function getStableEdgeId(edge, siblingIndex, preferredIds) {
6109
- const preferredId = preferredIds?.get(edge.id);
6110
- if (preferredId !== void 0) return preferredId;
6111
- const suffix = siblingIndex === 0 ? "" : `.${siblingIndex + 1}`;
6112
- return edgeSignature(edge).concat(suffix);
6113
- }
6114
- function removePresentationFields(entity, kind) {
6115
- const mutableEntity = entity;
6116
- for (const key of LAYOUT_KEYS$1[kind]) delete mutableEntity[key];
6117
- const data = mutableEntity.data;
6118
- if (data && typeof data === "object" && !Array.isArray(data)) delete data.color;
6119
- }
6120
- /**
6121
- * Rewrites entity ids to structural ("semantic") ids so two graphs that model
6122
- * the same machine compare equal regardless of how their ids were minted.
6123
- *
6124
- * Invariant: node and edge array order is preserved exactly. Callers
6125
- * (`alignCompareIds` in the editor's /v1/diff route) rely on
6126
- * `normalized.nodes[i]` describing `graph.nodes[i]` to translate a semantic id
6127
- * back to the caller's original id. Never sort, filter, or reorder the arrays
6128
- * on the returned graph; sorting for traversal must be done on a copy, as the
6129
- * depth ordering below does.
6130
- *
6131
- * Presentation-only fields defined by `LAYOUT_KEYS`, plus entity data colors,
6132
- * are removed. When preferred ids are supplied, matching raw ids retain the
6133
- * base graph's semantic identity so a stable-id rename is reported as an
6134
- * update.
6135
- *
6136
- * Entity `data` is treated as optional throughout: sparse graphs (a node with
6137
- * `data: {}`, an edge with `data: null`) normalize instead of throwing.
6138
- */
6139
- function normalizeGraphForSemanticDiff(graph, preferredIds) {
6140
- const normalized = structuredClone(graph);
6141
- const nodeIdMap = /* @__PURE__ */ new Map();
6142
- const siblingKeys = /* @__PURE__ */ new Map();
6143
- const reservedNodeIds = new Set(preferredIds?.nodes.values());
6144
- const claimedNodeIds = /* @__PURE__ */ new Set();
6145
- const nodesByDepth = [...normalized.nodes].sort((a, b) => nodeDepth$1(normalized, a) - nodeDepth$1(normalized, b));
6146
- for (const node of nodesByDepth) {
6147
- const parentStableId = node.parentId ? nodeIdMap.get(node.parentId) ?? node.parentId : null;
6148
- const key = `${parentStableId ?? ""}:${nodeKey(node)}`;
6149
- const siblingIndex = siblingKeys.get(key) ?? 0;
6150
- siblingKeys.set(key, siblingIndex + 1);
6151
- const proposedId = getStableNodeId(node, parentStableId, siblingIndex, preferredIds?.nodes);
6152
- let stableId = proposedId;
6153
- let collisionIndex = 2;
6154
- while (!preferredIds?.nodes.has(node.id) && (reservedNodeIds.has(stableId) || claimedNodeIds.has(stableId))) stableId = `${proposedId}:unmatched:${collisionIndex++}`;
6155
- claimedNodeIds.add(stableId);
6156
- nodeIdMap.set(node.id, stableId);
6157
- }
6158
- for (const node of normalized.nodes) {
6159
- removePresentationFields(node, "node");
6160
- node.id = nodeIdMap.get(node.id) ?? node.id;
6161
- if (node.parentId) node.parentId = nodeIdMap.get(node.parentId) ?? node.parentId;
6162
- if (node.initialNodeId) node.initialNodeId = nodeIdMap.get(node.initialNodeId) ?? node.initialNodeId;
6163
- if (node.data?.initialId) node.data.initialId = nodeIdMap.get(node.data.initialId) ?? node.data.initialId;
6164
- }
6165
- const edgeSiblingKeys = /* @__PURE__ */ new Map();
6166
- const reservedEdgeIds = new Set(preferredIds?.edges.values());
6167
- const claimedEdgeIds = /* @__PURE__ */ new Set();
6168
- for (const edge of normalized.edges) {
6169
- removePresentationFields(edge, "edge");
6170
- edge.sourceId = nodeIdMap.get(edge.sourceId) ?? edge.sourceId;
6171
- edge.targetId = nodeIdMap.get(edge.targetId) ?? edge.targetId;
6172
- const key = edgeSignature(edge);
6173
- const siblingIndex = edgeSiblingKeys.get(key) ?? 0;
6174
- edgeSiblingKeys.set(key, siblingIndex + 1);
6175
- const proposedId = getStableEdgeId(edge, siblingIndex, preferredIds?.edges);
6176
- let stableId = proposedId;
6177
- let collisionIndex = 2;
6178
- while (!preferredIds?.edges.has(edge.id) && (reservedEdgeIds.has(stableId) || claimedEdgeIds.has(stableId))) stableId = `${proposedId}:unmatched:${collisionIndex++}`;
6179
- claimedEdgeIds.add(stableId);
6180
- edge.id = stableId;
6181
- }
6182
- if (normalized.initialNodeId) normalized.initialNodeId = nodeIdMap.get(normalized.initialNodeId) ?? normalized.initialNodeId;
6183
- return normalized;
6184
- }
6185
- function diffGraphsSemantically(baseGraph, compareGraph) {
6186
- const base = normalizeGraphForSemanticDiff(baseGraph);
6187
- const compareNodeIds = new Set(compareGraph.nodes.map((node) => node.id));
6188
- const compareEdgeIds = new Set(compareGraph.edges.map((edge) => edge.id));
6189
- const compare = normalizeGraphForSemanticDiff(compareGraph, {
6190
- nodes: new Map(baseGraph.nodes.map((node, index) => [node.id, base.nodes[index].id]).filter(([id]) => compareNodeIds.has(id))),
6191
- edges: new Map(baseGraph.edges.map((edge, index) => [edge.id, base.edges[index].id]).filter(([id]) => compareEdgeIds.has(id)))
6192
- });
6193
- const diff = getDiff(base, compare);
6194
- return {
6195
- base,
6196
- compare,
6197
- diff,
6198
- summary: summarizeGraphDiff(diff)
6579
+ module.exports.Type = require_type();
6580
+ module.exports.Schema = require_schema();
6581
+ module.exports.FAILSAFE_SCHEMA = require_failsafe();
6582
+ module.exports.JSON_SCHEMA = require_json();
6583
+ module.exports.CORE_SCHEMA = require_core();
6584
+ module.exports.DEFAULT_SCHEMA = require_default();
6585
+ module.exports.load = loader.load;
6586
+ module.exports.loadAll = loader.loadAll;
6587
+ module.exports.dump = dumper.dump;
6588
+ module.exports.YAMLException = require_exception();
6589
+ module.exports.types = {
6590
+ binary: require_binary(),
6591
+ float: require_float(),
6592
+ map: require_map(),
6593
+ null: require_null(),
6594
+ pairs: require_pairs(),
6595
+ set: require_set(),
6596
+ timestamp: require_timestamp(),
6597
+ bool: require_bool(),
6598
+ int: require_int(),
6599
+ merge: require_merge(),
6600
+ omap: require_omap(),
6601
+ seq: require_seq(),
6602
+ str: require_str()
6199
6603
  };
6200
- }
6604
+ module.exports.safeLoad = renamed("safeLoad", "load");
6605
+ module.exports.safeLoadAll = renamed("safeLoadAll", "loadAll");
6606
+ module.exports.safeDump = renamed("safeDump", "dump");
6607
+ })))(), 1);
6608
+ var { Type, Schema, FAILSAFE_SCHEMA, JSON_SCHEMA, CORE_SCHEMA, DEFAULT_SCHEMA, load, loadAll, dump, YAMLException, types, safeLoad, safeLoadAll, safeDump } = import_js_yaml.default;
6609
+ var index_vite_proxy_tmp_default = import_js_yaml.default;
6610
+
6611
+ //#endregion
6612
+ //#region ../graph-tools/dist/index.mjs
6201
6613
  /** A patch changes machine semantics rather than presentation metadata. */
6202
6614
  function isStructuralPatch(patch) {
6203
6615
  if (patch.op === "createImplementation" && !patch.implementation.code) return false;
@@ -10174,7 +10586,7 @@ function graphToXStateTSResult(inputGraph, options = {}) {
10174
10586
  if (xstateVersion === 6) stripStateSchemas(machineConfig);
10175
10587
  let machineExpr;
10176
10588
  if (hasSetup) {
10177
- const setupObj = buildSetupObject(schemas, impls, {
10589
+ const setupObj = buildSetupObject(graph, schemas, impls, {
10178
10590
  includeTypeSyntax: supportsTypeSyntax,
10179
10591
  xstateVersion,
10180
10592
  xstateV6SchemaFormat
@@ -10974,7 +11386,7 @@ function getActorSourceImports(graph) {
10974
11386
  }
10975
11387
  return imports;
10976
11388
  }
10977
- function buildSetupObject(schemas, impls, options) {
11389
+ function buildSetupObject(graph, schemas, impls, options) {
10978
11390
  const setup = {};
10979
11391
  if (options.xstateVersion === 6) {
10980
11392
  const schemasBlock = buildV6SchemasBlock(schemas, options);
@@ -10984,7 +11396,10 @@ function buildSetupObject(schemas, impls, options) {
10984
11396
  if (types) setup.types = types;
10985
11397
  }
10986
11398
  if (impls?.actions.length) setup.actions = buildActionsBlock(impls.actions, options);
10987
- if (impls?.guards.length) setup.guards = buildGuardsBlock(impls.guards, options);
11399
+ if (impls?.guards.length) setup.guards = buildGuardsBlock(impls.guards, {
11400
+ ...options,
11401
+ argsTypes: options.xstateVersion === 6 && options.includeTypeSyntax ? new Map(impls.guards.map((guard) => [guard.name, getV6GuardArgsType(graph, guard.name)])) : void 0
11402
+ });
10988
11403
  if (impls?.actors.length) setup.actors = buildActorsBlock(impls.actors, {
10989
11404
  ...options,
10990
11405
  xstateVersion: options.xstateVersion
@@ -11085,18 +11500,68 @@ function buildActionsBlock(actions, options) {
11085
11500
  } else block[implementation.name] = raw(options.xstateVersion === 6 ? `function () {\n // TODO: implement ${implementation.name}\n}` : `function ({ context, event }) {\n // TODO: implement ${implementation.name}\n}`);
11086
11501
  return block;
11087
11502
  }
11503
+ /**
11504
+ * XState v6 types `setup({ guards })` entries as `(...params: any[]) => boolean`
11505
+ * and injects nothing: a named guard receives exactly what its caller passes.
11506
+ * Generated transition functions call `guards[name]({ context, event }, params)`
11507
+ * (as does `createMachineFromConfig`), so that is the argument type to declare.
11508
+ */
11509
+ function getV6GuardArgsType(graph, guardName) {
11510
+ const schemas = graph.data.schemas;
11511
+ const rootContextType = contextSchemaToTSType(schemas?.context);
11512
+ const contexts = graph.edges.filter((edge) => !edge.data.temp && edge.data.guard?.type === guardName).map((edge) => {
11513
+ const types = [];
11514
+ let node = graph.nodes.find((candidate) => candidate.id === edge.sourceId);
11515
+ while (node) {
11516
+ const type = contextSchemaToTSType(node.data.schemas?.context);
11517
+ if (type) types.unshift(type);
11518
+ node = graph.nodes.find((candidate) => candidate.id === node?.parentId);
11519
+ }
11520
+ if (rootContextType) types.unshift(rootContextType);
11521
+ return types.length ? types.join(" & ") : void 0;
11522
+ });
11523
+ const contextType = contexts.some(Boolean) ? [...new Set(contexts.map((type) => type ?? "any"))].join(" | ") : rootContextType;
11524
+ const eventSchemas = {
11525
+ ...schemas?.events,
11526
+ ...schemas?.internalEvents
11527
+ };
11528
+ const declaredEventType = eventsSchemaToTSType(eventSchemas);
11529
+ if (!contextType && !declaredEventType) return void 0;
11530
+ const eventType = declaredEventType ? `${declaredEventType} | Exclude<import('xstate').ReservedMachineEvent, { type: ${Object.keys(eventSchemas).map((type) => serializeJS(type)).join(" | ")} }>` : "any";
11531
+ return `{ context: ${contextType ?? "any"}; event: ${eventType} }`;
11532
+ }
11533
+ /**
11534
+ * True when an authored guard expression destructures the generated
11535
+ * `{ context, event }` argument without annotating it, so contextual typing
11536
+ * can supply the types without contradicting an authored signature.
11537
+ */
11538
+ function usesGeneratedGuardArgs(code) {
11539
+ const expression = parseSingleExpression(code)?.expression;
11540
+ if (!expression || !ts.isArrowFunction(expression) && !ts.isFunctionExpression(expression)) return false;
11541
+ const [first] = expression.parameters;
11542
+ if (!first || first.type || !ts.isObjectBindingPattern(first.name)) return false;
11543
+ return first.name.elements.every((element) => {
11544
+ const property = element.propertyName ?? element.name;
11545
+ return !element.dotDotDotToken && ts.isIdentifier(property) && (property.text === "context" || property.text === "event");
11546
+ });
11547
+ }
11088
11548
  function buildGuardsBlock(guards, options) {
11089
11549
  const block = {};
11090
- for (const guard of guards) if (guard.code?.body) {
11091
- const inlineExpression = getInlineExpression$1(guard.code.body);
11092
- if (inlineExpression) {
11093
- block[guard.name] = raw(inlineExpression, guard.code);
11094
- continue;
11095
- }
11096
- const params = hasSchemaProperties(guard.paramsSchema) ? options.includeTypeSyntax ? `, params: ${jsonSchemaToTSType(guard.paramsSchema)}` : ", params" : "";
11097
- const body = dedent(guard.code.body);
11098
- block[guard.name] = rawWithSourceSlice(`function ({ context, event }${params ? params : ""}) {\n `, body, "\n}", guard.code);
11099
- } else block[guard.name] = raw(`function ({ context, event }) {\n // TODO: implement ${guard.name}\n return false;\n}`);
11550
+ for (const guard of guards) {
11551
+ const argsType = options.argsTypes?.get(guard.name);
11552
+ const argsAnnotation = argsType ? `: ${argsType}` : "";
11553
+ const paramsType = hasSchemaProperties(guard.paramsSchema) ? jsonSchemaToTSType(guard.paramsSchema) : void 0;
11554
+ if (guard.code?.body) {
11555
+ const inlineExpression = getInlineExpression$1(guard.code.body);
11556
+ if (inlineExpression) {
11557
+ block[guard.name] = argsType && usesGeneratedGuardArgs(inlineExpression) ? rawWithSourceSlice("(", inlineExpression, `) satisfies (args: ${argsType}, params: ${paramsType ?? "any"}) => boolean`, guard.code) : raw(inlineExpression, guard.code);
11558
+ continue;
11559
+ }
11560
+ const params = paramsType ? options.includeTypeSyntax ? `, params: ${paramsType}` : ", params" : "";
11561
+ const body = dedent(guard.code.body);
11562
+ block[guard.name] = rawWithSourceSlice(`function ({ context, event }${argsAnnotation}${params}) {\n `, body, "\n}", guard.code);
11563
+ } else block[guard.name] = raw(`function ({ context, event }${argsAnnotation}) {\n // TODO: implement ${guard.name}\n return false;\n}`);
11564
+ }
11100
11565
  return block;
11101
11566
  }
11102
11567
  function buildActorsBlock(actors, options) {
@@ -13344,10 +13809,10 @@ function modify(text, path, value, options) {
13344
13809
  }
13345
13810
 
13346
13811
  //#endregion
13347
- //#region ../editor-sync/dist/adapters-Dt77BFUa.mjs
13812
+ //#region ../editor-sync/dist/adapters-9XXzVfbF.mjs
13348
13813
  const sourceFileUris = /* @__PURE__ */ new WeakMap();
13349
13814
  function collectXStateSourceLocations(sourceText, document, options = {}) {
13350
- const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2$1(document.fileName));
13815
+ const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2(document.fileName));
13351
13816
  const uri = document.uri?.toString() ?? document.fileName;
13352
13817
  sourceFileUris.set(sourceFile, uri);
13353
13818
  const bindings = collectBindings(sourceFile, uri, document.fileName, options);
@@ -13359,10 +13824,13 @@ function collectXStateSourceLocations(sourceText, document, options = {}) {
13359
13824
  const states = [];
13360
13825
  const statesObjectLocations = [];
13361
13826
  collectStateLocations(root, [], states, statesObjectLocations, bindings, staticValues, root);
13362
- const packageProfile = detectXStatePackageProfile(options.resolver?.resolvePackageJson?.({
13827
+ const packageProfile = detectXStatePackageProfile(machineFactoryPackage(sourceFile, {
13828
+ fileName: document.fileName,
13829
+ uri
13830
+ }, options.machineIndex, options.resolver) === "xstate" ? options.resolver?.resolvePackageJson?.({
13363
13831
  fileName: document.fileName,
13364
13832
  uri
13365
- }, "xstate"));
13833
+ }, "xstate") : void 0);
13366
13834
  const rootProfile = detectXStateSourceProfile(sourceText);
13367
13835
  const referencedStateSymbols = new Set([...states, ...statesObjectLocations].map((state) => state.symbol).filter((symbol) => typeof symbol === "string"));
13368
13836
  const resolvedTransitionFunctions = collectResolvedTransitionFunctions(root, bindings);
@@ -13390,6 +13858,104 @@ function collectXStateSourceLocations(sourceText, document, options = {}) {
13390
13858
  }])
13391
13859
  };
13392
13860
  }
13861
+ /** Only the package providing this machine's factory can establish its version. */
13862
+ function machineFactoryPackage(sourceFile, document, machineIndex = 0, resolver) {
13863
+ const modules = /* @__PURE__ */ new Map();
13864
+ const rootModule = {
13865
+ file: sourceFile,
13866
+ document
13867
+ };
13868
+ modules.set(document.uri, rootModule);
13869
+ const visited = /* @__PURE__ */ new Set();
13870
+ function imported(module, specifier, name) {
13871
+ if (!specifier.startsWith(".")) return specifier;
13872
+ const snapshot = resolver?.resolveImport(module.document, specifier);
13873
+ if (!snapshot || !name) return void 0;
13874
+ let target = modules.get(snapshot.uri);
13875
+ if (!target) {
13876
+ target = {
13877
+ file: ts$1.createSourceFile(snapshot.fileName, snapshot.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(snapshot.fileName)),
13878
+ document: snapshot
13879
+ };
13880
+ modules.set(snapshot.uri, target);
13881
+ }
13882
+ return exported(target, name);
13883
+ }
13884
+ function exported(module, name) {
13885
+ const key = `${module.document.uri}\0export\0${name}`;
13886
+ if (visited.has(key)) return void 0;
13887
+ visited.add(key);
13888
+ try {
13889
+ for (const statement of module.file.statements) {
13890
+ if (!ts$1.isExportDeclaration(statement) || !statement.exportClause || !ts$1.isNamedExports(statement.exportClause)) continue;
13891
+ const element = statement.exportClause.elements.find((element) => element.name.text === name);
13892
+ if (!element) continue;
13893
+ const localName = element.propertyName?.text ?? element.name.text;
13894
+ return statement.moduleSpecifier && ts$1.isStringLiteral(statement.moduleSpecifier) ? imported(module, statement.moduleSpecifier.text, localName) : symbol(module, localName);
13895
+ }
13896
+ for (const statement of module.file.statements) if (ts$1.isVariableStatement(statement) && statement.modifiers?.some((modifier) => modifier.kind === ts$1.SyntaxKind.ExportKeyword)) {
13897
+ if (statement.declarationList.declarations.some((declaration) => ts$1.isIdentifier(declaration.name) && declaration.name.text === name)) return symbol(module, name);
13898
+ }
13899
+ for (const statement of module.file.statements) {
13900
+ if (!ts$1.isExportDeclaration(statement) || statement.exportClause || !statement.moduleSpecifier || !ts$1.isStringLiteral(statement.moduleSpecifier)) continue;
13901
+ const result = imported(module, statement.moduleSpecifier.text, name);
13902
+ if (result) return result;
13903
+ }
13904
+ return;
13905
+ } finally {
13906
+ visited.delete(key);
13907
+ }
13908
+ }
13909
+ function symbol(module, name, member) {
13910
+ const key = `${module.document.uri}\0symbol\0${name}\0${member ?? ""}`;
13911
+ if (visited.has(key)) return void 0;
13912
+ visited.add(key);
13913
+ try {
13914
+ for (const statement of module.file.statements) {
13915
+ if (ts$1.isImportDeclaration(statement) && ts$1.isStringLiteral(statement.moduleSpecifier)) {
13916
+ const bindings = statement.importClause?.namedBindings;
13917
+ if (bindings && ts$1.isNamedImports(bindings)) {
13918
+ const element = bindings.elements.find((element) => element.name.text === name);
13919
+ if (element) return imported(module, statement.moduleSpecifier.text, element.propertyName?.text ?? element.name.text);
13920
+ } else if (bindings && ts$1.isNamespaceImport(bindings) && bindings.name.text === name) return imported(module, statement.moduleSpecifier.text, member);
13921
+ }
13922
+ if (!ts$1.isVariableStatement(statement)) continue;
13923
+ for (const declaration of statement.declarationList.declarations) {
13924
+ if (!declaration.initializer) continue;
13925
+ if (ts$1.isIdentifier(declaration.name) && declaration.name.text === name) return resolve(module, declaration.initializer, member);
13926
+ if (ts$1.isObjectBindingPattern(declaration.name)) {
13927
+ const element = declaration.name.elements.find((element) => !element.dotDotDotToken && ts$1.isIdentifier(element.name) && element.name.text === name);
13928
+ if (element) return resolve(module, declaration.initializer, element.propertyName ? element.propertyName.getText(module.file).replace(/^['"]|['"]$/g, "") : name);
13929
+ }
13930
+ }
13931
+ }
13932
+ return;
13933
+ } finally {
13934
+ visited.delete(key);
13935
+ }
13936
+ }
13937
+ function resolve(module, node, member) {
13938
+ if (ts$1.isIdentifier(node)) return symbol(module, node.text, member);
13939
+ if (ts$1.isCallExpression(node)) {
13940
+ if (ts$1.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1 && ts$1.isStringLiteral(node.arguments[0])) return imported(module, node.arguments[0].text, member);
13941
+ return resolve(module, node.expression);
13942
+ }
13943
+ if (ts$1.isPropertyAccessExpression(node)) return resolve(module, node.expression, node.name.text);
13944
+ if (ts$1.isParenthesizedExpression(node) || ts$1.isAsExpression(node) || ts$1.isSatisfiesExpression(node) || ts$1.isNonNullExpression(node)) return resolve(module, node.expression, member);
13945
+ }
13946
+ let factory;
13947
+ let index = 0;
13948
+ const visit = (node) => {
13949
+ if (factory) return;
13950
+ if (ts$1.isCallExpression(node) && isCreateMachineExpression$1(node.expression)) {
13951
+ if (index++ === machineIndex) factory = node;
13952
+ return;
13953
+ }
13954
+ ts$1.forEachChild(node, visit);
13955
+ };
13956
+ visit(sourceFile);
13957
+ return factory ? resolve(rootModule, factory) : void 0;
13958
+ }
13393
13959
  function detectXStatePackageProfile(packageJson) {
13394
13960
  if (!packageJson) return void 0;
13395
13961
  let manifest;
@@ -13462,7 +14028,7 @@ function parseRangePart(value) {
13462
14028
  }
13463
14029
  function collectImportedXStateTransitionSources(sourceText, document, options = {}) {
13464
14030
  if (!options.resolver) return [];
13465
- const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2$1(document.fileName));
14031
+ const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2(document.fileName));
13466
14032
  const uri = document.uri?.toString() ?? document.fileName;
13467
14033
  sourceFileUris.set(sourceFile, uri);
13468
14034
  const bindings = collectBindings(sourceFile, uri, document.fileName, options);
@@ -13553,7 +14119,7 @@ function resolveExportedStaticValue(document, exportName, resolver, visited) {
13553
14119
  if (visited.has(visitKey)) return null;
13554
14120
  visited.add(visitKey);
13555
14121
  try {
13556
- const sourceFile = ts$1.createSourceFile(document.fileName, document.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2$1(document.fileName));
14122
+ const sourceFile = ts$1.createSourceFile(document.fileName, document.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(document.fileName));
13557
14123
  sourceFileUris.set(sourceFile, document.uri);
13558
14124
  const localBindings = collectStaticValueBindings(sourceFile);
13559
14125
  const localBinding = localBindings.get(exportName);
@@ -13691,7 +14257,7 @@ function addImportedBindings(bindings, sourceFile, uri, fileName, options, cache
13691
14257
  uri
13692
14258
  }, statement.moduleSpecifier.text);
13693
14259
  if (!importedDocument) continue;
13694
- const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2$1(importedDocument.fileName));
14260
+ const importedSourceFile = ts$1.createSourceFile(importedDocument.fileName, importedDocument.text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(importedDocument.fileName));
13695
14261
  sourceFileUris.set(importedSourceFile, importedDocument.uri);
13696
14262
  const importedBindings = collectBindings(importedSourceFile, importedDocument.uri, importedDocument.fileName, options, cache);
13697
14263
  for (const element of namedBindings.elements) {
@@ -13762,7 +14328,7 @@ function findMachineConfigObjectLiteral$1(sourceFile, bindings, machineIndex = 0
13762
14328
  let currentMachineIndex = 0;
13763
14329
  const visit = (node) => {
13764
14330
  if (found) return;
13765
- if (ts$1.isCallExpression(node) && isCreateMachineExpression$1$1(node.expression)) {
14331
+ if (ts$1.isCallExpression(node) && isCreateMachineExpression$1(node.expression)) {
13766
14332
  const isSelectedMachine = currentMachineIndex === machineIndex;
13767
14333
  currentMachineIndex += 1;
13768
14334
  if (!isSelectedMachine) return;
@@ -14030,7 +14596,7 @@ function getEnumMemberName(name) {
14030
14596
  if (ts$1.isIdentifier(name) || ts$1.isStringLiteral(name) || ts$1.isNumericLiteral(name)) return name.text;
14031
14597
  return null;
14032
14598
  }
14033
- function isCreateMachineExpression$1$1(expression) {
14599
+ function isCreateMachineExpression$1(expression) {
14034
14600
  const isMachineFactoryName = (name) => name === "createMachine" || name === "createMachineFromConfig";
14035
14601
  return ts$1.isIdentifier(expression) && isMachineFactoryName(expression.text) || ts$1.isPropertyAccessExpression(expression) && isMachineFactoryName(expression.name.text);
14036
14602
  }
@@ -14066,7 +14632,7 @@ function toSourceRange$1(sourceFile, node) {
14066
14632
  endChar: end.character
14067
14633
  };
14068
14634
  }
14069
- function getScriptKind$2$1(fileName) {
14635
+ function getScriptKind$2(fileName) {
14070
14636
  if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
14071
14637
  if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
14072
14638
  if (fileName.endsWith(".js")) return ts$1.ScriptKind.JS;
@@ -14121,7 +14687,14 @@ function parseSourceToMachine(text, document, options = {}) {
14121
14687
  ].filter(removeOverlappingRanges).sort(compareSourceRangesDescending));
14122
14688
  if (sourceLocations.profile === "xstate-v6-alpha" || options.machineIndex !== void 0) {
14123
14689
  const importedTransitionSources = collectImportedXStateTransitionSources(text, document, options);
14124
- const parseText = importedTransitionSources.length ? `${hydratedText}\n${importedTransitionSources.join("\n")}` : hydratedText;
14690
+ const transitionDeclarations = ts$1.createSourceFile(document.fileName, importedTransitionSources.join("\n"), ts$1.ScriptTarget.Latest, true, ts$1.ScriptKind.TSX);
14691
+ const transitionSymbols = /* @__PURE__ */ new Set();
14692
+ for (const statement of transitionDeclarations.statements) if (ts$1.isFunctionDeclaration(statement) && statement.name) transitionSymbols.add(statement.name.text);
14693
+ else if (ts$1.isVariableStatement(statement)) {
14694
+ for (const declaration of statement.declarationList.declarations) if (ts$1.isIdentifier(declaration.name)) transitionSymbols.add(declaration.name.text);
14695
+ }
14696
+ const moduleText = applySourceRangeReplacements(hydratedText, collectHydratedImportReplacements(hydratedText, document, transitionSymbols).sort(compareSourceRangesDescending));
14697
+ const parseText = importedTransitionSources.length ? `${moduleText}\n${importedTransitionSources.join("\n")}` : moduleText;
14125
14698
  const selectedMachineIndex = options.machineIndex ?? 0;
14126
14699
  const config = extractXStateMachineConfigEntries(parseText, { profile: sourceLocations.profile }).find((entry) => entry.machineIndex === selectedMachineIndex)?.config;
14127
14700
  if (!config) throw new Error(`No machine at index ${selectedMachineIndex} was found in ${document.fileName}.`);
@@ -14139,17 +14712,38 @@ function parseSourceToMachine(text, document, options = {}) {
14139
14712
  setupImplementations[category] = Object.fromEntries(Object.entries(value).map(([name, body]) => [name, typeof body === "string" ? raw(body) : body]));
14140
14713
  delete machineConfig[category];
14141
14714
  }
14142
- if (Object.keys(setupImplementations).length) return `import { setup } from "xstate";
14143
-
14144
- export const machine = setup(${serializeJS(setupImplementations, 0)}).createMachine(${serializeJS(machineConfig, 0)});
14145
- `;
14146
- return `import { createMachine } from "xstate";
14147
-
14148
- export const machine = createMachine(${serializeJS(machineConfig, 0)});
14149
- `;
14715
+ const factory = Object.keys(setupImplementations).length ? "setup" : "createMachine";
14716
+ return selectMachineInModule(parseText, selectedMachineIndex, factory, factory === "setup" ? `setup(${serializeJS(setupImplementations, 0)}).createMachine(${serializeJS(machineConfig, 0)})` : `createMachine(${serializeJS(machineConfig, 0)})`);
14150
14717
  }
14151
14718
  return hydratedText;
14152
14719
  }
14720
+ function selectMachineInModule(source, machineIndex, factory, expression) {
14721
+ const file = ts$1.createSourceFile("machine.ts", source, ts$1.ScriptTarget.Latest, true, ts$1.ScriptKind.TSX);
14722
+ const replacements = [];
14723
+ let index = 0;
14724
+ let factoryAlias = "__stately_sync_factory";
14725
+ while (source.includes(factoryAlias)) factoryAlias += "_";
14726
+ const visit = (node) => {
14727
+ if (ts$1.isCallExpression(node) && isCreateMachineExpression$1(node.expression)) {
14728
+ if (index++ === machineIndex) replacements.push({
14729
+ start: node.getStart(file),
14730
+ end: node.getEnd(),
14731
+ text: `(() => { const ${factory} = ${factoryAlias}; return ${expression}; })()`
14732
+ });
14733
+ else replacements.push({
14734
+ start: node.expression.getStart(file),
14735
+ end: node.expression.getEnd(),
14736
+ text: `(${node.expression.getText(file)})`
14737
+ });
14738
+ return;
14739
+ }
14740
+ ts$1.forEachChild(node, visit);
14741
+ };
14742
+ visit(file);
14743
+ let result = source;
14744
+ for (const replacement of replacements.sort((a, b) => b.start - a.start)) result = result.slice(0, replacement.start) + replacement.text + result.slice(replacement.end);
14745
+ return `import { ${factory} as ${factoryAlias} } from 'xstate';\n${result}`;
14746
+ }
14153
14747
  function readHydratedStatesObject(uri, range, states, resolver) {
14154
14748
  if (!resolver) return null;
14155
14749
  const document = resolver.read(uri);
@@ -14188,11 +14782,13 @@ function collectHydratedImportReplacements(text, document, hydratedSymbols) {
14188
14782
  if (!statement.moduleSpecifier.text.startsWith(".")) continue;
14189
14783
  const namedBindings = statement.importClause?.namedBindings;
14190
14784
  if (!namedBindings || !ts$1.isNamedImports(namedBindings)) continue;
14191
- const importedNames = namedBindings.elements.map((element) => element.name.text);
14192
- if (importedNames.length === 0 || !importedNames.every((name) => hydratedSymbols.has(name))) continue;
14785
+ const remaining = namedBindings.elements.filter((element) => !hydratedSymbols.has(element.name.text));
14786
+ if (remaining.length === namedBindings.elements.length) continue;
14787
+ const clause = statement.importClause;
14788
+ const replacement = remaining.length || clause.name ? ts$1.createPrinter().printNode(ts$1.EmitHint.Unspecified, ts$1.factory.updateImportDeclaration(statement, statement.modifiers, ts$1.factory.updateImportClause(clause, clause.isTypeOnly, clause.name, remaining.length ? ts$1.factory.updateNamedImports(namedBindings, remaining) : void 0), statement.moduleSpecifier, statement.attributes), sourceFile) + "\n" : "";
14193
14789
  replacements.push({
14194
14790
  range: toSourceRange(sourceFile, statement, { includeTrailingNewline: true }),
14195
- newText: ""
14791
+ newText: replacement
14196
14792
  });
14197
14793
  }
14198
14794
  return replacements;
@@ -18434,7 +19030,7 @@ function diagnosticKey$1(diagnostic) {
18434
19030
  }
18435
19031
 
18436
19032
  //#endregion
18437
- //#region ../editor-sync/dist/localBackend-DH1qEsOz.mjs
19033
+ //#region ../editor-sync/dist/localBackend-CAt5onqr.mjs
18438
19034
  var LocalEditorSyncBackend = class {
18439
19035
  constructor(access) {
18440
19036
  this.access = access;
@@ -18882,7 +19478,7 @@ async function updateMachineDefinition(client, machineId, localGraph) {
18882
19478
  }
18883
19479
 
18884
19480
  //#endregion
18885
- //#region ../editor-sync/dist/presentationMetadata-YX6iRP4T.mjs
19481
+ //#region ../editor-sync/dist/presentationMetadata-CIXaHmqN.mjs
18886
19482
  function createStudioPresentationMetadataStore(client, machineId) {
18887
19483
  const load = async () => {
18888
19484
  return {
@@ -19248,6 +19844,54 @@ function isPresentationOverlay(value) {
19248
19844
  function clone(value) {
19249
19845
  return value === void 0 ? value : structuredClone(value);
19250
19846
  }
19847
+ /**
19848
+ * Apply only what an automatic layout computed to the last persisted graph:
19849
+ * the completed layout status of each newly laid-out region and the positions
19850
+ * of entities inside those regions. Colors, notes, assets, display choices and
19851
+ * manual moves the user has not saved stay as they were persisted.
19852
+ */
19853
+ function mergeAutomaticLayout(persisted, laidOut) {
19854
+ if (!isGraph(persisted) || !isGraph(laidOut)) return persisted;
19855
+ const result = clone(persisted);
19856
+ const laidOutNodes = new Map(laidOut.nodes.map((node) => [node.id, node]));
19857
+ const regions = /* @__PURE__ */ new Set();
19858
+ if (laidOut.layout?.status === "complete" && result.layout?.status !== "complete") {
19859
+ regions.add(null);
19860
+ result.layout = {
19861
+ ...result.layout,
19862
+ ...clone(laidOut.layout)
19863
+ };
19864
+ }
19865
+ for (const node of result.nodes) if (laidOutNodes.get(node.id)?.data?.layout?.status === "complete" && node.data?.layout?.status !== "complete") {
19866
+ regions.add(node.id);
19867
+ node.data = {
19868
+ ...node.data,
19869
+ layout: {
19870
+ ...node.data?.layout,
19871
+ status: "complete"
19872
+ }
19873
+ };
19874
+ }
19875
+ if (regions.size === 0) return result;
19876
+ const parentOf = new Map(result.nodes.map((node) => [node.id, node.parentId ?? null]));
19877
+ const inRegion = (nodeId) => typeof nodeId === "string" && parentOf.has(nodeId) && regions.has(parentOf.get(nodeId));
19878
+ const copyPosition = (target, source) => {
19879
+ if (!source) return;
19880
+ for (const field of [
19881
+ "position",
19882
+ "x",
19883
+ "y"
19884
+ ]) if (source[field] !== void 0) target[field] = clone(source[field]);
19885
+ };
19886
+ for (const node of result.nodes) if (inRegion(node.id)) copyPosition(node, laidOutNodes.get(node.id));
19887
+ const laidOutEdges = new Map(laidOut.edges.map((edge) => [edge.id, edge]));
19888
+ for (const edge of result.edges) if (inRegion(edge.sourceId) || inRegion(edge.targetId)) copyPosition(edge, laidOutEdges.get(edge.id));
19889
+ if (Array.isArray(result.annotations) && Array.isArray(laidOut.annotations)) {
19890
+ const laidOutAnnotations = new Map(laidOut.annotations.map((item) => [item.id, item]));
19891
+ for (const annotation of result.annotations) if (regions.has(annotation.parentId ?? null)) copyPosition(annotation, laidOutAnnotations.get(annotation.id));
19892
+ }
19893
+ return result;
19894
+ }
19251
19895
 
19252
19896
  //#endregion
19253
19897
  //#region ../../node_modules/.pnpm/@asteasolutions+zod-to-openapi@8.5.0_zod@4.4.3/node_modules/@asteasolutions/zod-to-openapi/dist/index.cjs
@@ -19681,7 +20325,7 @@ const editorSyncApplyResponseSchema = z$1.object({ replacements: z$1.array(edito
19681
20325
  const editorSyncAccessResponseSchema = z$1.object({ access: editorSyncAccessSchema });
19682
20326
 
19683
20327
  //#endregion
19684
- //#region ../editor-sync/dist/host-BX7mLyHj.mjs
20328
+ //#region ../editor-sync/dist/host-DCVLN7MS.mjs
19685
20329
  let diagnosticSequence = 0;
19686
20330
  function presentationsEqual(first, second) {
19687
20331
  return JSON.stringify(createPresentationSnapshot(first)) === JSON.stringify(createPresentationSnapshot(second));
@@ -19858,6 +20502,7 @@ var EditorHostSession = class {
19858
20502
  this.currentGraph = message.graph;
19859
20503
  if (message.source === "external") this.lastPersistedGraph = message.sourceBaselineGraph ?? message.graph;
19860
20504
  this.currentSourceLocations = mergeSourceLocations(this.currentSourceLocations, message.sourceLocations);
20505
+ if (message.source === "layout") await this.persistAutomaticLayout(message.graph, message.serialized);
19861
20506
  break;
19862
20507
  case "@statelyai.save":
19863
20508
  try {
@@ -20088,6 +20733,27 @@ var EditorHostSession = class {
20088
20733
  return graph;
20089
20734
  }
20090
20735
  }
20736
+ /**
20737
+ * Store automatic layout positions as presentation metadata so the next
20738
+ * session skips automatic layout. Unsaved colors, notes or moves are never
20739
+ * committed this way: such a graph waits for the user's save. Source files are never written, and a graph with unsaved structural edits
20740
+ * waits for the user's save instead.
20741
+ */
20742
+ async persistAutomaticLayout(graph, serialized) {
20743
+ const persisted = this.lastPersistedGraph;
20744
+ if (!this.options.presentation || this.access?.readOnly || persisted === void 0) return;
20745
+ try {
20746
+ const change = graphChangeToPatchesResult(persisted, graph);
20747
+ if (change.unsupportedChanges.length > 0 || change.patches.some(isStructuralPatch)) return;
20748
+ } catch {
20749
+ return;
20750
+ }
20751
+ if (!presentationsEqual(mergeAutomaticLayout(persisted, graph), graph)) return;
20752
+ try {
20753
+ await this.options.presentation.persist(graph, { serialized: serialized?.digraph !== void 0 ? serialized : await this.getSerializedPayload(serialized) });
20754
+ this.lastPersistedGraph = graph;
20755
+ } catch {}
20756
+ }
20091
20757
  async persistPresentation(graph, serialized) {
20092
20758
  if (!this.options.presentation) return;
20093
20759
  try {
@@ -20648,7 +21314,7 @@ function sourceLocationUris(sourceLocations) {
20648
21314
 
20649
21315
  //#endregion
20650
21316
  //#region src/version.ts
20651
- const CLI_VERSION = "0.12.8";
21317
+ const CLI_VERSION = "0.14.0";
20652
21318
 
20653
21319
  //#endregion
20654
21320
  //#region src/cliHost.ts
@@ -21100,7 +21766,7 @@ async function openSourceDocument(fileName) {
21100
21766
  };
21101
21767
  }
21102
21768
  function collectRelativeImportSpecifiers(text, fileName) {
21103
- const sourceFile = ts$1.createSourceFile(fileName, text, ts$1.ScriptTarget.Latest, true, getScriptKind$2(fileName));
21769
+ const sourceFile = ts$1.createSourceFile(fileName, text, ts$1.ScriptTarget.Latest, true, getScriptKind$1(fileName));
21104
21770
  const specifiers = [];
21105
21771
  for (const statement of sourceFile.statements) {
21106
21772
  const moduleSpecifier = ts$1.isImportDeclaration(statement) || ts$1.isExportDeclaration(statement) ? statement.moduleSpecifier : void 0;
@@ -21127,7 +21793,7 @@ function resolveRelativeImportCandidates(fromFileName, specifier) {
21127
21793
  ...importExtensions.map((extension) => path$1.join(basePath, `index${extension}`))
21128
21794
  ];
21129
21795
  }
21130
- function getScriptKind$2(fileName) {
21796
+ function getScriptKind$1(fileName) {
21131
21797
  if (fileName.endsWith(".tsx")) return ts$1.ScriptKind.TSX;
21132
21798
  if (fileName.endsWith(".jsx")) return ts$1.ScriptKind.JSX;
21133
21799
  if (/\.(?:c|m)?js$/.test(fileName)) return ts$1.ScriptKind.JS;
@@ -21650,106 +22316,7 @@ function describeCredentialBackend(backend, location) {
21650
22316
  }
21651
22317
 
21652
22318
  //#endregion
21653
- //#region ../editor-sync/dist/statelyPragma-D46lczJR.mjs
21654
- function findStatelyPragmaAttachments(sourceText, fileName = "machine.ts") {
21655
- const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$1(fileName));
21656
- const attachments = [];
21657
- const visit = (node) => {
21658
- if (ts.isCallExpression(node) && isCreateMachineExpression$1(node.expression)) {
21659
- const attachNode = findAttachNode(node);
21660
- if (attachNode) attachments.push({
21661
- machineStart: getMachineExpressionStart(node.expression, sourceFile),
21662
- attachStart: attachNode.getStart(sourceFile),
21663
- pragma: findAttachedStatelyPragma(sourceText, attachNode)
21664
- });
21665
- }
21666
- ts.forEachChild(node, visit);
21667
- };
21668
- visit(sourceFile);
21669
- return attachments;
21670
- }
21671
- function getStatelyPragma(sourceText, fileName = "machine.ts", machineIndex = 0) {
21672
- return findStatelyPragmaAttachments(sourceText, fileName)[machineIndex]?.pragma;
21673
- }
21674
- function upsertStatelyPragma(sourceText, id, options = {}) {
21675
- const attachment = findStatelyPragmaAttachments(sourceText, options.fileName ?? "machine.ts")[options.machineIndex ?? 0];
21676
- if (!attachment) return sourceText;
21677
- const lineEnding = options.lineEnding ?? detectLineEnding(sourceText);
21678
- const canonicalComment = `// @statelyai id=${id}`;
21679
- if (attachment.pragma) return sourceText.slice(0, attachment.pragma.start) + canonicalComment + sourceText.slice(attachment.pragma.end);
21680
- const insertText = `${getIndentationAtOffset(sourceText, attachment.attachStart)}${canonicalComment}${lineEnding}`;
21681
- return sourceText.slice(0, attachment.attachStart) + insertText + sourceText.slice(attachment.attachStart);
21682
- }
21683
- function findAttachedStatelyPragma(sourceText, node) {
21684
- const comments = ts.getLeadingCommentRanges(sourceText, node.getFullStart()) ?? [];
21685
- for (let index = comments.length - 1; index >= 0; index -= 1) {
21686
- const comment = comments[index];
21687
- const parsed = parseStatelyPragma(sourceText, comment.pos, comment.end);
21688
- if (parsed) return parsed;
21689
- }
21690
- }
21691
- function parseStatelyPragma(sourceText, start, end) {
21692
- const match = normalizeCommentContent(sourceText.slice(start, end)).match(/^@statelyai(?:\s+(.*))?$/s);
21693
- if (!match) return;
21694
- const fields = parseFields(match[1] ?? "");
21695
- return {
21696
- tag: "@statelyai",
21697
- id: fields.id,
21698
- fields,
21699
- start,
21700
- end
21701
- };
21702
- }
21703
- function normalizeCommentContent(rawComment) {
21704
- if (rawComment.startsWith("//")) return rawComment.slice(2).trim();
21705
- if (rawComment.startsWith("/*")) return rawComment.slice(2, rawComment.endsWith("*/") ? -2 : void 0).split(/\r?\n/).map((line) => line.replace(/^\s*\*\s?/, "")).join(" ").trim();
21706
- return rawComment.trim();
21707
- }
21708
- function parseFields(input) {
21709
- const fields = {};
21710
- for (const token of input.split(/\s+/)) {
21711
- if (!token) continue;
21712
- const equalsIndex = token.indexOf("=");
21713
- if (equalsIndex <= 0) continue;
21714
- const key = token.slice(0, equalsIndex);
21715
- const value = token.slice(equalsIndex + 1);
21716
- if (!key || !value) continue;
21717
- fields[key] = value;
21718
- }
21719
- return fields;
21720
- }
21721
- function findAttachNode(node) {
21722
- let current = node;
21723
- while (current?.parent) {
21724
- if (ts.isVariableStatement(current.parent) || ts.isExpressionStatement(current.parent) || ts.isExportAssignment(current.parent)) return current.parent;
21725
- current = current.parent;
21726
- }
21727
- return current;
21728
- }
21729
- function isCreateMachineExpression$1(expression) {
21730
- const isMachineFactoryName = (name) => name === "createMachine" || name === "createMachineFromConfig";
21731
- return ts.isIdentifier(expression) && isMachineFactoryName(expression.text) || ts.isPropertyAccessExpression(expression) && isMachineFactoryName(expression.name.text);
21732
- }
21733
- function getMachineExpressionStart(expression, sourceFile) {
21734
- if (ts.isPropertyAccessExpression(expression)) return expression.name.getStart(sourceFile);
21735
- return expression.getStart(sourceFile);
21736
- }
21737
- function getIndentationAtOffset(sourceText, offset) {
21738
- const lineStart = sourceText.lastIndexOf("\n", Math.max(0, offset - 1)) + 1;
21739
- return sourceText.slice(lineStart, offset).match(/^[ \t]*/)?.[0] ?? "";
21740
- }
21741
- function detectLineEnding(sourceText) {
21742
- return sourceText.includes("\r\n") ? "\r\n" : "\n";
21743
- }
21744
- function getScriptKind$1(fileName) {
21745
- if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
21746
- if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
21747
- if (fileName.endsWith(".js")) return ts.ScriptKind.JS;
21748
- return ts.ScriptKind.TS;
21749
- }
21750
-
21751
- //#endregion
21752
- //#region ../../node_modules/.pnpm/@statelyai+graph@2.1.0_@statelyai+layout@0.4.2_cytoscape@3.34.0_d3-force@3.0.0_d3-hiera_48d6687797f6ba8a844c48e50eec3ae1/node_modules/@statelyai/graph/dist/index.mjs
22319
+ //#region ../../node_modules/.pnpm/@statelyai+graph@2.1.0_@statelyai+layout@0.4.3_cytoscape@3.34.0_d3-force@3.0.0_d3-hiera_2afdfa88fae2fa10712f99651d0f58d6/node_modules/@statelyai/graph/dist/index.mjs
21753
22320
  const LAYOUT_KEYS = {
21754
22321
  node: [
21755
22322
  "x",
@@ -22200,7 +22767,7 @@ function fromXStateConfig(config) {
22200
22767
  };
22201
22768
  }
22202
22769
  async function pushLocalMachineLinks(options) {
22203
- const client = options.client ?? createStatelyClient({
22770
+ const client = options.client ?? createStatelyClient$1({
22204
22771
  apiKey: options.apiKey,
22205
22772
  baseUrl: options.baseUrl,
22206
22773
  fetch: options.fetch
@@ -22297,7 +22864,7 @@ async function resolveLocalFile(locator, options, machineIndex = 0) {
22297
22864
  let config = localExtraction.machines[0]?.config;
22298
22865
  const extractionIssue = localExtraction.error;
22299
22866
  if (!config) {
22300
- const extracted = await (options.client ?? createStatelyClient({
22867
+ const extracted = await (options.client ?? createStatelyClient$1({
22301
22868
  apiKey: options.apiKey,
22302
22869
  baseUrl: options.baseUrl,
22303
22870
  fetch: options.fetch
@@ -22369,7 +22936,7 @@ function inferXStateTargetLanguage(targetPath) {
22369
22936
  return /\.(?:c|m)?jsx?$/i.test(targetPath) ? "javascript" : "typescript";
22370
22937
  }
22371
22938
  async function resolveRemoteMachine(machineId, baseUrl, kind, locator, options) {
22372
- const digraph = (await (options.client ?? createStatelyClient({
22939
+ const digraph = (await (options.client ?? createStatelyClient$1({
22373
22940
  apiKey: options.apiKey,
22374
22941
  baseUrl,
22375
22942
  fetch: options.fetch
@@ -23141,7 +23708,7 @@ async function initProject(options) {
23141
23708
  reusedExistingConfig: true
23142
23709
  };
23143
23710
  if (existingConfig.config.projectId) {
23144
- const client = options.client ?? createStatelyClient$1({
23711
+ const client = options.client ?? createStatelyClient({
23145
23712
  apiKey: options.apiKey,
23146
23713
  baseUrl: apiBaseUrl
23147
23714
  });
@@ -23162,7 +23729,7 @@ async function initProject(options) {
23162
23729
  reusedExistingConfig: false
23163
23730
  };
23164
23731
  }
23165
- const client = options.client ?? createStatelyClient$1({
23732
+ const client = options.client ?? createStatelyClient({
23166
23733
  apiKey: options.apiKey,
23167
23734
  baseUrl: apiBaseUrl
23168
23735
  });
@@ -23288,9 +23855,9 @@ async function resolveConfiguredProject(options) {
23288
23855
  configPath: options.configPath,
23289
23856
  rewriteIfNeeded: options.rewriteIfNeeded ?? true
23290
23857
  });
23291
- if (!config.projectId) throw new Error("This is a local-only project. Run `statelyai init` to connect it to Stately Studio.");
23858
+ if (!config.projectId) throw new Error("This is a local-only project. Run `statelyai link` to connect it to Stately Studio.");
23292
23859
  return {
23293
- client: options.client ?? createStatelyClient$1({
23860
+ client: options.client ?? createStatelyClient({
23294
23861
  apiKey: options.apiKey,
23295
23862
  baseUrl: resolveApiBaseUrl(options.baseUrl ?? config.studioUrl)
23296
23863
  }),
@@ -23397,7 +23964,7 @@ async function resolveProjectStatus(options = {}) {
23397
23964
  };
23398
23965
  }
23399
23966
  const apiKey = options.apiKey ?? (options.client ? void 0 : (await resolveApiKey()).apiKey);
23400
- const client = options.client ?? createStatelyClient$1({
23967
+ const client = options.client ?? createStatelyClient({
23401
23968
  apiKey,
23402
23969
  baseUrl: resolveApiBaseUrl(studioUrl)
23403
23970
  });
@@ -23911,6 +24478,93 @@ async function discoverMachinesInFile(filePath) {
23911
24478
  relativePath: path.basename(filePath)
23912
24479
  }];
23913
24480
  }
24481
+ var LinkCommand = class LinkCommand extends Command {
24482
+ static summary = "Connect a workspace or source machine to Stately Studio without syncing definitions.";
24483
+ static args = { file: Args.string({ description: "Source file to associate with --machine" }) };
24484
+ static flags = {
24485
+ help: Flags.help({ char: "h" }),
24486
+ project: Flags.string({
24487
+ description: "Existing Studio project ID or URL",
24488
+ exclusive: ["create"]
24489
+ }),
24490
+ create: Flags.boolean({
24491
+ description: "Create a new Studio project",
24492
+ default: false
24493
+ }),
24494
+ name: Flags.string({
24495
+ description: "Name for --create",
24496
+ dependsOn: ["create"]
24497
+ }),
24498
+ visibility: Flags.string({
24499
+ description: "Visibility for --create",
24500
+ options: [
24501
+ "Private",
24502
+ "Public",
24503
+ "Unlisted"
24504
+ ]
24505
+ }),
24506
+ machine: Flags.string({ description: "Existing remote machine ID or URL; requires a source file" }),
24507
+ "machine-index": Flags.integer({
24508
+ description: "Zero-based machine index in a multi-machine source file",
24509
+ min: 0
24510
+ }),
24511
+ "base-url": Flags.string({ description: "Studio deployment origin" }),
24512
+ config: Flags.string({ description: "Path to statelyai.json" }),
24513
+ force: Flags.boolean({
24514
+ description: "Replace an existing project or machine association",
24515
+ default: false
24516
+ })
24517
+ };
24518
+ async run() {
24519
+ const { args, flags } = await this.parse(LinkCommand);
24520
+ let projectId = flags.project;
24521
+ let create = flags.create;
24522
+ const credential = await resolveApiKey();
24523
+ const config = await readStatelyProjectConfig({ configPath: flags.config }).catch((error) => {
24524
+ if (error.code === "ENOENT") return void 0;
24525
+ throw error;
24526
+ });
24527
+ const baseUrl = flags["base-url"] ?? config?.config.studioUrl ?? getResolvedStudioUrl();
24528
+ if (!projectId && !create && !config?.config.projectId && process.stdin.isTTY && process.stdout.isTTY) {
24529
+ const projects = await createStatelyClient({
24530
+ apiKey: credential.apiKey,
24531
+ baseUrl: resolveApiBaseUrl(baseUrl)
24532
+ }).projects.list();
24533
+ projects.forEach((project, index) => this.log(`${index + 1}. ${project.name} (${project.projectId})`));
24534
+ this.log("0. Create a new Studio project");
24535
+ const rl = createInterface({
24536
+ input: process.stdin,
24537
+ output: process.stdout
24538
+ });
24539
+ try {
24540
+ const answer = (await rl.question("Choose a project number: ")).trim();
24541
+ if (!/^\d+$/.test(answer)) this.error("Choose a project number, or pass --project <id> or --create.");
24542
+ const choice = Number(answer);
24543
+ if (choice === 0) create = true;
24544
+ else if (projects[choice - 1]) projectId = projects[choice - 1].projectId;
24545
+ else this.error("No project at that number.");
24546
+ } finally {
24547
+ rl.close();
24548
+ }
24549
+ }
24550
+ const result = await linkProject({
24551
+ projectId,
24552
+ create,
24553
+ apiKey: credential.apiKey,
24554
+ baseUrl,
24555
+ configPath: flags.config,
24556
+ name: flags.name,
24557
+ visibility: flags.visibility,
24558
+ force: flags.force,
24559
+ file: args.file,
24560
+ machineId: flags.machine,
24561
+ machineIndex: flags["machine-index"]
24562
+ });
24563
+ this.log(`Connected to Studio project ${result.project.name} (${result.project.projectId}).`);
24564
+ if (result.machineId) this.log(`Associated ${args.file} with machine ${result.machineId}.`);
24565
+ this.log("No machine definitions uploaded or downloaded. Use push or pull to sync.");
24566
+ }
24567
+ };
23914
24568
  var InitCommand = class InitCommand extends Command {
23915
24569
  static enableJsonFlag = false;
23916
24570
  static summary = "Create a local or Studio-connected statelyai.json.";
@@ -23961,6 +24615,7 @@ var InitCommand = class InitCommand extends Command {
23961
24615
  if (resolvedApiKey.source === "missing" && error instanceof StudioApiError && (error.status === 401 || error.status === 403)) this.error("Not logged in. Run `npx statelyai login` and retry. To initialize without an account, run `npx statelyai init --local` (add `--force` to replace an existing config).");
23962
24616
  throw error;
23963
24617
  }
24618
+ this.log("For separate local setup and Studio linking, use `statelyai init --local --scan`, then `statelyai link`.");
23964
24619
  const initMessage = result.project ? (() => {
23965
24620
  const projectEditorUrl = buildProjectEditorUrl(result.config.studioUrl, result.project.projectId);
23966
24621
  return result.reusedExistingConfig ? `Using existing statelyai.json at ${result.configPath} for project ${result.project.projectId}.\nEditor: ${projectEditorUrl}` : `Initialized project ${result.project.projectId} and wrote ${result.configPath}.\nEditor: ${projectEditorUrl}`;
@@ -24128,9 +24783,13 @@ const COMMANDS = {
24128
24783
  scan: ScanCommand,
24129
24784
  status: StatusCommand,
24130
24785
  init: InitCommand,
24786
+ link: LinkCommand,
24131
24787
  login: LoginCommand,
24132
24788
  logout: LogoutCommand,
24133
- whoami: WhoamiCommand
24789
+ whoami: WhoamiCommand,
24790
+ changes: ChangesCommand,
24791
+ brief: BriefCommand,
24792
+ pending: PendingCommand
24134
24793
  };
24135
24794
  async function run$1(argv = process.argv.slice(2), entryUrl = import.meta.url) {
24136
24795
  const normalizedArgv = argv.length === 1 && argv[0] === "-h" ? ["--help"] : argv;
@@ -24154,4 +24813,4 @@ function isDirectExecution() {
24154
24813
  if (isDirectExecution()) run$1();
24155
24814
 
24156
24815
  //#endregion
24157
- export { run$1 as C, verifyProject as D, resolveVerificationRequest as E, formatVerificationJUnit as O, resolveProjectStatus as S, scanProjectSources as T, isFileGitDirty as _, discoverMachinesInSource as a, resolveCredentialStatus as b, formatDiscoveredMachines as c, formatProjectStatus as d, formatStoredCredentialSource as f, initProject as g, inferInitProjectName as h, discoverLinkedPullTargets as i, createStatelyProjectConfig as k, formatMissingNewMachineDirMessage as l, getEnvCredential as m, buildOAuthLoginStart as n, discoverOAuthLogin as o, getEnvApiKey as p, classifyPushCandidates as r, discoverRemoteProjectMachineTargets as s, COMMANDS as t, formatPlanSummary as u, resolveApiKey as v, scanProjectMachines as w, resolveOpenRootDir as x, resolveConfiguredPullTargets as y };
24816
+ export { formatVerificationJUnit as A, run$1 as C, resourceId as D, linkProject as E, readGraphFile as F, verifyReport as I, briefReport as M, changesReport as N, resolveVerificationRequest as O, pendingReport as P, resolveProjectStatus as S, scanProjectSources as T, isFileGitDirty as _, discoverMachinesInSource as a, resolveCredentialStatus as b, formatDiscoveredMachines as c, formatProjectStatus as d, formatStoredCredentialSource as f, initProject as g, inferInitProjectName as h, discoverLinkedPullTargets as i, createStatelyProjectConfig as j, verifyProject as k, formatMissingNewMachineDirMessage as l, getEnvCredential as m, buildOAuthLoginStart as n, discoverOAuthLogin as o, getEnvApiKey as p, classifyPushCandidates as r, discoverRemoteProjectMachineTargets as s, COMMANDS as t, formatPlanSummary as u, resolveApiKey as v, scanProjectMachines as w, resolveOpenRootDir as x, resolveConfiguredPullTargets as y };