@statelyai/sdk 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sync.mjs CHANGED
@@ -1,313 +1,10 @@
1
1
  import { createStatelyClient } from "./studio.mjs";
2
2
  import { fromStudioMachine, toStudioMachine } from "./graph.mjs";
3
+ import { t as graphToXStateTS } from "./graphToXStateTS-C6HQUrBB.mjs";
3
4
  import { getDiff, isEmptyDiff } from "@statelyai/graph";
4
5
  import fs from "node:fs/promises";
5
6
  import path from "node:path";
6
7
 
7
- //#region src/serializeJS.ts
8
- const VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
9
- var RawCode = class {
10
- constructor(code) {
11
- this.code = code;
12
- }
13
- };
14
- function raw(code) {
15
- return new RawCode(code);
16
- }
17
- function serializeJS(value, indent = 0, step = 2) {
18
- if (value instanceof RawCode) return value.code;
19
- if (value === void 0) return "undefined";
20
- if (value === null) return "null";
21
- if (typeof value === "string") return `'${escapeString(value)}'`;
22
- if (typeof value === "number" || typeof value === "boolean") return String(value);
23
- if (Array.isArray(value)) return serializeArray(value, indent, step);
24
- if (typeof value === "object") return serializeObject(value, indent, step);
25
- return String(value);
26
- }
27
- function escapeString(value) {
28
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
29
- }
30
- function serializeArray(array, indent, step) {
31
- const values = array.filter((item) => item !== void 0);
32
- if (values.length === 0) return "[]";
33
- const innerIndent = indent + step;
34
- const pad = " ".repeat(innerIndent);
35
- const closePad = " ".repeat(indent);
36
- return `[\n${values.map((item) => `${pad}${serializeJS(item, innerIndent, step)}`).join(",\n")},\n${closePad}]`;
37
- }
38
- function serializeObject(object, indent, step) {
39
- const entries = Object.entries(object).filter(([, value]) => value !== void 0);
40
- if (entries.length === 0) return "{}";
41
- const innerIndent = indent + step;
42
- const pad = " ".repeat(innerIndent);
43
- const closePad = " ".repeat(indent);
44
- return `{\n${entries.map(([key, value]) => {
45
- const serializedKey = VALID_IDENT.test(key) ? key : `'${escapeString(key)}'`;
46
- const serializedValue = serializeJS(value, innerIndent, step);
47
- if (value instanceof RawCode && serializedValue.includes("\n")) return `${pad}${serializedKey}: ${indentRawCode(serializedValue, innerIndent)}`;
48
- return `${pad}${serializedKey}: ${serializedValue}`;
49
- }).join(",\n")},\n${closePad}}`;
50
- }
51
- function indentRawCode(code, indent) {
52
- const lines = code.split("\n");
53
- if (lines.length <= 1) return code;
54
- const pad = " ".repeat(indent);
55
- return [lines[0], ...lines.slice(1).map((line) => line.trim() === "" ? "" : `${pad}${line}`)].join("\n");
56
- }
57
-
58
- //#endregion
59
- //#region src/graphToMachineConfig.ts
60
- function singleOrArray(items) {
61
- return items.length === 1 ? items[0] : items;
62
- }
63
- function singleOrArrayRecord(record) {
64
- const next = {};
65
- for (const [key, value] of Object.entries(record)) next[key] = singleOrArray(value);
66
- return next;
67
- }
68
- function simplifyAttributes(value) {
69
- for (const key of Object.keys(value)) {
70
- const item = value[key];
71
- if (item === void 0 || item === null) {
72
- delete value[key];
73
- continue;
74
- }
75
- if (Array.isArray(item)) {
76
- if (item.length === 0) delete value[key];
77
- else if (item.length === 1) value[key] = item[0];
78
- continue;
79
- }
80
- if (typeof item === "object" && Object.keys(item).length === 0) {
81
- delete value[key];
82
- continue;
83
- }
84
- if (typeof item === "string" && item === "") delete value[key];
85
- }
86
- return value;
87
- }
88
- function deepSimplify(config) {
89
- for (const mapKey of ["on", "after"]) {
90
- const value = config[mapKey];
91
- if (!value || typeof value !== "object" || Array.isArray(value)) continue;
92
- for (const transition of Object.values(value)) if (transition && typeof transition === "object" && !Array.isArray(transition)) simplifyAttributes(transition);
93
- else if (Array.isArray(transition)) {
94
- for (const item of transition) if (item && typeof item === "object") simplifyAttributes(item);
95
- }
96
- }
97
- if (config.always && typeof config.always === "object") if (Array.isArray(config.always)) {
98
- for (const item of config.always) if (item && typeof item === "object") simplifyAttributes(item);
99
- } else simplifyAttributes(config.always);
100
- simplifyAttributes(config);
101
- if (config.states && typeof config.states === "object") for (const state of Object.values(config.states)) deepSimplify(state);
102
- return config;
103
- }
104
- function serializeActionItem(action) {
105
- const { type, params } = action;
106
- switch (type) {
107
- case "xstate.raise": return raw(`raise(${serializeJSValue(params?.event ?? { type: "unknown" })})`);
108
- case "xstate.sendTo": return raw(`sendTo(${serializeJSValue(params?.to ?? "unknown")}, ${serializeJSValue(params?.event ?? { type: "unknown" })})`);
109
- case "xstate.cancel": return raw(`cancel(${serializeJSValue(params?.sendId ?? "unknown")})`);
110
- case "xstate.log": return raw(`log(${serializeJSValue(params?.label ?? "")})`);
111
- default:
112
- if (!params || Object.keys(params).length === 0) return { type };
113
- return {
114
- type,
115
- params
116
- };
117
- }
118
- }
119
- function serializeJSValue(value) {
120
- if (typeof value === "string") return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
121
- if (value && typeof value === "object") return rawObject(value);
122
- return String(value);
123
- }
124
- function rawObject(value) {
125
- return `{ ${Object.entries(value).map(([key, item]) => `${key}: ${serializeJSValue(item)}`).join(", ")} }`;
126
- }
127
- function graphToMachineConfig(graph, options = {}) {
128
- const { showDescriptions = true } = options;
129
- function getNodeConfig(nodeId) {
130
- const node = graph.nodes.find((candidate) => candidate.id === nodeId);
131
- if (!node) throw new Error(`Node not found: ${nodeId}`);
132
- const config = {};
133
- const initialNode = node.data.initialId ? graph.nodes.find((candidate) => candidate.id === node.data.initialId) : null;
134
- Object.assign(config, {
135
- id: node.parentId == null ? graph.id : node.id.endsWith(`.${node.data.key}`) ? void 0 : node.id,
136
- ...node.data.type ? { type: node.data.type } : {},
137
- ...initialNode ? { initial: initialNode.data.key } : {},
138
- ...node.data.entry?.length ? { entry: node.data.entry.map(serializeActionItem) } : {},
139
- ...node.data.exit?.length ? { exit: node.data.exit.map(serializeActionItem) } : {},
140
- ...node.data.invokes?.length ? { invoke: node.data.invokes.map((invoke) => ({
141
- src: invoke.src,
142
- id: invoke.id,
143
- ...invoke.input ? { input: invoke.input } : {}
144
- })) } : {},
145
- ...node.data.tags?.length ? { tags: node.data.tags } : {},
146
- ...showDescriptions && node.data.description ? { description: node.data.description } : {},
147
- ...node.data.history ? { history: node.data.history } : {}
148
- });
149
- const childNodes = graph.nodes.filter((candidate) => candidate.parentId === node.id);
150
- const edges = graph.edges.filter((edge) => edge.sourceId === node.id);
151
- if (edges.length > 0) {
152
- const on = {};
153
- const after = {};
154
- const always = [];
155
- for (const edge of edges) {
156
- const targetNode = graph.nodes.find((candidate) => candidate.id === edge.targetId);
157
- const target = targetNode && targetNode.parentId === node.parentId ? targetNode.data.key : targetNode ? `#${targetNode.id}` : void 0;
158
- const transition = {
159
- ...target ? { target } : {},
160
- ...edge.data.transitionType === "reenter" ? { reenter: true } : {},
161
- ...edge.data.guard ? { guard: edge.data.guard } : {},
162
- ...edge.data.actions?.length ? { actions: edge.data.actions.map(serializeActionItem) } : {},
163
- ...edge.data.description ? { description: edge.data.description } : {}
164
- };
165
- if (edge.data.eventType === "") {
166
- always.push(transition);
167
- continue;
168
- }
169
- if (edge.data.eventType.startsWith("xstate.after.")) {
170
- const delay = edge.data.eventType.slice(13).split(".")[0];
171
- after[delay] ??= [];
172
- after[delay].push(transition);
173
- continue;
174
- }
175
- on[edge.data.eventType] ??= [];
176
- on[edge.data.eventType].push(transition);
177
- }
178
- if (Object.keys(on).length > 0) config.on = singleOrArrayRecord(on);
179
- if (Object.keys(after).length > 0) config.after = singleOrArrayRecord(after);
180
- if (always.length > 0) config.always = singleOrArray(always);
181
- }
182
- if (childNodes.length > 0) config.states = Object.fromEntries(childNodes.map((child) => [child.data.key, getNodeConfig(child.id)]));
183
- return config;
184
- }
185
- const rootNode = graph.nodes.find((candidate) => candidate.parentId == null);
186
- if (!rootNode) throw new Error("No root node found");
187
- return deepSimplify(getNodeConfig(rootNode.id));
188
- }
189
-
190
- //#endregion
191
- //#region src/jsonSchemaToTSType.ts
192
- function jsonSchemaToTSType(schema) {
193
- if (!schema) return "unknown";
194
- if ("const" in schema) {
195
- const value = schema.const;
196
- return typeof value === "string" ? `'${value}'` : String(value);
197
- }
198
- if (schema.type === "array") return `Array<${jsonSchemaToTSType(schema.items)}>`;
199
- if (schema.type === "object") {
200
- const props = schema.properties;
201
- if (!props || Object.keys(props).length === 0) return "Record<string, unknown>";
202
- return `{ ${Object.entries(props).map(([key, value]) => `${key}: ${jsonSchemaToTSType(value)}`).join("; ")} }`;
203
- }
204
- switch (schema.type) {
205
- case "string": return "string";
206
- case "number": return "number";
207
- case "boolean": return "boolean";
208
- case "null": return "null";
209
- default: return "unknown";
210
- }
211
- }
212
- function contextSchemaToTSType(context) {
213
- if (!context || Object.keys(context).length === 0) return null;
214
- return `{ ${Object.entries(context).map(([key, schema]) => `${key}: ${jsonSchemaToTSType(schema)}`).join("; ")} }`;
215
- }
216
- function eventsSchemaToTSType(events) {
217
- if (!events || Object.keys(events).length === 0) return null;
218
- return Object.entries(events).map(([eventType, schema]) => {
219
- const props = schema.properties;
220
- const extraProps = Object.entries(props ?? {}).filter(([key]) => key !== "type").map(([key, value]) => `${key}?: ${jsonSchemaToTSType(value)}`);
221
- if (extraProps.length === 0) return `{ type: '${eventType}' }`;
222
- return `{ type: '${eventType}'; ${extraProps.join("; ")} }`;
223
- }).join("\n | ");
224
- }
225
-
226
- //#endregion
227
- //#region src/graphToXStateTS.ts
228
- const BUILTIN_ACTION_IMPORTS = {
229
- "xstate.raise": "raise",
230
- "xstate.sendTo": "sendTo",
231
- "xstate.cancel": "cancel",
232
- "xstate.log": "log"
233
- };
234
- function graphToXStateTS(graph, options = {}) {
235
- const { exportStyle = "named" } = options;
236
- const schemas = graph.data.schemas;
237
- const impls = graph.data.implementations;
238
- const hasSchemas = Boolean(schemas && (schemas.context || schemas.events || schemas.input || schemas.output));
239
- const hasActions = Boolean(impls?.actions.length);
240
- const hasGuards = Boolean(impls?.guards.length);
241
- const hasActors = Boolean(impls?.actors.length);
242
- const hasDelays = Boolean(impls?.delays.length);
243
- const hasSetup = hasSchemas || hasActions || hasGuards || hasActors || hasDelays;
244
- const imports = new Set([hasSetup ? "setup" : "createMachine"]);
245
- for (const node of graph.nodes) for (const action of [...node.data.entry ?? [], ...node.data.exit ?? []]) {
246
- const builtInImport = BUILTIN_ACTION_IMPORTS[action.type];
247
- if (builtInImport) imports.add(builtInImport);
248
- }
249
- for (const edge of graph.edges) for (const action of edge.data.actions ?? []) {
250
- const builtInImport = BUILTIN_ACTION_IMPORTS[action.type];
251
- if (builtInImport) imports.add(builtInImport);
252
- }
253
- const machineConfig = graphToMachineConfig(graph);
254
- if (hasActors) imports.add("fromPromise");
255
- const machineExpr = hasSetup ? `setup(${serializeJS(buildSetupObject(graph), 0)}).createMachine(${serializeJS(machineConfig, 0)})` : `createMachine(${serializeJS(machineConfig, 0)})`;
256
- const lines = [
257
- `import { ${Array.from(imports).join(", ")} } from 'xstate';`,
258
- "",
259
- `const machine = ${machineExpr};`
260
- ];
261
- if (exportStyle === "named") lines.push("", "export { machine };");
262
- else if (exportStyle === "default") lines.push("", "export default machine;");
263
- return `${lines.join("\n")}\n`;
264
- }
265
- function buildSetupObject(graph) {
266
- const setup = {};
267
- const schemas = graph.data.schemas;
268
- const impls = graph.data.implementations;
269
- const types = buildTypesBlock(schemas);
270
- if (types) setup.types = types;
271
- if (impls?.actions.length) setup.actions = buildActionsBlock(impls.actions);
272
- if (impls?.guards.length) setup.guards = buildGuardsBlock(impls.guards);
273
- if (impls?.actors.length) setup.actors = buildActorsBlock(impls.actors);
274
- if (impls?.delays.length) setup.delays = buildDelaysBlock(impls.delays);
275
- return setup;
276
- }
277
- function buildTypesBlock(schemas) {
278
- if (!schemas) return;
279
- const types = {};
280
- const contextType = contextSchemaToTSType(schemas.context);
281
- if (contextType) types.context = raw(`{} as ${contextType}`);
282
- const eventsType = eventsSchemaToTSType(schemas.events);
283
- if (eventsType) types.events = raw(`{} as\n | ${eventsType}`);
284
- if (schemas.input) types.input = raw(`{} as ${jsonSchemaToTSType(schemas.input)}`);
285
- if (schemas.output) types.output = raw(`{} as ${jsonSchemaToTSType(schemas.output)}`);
286
- return Object.keys(types).length > 0 ? types : void 0;
287
- }
288
- function buildActionsBlock(actions) {
289
- return Object.fromEntries(actions.map((action) => [action.name, (() => {
290
- const paramsType = action.paramsSchema ? `: ${jsonSchemaToTSType(action.paramsSchema)}` : "";
291
- return raw(action.code?.body ? `function ({ context, event }, params${paramsType}) {\n ${action.code.body}\n}` : `function ({ context, event }) {\n // TODO: implement ${action.name}\n}`);
292
- })()]));
293
- }
294
- function buildGuardsBlock(guards) {
295
- return Object.fromEntries(guards.map((guard) => [guard.name, (() => {
296
- const paramsType = guard.paramsSchema ? `: ${jsonSchemaToTSType(guard.paramsSchema)}` : "";
297
- return raw(guard.code?.body ? `function ({ context, event }, params${paramsType}) {\n ${guard.code.body}\n}` : `function ({ context, event }) {\n // TODO: implement ${guard.name}\n return false;\n}`);
298
- })()]));
299
- }
300
- function buildActorsBlock(actors) {
301
- return Object.fromEntries(actors.map((actor) => [actor.name, (() => {
302
- const inputType = actor.inputSchema ? `: { input: ${jsonSchemaToTSType(actor.inputSchema)} }` : "";
303
- return raw(actor.code?.body ? `fromPromise(async ({ input }${inputType}) => {\n ${actor.code.body}\n})` : `fromPromise(async ({ input }) => {\n // TODO: implement ${actor.name}\n})`);
304
- })()]));
305
- }
306
- function buildDelaysBlock(delays) {
307
- return Object.fromEntries(delays.map((delay) => [delay.name, raw(delay.code?.body ? `function ({ context, event }) {\n ${delay.code.body}\n}` : `function () {\n // TODO: implement ${delay.name}\n return 1000;\n}`)]));
308
- }
309
-
310
- //#endregion
311
8
  //#region src/sync.ts
