@vitest-agent/sdk 7.1.0 → 9.0.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/errors/ProjectIdentityError.js +25 -1
- package/index.d.ts +701 -751
- package/index.js +8 -14
- package/package.json +1 -1
- package/schemas/5.0/run.json +113 -222
- package/schemas/AgentReport.js +12 -12
- package/schemas/Common.js +22 -1
- package/schemas/ConsoleLeaks.js +6 -5
- package/schemas/Coverage.js +6 -5
- package/schemas/RenderState.js +8 -1
- package/schemas/RunDiagnostics.js +61 -0
- package/schemas/RunEvent.js +8 -1
- package/schemas/RunReportFile.js +3 -1
- package/schemas/StrayOutput.js +5 -4
- package/schemas/Thresholds.js +5 -4
- package/tsdoc-metadata.json +1 -1
- package/utils/ansi.js +1 -35
- package/utils/console-leaks.js +27 -1
- package/utils/run-diagnostics-actions.js +58 -0
- package/utils/vitest-config-files.js +24 -0
- package/version.js +1 -1
- package/utils/compress-lines.js +0 -44
- package/utils/detect-pm.js +0 -57
- package/utils/format-console.js +0 -222
- package/utils/format-fatal-error.js +0 -46
- package/utils/format-gfm.js +0 -160
- package/utils/hyperlink.js +0 -10
- package/utils/normalize-workspace-key.js +0 -33
- package/utils/probe-host-metadata.js +0 -98
- package/utils/safe-filename.js +0 -33
package/schemas/ConsoleLeaks.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FiniteNumber } from "./Common.js";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
|
|
3
4
|
// src/schemas/ConsoleLeaks.ts
|
|
@@ -8,8 +9,8 @@ import { Schema } from "effect";
|
|
|
8
9
|
*/
|
|
9
10
|
const ConsoleLeakFile = Schema.Struct({
|
|
10
11
|
file: Schema.String,
|
|
11
|
-
stdout:
|
|
12
|
-
stderr:
|
|
12
|
+
stdout: FiniteNumber,
|
|
13
|
+
stderr: FiniteNumber,
|
|
13
14
|
tests: Schema.optional(Schema.Array(Schema.String)),
|
|
14
15
|
sample: Schema.optional(Schema.String)
|
|
15
16
|
}).annotate({ identifier: "ConsoleLeakFile" });
|
|
@@ -22,8 +23,8 @@ const ConsoleLeakFile = Schema.Struct({
|
|
|
22
23
|
* @public
|
|
23
24
|
*/
|
|
24
25
|
const ConsoleLeaksFromFailingTests = Schema.Struct({
|
|
25
|
-
total:
|
|
26
|
-
files:
|
|
26
|
+
total: FiniteNumber,
|
|
27
|
+
files: FiniteNumber
|
|
27
28
|
}).annotate({ identifier: "ConsoleLeaksFromFailingTests" });
|
|
28
29
|
/**
|
|
29
30
|
* Aggregate stray-console-output signal for a single test run. Omitted from
|
|
@@ -35,7 +36,7 @@ const ConsoleLeaksFromFailingTests = Schema.Struct({
|
|
|
35
36
|
* @public
|
|
36
37
|
*/
|
|
37
38
|
const ConsoleLeaks = Schema.Struct({
|
|
38
|
-
total:
|
|
39
|
+
total: FiniteNumber,
|
|
39
40
|
byFile: Schema.Array(ConsoleLeakFile),
|
|
40
41
|
truncated: Schema.optional(Schema.Boolean),
|
|
41
42
|
fromFailingTests: Schema.optional(ConsoleLeaksFromFailingTests)
|
package/schemas/Coverage.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FiniteNumber } from "./Common.js";
|
|
1
2
|
import { MetricThresholds, PatternThresholds } from "./Thresholds.js";
|
|
2
3
|
import { Effect, Schema } from "effect";
|
|
3
4
|
|
|
@@ -7,10 +8,10 @@ import { Effect, Schema } from "effect";
|
|
|
7
8
|
* @public
|
|
8
9
|
*/
|
|
9
10
|
const CoverageTotals = Schema.Struct({
|
|
10
|
-
statements:
|
|
11
|
-
branches:
|
|
12
|
-
functions:
|
|
13
|
-
lines:
|
|
11
|
+
statements: FiniteNumber,
|
|
12
|
+
branches: FiniteNumber,
|
|
13
|
+
functions: FiniteNumber,
|
|
14
|
+
lines: FiniteNumber
|
|
14
15
|
}).annotate({ identifier: "CoverageTotals" });
|
|
15
16
|
/**
|
|
16
17
|
* Per-file coverage data including uncovered line ranges.
|
|
@@ -58,7 +59,7 @@ const CoverageReport = Schema.Struct({
|
|
|
58
59
|
})),
|
|
59
60
|
scoped: Schema.Boolean.pipe(Schema.withDecodingDefaultKey(Effect.succeed(false))),
|
|
60
61
|
scopedFiles: Schema.optional(Schema.Array(Schema.String)),
|
|
61
|
-
totalFiles: Schema.optional(
|
|
62
|
+
totalFiles: Schema.optional(FiniteNumber),
|
|
62
63
|
lowCoverage: Schema.Array(FileCoverageReport),
|
|
63
64
|
lowCoverageFiles: Schema.Array(Schema.String),
|
|
64
65
|
belowTarget: Schema.optional(Schema.Array(FileCoverageReport)),
|
package/schemas/RenderState.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReportError, TestClassification, TestState } from "./Common.js";
|
|
2
|
+
import { ConsoleLeaks } from "./ConsoleLeaks.js";
|
|
2
3
|
import { MetricThresholds } from "./Thresholds.js";
|
|
3
4
|
import { CoverageTotals, GlobShortfall } from "./Coverage.js";
|
|
4
5
|
import { StrayOutput } from "./StrayOutput.js";
|
|
@@ -163,7 +164,13 @@ const RenderState = Schema.Struct({
|
|
|
163
164
|
* capture, folded from `RunFinished.strayOutput`. Absent when the run
|
|
164
165
|
* wrote nothing stray.
|
|
165
166
|
*/
|
|
166
|
-
strayOutput: Schema.optional(StrayOutput)
|
|
167
|
+
strayOutput: Schema.optional(StrayOutput),
|
|
168
|
+
/**
|
|
169
|
+
* `console.*` output Vitest captured from tests that did not fail, folded
|
|
170
|
+
* from `RunFinished.consoleLeaks`. Absent when the run logged nothing
|
|
171
|
+
* (issue #544).
|
|
172
|
+
*/
|
|
173
|
+
consoleLeaks: Schema.optional(ConsoleLeaks)
|
|
167
174
|
}).annotate({ identifier: "RenderState" });
|
|
168
175
|
/**
|
|
169
176
|
* Initial state — what the reducer returns when no events have been
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { FiniteNumber } from "./Common.js";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
// src/schemas/RunDiagnostics.ts
|
|
5
|
+
/**
|
|
6
|
+
* Which Vitest performance diagnostic a hint came from.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
const PerformanceHintKind = Schema.Literals([
|
|
10
|
+
"environment",
|
|
11
|
+
"import",
|
|
12
|
+
"transform",
|
|
13
|
+
"isolate"
|
|
14
|
+
]).annotate({ identifier: "PerformanceHintKind" });
|
|
15
|
+
/**
|
|
16
|
+
* One performance hint derived from Vitest's run diagnostics. `project` is
|
|
17
|
+
* omitted for a hint that applies to the whole run (for example `isolate`).
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
const PerformanceHint = Schema.Struct({
|
|
21
|
+
kind: PerformanceHintKind,
|
|
22
|
+
project: Schema.optionalKey(Schema.String),
|
|
23
|
+
message: Schema.String,
|
|
24
|
+
remedy: Schema.String,
|
|
25
|
+
detail: Schema.String,
|
|
26
|
+
learnMoreUrl: Schema.String,
|
|
27
|
+
estimatedSavingMs: Schema.optionalKey(FiniteNumber),
|
|
28
|
+
share: Schema.optionalKey(FiniteNumber)
|
|
29
|
+
}).annotate({ identifier: "PerformanceHint" });
|
|
30
|
+
/**
|
|
31
|
+
* Wall-clock timing of the plugin's post-run work, in milliseconds.
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
const PostRunTiming = Schema.Struct({
|
|
35
|
+
persistMs: FiniteNumber,
|
|
36
|
+
coverageScanMs: FiniteNumber,
|
|
37
|
+
totalMs: FiniteNumber,
|
|
38
|
+
renderMs: Schema.optionalKey(FiniteNumber),
|
|
39
|
+
vitestDurationMs: Schema.optionalKey(FiniteNumber)
|
|
40
|
+
}).annotate({ identifier: "PostRunTiming" });
|
|
41
|
+
/**
|
|
42
|
+
* Whether the run's results were persisted to the database.
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
const PersistenceStatus = Schema.Struct({
|
|
46
|
+
ok: Schema.Boolean,
|
|
47
|
+
reason: Schema.optionalKey(Schema.String),
|
|
48
|
+
migrationFailed: Schema.optionalKey(Schema.Boolean)
|
|
49
|
+
}).annotate({ identifier: "PersistenceStatus" });
|
|
50
|
+
/**
|
|
51
|
+
* Run-level diagnostics: performance hints, post-run timing and persistence
|
|
52
|
+
* status (issue #544).
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
const RunDiagnostics = Schema.Struct({
|
|
56
|
+
performanceHints: Schema.Array(PerformanceHint),
|
|
57
|
+
postRun: Schema.optionalKey(PostRunTiming),
|
|
58
|
+
persistence: Schema.optionalKey(PersistenceStatus)
|
|
59
|
+
}).annotate({ identifier: "RunDiagnostics" });
|
|
60
|
+
|
|
61
|
+
export { PerformanceHint, PerformanceHintKind, PersistenceStatus, PostRunTiming, RunDiagnostics };
|
package/schemas/RunEvent.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReportError, TestClassification, TestState } from "./Common.js";
|
|
2
|
+
import { ConsoleLeaks } from "./ConsoleLeaks.js";
|
|
2
3
|
import { MetricThresholds } from "./Thresholds.js";
|
|
3
4
|
import { CoverageTotals, FileCoverageReport, GlobShortfall } from "./Coverage.js";
|
|
4
5
|
import { StrayOutput } from "./StrayOutput.js";
|
|
@@ -226,7 +227,13 @@ const RunEvent = Schema.Union([
|
|
|
226
227
|
* capture (`AgentReport.strayOutput`). Optional: absent when the run
|
|
227
228
|
* wrote nothing stray, and in older replay data.
|
|
228
229
|
*/
|
|
229
|
-
strayOutput: Schema.optional(StrayOutput)
|
|
230
|
+
strayOutput: Schema.optional(StrayOutput),
|
|
231
|
+
/**
|
|
232
|
+
* `console.*` output Vitest captured from tests that did not fail
|
|
233
|
+
* (`AgentReport.consoleLeaks`). Optional: absent when the run logged
|
|
234
|
+
* nothing, and in older replay data (issue #544).
|
|
235
|
+
*/
|
|
236
|
+
consoleLeaks: Schema.optional(ConsoleLeaks)
|
|
230
237
|
})
|
|
231
238
|
]).annotate({ identifier: "RunEvent" });
|
|
232
239
|
|
package/schemas/RunReportFile.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AgentReport } from "./AgentReport.js";
|
|
2
|
+
import { RunDiagnostics } from "./RunDiagnostics.js";
|
|
2
3
|
import { Schema } from "effect";
|
|
3
4
|
|
|
4
5
|
// src/schemas/RunReportFile.ts
|
|
@@ -47,7 +48,8 @@ const RunReportFile = Schema.Struct({
|
|
|
47
48
|
$schema: Schema.optionalKey(Schema.String),
|
|
48
49
|
schemaVersion: Schema.Literal(1),
|
|
49
50
|
generatedAt: IsoInstant,
|
|
50
|
-
reports: Schema.Array(AgentReport)
|
|
51
|
+
reports: Schema.Array(AgentReport),
|
|
52
|
+
runDiagnostics: Schema.optionalKey(RunDiagnostics)
|
|
51
53
|
}).annotate({
|
|
52
54
|
identifier: "RunReportFile",
|
|
53
55
|
description: "The vitest-agent run report file (.vitest/vitest-agent/run.json): one AgentReport per Vitest project from the most recent run. Read reports[].summary for pass/fail counts and reports[].failed for the failing modules and their tests. schemaVersion is the envelope contract version; generatedAt is when the run finished."
|
package/schemas/StrayOutput.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FiniteNumber } from "./Common.js";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
|
|
3
4
|
// src/schemas/StrayOutput.ts
|
|
@@ -27,10 +28,10 @@ const StrayOutputSample = Schema.Struct({
|
|
|
27
28
|
* @public
|
|
28
29
|
*/
|
|
29
30
|
const StrayOutput = Schema.Struct({
|
|
30
|
-
total:
|
|
31
|
-
stdout:
|
|
32
|
-
stderr:
|
|
33
|
-
bytes:
|
|
31
|
+
total: FiniteNumber,
|
|
32
|
+
stdout: FiniteNumber,
|
|
33
|
+
stderr: FiniteNumber,
|
|
34
|
+
bytes: FiniteNumber,
|
|
34
35
|
samples: Schema.Array(StrayOutputSample)
|
|
35
36
|
}).annotate({ identifier: "StrayOutput" });
|
|
36
37
|
|
package/schemas/Thresholds.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FiniteNumber } from "./Common.js";
|
|
1
2
|
import { Effect, Schema } from "effect";
|
|
2
3
|
|
|
3
4
|
// src/schemas/Thresholds.ts
|
|
@@ -6,10 +7,10 @@ import { Effect, Schema } from "effect";
|
|
|
6
7
|
* @public
|
|
7
8
|
*/
|
|
8
9
|
const MetricThresholds = Schema.Struct({
|
|
9
|
-
lines: Schema.optional(
|
|
10
|
-
functions: Schema.optional(
|
|
11
|
-
branches: Schema.optional(
|
|
12
|
-
statements: Schema.optional(
|
|
10
|
+
lines: Schema.optional(FiniteNumber),
|
|
11
|
+
functions: Schema.optional(FiniteNumber),
|
|
12
|
+
branches: Schema.optional(FiniteNumber),
|
|
13
|
+
statements: Schema.optional(FiniteNumber)
|
|
13
14
|
}).annotate({ identifier: "MetricThresholds" });
|
|
14
15
|
/**
|
|
15
16
|
* A `perFile` setting. Vitest 5 allows either a plain boolean or a
|
package/tsdoc-metadata.json
CHANGED
package/utils/ansi.js
CHANGED
|
@@ -1,38 +1,4 @@
|
|
|
1
1
|
// src/utils/ansi.ts
|
|
2
|
-
const ANSI_CODES = {
|
|
3
|
-
reset: [0, 0],
|
|
4
|
-
bold: [1, 22],
|
|
5
|
-
dim: [2, 22],
|
|
6
|
-
red: [31, 39],
|
|
7
|
-
green: [32, 39],
|
|
8
|
-
yellow: [33, 39],
|
|
9
|
-
cyan: [36, 39]
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Wrap text with an ANSI color/style escape code.
|
|
13
|
-
*
|
|
14
|
-
* No-ops when `options.noColor` is true, keeping output as valid plain
|
|
15
|
-
* markdown when captured by agents or piped to files.
|
|
16
|
-
*
|
|
17
|
-
* @privateRemarks
|
|
18
|
-
* Uses raw ANSI escape sequences (`\x1b[...m`) rather than a dependency
|
|
19
|
-
* like `chalk` or `picocolors` to keep the package zero-dependency. The
|
|
20
|
-
* `noColor` flag respects the `NO_COLOR` environment variable convention.
|
|
21
|
-
*
|
|
22
|
-
* @param text - Text to wrap
|
|
23
|
-
* @param color - ANSI code name: `reset`, `bold`, `dim`, `red`, `green`, `yellow`, `cyan`
|
|
24
|
-
* @param options - Control color output
|
|
25
|
-
* @returns Text with ANSI escape codes, or plain text if noColor
|
|
26
|
-
*
|
|
27
|
-
* @see {@link https://no-color.org/ | NO_COLOR convention}
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
function ansi(text, color, options) {
|
|
31
|
-
if (options?.noColor) return text;
|
|
32
|
-
const codes = ANSI_CODES[color];
|
|
33
|
-
if (!codes) return text;
|
|
34
|
-
return `\x1b[${codes[0]}m${text}\x1b[${codes[1]}m`;
|
|
35
|
-
}
|
|
36
2
|
/**
|
|
37
3
|
* Strip all ANSI escape codes from a string.
|
|
38
4
|
*
|
|
@@ -45,4 +11,4 @@ function stripAnsi(text) {
|
|
|
45
11
|
return text.replace(/\x1b\[[0-9;]*m/g, "");
|
|
46
12
|
}
|
|
47
13
|
|
|
48
|
-
export {
|
|
14
|
+
export { stripAnsi };
|
package/utils/console-leaks.js
CHANGED
|
@@ -99,5 +99,31 @@ function collectConsoleLeakEntries(files) {
|
|
|
99
99
|
}
|
|
100
100
|
return entries;
|
|
101
101
|
}
|
|
102
|
+
const MAX_NOTE_FILES = 3;
|
|
103
|
+
/**
|
|
104
|
+
* The note a renderer prints for a run's console leaks: one line naming the
|
|
105
|
+
* write count, the file count and the fix, then up to three files with
|
|
106
|
+
* their stream split and sample, how many files it left out, and the output
|
|
107
|
+
* from failing tests that the leak count excludes. Empty when the run has no
|
|
108
|
+
* leak from a passing test (output from failing tests alone is not a leak).
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
const formatConsoleLeaksNote = (leaks) => {
|
|
112
|
+
if (leaks.total === 0) return [];
|
|
113
|
+
const files = leaks.byFile.length;
|
|
114
|
+
const lines = [`Console leaks: passing tests wrote ${leaks.total} console ${leaks.total === 1 ? "line" : "lines"} in ${files} ${files === 1 ? "file" : "files"}; Vitest captured them, so they are noise an agent never sees. Remove the logging or route it through a logger the tests silence.`];
|
|
115
|
+
const shown = leaks.byFile.slice(0, MAX_NOTE_FILES);
|
|
116
|
+
for (const file of shown) {
|
|
117
|
+
const streams = [file.stdout > 0 ? `${file.stdout} stdout` : "", file.stderr > 0 ? `${file.stderr} stderr` : ""].filter((part) => part.length > 0).join(", ");
|
|
118
|
+
const sample = file.sample !== void 0 ? `: ${file.sample}` : "";
|
|
119
|
+
lines.push(` ${file.file} (${streams})${sample}`);
|
|
120
|
+
}
|
|
121
|
+
const more = leaks.byFile.length - shown.length;
|
|
122
|
+
if (more > 0) lines.push(` (+${more} more ${more === 1 ? "file" : "files"}${leaks.truncated === true ? ", list truncated" : ""})`);
|
|
123
|
+
else if (leaks.truncated === true) lines.push(" (file list truncated)");
|
|
124
|
+
const failing = leaks.fromFailingTests;
|
|
125
|
+
if (failing !== void 0 && failing.total > 0) lines.push(` (${failing.total} more ${failing.total === 1 ? "line" : "lines"} from failing tests in ${failing.files} ${failing.files === 1 ? "file" : "files"}, not counted as leaks)`);
|
|
126
|
+
return lines;
|
|
127
|
+
};
|
|
102
128
|
|
|
103
|
-
export { buildConsoleLeaks, collectConsoleLeakEntries };
|
|
129
|
+
export { buildConsoleLeaks, collectConsoleLeakEntries, formatConsoleLeaksNote };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/utils/run-diagnostics-actions.ts
|
|
2
|
+
/**
|
|
3
|
+
* Post-run work at or above this many milliseconds is always "slow".
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
const POST_RUN_SLOW_MS = 5e3;
|
|
7
|
+
/**
|
|
8
|
+
* Post-run work at or above this share of Vitest's own duration is "slow"
|
|
9
|
+
* (once it also clears {@link POST_RUN_SLOW_MIN_MS}).
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
const POST_RUN_SLOW_SHARE = .1;
|
|
13
|
+
/**
|
|
14
|
+
* Minimum post-run milliseconds before the share rule applies.
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
const POST_RUN_SLOW_MIN_MS = 1e3;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the plugin's post-run work was slow enough to surface.
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
function isPostRunSlow(timing) {
|
|
23
|
+
if (timing.totalMs >= 5e3) return true;
|
|
24
|
+
return timing.totalMs >= 1e3 && timing.vitestDurationMs !== void 0 && timing.totalMs >= .1 * timing.vitestDurationMs;
|
|
25
|
+
}
|
|
26
|
+
const fmt = (ms) => `${(ms / 1e3).toFixed(1)}s`;
|
|
27
|
+
/**
|
|
28
|
+
* Map run diagnostics onto suggested actions: persistence failure first, then
|
|
29
|
+
* slow post-run work, then performance hints in input order.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
function suggestedActionsFromRunDiagnostics(d) {
|
|
33
|
+
const out = [];
|
|
34
|
+
if (d.persistence !== void 0 && !d.persistence.ok) {
|
|
35
|
+
const { reason, migrationFailed } = d.persistence;
|
|
36
|
+
out.push({
|
|
37
|
+
severity: "warn",
|
|
38
|
+
title: "Results were not saved",
|
|
39
|
+
detail: migrationFailed ? `${reason ?? "unknown error"}. Run \`vitest-agent db reset\` to recreate the database.` : reason ?? "unknown error"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (d.postRun !== void 0 && isPostRunSlow(d.postRun)) {
|
|
43
|
+
const { totalMs, persistMs, coverageScanMs } = d.postRun;
|
|
44
|
+
out.push({
|
|
45
|
+
severity: "warn",
|
|
46
|
+
title: `Post-run work took ${fmt(totalMs)} (persist ${fmt(persistMs)}, coverage scan ${fmt(coverageScanMs)})`,
|
|
47
|
+
detail: "Time vitest-agent spent after Vitest finished. Set persist: false where history is not needed (e.g. CI), or narrow coverage.include."
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
for (const hint of d.performanceHints) out.push({
|
|
51
|
+
severity: "info",
|
|
52
|
+
title: hint.project !== void 0 ? `${hint.project}: ${hint.message}` : hint.message,
|
|
53
|
+
detail: `${hint.remedy} — ${hint.detail} (${hint.learnMoreUrl})`
|
|
54
|
+
});
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { POST_RUN_SLOW_MIN_MS, POST_RUN_SLOW_MS, POST_RUN_SLOW_SHARE, isPostRunSlow, suggestedActionsFromRunDiagnostics };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/utils/vitest-config-files.ts
|
|
2
|
+
/**
|
|
3
|
+
* The config filenames Vitest 5 itself loads, in the order it prefers them:
|
|
4
|
+
* `vitest.config.*` before `vite.config.*`, each across
|
|
5
|
+
* `ts` / `mts` / `cts` / `js` / `mjs` / `cjs`. Mirrors `configFiles` in
|
|
6
|
+
* Vitest's own `constants.ts`. `vitest.workspace.*` is absent on purpose:
|
|
7
|
+
* Vitest no longer reads a workspace file (projects live in the config's
|
|
8
|
+
* `test.projects`).
|
|
9
|
+
*
|
|
10
|
+
* The single source both front ends derive their config lookups from, so the
|
|
11
|
+
* CLI's `check-test-path` and the MCP `run_tests` root walk cannot drift.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
const VITEST_CONFIG_FILES = ["vitest.config", "vite.config"].flatMap((name) => [
|
|
16
|
+
"ts",
|
|
17
|
+
"mts",
|
|
18
|
+
"cts",
|
|
19
|
+
"js",
|
|
20
|
+
"mjs",
|
|
21
|
+
"cjs"
|
|
22
|
+
].map((ext) => `${name}.${ext}`));
|
|
23
|
+
|
|
24
|
+
export { VITEST_CONFIG_FILES };
|
package/version.js
CHANGED
package/utils/compress-lines.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// src/utils/compress-lines.ts
|
|
2
|
-
/**
|
|
3
|
-
* Compress an array of line numbers into a compact range string.
|
|
4
|
-
*
|
|
5
|
-
* Consecutive line numbers are collapsed into `start-end` ranges,
|
|
6
|
-
* separated by commas. Duplicate values are removed and the input
|
|
7
|
-
* is sorted ascending before processing.
|
|
8
|
-
*
|
|
9
|
-
* @param lines - Array of line numbers to compress
|
|
10
|
-
* @returns Compressed range string, or empty string for empty input
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { compressLines } from "@vitest-agent/sdk/utils";
|
|
15
|
-
*
|
|
16
|
-
* compressLines([1, 2, 3, 5, 10, 11, 12]);
|
|
17
|
-
* // Returns: "1-3,5,10-12"
|
|
18
|
-
*
|
|
19
|
-
* compressLines([42]);
|
|
20
|
-
* // Returns: "42"
|
|
21
|
-
*
|
|
22
|
-
* compressLines([]);
|
|
23
|
-
* // Returns: ""
|
|
24
|
-
* ```
|
|
25
|
-
*
|
|
26
|
-
* @public
|
|
27
|
-
*/
|
|
28
|
-
function compressLines(lines) {
|
|
29
|
-
if (lines.length === 0) return "";
|
|
30
|
-
const sorted = [...new Set(lines)].sort((a, b) => a - b);
|
|
31
|
-
const ranges = [];
|
|
32
|
-
let start = sorted[0];
|
|
33
|
-
let end = sorted[0];
|
|
34
|
-
for (let i = 1; i < sorted.length; i++) if (sorted[i] === end + 1) end = sorted[i];
|
|
35
|
-
else {
|
|
36
|
-
ranges.push(start === end ? `${start}` : `${start}-${end}`);
|
|
37
|
-
start = sorted[i];
|
|
38
|
-
end = sorted[i];
|
|
39
|
-
}
|
|
40
|
-
ranges.push(start === end ? `${start}` : `${start}-${end}`);
|
|
41
|
-
return ranges.join(",");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export { compressLines };
|
package/utils/detect-pm.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// src/utils/detect-pm.ts
|
|
2
|
-
const LOCKFILE_MAP = [
|
|
3
|
-
["pnpm-lock.yaml", "pnpm"],
|
|
4
|
-
["package-lock.json", "npm"],
|
|
5
|
-
["yarn.lock", "yarn"],
|
|
6
|
-
["bun.lock", "bun"]
|
|
7
|
-
];
|
|
8
|
-
const RUN_COMMANDS = {
|
|
9
|
-
pnpm: "pnpm vitest run",
|
|
10
|
-
npm: "npx vitest run",
|
|
11
|
-
yarn: "yarn vitest run",
|
|
12
|
-
bun: "bun vitest run"
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Detect the package manager used in a project by inspecting the root directory.
|
|
16
|
-
*
|
|
17
|
-
* @remarks
|
|
18
|
-
* Detection order:
|
|
19
|
-
* 1. The `packageManager` field in `package.json` (e.g., `"pnpm\@10.32.1"`)
|
|
20
|
-
* 2. Lockfile presence: `pnpm-lock.yaml`, `package-lock.json`, `yarn.lock`, `bun.lock`
|
|
21
|
-
*
|
|
22
|
-
* Returns `null` when no package manager can be determined.
|
|
23
|
-
*
|
|
24
|
-
* @param root - Absolute path to the project root directory
|
|
25
|
-
* @param fs - Filesystem adapter for reading files and checking existence
|
|
26
|
-
* @returns The detected package manager, or `null`
|
|
27
|
-
*
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
async function detectPackageManager(root, fs) {
|
|
31
|
-
try {
|
|
32
|
-
const raw = await fs.readFile(`${root}/package.json`);
|
|
33
|
-
const pkg = JSON.parse(raw);
|
|
34
|
-
if (pkg.packageManager) {
|
|
35
|
-
const name = pkg.packageManager.split("@")[0];
|
|
36
|
-
if (name in RUN_COMMANDS) return name;
|
|
37
|
-
}
|
|
38
|
-
} catch {}
|
|
39
|
-
for (const [lockfile, pm] of LOCKFILE_MAP) if (await fs.exists(`${root}/${lockfile}`)) return pm;
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Return the vitest run command for the given package manager.
|
|
44
|
-
*
|
|
45
|
-
* Falls back to `"npx vitest run"` when `pm` is `null`.
|
|
46
|
-
*
|
|
47
|
-
* @param pm - Detected package manager, or `null` for unknown
|
|
48
|
-
* @returns Shell command string for running vitest
|
|
49
|
-
*
|
|
50
|
-
* @internal
|
|
51
|
-
*/
|
|
52
|
-
function getRunCommand(pm) {
|
|
53
|
-
if (!pm) return "npx vitest run";
|
|
54
|
-
return RUN_COMMANDS[pm];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export { detectPackageManager, getRunCommand };
|