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 { BUILTIN_COMPONENTS } from './constants';
|
||||||
|
import { createWasiShim } from './wasi-shim';
|
||||||
import type { Bindings, ComponentRunner } from '../types';
|
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);
|
const builtin = BUILTIN_COMPONENTS.get(componentId);
|
||||||
if (builtin) return builtin;
|
if (builtin) return builtin;
|
||||||
|
|
||||||
// 層 2:從 WASM_BUCKET R2 讀取
|
// 層 2:從 WASM_BUCKET R2 讀取(快取 Module 避免重複編譯)
|
||||||
const wasmKey = `${componentId}/${componentId}.wasm`;
|
const wasmKey = `${componentId}/${componentId}.wasm`;
|
||||||
const wasmObj = await env.WASM_BUCKET.get(wasmKey);
|
|
||||||
if (wasmObj) {
|
let wasmModule = moduleCache.get(componentId);
|
||||||
const wasmBuffer = await wasmObj.arrayBuffer();
|
if (!wasmModule) {
|
||||||
return createWasmRunner(componentId, wasmBuffer, env);
|
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:找不到
|
const compiledModule = wasmModule;
|
||||||
throw new Error(
|
return async (ctx: unknown): Promise<unknown> => {
|
||||||
`零件 ${componentId} 不存在。\n` +
|
const stdinJson = JSON.stringify(ctx);
|
||||||
`請確認 ${wasmKey} 已上傳至 WASM_BUCKET。\n` +
|
const shim = createWasiShim(stdinJson);
|
||||||
`修復:執行 acr parts 查看可用零件清單。`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const instance = await WebAssembly.instantiate(compiledModule, shim.imports);
|
||||||
* 建立 WASM 零件執行器
|
|
||||||
* 使用 WASI preview1 stdin/stdout JSON I/O 模型
|
const memory = instance.exports.memory as WebAssembly.Memory | undefined;
|
||||||
*/
|
if (memory) shim.setMemory(memory);
|
||||||
function createWasmRunner(
|
|
||||||
componentId: string,
|
const exports = instance.exports as Record<string, unknown>;
|
||||||
wasmBuffer: ArrayBuffer,
|
const entryFn = (exports._start ?? exports.main) as (() => void) | undefined;
|
||||||
_env: Bindings,
|
if (typeof entryFn !== 'function') {
|
||||||
): ComponentRunner {
|
throw new Error(`WASM 零件缺少 _start 或 main export(${componentId})`);
|
||||||
return async (ctx: unknown): Promise<unknown> => {
|
}
|
||||||
// 動態 import wasm-executor(避免頂層 import 造成 Worker 啟動問題)
|
|
||||||
const { executeWasm } = await import('./wasm-executor');
|
try {
|
||||||
return executeWasm(componentId, wasmBuffer, ctx);
|
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