@paretools/go 0.8.5 → 0.10.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 +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/formatters.d.ts +33 -9
- package/dist/lib/formatters.d.ts.map +1 -1
- package/dist/lib/formatters.js +291 -34
- package/dist/lib/formatters.js.map +1 -1
- package/dist/lib/go-runner.d.ts +1 -1
- package/dist/lib/go-runner.d.ts.map +1 -1
- package/dist/lib/go-runner.js +3 -3
- package/dist/lib/go-runner.js.map +1 -1
- package/dist/lib/parsers.d.ts +47 -12
- package/dist/lib/parsers.d.ts.map +1 -1
- package/dist/lib/parsers.js +653 -45
- package/dist/lib/parsers.js.map +1 -1
- package/dist/schemas/index.d.ts +218 -4
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +142 -5
- package/dist/schemas/index.js.map +1 -1
- package/dist/tools/build.d.ts.map +1 -1
- package/dist/tools/build.js +97 -12
- package/dist/tools/build.js.map +1 -1
- package/dist/tools/env.d.ts.map +1 -1
- package/dist/tools/env.js +15 -13
- package/dist/tools/env.js.map +1 -1
- package/dist/tools/fmt.d.ts.map +1 -1
- package/dist/tools/fmt.js +28 -12
- package/dist/tools/fmt.js.map +1 -1
- package/dist/tools/generate.d.ts.map +1 -1
- package/dist/tools/generate.js +77 -12
- package/dist/tools/generate.js.map +1 -1
- package/dist/tools/get.d.ts.map +1 -1
- package/dist/tools/get.js +91 -11
- package/dist/tools/get.js.map +1 -1
- package/dist/tools/golangci-lint.d.ts.map +1 -1
- package/dist/tools/golangci-lint.js +145 -13
- package/dist/tools/golangci-lint.js.map +1 -1
- package/dist/tools/list.d.ts.map +1 -1
- package/dist/tools/list.js +77 -13
- package/dist/tools/list.js.map +1 -1
- package/dist/tools/mod-tidy.d.ts.map +1 -1
- package/dist/tools/mod-tidy.js +119 -12
- package/dist/tools/mod-tidy.js.map +1 -1
- package/dist/tools/run.d.ts.map +1 -1
- package/dist/tools/run.js +108 -16
- package/dist/tools/run.js.map +1 -1
- package/dist/tools/test.d.ts.map +1 -1
- package/dist/tools/test.js +112 -11
- package/dist/tools/test.js.map +1 -1
- package/dist/tools/vet.d.ts.map +1 -1
- package/dist/tools/vet.js +55 -13
- package/dist/tools/vet.js.map +1 -1
- package/package.json +2 -2
package/dist/lib/parsers.js
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
const GO_ERROR_RE = /^(.+?\.go):(\d+)(?::(\d+))?: (.+)$/;
|
|
2
|
+
/**
|
|
3
|
+
* Regex to detect non-file build error lines. These are package-level or linker
|
|
4
|
+
* errors that don't match the standard file:line:col format.
|
|
5
|
+
* Matches: "package X is not in GOROOT", linker errors, build constraint errors, etc.
|
|
6
|
+
* Ignores: blank lines, "# package" headers (captured separately as context).
|
|
7
|
+
*/
|
|
8
|
+
const GO_NON_FILE_ERROR_PATTERNS = [
|
|
9
|
+
/^package .+ is not in (?:GOROOT|GOPATH|std)/,
|
|
10
|
+
/^can(?:not|'t) (?:find|load) package/,
|
|
11
|
+
/^no required module provides package/,
|
|
12
|
+
/^build constraints exclude all Go files/,
|
|
13
|
+
/^imports .+ is not in/,
|
|
14
|
+
// Linker errors
|
|
15
|
+
/^(?:\/[^\s]+\/)?ld[.:]/,
|
|
16
|
+
/^(?:#\s+command-line-arguments|link:)/,
|
|
17
|
+
/undefined reference to/,
|
|
18
|
+
/multiple definition of/,
|
|
19
|
+
// CGO errors
|
|
20
|
+
/^cgo: /,
|
|
21
|
+
// Module errors
|
|
22
|
+
/^go: /,
|
|
23
|
+
];
|
|
2
24
|
/** Parses `go build` stderr output into structured error data with file locations. */
|
|
3
25
|
export function parseGoBuildOutput(stdout, stderr, exitCode) {
|
|
4
26
|
const output = stdout + "\n" + stderr;
|
|
5
27
|
const lines = output.split("\n");
|
|
6
28
|
const errors = [];
|
|
29
|
+
const rawErrors = [];
|
|
7
30
|
for (const line of lines) {
|
|
8
|
-
const
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
if (!trimmed)
|
|
33
|
+
continue;
|
|
34
|
+
const match = trimmed.match(GO_ERROR_RE);
|
|
9
35
|
if (match) {
|
|
10
36
|
errors.push({
|
|
11
37
|
file: match[1],
|
|
@@ -13,12 +39,22 @@ export function parseGoBuildOutput(stdout, stderr, exitCode) {
|
|
|
13
39
|
column: match[3] ? parseInt(match[3], 10) : undefined,
|
|
14
40
|
message: match[4],
|
|
15
41
|
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Only capture non-file errors when the build actually failed
|
|
45
|
+
if (exitCode !== 0) {
|
|
46
|
+
const isNonFileError = GO_NON_FILE_ERROR_PATTERNS.some((re) => re.test(trimmed));
|
|
47
|
+
if (isNonFileError) {
|
|
48
|
+
rawErrors.push(trimmed);
|
|
49
|
+
}
|
|
16
50
|
}
|
|
17
51
|
}
|
|
52
|
+
const total = errors.length + rawErrors.length;
|
|
18
53
|
return {
|
|
19
54
|
success: exitCode === 0,
|
|
20
55
|
errors,
|
|
21
|
-
|
|
56
|
+
rawErrors: rawErrors.length > 0 ? rawErrors : undefined,
|
|
57
|
+
total,
|
|
22
58
|
};
|
|
23
59
|
}
|
|
24
60
|
/**
|
|
@@ -28,7 +64,13 @@ export function parseGoBuildOutput(stdout, stderr, exitCode) {
|
|
|
28
64
|
*/
|
|
29
65
|
export function parseGoTestJson(stdout, exitCode) {
|
|
30
66
|
const lines = stdout.trim().split("\n").filter(Boolean);
|
|
67
|
+
// Collect output lines per test (keyed by package/test)
|
|
68
|
+
const testOutputMap = new Map();
|
|
69
|
+
// Collect output lines per package (for package-level output)
|
|
70
|
+
const pkgOutputMap = new Map();
|
|
31
71
|
const testMap = new Map();
|
|
72
|
+
// Track package-level failures (where event.Test is undefined)
|
|
73
|
+
const pkgFailures = new Map();
|
|
32
74
|
for (const line of lines) {
|
|
33
75
|
let event;
|
|
34
76
|
try {
|
|
@@ -37,18 +79,64 @@ export function parseGoTestJson(stdout, exitCode) {
|
|
|
37
79
|
catch {
|
|
38
80
|
continue;
|
|
39
81
|
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
82
|
+
if (event.Test) {
|
|
83
|
+
const key = `${event.Package}/${event.Test}`;
|
|
84
|
+
// Collect output lines for tests (#51)
|
|
85
|
+
if (event.Action === "output" && event.Output) {
|
|
86
|
+
if (!testOutputMap.has(key)) {
|
|
87
|
+
testOutputMap.set(key, []);
|
|
88
|
+
}
|
|
89
|
+
testOutputMap.get(key).push(event.Output);
|
|
90
|
+
}
|
|
91
|
+
if (event.Action === "pass" || event.Action === "fail" || event.Action === "skip") {
|
|
92
|
+
const parent = deriveParentTestName(event.Test);
|
|
93
|
+
testMap.set(key, {
|
|
94
|
+
package: event.Package,
|
|
95
|
+
name: event.Test,
|
|
96
|
+
...(parent ? { parent } : {}),
|
|
97
|
+
status: event.Action,
|
|
98
|
+
elapsed: event.Elapsed,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
50
101
|
}
|
|
102
|
+
else {
|
|
103
|
+
// Package-level events (no Test field) (#52)
|
|
104
|
+
if (event.Action === "output" && event.Output) {
|
|
105
|
+
if (!pkgOutputMap.has(event.Package)) {
|
|
106
|
+
pkgOutputMap.set(event.Package, []);
|
|
107
|
+
}
|
|
108
|
+
pkgOutputMap.get(event.Package).push(event.Output);
|
|
109
|
+
}
|
|
110
|
+
if (event.Action === "fail") {
|
|
111
|
+
pkgFailures.set(event.Package, {
|
|
112
|
+
package: event.Package,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Attach output to failed tests (#51)
|
|
118
|
+
for (const [key, entry] of testMap) {
|
|
119
|
+
if (entry.status === "fail") {
|
|
120
|
+
const outputLines = testOutputMap.get(key);
|
|
121
|
+
if (outputLines && outputLines.length > 0) {
|
|
122
|
+
entry.output = outputLines.join("").trimEnd();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Attach package-level output to package failures (#52)
|
|
127
|
+
for (const [pkg, failure] of pkgFailures) {
|
|
128
|
+
const outputLines = pkgOutputMap.get(pkg);
|
|
129
|
+
if (outputLines && outputLines.length > 0) {
|
|
130
|
+
failure.output = outputLines.join("").trimEnd();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Filter out package failures that have corresponding test-level results
|
|
134
|
+
// (a package "fail" event with tests means the package summary, not a build failure)
|
|
135
|
+
const pkgWithTests = new Set();
|
|
136
|
+
for (const entry of testMap.values()) {
|
|
137
|
+
pkgWithTests.add(entry.package);
|
|
51
138
|
}
|
|
139
|
+
const packageFailures = Array.from(pkgFailures.values()).filter((f) => !pkgWithTests.has(f.package));
|
|
52
140
|
const tests = Array.from(testMap.values());
|
|
53
141
|
const passed = tests.filter((t) => t.status === "pass").length;
|
|
54
142
|
const failed = tests.filter((t) => t.status === "fail").length;
|
|
@@ -56,19 +144,146 @@ export function parseGoTestJson(stdout, exitCode) {
|
|
|
56
144
|
return {
|
|
57
145
|
success: exitCode === 0,
|
|
58
146
|
tests,
|
|
147
|
+
packageFailures: packageFailures.length > 0 ? packageFailures : undefined,
|
|
59
148
|
total: tests.length,
|
|
60
149
|
passed,
|
|
61
150
|
failed,
|
|
62
151
|
skipped,
|
|
63
152
|
};
|
|
64
153
|
}
|
|
65
|
-
|
|
66
|
-
|
|
154
|
+
function deriveParentTestName(testName) {
|
|
155
|
+
const idx = testName.lastIndexOf("/");
|
|
156
|
+
if (idx <= 0)
|
|
157
|
+
return undefined;
|
|
158
|
+
return testName.slice(0, idx);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Parses `go vet -json` output into structured diagnostics.
|
|
162
|
+
*
|
|
163
|
+
* The `-json` flag produces output grouped by package. Each package entry is a JSON
|
|
164
|
+
* object keyed by package import path, containing analyzer results:
|
|
165
|
+
* ```json
|
|
166
|
+
* {
|
|
167
|
+
* "<package>": {
|
|
168
|
+
* "<analyzer>": {
|
|
169
|
+
* "posn": "file.go:10:5",
|
|
170
|
+
* "message": "..."
|
|
171
|
+
* }
|
|
172
|
+
* // or an array of diagnostics per analyzer
|
|
173
|
+
* }
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* Falls back to text parsing if JSON parsing fails (e.g., older Go versions).
|
|
178
|
+
*/
|
|
179
|
+
export function parseGoVetOutput(stdout, stderr, exitCode) {
|
|
180
|
+
const compilationErrors = extractVetCompilationErrors(stdout, stderr);
|
|
181
|
+
// Try JSON parsing first (go vet -json)
|
|
182
|
+
const jsonResult = tryParseGoVetJson(stdout, stderr);
|
|
183
|
+
if (jsonResult) {
|
|
184
|
+
return {
|
|
185
|
+
...jsonResult,
|
|
186
|
+
success: exitCode === 0,
|
|
187
|
+
compilationErrors: compilationErrors.length > 0 ? compilationErrors : undefined,
|
|
188
|
+
total: jsonResult.total,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Fallback: text parsing
|
|
192
|
+
return parseGoVetText(stdout, stderr, exitCode, compilationErrors);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Attempt to parse `go vet -json` structured output.
|
|
196
|
+
* Returns null if the output doesn't look like JSON.
|
|
197
|
+
*
|
|
198
|
+
* go vet -json emits one JSON object per package on stdout/stderr.
|
|
199
|
+
* The structure is: { "<package-path>": { "<analyzer>": <diagnostic-or-array> } }
|
|
200
|
+
* Each diagnostic has: { posn: "file:line:col", message: "..." }
|
|
201
|
+
*/
|
|
202
|
+
function tryParseGoVetJson(stdout, stderr) {
|
|
203
|
+
// go vet -json writes JSON to stdout; stderr may have non-JSON messages
|
|
204
|
+
const combined = (stdout + "\n" + stderr).trim();
|
|
205
|
+
if (!combined || !combined.includes("{"))
|
|
206
|
+
return null;
|
|
207
|
+
const diagnostics = [];
|
|
208
|
+
// go vet -json outputs concatenated JSON objects (one per package)
|
|
209
|
+
const chunks = splitJsonObjects(combined);
|
|
210
|
+
if (chunks.length === 0)
|
|
211
|
+
return null;
|
|
212
|
+
let parsedAny = false;
|
|
213
|
+
for (const chunk of chunks) {
|
|
214
|
+
let pkgObj;
|
|
215
|
+
try {
|
|
216
|
+
pkgObj = JSON.parse(chunk);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// Each top-level key is a package path
|
|
222
|
+
for (const [, analyzerMap] of Object.entries(pkgObj)) {
|
|
223
|
+
if (typeof analyzerMap !== "object" || analyzerMap === null)
|
|
224
|
+
continue;
|
|
225
|
+
// Each key in the analyzerMap is an analyzer name
|
|
226
|
+
for (const [analyzerName, diagData] of Object.entries(analyzerMap)) {
|
|
227
|
+
const diagItems = Array.isArray(diagData) ? diagData : [diagData];
|
|
228
|
+
for (const item of diagItems) {
|
|
229
|
+
if (typeof item !== "object" || item === null)
|
|
230
|
+
continue;
|
|
231
|
+
const diag = item;
|
|
232
|
+
if (!diag.posn || !diag.message)
|
|
233
|
+
continue;
|
|
234
|
+
parsedAny = true;
|
|
235
|
+
const parsed = parseVetPosn(diag.posn);
|
|
236
|
+
diagnostics.push({
|
|
237
|
+
file: parsed.file,
|
|
238
|
+
line: parsed.line,
|
|
239
|
+
column: parsed.column,
|
|
240
|
+
message: diag.message,
|
|
241
|
+
analyzer: analyzerName,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!parsedAny)
|
|
248
|
+
return null;
|
|
249
|
+
return { diagnostics, total: diagnostics.length };
|
|
250
|
+
}
|
|
251
|
+
/** Parse a posn string like "file.go:10:5" or "file.go:10" into components. */
|
|
252
|
+
function parseVetPosn(posn) {
|
|
253
|
+
// posn format: "file.go:line:col" or "file.go:line"
|
|
254
|
+
const parts = posn.split(":");
|
|
255
|
+
if (parts.length >= 3) {
|
|
256
|
+
return {
|
|
257
|
+
file: parts.slice(0, -2).join(":"),
|
|
258
|
+
line: parseInt(parts[parts.length - 2], 10) || 0,
|
|
259
|
+
column: parseInt(parts[parts.length - 1], 10) || undefined,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (parts.length === 2) {
|
|
263
|
+
return {
|
|
264
|
+
file: parts[0],
|
|
265
|
+
line: parseInt(parts[1], 10) || 0,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return { file: posn, line: 0 };
|
|
269
|
+
}
|
|
270
|
+
/** Fallback text parser for go vet output (non-JSON mode). */
|
|
271
|
+
function parseGoVetText(stdout, stderr, exitCode, knownCompilationErrors = []) {
|
|
67
272
|
const output = stdout + "\n" + stderr;
|
|
68
273
|
const lines = output.split("\n");
|
|
69
274
|
const diagnostics = [];
|
|
275
|
+
const compilationErrors = [...knownCompilationErrors];
|
|
70
276
|
for (const line of lines) {
|
|
71
|
-
const
|
|
277
|
+
const trimmed = line.trim();
|
|
278
|
+
if (!trimmed)
|
|
279
|
+
continue;
|
|
280
|
+
if (isCompilationErrorLine(trimmed)) {
|
|
281
|
+
if (!compilationErrors.includes(trimmed)) {
|
|
282
|
+
compilationErrors.push(trimmed);
|
|
283
|
+
}
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const match = trimmed.match(GO_ERROR_RE);
|
|
72
287
|
if (match) {
|
|
73
288
|
diagnostics.push({
|
|
74
289
|
file: match[1],
|
|
@@ -78,77 +293,279 @@ export function parseGoVetOutput(stdout, stderr) {
|
|
|
78
293
|
});
|
|
79
294
|
}
|
|
80
295
|
}
|
|
81
|
-
return {
|
|
296
|
+
return {
|
|
297
|
+
success: exitCode === 0,
|
|
298
|
+
diagnostics,
|
|
299
|
+
compilationErrors: compilationErrors.length > 0 ? compilationErrors : undefined,
|
|
300
|
+
total: diagnostics.length,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function extractVetCompilationErrors(stdout, stderr) {
|
|
304
|
+
const lines = (stdout + "\n" + stderr)
|
|
305
|
+
.split("\n")
|
|
306
|
+
.map((l) => l.trim())
|
|
307
|
+
.filter(Boolean);
|
|
308
|
+
const seen = new Set();
|
|
309
|
+
const errors = [];
|
|
310
|
+
for (const line of lines) {
|
|
311
|
+
if (isCompilationErrorLine(line) && !seen.has(line)) {
|
|
312
|
+
seen.add(line);
|
|
313
|
+
errors.push(line);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return errors;
|
|
317
|
+
}
|
|
318
|
+
function isCompilationErrorLine(line) {
|
|
319
|
+
return (line.startsWith("# ") ||
|
|
320
|
+
line.includes("undefined:") ||
|
|
321
|
+
line.includes("could not import") ||
|
|
322
|
+
line.includes("build constraints exclude") ||
|
|
323
|
+
(line.includes("package ") && line.includes(" is not in ")) ||
|
|
324
|
+
line.includes("cannot find package"));
|
|
82
325
|
}
|
|
83
326
|
/** Parses `go run` output into structured result with stdout, stderr, and exit code. */
|
|
84
|
-
export function parseGoRunOutput(stdout, stderr, exitCode) {
|
|
327
|
+
export function parseGoRunOutput(stdout, stderr, exitCode, timedOut = false, signal) {
|
|
85
328
|
return {
|
|
86
329
|
exitCode,
|
|
87
330
|
stdout: stdout.trimEnd(),
|
|
88
331
|
stderr: stderr.trimEnd(),
|
|
89
|
-
|
|
332
|
+
...(timedOut ? { timedOut: true } : {}),
|
|
333
|
+
...(signal ? { signal } : {}),
|
|
334
|
+
success: exitCode === 0 && !timedOut,
|
|
90
335
|
};
|
|
91
336
|
}
|
|
92
|
-
/**
|
|
93
|
-
|
|
337
|
+
/**
|
|
338
|
+
* Parses `go mod tidy` output into structured result with success status, summary,
|
|
339
|
+
* and whether changes were made.
|
|
340
|
+
*
|
|
341
|
+
* @param goModHashBefore - MD5/hash of go.mod before tidy (optional, for change detection)
|
|
342
|
+
* @param goModHashAfter - MD5/hash of go.mod after tidy (optional, for change detection)
|
|
343
|
+
* @param goSumHashBefore - MD5/hash of go.sum before tidy (optional, for change detection)
|
|
344
|
+
* @param goSumHashAfter - MD5/hash of go.sum after tidy (optional, for change detection)
|
|
345
|
+
*/
|
|
346
|
+
export function parseGoModTidyOutput(stdout, stderr, exitCode, goModHashBefore, goModHashAfter, goSumHashBefore, goSumHashAfter, addedModules = [], removedModules = []) {
|
|
94
347
|
if (exitCode === 0) {
|
|
95
348
|
const combined = (stdout + "\n" + stderr).trim();
|
|
349
|
+
// Determine whether changes were made
|
|
350
|
+
let madeChanges;
|
|
351
|
+
if (goModHashBefore !== undefined && goModHashAfter !== undefined) {
|
|
352
|
+
const modChanged = goModHashBefore !== goModHashAfter;
|
|
353
|
+
const sumChanged = goSumHashBefore !== undefined &&
|
|
354
|
+
goSumHashAfter !== undefined &&
|
|
355
|
+
goSumHashBefore !== goSumHashAfter;
|
|
356
|
+
madeChanges = modChanged || sumChanged;
|
|
357
|
+
}
|
|
358
|
+
else if (combined) {
|
|
359
|
+
// If we have output (e.g., "go: downloading..."), changes were likely made
|
|
360
|
+
madeChanges = true;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
// No output and no hashes: assume no changes
|
|
364
|
+
madeChanges = false;
|
|
365
|
+
}
|
|
96
366
|
return {
|
|
97
367
|
success: true,
|
|
98
368
|
summary: combined || "go.mod and go.sum are already tidy.",
|
|
369
|
+
...(addedModules.length > 0 ? { addedModules } : {}),
|
|
370
|
+
...(removedModules.length > 0 ? { removedModules } : {}),
|
|
371
|
+
madeChanges,
|
|
99
372
|
};
|
|
100
373
|
}
|
|
101
374
|
const combined = (stderr + "\n" + stdout).trim();
|
|
375
|
+
const errorType = categorizeGoModTidyError(combined);
|
|
102
376
|
return {
|
|
103
377
|
success: false,
|
|
104
378
|
summary: combined || "go mod tidy failed.",
|
|
379
|
+
errorType,
|
|
105
380
|
};
|
|
106
381
|
}
|
|
107
|
-
|
|
382
|
+
function categorizeGoModTidyError(message) {
|
|
383
|
+
const lower = message.toLowerCase();
|
|
384
|
+
if (lower.includes("dial tcp") ||
|
|
385
|
+
lower.includes("i/o timeout") ||
|
|
386
|
+
lower.includes("connection refused") ||
|
|
387
|
+
lower.includes("no such host")) {
|
|
388
|
+
return "network";
|
|
389
|
+
}
|
|
390
|
+
if (lower.includes("checksum mismatch") || lower.includes("sumdb")) {
|
|
391
|
+
return "checksum";
|
|
392
|
+
}
|
|
393
|
+
if (lower.includes("syntax error") || lower.includes("invalid go version")) {
|
|
394
|
+
return "syntax";
|
|
395
|
+
}
|
|
396
|
+
return "unknown";
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Regex for gofmt stderr parse errors.
|
|
400
|
+
* Matches patterns like: "file.go:10:5: expected ..." or "file.go:10: expected ..."
|
|
401
|
+
*/
|
|
402
|
+
const GOFMT_ERROR_RE = /^(.+?):(\d+)(?::(\d+))?: (.+)$/;
|
|
403
|
+
/** Parses `gofmt -l` or `gofmt -l -w` output into structured result with file list. */
|
|
108
404
|
export function parseGoFmtOutput(stdout, stderr, exitCode, checkMode) {
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
const
|
|
405
|
+
// Both check mode (-l) and fix mode (-l -w) list changed files on stdout.
|
|
406
|
+
// With -d enabled, stdout may contain unified diffs.
|
|
407
|
+
const changes = parseGofmtChanges(stdout);
|
|
408
|
+
const filesFromDiff = changes.map((c) => c.file);
|
|
409
|
+
const filesFromList = stdout
|
|
114
410
|
.split("\n")
|
|
115
411
|
.map((f) => f.trim())
|
|
116
|
-
.filter(
|
|
412
|
+
.filter((f) => !!f && !f.startsWith("diff ") && !f.startsWith("--- ") && !f.startsWith("+++ "));
|
|
413
|
+
const files = filesFromDiff.length > 0 ? Array.from(new Set(filesFromDiff)) : filesFromList;
|
|
117
414
|
const hasErrors = exitCode !== 0 || (checkMode && files.length > 0);
|
|
415
|
+
// Parse stderr for parse errors (Gap #151)
|
|
416
|
+
const parseErrors = [];
|
|
417
|
+
if (stderr) {
|
|
418
|
+
const stderrLines = stderr.split("\n");
|
|
419
|
+
for (const line of stderrLines) {
|
|
420
|
+
const trimmed = line.trim();
|
|
421
|
+
if (!trimmed)
|
|
422
|
+
continue;
|
|
423
|
+
const match = trimmed.match(GOFMT_ERROR_RE);
|
|
424
|
+
if (match) {
|
|
425
|
+
parseErrors.push({
|
|
426
|
+
file: match[1],
|
|
427
|
+
line: parseInt(match[2], 10),
|
|
428
|
+
column: match[3] ? parseInt(match[3], 10) : undefined,
|
|
429
|
+
message: match[4],
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
118
434
|
return {
|
|
119
435
|
success: !hasErrors,
|
|
120
436
|
filesChanged: files.length,
|
|
121
437
|
files,
|
|
438
|
+
changes: changes.length > 0 ? changes : undefined,
|
|
439
|
+
parseErrors: parseErrors.length > 0 ? parseErrors : undefined,
|
|
122
440
|
};
|
|
123
441
|
}
|
|
442
|
+
function parseGofmtChanges(stdout) {
|
|
443
|
+
if (!stdout.includes("\ndiff ") && !stdout.startsWith("diff ")) {
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
const blocks = stdout
|
|
447
|
+
.split(/^diff /m)
|
|
448
|
+
.map((b) => b.trim())
|
|
449
|
+
.filter(Boolean);
|
|
450
|
+
const changes = [];
|
|
451
|
+
for (const block of blocks) {
|
|
452
|
+
const diffText = `diff ${block}`;
|
|
453
|
+
const firstLine = diffText.split("\n")[0] ?? "";
|
|
454
|
+
const fileMatch = firstLine.match(/^diff\s+\S+\s+(\S+)/);
|
|
455
|
+
const file = fileMatch?.[1];
|
|
456
|
+
if (!file)
|
|
457
|
+
continue;
|
|
458
|
+
changes.push({ file, diff: diffText });
|
|
459
|
+
}
|
|
460
|
+
return changes;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Regex for go generate -v output: prints package name as it's processed.
|
|
464
|
+
* Format: "mypackage" (just package name on stderr when -v is used)
|
|
465
|
+
*
|
|
466
|
+
* Regex for go generate -x output: prints the command being executed.
|
|
467
|
+
* Format on stderr: "cd /path/to/pkg; <command>" or just the command
|
|
468
|
+
*
|
|
469
|
+
* Combined regex for go generate verbose output lines indicating per-directive status.
|
|
470
|
+
* Patterns:
|
|
471
|
+
* - `<file>:<line>: running "<command>"...` (from -v, not always present)
|
|
472
|
+
* - `<file>.go:<line>: <command>` (from stderr with -x)
|
|
473
|
+
* - `cd /path; <command>` (from -x)
|
|
474
|
+
*/
|
|
475
|
+
const GO_GENERATE_DIRECTIVE_RE = /^(.+\.go):(\d+): running "(.+?)"$/;
|
|
476
|
+
const GO_GENERATE_X_FILE_RE = /^(.+\.go):(\d+): (.+)$/;
|
|
124
477
|
/** Parses `go generate` output into structured result with success status and output text. */
|
|
125
|
-
export function parseGoGenerateOutput(stdout, stderr, exitCode) {
|
|
478
|
+
export function parseGoGenerateOutput(stdout, stderr, exitCode, timedOut = false) {
|
|
126
479
|
const combined = (stdout + "\n" + stderr).trim();
|
|
480
|
+
// Parse per-directive output from -v/-x flags (Gap #152)
|
|
481
|
+
const directives = [];
|
|
482
|
+
const allLines = combined.split("\n");
|
|
483
|
+
for (const line of allLines) {
|
|
484
|
+
const trimmed = line.trim();
|
|
485
|
+
if (!trimmed)
|
|
486
|
+
continue;
|
|
487
|
+
// Match "file.go:10: running "command""
|
|
488
|
+
const verboseMatch = trimmed.match(GO_GENERATE_DIRECTIVE_RE);
|
|
489
|
+
if (verboseMatch) {
|
|
490
|
+
directives.push({
|
|
491
|
+
file: verboseMatch[1],
|
|
492
|
+
line: parseInt(verboseMatch[2], 10),
|
|
493
|
+
command: verboseMatch[3],
|
|
494
|
+
status: exitCode === 0 ? "completed" : "running",
|
|
495
|
+
});
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
// Match "file.go:10: <command>" from -x output
|
|
499
|
+
const xMatch = trimmed.match(GO_GENERATE_X_FILE_RE);
|
|
500
|
+
if (xMatch) {
|
|
501
|
+
// Avoid matching error messages that look like file:line: patterns
|
|
502
|
+
const command = xMatch[3];
|
|
503
|
+
// Skip lines that look like error messages (contain "exec:", "not found", etc.)
|
|
504
|
+
if (!command.startsWith("running ") &&
|
|
505
|
+
!command.includes("executable file not found") &&
|
|
506
|
+
!command.startsWith("bad flag syntax")) {
|
|
507
|
+
directives.push({
|
|
508
|
+
file: xMatch[1],
|
|
509
|
+
line: parseInt(xMatch[2], 10),
|
|
510
|
+
command: command,
|
|
511
|
+
status: exitCode === 0 ? "completed" : undefined,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
127
516
|
return {
|
|
128
|
-
success: exitCode === 0,
|
|
517
|
+
success: exitCode === 0 && !timedOut,
|
|
129
518
|
output: combined,
|
|
519
|
+
...(timedOut ? { timedOut: true } : {}),
|
|
520
|
+
directives: directives.length > 0 ? directives : undefined,
|
|
130
521
|
};
|
|
131
522
|
}
|
|
132
|
-
/**
|
|
133
|
-
|
|
134
|
-
|
|
523
|
+
/**
|
|
524
|
+
* Parses `go env -json` output into structured result with environment variables and key fields.
|
|
525
|
+
* When specific vars are queried, the result only contains those vars from the JSON output.
|
|
526
|
+
* The queriedVars parameter is used to ensure compact mode includes the queried variables.
|
|
527
|
+
*/
|
|
528
|
+
export function parseGoEnvOutput(stdout, _queriedVars) {
|
|
529
|
+
let allVars;
|
|
530
|
+
try {
|
|
531
|
+
allVars = JSON.parse(stdout || "{}");
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
return {
|
|
535
|
+
success: false,
|
|
536
|
+
vars: undefined,
|
|
537
|
+
goroot: "",
|
|
538
|
+
gopath: "",
|
|
539
|
+
goversion: "",
|
|
540
|
+
goos: "",
|
|
541
|
+
goarch: "",
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
const queriedVars = _queriedVars ?? [];
|
|
545
|
+
const includeVars = _queriedVars === undefined ? true : queriedVars.length > 0;
|
|
546
|
+
const vars = includeVars ? allVars : undefined;
|
|
547
|
+
const cgoRaw = allVars.CGO_ENABLED;
|
|
548
|
+
const cgoEnabled = cgoRaw !== undefined ? cgoRaw === "1" : undefined;
|
|
135
549
|
return {
|
|
550
|
+
success: true,
|
|
136
551
|
vars,
|
|
137
|
-
goroot:
|
|
138
|
-
gopath:
|
|
139
|
-
goversion:
|
|
140
|
-
goos:
|
|
141
|
-
goarch:
|
|
552
|
+
goroot: allVars.GOROOT ?? "",
|
|
553
|
+
gopath: allVars.GOPATH ?? "",
|
|
554
|
+
goversion: allVars.GOVERSION ?? "",
|
|
555
|
+
goos: allVars.GOOS ?? "",
|
|
556
|
+
goarch: allVars.GOARCH ?? "",
|
|
557
|
+
...(cgoEnabled !== undefined ? { cgoEnabled } : {}),
|
|
558
|
+
// Store queriedVars for compact mode usage (handled in formatter)
|
|
142
559
|
};
|
|
143
560
|
}
|
|
144
561
|
/**
|
|
145
562
|
* Parses `go list -json` output into structured result with package list and total count.
|
|
146
563
|
* go list -json emits concatenated JSON objects (JSONL), one per package.
|
|
147
564
|
*/
|
|
148
|
-
export function parseGoListOutput(stdout) {
|
|
565
|
+
export function parseGoListOutput(stdout, exitCode) {
|
|
149
566
|
const packages = [];
|
|
150
567
|
if (!stdout.trim()) {
|
|
151
|
-
return { packages, total: 0 };
|
|
568
|
+
return { success: exitCode === 0, packages, total: 0 };
|
|
152
569
|
}
|
|
153
570
|
// go list -json outputs concatenated JSON objects separated by newlines.
|
|
154
571
|
// Split by detecting `}\n{` boundary and parse each object individually.
|
|
@@ -156,18 +573,62 @@ export function parseGoListOutput(stdout) {
|
|
|
156
573
|
for (const chunk of chunks) {
|
|
157
574
|
try {
|
|
158
575
|
const pkg = JSON.parse(chunk);
|
|
159
|
-
|
|
576
|
+
const entry = {
|
|
160
577
|
dir: pkg.Dir ?? "",
|
|
161
578
|
importPath: pkg.ImportPath ?? "",
|
|
162
579
|
name: pkg.Name ?? "",
|
|
163
580
|
goFiles: pkg.GoFiles,
|
|
581
|
+
testGoFiles: pkg.TestGoFiles,
|
|
582
|
+
imports: pkg.Imports,
|
|
583
|
+
standard: typeof pkg.Standard === "boolean" ? pkg.Standard : undefined,
|
|
584
|
+
stale: typeof pkg.Stale === "boolean" ? pkg.Stale : undefined,
|
|
585
|
+
module: pkg.Module && typeof pkg.Module === "object"
|
|
586
|
+
? {
|
|
587
|
+
path: typeof pkg.Module.Path === "string" ? pkg.Module.Path : undefined,
|
|
588
|
+
version: typeof pkg.Module.Version === "string" ? pkg.Module.Version : undefined,
|
|
589
|
+
}
|
|
590
|
+
: undefined,
|
|
591
|
+
};
|
|
592
|
+
// Capture Error field (Gap #155)
|
|
593
|
+
if (pkg.Error && typeof pkg.Error === "object" && pkg.Error.Err) {
|
|
594
|
+
entry.error = { err: String(pkg.Error.Err) };
|
|
595
|
+
}
|
|
596
|
+
packages.push(entry);
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
// skip malformed JSON chunks
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return { success: exitCode === 0, packages, total: packages.length };
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Parses `go list -json -m` output into structured result with module list and total count.
|
|
606
|
+
* go list -json -m emits concatenated JSON objects, one per module.
|
|
607
|
+
*/
|
|
608
|
+
export function parseGoListModulesOutput(stdout, exitCode) {
|
|
609
|
+
const modules = [];
|
|
610
|
+
if (!stdout.trim()) {
|
|
611
|
+
return { success: exitCode === 0, modules, total: 0 };
|
|
612
|
+
}
|
|
613
|
+
const chunks = splitJsonObjects(stdout);
|
|
614
|
+
for (const chunk of chunks) {
|
|
615
|
+
try {
|
|
616
|
+
const mod = JSON.parse(chunk);
|
|
617
|
+
modules.push({
|
|
618
|
+
path: mod.Path ?? "",
|
|
619
|
+
version: mod.Version || undefined,
|
|
620
|
+
dir: mod.Dir || undefined,
|
|
621
|
+
goMod: mod.GoMod || undefined,
|
|
622
|
+
goVersion: mod.GoVersion || undefined,
|
|
623
|
+
main: mod.Main || undefined,
|
|
624
|
+
indirect: mod.Indirect || undefined,
|
|
164
625
|
});
|
|
165
626
|
}
|
|
166
627
|
catch {
|
|
167
628
|
// skip malformed JSON chunks
|
|
168
629
|
}
|
|
169
630
|
}
|
|
170
|
-
return {
|
|
631
|
+
return { success: exitCode === 0, modules, total: modules.length };
|
|
171
632
|
}
|
|
172
633
|
/** Splits concatenated JSON objects from go list -json output. */
|
|
173
634
|
function splitJsonObjects(text) {
|
|
@@ -211,15 +672,44 @@ export function parseGolangciLintJson(stdout, _exitCode) {
|
|
|
211
672
|
const issues = parsed.Issues ?? [];
|
|
212
673
|
for (const issue of issues) {
|
|
213
674
|
const severity = mapSeverity(issue.Severity);
|
|
214
|
-
|
|
675
|
+
const diag = {
|
|
215
676
|
file: issue.Pos?.Filename ?? "",
|
|
216
677
|
line: issue.Pos?.Line ?? 0,
|
|
217
678
|
column: issue.Pos?.Column || undefined,
|
|
218
679
|
linter: issue.FromLinter ?? "",
|
|
680
|
+
category: classifyLinter(issue.FromLinter ?? ""),
|
|
219
681
|
severity,
|
|
220
682
|
message: issue.Text ?? "",
|
|
221
683
|
sourceLine: issue.SourceLines?.[0] || undefined,
|
|
222
|
-
}
|
|
684
|
+
};
|
|
685
|
+
// Capture Replacement/fix data (Gap #154)
|
|
686
|
+
if (issue.Replacement) {
|
|
687
|
+
const replacement = issue.Replacement;
|
|
688
|
+
if (replacement.NeedOnlyDelete || replacement.NewLines !== undefined) {
|
|
689
|
+
const fix = {
|
|
690
|
+
text: replacement.NeedOnlyDelete ? "" : (replacement.NewLines ?? []).join("\n"),
|
|
691
|
+
};
|
|
692
|
+
// Include range if inline fix info is present
|
|
693
|
+
if (replacement.Inline) {
|
|
694
|
+
fix.range = {
|
|
695
|
+
start: {
|
|
696
|
+
line: replacement.Inline.StartLine ?? issue.Pos?.Line ?? 0,
|
|
697
|
+
column: replacement.Inline.StartCol,
|
|
698
|
+
},
|
|
699
|
+
end: {
|
|
700
|
+
line: replacement.Inline.EndLine ?? issue.Pos?.Line ?? 0,
|
|
701
|
+
column: replacement.Inline.EndCol,
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
diag.fix = fix;
|
|
706
|
+
}
|
|
707
|
+
else if (typeof replacement.NewLines === "undefined" && !replacement.NeedOnlyDelete) {
|
|
708
|
+
// Some replacements only have NewLines without inline info
|
|
709
|
+
// If Replacement exists but has no expected structure, still try to capture it
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
diagnostics.push(diag);
|
|
223
713
|
}
|
|
224
714
|
const errors = diagnostics.filter((d) => d.severity === "error").length;
|
|
225
715
|
const warnings = diagnostics.filter((d) => d.severity === "warning").length;
|
|
@@ -249,12 +739,130 @@ function mapSeverity(severity) {
|
|
|
249
739
|
return "info";
|
|
250
740
|
return "warning";
|
|
251
741
|
}
|
|
252
|
-
|
|
253
|
-
|
|
742
|
+
function classifyLinter(linter) {
|
|
743
|
+
const name = linter.toLowerCase();
|
|
744
|
+
if (["gosec"].includes(name))
|
|
745
|
+
return "security";
|
|
746
|
+
if (["govet", "staticcheck", "typecheck", "errcheck", "ineffassign", "nilerr"].includes(name)) {
|
|
747
|
+
return "bug-risk";
|
|
748
|
+
}
|
|
749
|
+
if (["gocyclo", "cyclop", "funlen", "nestif", "maintidx"].includes(name))
|
|
750
|
+
return "complexity";
|
|
751
|
+
if (["prealloc", "perfsprint", "makezero", "unconvert"].includes(name))
|
|
752
|
+
return "performance";
|
|
753
|
+
if (["gofmt", "goimports", "revive", "stylecheck", "whitespace", "misspell"].includes(name)) {
|
|
754
|
+
return "style";
|
|
755
|
+
}
|
|
756
|
+
return "other";
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Regex patterns for go get output lines that indicate version resolution.
|
|
760
|
+
* Matches lines like:
|
|
761
|
+
* go: upgraded golang.org/x/text v0.3.7 => v0.14.0
|
|
762
|
+
* go: added github.com/pkg/errors v0.9.1
|
|
763
|
+
* go: downgraded golang.org/x/net v0.10.0 => v0.8.0
|
|
764
|
+
*/
|
|
765
|
+
const GO_GET_UPGRADED_RE = /^go: (?:upgraded|downgraded)\s+(\S+)\s+(\S+)\s+=>\s+(\S+)$/;
|
|
766
|
+
const GO_GET_ADDED_RE = /^go: added\s+(\S+)\s+(\S+)$/;
|
|
767
|
+
/**
|
|
768
|
+
* Regex for go get error lines per package.
|
|
769
|
+
* Examples:
|
|
770
|
+
* go: module github.com/nonexistent/pkg: no matching versions for query "latest"
|
|
771
|
+
* go: github.com/some/pkg@v1.0.0: verifying module: ...
|
|
772
|
+
*/
|
|
773
|
+
const GO_GET_ERROR_RE = /^go: (?:module\s+)?(\S+?)(?:@(\S+))?:\s+(.+)$/;
|
|
774
|
+
/** Parses `go get` output into structured result with success status, output text, resolved packages, and per-package status. */
|
|
775
|
+
export function parseGoGetOutput(stdout, stderr, exitCode, requestedPackages) {
|
|
254
776
|
const combined = (stdout + "\n" + stderr).trim();
|
|
777
|
+
const resolvedPackages = [];
|
|
778
|
+
// Per-package status tracking (Gap #153)
|
|
779
|
+
const packageStatuses = new Map();
|
|
780
|
+
const lines = combined.split("\n");
|
|
781
|
+
for (const line of lines) {
|
|
782
|
+
const trimmed = line.trim();
|
|
783
|
+
// Match "go: upgraded <pkg> <old> => <new>" or "go: downgraded <pkg> <old> => <new>"
|
|
784
|
+
const upgradeMatch = trimmed.match(GO_GET_UPGRADED_RE);
|
|
785
|
+
if (upgradeMatch) {
|
|
786
|
+
resolvedPackages.push({
|
|
787
|
+
package: upgradeMatch[1],
|
|
788
|
+
previousVersion: upgradeMatch[2],
|
|
789
|
+
newVersion: upgradeMatch[3],
|
|
790
|
+
});
|
|
791
|
+
packageStatuses.set(upgradeMatch[1], {
|
|
792
|
+
path: upgradeMatch[1],
|
|
793
|
+
version: upgradeMatch[3],
|
|
794
|
+
});
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
// Match "go: added <pkg> <version>"
|
|
798
|
+
const addedMatch = trimmed.match(GO_GET_ADDED_RE);
|
|
799
|
+
if (addedMatch) {
|
|
800
|
+
resolvedPackages.push({
|
|
801
|
+
package: addedMatch[1],
|
|
802
|
+
newVersion: addedMatch[2],
|
|
803
|
+
});
|
|
804
|
+
packageStatuses.set(addedMatch[1], {
|
|
805
|
+
path: addedMatch[1],
|
|
806
|
+
version: addedMatch[2],
|
|
807
|
+
});
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
// Match per-package error lines (Gap #153)
|
|
811
|
+
const errorMatch = trimmed.match(GO_GET_ERROR_RE);
|
|
812
|
+
if (errorMatch) {
|
|
813
|
+
const pkgPath = errorMatch[1];
|
|
814
|
+
const errMsg = errorMatch[3];
|
|
815
|
+
// Skip "downloading" lines which are progress, not errors
|
|
816
|
+
if (!errMsg.startsWith("downloading ")) {
|
|
817
|
+
const errorType = categorizeGoGetError(errMsg);
|
|
818
|
+
packageStatuses.set(pkgPath, {
|
|
819
|
+
path: pkgPath,
|
|
820
|
+
version: errorMatch[2] || undefined,
|
|
821
|
+
error: errMsg,
|
|
822
|
+
errorType,
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// If we have requestedPackages but some aren't in statuses, add them as successful (no error, no explicit status)
|
|
828
|
+
if (requestedPackages) {
|
|
829
|
+
for (const reqPkg of requestedPackages) {
|
|
830
|
+
// Strip version suffix from request (e.g., "github.com/pkg/errors@latest" -> "github.com/pkg/errors")
|
|
831
|
+
const pkgPath = reqPkg.replace(/@.*$/, "");
|
|
832
|
+
if (!packageStatuses.has(pkgPath)) {
|
|
833
|
+
// If the overall command succeeded, mark as successful
|
|
834
|
+
if (exitCode === 0) {
|
|
835
|
+
packageStatuses.set(pkgPath, { path: pkgPath });
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
const packages = packageStatuses.size > 0 ? Array.from(packageStatuses.values()) : undefined;
|
|
255
841
|
return {
|
|
256
842
|
success: exitCode === 0,
|
|
257
843
|
output: combined || undefined,
|
|
844
|
+
resolvedPackages: resolvedPackages.length > 0 ? resolvedPackages : undefined,
|
|
845
|
+
packages,
|
|
258
846
|
};
|
|
259
847
|
}
|
|
848
|
+
function categorizeGoGetError(message) {
|
|
849
|
+
const lower = message.toLowerCase();
|
|
850
|
+
if (lower.includes("timeout") || lower.includes("context deadline exceeded")) {
|
|
851
|
+
return "timeout";
|
|
852
|
+
}
|
|
853
|
+
if (lower.includes("no such host") || lower.includes("lookup ")) {
|
|
854
|
+
return "dns";
|
|
855
|
+
}
|
|
856
|
+
if (lower.includes("authentication required") ||
|
|
857
|
+
lower.includes("permission denied") ||
|
|
858
|
+
lower.includes("access denied") ||
|
|
859
|
+
lower.includes("401") ||
|
|
860
|
+
lower.includes("403")) {
|
|
861
|
+
return "authentication";
|
|
862
|
+
}
|
|
863
|
+
if (lower.includes("dial tcp") || lower.includes("connection refused") || lower.includes("tls")) {
|
|
864
|
+
return "network";
|
|
865
|
+
}
|
|
866
|
+
return "unknown";
|
|
867
|
+
}
|
|
260
868
|
//# sourceMappingURL=parsers.js.map
|