Skip to content

Commit c5571f9

Browse files
committed
fix: use separate TextDecoders per stream in background process
A single TextDecoder with stream:true buffers partial multibyte sequences. When stdout and stderr interleave, shared state corrupts output by combining bytes from different streams. Use independent decoders and flush on exit.
1 parent 51dfcfd commit c5571f9

File tree

1 file changed

+9
-3
lines changed

1 file changed

+9
-3
lines changed

src/node/runtime/LocalRuntime.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,15 @@ export class LocalRuntime implements Runtime {
245245
let handle: LocalBackgroundHandle;
246246

247247
// Set up line-buffered output streaming
248-
const decoder = new TextDecoder();
248+
// Use separate decoders per stream - TextDecoder with stream:true buffers
249+
// partial multibyte sequences, so sharing would corrupt output when chunks interleave
250+
const stdoutDecoder = new TextDecoder();
251+
const stderrDecoder = new TextDecoder();
249252
let stdoutBuffer = "";
250253
let stderrBuffer = "";
251254

252255
childProcess.stdout.on("data", (chunk: Buffer) => {
253-
stdoutBuffer += decoder.decode(chunk, { stream: true });
256+
stdoutBuffer += stdoutDecoder.decode(chunk, { stream: true });
254257
const lines = stdoutBuffer.split("\n");
255258
stdoutBuffer = lines.pop() ?? "";
256259
for (const line of lines) {
@@ -259,7 +262,7 @@ export class LocalRuntime implements Runtime {
259262
});
260263

261264
childProcess.stderr.on("data", (chunk: Buffer) => {
262-
stderrBuffer += decoder.decode(chunk, { stream: true });
265+
stderrBuffer += stderrDecoder.decode(chunk, { stream: true });
263266
const lines = stderrBuffer.split("\n");
264267
stderrBuffer = lines.pop() ?? "";
265268
for (const line of lines) {
@@ -268,6 +271,9 @@ export class LocalRuntime implements Runtime {
268271
});
269272

270273
childProcess.on("exit", (code, signal) => {
274+
// Flush decoder buffers (emits any incomplete multibyte sequences)
275+
stdoutBuffer += stdoutDecoder.decode();
276+
stderrBuffer += stderrDecoder.decode();
271277
// Flush remaining partial lines
272278
if (stdoutBuffer) handle._emitStdout(stdoutBuffer);
273279
if (stderrBuffer) handle._emitStderr(stderrBuffer);

0 commit comments

Comments
 (0)