Remove WASM plugin support

This commit is contained in:
KingKDot
2026-08-18 21:41:28 -05:00
parent 4b8de29d32
commit 7800c24c65
42 changed files with 125 additions and 1773 deletions
+1 -6
View File
@@ -146,24 +146,19 @@ function renderPlugins(plugins) {
titleRow.appendChild(trustBadge);
const runtimeBadge = document.createElement("span");
const isWasm = plugin.runtime === "wasm";
const isV2 = Number(plugin.apiVersion || 1) >= 2;
runtimeBadge.className = `inline-flex items-center gap-1 text-[10px] uppercase tracking-wider px-2 py-0.5 rounded-full border ${
isServerOnly
? "text-fuchsia-200 border-fuchsia-700 bg-fuchsia-950/50"
: isWasm
? "text-cyan-300 border-cyan-700 bg-cyan-950/40"
: isV2
? "text-emerald-300 border-emerald-700 bg-emerald-950/40"
: "text-slate-300 border-slate-700 bg-slate-900/60"
}`;
runtimeBadge.innerHTML = isServerOnly
? '<i class="fa-solid fa-server"></i> Server Extension'
: isWasm
? '<i class="fa-solid fa-cube"></i> WASM 2.0'
: isV2
? '<i class="fa-solid fa-code"></i> Plugin 2.0'
: '<i class="fa-solid fa-puzzle-piece"></i> Native Legacy';
: '<i class="fa-solid fa-puzzle-piece"></i> Native';
titleRow.appendChild(runtimeBadge);
const subtitle = document.createElement("div");
+1 -2
View File
@@ -343,7 +343,7 @@ export type PluginManifest = {
id: string;
name: string;
apiVersion?: number;
runtime?: "native" | "wasm" | "server" | string;
runtime?: "native" | "server" | string;
nativeLoader?: "memory" | "os" | string;
autoLoadByDefault?: boolean;
nativeEntrypoints?: PluginNativeEntrypoints;
@@ -351,7 +351,6 @@ export type PluginManifest = {
description?: string;
binary?: string;
binaries?: Record<string, string>;
wasm?: string;
needs?: PluginNeeds;
entry?: string;
assets?: {
@@ -45,7 +45,7 @@ describe("loadPluginBundle", () => {
expect(bundle.size).toBe("test-binary".length);
});
test("loads wasm manifest independently of client platform", async () => {
test("rejects an extracted WASM manifest", async () => {
const root = await createTempRoot();
const pluginDir = join(root, "wasm-demo");
await mkdir(pluginDir, { recursive: true });
@@ -62,13 +62,23 @@ describe("loadPluginBundle", () => {
}),
);
const bundle = await loadPluginBundle(root, "wasm-demo", async () => {}, "Windows 11", "amd64");
await expect(loadPluginBundle(root, "wasm-demo", async () => {}, "Windows 11", "amd64"))
.rejects.toThrow("WASM plugins are not supported");
});
expect(bundle.manifest.apiVersion).toBe(2);
expect(bundle.manifest.runtime).toBe("wasm");
expect(bundle.manifest.wasm).toBe("plugin.wasm");
expect(bundle.binaryPath).toBe(join(root, "wasm-demo", "plugin.wasm"));
expect(bundle.size).toBe(4);
test("rejects a bundle containing a WASM module", async () => {
const root = await createTempRoot();
const AdmZip = require("adm-zip");
const zip = new AdmZip();
zip.addFile("config.json", Buffer.from(JSON.stringify({ name: "WASM Demo", runtime: "wasm" })));
zip.addFile("wasm-demo.html", Buffer.from("<div></div>"));
zip.addFile("wasm-demo.css", Buffer.from("body {}"));
zip.addFile("wasm-demo.js", Buffer.from("console.log('demo')"));
zip.addFile("wasm-demo.wasm", Buffer.from([0x00, 0x61, 0x73, 0x6d]));
zip.writeZip(join(root, "wasm-demo.zip"));
await expect(ensurePluginExtracted(root, "wasm-demo", (name) => name))
.rejects.toThrow("WASM plugins are not supported");
});
test("keeps helper DLLs out of the native platform binary map", async () => {
@@ -66,11 +66,15 @@ export async function ensurePluginExtracted(
}
if (!zipStat) {
if (manifestStat) return;
if (manifestStat) {
await rejectUnsupportedWasmManifest(manifestPath, safeId);
return;
}
throw new Error(`Plugin bundle not found: ${safeId}`);
}
if (manifestStat && manifestStat.mtimeMs >= zipStat.mtimeMs) {
await rejectUnsupportedWasmManifest(manifestPath, safeId);
return;
}
@@ -101,7 +105,6 @@ export async function ensurePluginExtracted(
let jsEntry: Buffer | null = null;
let serverEntry: Buffer | null = null;
let configEntry: Buffer | null = null;
let wasmEntry: { filename: string; data: Buffer } | null = null;
const sourceEntries: Map<string, Buffer> = new Map();
const nativeBinaries: Map<string, Buffer> = new Map();
@@ -113,7 +116,7 @@ export async function ensurePluginExtracted(
if (lower.endsWith(".so") || lower.endsWith(".dll") || lower.endsWith(".dylib")) {
nativeBinaries.set(base, entry.getData());
} else if (lower.endsWith(".wasm")) {
wasmEntry = { filename: base, data: entry.getData() };
throw new Error(`Invalid plugin bundle: ${safeId} (WASM plugins are not supported)`);
} else if (lower === "server.js") {
serverEntry = entry.getData();
} else if (lower.endsWith(".html")) {
@@ -139,6 +142,10 @@ export async function ensurePluginExtracted(
}
}
if (declaresWasmPlugin(extraConfig)) {
throw new Error(`Invalid plugin bundle: ${safeId} (WASM plugins are not supported)`);
}
const uiTsEntry = getConfiguredSourceEntry(extraConfig, ["uiEntry", "ui.entry", "build.ui", "build.uiEntry"], sourceEntries)
|| (sourceEntries.has("src/ui.ts") ? "src/ui.ts" : "")
|| (sourceEntries.has("src/index.ts") ? "src/index.ts" : "");
@@ -163,11 +170,6 @@ export async function ensurePluginExtracted(
binariesMap[platformKey] = filename;
}
}
if (wasmEntry) {
await fs.writeFile(path.join(pluginDir, wasmEntry.filename), wasmEntry.data);
binariesMap["wasm32-wasi"] = wasmEntry.filename;
}
if (sourceEntries.size > 0) {
await fs.rm(path.join(pluginDir, "src"), { recursive: true, force: true });
for (const [relPath, data] of sourceEntries) {
@@ -199,7 +201,7 @@ export async function ensurePluginExtracted(
try { await fs.rm(serverScriptPath, { force: true }); } catch {}
}
const runtime = normalizePluginRuntime(extraConfig.runtime, wasmEntry !== null);
const runtime = normalizePluginRuntime(extraConfig.runtime);
const nativeLoader = normalizeNativeLoader(extraConfig.nativeLoader);
const nativeEntrypoints = normalizeNativeEntrypoints(extraConfig.nativeEntrypoints);
const needs = normalizePluginNeeds(extraConfig.needs);
@@ -207,7 +209,7 @@ export async function ensurePluginExtracted(
const manifest: PluginManifest = {
id: safeId,
name: extraConfig.name || safeId,
apiVersion: Number(extraConfig.apiVersion) === 2 || runtime === "wasm" ? 2 : 1,
apiVersion: Number(extraConfig.apiVersion) === 2 ? 2 : 1,
runtime,
...(nativeLoader && { nativeLoader }),
...(extraConfig.autoLoadByDefault === true && { autoLoadByDefault: true }),
@@ -215,7 +217,6 @@ export async function ensurePluginExtracted(
version: extraConfig.version || "1.0.0",
description: extraConfig.description,
...(typeof extraConfig.binary === "string" && extraConfig.binary && { binary: extraConfig.binary }),
...(runtime === "wasm" && { wasm: String(extraConfig.wasm || wasmEntry?.filename || binariesMap["wasm32-wasi"] || "") }),
...(needs && { needs }),
binaries: binariesMap,
entry: `${safeId}.html`,
@@ -280,6 +281,10 @@ export async function listPluginManifests(
try {
const raw = await fs.readFile(manifestPath, "utf-8");
const manifest = JSON.parse(raw) as PluginManifest;
if (declaresWasmPlugin(manifest)) {
warnPlugin(`[plugin] skipped ${ent.name}: WASM plugins are not supported`);
continue;
}
const id = manifest.id || ent.name;
const name = manifest.name || ent.name;
if (pluginState.enabled[id] === undefined) {
@@ -318,14 +323,8 @@ export async function loadPluginBundle(
manifest.id = manifest.id || pluginId;
manifest.name = manifest.name || pluginId;
if (manifest.runtime === "wasm" || manifest.wasm || manifest.binaries?.["wasm32-wasi"]) {
const wasmName = manifest.wasm || manifest.binaries?.["wasm32-wasi"];
if (!wasmName) {
throw new Error(`No WASM module configured for ${pluginId}`);
}
const binaryPath = path.join(dir, wasmName);
const stat = await fs.stat(binaryPath);
return { manifest: { ...manifest, runtime: "wasm", apiVersion: 2 }, binaryPath, size: stat.size };
if (declaresWasmPlugin(manifest)) {
throw new Error(`WASM plugins are not supported: ${pluginId}`);
}
manifest.runtime = manifest.runtime || "native";
@@ -632,13 +631,30 @@ export function normalizePluginNeeds(raw: any): PluginNeeds | undefined {
return files.length > 0 ? { files } : undefined;
}
function normalizePluginRuntime(runtime: any, hasWasm: boolean): "native" | "wasm" | "server" {
function normalizePluginRuntime(runtime: any): "native" | "server" {
const value = typeof runtime === "string" ? runtime.trim().toLowerCase() : "";
if (value === "wasm" || hasWasm) return "wasm";
if (value === "server") return "server";
return "native";
}
function declaresWasmPlugin(value: any): boolean {
if (!value || typeof value !== "object") return false;
if (typeof value.runtime === "string" && value.runtime.trim().toLowerCase() === "wasm") return true;
if (typeof value.wasm === "string" && value.wasm.trim()) return true;
if (!value.binaries || typeof value.binaries !== "object" || Array.isArray(value.binaries)) return false;
return Object.entries(value.binaries).some(([key, filename]) =>
key.trim().toLowerCase().includes("wasm")
|| (typeof filename === "string" && filename.trim().toLowerCase().endsWith(".wasm"))
);
}
async function rejectUnsupportedWasmManifest(manifestPath: string, pluginId: string): Promise<void> {
const manifest = JSON.parse(await fs.readFile(manifestPath, "utf-8"));
if (declaresWasmPlugin(manifest)) {
throw new Error(`WASM plugins are not supported: ${pluginId}`);
}
}
function normalizeNativeLoader(loader: any): "memory" | "os" | undefined {
const value = typeof loader === "string" ? loader.trim().toLowerCase() : "";
if (["memory", "mem", "reflective", "manual"].includes(value)) return "memory";