@blzsky/variant 0.2.1 → 0.3.0-canary.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.
Files changed (58) hide show
  1. package/README.md +222 -85
  2. package/dist/{build-3DSBMMI3.js → build-GCUFPPIB.js} +11 -10
  3. package/dist/check-AKYABBIV.js +1459 -0
  4. package/dist/{chunk-43VLELTL.js → chunk-2UVLKBLV.js} +1 -1
  5. package/dist/chunk-34VGKPEM.js +59 -0
  6. package/dist/chunk-BNACT47L.js +590 -0
  7. package/dist/chunk-C3NZIFW6.js +174 -0
  8. package/dist/{chunk-ZH2PQUNL.js → chunk-EFCMBBBT.js} +72 -110
  9. package/dist/chunk-H55GYSQ6.js +102 -0
  10. package/dist/chunk-HL65IPEQ.js +167 -0
  11. package/dist/chunk-IHXJ72XA.js +31 -0
  12. package/dist/{chunk-FZEXZBXB.js → chunk-JEJRXPCU.js} +41 -9
  13. package/dist/chunk-LUTP2TVU.js +94 -0
  14. package/dist/{chunk-7OPTWU6E.js → chunk-MKUYZIXF.js} +1 -1
  15. package/dist/chunk-MNYKLWRC.js +377 -0
  16. package/dist/{chunk-Q7RLUZSN.js → chunk-NJSTAWDM.js} +10 -3
  17. package/dist/{chunk-26DHEXGS.js → chunk-NOC53Z5Q.js} +1 -1
  18. package/dist/chunk-O6FCFZDF.js +127 -0
  19. package/dist/{chunk-6BXSZPHC.js → chunk-OON6O4S5.js} +99 -6
  20. package/dist/{chunk-FA64KWJ2.js → chunk-TPMHY4L6.js} +16 -31
  21. package/dist/chunk-TRUK3TJF.js +575 -0
  22. package/dist/chunk-WTXWEEI2.js +21 -0
  23. package/dist/{chunk-RC23JQKI.js → chunk-WULBOSG5.js} +15 -2
  24. package/dist/chunk-X3VVGTEV.js +268 -0
  25. package/dist/{chunk-TCISS2AS.js → chunk-XXMD4NC5.js} +120 -25
  26. package/dist/cli.js +89 -30
  27. package/dist/{diff-HSJBZFMC.js → diff-ST5KHT7P.js} +5 -5
  28. package/dist/{doctor-B7MHFIYJ.js → doctor-OEIY4Y5X.js} +41 -8
  29. package/dist/{env-I7JZRWDN.js → env-EQAOZCFR.js} +8 -9
  30. package/dist/git-VGHUF2SY.js +30 -0
  31. package/dist/impact-AXLZTLVT.js +266 -0
  32. package/dist/index.js +2 -2
  33. package/dist/init-QCOZWLOO.js +793 -0
  34. package/dist/{insight-JQLTGFYB.js → insight-KRWT6Z47.js} +9 -10
  35. package/dist/pr-7JXUC5BQ.js +710 -0
  36. package/dist/{run-C6VAQOVM.js → run-UOPS5FRU.js} +11 -10
  37. package/dist/stryker-host.d.ts +2 -0
  38. package/dist/stryker-host.js +135 -0
  39. package/dist/stryker-plugins.d.ts +33 -0
  40. package/dist/stryker-plugins.js +142 -0
  41. package/dist/{symbol-graph-E5GXKDFZ.js → symbol-graph-GYOI4GU4.js} +3 -3
  42. package/dist/{tsconfig-paths-E3UEDGXZ.js → tsconfig-paths-SV3PULCW.js} +3 -1
  43. package/dist/vitest.cjs +629 -0
  44. package/dist/vitest.d.cts +181 -0
  45. package/dist/vitest.d.ts +181 -0
  46. package/dist/vitest.js +358 -0
  47. package/dist/workspace-IQFLR3RY.js +65 -0
  48. package/package.json +17 -3
  49. package/dist/check-SIQJU6Z2.js +0 -44
  50. package/dist/chunk-DRMASVYN.js +0 -25
  51. package/dist/chunk-KSHWC2XN.js +0 -78
  52. package/dist/chunk-QRPI6TJE.js +0 -68
  53. package/dist/chunk-VYWPQKMS.js +0 -88
  54. package/dist/chunk-XTYVVUWW.js +0 -279
  55. package/dist/impact-PRZ6HEP5.js +0 -735
  56. package/dist/init-O2BY3FOY.js +0 -158
  57. package/dist/pr-Y724QAHE.js +0 -165
  58. package/dist/workspace-HRTFGWKN.js +0 -186
