// isolator/deno_server.ts import { serve } from "https://deno.land/std@0.201.0/http/server.ts"; import { crypto } from "https://deno.land/std@0.201.0/crypto/mod.ts"; import { ensureDir } from "https://deno.land/std@0.201.0/fs/ensure_dir.ts"; const TIMEOUT_MS = 5_000; const States = Object.freeze({ TIMEOUT: "TIMEOUT", }); const CACHE_DIR = "./.cache"; await ensureDir(CACHE_DIR); async function handler(req: Request): Promise { if (req.method !== "POST" || new URL(req.url).pathname !== "/execute") { return new Response("Not Found", { status: 404 }); } let worker: Worker | undefined; try { const { code = "", request = {}, environment = {}, name = "userFunc", } = await req.json(); // Create a content-addressed hash of the code. const hash = await crypto.subtle.digest( "SHA-256", new TextEncoder().encode(code) ); const hashString = Array.from(new Uint8Array(hash)) .map((b) => b.toString(16).padStart(2, "0")) .join(""); const filePath = `${CACHE_DIR}/${hashString}.mjs`; // Write the code to a file if it doesn't already exist. try { await Deno.stat(filePath); } catch (e) { if (e instanceof Deno.errors.NotFound) { await Deno.writeTextFile(filePath, code); } else { throw e; } } // Create a new worker for each request. const worker = new Worker(new URL("./worker.ts", import.meta.url).href, { type: "module", deno: { namespace: true, permissions: "inherit", }, }); const executionPromise = new Promise((resolve, reject) => { const messageHandler = (e: MessageEvent) => { resolve(e.data); cleanup(); }; const errorHandler = (e: ErrorEvent) => { reject(new Error(`Worker error: ${e.message}`)); cleanup(); }; const cleanup = () => { worker.removeEventListener("message", messageHandler); worker.removeEventListener("error", errorHandler); }; worker.addEventListener("message", messageHandler); worker.addEventListener("error", errorHandler); }); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Timeout")), TIMEOUT_MS) ); worker.postMessage({ filePath: new URL(filePath, import.meta.url).href, request, environment, name, }); const startTime = performance.now(); try { const result: any = await Promise.race([ executionPromise, timeoutPromise, ]); // Check if the result from the worker looks like a Response object. if ( result && typeof result === "object" && "body" in result && "status" in result && "headers" in result ) { return new Response(result.body, { status: result.status, headers: result.headers, }); } else { // Otherwise, fall back to the old behavior. return new Response(JSON.stringify(result), { headers: { "Content-Type": "application/json" }, }); } } catch (err) { const executionTime = performance.now() - startTime; const payload = { status: States.TIMEOUT, result: err.message, logs: [], environment: environment, execution_time: executionTime, }; return new Response(JSON.stringify(payload), { status: 500, headers: { "Content-Type": "application/json" }, }); } } catch (e) { return new Response(`Bad Request: ${e.message}`, { status: 400 }); } finally { if (worker) { worker.terminate(); // Terminate the worker after the request is handled. } } } const port = parseInt(Deno.env.get("PORT") || "8000"); console.log(`⚡ Deno server ready on :${port}, ready to spawn workers.`); await serve(handler, { port });