diff --git a/cypher-executor/src/lib/component-loader.ts b/cypher-executor/src/lib/component-loader.ts index a9dc02b..5725161 100644 --- a/cypher-executor/src/lib/component-loader.ts +++ b/cypher-executor/src/lib/component-loader.ts @@ -1,6 +1,10 @@ import { BUILTIN_COMPONENTS } from './constants'; +import { createWasiShim } from './wasi-shim'; import type { Bindings, ComponentRunner } from '../types'; +// Worker 記憶體快取:componentId → WebAssembly.Module(跨請求共享,避免重複編譯) +const moduleCache = new Map(); + /** * 建立零件載入器 * @@ -15,35 +19,57 @@ export function createComponentLoader(env: Bindings) { const builtin = BUILTIN_COMPONENTS.get(componentId); if (builtin) return builtin; - // 層 2:從 WASM_BUCKET R2 讀取 + // 層 2:從 WASM_BUCKET R2 讀取(快取 Module 避免重複編譯) const wasmKey = `${componentId}/${componentId}.wasm`; - const wasmObj = await env.WASM_BUCKET.get(wasmKey); - if (wasmObj) { - const wasmBuffer = await wasmObj.arrayBuffer(); - return createWasmRunner(componentId, wasmBuffer, env); + + let wasmModule = moduleCache.get(componentId); + if (!wasmModule) { + const wasmObj = await env.WASM_BUCKET.get(wasmKey); + if (!wasmObj) { + throw new Error( + `零件 ${componentId} 不存在。\n` + + `請確認 ${wasmKey} 已上傳至 WASM_BUCKET。\n` + + `修復:執行 acr parts 查看可用零件清單。` + ); + } + const buffer = await wasmObj.arrayBuffer(); + wasmModule = await WebAssembly.compile(buffer); + moduleCache.set(componentId, wasmModule); } - // 層 3:找不到 - throw new Error( - `零件 ${componentId} 不存在。\n` + - `請確認 ${wasmKey} 已上傳至 WASM_BUCKET。\n` + - `修復:執行 acr parts 查看可用零件清單。` - ); - }; -} + const compiledModule = wasmModule; + return async (ctx: unknown): Promise => { + const stdinJson = JSON.stringify(ctx); + const shim = createWasiShim(stdinJson); -/** - * 建立 WASM 零件執行器 - * 使用 WASI preview1 stdin/stdout JSON I/O 模型 - */ -function createWasmRunner( - componentId: string, - wasmBuffer: ArrayBuffer, - _env: Bindings, -): ComponentRunner { - return async (ctx: unknown): Promise => { - // 動態 import wasm-executor(避免頂層 import 造成 Worker 啟動問題) - const { executeWasm } = await import('./wasm-executor'); - return executeWasm(componentId, wasmBuffer, ctx); + const instance = await WebAssembly.instantiate(compiledModule, shim.imports); + + const memory = instance.exports.memory as WebAssembly.Memory | undefined; + if (memory) shim.setMemory(memory); + + const exports = instance.exports as Record; + const entryFn = (exports._start ?? exports.main) as (() => void) | undefined; + if (typeof entryFn !== 'function') { + throw new Error(`WASM 零件缺少 _start 或 main export(${componentId})`); + } + + try { + entryFn(); + } catch (e) { + // proc_exit(0) 拋出 "wasm exit: 0",視為正常結束 + if (!(e instanceof Error && e.message === 'wasm exit: 0')) { + throw e; + } + } + + const stdout = shim.getStdout().trim(); + if (!stdout) throw new Error(`WASM 零件沒有輸出(stdout 為空):${componentId}`); + + try { + return JSON.parse(stdout); + } catch { + throw new Error(`WASM 零件輸出不是合法 JSON:${stdout.slice(0, 200)}`); + } + }; }; }