dll plugin loading heavily improved

This commit is contained in:
KingKDot
2026-06-28 23:46:56 -05:00
parent d3ded9f877
commit 2c42a989cf
13 changed files with 471 additions and 63 deletions
+10
View File
@@ -357,6 +357,8 @@ export type PluginManifest = {
name: string;
apiVersion?: number;
runtime?: "native" | "wasm" | "server" | string;
nativeLoader?: "memory" | "os" | string;
nativeEntrypoints?: PluginNativeEntrypoints;
version?: string;
description?: string;
binary?: string;
@@ -378,6 +380,14 @@ export type PluginManifest = {
hasServer?: boolean;
};
export type PluginNativeEntrypoints = {
onLoad?: string;
onEvent?: string;
onUnload?: string;
setCallback?: string;
getRuntime?: string;
};
export type PluginDashboardIntegration = {
clientBadges?: PluginDashboardBadgeDefinition[];
};
@@ -96,6 +96,40 @@ describe("loadPluginBundle", () => {
expect(bundle.binaryPath).toBe(join(root, "plink", "plink-windows-amd64.dll"));
});
test("preserves native loader and custom entrypoint metadata from config", async () => {
const root = await createTempRoot();
const AdmZip = require("adm-zip");
const zip = new AdmZip();
zip.addFile("config.json", Buffer.from(JSON.stringify({
name: "Native Loader Demo",
apiVersion: 2,
runtime: "native",
nativeLoader: "loadlibraryex",
nativeEntrypoints: {
onLoad: "StartPlugin",
onEvent: "HandlePluginEvent",
onUnload: "StopPlugin",
setCallback: "SetHostCallback",
getRuntime: "RuntimeName",
},
})));
zip.addFile("native-loader-demo.html", Buffer.from("<div id=\"native-loader-demo\"></div>"));
zip.addFile("native-loader-demo.css", Buffer.from("#native-loader-demo { display: block; }"));
zip.addFile("native-loader-demo.js", Buffer.from("document.body.dataset.nativeLoaderDemo = '1';"));
zip.addFile("native-loader-demo-windows-amd64.dll", Buffer.from("native-dll"));
zip.writeZip(join(root, "native-loader-demo.zip"));
await ensurePluginExtracted(root, "native-loader-demo", (name) => name);
const manifest = JSON.parse(await readFile(join(root, "native-loader-demo", "manifest.json"), "utf-8"));
expect(manifest.nativeLoader).toBe("os");
expect(manifest.nativeEntrypoints.onLoad).toBe("StartPlugin");
expect(manifest.nativeEntrypoints.onEvent).toBe("HandlePluginEvent");
expect(manifest.nativeEntrypoints.onUnload).toBe("StopPlugin");
expect(manifest.nativeEntrypoints.setCallback).toBe("SetHostCallback");
expect(manifest.nativeEntrypoints.getRuntime).toBe("RuntimeName");
});
test("preserves dashboard badge metadata from config", async () => {
const root = await createTempRoot();
const AdmZip = require("adm-zip");
@@ -200,6 +200,8 @@ export async function ensurePluginExtracted(
}
const runtime = normalizePluginRuntime(extraConfig.runtime, wasmEntry !== null);
const nativeLoader = normalizeNativeLoader(extraConfig.nativeLoader);
const nativeEntrypoints = normalizeNativeEntrypoints(extraConfig.nativeEntrypoints);
const needs = normalizePluginNeeds(extraConfig.needs);
const build = normalizePluginBuildIntegration(extraConfig.build);
const manifest: PluginManifest = {
@@ -207,6 +209,8 @@ export async function ensurePluginExtracted(
name: extraConfig.name || safeId,
apiVersion: Number(extraConfig.apiVersion) === 2 || runtime === "wasm" ? 2 : 1,
runtime,
...(nativeLoader && { nativeLoader }),
...(nativeEntrypoints && { nativeEntrypoints }),
version: extraConfig.version || "1.0.0",
description: extraConfig.description,
...(typeof extraConfig.binary === "string" && extraConfig.binary && { binary: extraConfig.binary }),
@@ -634,6 +638,26 @@ function normalizePluginRuntime(runtime: any, hasWasm: boolean): "native" | "was
return "native";
}
function normalizeNativeLoader(loader: any): "memory" | "os" | undefined {
const value = typeof loader === "string" ? loader.trim().toLowerCase() : "";
if (["memory", "mem", "reflective", "manual"].includes(value)) return "memory";
if (["os", "disk", "file", "loadlibrary", "loadlibraryex"].includes(value)) return "os";
return undefined;
}
function normalizeNativeEntrypoints(raw: any): any | undefined {
if (!raw || typeof raw !== "object") return undefined;
const out: any = {};
for (const key of ["onLoad", "onEvent", "onUnload", "setCallback", "getRuntime"]) {
const value = raw[key];
if (typeof value !== "string") continue;
const trimmed = value.trim();
if (!/^[A-Za-z_][A-Za-z0-9_@$?.-]{0,127}$/.test(trimmed)) continue;
out[key] = trimmed;
}
return Object.keys(out).length > 0 ? out : undefined;
}
function normalizePluginBuildIntegration(raw: any): any | undefined {
if (!raw || typeof raw !== "object") return undefined;
const build: any = {};