statelyai 0.3.7 → 0.4.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 CHANGED
@@ -4,6 +4,7 @@ CLI implementation for Stately.
4
4
 
5
5
  This package contains the `statelyai` command implementation.
6
6
 
7
+ <!-- happy-path CLI usage and statelyai.json shape derived from packages/cli/src/cli.ts#COMMANDS and packages/cli/src/projectConfig.ts -->
7
8
  ## Happy Path
8
9
 
9
10
  1. Get an API key from [Stately API Key settings](https://stately.ai/registry/user/my-settings?tab=API+Key).
@@ -25,18 +26,42 @@ statelyai init
25
26
  statelyai init --scan
26
27
  ```
27
28
 
29
+ That writes a `statelyai.json` like:
30
+
31
+ ```json
32
+ {
33
+ "$schema": "https://stately.ai/schemas/statelyai.json",
34
+ "version": "1.0.0",
35
+ "projectId": "project_123",
36
+ "studioUrl": "https://stately.ai",
37
+ "defaultXStateVersion": 5,
38
+ "include": ["src/**/*.ts"],
39
+ "exclude": ["**/*.test.*", "**/*.spec.*"]
40
+ }
41
+ ```
42
+
28
43
  5. Push local machines:
29
44
 
30
45
  ```bash
31
46
  statelyai push
32
47
  ```
33
48
 
49
+ Preview what `push` would do without updating Studio or local files:
50
+
51
+ ```bash
52
+ statelyai push --dry-run
53
+ ```
54
+
55
+ If a saved `// @statelyai id=...` points to a deleted or inaccessible remote machine, `push` will prompt to relink the file as a new remote machine and replace the local id.
56
+
34
57
  6. Pull remote changes back into linked local files:
35
58
 
36
59
  ```bash
37
60
  statelyai pull
38
61
  ```
39
62
 
63
+ `pull` skips locally modified files unless you pass `--force`.
64
+
40
65
  Run it without installing it globally:
41
66
 
42
67
  ```bash
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { l as run } from "./cli-B9CRx5db.mjs";
2
+ import { u as run } from "./cli-DkWxDq0T.mjs";
3
3
 
4
4
  //#region src/bin.ts
5
5
  run();
@@ -12,7 +12,7 @@ import * as crypto from "node:crypto";
12
12
  import * as http from "node:http";
13
13
  import * as https from "node:https";
14
14
  import os from "node:os";
15
- import { createStatelyClient, getStatelyPragma } from "@statelyai/sdk";
15
+ import { StudioApiError, createStatelyClient, getStatelyPragma } from "@statelyai/sdk";
16
16
  import { planSync, pullSync, pushLocalMachineLinks } from "@statelyai/sdk/sync";
17
17
 
18
18
  //#region src/cliHost.ts
@@ -945,6 +945,15 @@ const CODE_SOURCE_EXTENSIONS = new Set([
945
945
  ".cjs"
946
946
  ]);
947
947
  const execFileAsync$1 = promisify(execFile);
948
+ const ALWAYS_IGNORED_DIRECTORIES = new Set([
949
+ ".git",
950
+ "node_modules",
951
+ "dist",
952
+ "build",
953
+ "out",
954
+ "coverage",
955
+ ".next"
956
+ ]);
948
957
  function createStatelyProjectConfig(options) {
949
958
  const defaultXStateVersion = options.defaultXStateVersion ?? 5;
950
959
  return {
@@ -953,21 +962,20 @@ function createStatelyProjectConfig(options) {
953
962
  projectId: options.projectId,
954
963
  studioUrl: options.studioUrl,
955
964
  defaultXStateVersion,
956
- sources: []
957
- };
958
- }
959
- function normalizeSourceConfig(source) {
960
- return {
961
- include: [...source.include ?? []],
962
- exclude: source.exclude == null ? [...DEFAULT_SOURCE_EXCLUDES] : [...source.exclude],
963
- format: source.format ?? "xstate",
964
- ...source.xstateVersion == null ? {} : { xstateVersion: source.xstateVersion }
965
+ include: [],
966
+ exclude: [...DEFAULT_SOURCE_EXCLUDES]
965
967
  };
966
968
  }
967
969
  function normalizeProjectConfig(config) {
970
+ const firstSource = config.sources?.[0];
968
971
  return {
969
- ...config,
970
- sources: (config.sources ?? []).map((source) => normalizeSourceConfig(source))
972
+ $schema: config.$schema ?? STATELY_CONFIG_SCHEMA_URL,
973
+ version: config.version ?? STATELY_CONFIG_VERSION,
974
+ projectId: config.projectId ?? "",
975
+ studioUrl: config.studioUrl ?? "https://stately.ai",
976
+ defaultXStateVersion: Math.max(5, config.defaultXStateVersion ?? 5),
977
+ include: [...config.include ?? firstSource?.include ?? []],
978
+ exclude: config.exclude == null ? [...firstSource?.exclude ?? DEFAULT_SOURCE_EXCLUDES] : [...config.exclude]
971
979
  };
972
980
  }
973
981
  async function readStatelyProjectConfig(options = {}) {
@@ -984,7 +992,7 @@ async function walkFiles(rootDir, currentDir = rootDir) {
984
992
  const entries = await fsPromises.readdir(currentDir, { withFileTypes: true });
985
993
  const files = [];
986
994
  for (const entry of entries) {
987
- if (entry.name === ".git") continue;
995
+ if (ALWAYS_IGNORED_DIRECTORIES.has(entry.name)) continue;
988
996
  const absolutePath = path.join(currentDir, entry.name);
989
997
  if (entry.isDirectory()) {
990
998
  files.push(...await walkFiles(rootDir, absolutePath));
@@ -996,21 +1004,14 @@ async function walkFiles(rootDir, currentDir = rootDir) {
996
1004
  return files;
997
1005
  }
998
1006
  async function filterGitIgnoredPaths(rootDir, relativeFiles) {
1007
+ if (relativeFiles.length === 0) return relativeFiles;
999
1008
  try {
1000
- const { stdout } = await execFileAsync$1("git", [
1001
- "ls-files",
1002
- "--others",
1003
- "--ignored",
1004
- "--exclude-standard",
1005
- "--directory"
1006
- ], { cwd: rootDir });
1009
+ const { stdout } = await execFileAsync$1("git", ["check-ignore", ...relativeFiles], { cwd: rootDir });
1007
1010
  const ignoredEntries = stdout.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
1008
1011
  if (ignoredEntries.length === 0) return relativeFiles;
1012
+ const ignoredPaths = new Set(ignoredEntries);
1009
1013
  return relativeFiles.filter((relativePath) => {
1010
- return !ignoredEntries.some((ignoredEntry) => {
1011
- if (ignoredEntry.endsWith("/")) return relativePath.startsWith(ignoredEntry);
1012
- return relativePath === ignoredEntry;
1013
- });
1014
+ return !ignoredPaths.has(relativePath);
1014
1015
  });
1015
1016
  } catch {
1016
1017
  return relativeFiles;
@@ -1063,30 +1064,51 @@ async function discoverCodeSourceFiles(options = {}) {
1063
1064
  const rootDir = path.resolve(options.cwd ?? process.cwd());
1064
1065
  return (await filterGitIgnoredPaths(rootDir, await walkFiles(rootDir))).filter((relativePath) => isCodeSourceFile(relativePath)).filter((relativePath) => !matchesAny(DEFAULT_SOURCE_EXCLUDES, relativePath)).sort((left, right) => left.localeCompare(right));
1065
1066
  }
1066
- function createSuggestedSource(include) {
1067
+ function createSuggestedConfig(include) {
1067
1068
  return {
1068
1069
  include: [include],
1069
- exclude: [...DEFAULT_SOURCE_EXCLUDES],
1070
- format: "xstate",
1071
- xstateVersion: 5
1070
+ exclude: [...DEFAULT_SOURCE_EXCLUDES]
1072
1071
  };
1073
1072
  }
1074
- function suggestStatelySourceConfigs(relativePaths, defaultXStateVersion = 5) {
1073
+ function toExtensionToken(extname) {
1074
+ return extname.startsWith(".") ? extname.slice(1) : extname;
1075
+ }
1076
+ function formatExtensionPattern(extnames) {
1077
+ const unique = [...new Set(extnames.map(toExtensionToken))].sort();
1078
+ if (unique.length === 1) return `.${unique[0]}`;
1079
+ return `.{${unique.join(",")}}`;
1080
+ }
1081
+ function getSuggestionRoot(relativePath) {
1082
+ if (relativePath.startsWith("src/")) return "src/";
1083
+ const packageMatch = relativePath.match(/^packages\/([^/]+)\/src\//);
1084
+ if (packageMatch) return `packages/${packageMatch[1]}/src/`;
1085
+ const appMatch = relativePath.match(/^apps\/([^/]+)\/src\//);
1086
+ if (appMatch) return `apps/${appMatch[1]}/src/`;
1087
+ return null;
1088
+ }
1089
+ function suggestGroupedInclude(relativePaths, root) {
1090
+ const extPattern = formatExtensionPattern(relativePaths.map((relativePath) => path.posix.extname(relativePath)));
1091
+ if (relativePaths.map((relativePath) => path.posix.basename(relativePath, path.posix.extname(relativePath))).every((basename) => basename.toLowerCase().endsWith("machine"))) return `${root}**/*machine${extPattern}`;
1092
+ return `${root}**/*${extPattern}`;
1093
+ }
1094
+ function suggestStatelySourceConfigs(relativePaths) {
1075
1095
  const pending = new Set(relativePaths.map((relativePath) => relativePath.replace(/\\/g, "/")).filter(Boolean));
1076
1096
  const suggestions = [];
1077
1097
  const addSuggestion = (include, predicate) => {
1078
1098
  const matched = [...pending].filter(predicate);
1079
1099
  if (matched.length === 0) return;
1080
- suggestions.push({
1081
- ...createSuggestedSource(include),
1082
- xstateVersion: defaultXStateVersion
1083
- });
1100
+ suggestions.push(createSuggestedConfig(include));
1084
1101
  for (const matchedPath of matched) pending.delete(matchedPath);
1085
1102
  };
1086
- addSuggestion("**/*.machine.ts", (relativePath) => relativePath.endsWith(".machine.ts"));
1087
- addSuggestion("src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => relativePath.startsWith("src/"));
1088
- addSuggestion("packages/*/src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => /^packages\/[^/]+\/src\//.test(relativePath));
1089
- addSuggestion("apps/*/src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs}", (relativePath) => /^apps\/[^/]+\/src\//.test(relativePath));
1103
+ const groupedRoots = /* @__PURE__ */ new Map();
1104
+ for (const relativePath of pending) {
1105
+ const root = getSuggestionRoot(relativePath);
1106
+ if (!root) continue;
1107
+ const bucket = groupedRoots.get(root) ?? [];
1108
+ bucket.push(relativePath);
1109
+ groupedRoots.set(root, bucket);
1110
+ }
1111
+ for (const [root, groupedPaths] of [...groupedRoots.entries()].sort((left, right) => left[0].localeCompare(right[0]))) addSuggestion(suggestGroupedInclude(groupedPaths, root), (relativePath) => groupedPaths.includes(relativePath));
1090
1112
  const byDirectory = /* @__PURE__ */ new Map();
1091
1113
  for (const relativePath of pending) {
1092
1114
  const directory = path.posix.dirname(relativePath);
@@ -1112,7 +1134,13 @@ async function discoverStatelySourceFiles(options = {}) {
1112
1134
  } : await readStatelyProjectConfig(options);
1113
1135
  const relativeFiles = await filterGitIgnoredPaths(rootDir, await walkFiles(rootDir));
1114
1136
  const discovered = /* @__PURE__ */ new Map();
1115
- for (const source of config.sources) for (const relativePath of relativeFiles) {
1137
+ const source = {
1138
+ include: config.include,
1139
+ exclude: config.exclude ?? DEFAULT_SOURCE_EXCLUDES,
1140
+ format: "xstate",
1141
+ xstateVersion: config.defaultXStateVersion
1142
+ };
1143
+ for (const relativePath of relativeFiles) {
1116
1144
  if (!matchesAny(source.include, relativePath)) continue;
1117
1145
  if (matchesAny(source.exclude, relativePath)) continue;
1118
1146
  const filePath = path.join(rootDir, relativePath);
@@ -1293,6 +1321,13 @@ function buildMachineEditorUrl(studioUrl, projectId, machineId) {
1293
1321
  url.searchParams.set("machineId", machineId);
1294
1322
  return url.toString();
1295
1323
  }
1324
+ function isRelinkableLinkedMachineError(error) {
1325
+ return error instanceof StudioApiError && (error.status === 403 || error.status === 404);
1326
+ }
1327
+ async function fileHasLinkedMachineId(filePath) {
1328
+ const contents = await fsPromises.readFile(filePath, "utf8");
1329
+ return Boolean(getStatelyPragma(contents, filePath)?.id);
1330
+ }
1296
1331
  async function fileExists(filePath) {
1297
1332
  try {
1298
1333
  await fsPromises.access(filePath);
@@ -1414,13 +1449,28 @@ async function scanProjectSources(options) {
1414
1449
  const filePath = path.join(cwd, relativePath);
1415
1450
  if (looksLikeXStateMachineSource(await fsPromises.readFile(filePath, "utf8"))) machineRelativePaths.push(relativePath);
1416
1451
  }
1417
- return suggestStatelySourceConfigs(machineRelativePaths, Math.max(5, options.defaultXStateVersion ?? 5));
1452
+ return suggestStatelySourceConfigs(machineRelativePaths);
1418
1453
  }
1419
1454
  function looksLikeXStateMachineSource(source) {
1420
1455
  const hasXStateImport = /from\s+['"]xstate(?:\/[^'"]+)?['"]/.test(source) || /require\(\s*['"]xstate(?:\/[^'"]+)?['"]\s*\)/.test(source);
1421
1456
  const hasMachineFactory = /\bcreateMachine\s*\(/.test(source) || /\.createMachine\s*\(/.test(source);
1422
1457
  return hasXStateImport && hasMachineFactory;
1423
1458
  }
1459
+ async function classifyPushCandidates(files) {
1460
+ const machineFiles = [];
1461
+ const nonMachineFiles = [];
1462
+ for (const file of files) {
1463
+ if (looksLikeXStateMachineSource(await fsPromises.readFile(file.filePath, "utf8"))) {
1464
+ machineFiles.push(file);
1465
+ continue;
1466
+ }
1467
+ nonMachineFiles.push(file);
1468
+ }
1469
+ return {
1470
+ machineFiles,
1471
+ nonMachineFiles
1472
+ };
1473
+ }
1424
1474
  function supportsMachineDiscovery(file) {
1425
1475
  return file.source.format === "xstate" || file.source.format === "auto";
1426
1476
  }
@@ -1629,14 +1679,28 @@ var PushCommand = class PushCommand extends Command {
1629
1679
  help: Flags.help({ char: "h" }),
1630
1680
  "api-key": Flags.string({ description: "Stately API key used to create or update remote machines" }),
1631
1681
  "base-url": Flags.string({ description: "Base URL for Stately Studio or a self-hosted deployment" }),
1632
- config: Flags.string({ description: "Path to statelyai.json" })
1682
+ config: Flags.string({ description: "Path to statelyai.json" }),
1683
+ "dry-run": Flags.boolean({
1684
+ description: "Show which matched files contain machines and what push would do without changing remote or local state",
1685
+ default: false
1686
+ })
1633
1687
  };
1634
1688
  async run() {
1635
1689
  const { args, flags } = await this.parse(PushCommand);
1636
- const resolvedApiKey = await resolveApiKey(flags["api-key"]);
1637
- if (!resolvedApiKey.apiKey) this.error(getMissingApiKeyMessage());
1690
+ const resolvedApiKey = flags["dry-run"] ? { source: "missing" } : await resolveApiKey(flags["api-key"]);
1691
+ if (!flags["dry-run"] && !resolvedApiKey.apiKey) this.error(getMissingApiKeyMessage());
1638
1692
  this.log("Resolving configured project and source files...");
1639
- const { client, config, files } = await resolveConfiguredProject({
1693
+ const { client, config, files } = flags["dry-run"] ? await (async () => {
1694
+ const { config, rootDir } = await readStatelyProjectConfig({ configPath: flags.config });
1695
+ return {
1696
+ client: void 0,
1697
+ config,
1698
+ files: await discoverStatelySourceFiles({
1699
+ cwd: rootDir,
1700
+ config
1701
+ })
1702
+ };
1703
+ })() : await resolveConfiguredProject({
1640
1704
  apiKey: resolvedApiKey.apiKey,
1641
1705
  baseUrl: flags["base-url"],
1642
1706
  configPath: flags.config
@@ -1646,6 +1710,7 @@ var PushCommand = class PushCommand extends Command {
1646
1710
  relativePath: path.relative(process.cwd(), path.resolve(args.file)),
1647
1711
  source: {
1648
1712
  include: [args.file],
1713
+ exclude: [],
1649
1714
  format: "xstate",
1650
1715
  xstateVersion: config.defaultXStateVersion
1651
1716
  }
@@ -1654,24 +1719,66 @@ var PushCommand = class PushCommand extends Command {
1654
1719
  this.log("No matching local machine source files were discovered.");
1655
1720
  return;
1656
1721
  }
1657
- this.log(`Processing ${candidateFiles.length} ${pluralize(candidateFiles.length, "source file")}...`);
1722
+ const { machineFiles } = await classifyPushCandidates(candidateFiles);
1723
+ if (machineFiles.length === 0) {
1724
+ this.log(`Matched ${candidateFiles.length} ${pluralize(candidateFiles.length, "file")}, but no local machine sources were discovered.`);
1725
+ return;
1726
+ }
1727
+ this.log(`Discovered ${machineFiles.length} ${pluralize(machineFiles.length, "file with a machine")} from ${candidateFiles.length} matched ${pluralize(candidateFiles.length, "file")}.`);
1728
+ if (flags["dry-run"]) {
1729
+ const wouldLink = [];
1730
+ const wouldUpdate = [];
1731
+ for (const file of machineFiles) {
1732
+ const pragma = getStatelyPragma(await fsPromises.readFile(file.filePath, "utf8"), file.filePath);
1733
+ if (pragma?.id) wouldUpdate.push(`${file.relativePath}: ${pragma.id}`);
1734
+ else wouldLink.push(file.relativePath);
1735
+ }
1736
+ if (wouldLink.length > 0) this.log(`Would link:\n${wouldLink.join("\n")}`);
1737
+ if (wouldUpdate.length > 0) this.log(`Would update:\n${wouldUpdate.join("\n")}`);
1738
+ return;
1739
+ }
1740
+ this.log(`Processing ${machineFiles.length} ${pluralize(machineFiles.length, "source file")}...`);
1658
1741
  const linked = [];
1659
1742
  const refreshed = [];
1660
1743
  const skipped = [];
1661
- for (const file of candidateFiles) {
1744
+ for (const file of machineFiles) {
1662
1745
  this.log(`Pushing ${file.relativePath}...`);
1663
1746
  if (!supportsMachineDiscovery(file)) {
1664
1747
  skipped.push(`${file.relativePath}: unsupported format ${file.source.format}`);
1665
1748
  continue;
1666
1749
  }
1667
- const result = await pushLocalMachineLinks({
1668
- source: file.filePath,
1669
- apiKey: resolvedApiKey.apiKey,
1670
- baseUrl: flags["base-url"] ?? config.studioUrl,
1671
- client,
1672
- project: { projectId: config.projectId },
1673
- xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion)
1674
- });
1750
+ let result;
1751
+ try {
1752
+ result = await pushLocalMachineLinks({
1753
+ source: file.filePath,
1754
+ apiKey: resolvedApiKey.apiKey,
1755
+ baseUrl: flags["base-url"] ?? config.studioUrl,
1756
+ client,
1757
+ project: { projectId: config.projectId },
1758
+ xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion)
1759
+ });
1760
+ } catch (error) {
1761
+ if (isRelinkableLinkedMachineError(error) && await fileHasLinkedMachineId(file.filePath)) {
1762
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1763
+ skipped.push(`${file.relativePath}: linked remote machine is missing or inaccessible. Re-run interactively to relink it as a new machine.`);
1764
+ continue;
1765
+ }
1766
+ if (!await promptYesNo(`Linked remote machine for ${file.relativePath} is missing or inaccessible. Push it as a new machine and replace the local @statelyai id?`, true)) {
1767
+ skipped.push(`${file.relativePath}: linked remote machine is missing or inaccessible.`);
1768
+ continue;
1769
+ }
1770
+ this.log(`Relinking ${file.relativePath} as a new remote machine...`);
1771
+ result = await pushLocalMachineLinks({
1772
+ source: file.filePath,
1773
+ apiKey: resolvedApiKey.apiKey,
1774
+ baseUrl: flags["base-url"] ?? config.studioUrl,
1775
+ client,
1776
+ project: { projectId: config.projectId },
1777
+ xstateVersion: Math.max(5, file.source.xstateVersion ?? config.defaultXStateVersion),
1778
+ relink: true
1779
+ });
1780
+ } else throw error;
1781
+ }
1675
1782
  if (result.created.length > 0) linked.push(`${file.relativePath}: ${result.created.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}] ${buildMachineEditorUrl(config.studioUrl, config.projectId, machine.id)}`).join(", ")}`);
