/** * arcrun `code` 零件 —— Worker host(可部署) * * POST / → { code, input?, limits? } * → QuickJS-wasm 沙箱(./sandbox.mjs 的 runCode) * → { success:true, data } | { success:false, error, error_type } * * 封裝=A(quickjs-emscripten wasmfile variant)。關鍵:CF Workers 禁止 runtime 從 bytes * 編譯 wasm(WebAssembly.instantiate(bytes) 被 embedder 擋),故不能用 singlefile(base64) 內嵌。 * 改為 `import wasm from '.../wasm'` 讓 wrangler 在 build 時把 .wasm 綁成一個「已編好的 * WebAssembly.Module」,再以 newVariant({ wasmModule }) 注入沙箱 → 執行期只 instantiate 既有 * Module、不編譯,合 Workers 規則。無需 nodejs_compat(sandbox 用 TextEncoder 計 bytes)。 * * 與其他 logic 零件不同:`code` 是自足 Worker(自帶 index.ts + sandbox.mjs + quickjs variant), * 不走 component-worker-template 的 TinyGo-wasm bundling 流程。部署見 DEPLOY.md。 */ import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { newVariant } from 'quickjs-emscripten-core'; import baseVariant from '@jitl/quickjs-wasmfile-release-sync'; // wrangler 把相對路徑 .wasm import 綁成 WebAssembly.Module(build 時編好,執行期不重編)。 // wasm 從 quickjs-wasmfile-release-sync vendored 進 vendor/(見 DEPLOY.md「vendor 步驟」)。 import wasmModule from './vendor/quickjs.wasm'; // @ts-expect-error —— sandbox.mjs 為 runtime-agnostic JS 核心(Node 測試與 Worker 共用同一份) import { runCode, setVariant } from './sandbox.mjs'; // 注入預編 Module(模組載入時一次)。之後 runCode 只 instantiate、不編譯。 setVariant(newVariant(baseVariant, { wasmModule: wasmModule as WebAssembly.Module })); const app = new Hono(); app.use('*', cors()); app.get('/', (c) => c.json({ ok: true, component: 'code' })); app.post('/', async (c) => { let body: { code?: unknown; input?: unknown; limits?: Record }; try { body = await c.req.json(); } catch { return c.json({ success: false, error: 'request body must be JSON', error_type: 'ContractError' }, 400); } if (typeof body.code !== 'string') { return c.json({ success: false, error: 'code (string) is required', error_type: 'ContractError' }, 400); } try { const result = await runCode(body.code, body.input, { limits: body.limits }); // sandbox 永遠回結構化 envelope;success=false 仍以 200 帶 error_type 回(零件語義層錯,非 HTTP 錯) return c.json(result); } catch (e) { // 理論上 runCode 自己 try/catch;這層是最後保險,Worker 絕不掛。 return c.json( { success: false, error: e instanceof Error ? e.message : String(e), error_type: 'SandboxError' }, 500, ); } }); export default app;