@@ -2,19 +2,19 @@ import {
2
2
  DEFAULT_DIFF_BASE_REF,
3
3
  classifyFileAgainstRef,
4
4
  toWorkspaceRelative
5
- } from "./chunk-Q7RLUZSN.js";
5
+ } from "./chunk-NJSTAWDM.js";
6
6
  import {
7
7
  assertBaseRef
8
- } from "./chunk-FZEXZBXB.js";
9
- import "./chunk-6BXSZPHC.js";
8
+ } from "./chunk-JEJRXPCU.js";
10
9
  import {
11
10
  lines
12
11
  } from "./chunk-6J4XDG44.js";
13
12
  import {
14
13
  getPrinter
15
14
  } from "./chunk-EISTWXDW.js";
16
- import "./chunk-RC23JQKI.js";
17
- import "./chunk-VYWPQKMS.js";
15
+ import "./chunk-OON6O4S5.js";
16
+ import "./chunk-WULBOSG5.js";
17
+ import "./chunk-C3NZIFW6.js";
18
18
 
19
19
  // src/cli/render/diff.ts
20
20
  var CLASS_MEANING = {
@@ -1,22 +1,45 @@
1
+ import {
2
+ buildGraph
3
+ } from "./chunk-H55GYSQ6.js";
4
+ import {
5
+ lines
6
+ } from "./chunk-6J4XDG44.js";
1
7
  import {
2
8
  findVariantConfigPath,
3
9
  loadConfig
4
- } from "./chunk-26DHEXGS.js";
10
+ } from "./chunk-NOC53Z5Q.js";
5
11
  import {
6
12
  getColors
7
13
  } from "./chunk-YYGCSKIQ.js";
8
- import {
9
- lines
10
- } from "./chunk-6J4XDG44.js";
11
14
  import {
12
15
  getPrinter
13
16
  } from "./chunk-EISTWXDW.js";
14
- import "./chunk-VYWPQKMS.js";
17
+ import {
18
+ ConfigError
19
+ } from "./chunk-C3NZIFW6.js";
15
20
 
16
21
  // src/core/doctor/diagnostics.ts
17
22
  import { readdir, stat } from "fs/promises";
18
23
  import path from "path";
19
- var MINIMUM_NODE_MAJOR = 20;
24
+
25
+ // src/core/graph/validation.ts
26
+ function validateTaskGraph(tasks, graph) {
27
+ for (const [name, task] of Object.entries(tasks)) {
28
+ for (const dependency of task.dependsOn ?? []) {
29
+ if (!(dependency in tasks)) {
30
+ throw new ConfigError(
31
+ `Task "${name}" depends on unknown task "${dependency}"`
32
+ );
33
+ }
34
+ }
35
+ }
36
+ for (const name of Object.keys(tasks)) {
37
+ graph.toLevels(name);
38
+ }
39
+ }
40
+
41
+ // src/core/doctor/diagnostics.ts
42
+ var MINIMUM_NODE_MAJOR = 22;
20
43
  var CACHE_WARN_MB = 500;