1676
1783
  if (result.updated.length > 0) refreshed.push(`${file.relativePath}: ${result.updated.map(({ machineIndex, machine }) => `${machine.id} [${machineIndex}] ${buildMachineEditorUrl(config.studioUrl, config.projectId, machine.id)}`).join(", ")}`);
1677
1784
  for (const entry of result.skipped) skipped.push(`${file.relativePath} [${entry.machineIndex}]: ${entry.reason}`);
@@ -1776,7 +1883,7 @@ var InitCommand = class InitCommand extends Command {
1776
1883
  defaultXStateVersion: result.config.defaultXStateVersion
1777
1884
  });
1778
1885
  if (suggestions.length === 0) {
1779
- this.log(`${initMessage}\nNo machine source globs were suggested. Edit statelyai.json before running statelyai push.`);
1886
+ this.log(`${initMessage}\nNo include globs were suggested from files with a machine. Edit statelyai.json before running statelyai push.`);
1780
1887
  return;
1781
1888
  }
1782
1889
  this.log(initMessage);
@@ -1784,14 +1891,15 @@ var InitCommand = class InitCommand extends Command {
1784
1891
  if (await promptYesNo("Save these source globs to statelyai.json?", true)) {
1785
1892
  const nextConfig = {
1786
1893
  ...result.config,
1787
- sources: suggestions
1894
+ include: suggestions.flatMap((suggestion) => suggestion.include),
1895
+ exclude: [...new Set(suggestions.flatMap((suggestion) => suggestion.exclude ?? []))]
1788
1896
  };
1789
1897
  await fsPromises.writeFile(result.configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf8");
1790
1898
  this.log("Saved scanned source globs to statelyai.json.");
1791
- } else this.log("Left statelyai.json with an empty sources array. Edit it before running statelyai push.");
1899
+ } else this.log("Left statelyai.json unchanged. Edit it before running statelyai push.");
1792
1900
  return;
1793
1901
  }
