feat(FN-4128): fix bundled plugin entry resolution and surface load errors
Bundled plugin entry resolution is fixed to use proper import.meta.url path normalization, with plugin load errors now surfaced in the PluginManager UI and comprehensive regression tests added for the bundled plugin path migration and state handling. Fusion-Task-Id: FN-4128
This commit is contained in:
@@ -425,6 +425,31 @@ describe("PluginLoader", () => {
|
||||
expect(updated.state).toBe("started");
|
||||
});
|
||||
|
||||
it("recovers a previously errored plugin to started and clears the stored error", async () => {
|
||||
await pluginStore.init();
|
||||
|
||||
const plugin = makePlugin(makeManifest({ id: "recover-error-test" }));
|
||||
const pluginDir = join(rootDir, "plugins");
|
||||
const pluginPath = await writePluginModule(pluginDir, "recover-error.js", plugin);
|
||||
|
||||
await pluginStore.registerPlugin({
|
||||
manifest: plugin.manifest,
|
||||
path: pluginPath,
|
||||
});
|
||||
await pluginStore.updatePluginState("recover-error-test", "error", "previous load failed");
|
||||
|
||||
const loader = new PluginLoader({
|
||||
pluginStore,
|
||||
taskStore: mockTaskStore,
|
||||
});
|
||||
|
||||
await loader.loadPlugin("recover-error-test");
|
||||
|
||||
const updated = await pluginStore.getPlugin("recover-error-test");
|
||||
expect(updated.state).toBe("started");
|
||||
expect(updated.error ?? null).toBeNull();
|
||||
});
|
||||
|
||||
it("skips disabled plugins", async () => {
|
||||
await pluginStore.init();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
import { basename, dirname, extname, isAbsolute, resolve } from "node:path";
|
||||
import { stat } from "node:fs/promises";
|
||||
import { copyFile } from "node:fs/promises";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { EventEmitter } from "node:events";
|
||||
@@ -313,6 +314,18 @@ export class PluginLoader extends EventEmitter<{
|
||||
return this.loadedModules.get(path)!;
|
||||
}
|
||||
|
||||
let pathStats;
|
||||
try {
|
||||
pathStats = await stat(path);
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(`Plugin entry does not exist: ${path} (${errorMessage})`);
|
||||
}
|
||||
|
||||
if (pathStats.isDirectory()) {
|
||||
throw new Error(`Plugin entry must be a file, got directory: ${path}`);
|
||||
}
|
||||
|
||||
// Dynamic import - normalize to file URL so query params are honored
|
||||
// consistently across Node + Vitest environments.
|
||||
const moduleUrl = pathToFileURL(path).href;
|
||||
|
||||
Reference in New Issue
Block a user