fix: decode console output to ensure tracebacks are detected even when encoded
This commit is contained in:
@@ -482,4 +482,26 @@ describe("monitorConsole", () => {
|
|||||||
// We expect it to have called api.time fewer than 10 times (excluding the startTick call)
|
// We expect it to have called api.time fewer than 10 times (excluding the startTick call)
|
||||||
expect(api.time.mock.calls.length).toBeLessThan(10);
|
expect(api.time.mock.calls.length).toBeLessThan(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("detects encoded tracebacks in log lines", async () => {
|
||||||
|
const api = buildMockApi({ ticks: [100, 101, 102] });
|
||||||
|
setTimeout(() => {
|
||||||
|
api._fireConsole({
|
||||||
|
messages: {
|
||||||
|
log: [
|
||||||
|
"Error: ReferenceError: a is not defined%0A at eval (eval at <anonymous> (_console1778948572008_0:1:46), <anonymous>:1:1)%0A at _console1778948572008_0:1:46",
|
||||||
|
],
|
||||||
|
results: [],
|
||||||
|
},
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
const result = await monitorConsole(api, {
|
||||||
|
...BASE_OPTS,
|
||||||
|
onTraceback: "fail",
|
||||||
|
});
|
||||||
|
expect(result.sawTraceback).toBe(true);
|
||||||
|
expect(result.sawErrorLog).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+31
-5
@@ -51,7 +51,8 @@ export function buildSubscribePath(hostname, shard) {
|
|||||||
*/
|
*/
|
||||||
export function detectTraceback(errorText) {
|
export function detectTraceback(errorText) {
|
||||||
if (!errorText) return false;
|
if (!errorText) return false;
|
||||||
return /^\s{4}at /m.test(errorText);
|
const text = safeDecode(errorText);
|
||||||
|
return /^\s{4}at /m.test(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,6 +68,22 @@ export function detectWarning(logLines) {
|
|||||||
return logLines.some((line) => warnPattern.test(line));
|
return logLines.some((line) => warnPattern.test(line));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely decodes a URI-encoded string from the Screeps console.
|
||||||
|
* Returns the original string if decoding fails or if no '%' is present.
|
||||||
|
*
|
||||||
|
* @param {string} text
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function safeDecode(text) {
|
||||||
|
if (typeof text !== "string" || !text.includes("%")) return text;
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(text);
|
||||||
|
} catch (err) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ────────────────────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────────────────────
|
||||||
// Output helpers
|
// Output helpers
|
||||||
// ────────────────────────────────────────────────────────────────────────────
|
// ────────────────────────────────────────────────────────────────────────────
|
||||||
@@ -166,11 +183,19 @@ export function handleConsoleEvent(event, opts, stdoutBuffer, state) {
|
|||||||
if (detectWarning(logLines)) {
|
if (detectWarning(logLines)) {
|
||||||
state.sawWarningLog = true;
|
state.sawWarningLog = true;
|
||||||
const warnPattern = /<font\s+color=['"](?:orange|yellow)['"]/i;
|
const warnPattern = /<font\s+color=['"](?:orange|yellow)['"]/i;
|
||||||
logLines.filter((l) => warnPattern.test(l)).forEach((l) => core.warning(l));
|
logLines
|
||||||
|
.filter((l) => warnPattern.test(l))
|
||||||
|
.forEach((l) => core.warning(safeDecode(l)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Traceback detection in log lines (Screeps sometimes sends errors here) ─
|
||||||
|
if (logLines.some((l) => detectTraceback(l))) {
|
||||||
|
state.sawTraceback = true;
|
||||||
|
state.sawErrorLog = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Stdout lines ──────────────────────────────────────────────────────────
|
// ── Stdout lines ──────────────────────────────────────────────────────────
|
||||||
const allStdout = [...logLines, ...results];
|
const allStdout = [...logLines, ...results].map(safeDecode);
|
||||||
if (allStdout.length > 0) {
|
if (allStdout.length > 0) {
|
||||||
if (logToFile) {
|
if (logToFile) {
|
||||||
stdoutBuffer.push(...allStdout);
|
stdoutBuffer.push(...allStdout);
|
||||||
@@ -182,8 +207,9 @@ export function handleConsoleEvent(event, opts, stdoutBuffer, state) {
|
|||||||
// ── Error field (always live) ─────────────────────────────────────────────
|
// ── Error field (always live) ─────────────────────────────────────────────
|
||||||
if (errorText) {
|
if (errorText) {
|
||||||
state.sawErrorLog = true;
|
state.sawErrorLog = true;
|
||||||
core.error(errorText);
|
const decodedError = safeDecode(errorText);
|
||||||
if (detectTraceback(errorText)) {
|
core.error(decodedError);
|
||||||
|
if (detectTraceback(decodedError)) {
|
||||||
state.sawTraceback = true;
|
state.sawTraceback = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
function detectTraceback(errorText) {
|
||||||
|
if (!errorText) return false;
|
||||||
|
return /^\s{4}at /m.test(errorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodedTraceback =
|
||||||
|
"Error: ReferenceError: a is not defined%0A at eval (eval at <anonymous> (_console1778948572008_0:1:46), <anonymous>:1:1)%0A at _console1778948572008_0:1:46%0A at _console1778948572008_0:1:60%0A at exports.evalCode (<runtime>:15347:63)%0A at exports.run (<runtime>:20876:41)%0A";
|
||||||
|
|
||||||
|
console.log("Encoded match:", detectTraceback(encodedTraceback));
|
||||||
|
|
||||||
|
const decodedTraceback = decodeURIComponent(encodedTraceback);
|
||||||
|
console.log("Decoded match:", detectTraceback(decodedTraceback));
|
||||||
Reference in New Issue
Block a user