fix: component-loader was calling wasm-executor with wrong signature
Rewrote createComponentLoader to directly use createWasiShim inline instead of calling executeWasm(componentId, buffer, ctx) which doesn't match wasm-executor's actual signature of executeWasm(input, options). Also adds Module caching to avoid recompiling WASM on every request. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, WebAssembly.Module>();
|
||||
|
||||
/**
|
||||
* 建立零件載入器
|
||||
*
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// 層 3:找不到
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 建立 WASM 零件執行器
|
||||
* 使用 WASI preview1 stdin/stdout JSON I/O 模型
|
||||
*/
|
||||
function createWasmRunner(
|
||||
componentId: string,
|
||||
wasmBuffer: ArrayBuffer,
|
||||
_env: Bindings,
|
||||
): ComponentRunner {
|
||||
const compiledModule = wasmModule;
|
||||
return async (ctx: unknown): Promise<unknown> => {
|
||||
// 動態 import wasm-executor(避免頂層 import 造成 Worker 啟動問題)
|
||||
const { executeWasm } = await import('./wasm-executor');
|
||||
return executeWasm(componentId, wasmBuffer, ctx);
|
||||
const stdinJson = JSON.stringify(ctx);
|
||||
const shim = createWasiShim(stdinJson);
|
||||
|
||||
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<string, unknown>;
|
||||
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)}`);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user