@statelyai/sdk 0.5.0 → 0.5.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/README.md +4 -3
- package/dist/cli.mjs +645 -5
- package/dist/embed.d.mts +1 -1
- package/dist/embed.mjs +1 -1
- package/dist/{graphToXStateTS-BSUj97r0.mjs → graphToXStateTS-CtecEESq.mjs} +13 -2
- package/dist/index.d.mts +17 -3
- package/dist/index.mjs +2 -2
- package/dist/{inspect-WUC2inmJ.d.mts → inspect-ttRIjoCu.d.mts} +1 -1
- package/dist/inspect.d.mts +2 -2
- package/dist/inspect.mjs +1 -1
- package/dist/patchTypes.d.mts +25 -1
- package/dist/{protocol-B1cNV7QB.d.mts → protocol-BPuwbNCz.d.mts} +4 -0
- package/dist/sync.mjs +1 -1
- package/dist/{transport-lomH7b0v.mjs → transport-DoCHBLTu.mjs} +5 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -69,6 +69,7 @@ The common environment variables are:
|
|
|
69
69
|
| Variable | Purpose |
|
|
70
70
|
| --- | --- |
|
|
71
71
|
| `AUTH_PROVIDER` | Auth strategy used by the editor host |
|
|
72
|
+
| `EDITOR_SYNC_AUTH_REQUIRED` | Set to `false` to disable API-key checks for editor-sync endpoints only |
|
|
72
73
|
| `STATELY_API_KEY` | Server-side API key for Stately data fetching |
|
|
73
74
|
| `STATELY_API_URL` | Stately API base URL override |
|
|
74
75
|
| `NEXT_PUBLIC_BASE_URL` | Public-facing editor URL |
|
|
@@ -354,7 +355,7 @@ const aslYaml = await embed.export('asl-yaml');
|
|
|
354
355
|
|
|
355
356
|
<!-- supported export formats from ExportFormatMap in src/protocol.ts -->
|
|
356
357
|
|
|
357
|
-
Supported formats: `xstate`, `json`, `digraph`, `mermaid`, `rtk`, `zustand`, `asl-json`, `asl-yaml`, `scxml`
|
|
358
|
+
Supported formats: `xstate`, `json`, `xgraph`, `digraph`, `mermaid`, `rtk`, `zustand`, `asl-json`, `asl-yaml`, `scxml`
|
|
358
359
|
|
|
359
360
|
#### `embed.on(event, handler)` / `embed.off(event, handler)`
|
|
360
361
|
|
|
@@ -507,11 +508,11 @@ Available commands:
|
|
|
507
508
|
|
|
508
509
|
Common flags:
|
|
509
510
|
|
|
510
|
-
- `--api-key` for remote machine resolution
|
|
511
|
+
- `--api-key` for remote machine resolution or editor servers that require auth
|
|
511
512
|
- `--base-url` for self-hosted or non-default Stately deployments
|
|
512
513
|
- `--fail-on-changes` to return a nonzero exit code when a diff is detected
|
|
513
514
|
|
|
514
|
-
`statelyai open` also supports `--editor-url`, `--host`, `--port`, `--no-open`, and `--debug`. It watches the local file on disk,
|
|
515
|
+
`statelyai open` also supports `--api-key`, `--editor-url`, `--host`, `--port`, `--no-open`, and `--debug`. It watches the local file on disk and sends source snapshots to `/api/editor-sync/*` endpoints, which return the text replacements to apply locally. Pass `--api-key` or set `STATELY_API_KEY` when the editor server requires auth. Self-hosted servers can disable editor-sync API-key checks with `EDITOR_SYNC_AUTH_REQUIRED=false`. The private source reconciliation engine is not bundled into the published CLI.
|
|
515
516
|
|
|
516
517
|
## Transport Helpers
|
|
517
518
|
|
package/dist/cli.mjs
CHANGED
|
@@ -1,11 +1,651 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { planSync, pullSync } from "./sync.mjs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import * as path$1 from "node:path";
|
|
3
5
|
import path from "node:path";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
+
import fs$1, { watch } from "node:fs";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
8
|
import { Args, Command, Flags, flush, handle, run as run$1 } from "@oclif/core";
|
|
7
|
-
import
|
|
9
|
+
import * as crypto from "node:crypto";
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import * as http from "node:http";
|
|
8
12
|
|
|
13
|
+
//#region src/cliHost.ts
|
|
14
|
+
const DEFAULT_SYNC_MAX_FILES = 150;
|
|
15
|
+
const DEFAULT_SYNC_MAX_BYTES = 2e6;
|
|
16
|
+
const supportedDocumentPattern = /\.(?:c|m)?(?:jsx?|tsx?)$|\.jsonc?$|\.ya?ml$|\.mmd$|\.mermaid$/i;
|
|
17
|
+
var WebSocketClient = class {
|
|
18
|
+
buffer = Buffer.alloc(0);
|
|
19
|
+
listeners = /* @__PURE__ */ new Set();
|
|
20
|
+
constructor(socket) {
|
|
21
|
+
this.socket = socket;
|
|
22
|
+
socket.on("data", (chunk) => {
|
|
23
|
+
const nextChunk = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
24
|
+
this.buffer = Buffer.concat([this.buffer, nextChunk]);
|
|
25
|
+
this.readFrames();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
onMessage(listener) {
|
|
29
|
+
this.listeners.add(listener);
|
|
30
|
+
}
|
|
31
|
+
send(message) {
|
|
32
|
+
this.socket.write(encodeWebSocketFrame(JSON.stringify(message)));
|
|
33
|
+
}
|
|
34
|
+
close() {
|
|
35
|
+
this.socket.end();
|
|
36
|
+
}
|
|
37
|
+
readFrames() {
|
|
38
|
+
while (this.buffer.length >= 2) {
|
|
39
|
+
const first = this.buffer[0];
|
|
40
|
+
const second = this.buffer[1];
|
|
41
|
+
const opcode = first & 15;
|
|
42
|
+
const masked = (second & 128) !== 0;
|
|
43
|
+
let length = second & 127;
|
|
44
|
+
let offset = 2;
|
|
45
|
+
if (length === 126) {
|
|
46
|
+
if (this.buffer.length < offset + 2) return;
|
|
47
|
+
length = this.buffer.readUInt16BE(offset);
|
|
48
|
+
offset += 2;
|
|
49
|
+
} else if (length === 127) {
|
|
50
|
+
if (this.buffer.length < offset + 8) return;
|
|
51
|
+
const bigLength = this.buffer.readBigUInt64BE(offset);
|
|
52
|
+
if (bigLength > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
53
|
+
this.socket.destroy(/* @__PURE__ */ new Error("WebSocket frame is too large."));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
length = Number(bigLength);
|
|
57
|
+
offset += 8;
|
|
58
|
+
}
|
|
59
|
+
const maskLength = masked ? 4 : 0;
|
|
60
|
+
if (this.buffer.length < offset + maskLength + length) return;
|
|
61
|
+
const mask = masked ? this.buffer.subarray(offset, offset + 4) : void 0;
|
|
62
|
+
offset += maskLength;
|
|
63
|
+
const payload = Buffer.from(this.buffer.subarray(offset, offset + length));
|
|
64
|
+
this.buffer = this.buffer.subarray(offset + length);
|
|
65
|
+
if (mask) for (let index = 0; index < payload.length; index++) payload[index] = payload[index] ^ mask[index % 4];
|
|
66
|
+
if (opcode === 8) {
|
|
67
|
+
this.close();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (opcode === 9) {
|
|
71
|
+
this.socket.write(encodeWebSocketFrame(payload, 10));
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (opcode !== 1) continue;
|
|
75
|
+
const message = JSON.parse(payload.toString("utf8"));
|
|
76
|
+
for (const listener of this.listeners) listener(message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var RemoteEditorSession = class {
|
|
81
|
+
currentGraph;
|
|
82
|
+
lastPersistedGraph;
|
|
83
|
+
currentSourceLocations;
|
|
84
|
+
currentFormat;
|
|
85
|
+
ready = false;
|
|
86
|
+
pendingMessages = [];
|
|
87
|
+
pendingRetrievals = /* @__PURE__ */ new Map();
|
|
88
|
+
retrieveRequestCount = 0;
|
|
89
|
+
watchedSourceUris = /* @__PURE__ */ new Set();
|
|
90
|
+
selfEditCount = 0;
|
|
91
|
+
watcher;
|
|
92
|
+
constructor(options, sendMessage) {
|
|
93
|
+
this.options = options;
|
|
94
|
+
this.sendMessage = sendMessage;
|
|
95
|
+
this.watcher = watchWorkspace(this.options.rootDir, (uri) => {
|
|
96
|
+
this.handleSavedDocument(uri);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
dispose() {
|
|
100
|
+
for (const pending of this.pendingRetrievals.values()) pending.reject(/* @__PURE__ */ new Error("Editor session disposed before export completed."));
|
|
101
|
+
this.pendingRetrievals.clear();
|
|
102
|
+
this.watcher?.dispose();
|
|
103
|
+
}
|
|
104
|
+
async receiveMessage(msg) {
|
|
105
|
+
this.log("recv", msg.type, msg);
|
|
106
|
+
switch (msg.type) {
|
|
107
|
+
case "@statelyai.ready":
|
|
108
|
+
await this.initializeEditor();
|
|
109
|
+
break;
|
|
110
|
+
case "@statelyai.loaded":
|
|
111
|
+
this.currentGraph = msg.graph;
|
|
112
|
+
this.lastPersistedGraph = msg.graph;
|
|
113
|
+
this.currentSourceLocations = msg.sourceLocations ?? this.currentSourceLocations;
|
|
114
|
+
this.updateSourceLocations(this.currentSourceLocations);
|
|
115
|
+
break;
|
|
116
|
+
case "@statelyai.change":
|
|
117
|
+
this.currentGraph = msg.graph;
|
|
118
|
+
this.currentSourceLocations = msg.sourceLocations ?? this.currentSourceLocations;
|
|
119
|
+
this.updateSourceLocations(this.currentSourceLocations);
|
|
120
|
+
break;
|
|
121
|
+
case "@statelyai.save":
|
|
122
|
+
await this.saveFromEditor(msg);
|
|
123
|
+
break;
|
|
124
|
+
case "@statelyai.retrieved":
|
|
125
|
+
this.resolveRetrievedSource(msg);
|
|
126
|
+
break;
|
|
127
|
+
case "@statelyai.clipboardRead":
|
|
128
|
+
this.postMessage({
|
|
129
|
+
type: "@statelyai.clipboardReadResponse",
|
|
130
|
+
requestId: msg.requestId,
|
|
131
|
+
text: ""
|
|
132
|
+
});
|
|
133
|
+
break;
|
|
134
|
+
case "@statelyai.error":
|
|
135
|
+
this.handleErrorMessage(msg);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async initializeEditor() {
|
|
140
|
+
this.ready = true;
|
|
141
|
+
try {
|
|
142
|
+
const update = await this.parseRootDocument();
|
|
143
|
+
this.currentFormat = update.format;
|
|
144
|
+
this.currentSourceLocations = update.sourceLocations;
|
|
145
|
+
this.updateSourceLocations(update.sourceLocations);
|
|
146
|
+
this.postMessage({
|
|
147
|
+
type: "@statelyai.init",
|
|
148
|
+
machine: update.machine,
|
|
149
|
+
format: update.format,
|
|
150
|
+
sourceLocations: update.sourceLocations,
|
|
151
|
+
mode: "editing",
|
|
152
|
+
theme: "light",
|
|
153
|
+
unsavedIndicator: { enabled: true },
|
|
154
|
+
leftPanels: [],
|
|
155
|
+
rightPanels: [],
|
|
156
|
+
activePanels: []
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
this.showError(formatError(error, "Failed to initialize the visual editor."));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
for (const pending of this.pendingMessages) this.sendMessage(pending);
|
|
163
|
+
this.pendingMessages = [];
|
|
164
|
+
}
|
|
165
|
+
async handleSavedDocument(uri) {
|
|
166
|
+
if (uri !== this.options.rootUri && !this.watchedSourceUris.has(uri)) return;
|
|
167
|
+
if (this.selfEditCount > 0) return;
|
|
168
|
+
try {
|
|
169
|
+
const update = await this.parseRootDocument();
|
|
170
|
+
this.currentFormat = update.format;
|
|
171
|
+
this.currentSourceLocations = update.sourceLocations;
|
|
172
|
+
this.updateSourceLocations(update.sourceLocations);
|
|
173
|
+
this.postMessage({
|
|
174
|
+
type: "@statelyai.update",
|
|
175
|
+
machine: update.machine,
|
|
176
|
+
format: update.format,
|
|
177
|
+
sourceLocations: update.sourceLocations
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
this.showError(formatError(error, "Failed to read the saved document."));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async saveFromEditor(msg) {
|
|
184
|
+
const validationErrors = getValidationErrors(msg.validations);
|
|
185
|
+
if (validationErrors.length > 0) {
|
|
186
|
+
const message = formatValidationErrorMessage(validationErrors);
|
|
187
|
+
this.postMessage({
|
|
188
|
+
type: "@statelyai.toast",
|
|
189
|
+
message,
|
|
190
|
+
toastType: "error"
|
|
191
|
+
});
|
|
192
|
+
this.showError(message);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.currentSourceLocations = msg.sourceLocations ?? this.currentSourceLocations;
|
|
196
|
+
this.updateSourceLocations(this.currentSourceLocations);
|
|
197
|
+
try {
|
|
198
|
+
const serialized = await this.getSerializedPayloadForCurrentFormat();
|
|
199
|
+
const documents = await collectSourceDocuments({
|
|
200
|
+
rootFileName: this.options.fileName,
|
|
201
|
+
rootDir: this.options.rootDir,
|
|
202
|
+
extraUris: sourceLocationUris(this.currentSourceLocations)
|
|
203
|
+
});
|
|
204
|
+
const response = await this.postSync("/api/editor-sync/apply", {
|
|
205
|
+
rootUri: this.options.rootUri,
|
|
206
|
+
rootFileName: this.options.fileName,
|
|
207
|
+
rootDir: this.options.rootDir,
|
|
208
|
+
documents,
|
|
209
|
+
machineConfig: msg.machineConfig,
|
|
210
|
+
patches: msg.patches,
|
|
211
|
+
prevGraph: this.lastPersistedGraph ?? this.currentGraph,
|
|
212
|
+
nextGraph: msg.graph,
|
|
213
|
+
sourceLocations: this.currentSourceLocations,
|
|
214
|
+
serialized
|
|
215
|
+
});
|
|
216
|
+
if (response.replacements.length === 0) {
|
|
217
|
+
const message = "Unable to apply Viz changes to the source file automatically.";
|
|
218
|
+
this.postMessage({
|
|
219
|
+
type: "@statelyai.toast",
|
|
220
|
+
message,
|
|
221
|
+
toastType: "error"
|
|
222
|
+
});
|
|
223
|
+
this.showError(message);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
await this.runWithSelfEdit(async () => {
|
|
227
|
+
await applyFileReplacements(response.replacements, this.options.rootUri);
|
|
228
|
+
});
|
|
229
|
+
this.currentGraph = msg.graph;
|
|
230
|
+
this.lastPersistedGraph = msg.graph;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
const message = formatError(error, "Failed to apply Viz changes.");
|
|
233
|
+
this.postMessage({
|
|
234
|
+
type: "@statelyai.toast",
|
|
235
|
+
message,
|
|
236
|
+
toastType: "error"
|
|
237
|
+
});
|
|
238
|
+
this.showError(message);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async parseRootDocument() {
|
|
242
|
+
const documents = await collectSourceDocuments({
|
|
243
|
+
rootFileName: this.options.fileName,
|
|
244
|
+
rootDir: this.options.rootDir,
|
|
245
|
+
extraUris: sourceLocationUris(this.currentSourceLocations)
|
|
246
|
+
});
|
|
247
|
+
return this.postSync("/api/editor-sync/parse", {
|
|
248
|
+
rootUri: this.options.rootUri,
|
|
249
|
+
rootFileName: this.options.fileName,
|
|
250
|
+
rootDir: this.options.rootDir,
|
|
251
|
+
documents
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async postSync(pathname, body) {
|
|
255
|
+
const url = new URL(pathname, normalizedBaseUrl(this.options.editorUrl));
|
|
256
|
+
const headers = { "Content-Type": "application/json" };
|
|
257
|
+
if (this.options.apiKey) headers.Authorization = `Bearer ${this.options.apiKey}`;
|
|
258
|
+
const response = await fetch(url, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers,
|
|
261
|
+
body: JSON.stringify(body)
|
|
262
|
+
});
|
|
263
|
+
const data = await response.json().catch(() => null);
|
|
264
|
+
if (!response.ok) {
|
|
265
|
+
const message = data && typeof data === "object" && "error" in data && typeof data.error === "string" ? data.error : `HTTP ${response.status}`;
|
|
266
|
+
throw new Error(message);
|
|
267
|
+
}
|
|
268
|
+
return data;
|
|
269
|
+
}
|
|
270
|
+
async getSerializedPayloadForCurrentFormat() {
|
|
271
|
+
const format = this.currentFormat;
|
|
272
|
+
if (!format || format === "xstate") return;
|
|
273
|
+
try {
|
|
274
|
+
const serialized = await this.requestSerialized(format);
|
|
275
|
+
return { [format]: serialized };
|
|
276
|
+
} catch {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
requestSerialized(format) {
|
|
281
|
+
const requestId = `retrieve-${++this.retrieveRequestCount}`;
|
|
282
|
+
return new Promise((resolve, reject) => {
|
|
283
|
+
this.pendingRetrievals.set(requestId, {
|
|
284
|
+
resolve,
|
|
285
|
+
reject
|
|
286
|
+
});
|
|
287
|
+
this.postMessage({
|
|
288
|
+
type: "@statelyai.retrieve",
|
|
289
|
+
requestId,
|
|
290
|
+
format
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
resolveRetrievedSource(msg) {
|
|
295
|
+
const pending = this.pendingRetrievals.get(msg.requestId);
|
|
296
|
+
if (!pending) return;
|
|
297
|
+
this.pendingRetrievals.delete(msg.requestId);
|
|
298
|
+
const serialized = typeof msg.data === "string" ? msg.data : JSON.stringify(msg.data, null, 2);
|
|
299
|
+
pending.resolve(serialized);
|
|
300
|
+
}
|
|
301
|
+
handleErrorMessage(msg) {
|
|
302
|
+
if (msg.requestId) {
|
|
303
|
+
const pending = this.pendingRetrievals.get(msg.requestId);
|
|
304
|
+
if (pending) {
|
|
305
|
+
this.pendingRetrievals.delete(msg.requestId);
|
|
306
|
+
pending.reject(new Error(msg.message));
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
this.showError(msg.message);
|
|
311
|
+
}
|
|
312
|
+
updateSourceLocations(sourceLocations) {
|
|
313
|
+
this.watchedSourceUris.clear();
|
|
314
|
+
for (const location of sourceLocations?.states ?? []) if (location.uri !== this.options.rootUri) this.watchedSourceUris.add(location.uri);
|
|
315
|
+
}
|
|
316
|
+
async runWithSelfEdit(operation) {
|
|
317
|
+
this.selfEditCount++;
|
|
318
|
+
try {
|
|
319
|
+
return await operation();
|
|
320
|
+
} finally {
|
|
321
|
+
this.selfEditCount--;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
postMessage(msg) {
|
|
325
|
+
this.log("send", msg.type, msg);
|
|
326
|
+
if (!this.ready) {
|
|
327
|
+
this.pendingMessages.push(msg);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
this.sendMessage(msg);
|
|
331
|
+
}
|
|
332
|
+
showError(message) {
|
|
333
|
+
console.error(`Stately Editor: ${message}`);
|
|
334
|
+
}
|
|
335
|
+
log(direction, type, payload) {
|
|
336
|
+
if (this.options.debug) console.error(`[${direction}] ${type}`, payload ?? "");
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
async function openEditor(options) {
|
|
340
|
+
const fileName = path$1.resolve(options.fileName);
|
|
341
|
+
await fs.access(fileName);
|
|
342
|
+
const rootUri = fileNameToUri(fileName);
|
|
343
|
+
const rootDir = path$1.dirname(fileName);
|
|
344
|
+
let activeClient;
|
|
345
|
+
let session;
|
|
346
|
+
const server = http.createServer((request, response) => {
|
|
347
|
+
if (new URL(request.url ?? "/", `http://${request.headers.host}`).pathname !== "/") {
|
|
348
|
+
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
349
|
+
response.end("Not found");
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
353
|
+
response.end(getCliWebviewContent({
|
|
354
|
+
editorUrl: options.editorUrl,
|
|
355
|
+
apiKey: options.apiKey
|
|
356
|
+
}));
|
|
357
|
+
});
|
|
358
|
+
server.on("upgrade", (request, socket) => {
|
|
359
|
+
if (request.url !== "/ws") {
|
|
360
|
+
socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
const key = request.headers["sec-websocket-key"];
|
|
364
|
+
if (typeof key !== "string") {
|
|
365
|
+
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const accept = crypto.createHash("sha1").update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`).digest("base64");
|
|
369
|
+
socket.write([
|
|
370
|
+
"HTTP/1.1 101 Switching Protocols",
|
|
371
|
+
"Upgrade: websocket",
|
|
372
|
+
"Connection: Upgrade",
|
|
373
|
+
`Sec-WebSocket-Accept: ${accept}`,
|
|
374
|
+
"\r\n"
|
|
375
|
+
].join("\r\n"));
|
|
376
|
+
activeClient?.close();
|
|
377
|
+
session?.dispose();
|
|
378
|
+
activeClient = new WebSocketClient(socket);
|
|
379
|
+
session = new RemoteEditorSession({
|
|
380
|
+
...options,
|
|
381
|
+
fileName,
|
|
382
|
+
rootUri,
|
|
383
|
+
rootDir
|
|
384
|
+
}, (message) => activeClient?.send(message));
|
|
385
|
+
activeClient.onMessage((message) => {
|
|
386
|
+
session?.receiveMessage(message);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
await listen(server, options.port, options.host);
|
|
390
|
+
const address = server.address();
|
|
391
|
+
const port = typeof address === "object" && address ? address.port : options.port;
|
|
392
|
+
const editorPageUrl = `http://${options.host}:${port}`;
|
|
393
|
+
console.log(`Stately visual editor: ${editorPageUrl}`);
|
|
394
|
+
console.log(`Editing ${fileName}`);
|
|
395
|
+
if (options.shouldOpen) openBrowser(editorPageUrl);
|
|
396
|
+
const shutdown = () => {
|
|
397
|
+
session?.dispose();
|
|
398
|
+
activeClient?.close();
|
|
399
|
+
server.close();
|
|
400
|
+
};
|
|
401
|
+
process.once("SIGINT", () => {
|
|
402
|
+
shutdown();
|
|
403
|
+
process.exit(0);
|
|
404
|
+
});
|
|
405
|
+
process.once("SIGTERM", () => {
|
|
406
|
+
shutdown();
|
|
407
|
+
process.exit(0);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
async function collectSourceDocuments(options) {
|
|
411
|
+
const files = new Set([options.rootFileName]);
|
|
412
|
+
const maxFiles = Number.parseInt(process.env.STATELY_SYNC_MAX_FILES ?? "", 10) || DEFAULT_SYNC_MAX_FILES;
|
|
413
|
+
const maxBytes = Number.parseInt(process.env.STATELY_SYNC_MAX_BYTES ?? "", 10) || DEFAULT_SYNC_MAX_BYTES;
|
|
414
|
+
for (const uri of options.extraUris ?? []) files.add(sourceUriToFileName(uri));
|
|
415
|
+
await collectDirectoryFiles(options.rootDir, files, maxFiles);
|
|
416
|
+
const documents = [];
|
|
417
|
+
let totalBytes = 0;
|
|
418
|
+
for (const fileName of files) {
|
|
419
|
+
if (!isSupportedDocument(fileName)) continue;
|
|
420
|
+
const text = await fs.readFile(fileName, "utf8").catch(() => null);
|
|
421
|
+
if (text === null) continue;
|
|
422
|
+
totalBytes += Buffer.byteLength(text);
|
|
423
|
+
if (totalBytes > maxBytes) throw new Error(`Source payload exceeds ${maxBytes} bytes.`);
|
|
424
|
+
documents.push({
|
|
425
|
+
uri: fileNameToUri(fileName),
|
|
426
|
+
fileName,
|
|
427
|
+
text
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return documents;
|
|
431
|
+
}
|
|
432
|
+
async function collectDirectoryFiles(rootDir, files, maxFiles) {
|
|
433
|
+
const queue = [rootDir];
|
|
434
|
+
while (queue.length > 0 && files.size < maxFiles) {
|
|
435
|
+
const dir = queue.shift();
|
|
436
|
+
const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
437
|
+
for (const entry of entries) {
|
|
438
|
+
if (files.size >= maxFiles) break;
|
|
439
|
+
if (entry.name.startsWith(".") || shouldSkipDirectory(entry.name)) continue;
|
|
440
|
+
const fileName = path$1.join(dir, entry.name);
|
|
441
|
+
if (entry.isDirectory()) queue.push(fileName);
|
|
442
|
+
else if (entry.isFile() && isSupportedDocument(fileName)) files.add(fileName);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function shouldSkipDirectory(name) {
|
|
447
|
+
return [
|
|
448
|
+
"node_modules",
|
|
449
|
+
"dist",
|
|
450
|
+
"out",
|
|
451
|
+
"build",
|
|
452
|
+
"coverage",
|
|
453
|
+
".git"
|
|
454
|
+
].includes(name);
|
|
455
|
+
}
|
|
456
|
+
function isSupportedDocument(fileName) {
|
|
457
|
+
return supportedDocumentPattern.test(fileName);
|
|
458
|
+
}
|
|
459
|
+
async function applyFileReplacements(replacements, defaultUri) {
|
|
460
|
+
const replacementsByUri = /* @__PURE__ */ new Map();
|
|
461
|
+
for (const replacement of replacements) {
|
|
462
|
+
const uri = replacement.uri ?? defaultUri;
|
|
463
|
+
const existing = replacementsByUri.get(uri) ?? [];
|
|
464
|
+
existing.push(replacement);
|
|
465
|
+
replacementsByUri.set(uri, existing);
|
|
466
|
+
}
|
|
467
|
+
for (const [uri, uriReplacements] of replacementsByUri) {
|
|
468
|
+
const fileName = sourceUriToFileName(uri);
|
|
469
|
+
const nextText = applyReplacementsToText(await fs.readFile(fileName, "utf8"), uriReplacements);
|
|
470
|
+
await fs.writeFile(fileName, nextText, "utf8");
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function applyReplacementsToText(text, replacements) {
|
|
474
|
+
let updated = text;
|
|
475
|
+
const sorted = [...replacements].sort(compareReplacementRangesDescending);
|
|
476
|
+
for (const replacement of sorted) {
|
|
477
|
+
const lineStarts = getLineStarts(updated);
|
|
478
|
+
const start = offsetAt(lineStarts, replacement.range.startLine, replacement.range.startChar);
|
|
479
|
+
const end = offsetAt(lineStarts, replacement.range.endLine, replacement.range.endChar);
|
|
480
|
+
updated = `${updated.slice(0, start)}${replacement.newText}${updated.slice(end)}`;
|
|
481
|
+
}
|
|
482
|
+
return updated;
|
|
483
|
+
}
|
|
484
|
+
function watchWorkspace(rootDir, listener) {
|
|
485
|
+
let watcher;
|
|
486
|
+
const timers = /* @__PURE__ */ new Map();
|
|
487
|
+
const queue = (fileName) => {
|
|
488
|
+
const existing = timers.get(fileName);
|
|
489
|
+
if (existing) clearTimeout(existing);
|
|
490
|
+
timers.set(fileName, setTimeout(() => {
|
|
491
|
+
timers.delete(fileName);
|
|
492
|
+
listener(fileNameToUri(fileName));
|
|
493
|
+
}, 75));
|
|
494
|
+
};
|
|
495
|
+
try {
|
|
496
|
+
watcher = watch(rootDir, { recursive: true }, (_event, fileName) => {
|
|
497
|
+
if (fileName) queue(path$1.join(rootDir, fileName.toString()));
|
|
498
|
+
});
|
|
499
|
+
} catch {
|
|
500
|
+
watcher = watch(rootDir, (_event, fileName) => {
|
|
501
|
+
if (fileName) queue(path$1.join(rootDir, fileName.toString()));
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
return { dispose() {
|
|
505
|
+
watcher?.close();
|
|
506
|
+
for (const timer of timers.values()) clearTimeout(timer);
|
|
507
|
+
timers.clear();
|
|
508
|
+
} };
|
|
509
|
+
}
|
|
510
|
+
function getCliWebviewContent(options) {
|
|
511
|
+
const baseUrl = normalizedBaseUrl(options.editorUrl);
|
|
512
|
+
const url = new URL(`${baseUrl}/embed`);
|
|
513
|
+
if (options.apiKey) url.searchParams.set("api_key", options.apiKey);
|
|
514
|
+
return `<!DOCTYPE html>
|
|
515
|
+
<html lang="en" style="height:100%;margin:0">
|
|
516
|
+
<head>
|
|
517
|
+
<meta charset="UTF-8">
|
|
518
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
519
|
+
<style>
|
|
520
|
+
body { margin: 0; padding: 0; height: 100vh; overflow: hidden; }
|
|
521
|
+
iframe { border: 0; width: 100%; height: 100%; display: block; }
|
|
522
|
+
</style>
|
|
523
|
+
</head>
|
|
524
|
+
<body>
|
|
525
|
+
<iframe
|
|
526
|
+
id="stately-editor"
|
|
527
|
+
src="${escapeAttribute(url.toString())}"
|
|
528
|
+
allow="clipboard-read; clipboard-write"
|
|
529
|
+
></iframe>
|
|
530
|
+
<script>
|
|
531
|
+
const iframe = document.getElementById('stately-editor');
|
|
532
|
+
const socket = new WebSocket(
|
|
533
|
+
(location.protocol === 'https:' ? 'wss:' : 'ws:') + '//' + location.host + '/ws'
|
|
534
|
+
);
|
|
535
|
+
const pending = [];
|
|
536
|
+
|
|
537
|
+
function sendToCli(message) {
|
|
538
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
539
|
+
socket.send(JSON.stringify(message));
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
pending.push(message);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
socket.addEventListener('open', () => {
|
|
546
|
+
while (pending.length > 0) {
|
|
547
|
+
socket.send(JSON.stringify(pending.shift()));
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
socket.addEventListener('message', (event) => {
|
|
552
|
+
iframe.contentWindow?.postMessage(JSON.parse(event.data), '*');
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
window.addEventListener('message', (event) => {
|
|
556
|
+
if (!event.data?.type?.startsWith?.('@statelyai.')) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
sendToCli(event.data);
|
|
560
|
+
});
|
|
561
|
+
<\/script>
|
|
562
|
+
</body>
|
|
563
|
+
</html>`;
|
|
564
|
+
}
|
|
565
|
+
function encodeWebSocketFrame(payload, opcode = 1) {
|
|
566
|
+
const data = typeof payload === "string" ? Buffer.from(payload) : payload;
|
|
567
|
+
const headerLength = data.length < 126 ? 2 : data.length <= 65535 ? 4 : 10;
|
|
568
|
+
const frame = Buffer.alloc(headerLength + data.length);
|
|
569
|
+
frame[0] = 128 | opcode;
|
|
570
|
+
if (data.length < 126) {
|
|
571
|
+
frame[1] = data.length;
|
|
572
|
+
data.copy(frame, 2);
|
|
573
|
+
} else if (data.length <= 65535) {
|
|
574
|
+
frame[1] = 126;
|
|
575
|
+
frame.writeUInt16BE(data.length, 2);
|
|
576
|
+
data.copy(frame, 4);
|
|
577
|
+
} else {
|
|
578
|
+
frame[1] = 127;
|
|
579
|
+
frame.writeBigUInt64BE(BigInt(data.length), 2);
|
|
580
|
+
data.copy(frame, 10);
|
|
581
|
+
}
|
|
582
|
+
return frame;
|
|
583
|
+
}
|
|
584
|
+
function listen(server, port, host) {
|
|
585
|
+
return new Promise((resolve) => {
|
|
586
|
+
server.listen(port, host, resolve);
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function openBrowser(url) {
|
|
590
|
+
spawn(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open", process.platform === "win32" ? [
|
|
591
|
+
"/c",
|
|
592
|
+
"start",
|
|
593
|
+
"",
|
|
594
|
+
url
|
|
595
|
+
] : [url], {
|
|
596
|
+
detached: true,
|
|
597
|
+
stdio: "ignore"
|
|
598
|
+
}).unref();
|
|
599
|
+
}
|
|
600
|
+
function getValidationErrors(validations) {
|
|
601
|
+
return (validations ?? []).filter((validation) => validation.level === "error");
|
|
602
|
+
}
|
|
603
|
+
function formatValidationErrorMessage(errors) {
|
|
604
|
+
const [firstError] = errors;
|
|
605
|
+
if (!firstError) return "Cannot save Viz changes because the graph has validation errors.";
|
|
606
|
+
if (errors.length === 1) return `Cannot save Viz changes: ${firstError.message}`;
|
|
607
|
+
return `Cannot save Viz changes: ${errors.length} validation errors. First: ${firstError.message}`;
|
|
608
|
+
}
|
|
609
|
+
function sourceLocationUris(sourceLocations) {
|
|
610
|
+
return [
|
|
611
|
+
...sourceLocations?.root ? [sourceLocations.root.uri] : [],
|
|
612
|
+
...(sourceLocations?.states ?? []).map((state) => state.uri),
|
|
613
|
+
...(sourceLocations?.staticValues ?? []).map((value) => value.uri)
|
|
614
|
+
];
|
|
615
|
+
}
|
|
616
|
+
function fileNameToUri(fileName) {
|
|
617
|
+
return pathToFileURL(path$1.resolve(fileName)).toString();
|
|
618
|
+
}
|
|
619
|
+
function sourceUriToFileName(uriOrPath) {
|
|
620
|
+
if (uriOrPath.startsWith("file://")) return fileURLToPath(uriOrPath);
|
|
621
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(uriOrPath)) throw new Error(`Unsupported non-file URI: ${uriOrPath}`);
|
|
622
|
+
return path$1.resolve(uriOrPath);
|
|
623
|
+
}
|
|
624
|
+
function getLineStarts(text) {
|
|
625
|
+
const lineStarts = [0];
|
|
626
|
+
for (let index = 0; index < text.length; index += 1) if (text.charCodeAt(index) === 10) lineStarts.push(index + 1);
|
|
627
|
+
return lineStarts;
|
|
628
|
+
}
|
|
629
|
+
function offsetAt(lineStarts, line, character) {
|
|
630
|
+
const lineStart = lineStarts[line];
|
|
631
|
+
if (lineStart === void 0) return lineStarts[lineStarts.length - 1] + character;
|
|
632
|
+
return lineStart + character;
|
|
633
|
+
}
|
|
634
|
+
function compareReplacementRangesDescending(left, right) {
|
|
635
|
+
if (left.range.startLine !== right.range.startLine) return right.range.startLine - left.range.startLine;
|
|
636
|
+
return right.range.startChar - left.range.startChar;
|
|
637
|
+
}
|
|
638
|
+
function normalizedBaseUrl(value) {
|
|
639
|
+
return value.replace(/\/+$/, "");
|
|
640
|
+
}
|
|
641
|
+
function formatError(error, fallback) {
|
|
642
|
+
return error instanceof Error ? error.message : fallback;
|
|
643
|
+
}
|
|
644
|
+
function escapeAttribute(value) {
|
|
645
|
+
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
//#endregion
|
|
9
649
|
//#region src/cli.ts
|
|
10
650
|
function loadLocalEnv() {
|
|
11
651
|
if (typeof process.loadEnvFile !== "function") return;
|
|
@@ -136,7 +776,7 @@ var OpenCommand = class OpenCommand extends Command {
|
|
|
136
776
|
}) };
|
|
137
777
|
static flags = {
|
|
138
778
|
help: Flags.help({ char: "h" }),
|
|
139
|
-
"api-key": Flags.string({ description: "Stately API key used
|
|
779
|
+
"api-key": Flags.string({ description: "Stately API key used when the editor server requires auth" }),
|
|
140
780
|
"editor-url": Flags.string({ description: "Base URL for the Stately editor embed" }),
|
|
141
781
|
host: Flags.string({
|
|
142
782
|
description: "Local server host",
|
|
@@ -190,7 +830,7 @@ function isDirectExecution() {
|
|
|
190
830
|
if (!entryPath) return false;
|
|
191
831
|
const modulePath = fileURLToPath(import.meta.url);
|
|
192
832
|
try {
|
|
193
|
-
return fs.realpathSync(entryPath) === fs.realpathSync(modulePath);
|
|
833
|
+
return fs$1.realpathSync(entryPath) === fs$1.realpathSync(modulePath);
|
|
194
834
|
} catch {
|
|
195
835
|
return path.resolve(entryPath) === modulePath;
|
|
196
836
|
}
|
package/dist/embed.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as EmbedMode, c as ExportFormatMap, f as UploadResult, i as EmbedEventName, l as InitOptions, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig, u as MachineSourceLocations } from "./protocol-
|
|
1
|
+
import { a as EmbedMode, c as ExportFormatMap, f as UploadResult, i as EmbedEventName, l as InitOptions, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig, u as MachineSourceLocations } from "./protocol-BPuwbNCz.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/embed.d.ts
|
|
4
4
|
interface AssetConfig {
|
package/dist/embed.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as toInitMessage, i as createPendingExportManager, r as createEventRegistry, t as createPostMessageTransport } from "./transport-
|
|
1
|
+
import { a as toInitMessage, i as createPendingExportManager, r as createEventRegistry, t as createPostMessageTransport } from "./transport-DoCHBLTu.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/embed.ts
|
|
4
4
|
function buildEmbedUrl(options) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - Single-quoted strings
|
|
6
6
|
* - Omits undefined values
|
|
7
7
|
* - Supports RawCode for verbatim expressions
|
|
8
|
+
* - Supports inline expression directives for verbatim expressions
|
|
8
9
|
*/
|
|
9
10
|
const VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
10
11
|
var RawCode = class {
|
|
@@ -17,6 +18,7 @@ function raw(code) {
|
|
|
17
18
|
}
|
|
18
19
|
function serializeJS(value, indent = 0, step = 2) {
|
|
19
20
|
if (value instanceof RawCode) return value.code;
|
|
21
|
+
if (isInlineExpressionDirective(value)) return value.expr;
|
|
20
22
|
if (value === void 0) return "undefined";
|
|
21
23
|
if (value === null) return "null";
|
|
22
24
|
if (typeof value === "string") return `'${escapeString(value)}'`;
|
|
@@ -45,10 +47,18 @@ function serializeObject(obj, indent, step) {
|
|
|
45
47
|
return `{\n${entries.map(([key, val]) => {
|
|
46
48
|
const k = VALID_IDENT.test(key) ? key : `'${escapeString(key)}'`;
|
|
47
49
|
const serialized = serializeJS(val, inner, step);
|
|
48
|
-
if (val
|
|
50
|
+
if (isRawExpression(val) && serialized.includes("\n")) return `${pad}${k}: ${indentRawCode(serialized, inner)}`;
|
|
49
51
|
return `${pad}${k}: ${serialized}`;
|
|
50
52
|
}).join(",\n")},\n${closePad}}`;
|
|
51
53
|
}
|
|
54
|
+
function isRawExpression(value) {
|
|
55
|
+
return value instanceof RawCode || isInlineExpressionDirective(value);
|
|
56
|
+
}
|
|
57
|
+
function isInlineExpressionDirective(value) {
|
|
58
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
59
|
+
const directive = value;
|
|
60
|
+
return directive["@type"] === "code" && typeof directive.expr === "string";
|
|
61
|
+
}
|
|
52
62
|
function indentRawCode(code, indent) {
|
|
53
63
|
const lines = code.split("\n");
|
|
54
64
|
if (lines.length <= 1) return code;
|
|
@@ -250,7 +260,8 @@ function graphToMachineConfig(graph, options = {}) {
|
|
|
250
260
|
...node.data.invokes?.length ? { invoke: node.data.invokes.map((inv) => ({
|
|
251
261
|
src: inv.src,
|
|
252
262
|
id: inv.id,
|
|
253
|
-
...inv.input ? { input: inv.input } : void 0
|
|
263
|
+
...inv.input ? { input: inv.input } : void 0,
|
|
264
|
+
...inv.output ? { output: inv.output } : void 0
|
|
254
265
|
})) } : void 0,
|
|
255
266
|
...tags?.length ? { tags: tags.map(tagToString) } : void 0,
|
|
256
267
|
...showDescriptions && node.data.description ? { description: node.data.description } : void 0,
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { C as EventTypeData, S as DigraphNodeConfig, _ as studioMachineConverter, a as StatelyGraphData, b as DigraphConfig, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, o as StatelyGuard, r as StatelyEdgeData, t as StatelyAction, v as toStudioMachine, w as StateNodeJSONData, x as DigraphEdgeConfig, y as DigraphAction } from "./graph-BfezxFKJ.mjs";
|
|
2
2
|
import { a as ProjectMachine, c as StudioClientOptions, i as ProjectData, l as VerifyApiKeyResponse, n as ExtractedMachine, o as StudioApiError, r as GetMachineOptions, s as StudioClient, t as ExtractMachinesResponse, u as createStatelyClient } from "./studio-D2uQhrvX.mjs";
|
|
3
3
|
import { PlanSyncOptions, PullSyncResult, ResolvedSyncInput, SyncInputFormat, SyncPlan, SyncPlanSummary } from "./sync.mjs";
|
|
4
|
-
import { a as EmbedMode, c as ExportFormatMap, f as UploadResult, i as EmbedEventName, l as InitOptions, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig } from "./protocol-
|
|
4
|
+
import { a as EmbedMode, c as ExportFormatMap, f as UploadResult, i as EmbedEventName, l as InitOptions, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig } from "./protocol-BPuwbNCz.mjs";
|
|
5
5
|
import { AssetConfig, StatelyEmbed, StatelyEmbedOptions, createStatelyEmbed } from "./embed.mjs";
|
|
6
|
-
import { a as ManualActorOptions, c as createPostMessageTransport, i as InspectorEvents, l as createWebSocketTransport, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-
|
|
6
|
+
import { a as ManualActorOptions, c as createPostMessageTransport, i as InspectorEvents, l as createWebSocketTransport, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-ttRIjoCu.mjs";
|
|
7
7
|
import { ActionLocation, GraphPatch } from "./patchTypes.mjs";
|
|
8
8
|
import { JSONSchema7 } from "json-schema";
|
|
9
9
|
import { UnknownMachineConfig } from "xstate";
|
|
@@ -18,10 +18,23 @@ interface CodeGenGuard {
|
|
|
18
18
|
code?: string;
|
|
19
19
|
params?: Record<string, unknown>;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Serialized inline expression directive.
|
|
23
|
+
*
|
|
24
|
+
* Code generation treats this object as JavaScript/TypeScript source, not as a
|
|
25
|
+
* machine config object. It is used for fields such as invoke `input` and
|
|
26
|
+
* `output` where XState accepts mapper expressions.
|
|
27
|
+
*/
|
|
28
|
+
interface CodeExpression {
|
|
29
|
+
'@type': 'code';
|
|
30
|
+
lang: 'js' | 'ts';
|
|
31
|
+
expr: string;
|
|
32
|
+
}
|
|
21
33
|
interface CodeGenInvoke {
|
|
22
34
|
src: string;
|
|
23
35
|
id: string;
|
|
24
|
-
input?: unknown;
|
|
36
|
+
input?: Record<string, unknown> | CodeExpression;
|
|
37
|
+
output?: Record<string, unknown> | CodeExpression;
|
|
25
38
|
}
|
|
26
39
|
interface CodeGenNodeData {
|
|
27
40
|
nodeId?: string | null;
|
|
@@ -126,6 +139,7 @@ declare function graphToXStateTS(graph: CodeGenGraph, options?: XStateTSOptions)
|
|
|
126
139
|
* - Single-quoted strings
|
|
127
140
|
* - Omits undefined values
|
|
128
141
|
* - Supports RawCode for verbatim expressions
|
|
142
|
+
* - Supports inline expression directives for verbatim expressions
|
|
129
143
|
*/
|
|
130
144
|
declare class RawCode {
|
|
131
145
|
code: string;
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { n as createWebSocketTransport, t as createPostMessageTransport } from "./transport-
|
|
1
|
+
import { n as createWebSocketTransport, t as createPostMessageTransport } from "./transport-DoCHBLTu.mjs";
|
|
2
2
|
import { createStatelyEmbed } from "./embed.mjs";
|
|
3
3
|
import { createStatelyInspector } from "./inspect.mjs";
|
|
4
4
|
import { StudioApiError, createStatelyClient } from "./studio.mjs";
|
|
5
5
|
import { fromStudioMachine, studioMachineConverter, toStudioMachine } from "./graph.mjs";
|
|
6
|
-
import { a as graphToMachineConfig, c as serializeJS, i as jsonSchemaToTSType, n as contextSchemaToTSType, o as RawCode, r as eventsSchemaToTSType, s as raw, t as graphToXStateTS } from "./graphToXStateTS-
|
|
6
|
+
import { a as graphToMachineConfig, c as serializeJS, i as jsonSchemaToTSType, n as contextSchemaToTSType, o as RawCode, r as eventsSchemaToTSType, s as raw, t as graphToXStateTS } from "./graphToXStateTS-CtecEESq.mjs";
|
|
7
7
|
|
|
8
8
|
export { RawCode, StudioApiError, contextSchemaToTSType, createPostMessageTransport, createStatelyClient, createStatelyEmbed, createStatelyInspector, createWebSocketTransport, eventsSchemaToTSType, fromStudioMachine, graphToMachineConfig, graphToXStateTS, jsonSchemaToTSType, raw, serializeJS, studioMachineConverter, toStudioMachine };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as ExportFormatMap, d as ProtocolMessage, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat } from "./protocol-
|
|
1
|
+
import { c as ExportFormatMap, d as ProtocolMessage, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat } from "./protocol-BPuwbNCz.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/transport.d.ts
|
|
4
4
|
interface Transport {
|
package/dist/inspect.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as EmbedMode, c as ExportFormatMap, i as EmbedEventName, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat } from "./protocol-
|
|
2
|
-
import { a as ManualActorOptions, i as InspectorEvents, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-
|
|
1
|
+
import { a as EmbedMode, c as ExportFormatMap, i as EmbedEventName, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat } from "./protocol-BPuwbNCz.mjs";
|
|
2
|
+
import { a as ManualActorOptions, i as InspectorEvents, n as CreateInspectorOptions, o as createStatelyInspector, r as Inspector, s as Transport, t as AdoptedActor } from "./inspect-ttRIjoCu.mjs";
|
|
3
3
|
export { AdoptedActor, CreateInspectorOptions, EmbedEventHandler, EmbedEventMap, EmbedEventName, EmbedMode, ExportCallOptions, ExportFormat, ExportFormatMap, Inspector, InspectorEvents, ManualActorOptions, Transport, createStatelyInspector };
|
package/dist/inspect.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-
|
|
1
|
+
import { i as createPendingExportManager, n as createWebSocketTransport, r as createEventRegistry } from "./transport-DoCHBLTu.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/inspect.ts
|
|
4
4
|
const defaultSerializeSnapshot = (snapshot) => ({
|
package/dist/patchTypes.d.mts
CHANGED
|
@@ -8,6 +8,7 @@ type ActionLocation = {
|
|
|
8
8
|
edgeId: string;
|
|
9
9
|
group: 'transition';
|
|
10
10
|
};
|
|
11
|
+
type ImplementationSourceType = 'action' | 'guard' | 'actor' | 'delay';
|
|
11
12
|
type GraphPatch = {
|
|
12
13
|
op: 'createNode';
|
|
13
14
|
description?: string;
|
|
@@ -163,6 +164,29 @@ type GraphPatch = {
|
|
|
163
164
|
description?: string;
|
|
164
165
|
nodeId: string;
|
|
165
166
|
invokeId: string;
|
|
167
|
+
} | {
|
|
168
|
+
op: 'createImplementation';
|
|
169
|
+
description?: string;
|
|
170
|
+
sourceType: ImplementationSourceType;
|
|
171
|
+
implementation: {
|
|
172
|
+
id: string;
|
|
173
|
+
name?: string;
|
|
174
|
+
code?: string;
|
|
175
|
+
};
|
|
176
|
+
} | {
|
|
177
|
+
op: 'updateImplementation';
|
|
178
|
+
description?: string;
|
|
179
|
+
sourceType: ImplementationSourceType;
|
|
180
|
+
implementationId: string;
|
|
181
|
+
data: {
|
|
182
|
+
name?: string;
|
|
183
|
+
code?: string;
|
|
184
|
+
};
|
|
185
|
+
} | {
|
|
186
|
+
op: 'deleteImplementation';
|
|
187
|
+
description?: string;
|
|
188
|
+
sourceType: ImplementationSourceType;
|
|
189
|
+
implementationId: string;
|
|
166
190
|
} | {
|
|
167
191
|
op: 'setGuard';
|
|
168
192
|
description?: string;
|
|
@@ -222,4 +246,4 @@ type GraphPatch = {
|
|
|
222
246
|
};
|
|
223
247
|
};
|
|
224
248
|
//#endregion
|
|
225
|
-
export { ActionLocation, CanvasColor, EditorStateType, GraphPatch };
|
|
249
|
+
export { ActionLocation, CanvasColor, EditorStateType, GraphPatch, ImplementationSourceType };
|
package/dist/sync.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createStatelyClient } from "./studio.mjs";
|
|
2
2
|
import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
|
|
3
|
-
import { t as graphToXStateTS } from "./graphToXStateTS-
|
|
3
|
+
import { t as graphToXStateTS } from "./graphToXStateTS-CtecEESq.mjs";
|
|
4
4
|
import { getDiff, isEmptyDiff } from "@statelyai/graph";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
//#region src/clientUtils.ts
|
|
2
|
-
const jsonResultFormats = new Set([
|
|
2
|
+
const jsonResultFormats = new Set([
|
|
3
|
+
"digraph",
|
|
4
|
+
"json",
|
|
5
|
+
"xgraph"
|
|
6
|
+
]);
|
|
3
7
|
function createRequestId() {
|
|
4
8
|
const cryptoObject = globalThis.crypto;
|
|
5
9
|
if (cryptoObject?.randomUUID) return cryptoObject.randomUUID();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statelyai/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -63,8 +63,7 @@
|
|
|
63
63
|
"@types/json-schema": "^7.0.15",
|
|
64
64
|
"jsdom": "^27.4.0",
|
|
65
65
|
"tsdown": "0.21.0-beta.2",
|
|
66
|
-
"vitest": "^3.2.4"
|
|
67
|
-
"@statelyai/editor-sync": "0.0.0"
|
|
66
|
+
"vitest": "^3.2.4"
|
|
68
67
|
},
|
|
69
68
|
"scripts": {
|
|
70
69
|
"build": "tsdown",
|