playground: most of the logic for both frontend and backend is implemented, some bugs remain

This commit is contained in:
Yeicor
2025-07-20 21:35:45 +02:00
parent 0460e939e4
commit a63d018850
22 changed files with 617 additions and 165 deletions

View File

@@ -1,44 +1,31 @@
// Each message needs a unique id to identify the response. In a real example,
// we might use a real uuid package
let lastId = 1;
import type {loadPyodide} from "pyodide";
function getId() {
return lastId++;
/** Simple API for the Pyodide worker. */
export function newPyodideWorker(initOpts: Parameters<typeof loadPyodide>[0]) {
let worker = new Worker(new URL('./pyodide-worker.ts', import.meta.url), {type: "module"});
worker.postMessage(initOpts);
return {
asyncRun: (code: String, stdout: (msg: string) => void, stderr: (msg: string) => void) => new Promise((resolve, reject) => {
worker.addEventListener("message", function listener(event) {
if (event.data?.stdout) {
stdout(event.data.stdout);
return;
}
if (event.data?.stderr) {
stderr(event.data.stderr);
return;
}
// Result or error.
worker.removeEventListener("message", listener);
if (event.data?.error) {
reject(event.data.error);
} else {
resolve(event.data?.result);
}
});
worker.postMessage(code);
}),
terminate: () => worker.terminate()
}
}
// Add an id to msg, send it to worker, then wait for a response with the same id.
// When we get such a response, use it to resolve the promise.
function requestResponse(worker: Worker, code: String, output: (msg: string) => void) {
return new Promise((resolve) => {
const idWorker = getId();
worker.addEventListener("message", function listener(event) {
if (event.data?.stdout) {
output(event.data.stdout + "\n");
return;
}
if (event.data?.stderr) {
output(event.data.stderr + "\n");
return;
}
if (event.data?.id !== idWorker) return;
// This listener is done so remove it.
worker.removeEventListener("message", listener);
// Filter the id out of the result
const {id, ...rest} = event.data;
resolve(rest);
});
worker.postMessage({id: idWorker, code});
});
}
export function asyncRun(code: String, output: (msg: string) => void) {
return requestResponse(pyodideWorker as Worker, code, output);
}
export function resetState() {
// Reset the worker state by terminating it and creating a new one.
if (pyodideWorker) pyodideWorker.terminate();
pyodideWorker = new Worker(new URL('./pyodide-worker.ts', import.meta.url), {type: "module"});
}
export let pyodideWorker: Worker | null = null;