@statelyai/sdk 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assetStorage.d.mts +1 -1
- package/dist/cli.d.mts +0 -2
- package/dist/cli.mjs +65 -21
- package/dist/embed.d.mts +1 -1
- package/dist/graph.d.mts +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/{inspect-Bg9FTvb3.d.mts → inspect-YoEwfiKb.d.mts} +1 -1
- package/dist/inspect.d.mts +2 -2
- package/dist/studio.mjs +11 -3
- package/dist/sync-DLkTmSyA.mjs +2513 -0
- package/dist/sync.d.mts +1 -1
- package/dist/sync.mjs +3 -467
- package/package.json +2 -1
- /package/dist/{graph-DpBGHZwl.d.mts → graph-zuNj3kfa.d.mts} +0 -0
- /package/dist/{protocol-DN4mH4jR.d.mts → protocol-s9zwsiCW.d.mts} +0 -0
package/dist/sync.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CreateProjectInput, ProjectData, StudioClient, StudioMachineRecord, XStateVersion } from "./studio.mjs";
|
|
2
|
-
import { b as DigraphConfig, i as StatelyGraph } from "./graph-
|
|
2
|
+
import { b as DigraphConfig, i as StatelyGraph } from "./graph-zuNj3kfa.mjs";
|
|
3
3
|
import { GraphDiff } from "@statelyai/graph";
|
|
4
4
|
|
|
5
5
|
//#region src/sync.d.ts
|
package/dist/sync.mjs
CHANGED
|
@@ -1,469 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { getDiff, isEmptyDiff } from "@statelyai/graph";
|
|
5
|
-
import fs from "node:fs/promises";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { execFile } from "node:child_process";
|
|
8
|
-
import { promisify } from "node:util";
|
|
1
|
+
import "./studio.mjs";
|
|
2
|
+
import "./graphToXStateTS-Gzh0ZqbN.mjs";
|
|
3
|
+
import { i as pushSync, n as pullSync, r as pushLocalMachineLinks, t as planSync } from "./sync-DLkTmSyA.mjs";
|
|
9
4
|
|
|
10
|
-
//#region src/sync.ts
|
|
11
|
-
const execFileAsync = promisify(execFile);
|
|
12
|
-
function isUrl(value) {
|
|
13
|
-
try {
|
|
14
|
-
const url = new URL(value);
|
|
15
|
-
return url.protocol === "http:" || url.protocol === "https:";
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async function fileExists(filePath) {
|
|
21
|
-
try {
|
|
22
|
-
await fs.access(filePath);
|
|
23
|
-
return true;
|
|
24
|
-
} catch {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function parseGitHubRemote(remoteUrl) {
|
|
29
|
-
const httpsMatch = remoteUrl.match(/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
30
|
-
if (httpsMatch) {
|
|
31
|
-
const [, owner, repo] = httpsMatch;
|
|
32
|
-
if (!owner || !repo) return null;
|
|
33
|
-
return {
|
|
34
|
-
url: `https://github.com/${owner}/${repo}`,
|
|
35
|
-
owner,
|
|
36
|
-
repo
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
const sshMatch = remoteUrl.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
40
|
-
if (sshMatch) {
|
|
41
|
-
const [, owner, repo] = sshMatch;
|
|
42
|
-
if (!owner || !repo) return null;
|
|
43
|
-
return {
|
|
44
|
-
url: `https://github.com/${owner}/${repo}`,
|
|
45
|
-
owner,
|
|
46
|
-
repo
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
async function inferConnectedRepo(filePath, cwd) {
|
|
52
|
-
const workingDir = cwd ?? path.dirname(filePath);
|
|
53
|
-
try {
|
|
54
|
-
const [{ stdout: repoRootStdout }, { stdout: remoteStdout }, { stdout: branchStdout }, { stdout: treeShaStdout }] = await Promise.all([
|
|
55
|
-
execFileAsync("git", ["rev-parse", "--show-toplevel"], { cwd: workingDir }),
|
|
56
|
-
execFileAsync("git", [
|
|
57
|
-
"remote",
|
|
58
|
-
"get-url",
|
|
59
|
-
"origin"
|
|
60
|
-
], { cwd: workingDir }),
|
|
61
|
-
execFileAsync("git", ["branch", "--show-current"], { cwd: workingDir }),
|
|
62
|
-
execFileAsync("git", ["rev-parse", "HEAD"], { cwd: workingDir })
|
|
63
|
-
]);
|
|
64
|
-
const repoRoot = repoRootStdout.trim();
|
|
65
|
-
const remote = parseGitHubRemote(remoteStdout.trim());
|
|
66
|
-
const branch = branchStdout.trim();
|
|
67
|
-
const treeSha = treeShaStdout.trim();
|
|
68
|
-
if (!remote || !branch || !treeSha) return;
|
|
69
|
-
const relativePath = path.relative(repoRoot, filePath).replace(/\\/g, "/");
|
|
70
|
-
const relativeDir = path.dirname(relativePath).replace(/\\/g, "/");
|
|
71
|
-
const selectedPaths = relativePath && relativePath !== "." ? [relativePath] : [];
|
|
72
|
-
return {
|
|
73
|
-
...remote,
|
|
74
|
-
branch,
|
|
75
|
-
treeSha,
|
|
76
|
-
autoSync: false,
|
|
77
|
-
pathForNewFiles: relativeDir && relativeDir !== "." ? relativeDir : "src/stately-studio",
|
|
78
|
-
selectedPaths
|
|
79
|
-
};
|
|
80
|
-
} catch {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function normalizeActions(value) {
|
|
85
|
-
if (value == null) return [];
|
|
86
|
-
return (Array.isArray(value) ? value : [value]).map((item) => {
|
|
87
|
-
if (typeof item === "string") return { type: item };
|
|
88
|
-
if (typeof item === "object" && item !== null && "type" in item) {
|
|
89
|
-
const type = item.type;
|
|
90
|
-
const params = "params" in item && typeof item.params === "object" && item.params ? item.params : void 0;
|
|
91
|
-
return {
|
|
92
|
-
type,
|
|
93
|
-
...params ? { params } : {}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
return { type: String(item) };
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
function normalizeTags(value) {
|
|
100
|
-
if (!value) return [];
|
|
101
|
-
if (Array.isArray(value)) return value.map((tag) => String(tag));
|
|
102
|
-
return [String(value)];
|
|
103
|
-
}
|
|
104
|
-
function normalizeInvoke(value) {
|
|
105
|
-
if (!value) return [];
|
|
106
|
-
return (Array.isArray(value) ? value : [value]).map((invoke, index) => ({
|
|
107
|
-
src: invoke.src,
|
|
108
|
-
id: invoke.id ?? `invoke[${index}]`,
|
|
109
|
-
...invoke.input ? { input: invoke.input } : {}
|
|
110
|
-
}));
|
|
111
|
-
}
|
|
112
|
-
function resolveTargetId(sourceParentPath, target) {
|
|
113
|
-
if (!target) return;
|
|
114
|
-
if (target.startsWith("#")) {
|
|
115
|
-
const stripped = target.slice(1);
|
|
116
|
-
const machineIndex = stripped.indexOf(".");
|
|
117
|
-
if (machineIndex >= 0) return `(machine).${stripped.slice(machineIndex + 1)}`;
|
|
118
|
-
return `(machine).${stripped}`;
|
|
119
|
-
}
|
|
120
|
-
if (target.startsWith("(machine)")) return target;
|
|
121
|
-
if (target.includes(".")) return `(machine).${target}`;
|
|
122
|
-
return `${sourceParentPath}.${target}`;
|
|
123
|
-
}
|
|
124
|
-
function appendTransitionEdges(edges, sourceId, sourceParentPath, eventType, transition, edgeCounts) {
|
|
125
|
-
const transitions = Array.isArray(transition) ? transition : [transition];
|
|
126
|
-
for (const item of transitions) {
|
|
127
|
-
const normalized = typeof item === "string" ? { target: item } : item;
|
|
128
|
-
const targetId = resolveTargetId(sourceParentPath, Array.isArray(normalized.target) ? normalized.target[0] : normalized.target);
|
|
129
|
-
const groupKey = `${sourceId}:${eventType}`;
|
|
130
|
-
const index = edgeCounts.get(groupKey) ?? 0;
|
|
131
|
-
edgeCounts.set(groupKey, index + 1);
|
|
132
|
-
edges.push({
|
|
133
|
-
type: "edge",
|
|
134
|
-
id: `${sourceId}#${eventType}[${index}]`,
|
|
135
|
-
sourceId,
|
|
136
|
-
targetId: targetId ?? sourceId,
|
|
137
|
-
label: eventType,
|
|
138
|
-
data: {
|
|
139
|
-
eventType,
|
|
140
|
-
transitionType: normalized.reenter === true ? "reenter" : normalized.internal === true || !targetId ? "targetless" : "normal",
|
|
141
|
-
...normalized.guard ? { guard: typeof normalized.guard === "string" ? { type: normalized.guard } : {
|
|
142
|
-
type: normalized.guard.type,
|
|
143
|
-
...normalized.guard.params ? { params: normalized.guard.params } : {}
|
|
144
|
-
} } : {},
|
|
145
|
-
actions: normalizeActions(normalized.actions),
|
|
146
|
-
...normalized.description ? { description: normalized.description } : {}
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
function fromXStateConfig(config) {
|
|
152
|
-
const nodes = [];
|
|
153
|
-
const edges = [];
|
|
154
|
-
const edgeCounts = /* @__PURE__ */ new Map();
|
|
155
|
-
function visitNode(key, nodeConfig, parentId, parentPath) {
|
|
156
|
-
const nodeId = parentPath ? `${parentPath}.${key}` : "(machine)";
|
|
157
|
-
const currentPath = parentPath ? nodeId : "(machine)";
|
|
158
|
-
const initialId = nodeConfig.initial ? `${currentPath}.${nodeConfig.initial}` : void 0;
|
|
159
|
-
nodes.push({
|
|
160
|
-
type: "node",
|
|
161
|
-
id: nodeId,
|
|
162
|
-
parentId,
|
|
163
|
-
label: key,
|
|
164
|
-
...initialId ? { initialNodeId: initialId } : {},
|
|
165
|
-
data: {
|
|
166
|
-
key,
|
|
167
|
-
...nodeConfig.type ? { type: nodeConfig.type } : {},
|
|
168
|
-
...nodeConfig.history ? { history: nodeConfig.history } : {},
|
|
169
|
-
...initialId ? { initialId } : {},
|
|
170
|
-
entry: normalizeActions(nodeConfig.entry),
|
|
171
|
-
exit: normalizeActions(nodeConfig.exit),
|
|
172
|
-
invokes: normalizeInvoke(nodeConfig.invoke),
|
|
173
|
-
tags: normalizeTags(nodeConfig.tags),
|
|
174
|
-
...nodeConfig.description ? { description: nodeConfig.description } : {}
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
for (const [eventType, transition] of Object.entries(nodeConfig.on ?? {})) appendTransitionEdges(edges, nodeId, parentPath ?? "(machine)", eventType, transition, edgeCounts);
|
|
178
|
-
if (nodeConfig.always) appendTransitionEdges(edges, nodeId, parentPath ?? "(machine)", "", nodeConfig.always, edgeCounts);
|
|
179
|
-
for (const [childKey, childConfig] of Object.entries(nodeConfig.states ?? {})) visitNode(childKey, childConfig, nodeId, currentPath);
|
|
180
|
-
}
|
|
181
|
-
visitNode("(machine)", config, null, null);
|
|
182
|
-
return {
|
|
183
|
-
id: config.id ?? "machine",
|
|
184
|
-
nodes,
|
|
185
|
-
edges,
|
|
186
|
-
data: {}
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
async function pushLocalMachineLinks(options) {
|
|
190
|
-
const client = options.client ?? createStatelyClient({
|
|
191
|
-
apiKey: options.apiKey,
|
|
192
|
-
baseUrl: options.baseUrl,
|
|
193
|
-
fetch: options.fetch
|
|
194
|
-
});
|
|
195
|
-
const sourcePath = path.resolve(options.cwd ?? process.cwd(), options.source);
|
|
196
|
-
const project = await resolvePushProject(client, sourcePath, options);
|
|
197
|
-
if (!project.projectVersionId) throw new Error("Resolved project is missing projectVersionId.");
|
|
198
|
-
const contents = await fs.readFile(sourcePath, "utf8");
|
|
199
|
-
const attachments = findStatelyPragmaAttachments(contents, sourcePath);
|
|
200
|
-
const extracted = await client.code.extractMachines(contents);
|
|
201
|
-
if (attachments.length === 0 || extracted.machines.length === 0) return {
|
|
202
|
-
sourcePath,
|
|
203
|
-
project,
|
|
204
|
-
created: [],
|
|
205
|
-
updated: [],
|
|
206
|
-
skipped: [{
|
|
207
|
-
machineIndex: 0,
|
|
208
|
-
reason: "No local machines were discovered in this file."
|
|
209
|
-
}]
|
|
210
|
-
};
|
|
211
|
-
if (attachments.length !== extracted.machines.length) return {
|
|
212
|
-
sourcePath,
|
|
213
|
-
project,
|
|
214
|
-
created: [],
|
|
215
|
-
updated: [],
|
|
216
|
-
skipped: [{
|
|
217
|
-
machineIndex: 0,
|
|
218
|
-
reason: "The local machine extractor did not align with the source attachments for this file."
|
|
219
|
-
}]
|
|
220
|
-
};
|
|
221
|
-
let nextContents = contents;
|
|
222
|
-
let outputPath;
|
|
223
|
-
const created = [];
|
|
224
|
-
const updated = [];
|
|
225
|
-
const skipped = [];
|
|
226
|
-
for (const [machineIndex, attachment] of attachments.entries()) {
|
|
227
|
-
const extractedMachine = extracted.machines[machineIndex];
|
|
228
|
-
if (!extractedMachine?.config) {
|
|
229
|
-
skipped.push({
|
|
230
|
-
machineIndex,
|
|
231
|
-
reason: "No extracted machine config was available for this source."
|
|
232
|
-
});
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
if (attachment.pragma?.id) {
|
|
236
|
-
const updatedMachine = await client.machines.update({
|
|
237
|
-
id: attachment.pragma.id,
|
|
238
|
-
definition: toStudioMachine(fromXStateConfig(extractedMachine.config))
|
|
239
|
-
});
|
|
240
|
-
updated.push({
|
|
241
|
-
machineIndex,
|
|
242
|
-
machine: updatedMachine
|
|
243
|
-
});
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
const machine = await client.machines.create({
|
|
247
|
-
projectVersionId: project.projectVersionId,
|
|
248
|
-
definition: toStudioMachine(fromXStateConfig(extractedMachine.config)),
|
|
249
|
-
xstateVersion: Math.max(5, options.xstateVersion ?? 5)
|
|
250
|
-
});
|
|
251
|
-
nextContents = upsertStatelyPragma(nextContents, machine.id, {
|
|
252
|
-
fileName: sourcePath,
|
|
253
|
-
machineIndex
|
|
254
|
-
});
|
|
255
|
-
created.push({
|
|
256
|
-
machineIndex,
|
|
257
|
-
machine
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
if (nextContents !== contents) {
|
|
261
|
-
await fs.writeFile(sourcePath, nextContents, "utf8");
|
|
262
|
-
outputPath = sourcePath;
|
|
263
|
-
}
|
|
264
|
-
return {
|
|
265
|
-
sourcePath,
|
|
266
|
-
project,
|
|
267
|
-
created,
|
|
268
|
-
updated,
|
|
269
|
-
skipped,
|
|
270
|
-
...outputPath ? { outputPath } : {}
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
async function resolveLocalFile(locator, options) {
|
|
274
|
-
const filePath = path.resolve(options.cwd ?? process.cwd(), locator);
|
|
275
|
-
const contents = await fs.readFile(filePath, "utf8");
|
|
276
|
-
const extension = path.extname(filePath).toLowerCase();
|
|
277
|
-
if (extension === ".ts" || extension === ".tsx" || extension === ".js" || extension === ".jsx") {
|
|
278
|
-
const config = (await (options.client ?? createStatelyClient({
|
|
279
|
-
apiKey: options.apiKey,
|
|
280
|
-
baseUrl: options.baseUrl,
|
|
281
|
-
fetch: options.fetch
|
|
282
|
-
})).code.extractMachines(contents)).machines[0]?.config;
|
|
283
|
-
if (!config) throw new Error(`No machines extracted from ${filePath}`);
|
|
284
|
-
return {
|
|
285
|
-
kind: "local-file",
|
|
286
|
-
locator: filePath,
|
|
287
|
-
format: "xstate",
|
|
288
|
-
graph: fromXStateConfig(config)
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const parsed = JSON.parse(contents);
|
|
292
|
-
if ("rootNode" in parsed && "edges" in parsed) return {
|
|
293
|
-
kind: "local-file",
|
|
294
|
-
locator: filePath,
|
|
295
|
-
format: "digraph",
|
|
296
|
-
graph: fromStudioMachine(parsed)
|
|
297
|
-
};
|
|
298
|
-
if ("nodes" in parsed && "edges" in parsed) return {
|
|
299
|
-
kind: "local-file",
|
|
300
|
-
locator: filePath,
|
|
301
|
-
format: "graph",
|
|
302
|
-
graph: parsed
|
|
303
|
-
};
|
|
304
|
-
if ("states" in parsed) return {
|
|
305
|
-
kind: "local-file",
|
|
306
|
-
locator: filePath,
|
|
307
|
-
format: "xstate",
|
|
308
|
-
graph: fromXStateConfig(parsed)
|
|
309
|
-
};
|
|
310
|
-
throw new Error(`Unsupported local sync input: ${filePath}`);
|
|
311
|
-
}
|
|
312
|
-
function inferWritableTargetFormat(filePath) {
|
|
313
|
-
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx") || filePath.endsWith(".js") || filePath.endsWith(".jsx")) return "xstate";
|
|
314
|
-
if (filePath.endsWith(".digraph.json")) return "digraph";
|
|
315
|
-
if (filePath.endsWith(".graph.json")) return "graph";
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
function serializeGraph(graph, format, options = {}) {
|
|
319
|
-
switch (format) {
|
|
320
|
-
case "digraph": return `${JSON.stringify(toStudioMachine(graph), null, 2)}\n`;
|
|
321
|
-
case "graph": return `${JSON.stringify(graph, null, 2)}\n`;
|
|
322
|
-
case "xstate": {
|
|
323
|
-
const source = graphToXStateTS(graph);
|
|
324
|
-
if (!options.remoteMachineId) return source;
|
|
325
|
-
return upsertStatelyPragma(source, options.remoteMachineId, options.targetPath ? { fileName: options.targetPath } : {});
|
|
326
|
-
}
|
|
327
|
-
default: {
|
|
328
|
-
const exhaustive = format;
|
|
329
|
-
throw new Error(`Unsupported sync output format: ${exhaustive}`);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
async function resolveRemoteMachine(machineId, baseUrl, kind, locator, options) {
|
|
334
|
-
return {
|
|
335
|
-
kind,
|
|
336
|
-
locator,
|
|
337
|
-
format: "digraph",
|
|
338
|
-
graph: fromStudioMachine(await (options.client ?? createStatelyClient({
|
|
339
|
-
apiKey: options.apiKey,
|
|
340
|
-
baseUrl,
|
|
341
|
-
fetch: options.fetch
|
|
342
|
-
})).machines.get(machineId)),
|
|
343
|
-
remoteMachineId: machineId
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
async function resolveSyncInput(locator, options) {
|
|
347
|
-
if (await fileExists(path.resolve(options.cwd ?? process.cwd(), locator))) return resolveLocalFile(locator, options);
|
|
348
|
-
if (isUrl(locator)) {
|
|
349
|
-
const url = new URL(locator);
|
|
350
|
-
const machineId = url.pathname.split("/").filter(Boolean).at(-1);
|
|
351
|
-
if (!machineId) throw new Error(`Could not resolve machine ID from URL: ${locator}`);
|
|
352
|
-
return resolveRemoteMachine(machineId, url.origin, "studio-url", locator, options);
|
|
353
|
-
}
|
|
354
|
-
return resolveRemoteMachine(locator, options.baseUrl, "studio-machine-id", locator, options);
|
|
355
|
-
}
|
|
356
|
-
function summarizeDiff(diff) {
|
|
357
|
-
const nodeChanges = diff.nodes.added.length + diff.nodes.removed.length + diff.nodes.updated.length;
|
|
358
|
-
const edgeChanges = diff.edges.added.length + diff.edges.removed.length + diff.edges.updated.length;
|
|
359
|
-
return {
|
|
360
|
-
hasChanges: !isEmptyDiff(diff),
|
|
361
|
-
nodeChanges,
|
|
362
|
-
edgeChanges
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
async function planSync(options) {
|
|
366
|
-
const source = await resolveSyncInput(options.source, options);
|
|
367
|
-
const target = await resolveSyncInput(options.target, options);
|
|
368
|
-
const diff = getDiff(source.graph, target.graph);
|
|
369
|
-
return {
|
|
370
|
-
source,
|
|
371
|
-
target,
|
|
372
|
-
diff,
|
|
373
|
-
summary: summarizeDiff(diff),
|
|
374
|
-
warnings: []
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
async function pullSync(options) {
|
|
378
|
-
const source = await resolveSyncInput(options.source, options);
|
|
379
|
-
const outputPath = path.resolve(options.cwd ?? process.cwd(), options.target);
|
|
380
|
-
const fallbackFormat = inferWritableTargetFormat(outputPath);
|
|
381
|
-
let targetFormat = fallbackFormat;
|
|
382
|
-
if (await fileExists(outputPath)) try {
|
|
383
|
-
targetFormat = (await resolveLocalFile(options.target, options)).format;
|
|
384
|
-
} catch (error) {
|
|
385
|
-
if (!fallbackFormat) throw error;
|
|
386
|
-
}
|
|
387
|
-
if (!targetFormat) throw new Error(`Could not infer a writable target format from ${outputPath}. Use an existing digraph/graph file or a .digraph.json/.graph.json target.`);
|
|
388
|
-
const serialized = serializeGraph(source.graph, targetFormat, {
|
|
389
|
-
remoteMachineId: source.remoteMachineId,
|
|
390
|
-
targetPath: outputPath
|
|
391
|
-
});
|
|
392
|
-
await fs.writeFile(outputPath, serialized, "utf8");
|
|
393
|
-
return {
|
|
394
|
-
source,
|
|
395
|
-
target: {
|
|
396
|
-
kind: "local-file",
|
|
397
|
-
locator: outputPath,
|
|
398
|
-
format: targetFormat,
|
|
399
|
-
graph: source.graph
|
|
400
|
-
},
|
|
401
|
-
outputPath
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
function inferDefaultProjectName(sourcePath, repo) {
|
|
405
|
-
if (repo?.repo) return repo.repo;
|
|
406
|
-
const parentDir = path.basename(path.dirname(sourcePath));
|
|
407
|
-
if (parentDir && parentDir !== ".") return parentDir;
|
|
408
|
-
return path.basename(sourcePath, path.extname(sourcePath));
|
|
409
|
-
}
|
|
410
|
-
async function resolvePushProject(client, sourcePath, options) {
|
|
411
|
-
const explicitProject = options.project;
|
|
412
|
-
if (explicitProject?.projectVersionId) return client.projects.get(explicitProject.projectVersionId);
|
|
413
|
-
if (explicitProject?.projectId) return client.projects.get(explicitProject.projectId);
|
|
414
|
-
const inferredRepo = explicitProject?.repo ?? await inferConnectedRepo(sourcePath, options.cwd);
|
|
415
|
-
const projectInput = {
|
|
416
|
-
name: explicitProject?.name ?? inferDefaultProjectName(sourcePath, inferredRepo),
|
|
417
|
-
visibility: explicitProject?.visibility ?? "Private",
|
|
418
|
-
...explicitProject?.description ? { description: explicitProject.description } : {},
|
|
419
|
-
...explicitProject?.keywords ? { keywords: explicitProject.keywords } : {},
|
|
420
|
-
...inferredRepo ? { repo: inferredRepo } : {}
|
|
421
|
-
};
|
|
422
|
-
return client.projects.ensure(projectInput);
|
|
423
|
-
}
|
|
424
|
-
async function pushSync(options) {
|
|
425
|
-
const client = options.client ?? createStatelyClient({
|
|
426
|
-
apiKey: options.apiKey,
|
|
427
|
-
baseUrl: options.baseUrl,
|
|
428
|
-
fetch: options.fetch
|
|
429
|
-
});
|
|
430
|
-
const source = await resolveSyncInput(options.source, {
|
|
431
|
-
...options,
|
|
432
|
-
target: options.source
|
|
433
|
-
});
|
|
434
|
-
if (source.kind !== "local-file") throw new Error("pushSync currently requires a local source file.");
|
|
435
|
-
const sourcePath = source.locator;
|
|
436
|
-
const project = await resolvePushProject(client, sourcePath, options);
|
|
437
|
-
const existingMachineId = source.format === "xstate" ? getStatelyPragma(await fs.readFile(sourcePath, "utf8"), sourcePath)?.id : void 0;
|
|
438
|
-
let machine;
|
|
439
|
-
if (existingMachineId) machine = await client.machines.update({
|
|
440
|
-
id: existingMachineId,
|
|
441
|
-
definition: toStudioMachine(source.graph)
|
|
442
|
-
});
|
|
443
|
-
else {
|
|
444
|
-
if (!project.projectVersionId) throw new Error("Resolved project is missing projectVersionId.");
|
|
445
|
-
machine = await client.machines.create({
|
|
446
|
-
projectVersionId: project.projectVersionId,
|
|
447
|
-
definition: toStudioMachine(source.graph),
|
|
448
|
-
xstateVersion: options.xstateVersion ?? 5
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
let outputPath;
|
|
452
|
-
if (source.format === "xstate") {
|
|
453
|
-
const contents = await fs.readFile(sourcePath, "utf8");
|
|
454
|
-
const nextContents = upsertStatelyPragma(contents, machine.id, { fileName: sourcePath });
|
|
455
|
-
if (nextContents !== contents) {
|
|
456
|
-
await fs.writeFile(sourcePath, nextContents, "utf8");
|
|
457
|
-
outputPath = sourcePath;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
return {
|
|
461
|
-
source,
|
|
462
|
-
project,
|
|
463
|
-
machine,
|
|
464
|
-
...outputPath ? { outputPath } : {}
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
//#endregion
|
|
469
5
|
export { planSync, pullSync, pushLocalMachineLinks, pushSync };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bin": {
|
|
6
6
|
"statelyai": "./dist/cli.mjs"
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@oclif/core": "^4.10.3",
|
|
63
63
|
"@statelyai/graph": "^0.9.0",
|
|
64
|
+
"esbuild": "^0.27.0",
|
|
64
65
|
"typescript": "^5.9.3",
|
|
65
66
|
"xstate": "^5.0.0"
|
|
66
67
|
},
|
|
File without changes
|
|
File without changes
|