@statelyai/sdk 0.6.0 → 0.7.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/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { createStatelyClient } from "./studio.mjs";
3
- import "./graphToXStateTS-CvXM8wHL.mjs";
4
- import { planSync, pullSync } from "./sync.mjs";
3
+ import { u as getStatelyPragma } from "./graphToXStateTS-Gzh0ZqbN.mjs";
4
+ import { planSync, pullSync, pushLocalMachineLinks } from "./sync.mjs";
5
5
  import fs from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
7
7
  import path from "node:path";
@@ -931,24 +931,26 @@ function describeCredentialBackend(backend, location) {
931
931
  }
932
932
 
933
933
  //#endregion
934
- //#region src/cli.ts
935
- const execFileAsync = promisify(execFile);
934
+ //#region src/projectConfig.ts
936
935
  const STATELY_CONFIG_FILE = "statelyai.json";
937
936
  const STATELY_CONFIG_SCHEMA_URL = "https://stately.ai/schemas/statelyai.json";
938
937
  const STATELY_CONFIG_VERSION = "1.0.0";
939
- function getDefaultSources(defaultXStateVersion) {
940
- return [{
941
- include: ["**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}"],
942
- exclude: [
943
- "**/*.test.*",
944
- "**/*.spec.*",
945
- "**/dist/**",
946
- "**/node_modules/**"
947
- ],
948
- format: "xstate",
949
- xstateVersion: defaultXStateVersion
950
- }];
951
- }
938
+ const DEFAULT_SOURCE_EXCLUDES = [
939
+ "**/*.test.*",
940
+ "**/*.spec.*",
941
+ "**/dist/**",
942
+ "**/node_modules/**"
943
+ ];
944
+ const CODE_SOURCE_EXTENSIONS = new Set([
945
+ ".ts",
946
+ ".tsx",
947
+ ".js",
948
+ ".jsx",
949
+ ".mts",
950
+ ".cts",
951
+ ".mjs",
952
+ ".cjs"
953
+ ]);
952
954
  function createStatelyProjectConfig(options) {
953
955
  const defaultXStateVersion = options.defaultXStateVersion ?? 5;
954
956
  return {
@@ -957,9 +959,145 @@ function createStatelyProjectConfig(options) {
957
959
  projectId: options.projectId,
958
960
  studioUrl: options.studioUrl,
959
961
  defaultXStateVersion,
960
- sources: getDefaultSources(defaultXStateVersion)
962
+ sources: []
961
963
  };
962
964
  }
965
+ async function readStatelyProjectConfig(options = {}) {
966
+ const rootDir = path.resolve(options.cwd ?? process.cwd());
967
+ const configPath = path.resolve(rootDir, options.configPath ?? STATELY_CONFIG_FILE);
968
+ const raw = await fs.readFile(configPath, "utf8");
969
+ return {
970
+ config: JSON.parse(raw),
971
+ configPath,
972
+ rootDir
973
+ };
974
+ }
975
+ async function walkFiles(rootDir, currentDir = rootDir) {
976
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
977
+ const files = [];
978
+ for (const entry of entries) {
979
+ if (entry.name === ".git") continue;
980
+ const absolutePath = path.join(currentDir, entry.name);
981
+ if (entry.isDirectory()) {
982
+ files.push(...await walkFiles(rootDir, absolutePath));
983
+ continue;
984
+ }
985
+ if (!entry.isFile()) continue;
986
+ files.push(path.relative(rootDir, absolutePath).replace(/\\/g, "/"));
987
+ }
988
+ return files;
989
+ }
990
+ function isCodeSourceFile(relativePath) {
991
+ return CODE_SOURCE_EXTENSIONS.has(path.extname(relativePath).toLowerCase());
992
+ }
993
+ function expandBraces(pattern) {
994
+ const match = pattern.match(/\{([^{}]+)\}/);
995
+ if (!match || match.index == null) return [pattern];
996
+ const [token, inner] = match;
997
+ return inner.split(",").flatMap((variant) => expandBraces(`${pattern.slice(0, match.index)}${variant}${pattern.slice(match.index + token.length)}`));
998
+ }
999
+ function globToRegExp(pattern) {
1000
+ let regex = "^";
1001
+ for (let index = 0; index < pattern.length; index += 1) {
1002
+ const char = pattern[index];
1003
+ const next = pattern[index + 1];
1004
+ if (char === "*") {
1005
+ if (next === "*") {
1006
+ const slashAfterGlobstar = pattern[index + 2] === "/";
1007
+ regex += slashAfterGlobstar ? "(?:.*/)?" : ".*";
1008
+ index += slashAfterGlobstar ? 2 : 1;
1009
+ continue;
1010
+ }
1011
+ regex += "[^/]*";
1012
+ continue;
1013
+ }
1014
+ if (char === "?") {
1015
+ regex += "[^/]";
1016
+ continue;
1017
+ }
1018
+ if ("\\.[]{}()+-^$|".includes(char)) {
1019
+ regex += `\\${char}`;
1020
+ continue;
1021
+ }
1022
+ regex += char;
1023
+ }
1024
+ regex += "$";
1025
+ return new RegExp(regex);
1026
+ }
1027
+ function matchesGlob(relativePath, pattern) {
1028
+ return expandBraces(pattern).some((expanded) => globToRegExp(expanded).test(relativePath));
1029
+ }
1030
+ function matchesAny(patterns, relativePath) {
1031
+ return (patterns ?? []).some((pattern) => matchesGlob(relativePath, pattern));
1032
+ }
1033
+ async function discoverCodeSourceFiles(options = {}) {
1034
+ return (await walkFiles(path.resolve(options.cwd ?? process.cwd()))).filter((relativePath) => isCodeSourceFile(relativePath)).filter((relativePath) => !matchesAny(DEFAULT_SOURCE_EXCLUDES, relativePath)).sort((left, right) => left.localeCompare(right));
1035
+ }
1036
+ function createSuggestedSource(include) {
1037
+ return {
1038
+ include: [include],
1039
+ exclude: [...DEFAULT_SOURCE_EXCLUDES],
1040
+ format: "xstate",
1041
+ xstateVersion: 5
1042
+ };
1043
+ }
1044
+ function suggestStatelySourceConfigs(relativePaths, defaultXStateVersion = 5) {
1045
+ const pending = new Set(relativePaths.map((relativePath) => relativePath.replace(/\\/g, "/")).filter(Boolean));
1046
+ const suggestions = [];
1047
+ const addSuggestion = (include, predicate) => {
1048
+ const matched = [...pending].filter(predicate);
1049
+ if (matched.length === 0) return;
1050
+ suggestions.push({
1051
+ ...createSuggestedSource(include),
1052
+ xstateVersion: defaultXStateVersion
1053
+ });
1054
+ for (const matchedPath of matched) pending.delete(matchedPath);
1055
+ };
1056
+ addSuggestion("**/*.machine.ts", (relativePath) => relativePath.endsWith(".machine.ts"));
1057
+ addSuggestion("src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => relativePath.startsWith("src/"));
1058
+ addSuggestion("packages/*/src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => /^packages\/[^/]+\/src\//.test(relativePath));
1059
+ addSuggestion("apps/*/src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => /^apps\/[^/]+\/src\//.test(relativePath));
1060
+ const byDirectory = /* @__PURE__ */ new Map();
1061
+ for (const relativePath of pending) {
1062
+ const directory = path.posix.dirname(relativePath);
1063
+ const key = directory === "." ? relativePath : directory;
1064
+ const bucket = byDirectory.get(key) ?? [];
1065
+ bucket.push(relativePath);
1066
+ byDirectory.set(key, bucket);
1067
+ }
1068
+ for (const [key, matchedPaths] of [...byDirectory.entries()].sort((left, right) => left[0].localeCompare(right[0]))) {
1069
+ if (matchedPaths.length > 1 && key !== "." && key !== matchedPaths[0]) {
1070
+ addSuggestion(`${key}/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}`, (relativePath) => relativePath.startsWith(`${key}/`));
1071
+ continue;
1072
+ }
1073
+ const exactPath = matchedPaths[0];
1074
+ addSuggestion(exactPath, (relativePath) => relativePath === exactPath);
1075
+ }
1076
+ return suggestions;
1077
+ }
1078
+ async function discoverStatelySourceFiles(options = {}) {
1079
+ const { config, rootDir } = options.config ? {
1080
+ config: options.config,
1081
+ rootDir: path.resolve(options.cwd ?? process.cwd())
1082
+ } : await readStatelyProjectConfig(options);
1083
+ const relativeFiles = await walkFiles(rootDir);
1084
+ const discovered = /* @__PURE__ */ new Map();
1085
+ for (const source of config.sources) for (const relativePath of relativeFiles) {
1086
+ if (!matchesAny(source.include, relativePath)) continue;
1087
+ if (matchesAny(source.exclude, relativePath)) continue;
1088
+ const filePath = path.join(rootDir, relativePath);
1089
+ if (!discovered.has(filePath)) discovered.set(filePath, {
1090
+ filePath,
1091
+ relativePath,
1092
+ source
1093
+ });
1094
+ }
1095
+ return [...discovered.values()].sort((left, right) => left.relativePath.localeCompare(right.relativePath));
1096
+ }
1097
+
1098
+ //#endregion
1099
+ //#region src/cli.ts
1100
+ const execFileAsync = promisify(execFile);
963
1101
  function loadLocalEnv() {
964
1102
  if (typeof process.loadEnvFile !== "function") return;
965
1103
  const cwdEnvPath = path.join(process.cwd(), ".env.local");
@@ -1090,6 +1228,22 @@ async function promptForApiKey() {
1090
1228
  rl.close();
1091
1229
  }
1092
1230
  }
1231
+ async function promptYesNo(question, defaultValue = true) {
1232
+ if (!process.stdin.isTTY || !process.stdout.isTTY) throw new Error("No interactive terminal available.");
1233
+ const rl = createInterface({
1234
+ input: process.stdin,
1235
+ output: process.stdout,
1236
+ terminal: true
1237
+ });
1238
+ try {
1239
+ const suffix = defaultValue ? " [Y/n] " : " [y/N] ";
1240
+ const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
1241
+ if (!answer) return defaultValue;
1242
+ return answer === "y" || answer === "yes";
1243
+ } finally {
1244
+ rl.close();
1245
+ }
1246
+ }
1093
1247
  function normalizeApiKey(value) {
1094
1248
  const trimmed = value?.trim();
1095
1249
  return trimmed ? trimmed : void 0;
@@ -1133,6 +1287,45 @@ async function initProject(options) {
1133
1287
  project
1134
1288
  };
1135
1289
  }
1290
+ async function scanProjectSources(options) {
1291
+ const cwd = path.resolve(options.cwd ?? process.cwd());
1292
+ const candidateRelativePaths = await discoverCodeSourceFiles({ cwd });
1293
+ const machineRelativePaths = [];
1294
+ for (const relativePath of candidateRelativePaths) {
1295
+ const filePath = path.join(cwd, relativePath);
1296
+ const contents = await fs.readFile(filePath, "utf8");
1297
+ let extracted;
1298
+ try {
1299
+ extracted = await options.client.code.extractMachines(contents);
1300
+ } catch {
1301
+ continue;
1302
+ }
1303
+ if (extracted.machines.length > 0) machineRelativePaths.push(relativePath);
1304
+ }
1305
+ return suggestStatelySourceConfigs(machineRelativePaths, Math.max(5, options.defaultXStateVersion ?? 5));
1306
+ }
1307
+ function supportsMachineDiscovery(file) {
1308
+ return file.source.format === "xstate" || file.source.format === "auto";
1309
+ }
1310
+ async function resolveConfiguredProject(options) {
1311
+ const { config, configPath, rootDir } = await readStatelyProjectConfig({
1312
+ cwd: options.cwd,
1313
+ configPath: options.configPath
1314
+ });
1315
+ const studioUrl = options.baseUrl ?? config.studioUrl;
1316
+ return {
1317
+ client: options.client ?? createStatelyClient({
1318
+ apiKey: options.apiKey,
1319
+ baseUrl: studioUrl
1320
+ }),
1321
+ config,
1322
+ configPath,
1323
+ files: await discoverStatelySourceFiles({
1324
+ cwd: rootDir,
1325
+ config
1326
+ })
1327
+ };
1328
+ }
1136
1329
  const sharedFlags = {
1137
1330
  help: Flags.help({ char: "h" }),
1138
1331
  "fail-on-changes": Flags.boolean({
@@ -1225,19 +1418,99 @@ var DiffCommand = class DiffCommand extends ParsedSyncCommand {
1225
1418
  var PullCommand = class PullCommand extends ParsedSyncCommand {
1226
1419
  static summary = "Pull a source locator into a local target file.";
1227
1420
  static description = "Resolves the source, materializes it in the target format inferred from the target file, and writes the result locally.";
1228
- static args = sharedArgs;
1421
+ static args = {
1422
+ source: Args.string({
1423
+ required: true,
1424
+ description: "Source locator. Supports a local linked file, machine ID, URL, or local file path."
1425
+ }),
1426
+ target: Args.string({
1427
+ required: false,
1428
+ description: "Optional local target path. Defaults to the source file when pulling a linked local file."
1429
+ })
1430
+ };
1229
1431
  async run() {
1230
1432
  const { args, flags } = await this.parseSync(PullCommand);
1231
1433
  const apiKey = (await resolveApiKey(flags["api-key"])).apiKey;
1434
+ const localCandidate = path.resolve(process.cwd(), args.source);
1435
+ let source = args.source;
1436
+ let target = args.target;
1437
+ if (!target && await fileExists(localCandidate)) {
1438
+ const pragma = getStatelyPragma(await fs.readFile(localCandidate, "utf8"), localCandidate);
1439
+ if (!pragma?.id) this.error(`No @statelyai id found in ${localCandidate}. Pass an explicit source machine ID or URL and target path.`);
1440
+ source = pragma.id;
1441
+ target = localCandidate;
1442
+ }
1443
+ if (!target) this.error("Missing target path. Pass `statelyai pull <machine-id|url> <file>` or `statelyai pull <linked-file>`.");
1232
1444
  const result = await pullSync({
1233
- source: args.source,
1234
- target: args.target,
1445
+ source,
1446
+ target,
1235
1447
  apiKey,
1236
1448
  baseUrl: flags["base-url"] ?? getDefaultBaseUrl()
1237
1449
  });
1238
1450
  this.log(`Pulled: ${result.source.locator} -> ${result.outputPath}\nTarget: ${result.target.kind} (${result.target.format})`);
1239
1451
  }
1240
1452
  };
1453
+ var PushCommand = class PushCommand extends Command {
1454
+ static enableJsonFlag = false;
1455
+ static summary = "Create or update remote machines for local source files.";
1456
+ static description = "Uses statelyai.json source discovery by default, creates remote machines for unlabeled local machine sources, updates already-linked machines, and writes returned @statelyai ids back into source files.";
1457
+ static args = { file: Args.string({
1458
+ required: false,
1459
+ description: "Optional local source file to push instead of scanning statelyai.json."
1460
+ }) };
1461
+ static flags = {
1462
+ help: Flags.help({ char: "h" }),
1463
+ "api-key": Flags.string({ description: "Stately API key used to create or update remote machines" }),
1464
+ "base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" }),
1465
+ config: Flags.string({ description: "Path to statelyai.json" })
1466
+ };
1467
+ async run() {
1468
+ const { args, flags } = await this.parse(PushCommand);
1469
+ const resolvedApiKey = await resolveApiKey(flags["api-key"]);
1470
+ if (!resolvedApiKey.apiKey) this.error("No API key configured. Use `statelyai login`, set `STATELY_API_KEY`, or pass `--api-key`.");
1471
+ const { client, config, files } = await resolveConfiguredProject({
1472
+ apiKey: resolvedApiKey.apiKey,
1473
+ baseUrl: flags["base-url"],
1474
+ configPath: flags.config
1475
+ });
1476
+ const candidateFiles = args.file ? [{
1477
+ filePath: path.resolve(args.file),
1478
+ relativePath: path.relative(process.cwd(), path.resolve(args.file)),
1479
+ source: {
1480
+ include: [args.file],
1481
+ format: "xstate",
1482
+ xstateVersion: config.defaultXStateVersion
1483
+ }
1484
+ }] : files.filter(supportsMachineDiscovery);
1485
+ if (candidateFiles.length === 0) {
1486
+ this.log("No matching local machine source files were discovered.");
1487
+ return;
1488
+ }
1489
+ const linked = [];
1490
+ const refreshed = [];
1491
+ const skipped = [];
1492
+ for (const file of candidateFiles) {
1493
+ if (!supportsMachineDiscovery(file)) {
1494
+ skipped.push(`${file.relativePath}: unsupported format ${file.source.format}`);
1495
+ continue;
1496
+ }
1497
+ const result = await pushLocalMachineLinks({
1498
+ source: file.filePath,
1499
+ apiKey: resolvedApiKey.apiKey,
1500
+ baseUrl: flags["base-url"] ?? config.studioUrl,
1501
+ client,
1502
+ project: { projectId: config.projectId },
1503
+ xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion)
1504
+ });
1505
+ if (result.created.length > 0) linked.push(`${file.relativePath}: ${result.created.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}]`).join(", ")}`);
1506
+ if (result.updated.length > 0) refreshed.push(`${file.relativePath}: ${result.updated.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}]`).join(", ")}`);
1507
+ for (const entry of result.skipped) skipped.push(`${file.relativePath} [${entry.machineIndex}]: ${entry.reason}`);
1508
+ }
1509
+ if (linked.length > 0) this.log(`Linked:\n${linked.join("\n")}`);
1510
+ if (refreshed.length > 0) this.log(`Updated:\n${refreshed.join("\n")}`);
1511
+ if (skipped.length > 0) this.log(`Skipped:\n${skipped.join("\n")}`);
1512
+ }
1513
+ };
1241
1514
  var OpenCommand = class OpenCommand extends Command {
1242
1515
  static enableJsonFlag = false;
1243
1516
  static summary = "Open a local file in the Stately visual editor.";
@@ -1304,6 +1577,10 @@ var InitCommand = class InitCommand extends Command {
1304
1577
  force: Flags.boolean({
1305
1578
  description: "Overwrite an existing statelyai.json file",
1306
1579
  default: false
1580
+ }),
1581
+ scan: Flags.boolean({
1582
+ description: "Scan the repo for machine-bearing files and suggest source globs to save into statelyai.json",
1583
+ default: false
1307
1584
  })
1308
1585
  };
1309
1586
  async run() {
@@ -1319,7 +1596,33 @@ var InitCommand = class InitCommand extends Command {
1319
1596
  visibility: flags.visibility
1320
1597
  }
1321
1598
  });
1322
- this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.`);
1599
+ if (flags.scan) {
1600
+ const scanClient = createStatelyClient({
1601
+ apiKey: resolvedApiKey.apiKey,
1602
+ baseUrl: flags["base-url"] ?? result.config.studioUrl
1603
+ });
1604
+ const suggestions = await scanProjectSources({
1605
+ cwd: path.dirname(result.configPath),
1606
+ client: scanClient,
1607
+ defaultXStateVersion: result.config.defaultXStateVersion
1608
+ });
1609
+ if (suggestions.length === 0) {
1610
+ this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.\nNo machine source globs were suggested. Edit statelyai.json before running statelyai push.`);
1611
+ return;
1612
+ }
1613
+ this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.`);
1614
+ this.log(`Suggested source globs:\n${suggestions.map((source) => `- ${source.include.join(", ")}`).join("\n")}`);
1615
+ if (await promptYesNo("Save these source globs to statelyai.json?", true)) {
1616
+ const nextConfig = {
1617
+ ...result.config,
1618
+ sources: suggestions
1619
+ };
1620
+ await fs.writeFile(result.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
1621
+ this.log("Saved scanned source globs to statelyai.json.");
1622
+ } else this.log("Left statelyai.json with an empty sources array. Edit it before running statelyai push.");
1623
+ return;
1624
+ }
1625
+ this.log(`Initialized project ${result.project.projectId} and wrote ${result.configPath}.\nNo source globs configured yet. Edit statelyai.json before running statelyai push, or rerun with --scan.`);
1323
1626
  }
1324
1627
  };
1325
1628
  var LoginCommand = class LoginCommand extends Command {
@@ -1381,6 +1684,7 @@ const COMMANDS = {
1381
1684
  plan: PlanCommand,
1382
1685
  diff: DiffCommand,
1383
1686
  pull: PullCommand,
1687
+ push: PushCommand,
1384
1688
  open: OpenCommand,
1385
1689
  init: InitCommand,
1386
1690
  login: LoginCommand,
@@ -1409,4 +1713,4 @@ function isDirectExecution() {
1409
1713
  if (isDirectExecution()) run();
1410
1714
 
1411
1715
  //#endregion
1412
- export { COMMANDS, createStatelyProjectConfig, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, resolveApiKey, run };
1716
+ export { COMMANDS, createStatelyProjectConfig, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, resolveApiKey, run, scanProjectSources };
package/dist/embed.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { a as EmbedMode, c as ExportFormatMap, d as MachineSourceLocations, f as ProjectEmbedMachine, i as EmbedEventName, l as InitOptions, m as UploadResult, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig, u as MachineInitOptions } from "./protocol-CEbWQPYe.mjs";
1
+ import { a as EmbedMode, c as ExportFormatMap, d as MachineSourceLocations, f as ProjectEmbedMachine, i as EmbedEventName, l as InitOptions, m as UploadResult, n as EmbedEventHandler, o as ExportCallOptions, r as EmbedEventMap, s as ExportFormat, t as CommentsConfig, u as MachineInitOptions } from "./protocol-DN4mH4jR.mjs";
2
+ import { AssetUploadAdapter } from "./assetStorage.mjs";
2
3
 
3
4
  //#region src/embed.d.ts
4
5
  interface AssetConfig {
@@ -7,9 +8,15 @@ interface AssetConfig {
7
8
  * Receives a real File object (reconstructed from serialized data).
8
9
  * Throwing or rejecting will show an error toast in the embed.
9
10
  */
10
- onUploadRequest: (file: File, context: {
11
+ onUploadRequest?: (file: File, context: {
11
12
  stateNodeId: string;
12
13
  }) => Promise<UploadResult>;
14
+ /**
15
+ * Storage adapter used by the embed upload bridge. This keeps the editor
16
+ * protocol storage-neutral while allowing integrations to plug in S3, R2,
17
+ * Supabase, or any other backing store.
18
+ */
19
+ adapter?: AssetUploadAdapter;
13
20
  /**
14
21
  * Accepted MIME types. Supports wildcards (e.g. 'image/*').
15
22
  * @default ['image/*']
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-C0eTgNNu.mjs";
1
+ import { a as toInitMessage, i as createPendingExportManager, r as createEventRegistry, t as createPostMessageTransport } from "./transport-C8UTS3Fa.mjs";
2
2
 
3
3
  //#region src/embed.ts
4
4
  function buildEmbedUrl(options) {
@@ -43,6 +43,13 @@ function createStatelyEmbed(options) {
43
43
  const pendingMessages = [];
44
44
  const events = createEventRegistry();
45
45
  const exportManager = createPendingExportManager((message) => send(message));
46
+ const assetUploadAdapter = options.assets?.adapter;
47
+ const uploadAsset = options.assets?.onUploadRequest ? (file, context) => options.assets.onUploadRequest(file, context) : assetUploadAdapter ? (file, context) => assetUploadAdapter.upload({
48
+ file,
49
+ ...context
50
+ }) : void 0;
51
+ const assetAccept = options.assets?.accept ?? assetUploadAdapter?.accept;
52
+ const assetMaxFileSize = options.assets?.maxFileSize ?? assetUploadAdapter?.maxFileSize;
46
53
  function send(msg) {
47
54
  if (!transport?.ready) {
48
55
  pendingMessages.push(msg);
@@ -63,11 +70,11 @@ function createStatelyEmbed(options) {
63
70
  case "@statelyai.ready": {
64
71
  const ready = data;
65
72
  flush();
66
- if (options.assets?.onUploadRequest) send({
73
+ if (uploadAsset) send({
67
74
  type: "@statelyai.uploadCapabilities",
68
75
  enabled: true,
69
- accept: options.assets.accept,
70
- maxFileSize: options.assets.maxFileSize
76
+ accept: assetAccept,
77
+ maxFileSize: assetMaxFileSize
71
78
  });
72
79
  options.onReady?.();
73
80
  events.emit("ready", { version: ready.version });
@@ -129,7 +136,7 @@ function createStatelyEmbed(options) {
129
136
  }
130
137
  case "@statelyai.uploadRequest": {
131
138
  const req = data;
132
- if (!options.assets?.onUploadRequest) {
139
+ if (!uploadAsset) {
133
140
  send({
134
141
  type: "@statelyai.error",
135
142
  requestId: req.requestId,
@@ -138,7 +145,7 @@ function createStatelyEmbed(options) {
138
145
  });
139
146
  break;
140
147
  }
141
- const maxSize = options.assets.maxFileSize ?? 10485760;
148
+ const maxSize = assetMaxFileSize ?? 10485760;
142
149
  if (req.file.size > maxSize) {
143
150
  send({
144
151
  type: "@statelyai.error",
@@ -151,8 +158,7 @@ function createStatelyEmbed(options) {
151
158
  const binary = atob(req.file.data);
152
159
  const bytes = new Uint8Array(binary.length);
153
160
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
154
- const file = new File([bytes], req.file.name, { type: req.file.mimeType });
155
- options.assets.onUploadRequest(file, { stateNodeId: req.stateNodeId }).then((result) => {
161
+ uploadAsset(new File([bytes], req.file.name, { type: req.file.mimeType }), { stateNodeId: req.stateNodeId }).then((result) => {
156
162
  send({
157
163
  type: "@statelyai.uploadResponse",
158
164
  requestId: req.requestId,
package/dist/graph.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { _ as studioMachineConverter, a as StatelyGraphData, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, n as StatelyActorImplementation, o as StatelyGuard, p as StudioEventTypeData, r as StatelyEdgeData, s as StatelyImplementation, t as StatelyAction, u as StatelyTagImplementation, v as toStudioMachine } from "./graph-CB-ALrdk.mjs";
1
+ import { _ as studioMachineConverter, a as StatelyGraphData, c as StatelyInvoke, d as StudioAction, f as StudioEdge, g as fromStudioMachine, h as StudioNode, i as StatelyGraph, l as StatelyNodeData, m as StudioMachine, n as StatelyActorImplementation, o as StatelyGuard, p as StudioEventTypeData, r as StatelyEdgeData, s as StatelyImplementation, t as StatelyAction, u as StatelyTagImplementation, v as toStudioMachine } from "./graph-DpBGHZwl.mjs";
2
2
  export { StatelyAction, StatelyActorImplementation, StatelyEdgeData, StatelyGraph, StatelyGraphData, StatelyGuard, StatelyImplementation, StatelyInvoke, StatelyNodeData, StatelyTagImplementation, StudioAction, StudioEdge, StudioEventTypeData, StudioMachine, StudioNode, fromStudioMachine, studioMachineConverter, toStudioMachine };
package/dist/graph.mjs CHANGED
@@ -2,8 +2,12 @@ import { createFormatConverter, createGraph } from "@statelyai/graph";
2
2
 
3
3
  //#region src/graph.ts
4
4
  const EXPR_ACTION_TYPE = "xstate.expr";
5
+ function stripMarkdownLinks(code) {
6
+ return code.replace(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, "$1");
7
+ }
5
8
  function stripExportDefault(code) {
6
- return code.match(/^export\s+default\s+(.+)/s)?.[1]?.trim() ?? code.trim();
9
+ const normalized = stripMarkdownLinks(code).trim();
10
+ return normalized.match(/^export\s+default\s+(.+)/s)?.[1]?.trim() ?? normalized;
7
11
  }
8
12
  function toJsonObject(value) {
9
13
  return value;
@@ -332,7 +336,7 @@ function fromStudioMachine(studioMachine) {
332
336
  ...edge.data.guard ? { guard: {
333
337
  type: edge.data.guard.type,
334
338
  params: toUnknownRecord(edge.data.guard.params),
335
- ...edge.data.guard.kind === "inline" ? { code: edge.data.guard.type } : {}
339
+ ...edge.data.guard.kind === "inline" ? { code: stripExportDefault(studioMachine.implementations?.guards?.[edge.data.guard.type]?.code ?? edge.data.guard.type) } : {}
336
340
  } } : {},
337
341
  actions: edge.data.actions.map((action) => fromStudioAction(action, studioMachine.implementations?.actions)),
338
342
  ...edge.data.description ? { description: edge.data.description } : {},
@@ -350,9 +354,9 @@ function fromStudioMachine(studioMachine) {
350
354
  data: {
351
355
  ...studioMachine.schemas ? { schemas: studioMachine.schemas } : {},
352
356
  ...studioMachine.implementations ? { implementations: {
353
- actions: Object.values(studioMachine.implementations.actions).map(fromActionSource),
354
- guards: Object.values(studioMachine.implementations.guards).map(fromGuardSource),
355
- actors: Object.values(studioMachine.implementations.actors).map(fromActorSource),
357
+ actions: Object.entries(studioMachine.implementations.actions).filter(([key]) => !key.startsWith("inline:")).map(([, source]) => fromActionSource(source)),
358
+ guards: Object.entries(studioMachine.implementations.guards).filter(([key]) => !key.startsWith("inline:")).map(([, guard]) => fromGuardSource(guard)),
359
+ actors: Object.entries(studioMachine.implementations.actors).filter(([key]) => !key.startsWith("inline:") && !/:invocation\[\d+\]$/.test(key)).map(([, actor]) => fromActorSource(actor)),
356
360
  delays: [],
357
361
  tags: []
358
362
  } } : {}