feat(web): preload completed wasm and load debug modules on demand
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled

This commit is contained in:
mes123456
2026-09-04 11:17:27 +08:00
parent a72b984f4c
commit 2ffb965b67
6 changed files with 95 additions and 29 deletions

View File

@@ -83,6 +83,17 @@ export class WebEngineClient {
return (await this.request({ type: "init" })).status;
}
async shutdown(): Promise<void> {
if (!this.worker) return;
try {
await this.request({ type: "shutdown" });
}
finally {
this.worker?.terminate();
this.worker = null;
}
}
/** Load one debug WASM module without replacing the completed engine module. */
async loadDebugWasmModule(id: string): Promise<NonNullable<WebEngineResult["wasmModules"]>> {
const result = await this.request({ type: "loadWasmModule", id });

View File

@@ -37,20 +37,27 @@ export function createWasmModuleLoader(
const bytes = new Map<string, ArrayBuffer>();
const statuses = new Map<string, WasmModuleState>(manifest.modules.map((entry) => [entry.id, { id: entry.id, lifecycle: entry.lifecycle, status: "pending", byteLength: 0 }]));
const loading = new Map<string, Promise<ArrayBuffer>>();
const markLoaded = (entry: WasmModuleEntry, value: ArrayBuffer): ArrayBuffer => {
statuses.set(entry.id, { id: entry.id, lifecycle: entry.lifecycle, status: "loaded", byteLength: value.byteLength });
return value;
};
const load = (entry: WasmModuleEntry): Promise<ArrayBuffer> => {
const existing = bytes.get(entry.id);
if (existing) return Promise.resolve(existing);
const active = loading.get(entry.id);
if (active) return active;
const promise = fetchVerified(entry.resources[variant], fetcher).then((value) => {
bytes.set(entry.id, value);
statuses.set(entry.id, { id: entry.id, lifecycle: entry.lifecycle, status: "loaded", byteLength: value.byteLength });
return value;
const resource = entry.resources[variant];
const existing = bytes.get(resource.url);
if (existing) return Promise.resolve(markLoaded(entry, existing));
const active = loading.get(resource.url);
if (active) return active.then((value) => markLoaded(entry, value), (error: unknown) => {
statuses.set(entry.id, { id: entry.id, lifecycle: entry.lifecycle, status: "failed", byteLength: 0, error: error instanceof Error ? error.message : String(error) });
throw error;
});
const promise = fetchVerified(resource, fetcher).then((value) => {
bytes.set(resource.url, value);
return markLoaded(entry, value);
}).catch((error: unknown) => {
statuses.set(entry.id, { id: entry.id, lifecycle: entry.lifecycle, status: "failed", byteLength: 0, error: error instanceof Error ? error.message : String(error) });
throw error;
}).finally(() => loading.delete(entry.id));
loading.set(entry.id, promise);
}).finally(() => loading.delete(resource.url));
loading.set(resource.url, promise);
return promise;
};
return {