Skip to content

Commit 010bce0

Browse files
Copilotna-trium-144
andcommitted
Refactor message processing to use Comlink library
- Install comlink dependency - Refactor jsEval.worker.ts to expose API via Comlink - Refactor pyodide.worker.ts to expose API via Comlink - Refactor ruby.worker.ts to expose API via Comlink - Refactor runtime.tsx to use Comlink wrap() instead of manual message handling - Remove manual ID tracking and callback management - Simplify worker communication using Comlink's RPC pattern Co-authored-by: na-trium-144 <100704180+na-trium-144@users.noreply.github.com>
1 parent 832f3cd commit 010bce0

6 files changed

Lines changed: 157 additions & 332 deletions

File tree

app/terminal/worker/jsEval.worker.ts

Lines changed: 31 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/// <reference lib="webworker" />
22

3+
import { expose } from "comlink";
34
import type { ReplOutput } from "../repl";
4-
import type { MessageType, WorkerRequest, WorkerResponse } from "./runtime";
5+
import type { WorkerCapabilities } from "./runtime";
56
import inspect from "object-inspect";
67

78
function format(...args: unknown[]): string {
@@ -29,12 +30,9 @@ self.console = {
2930
},
3031
};
3132

32-
async function init({ id }: WorkerRequest["init"]) {
33+
async function init(): Promise<{ capabilities: WorkerCapabilities }> {
3334
// Initialize the worker and report capabilities
34-
self.postMessage({
35-
id,
36-
payload: { capabilities: { interrupt: "restart" } },
37-
} satisfies WorkerResponse["init"]);
35+
return { capabilities: { interrupt: "restart" } };
3836
}
3937

4038
async function replLikeEval(code: string): Promise<unknown> {
@@ -72,8 +70,10 @@ async function replLikeEval(code: string): Promise<unknown> {
7270
}
7371
}
7472

75-
async function runCode({ id, payload }: WorkerRequest["runCode"]) {
76-
const { code } = payload;
73+
async function runCode(code: string): Promise<{
74+
output: ReplOutput[];
75+
updatedFiles: Record<string, string>;
76+
}> {
7777
try {
7878
const result = await replLikeEval(code);
7979
jsOutput.push({
@@ -99,14 +99,13 @@ async function runCode({ id, payload }: WorkerRequest["runCode"]) {
9999
const output = [...jsOutput];
100100
jsOutput = []; // Clear output
101101

102-
self.postMessage({
103-
id,
104-
payload: { output, updatedFiles: [] },
105-
} satisfies WorkerResponse["runCode"]);
102+
return { output, updatedFiles: {} };
106103
}
107104

108-
function runFile({ id, payload }: WorkerRequest["runFile"]) {
109-
const { name, files } = payload;
105+
function runFile(
106+
name: string,
107+
files: Record<string, string>
108+
): { output: ReplOutput[]; updatedFiles: Record<string, string> } {
110109
// pyodide worker などと異なり、複数ファイルを読み込んでimportのようなことをするのには対応していません。
111110
try {
112111
self.eval(files[name]);
@@ -129,23 +128,17 @@ function runFile({ id, payload }: WorkerRequest["runFile"]) {
129128
const output = [...jsOutput];
130129
jsOutput = []; // Clear output
131130

132-
self.postMessage({
133-
id,
134-
payload: { output, updatedFiles: [] },
135-
} satisfies WorkerResponse["runFile"]);
131+
return { output, updatedFiles: {} };
136132
}
137133

138-
async function checkSyntax({ id, payload }: WorkerRequest["checkSyntax"]) {
139-
const { code } = payload;
140-
134+
async function checkSyntax(
135+
code: string
136+
): Promise<{ status: "complete" | "incomplete" | "invalid" }> {
141137
try {
142138
// Try to create a Function to check syntax
143139
// new Function(code); // <- not working
144140
self.eval(`() => {${code}}`);
145-
self.postMessage({
146-
id,
147-
payload: { status: "complete" },
148-
} satisfies WorkerResponse["checkSyntax"]);
141+
return { status: "complete" };
149142
} catch (e) {
150143
// Check if it's a syntax error or if more input is expected
151144
if (e instanceof SyntaxError) {
@@ -154,28 +147,18 @@ async function checkSyntax({ id, payload }: WorkerRequest["checkSyntax"]) {
154147
e.message.includes("Unexpected token '}'") ||
155148
e.message.includes("Unexpected end of input")
156149
) {
157-
self.postMessage({
158-
id,
159-
payload: { status: "incomplete" },
160-
} satisfies WorkerResponse["checkSyntax"]);
150+
return { status: "incomplete" };
161151
} else {
162-
self.postMessage({
163-
id,
164-
payload: { status: "invalid" },
165-
} satisfies WorkerResponse["checkSyntax"]);
152+
return { status: "invalid" };
166153
}
167154
} else {
168-
self.postMessage({
169-
id,
170-
payload: { status: "invalid" },
171-
} satisfies WorkerResponse["checkSyntax"]);
155+
return { status: "invalid" };
172156
}
173157
}
174158
}
175159

176-
async function restoreState({ id, payload }: WorkerRequest["restoreState"]) {
160+
async function restoreState(commands: string[]): Promise<object> {
177161
// Re-execute all previously successful commands to restore state
178-
const { commands } = payload;
179162
jsOutput = []; // Clear output for restoration
180163

181164
for (const command of commands) {
@@ -188,32 +171,15 @@ async function restoreState({ id, payload }: WorkerRequest["restoreState"]) {
188171
}
189172

190173
jsOutput = []; // Clear any output from restoration
191-
self.postMessage({
192-
id,
193-
payload: {},
194-
} satisfies WorkerResponse["restoreState"]);
174+
return {};
195175
}
196176

197-
self.onmessage = async (event: MessageEvent<WorkerRequest[MessageType]>) => {
198-
switch (event.data.type) {
199-
case "init":
200-
await init(event.data);
201-
return;
202-
case "runCode":
203-
await runCode(event.data);
204-
return;
205-
case "runFile":
206-
runFile(event.data);
207-
return;
208-
case "checkSyntax":
209-
await checkSyntax(event.data);
210-
return;
211-
case "restoreState":
212-
await restoreState(event.data);
213-
return;
214-
default:
215-
event.data satisfies never;
216-
originalConsole.error(`Unknown message: ${event.data}`);
217-
return;
218-
}
177+
const api = {
178+
init,
179+
runCode,
180+
runFile,
181+
checkSyntax,
182+
restoreState,
219183
};
184+
185+
expose(api);

app/terminal/worker/pyodide.worker.ts

Lines changed: 37 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/// <reference lib="webworker" />
22
/// <reference lib="ES2023" />
33

4+
import { expose } from "comlink";
45
import type { PyodideInterface } from "pyodide";
56
// import { loadPyodide } from "pyodide"; -> Reading from "node:child_process" is not handled by plugins
67
import { version as pyodideVersion } from "pyodide/package.json";
78
import type { PyCallable } from "pyodide/ffi";
8-
import type { MessageType, WorkerRequest, WorkerResponse } from "./runtime";
9+
import type { WorkerCapabilities } from "./runtime";
910
import type { ReplOutput } from "../repl";
1011

1112
import execfile_py from "./pyodide/execfile.py?raw";
@@ -36,8 +37,9 @@ function readAllFiles(): Record<string, string> {
3637
return updatedFiles;
3738
}
3839

39-
async function init({ id, payload }: WorkerRequest["init"]) {
40-
const { interruptBuffer } = payload;
40+
async function init(
41+
interruptBuffer: Uint8Array
42+
): Promise<{ capabilities: WorkerCapabilities }> {
4143
if (!pyodide) {
4244
self.importScripts(`${PYODIDE_CDN}pyodide.js`);
4345

@@ -57,20 +59,15 @@ async function init({ id, payload }: WorkerRequest["init"]) {
5759

5860
pyodide.setInterruptBuffer(interruptBuffer);
5961
}
60-
self.postMessage({
61-
id,
62-
payload: { capabilities: { interrupt: "buffer" } },
63-
} satisfies WorkerResponse["init"]);
62+
return { capabilities: { interrupt: "buffer" } };
6463
}
6564

66-
async function runCode({ id, payload }: WorkerRequest["runCode"]) {
67-
const { code } = payload;
65+
async function runCode(code: string): Promise<{
66+
output: ReplOutput[];
67+
updatedFiles: Record<string, string>;
68+
}> {
6869
if (!pyodide) {
69-
self.postMessage({
70-
id,
71-
error: "Pyodide not initialized",
72-
} satisfies WorkerResponse["runCode"]);
73-
return;
70+
throw new Error("Pyodide not initialized");
7471
}
7572
try {
7673
const result = await pyodide.runPythonAsync(code);
@@ -115,20 +112,15 @@ async function runCode({ id, payload }: WorkerRequest["runCode"]) {
115112
const output = [...pyodideOutput];
116113
pyodideOutput = []; // 出力をクリア
117114

118-
self.postMessage({
119-
id,
120-
payload: { output, updatedFiles },
121-
} satisfies WorkerResponse["runCode"]);
115+
return { output, updatedFiles };
122116
}
123117

124-
async function runFile({ id, payload }: WorkerRequest["runFile"]) {
125-
const { name, files } = payload;
118+
async function runFile(
119+
name: string,
120+
files: Record<string, string>
121+
): Promise<{ output: ReplOutput[]; updatedFiles: Record<string, string> }> {
126122
if (!pyodide) {
127-
self.postMessage({
128-
id,
129-
error: "Pyodide not initialized",
130-
} satisfies WorkerResponse["runFile"]);
131-
return;
123+
throw new Error("Pyodide not initialized");
132124
}
133125
try {
134126
// Use Pyodide FS API to write files to the file system
@@ -175,70 +167,41 @@ async function runFile({ id, payload }: WorkerRequest["runFile"]) {
175167
const updatedFiles = readAllFiles();
176168
const output = [...pyodideOutput];
177169
pyodideOutput = []; // 出力をクリア
178-
self.postMessage({
179-
id,
180-
payload: { output, updatedFiles },
181-
} satisfies WorkerResponse["runFile"]);
170+
return { output, updatedFiles };
182171
}
183172

184-
async function checkSyntax({ id, payload }: WorkerRequest["checkSyntax"]) {
185-
const { code } = payload;
173+
async function checkSyntax(
174+
code: string
175+
): Promise<{ status: "complete" | "incomplete" | "invalid" }> {
186176
if (!pyodide) {
187-
self.postMessage({
188-
id,
189-
payload: { status: "invalid" },
190-
} satisfies WorkerResponse["checkSyntax"]);
191-
return;
177+
return { status: "invalid" };
192178
}
193179

194180
// 複数行コマンドは最後に空行を入れないと完了しないものとする
195181
if (code.includes("\n") && code.split("\n").at(-1) !== "") {
196-
self.postMessage({
197-
id,
198-
payload: { status: "incomplete" },
199-
} satisfies WorkerResponse["checkSyntax"]);
200-
return;
182+
return { status: "incomplete" };
201183
}
202184

203185
try {
204186
// Pythonのコードを実行して結果を受け取る
205187
const status = (pyodide.runPython(check_syntax_py) as PyCallable)(code);
206-
self.postMessage({
207-
id,
208-
payload: { status },
209-
} satisfies WorkerResponse["checkSyntax"]);
188+
return { status };
210189
} catch (e) {
211190
console.error("Syntax check error:", e);
212-
self.postMessage({
213-
id,
214-
payload: { status: "invalid" },
215-
} satisfies WorkerResponse["checkSyntax"]);
191+
return { status: "invalid" };
216192
}
217193
}
218194

219-
self.onmessage = async (event: MessageEvent<WorkerRequest[MessageType]>) => {
220-
switch (event.data.type) {
221-
case "init":
222-
await init(event.data);
223-
return;
224-
case "runCode":
225-
await runCode(event.data);
226-
return;
227-
case "runFile":
228-
await runFile(event.data);
229-
return;
230-
case "checkSyntax":
231-
await checkSyntax(event.data);
232-
return;
233-
case "restoreState":
234-
self.postMessage({
235-
id: event.data.id,
236-
error: "not implemented",
237-
} satisfies WorkerResponse["restoreState"]);
238-
return;
239-
default:
240-
event.data satisfies never;
241-
console.error(`Unknown message: ${event.data}`);
242-
return;
243-
}
195+
async function restoreState(): Promise<object> {
196+
throw new Error("not implemented");
197+
}
198+
199+
const api = {
200+
init,
201+
runCode,
202+
runFile,
203+
checkSyntax,
204+
restoreState,
244205
};
206+
207+
expose(api);

0 commit comments

Comments
 (0)