21
44
  function checkNodeVersion(version = process.version) {
22
45
  const match = version.match(/^v(\d+)/);
@@ -71,9 +94,9 @@ async function checkConfig(cwd) {
71
94
  if (!configPath) {
72
95
  return [
73
96
  {
74
- level: "error",
97
+ level: "warn",
75
98
  label: "No variant.config.ts found",
76
- detail: "Run `variant init` to create one."
99
+ detail: "`impact`, `diff`, `pr` and `workspace check` work without one. `build` and `run` need it: `variant init --tasks` creates one."
77
100
  }
78
101
  ];
79
102
  }
@@ -92,6 +115,16 @@ async function checkConfig(cwd) {
92
115
  return diagnostics;
93
116
  }
94
117
  diagnostics.push({ level: "ok", label: "Config is valid" });
118
+ try {
119
+ validateTaskGraph(config.tasks, buildGraph(config));
120
+ diagnostics.push({ level: "ok", label: "Task graph is valid" });
121
+ } catch (err) {
122
+ diagnostics.push({
123
+ level: "error",
124
+ label: "Task graph is invalid",
125
+ detail: err instanceof Error ? err.message : String(err)
126
+ });
127
+ }
95
128
  diagnostics.push(await checkCacheSize(cwd, config.cache.directory));
96
129
  return diagnostics;
97
130
  }
@@ -1,18 +1,17 @@
1
1
  import {
2
+ createContext,
2
3
  renderEnv
3
- } from "./chunk-QRPI6TJE.js";
4
- import {
5
- createContext
6
- } from "./chunk-ZH2PQUNL.js";
4
+ } from "./chunk-EFCMBBBT.js";
5
+ import "./chunk-H55GYSQ6.js";
6
+ import "./chunk-MKUYZIXF.js";
7
7
  import "./chunk-V5CTGXMM.js";
8
- import "./chunk-26DHEXGS.js";
9
- import "./chunk-YYGCSKIQ.js";
10
- import "./chunk-7OPTWU6E.js";
11
8
  import "./chunk-3E5LBU3O.js";
12
- import "./chunk-6BXSZPHC.js";
13
9
  import "./chunk-6J4XDG44.js";
10
+ import "./chunk-NOC53Z5Q.js";
11
+ import "./chunk-YYGCSKIQ.js";
14
12
  import "./chunk-EISTWXDW.js";
15
- import "./chunk-VYWPQKMS.js";
13
+ import "./chunk-OON6O4S5.js";
14
+ import "./chunk-C3NZIFW6.js";
16
15
 
17
16
  // src/cli/commands/env.ts
