From 48f45d2b1b1972929efb91c6aa340d22de174f24 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:05:57 +0000 Subject: [PATCH 1/5] Initial plan From dd6e71660bd8614ff7624fc1707485a718e1217e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:11:30 +0000 Subject: [PATCH 2/5] Migrate Wandbox API from /api/compile.json to /api/compile.ndjson Co-authored-by: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> --- app/terminal/wandbox/api.ts | 67 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/app/terminal/wandbox/api.ts b/app/terminal/wandbox/api.ts index 2872d12..074dd8b 100644 --- a/app/terminal/wandbox/api.ts +++ b/app/terminal/wandbox/api.ts @@ -62,6 +62,11 @@ export interface CompileParameter { is_private?: boolean; "compiler-info"?: CompilerInfo; } +export interface CompileNdjsonResult { + type: string; + data: string; +} + export interface CompileResult { status: string; signal: string; @@ -102,8 +107,9 @@ export interface CompileResultWithOutput extends CompileResult { export async function compileAndRun( options: CompileProps ): Promise { - const result: CompileResult = await fetch( - new URL("/api/compile.json", WANDBOX), + // Call the ndjson API instead of json API + const response = await fetch( + new URL("/api/compile.ndjson", WANDBOX), { method: "post", headers: { @@ -121,7 +127,62 @@ export async function compileAndRun( is_private: true, } satisfies CompileParameter), } - ).then((res) => res.json()); + ); + + // Read the ndjson response line by line + const text = await response.text(); + const lines = text.trim().split("\n"); + const ndjsonResults: CompileNdjsonResult[] = lines + .filter((line) => line.trim().length > 0) + .map((line) => JSON.parse(line) as CompileNdjsonResult); + + // Merge ndjson results into a CompileResult (same logic as Rust merge_compile_result) + const result: CompileResult = { + status: "", + signal: "", + compiler_output: "", + compiler_error: "", + compiler_message: "", + program_output: "", + program_error: "", + program_message: "", + permlink: "", + url: "", + }; + + for (const r of ndjsonResults) { + switch (r.type) { + case "Control": + // Ignore control messages + break; + case "CompilerMessageS": + result.compiler_output += r.data; + result.compiler_message += r.data; + break; + case "CompilerMessageE": + result.compiler_error += r.data; + result.compiler_message += r.data; + break; + case "StdOut": + result.program_output += r.data; + result.program_message += r.data; + break; + case "StdErr": + result.program_error += r.data; + result.program_message += r.data; + break; + case "ExitCode": + result.status += r.data; + break; + case "Signal": + result.signal += r.data; + break; + default: + // Ignore unknown types + break; + } + } + return { ...result, compilerOutput: result.compiler_output.trim() From 200d713f99ffdb7bdf2390ed3da7a3d82d17a04c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:13:16 +0000 Subject: [PATCH 3/5] Add documentation comment to CompileNdjsonResult interface Co-authored-by: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> --- app/terminal/wandbox/api.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/terminal/wandbox/api.ts b/app/terminal/wandbox/api.ts index 074dd8b..8ea3e2b 100644 --- a/app/terminal/wandbox/api.ts +++ b/app/terminal/wandbox/api.ts @@ -62,6 +62,9 @@ export interface CompileParameter { is_private?: boolean; "compiler-info"?: CompilerInfo; } +/** + * Represents a single line in the ndjson response from /api/compile.ndjson + */ export interface CompileNdjsonResult { type: string; data: string; From 1fcdfc54f24f408274fcd9a9397559bde1c64424 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:15:51 +0000 Subject: [PATCH 4/5] Add HTTP error handling to compileAndRun function Co-authored-by: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> --- app/terminal/wandbox/api.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/terminal/wandbox/api.ts b/app/terminal/wandbox/api.ts index 8ea3e2b..1df31ba 100644 --- a/app/terminal/wandbox/api.ts +++ b/app/terminal/wandbox/api.ts @@ -132,6 +132,10 @@ export async function compileAndRun( } ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + // Read the ndjson response line by line const text = await response.text(); const lines = text.trim().split("\n"); From b3f566edba4429e68b7dbff9545d9a0ae6df83ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 18 Nov 2025 11:07:40 +0000 Subject: [PATCH 5/5] Refactor to use streaming API and unified output array - Use response.body stream instead of response.text() for better flexibility - Change CompileResultWithOutput to use single output array instead of separate arrays - Store outputs in the order they are received from the ndjson stream - Update cpp.ts to work with the new unified output structure Co-authored-by: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> --- app/terminal/wandbox/api.ts | 98 ++++++++++++++++++++++++------------- app/terminal/wandbox/cpp.ts | 25 +++++----- 2 files changed, 77 insertions(+), 46 deletions(-) diff --git a/app/terminal/wandbox/api.ts b/app/terminal/wandbox/api.ts index 1df31ba..4de1f12 100644 --- a/app/terminal/wandbox/api.ts +++ b/app/terminal/wandbox/api.ts @@ -101,10 +101,7 @@ interface CompileProps { codes: Code[]; } export interface CompileResultWithOutput extends CompileResult { - compilerOutput: ReplOutput[]; - compilerError: ReplOutput[]; - programOutput: ReplOutput[]; - programError: ReplOutput[]; + output: ReplOutput[]; } export async function compileAndRun( @@ -136,12 +133,41 @@ export async function compileAndRun( throw new Error(`HTTP error! status: ${response.status}`); } - // Read the ndjson response line by line - const text = await response.text(); - const lines = text.trim().split("\n"); - const ndjsonResults: CompileNdjsonResult[] = lines - .filter((line) => line.trim().length > 0) - .map((line) => JSON.parse(line) as CompileNdjsonResult); + // Read the ndjson response as a stream + const reader = response.body?.getReader(); + if (!reader) { + throw new Error("Response body is not readable"); + } + + const decoder = new TextDecoder(); + let buffer = ""; + const ndjsonResults: CompileNdjsonResult[] = []; + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split("\n"); + + // Keep the last incomplete line in the buffer + buffer = lines.pop() || ""; + + for (const line of lines) { + if (line.trim().length > 0) { + ndjsonResults.push(JSON.parse(line) as CompileNdjsonResult); + } + } + } + + // Process any remaining data in the buffer + if (buffer.trim().length > 0) { + ndjsonResults.push(JSON.parse(buffer) as CompileNdjsonResult); + } + } finally { + reader.releaseLock(); + } // Merge ndjson results into a CompileResult (same logic as Rust merge_compile_result) const result: CompileResult = { @@ -157,6 +183,9 @@ export async function compileAndRun( url: "", }; + // Build output array in the order messages are received + const output: ReplOutput[] = []; + for (const r of ndjsonResults) { switch (r.type) { case "Control": @@ -165,18 +194,42 @@ export async function compileAndRun( case "CompilerMessageS": result.compiler_output += r.data; result.compiler_message += r.data; + // Add to output in order + if (r.data.trim()) { + for (const line of r.data.trim().split("\n")) { + output.push({ type: "stdout", message: line }); + } + } break; case "CompilerMessageE": result.compiler_error += r.data; result.compiler_message += r.data; + // Add to output in order + if (r.data.trim()) { + for (const line of r.data.trim().split("\n")) { + output.push({ type: "error", message: line }); + } + } break; case "StdOut": result.program_output += r.data; result.program_message += r.data; + // Add to output in order + if (r.data.trim()) { + for (const line of r.data.trim().split("\n")) { + output.push({ type: "stdout", message: line }); + } + } break; case "StdErr": result.program_error += r.data; result.program_message += r.data; + // Add to output in order + if (r.data.trim()) { + for (const line of r.data.trim().split("\n")) { + output.push({ type: "stderr", message: line }); + } + } break; case "ExitCode": result.status += r.data; @@ -192,29 +245,6 @@ export async function compileAndRun( return { ...result, - compilerOutput: result.compiler_output.trim() - ? result.compiler_output - .trim() - .split("\n") - .map((line) => ({ type: "stdout" as const, message: line })) - : [], - compilerError: result.compiler_error.trim() - ? result.compiler_error - .trim() - .split("\n") - .map((line) => ({ type: "error" as const, message: line })) - : [], - programOutput: result.program_output.trim() - ? result.program_output - .trim() - .split("\n") - .map((line) => ({ type: "stdout" as const, message: line })) - : [], - programError: result.program_error.trim() - ? result.program_error - .trim() - .split("\n") - .map((line) => ({ type: "error" as const, message: line })) - : [], + output, }; } diff --git a/app/terminal/wandbox/cpp.ts b/app/terminal/wandbox/cpp.ts index da6f188..ed0d5e2 100644 --- a/app/terminal/wandbox/cpp.ts +++ b/app/terminal/wandbox/cpp.ts @@ -101,33 +101,34 @@ export async function cppRunFiles( ], }); - const traceIndex = result.programError.findIndex( - (line) => line.message === "Stack trace:" + // Find stack trace in the output + const traceIndex = result.output.findIndex( + (line) => line.type === "stderr" && line.message === "Stack trace:" ); - const outputs: ReplOutput[] = [ - ...result.compilerOutput, - ...result.compilerError, - ...result.programOutput, - ...(traceIndex >= 0 - ? result.programError.slice(0, traceIndex) - : result.programError), - ]; + + let outputs: ReplOutput[]; + if (traceIndex >= 0) { // CPP_STACKTRACE_HANDLER のコードで出力されるスタックトレースを、js側でパースしていい感じに表示する + outputs = result.output.slice(0, traceIndex); outputs.push({ type: "trace" as const, message: "Stack trace (filtered):", }); - for (const line of result.programError.slice(traceIndex + 1)) { + + for (const line of result.output.slice(traceIndex + 1)) { // ユーザーのソースコードだけを対象にする - if (line.message.includes("/home/wandbox")) { + if (line.type === "stderr" && line.message.includes("/home/wandbox")) { outputs.push({ type: "trace" as const, message: line.message.replace("/home/wandbox/", ""), }); } } + } else { + outputs = [...result.output]; } + if (result.status !== "0") { outputs.push({ type: "system" as const,