312
9
  function isUrl(value) {
313
10
  try {
@@ -95,7 +95,8 @@ function toInitMessage(options) {
95
95
  depth: options.depth,
96
96
  leftPanels: options.panels?.leftPanels,
97
97
  rightPanels: options.panels?.rightPanels,
98
- activePanels: options.panels?.activePanels
98
+ activePanels: options.panels?.activePanels,
99
+ comments: options.comments
99
100
  };
100
101
  }
101
102
 
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@statelyai/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
8
8
  "type": "module",
9
+ "bin": {
10
+ "stately": "./dist/cli.mjs"
11
+ },
9
12
  "main": "./dist/index.mjs",
10
13
  "types": "./dist/index.d.mts",
11
14
  "exports": {
@@ -35,9 +38,20 @@
35
38
  }
36
39
  },
37
40
  "dependencies": {
38
- "@statelyai/graph": "^0.4.0"
41
+ "@oclif/core": "^4.10.3",
42
+ "@statelyai/graph": "^0.9.0",
43
+ "xstate": "^5.0.0"
44
+ },
45
+ "oclif": {
46
+ "bin": "stately",
47
+ "commands": {
48
+ "strategy": "explicit",
49
+ "target": "./dist/cli.mjs",
50
+ "identifier": "COMMANDS"
51
+ }
39
52
  },
40
53
  "devDependencies": {
54
+ "@types/json-schema": "^7.0.15",
41
55
  "jsdom": "^27.4.0",
42
56
  "tsdown": "0.21.0-beta.2",
43
57
  "typescript": "^5.9.3",