18
17
  async function registerEnvAction() {
@@ -0,0 +1,30 @@
1
+ import {
2
+ defaultBranchRef,
3
+ hasLocalChanges,
4
+ listChangedFiles,
5
+ listChangedFilesSinceMergeBase,
6
+ listRepositoryFiles,
7
+ listUntrackedFiles,
8
+ mergeBase,
9
+ readFileAtRef,
10
+ readFilesAtRef,
11
+ readHeadSha,
12
+ readZeroContextDiff,
13
+ repositoryRoot,
14
+ resolveCommit
15
+ } from "./chunk-OON6O4S5.js";
16
+ export {
17
+ defaultBranchRef,
18
+ hasLocalChanges,
19
+ listChangedFiles,
20
+ listChangedFilesSinceMergeBase,
21
+ listRepositoryFiles,
22
+ listUntrackedFiles,
23
+ mergeBase,
24
+ readFileAtRef,
25
+ readFilesAtRef,
26
+ readHeadSha,
27
+ readZeroContextDiff,
28
+ repositoryRoot,
29
+ resolveCommit
30
+ };
@@ -0,0 +1,266 @@
1
+ import {
2
+ predictImpact
3
+ } from "./chunk-LUTP2TVU.js";
4
+ import {
5
+ verdictText
6
+ } from "./chunk-BNACT47L.js";
7
+ import "./chunk-JEJRXPCU.js";
8
+ import "./chunk-TRUK3TJF.js";
9
+ import "./chunk-GDSZFIHM.js";
10
+ import "./chunk-MKUYZIXF.js";
11
+ import {
12
+ classificationLabel,
13
+ plural
14
+ } from "./chunk-CXNXZLDG.js";
15
+ import "./chunk-3E5LBU3O.js";
16
+ import {
17
+ lines
18
+ } from "./chunk-6J4XDG44.js";
19
+ import {
20
+ getColors
21
+ } from "./chunk-YYGCSKIQ.js";
22
+ import {
23
+ getPrinter
24
+ } from "./chunk-EISTWXDW.js";
25
+ import {
26
+ verifyImpact
27
+ } from "./chunk-HL65IPEQ.js";
28
+ import "./chunk-34VGKPEM.js";
29
+ import {
30
+ changeBaseDeps,
31
+ detectHeadSha
32
+ } from "./chunk-O6FCFZDF.js";
33
+ import "./chunk-X3VVGTEV.js";
34
+ import "./chunk-OON6O4S5.js";
35
+ import "./chunk-XXMD4NC5.js";
36
+ import "./chunk-WULBOSG5.js";
37
+ import {
38
+ NoBaseCommitError
39
+ } from "./chunk-C3NZIFW6.js";
40
+
41
+ // src/cli/render/impact.ts
42
+ var MAX_LISTED_FILES = 20;
43
+ var count = (value) => value.toLocaleString("en-US");
44
+ var NO_PREDICTION_MESSAGE = "impact verify: no logged prediction to reconcile against. Run `variant impact` before the test run, or pass --head-sha for a specific commit.";
45
+ function shortRef(ref) {
46
+ return /^[0-9a-f]{40}$/.test(ref) ? ref.slice(0, 12) : ref;
47
+ }
48
+ function renderNoBaseCommit(reason, json, printer = getPrinter()) {
49
+ lines(
50
+ printer,
51
+ json ? JSON.stringify(
52
+ { prediction: null, code: "NO_BASE_COMMIT", reason },
53
+ null,
54
+ 2
55
+ ) : `impact: ${reason}; no prediction was made.`
56
+ );
57
+ }
58
+ function renderImpactJson(report, printer = getPrinter()) {
59
+ const { result, ...rest } = report;
60
+ lines(
61
+ printer,
62
+ JSON.stringify(
63
+ { ...rest, radius: result.radius, tests: result.tests },
64
+ null,
65
+ 2
66
+ )
67
+ );
68
+ }
69
+ function renderImpact(report, printer = getPrinter()) {
70
+ const { radius, tests } = report.result;
71
+ lines(
72
+ printer,
73
+ "",
74
+ `Base ref: ${shortRef(report.baseRef)}${report.baseLabel === report.baseRef ? "" : ` (${report.baseLabel})`}`,
75
+ `Workspace: ${report.packagesFound === 0 ? "no workspace packages found" : plural(report.packagesFound, "package")}`,
76
+ "",
77
+ `You changed ${plural(radius.changed.length, "file")}.`
78
+ );
79
+ for (const impact of radius.changed.slice(0, MAX_LISTED_FILES)) {
80
+ const symbols = impact.impactedSymbols.length > 0 ? ` (${impact.impactedSymbols.join(", ")})` : "";
81
+ lines(
82
+ printer,
83
+ ` ${classificationLabel(impact.classification)} ${impact.filePath}${symbols}`
84
+ );
85
+ }
86
+ if (radius.changed.length > MAX_LISTED_FILES) {
87
+ lines(
88
+ printer,
89
+ ` \u2026 and ${count(radius.changed.length - MAX_LISTED_FILES)} more`
90
+ );
91
+ }
92
+ const packages = radius.affectedPackages.length > 0 ? `, ${plural(radius.affectedPackages.length, "package")} (${radius.affectedPackages.join(", ")})` : "";
93
+ lines(
94
+ printer,
95
+ "",
96
+ `Impact: ${plural(radius.affectedFiles.length, "file")}${packages}`
97
+ );
98
+ const skipped = tests.totalTests - tests.affectedTests.length;
99
+ lines(
100
+ printer,
101
+ "",
102
+ `Run: ${plural(tests.affectedTests.length, "test file")}`,
103
+ `Skip: ${plural(skipped, "test file")} (of ${count(tests.totalTests)} total)`
104
+ );
105
+ if (tests.unreached.length > 0) {
106
+ const listed = tests.unreached.slice(0, MAX_LISTED_FILES).join(", ");
107
+ const more = tests.unreached.length > MAX_LISTED_FILES ? `, \u2026 ${count(tests.unreached.length - MAX_LISTED_FILES)} more` : "";
108
+ const one = tests.unreached.length === 1;
109
+ lines(
110
+ printer,
111
+ "",
112
+ `${getColors().yellow("Unreached:")} ${plural(tests.unreached.length, "changed file")} no test reaches (${listed}${more}). No test is selected for ${one ? "it: it is" : "them: they are"} untested, or used in a way variant cannot follow.`
113
+ );
114
+ }
115
+ if (tests.unselectedTests.length > 0) {
116
+ const listed = tests.unselectedTests.slice(0, MAX_LISTED_FILES).join(", ");
117
+ const more = tests.unselectedTests.length > MAX_LISTED_FILES ? `, \u2026 ${count(tests.unselectedTests.length - MAX_LISTED_FILES)} more` : "";
118
+ const one = tests.unselectedTests.length === 1;
119
+ lines(
120
+ printer,
121
+ "",
122
+ `${getColors().yellow("Not selected:")} ${plural(tests.unselectedTests.length, "changed test file")} ${one ? "is" : "are"} JavaScript (${listed}${more}). variant does not index JavaScript tests, so it cannot select them; run them yourself.`
123
+ );
124
+ }
125
+ const nothingAnalyzed = !tests.selectAll && radius.changed.length > 0 && radius.changed.every((impact) => impact.classification === "unanalyzed");
126
+ if (nothingAnalyzed) {
127
+ lines(
128
+ printer,
129
+ "",
130
+ "Nothing in this change was analyzed: none of the changed files is TypeScript that variant indexes."
131
+ );
132
+ }
133
+ lines(
134
+ printer,
135
+ "",
136
+ `Verdict: ${verdictText(report.verdict)}`,
137
+ `Confidence: ${tests.resolution} (${Math.round(tests.confidence * 100)}%) (report-only \u2014 run the full suite; skipping unlocks after shadow-mode validation)`
138
+ );
139
+ const notes = [...radius.notes, ...tests.notes];
140
+ if (notes.length > 0) {
141
+ lines(printer, "", "Notes:");
142
+ for (const note of notes) lines(printer, ` - ${note}`);
143
+ }
144
+ const standing = [...radius.repositoryNotes, ...tests.repositoryNotes];
145
+ if (standing.length > 0 && printer.mode === "verbose") {
146
+ lines(printer, "", "Repository (the same whatever changed):");
147
+ for (const note of standing) lines(printer, ` - ${note}`);
148
+ } else if (standing.length > 0) {
149
+ lines(
150
+ printer,
151
+ "",
152
+ `Repository: ${plural(standing.length, "standing note")}, the same whatever changed (-v lists them)`
153
+ );
154
+ }
155
+ if (!report.historyLogged) {
156
+ lines(
157
+ printer,
158
+ "",
159
+ "(warning: could not write .variant/history/impact.jsonl \u2014 shadow-mode logging skipped)"
160
+ );
161
+ }
162
+ }
163
+ function renderImpactVerifyJson(result, printer = getPrinter()) {
164
+ const { prediction, ...rest } = result;
165
+ lines(
166
+ printer,
167
+ JSON.stringify(
168
+ { ...rest, predictedAt: prediction.at, baseRef: prediction.baseRef },
169
+ null,
170
+ 2
171
+ )
172
+ );
173
+ }
174
+ function renderImpactVerify(result, printer = getPrinter()) {
175
+ const { prediction } = result;
176
+ lines(
177
+ printer,
178
+ "",
179
+ `Prediction: ${prediction.at} (${prediction.headSha ?? "no commit recorded"})`,
180
+ `Matched by: ${result.matchedBy === "head-sha" ? "head SHA" : "most recent \u2014 verify this is the right run"}`,
181
+ "",
182
+ `Predicted: ${count(prediction.affectedTests.length)} of ${count(prediction.totalTests)} test files${prediction.selectAll ? " (selected all)" : ""}`,
183
+ `Ran: ${plural(result.ranTests, "test file")}, ${count(result.predictedRan)} of them predicted`,
184
+ `Failed: ${plural(result.failedTests.length, "test file")}`
185
+ );
186
+ if (result.failedTests.length === 0) {
187
+ lines(
188
+ printer,
189
+ "",
190
+ "No failures in this run, so it neither confirms nor refutes the prediction."
191
+ );
192
+ return;
193
+ }
194
+ lines(
195
+ printer,
196
+ "",
197
+ `Caught: ${count(result.caught.length)} (inside the predicted set)`,
198
+ `False skips: ${count(result.falseSkips.length)} (would have been missed)`
199
+ );
200
+ if (result.falseSkips.length > 0) {
201
+ lines(printer, "", "False skips:");
202
+ for (const test of result.falseSkips.slice(0, MAX_LISTED_FILES)) {
203
+ lines(printer, ` ${test}`);
204
+ }
205
+ if (result.falseSkips.length > MAX_LISTED_FILES) {
206
+ lines(
207
+ printer,
208
+ ` \u2026 and ${count(result.falseSkips.length - MAX_LISTED_FILES)} more`
209
+ );
210
+ }
211
+ }
212
+ lines(
213
+ printer,
214
+ "",
215
+ `False-skip rate this run: ${(result.falseSkipRate * 100).toFixed(1)}%`,
216
+ "One run is not a rate. Reconcile many before reading anything into it."
217
+ );
218
+ if (!result.historyLogged) {
219
+ lines(
220
+ printer,
221
+ "",
222
+ "(warning: could not write .variant/history/reconciliation.jsonl)"
223
+ );
224
+ }
225
+ }
226
+
227
+ // src/cli/commands/impact.ts
228
+ async function registerImpactAction(opts = {}) {
229
+ let report;
230
+ try {
231
+ report = await predictImpact(process.cwd(), opts);
232
+ } catch (error) {
233
+ if (!(error instanceof NoBaseCommitError)) throw error;
234
+ renderNoBaseCommit(error.message, opts.json === true);
235
+ return;
236
+ }
237
+ if (opts.json === true) {
238
+ renderImpactJson(report);
239
+ return;
240
+ }
241
+ renderImpact(report);
242
+ }
243
+ async function registerImpactVerifyAction(reportPaths, opts = {}) {
244
+ const cwd = process.cwd();
245
+ const detected = opts.headSha === void 0 ? await detectHeadSha(changeBaseDeps(cwd)) : void 0;
246
+ const headSha = opts.headSha ?? detected;
247
+ let result = await verifyImpact(cwd, reportPaths, {
248
+ ...headSha !== void 0 && { headSha }
249
+ });
250
+ if (result === null && opts.headSha === void 0 && detected !== void 0) {
251
+ result = await verifyImpact(cwd, reportPaths);
252
+ }
253
+ if (result === null) {
254
+ lines(getPrinter(), NO_PREDICTION_MESSAGE);
255
+ return;
256
+ }
257
+ if (opts.json === true) {
258
+ renderImpactVerifyJson(result);
259
+ return;
260
+ }
261
+ renderImpactVerify(result);
262
+ }
263
+ export {
264
+ registerImpactAction,
265
+ registerImpactVerifyAction
266
+ };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineConfig
3
- } from "./chunk-26DHEXGS.js";
4
- import "./chunk-VYWPQKMS.js";
3
+ } from "./chunk-NOC53Z5Q.js";
4
+ import "./chunk-C3NZIFW6.js";
5
5
  export {
6
6
  defineConfig
7
7
  };