From 883c6bd591fa51465a90fbef9ac2b4f91c330718 Mon Sep 17 00:00:00 2001 From: drakeo338 Date: Tue, 29 Sep 2026 18:02:31 +0000 Subject: [PATCH] fix(interpreter): send console arguments to devtools as strings The patched console methods put the raw arguments into the Log message. The server expects `messages: Vec`, so any non-string argument, such as `console.log("Wee", {a: 1})`, failed to deserialize and was dropped. Map each argument to a string before sending: strings are kept as-is, everything else goes through JSON.stringify, falling back to String() when that throws or returns undefined. Fixes #5869 --- packages/interpreter/src/js/hash.txt | 2 +- packages/interpreter/src/js/patch_console.js | 2 +- .../interpreter/src/ts/patch_console.spec.ts | 38 +++++++++++++++++++ packages/interpreter/src/ts/patch_console.ts | 22 ++++++++--- 4 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 packages/interpreter/src/ts/patch_console.spec.ts diff --git a/packages/interpreter/src/js/hash.txt b/packages/interpreter/src/js/hash.txt index e8e88d736e..1f64435330 100644 --- a/packages/interpreter/src/js/hash.txt +++ b/packages/interpreter/src/js/hash.txt @@ -1 +1 @@ -[4905932134995611186, 10317260808282072637, 14897950097980380376, 5444526391971481782, 16137391248650656019, 5052021921702764563, 10728655726121042447, 11339769846046015954] \ No newline at end of file +[4905932134995611186, 10317260808282072637, 14897950097980380376, 5444526391971481782, 16137391248650656019, 13191714558229096046, 12344782490540418386, 10728655726121042447, 11339769846046015954] \ No newline at end of file diff --git a/packages/interpreter/src/js/patch_console.js b/packages/interpreter/src/js/patch_console.js index 4383386128..2a6b18ab0c 100644 --- a/packages/interpreter/src/js/patch_console.js +++ b/packages/interpreter/src/js/patch_console.js @@ -1 +1 @@ -function monkeyPatchConsole(ws){let console=window.console,log=console.log,info=console.info,warn=console.warn,error=console.error,debug=console.debug;console.log=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"log",messages:args}}));log.apply(console,args)},console.info=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"info",messages:args}}));info.apply(console,args)},console.warn=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"warn",messages:args}}));warn.apply(console,args)},console.error=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"error",messages:args}}));error.apply(console,args)},console.debug=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"debug",messages:args}}));debug.apply(console,args)}}export{monkeyPatchConsole}; +function stringify(args){return args.map((a)=>{if(typeof a==="string")return a;try{let s=JSON.stringify(a);return s===void 0?String(a):s}catch{return String(a)}})}function monkeyPatchConsole(ws){let console=window.console,{log,info,warn,error,debug}=console;console.log=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"log",messages:stringify(args)}}));log.apply(console,args)},console.info=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"info",messages:stringify(args)}}));info.apply(console,args)},console.warn=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"warn",messages:stringify(args)}}));warn.apply(console,args)},console.error=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"error",messages:stringify(args)}}));error.apply(console,args)},console.debug=function(...args){if(ws.readyState===WebSocket.OPEN)ws.send(JSON.stringify({Log:{level:"debug",messages:stringify(args)}}));debug.apply(console,args)}}export{monkeyPatchConsole}; diff --git a/packages/interpreter/src/ts/patch_console.spec.ts b/packages/interpreter/src/ts/patch_console.spec.ts new file mode 100644 index 0000000000..95fe5179c6 --- /dev/null +++ b/packages/interpreter/src/ts/patch_console.spec.ts @@ -0,0 +1,38 @@ +// Run with: node --experimental-strip-types --test patch_console.spec.ts +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { monkeyPatchConsole } from "./patch_console.ts"; + +function patched() { + const sent: string[] = []; + const fakeConsole: any = {}; + for (const level of ["log", "info", "warn", "error", "debug"]) { + fakeConsole[level] = () => {}; + } + (globalThis as any).WebSocket = { OPEN: 1 }; + (globalThis as any).window = { console: fakeConsole }; + monkeyPatchConsole({ readyState: 1, send: (s: string) => sent.push(s) } as any); + return { fakeConsole, sent }; +} + +test("every console level sends only string messages", () => { + const { fakeConsole, sent } = patched(); + for (const level of ["log", "info", "warn", "error", "debug"]) { + fakeConsole[level]("Wee", { some: "object" }, 42, [1, 2]); + } + assert.equal(sent.length, 5); + for (const raw of sent) { + const { Log } = JSON.parse(raw); + assert.deepEqual(Log.messages, ["Wee", '{"some":"object"}', "42", "[1,2]"]); + } +}); + +test("values JSON.stringify cannot handle fall back to strings", () => { + const { fakeConsole, sent } = patched(); + const circular: any = {}; + circular.self = circular; + fakeConsole.debug(circular, 10n, undefined, () => {}); + const { Log } = JSON.parse(sent[0]); + assert.equal(Log.messages.length, 4); + for (const m of Log.messages) assert.equal(typeof m, "string"); +}); diff --git a/packages/interpreter/src/ts/patch_console.ts b/packages/interpreter/src/ts/patch_console.ts index bcdbef9383..3a402f8821 100644 --- a/packages/interpreter/src/ts/patch_console.ts +++ b/packages/interpreter/src/ts/patch_console.ts @@ -1,3 +1,15 @@ +function stringify(args: any[]): string[] { + return args.map((a) => { + if (typeof a === "string") return a; + try { + const s = JSON.stringify(a); + return s === undefined ? String(a) : s; + } catch { + return String(a); + } + }); +} + export function monkeyPatchConsole(ws: WebSocket) { const console = window.console; @@ -10,7 +22,7 @@ export function monkeyPatchConsole(ws: WebSocket) { console.log = function (...args: any[]) { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ - "Log": { level: "log", messages: args } + "Log": { level: "log", messages: stringify(args) } })); } log.apply(console, args); @@ -19,7 +31,7 @@ export function monkeyPatchConsole(ws: WebSocket) { console.info = function (...args: any[]) { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ - "Log": { level: "info", messages: args } + "Log": { level: "info", messages: stringify(args) } })); } info.apply(console, args); @@ -28,7 +40,7 @@ export function monkeyPatchConsole(ws: WebSocket) { console.warn = function (...args: any[]) { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ - "Log": { level: "warn", messages: args } + "Log": { level: "warn", messages: stringify(args) } })); } warn.apply(console, args); @@ -37,7 +49,7 @@ export function monkeyPatchConsole(ws: WebSocket) { console.error = function (...args: any[]) { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ - "Log": { level: "error", messages: args } + "Log": { level: "error", messages: stringify(args) } })); } error.apply(console, args); @@ -46,7 +58,7 @@ export function monkeyPatchConsole(ws: WebSocket) { console.debug = function (...args: any[]) { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ - "Log": { level: "debug", messages: args } + "Log": { level: "debug", messages: stringify(args) } })); } debug.apply(console, args);