statelyai 0.16.0 → 0.17.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.
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,178 @@
1
+ import { createRequire, registerHooks } from "node:module";
2
+ import { readFile, realpath } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { parentPort, workerData } from "node:worker_threads";
6
+ import ts from "typescript";
7
+ import { register } from "tsx/esm/api";
8
+ import { createXStateSourceCoverageGraph, extractXStateSourceCoverage, getXStateSourceCoverageEventCases, instrumentXStateSourceCoverage } from "@statelyai/sdk/source-coverage";
9
+
10
+ //#region src/sourceCoverageRunner.ts
11
+ /** Executes trusted source with ordinary local permissions; called in a worker. */
12
+ async function runSourceCoverageFile(options) {
13
+ const fileName = await realpath(resolve(options.sourceFile));
14
+ const source = await readFile(fileName, "utf8");
15
+ const manifest = await extractXStateSourceCoverage(source, { fileName });
16
+ if (!options.execute) return {
17
+ schemaVersion: 1,
18
+ mode: "extract",
19
+ status: manifest.complete ? "complete" : "incomplete",
20
+ manifest,
21
+ exitCode: manifest.complete ? 0 : 2
22
+ };
23
+ if (!manifest.complete) throw Object.assign(/* @__PURE__ */ new Error("Source extraction is incomplete."), { diagnostics: manifest.diagnostics });
24
+ const projectRequire = createRequire(fileName);
25
+ const load = async (name) => import(pathToFileURL(projectRequire.resolve(name)).href);
26
+ const graph = await load("@statelyai/graph");
27
+ const testing = await load("@xstate/test");
28
+ if (typeof graph.getSourceCoverageWitnesses !== "function" || typeof testing.createSourceCoverageSession !== "function") throw new Error("Source coverage requires @statelyai/graph with source-coverage planning and @xstate/test with createSourceCoverageSession. Upgrade these project dependencies.");
29
+ const runtimeName = options.runtime ?? "xstate";
30
+ const runtime = await load(runtimeName);
31
+ const runtimePackage = projectRequire(`${runtimeName}/package.json`);
32
+ const runtimeId = `${runtimePackage.name}@${runtimePackage.version}`;
33
+ if (typeof runtime.getMicrosteps !== "function") throw new Error("The selected runtime does not expose microstep execution.");
34
+ const instrumented = await instrumentXStateSourceCoverage(source, { fileName });
35
+ const transpiled = ts.transpileModule(instrumented.source, {
36
+ fileName,
37
+ compilerOptions: {
38
+ target: ts.ScriptTarget.ES2022,
39
+ module: ts.ModuleKind.ESNext
40
+ },
41
+ reportDiagnostics: true
42
+ });
43
+ if (transpiled.diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) throw new Error("The instrumented module could not be compiled.");
44
+ const sourceUrl = pathToFileURL(fileName).href;
45
+ const unregister = register({ tsconfig: ts.findConfigFile(dirname(fileName), ts.sys.fileExists) ?? false });
46
+ const hooks = registerHooks({ load(url, context, nextLoad) {
47
+ if (url === sourceUrl) return {
48
+ format: "module",
49
+ source: transpiled.outputText,
50
+ shortCircuit: true
51
+ };
52
+ return nextLoad(url, context);
53
+ } });
54
+ try {
55
+ const module = await import(sourceUrl);
56
+ const selected = module[options.exportName ?? "machine"];
57
+ const control = module[instrumented.controlExport];
58
+ if (!selected || !control) throw new Error("Expected the selected machine export and source coverage control.");
59
+ const input = options.inputFile ? JSON.parse(await readFile(options.inputFile, "utf8")) : void 0;
60
+ const machineIndex = options.machineIndex ?? 0;
61
+ if (!Number.isSafeInteger(machineIndex) || machineIndex < 0 || machineIndex >= manifest.machineCount) throw new Error("machineIndex is outside the extracted machine range.");
62
+ const selectedManifest = {
63
+ ...manifest,
64
+ obligations: manifest.obligations.filter((obligation) => obligation.machineIndex === machineIndex)
65
+ };
66
+ const createSession = (input) => {
67
+ const machine = typeof selected === "function" ? selected(input) : selected;
68
+ if (!machine.eventSchema || !machine.sources) throw new Error("Source coverage execution currently requires an XState v6 machine.");
69
+ return testing.createSourceCoverageSession(machine, control, {
70
+ input,
71
+ runtimeId,
72
+ validateEvent: async (event, snapshot) => {
73
+ const candidate = event;
74
+ if (typeof candidate.type === "string" && candidate.type.startsWith("xstate.") && typeof runtime.getNextTransitions === "function" && runtime.getNextTransitions(snapshot).some((transition) => transition.eventType === candidate.type && Object.entries(transition.matches ?? {}).every(([key, value]) => Object.is(candidate[key], value)))) return;
75
+ const validation = await machine.eventSchema["~standard"].validate(event);
76
+ if (validation.issues) throw new Error(`Event validation failed: ${JSON.stringify(validation.issues)}`);
77
+ }
78
+ });
79
+ };
80
+ let result;
81
+ if (options.replayFile) {
82
+ const saved = JSON.parse(await readFile(options.replayFile, "utf8"));
83
+ const witnesses = Array.isArray(saved) ? saved : saved.witnesses;
84
+ if (!Array.isArray(witnesses)) throw new Error("Replay file must contain a witness array or a report with witnesses.");
85
+ const actual = [];
86
+ for (const witness of witnesses) actual.push((await graph.getSourceCoverageReplay(manifest, witness, createSession)).witness);
87
+ result = {
88
+ report: graph.getSourceCoverageReport(selectedManifest, actual),
89
+ witnesses: actual,
90
+ exploration: { replayed: actual.length }
91
+ };
92
+ } else {
93
+ if (!options.casesFile) throw new Error("Execution requires --cases or --replay.");
94
+ const cases = JSON.parse(await readFile(options.casesFile, "utf8"));
95
+ if (!Array.isArray(cases) || cases.some((event) => !event || typeof event.type !== "string")) throw new Error("Cases must be an array of event objects with string types.");
96
+ const suggest = options.suggestFile ? (await import(pathToFileURL(resolve(options.suggestFile)).href)).default : void 0;
97
+ if (options.suggestFile && typeof suggest !== "function") throw new Error("Suggestion module must default-export a function.");
98
+ result = await graph.getSourceCoverageWitnesses({
99
+ manifest,
100
+ machineIndex,
101
+ input,
102
+ createSession,
103
+ events: cases,
104
+ maxStates: options.maxStates,
105
+ maxDepth: options.maxDepth,
106
+ maxExecutions: options.maxExecutions,
107
+ suggestEvents: async (args) => {
108
+ const boundaries = getXStateSourceCoverageEventCases({
109
+ ...selectedManifest,
110
+ obligations: args.uncovered
111
+ }, args.state.snapshot, cases);
112
+ const suggestions = await suggest?.({
113
+ ...args,
114
+ sourceHash: manifest.source.hash
115
+ }) ?? [];
116
+ if (!Array.isArray(suggestions) || suggestions.some((event) => !event || typeof event.type !== "string")) throw new Error("Suggested cases must be event objects.");
117
+ return [...boundaries, ...suggestions];
118
+ }
119
+ });
120
+ if (options.shrink !== false) {
121
+ const minimized = [];
122
+ for (const witness of result.witnesses) minimized.push((await graph.getMinimizedSourceCoverageWitness(manifest, witness, createSession, { maxExecutions: 100 })).witness);
123
+ result.witnesses = graph.getReducedSourceCoverageWitnesses(selectedManifest, minimized);
124
+ }
125
+ const verified = [];
126
+ for (const witness of result.witnesses) verified.push((await graph.getSourceCoverageReplay(manifest, witness, createSession)).witness);
127
+ result.witnesses = verified;
128
+ result.report = graph.getSourceCoverageReport(selectedManifest, verified);
129
+ }
130
+ const projection = await createXStateSourceCoverageGraph(source, result.report, {
131
+ fileName,
132
+ machineIndex,
133
+ profile: "xstate-v6-alpha"
134
+ });
135
+ const machine = typeof selected === "function" ? selected(input) : selected;
136
+ const schemas = Object.entries(machine.schemas?.events ?? {});
137
+ const typeOnlyEvents = schemas.filter(([, schema]) => schema?.["~standard"]?.vendor === "xstate.types").map(([type]) => type);
138
+ const runtimeSchemaEvents = schemas.filter(([, schema]) => schema?.["~standard"]?.vendor && schema["~standard"].vendor !== "xstate.types").map(([type]) => type);
139
+ const unconstrainedEvents = [...new Set(result.witnesses.flatMap((witness) => witness.events.map((event) => event.type)))].filter((type) => !schemas.some(([key]) => key === type));
140
+ return {
141
+ schemaVersion: 1,
142
+ mode: options.replayFile ? "replay" : "execute",
143
+ status: result.report.complete ? "complete" : "incomplete",
144
+ machineIndex,
145
+ runtimeId,
146
+ effectsExecuted: false,
147
+ payloadValidation: {
148
+ typeOnlyEvents,
149
+ runtimeSchemaEvents,
150
+ unconstrainedEvents,
151
+ modeledRuntimeEvents: result.witnesses.flatMap((witness) => witness.events.filter((event) => event.type.startsWith("xstate.")))
152
+ },
153
+ manifest: selectedManifest,
154
+ ...result,
155
+ graph: projection.graph,
156
+ exitCode: result.report.complete ? 0 : 1
157
+ };
158
+ } finally {
159
+ hooks.deregister();
160
+ await unregister();
161
+ }
162
+ }
163
+
164
+ //#endregion
165
+ //#region src/coverageWorker.ts
166
+ if (parentPort) runSourceCoverageFile(workerData).then((result) => parentPort.postMessage({
167
+ ok: true,
168
+ result
169
+ }), (error) => parentPort.postMessage({
170
+ ok: false,
171
+ error: {
172
+ message: error instanceof Error ? error.message : String(error),
173
+ diagnostics: error?.diagnostics
174
+ }
175
+ }));
176
+
177
+ //#endregion
178
+ export { };
package/dist/index.d.mts CHANGED
@@ -69,10 +69,10 @@ declare const behaviorCiResultsSchema: z.ZodArray<z.ZodObject<{
69
69
  file: z.ZodString;
70
70
  fullName: z.ZodString;
71
71
  outcome: z.ZodEnum<{
72
- error: "error";
73
72
  passed: "passed";
74
73
  failed: "failed";
75
74
  skipped: "skipped";
75
+ error: "error";
76
76
  }>;
77
77
  detail: z.ZodString;
78
78
  }, z.core.$strict>>;
