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.
- package/README.md +93 -5
- package/dist/bin.mjs +1 -1
- package/dist/{cli-B2Dd5vY-.mjs → cli-BtgsQvTR.mjs} +1288 -629
- package/dist/index.d.mts +131 -3
- package/dist/index.mjs +2 -2
- package/package.json +5 -4
|
@@ -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
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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
|
-
*
|
|
999
|
-
* specified `delay`.
|
|
1365
|
+
* Convert a resolved {@link GraphEdge} back into an {@link EdgeConfig}.
|
|
1000
1366
|
*
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
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
|
|
1005
|
-
|
|
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
|
-
*
|
|
1009
|
-
* the parent state node.
|
|
1454
|
+
* Compute a structured diff from graph `a` to graph `b` by matching IDs.
|
|
1010
1455
|
*
|
|
1011
|
-
* @
|
|
1012
|
-
*
|
|
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
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
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
|
-
*
|
|
1532
|
+
* Check if a diff has zero changes.
|
|
1022
1533
|
*
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1534
|
+
* @example
|
|
1535
|
+
* ```ts
|
|
1536
|
+
* import { createGraph, getDiff, isEmptyDiff } from '@statelyai/graph';
|
|
1025
1537
|
*
|
|
1026
|
-
*
|
|
1027
|
-
*
|
|
1538
|
+
* const g = createGraph({ nodes: [{ id: 'n1' }], edges: [] });
|
|
1539
|
+
* const diff = getDiff(g, g);
|
|
1540
|
+
* isEmptyDiff(diff); // true
|
|
1541
|
+
* ```
|
|
1028
1542
|
*/
|
|
1029
|
-
function
|
|
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
|
|
1546
|
+
function summarizeGraphDiff(diff) {
|
|
1037
1547
|
return {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
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
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
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
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
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,
|
|
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)
|
|
11091
|
-
const
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
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-
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
14192
|
-
if (
|
|
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-
|
|
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-
|
|
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-
|
|
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.
|
|
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$
|
|
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$
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 };
|