1794
- this.log(`${initMessage}\nNo source globs configured yet. Edit statelyai.json before running statelyai push, or rerun with --scan.`);
1902
+ this.log(`${initMessage}\nNo include globs configured yet. Edit statelyai.json before running statelyai push, or rerun with --scan.`);
1795
1903
  }
1796
1904
  };
1797
1905
  var LoginCommand = class LoginCommand extends Command {
@@ -1883,4 +1991,4 @@ function isDirectExecution() {
1883
1991
  if (isDirectExecution()) run$1();
1884
1992
 
1885
1993
  //#endregion
1886
- export { inferInitProjectName as a, resolveApiKey as c, createStatelyProjectConfig as d, getEnvApiKey as i, run$1 as l, discoverLinkedPullTargets as n, initProject as o, formatPlanSummary as r, isFileGitDirty as s, COMMANDS as t, scanProjectSources as u };
1994
+ export { getEnvApiKey as a, isFileGitDirty as c, scanProjectSources as d, createStatelyProjectConfig as f, formatPlanSummary as i, resolveApiKey as l, classifyPushCandidates as n, inferInitProjectName as o, discoverLinkedPullTargets as r, initProject as s, COMMANDS as t, run$1 as u };
package/dist/index.d.mts CHANGED
@@ -16,7 +16,8 @@ interface StatelyProjectConfig {
16
16
  projectId: string;
17
17
  studioUrl: string;
18
18
  defaultXStateVersion: number;
19
- sources: StatelySourceConfig[];
19
+ include: string[];
20
+ exclude?: string[];
20
21
  }
21
22
  interface DiscoveredSourceFile {
22
23
  filePath: string;
@@ -54,6 +55,10 @@ interface LinkedPullTarget {
54
55
  file: DiscoveredSourceFile;
55
56
  machineId: string;
56
57
  }
58
+ interface PushCandidateClassification {
59
+ machineFiles: DiscoveredSourceFile[];
60
+ nonMachineFiles: DiscoveredSourceFile[];
61
+ }
57
62
  type ApiKeyResolution = {
58
63
  apiKey: string;
59
64
  detail: string;
@@ -72,7 +77,8 @@ declare function inferInitProjectName(cwd: string, repo?: ConnectedRepo): string
72
77
  declare function isFileGitDirty(filePath: string): Promise<boolean>;
73
78
  declare function discoverLinkedPullTargets(files: DiscoveredSourceFile[]): Promise<LinkedPullTarget[]>;
74
79
  declare function initProject(options: InitProjectOptions): Promise<InitProjectResult>;
75
- declare function scanProjectSources(options: ScanProjectSourcesOptions): Promise<StatelySourceConfig[]>;
80
+ declare function scanProjectSources(options: ScanProjectSourcesOptions): Promise<Array<Pick<StatelyProjectConfig, 'include' | 'exclude'>>>;
81
+ declare function classifyPushCandidates(files: DiscoveredSourceFile[]): Promise<PushCandidateClassification>;
76
82
  declare function formatPlanSummary(plan: SyncPlan): string;
77
83
  declare abstract class BaseSyncCommand extends Command {
78
84
  static enableJsonFlag: boolean;
@@ -142,6 +148,7 @@ declare class PushCommand extends Command {
142
148
  'api-key': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
143
149
  'base-url': _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
144
150
  config: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
151
+ 'dry-run': _oclif_core_interfaces0.BooleanFlag<boolean>;
145
152
  };
146
153
  run(): Promise<void>;
147
154
  }
@@ -220,4 +227,4 @@ declare const COMMANDS: {
220
227
  };
221
228
  declare function run(argv?: any, entryUrl?: string): Promise<void>;
222
229
  //#endregion
223
- export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, ScanProjectSourcesOptions, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
230
+ export { ApiKeyResolution, COMMANDS, InitProjectOptions, InitProjectResult, LinkedPullTarget, PushCandidateClassification, ScanProjectSourcesOptions, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as inferInitProjectName, c as resolveApiKey, d as createStatelyProjectConfig, i as getEnvApiKey, l as run, n as discoverLinkedPullTargets, o as initProject, r as formatPlanSummary, s as isFileGitDirty, t as COMMANDS, u as scanProjectSources } from "./cli-B9CRx5db.mjs";
1
+ import { a as getEnvApiKey, c as isFileGitDirty, d as scanProjectSources, f as createStatelyProjectConfig, i as formatPlanSummary, l as resolveApiKey, n as classifyPushCandidates, o as inferInitProjectName, r as discoverLinkedPullTargets, s as initProject, t as COMMANDS, u as run } from "./cli-DkWxDq0T.mjs";
2
2
 
3
- export { COMMANDS, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
3
+ export { COMMANDS, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, formatPlanSummary, getEnvApiKey, inferInitProjectName, initProject, isFileGitDirty, resolveApiKey, run, scanProjectSources };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statelyai",
3
- "version": "0.3.7",
3
+ "version": "0.4.1",
4
4
  "description": "Command-line tools for Stately",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@oclif/core": "^4.10.3",
24
- "@statelyai/sdk": "0.8.1"
24
+ "@statelyai/sdk": "0.9.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsdown": "0.21.0-beta.2",