@@ -129,6 +129,53 @@ declare class BehaviorCalibrateCommand extends Command {
129
129
  run(): Promise<void>;
130
130
  }
131
131
  //#endregion
132
+ //#region src/sourceCoverageRunner.d.ts
133
+ interface SourceCoverageRunOptions {
134
+ sourceFile: string;
135
+ execute?: boolean;
136
+ casesFile?: string;
137
+ inputFile?: string;
138
+ replayFile?: string;
139
+ runtime?: string;
140
+ exportName?: string;
141
+ machineIndex?: number;
142
+ maxStates?: number;
143
+ maxDepth?: number;
144
+ maxExecutions?: number;
145
+ shrink?: boolean;
146
+ /** Trusted callback module supplying additional event cases from coverage gaps. */
147
+ suggestFile?: string;
148
+ }
149
+ //#endregion
150
+ //#region src/sourceCoverageCommand.d.ts
151
+ declare function runSourceCoverageWorker(options: SourceCoverageRunOptions, timeoutMs?: number, workerUrl?: URL): Promise<any>;
152
+ declare class SourceCoverageCommand extends Command {
153
+ static enableJsonFlag: boolean;
154
+ static summary: string;
155
+ static description: string;
156
+ static args: {
157
+ source: _oclif_core_interfaces0.Arg<string, Record<string, unknown>>;
158
+ };
159
+ static flags: {
160
+ execute: _oclif_core_interfaces0.BooleanFlag<boolean>;
161
+ cases: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
162
+ input: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
163
+ replay: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
164
+ runtime: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
165
+ export: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
166
+ "machine-index": _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
167
+ "max-states": _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
168
+ "max-depth": _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
169
+ "max-executions": _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
170
+ timeout: _oclif_core_interfaces0.OptionFlag<number, _oclif_core_interfaces0.CustomOptions>;
171
+ shrink: _oclif_core_interfaces0.BooleanFlag<boolean>;
172
+ suggest: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
173
+ report: _oclif_core_interfaces0.OptionFlag<string, _oclif_core_interfaces0.CustomOptions>;
174
+ graph: _oclif_core_interfaces0.OptionFlag<string | undefined, _oclif_core_interfaces0.CustomOptions>;
175
+ };
176
+ run(): Promise<void>;
177
+ }
178
+ //#endregion
132
179
  //#region src/testCommand.d.ts
