@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
@@ -0,0 +1,181 @@
1
+ import { Reporter } from 'vitest/node';
2
+
3
+ /**
4
+ * @module
5
+ * Shadow-mode prediction log (the seed of a shared build history). Every
6
+ * `variant impact` run appends its prediction to
7
+ * `.variant/history/impact.jsonl` so prediction-vs-reality can be measured
8
+ * before test skipping is ever enabled — the measured false-skip rate, not an
9
+ * asserted number, is what earns the right to skip.
10
+ *
11
+ * Logging is best-effort by design: a history write failure must never fail
12
+ * the command, so these functions return booleans / empty lists instead of
13
+ * throwing.
14
+ */
15
+ interface ImpactPrediction {
16
+ /** ISO timestamp of the prediction. */
17
+ at: string;
18
+ baseRef: string;
19
+ /**
20
+ * The commit the prediction was made against. This is what lets a
21
+ * prediction be matched to the test run that actually happened — without
22
+ * it the log records what variant thought, with nothing to check it
23
+ * against. Null when the working tree is not a git repository.
24
+ */
25
+ headSha: string | null;
26
+ changedFiles: string[];
27
+ affectedFiles: number;
28
+ affectedPackages: string[];
29
+ /** The predicted run set — reality reconciliation diffs failures against it. */
30
+ affectedTests: string[];
31
+ totalTests: number;
32
+ selectAll: boolean;
33
+ verdict: string;
34
+ confidence: number;
35
+ notes: string[];
36
+ }
37
+
38
+ /**
39
+ * @module
40
+ * `variant impact verify` — the other half of shadow mode. `predict.ts` records
41
+ * what variant thought; this reads what actually happened and diffs the two.
42
+ *
43
+ * A failing test the prediction did not select is a **false skip**: had
44
+ * skipping been enabled, that failure would have been missed. That number, over
45
+ * many runs, is the only thing that can earn the right to skip. Everything else
46
+ * `impact` prints is a description of a graph.
47
+ */
48
+
49
+ interface VerifyResult {
50
+ prediction: ImpactPrediction;
51
+ /** How the prediction was located — reported so a mismatch is visible. */
52
+ matchedBy: "head-sha" | "most-recent";
53
+ /** Test files the runner reported as failing, workspace-relative POSIX. */
54
+ failedTests: string[];
55
+ /** Failures the prediction selected. Skipping would not have missed these. */
56
+ caught: string[];
57
+ /** Failures the prediction did NOT select. Skipping would have missed these. */
58
+ falseSkips: string[];
59
+ /**
60
+ * `falseSkips / failedTests`. Zero when the run had no failures — a clean
61
+ * run is evidence of nothing, which is why the count matters more than the
62
+ * rate until many runs accumulate.
63
+ */
64
+ falseSkipRate: number;
65
+ /**
66
+ * Test files the runner reported, and how many of them the prediction
67
+ * selected. The share a prediction would skip is measured against these:
68
+ * its own `totalTests` counts every file that looks like a test, including
69
+ * helpers and suites this run never ran.
70
+ */
71
+ ranTests: number;
72
+ predictedRan: number;
73
+ /** False when the reconciliation could not be persisted. */
74
+ historyLogged: boolean;
75
+ }
76
+
77
+ /**
78
+ * @module
79
+ * The shared vocabulary `pr check` and `impact` report their conclusions in.
80
+ * Both walk a set of classified file changes and reduce it to one
81
+ * recommendation, and they must agree — a change that is "build required" for
82
+ * one command cannot be "safe to skip" for the other.
83
+ */
84
+
85
+ type Resolution = "high" | "medium" | "low";
86
+
87
+ /**
88
+ * @module
89
+ * What every runner adapter shares: start a prediction as the run starts,
90
+ * reconcile it as the run ends, say one line, record. Nothing here knows a
91
+ * runner's types.
92
+ *
93
+ * This runs inside someone else's test process, so its rules are not the
94
+ * CLI's. It never throws and never sets an exit code: a failure in variant
95
+ * must not fail a suite. The prediction runs in a child process, so ts-morph
96
+ * never loads beside the tests and a hang there cannot hold them up. And it
97
+ * runs alongside the tests, not before them: nothing is skipped, so the tests
98
+ * do not depend on it.
99
+ */
100
+
101
+ interface ShadowOptions {
102
+ /**
103
+ * Run outside CI too. By default the adapter is on only when `CI` is set
104
+ * (or `VARIANT_SHADOW=1`): a local run is often one file, which makes a
105
+ * poor reconciliation and a line of output nobody asked for.
106
+ */
107
+ local?: boolean;
108
+ /** Record without printing. */
109
+ silent?: boolean;
110
+ /** How long to wait for the prediction once the tests have ended. */
111
+ timeoutMs?: number;
112
+ }
113
+ /** One test file's outcome, as the runner reported it. */
114
+ interface FileResult {
115
+ file: string;
116
+ failed: boolean;
117
+ }
118
+ /** The parts of `impact --json` the adapter reads. */
119
+ interface PredictionSummary {
120
+ resolution: Resolution;
121
+ selectAll: boolean;
122
+ unreached: number;
123
+ unselectedTests: number;
124
+ }
125
+ type PredictionOutcome = {
126
+ kind: "predicted";
127
+ summary: PredictionSummary;
128
+ }
129
+ /** Nothing to compare against: not a failure, and not a prediction. */
130
+ | {
131
+ kind: "none";
132
+ reason: string;
133
+ } | {
134
+ kind: "failed";
135
+ reason: string;
136
+ };
137
+ interface PredictionHandle {
138
+ result: Promise<PredictionOutcome>;
139
+ cancel: () => void;
140
+ }
141
+ /** Everything with an effect, so the flow can be tested without processes. */
142
+ interface ShadowDeps {
143
+ env: Readonly<Record<string, string | undefined>>;
144
+ now: () => number;
145
+ repositoryRoot: (cwd: string) => Promise<string>;
146
+ headSha: (root: string) => Promise<string | null>;
147
+ predict: (root: string, headSha: string) => PredictionHandle;
148
+ reconcile: (root: string, results: readonly FileResult[], headSha: string) => Promise<VerifyResult | null>;
149
+ write: (line: string) => void;
150
+ appendSummary: (file: string, text: string) => Promise<void>;
151
+ }
152
+
153
+ /**
154
+ * @module
155
+ * The Vitest adapter. It translates Vitest's reporter hooks into a shadow run
156
+ * and holds no logic of its own; see `shadow.ts`.
157
+ *
158
+ * ```ts
159
+ * import variant from "@blzsky/variant/vitest";
160
+ * export default defineConfig({ test: { reporters: ["default", variant()] } });
161
+ * ```
162
+ *
163
+ * or, in a config that sets no reporters of its own:
164
+ *
165
+ * ```ts
166
+ * import { variantReporters } from "@blzsky/variant/vitest";
167
+ * export default defineConfig({ test: { reporters: variantReporters() } });
168
+ * ```
169
+ */
170
+
171
+ type VariantReporterOptions = ShadowOptions;
172
+ declare function variantReporter(options?: VariantReporterOptions, deps?: ShadowDeps): Reporter;
173
+ /**
174
+ * Vitest's own default reporters with the adapter after them, for a config
175
+ * that names none. Setting `reporters` replaces the defaults, and one of
176
+ * them exists only on GitHub Actions: writing `["default", variant()]` into
177
+ * such a config would quietly take the annotations off every pull request.
178
+ */
179
+ declare function variantReporters(options?: VariantReporterOptions): Array<string | Reporter>;
180
+
181
+ export { type VariantReporterOptions, variantReporter as default, variantReporter, variantReporters };
@@ -0,0 +1,181 @@
1
+ import { Reporter } from 'vitest/node';
2
+
3
+ /**
4
+ * @module
5
+ * Shadow-mode prediction log (the seed of a shared build history). Every
6
+ * `variant impact` run appends its prediction to
7
+ * `.variant/history/impact.jsonl` so prediction-vs-reality can be measured
8
+ * before test skipping is ever enabled — the measured false-skip rate, not an
9
+ * asserted number, is what earns the right to skip.
10
+ *
11
+ * Logging is best-effort by design: a history write failure must never fail
12
+ * the command, so these functions return booleans / empty lists instead of
13
+ * throwing.
14
+ */
15
+ interface ImpactPrediction {
16
+ /** ISO timestamp of the prediction. */
17
+ at: string;
18
+ baseRef: string;
19
+ /**
20
+ * The commit the prediction was made against. This is what lets a
21
+ * prediction be matched to the test run that actually happened — without
22
+ * it the log records what variant thought, with nothing to check it
23
+ * against. Null when the working tree is not a git repository.
24
+ */
25
+ headSha: string | null;
26
+ changedFiles: string[];
27
+ affectedFiles: number;
28
+ affectedPackages: string[];
29
+ /** The predicted run set — reality reconciliation diffs failures against it. */
30
+ affectedTests: string[];
31
+ totalTests: number;
32
+ selectAll: boolean;
33
+ verdict: string;
34
+ confidence: number;
35
+ notes: string[];
36
+ }
37
+
38
+ /**
39
+ * @module
40
+ * `variant impact verify` — the other half of shadow mode. `predict.ts` records
41
+ * what variant thought; this reads what actually happened and diffs the two.
42
+ *
43
+ * A failing test the prediction did not select is a **false skip**: had
44
+ * skipping been enabled, that failure would have been missed. That number, over
45
+ * many runs, is the only thing that can earn the right to skip. Everything else
46
+ * `impact` prints is a description of a graph.
47
+ */
48
+
49
+ interface VerifyResult {
50
+ prediction: ImpactPrediction;
51
+ /** How the prediction was located — reported so a mismatch is visible. */
52
+ matchedBy: "head-sha" | "most-recent";
53
+ /** Test files the runner reported as failing, workspace-relative POSIX. */
54
+ failedTests: string[];
55
+ /** Failures the prediction selected. Skipping would not have missed these. */
56
+ caught: string[];
57
+ /** Failures the prediction did NOT select. Skipping would have missed these. */
58
+ falseSkips: string[];
59
+ /**
60
+ * `falseSkips / failedTests`. Zero when the run had no failures — a clean
61
+ * run is evidence of nothing, which is why the count matters more than the
62
+ * rate until many runs accumulate.
63
+ */
64
+ falseSkipRate: number;
65
+ /**
66
+ * Test files the runner reported, and how many of them the prediction
67
+ * selected. The share a prediction would skip is measured against these:
68
+ * its own `totalTests` counts every file that looks like a test, including
69
+ * helpers and suites this run never ran.
70
+ */
71
+ ranTests: number;
72
+ predictedRan: number;
73
+ /** False when the reconciliation could not be persisted. */
74
+ historyLogged: boolean;
75
+ }
76
+
77
+ /**
78
+ * @module
79
+ * The shared vocabulary `pr check` and `impact` report their conclusions in.
80
+ * Both walk a set of classified file changes and reduce it to one
81
+ * recommendation, and they must agree — a change that is "build required" for
82
+ * one command cannot be "safe to skip" for the other.
83
+ */
84
+
85
+ type Resolution = "high" | "medium" | "low";
86
+
87
+ /**
88
+ * @module
89
+ * What every runner adapter shares: start a prediction as the run starts,
90
+ * reconcile it as the run ends, say one line, record. Nothing here knows a
91
+ * runner's types.
92
+ *
93
+ * This runs inside someone else's test process, so its rules are not the
94
+ * CLI's. It never throws and never sets an exit code: a failure in variant
95
+ * must not fail a suite. The prediction runs in a child process, so ts-morph
96
+ * never loads beside the tests and a hang there cannot hold them up. And it
97
+ * runs alongside the tests, not before them: nothing is skipped, so the tests
98
+ * do not depend on it.
99
+ */
100
+
101
+ interface ShadowOptions {
102
+ /**
103
+ * Run outside CI too. By default the adapter is on only when `CI` is set
104
+ * (or `VARIANT_SHADOW=1`): a local run is often one file, which makes a
105
+ * poor reconciliation and a line of output nobody asked for.
106
+ */
107
+ local?: boolean;
108
+ /** Record without printing. */
109
+ silent?: boolean;
110
+ /** How long to wait for the prediction once the tests have ended. */
111
+ timeoutMs?: number;
112
+ }
113
+ /** One test file's outcome, as the runner reported it. */
114
+ interface FileResult {
115
+ file: string;
116
+ failed: boolean;
117
+ }
118
+ /** The parts of `impact --json` the adapter reads. */
119
+ interface PredictionSummary {
120
+ resolution: Resolution;
121
+ selectAll: boolean;
122
+ unreached: number;
123
+ unselectedTests: number;
124
+ }
125
+ type PredictionOutcome = {
126
+ kind: "predicted";
127
+ summary: PredictionSummary;
128
+ }
129
+ /** Nothing to compare against: not a failure, and not a prediction. */
130
+ | {
131
+ kind: "none";
132
+ reason: string;
133
+ } | {
134
+ kind: "failed";
135
+ reason: string;
136
+ };
137
+ interface PredictionHandle {
138
+ result: Promise<PredictionOutcome>;
139
+ cancel: () => void;
140
+ }
141
+ /** Everything with an effect, so the flow can be tested without processes. */
142
+ interface ShadowDeps {
143
+ env: Readonly<Record<string, string | undefined>>;
144
+ now: () => number;
145
+ repositoryRoot: (cwd: string) => Promise<string>;
146
+ headSha: (root: string) => Promise<string | null>;
147
+ predict: (root: string, headSha: string) => PredictionHandle;
148
+ reconcile: (root: string, results: readonly FileResult[], headSha: string) => Promise<VerifyResult | null>;
149
+ write: (line: string) => void;
150
+ appendSummary: (file: string, text: string) => Promise<void>;
151
+ }
152
+
153
+ /**
154
+ * @module
155
+ * The Vitest adapter. It translates Vitest's reporter hooks into a shadow run
156
+ * and holds no logic of its own; see `shadow.ts`.
157
+ *
158
+ * ```ts
159
+ * import variant from "@blzsky/variant/vitest";
160
+ * export default defineConfig({ test: { reporters: ["default", variant()] } });
161
+ * ```
162
+ *
163
+ * or, in a config that sets no reporters of its own:
164
+ *
165
+ * ```ts
166
+ * import { variantReporters } from "@blzsky/variant/vitest";
167
+ * export default defineConfig({ test: { reporters: variantReporters() } });
168
+ * ```
169
+ */
170
+
171
+ type VariantReporterOptions = ShadowOptions;
172
+ declare function variantReporter(options?: VariantReporterOptions, deps?: ShadowDeps): Reporter;
173
+ /**
174
+ * Vitest's own default reporters with the adapter after them, for a config
175
+ * that names none. Setting `reporters` replaces the defaults, and one of
176
+ * them exists only on GitHub Actions: writing `["default", variant()]` into
177
+ * such a config would quietly take the annotations off every pull request.
178
+ */
179
+ declare function variantReporters(options?: VariantReporterOptions): Array<string | Reporter>;
180
+
181
+ export { type VariantReporterOptions, variantReporter as default, variantReporter, variantReporters };