@blzsky/variant 0.2.0 → 0.3.0-canary.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/README.md +241 -136
- package/dist/{build-2LO7GD2S.js → build-GKWNLU4Q.js} +11 -10
- package/dist/{check-ZTKGGF4M.js → check-KO5VY67Z.js} +9 -9
- package/dist/{chunk-4LLK2S4R.js → chunk-7NC7AO75.js} +19 -32
- package/dist/{chunk-RVXUCJYA.js → chunk-ACTY42RE.js} +27 -0
- package/dist/{chunk-SWPVR2DF.js → chunk-CN25SRKD.js} +9 -9
- package/dist/{chunk-LXKLCLL6.js → chunk-CWF6QXEW.js} +1 -1
- package/dist/{chunk-MZZOE7L4.js → chunk-DMDAJPFJ.js} +1 -1
- package/dist/{chunk-O4LZKXNZ.js → chunk-DSQDMFQ6.js} +59 -2
- package/dist/{chunk-V22IPJ7A.js → chunk-EISTWXDW.js} +5 -1
- package/dist/chunk-H5C4SE73.js +184 -0
- package/dist/chunk-IFFZ46YS.js +575 -0
- package/dist/chunk-IHXJ72XA.js +31 -0
- package/dist/chunk-KCEGA56S.js +670 -0
- package/dist/{chunk-LJDDS4RR.js → chunk-MAK3HG46.js} +54 -8
- package/dist/chunk-MT4BOAAV.js +167 -0
- package/dist/{chunk-VTJ67TT5.js → chunk-NFFXYYM6.js} +122 -27
- package/dist/{chunk-ISRF5F3Q.js → chunk-QRPI6TJE.js} +1 -1
- package/dist/{chunk-RGD422OM.js → chunk-RJBFCURK.js} +1 -1
- package/dist/{chunk-XUBWCTJZ.js → chunk-RSIOYQOQ.js} +85 -8
- package/dist/chunk-SGP5JX6I.js +377 -0
- package/dist/chunk-X3VVGTEV.js +268 -0
- package/dist/{chunk-NSNZOH5P.js → chunk-YII4TAKS.js} +10 -3
- package/dist/cli.js +76 -37
- package/dist/{diff-GFI6RAEA.js → diff-A3TQWWUJ.js} +9 -5
- package/dist/{doctor-GH4RZVEH.js → doctor-6ONS6LKY.js} +5 -5
- package/dist/{env-QORDD6FQ.js → env-XF2US3CB.js} +8 -8
- package/dist/git-W6N3WMYQ.js +24 -0
- package/dist/impact-DPZIAJR4.js +263 -0
- package/dist/index.js +2 -2
- package/dist/init-VNK5E4H3.js +777 -0
- package/dist/{insight-DZ5NQY6D.js → insight-LNYVMCAE.js} +9 -9
- package/dist/pr-6CYMM2XU.js +707 -0
- package/dist/{run-6PNH4QRA.js → run-44PKMOV5.js} +11 -10
- package/dist/{symbol-graph-YYUEMZGR.js → symbol-graph-OB4AJCLW.js} +3 -3
- package/dist/{tsconfig-paths-E3UEDGXZ.js → tsconfig-paths-SV3PULCW.js} +3 -1
- package/dist/vitest.cjs +629 -0
- package/dist/vitest.d.cts +181 -0
- package/dist/vitest.d.ts +181 -0
- package/dist/vitest.js +356 -0
- package/dist/workspace-PIQXG4ZD.js +65 -0
- package/package.json +15 -4
- package/dist/chunk-DRMASVYN.js +0 -25
- package/dist/chunk-KSHWC2XN.js +0 -78
- package/dist/chunk-XTYVVUWW.js +0 -279
- package/dist/impact-AF453PLK.js +0 -702
- package/dist/init-KJIUNGAN.js +0 -158
- package/dist/pr-UFZRZX6S.js +0 -152
- package/dist/workspace-X5FUBTY3.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 };
|
package/dist/vitest.d.ts
ADDED
|
@@ -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 };
|