133
180
  declare class TestCommand extends Command {
134
181
  static enableJsonFlag: boolean;
@@ -885,6 +932,7 @@ declare const COMMANDS: {
885
932
  'behavior:check': typeof BehaviorCheckCommand;
886
933
  'behavior:adopt': typeof BehaviorAdoptCommand;
887
934
  test: typeof TestCommand;
935
+ coverage: typeof SourceCoverageCommand;
888
936
  verify: typeof VerifyCommand;
889
937
  plan: typeof PlanCommand;
890
938
  diff: typeof DiffCommand;
@@ -1822,4 +1870,4 @@ declare function verifyProject(options?: VerifyProjectOptions): Promise<{
1822
1870
  /** One case per recorded path/probe. Unattributed divergences remain separate cases. */
1823
1871
  declare function formatVerificationJUnit(report: VerificationRunReport): string;
1824
1872
  //#endregion
1825
- export { ApiKeyResolution, type BehaviorCiAdapter, type BehaviorCiConfig, type BehaviorCiResult, COMMANDS, CredentialStatus, DiscoveredMachine, InitProjectOptions, InitProjectResult, type LinkProjectOptions, LinkedPullTarget, OAuthLoginDiscovery, ProjectMachineStatus, ProjectStatusMachine, ProjectStatusResult, PushCandidateClassification, RemoteProjectMachineTarget, ResolveApiKeyOptions, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, type VerificationExecutor, type VerificationRequest, type VerificationRunReport, type VerifyProjectOptions, adoptBehavior, behaviorCiConfigSchema, behaviorCiResultsSchema, briefReport, buildOAuthLoginStart, calibrateBehavior, changesReport, checkBehavior, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverMachinesInSource, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatDiscoveredMachines, formatMissingNewMachineDirMessage, formatPlanSummary, formatProjectStatus, formatStoredCredentialSource, formatVerificationJUnit, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, linkProject, pendingReport, readGraphFile, resolveApiKey, resolveConfiguredPullTargets, resolveCredentialStatus, resolveOpenRootDir, resolveProjectStatus, resolveVerificationRequest, resourceId, run, scanProjectMachines, scanProjectSources, verifyProject, verifyReport };
1873
+ export { ApiKeyResolution, type BehaviorCiAdapter, type BehaviorCiConfig, type BehaviorCiResult, COMMANDS, CredentialStatus, DiscoveredMachine, InitProjectOptions, InitProjectResult, type LinkProjectOptions, LinkedPullTarget, OAuthLoginDiscovery, ProjectMachineStatus, ProjectStatusMachine, ProjectStatusResult, PushCandidateClassification, RemoteProjectMachineTarget, ResolveApiKeyOptions, ResolvedConfiguredPullTargets, ScanProjectSourcesOptions, SourceCoverageCommand, type SourceCoverageRunOptions, type VerificationExecutor, type VerificationRequest, type VerificationRunReport, type VerifyProjectOptions, adoptBehavior, behaviorCiConfigSchema, behaviorCiResultsSchema, briefReport, buildOAuthLoginStart, calibrateBehavior, changesReport, checkBehavior, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverMachinesInSource, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatDiscoveredMachines, formatMissingNewMachineDirMessage, formatPlanSummary, formatProjectStatus, formatStoredCredentialSource, formatVerificationJUnit, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, linkProject, pendingReport, readGraphFile, resolveApiKey, resolveConfiguredPullTargets, resolveCredentialStatus, resolveOpenRootDir, resolveProjectStatus, resolveVerificationRequest, resourceId, run, runSourceCoverageWorker, scanProjectMachines, scanProjectSources, verifyProject, verifyReport };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { A as formatVerificationJUnit, B as calibrateBehavior, C as run, D as resourceId, E as linkProject, F as readGraphFile, I as verifyReport, L as adoptBehavior, M as briefReport, N as changesReport, O as resolveVerificationRequest, P as pendingReport, R as behaviorCiConfigSchema, S as resolveProjectStatus, T as scanProjectSources, V as checkBehavior, _ as isFileGitDirty, a as discoverMachinesInSource, b as resolveCredentialStatus, c as formatDiscoveredMachines, d as formatProjectStatus, f as formatStoredCredentialSource, g as initProject, h as inferInitProjectName, i as discoverLinkedPullTargets, j as createStatelyProjectConfig, k as verifyProject, l as formatMissingNewMachineDirMessage, m as getEnvCredential, n as buildOAuthLoginStart, o as discoverOAuthLogin, p as getEnvApiKey, r as classifyPushCandidates, s as discoverRemoteProjectMachineTargets, t as COMMANDS, u as formatPlanSummary, v as resolveApiKey, w as scanProjectMachines, x as resolveOpenRootDir, y as resolveConfiguredPullTargets, z as behaviorCiResultsSchema } from "./cli-CqVcBAPG.mjs";
1
+ import { A as formatVerificationJUnit, B as behaviorCiConfigSchema, C as run, D as resourceId, E as linkProject, F as readGraphFile, H as calibrateBehavior, I as verifyReport, L as SourceCoverageCommand, M as briefReport, N as changesReport, O as resolveVerificationRequest, P as pendingReport, R as runSourceCoverageWorker, S as resolveProjectStatus, T as scanProjectSources, U as checkBehavior, V as behaviorCiResultsSchema, _ as isFileGitDirty, a as discoverMachinesInSource, b as resolveCredentialStatus, c as formatDiscoveredMachines, d as formatProjectStatus, f as formatStoredCredentialSource, g as initProject, h as inferInitProjectName, i as discoverLinkedPullTargets, j as createStatelyProjectConfig, k as verifyProject, l as formatMissingNewMachineDirMessage, m as getEnvCredential, n as buildOAuthLoginStart, o as discoverOAuthLogin, p as getEnvApiKey, r as classifyPushCandidates, s as discoverRemoteProjectMachineTargets, t as COMMANDS, u as formatPlanSummary, v as resolveApiKey, w as scanProjectMachines, x as resolveOpenRootDir, y as resolveConfiguredPullTargets, z as adoptBehavior } from "./cli-BBG--n3O.mjs";
2
2
 
3
- export { COMMANDS, adoptBehavior, behaviorCiConfigSchema, behaviorCiResultsSchema, briefReport, buildOAuthLoginStart, calibrateBehavior, changesReport, checkBehavior, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverMachinesInSource, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatDiscoveredMachines, formatMissingNewMachineDirMessage, formatPlanSummary, formatProjectStatus, formatStoredCredentialSource, formatVerificationJUnit, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, linkProject, pendingReport, readGraphFile, resolveApiKey, resolveConfiguredPullTargets, resolveCredentialStatus, resolveOpenRootDir, resolveProjectStatus, resolveVerificationRequest, resourceId, run, scanProjectMachines, scanProjectSources, verifyProject, verifyReport };
3
+ export { COMMANDS, SourceCoverageCommand, adoptBehavior, behaviorCiConfigSchema, behaviorCiResultsSchema, briefReport, buildOAuthLoginStart, calibrateBehavior, changesReport, checkBehavior, classifyPushCandidates, createStatelyProjectConfig, discoverLinkedPullTargets, discoverMachinesInSource, discoverOAuthLogin, discoverRemoteProjectMachineTargets, formatDiscoveredMachines, formatMissingNewMachineDirMessage, formatPlanSummary, formatProjectStatus, formatStoredCredentialSource, formatVerificationJUnit, getEnvApiKey, getEnvCredential, inferInitProjectName, initProject, isFileGitDirty, linkProject, pendingReport, readGraphFile, resolveApiKey, resolveConfiguredPullTargets, resolveCredentialStatus, resolveOpenRootDir, resolveProjectStatus, resolveVerificationRequest, resourceId, run, runSourceCoverageWorker, scanProjectMachines, scanProjectSources, verifyProject, verifyReport };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "statelyai",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Command-line tools for Stately",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -26,18 +26,19 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@oclif/core": "^4.10.3",
29
+ "tsx": "^4.23.13",
29
30
  "typescript": "^5.9.3",
30
31
  "ws": "^8.21.0",
31
32
  "zod": "^4.4.1",
32
33
  "@statelyai/mcp": "0.21.0",
33
- "@statelyai/sdk": "0.40.0"
34
+ "@statelyai/sdk": "0.41.0"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/ws": "^8.18.1",
37
38
  "tsdown": "0.21.0-beta.2",
38
39
  "vitest": "^3.2.4",
39
- "@statelyai/editor-sync": "0.2.7",
40
- "@statelyai/api-contract": "0.10.0"
40
+ "@statelyai/api-contract": "0.10.1",
41
+ "@statelyai/editor-sync": "0.2.8"
41
42
  },
42
43
  "oclif": {
43
44
  "bin": "statelyai",