fix(plugins): support export default { activate }, cleanup partiel et erreurs visibles
Le loader accepte désormais aussi la forme export default { activate }
en plus de l'export nommé, les subscriptions partiellement établies
sont nettoyées en cas d'échec d'activation, et une erreur de
runtime-catalog est remontée visuellement dans PluginsPanel avec un
badge Invalide/Isolé sur les plugins concernés (#116/#120).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -52,6 +52,26 @@ describe("loadPlugins", () => {
|
||||
expect((globalThis as Record<string, unknown>).__activatedWith).toBe("dev.acme.one");
|
||||
});
|
||||
|
||||
it("loads an SDK-style default export containing activate(ctx)", async () => {
|
||||
const bundle = dataUrl(`
|
||||
export default {
|
||||
activate(ctx) {
|
||||
globalThis.__defaultExportActivatedWith = ctx.pluginId;
|
||||
},
|
||||
};
|
||||
`);
|
||||
const { registry, failures } = await loadPlugins(
|
||||
[entry({ id: "com.example.hello-plugin", displayName: "Hello Plugin", bundleUrl: bundle })],
|
||||
gateways,
|
||||
);
|
||||
|
||||
expect(failures).toEqual([]);
|
||||
expect(registry.list().map((p) => p.pluginId)).toEqual(["com.example.hello-plugin"]);
|
||||
expect((globalThis as Record<string, unknown>).__defaultExportActivatedWith).toBe(
|
||||
"com.example.hello-plugin",
|
||||
);
|
||||
});
|
||||
|
||||
it("refuses to register a command not declared in the manifest", async () => {
|
||||
const bundle = dataUrl(`
|
||||
export function activate(ctx) {
|
||||
@ -259,6 +279,32 @@ describe("loadPlugins", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("disposes partial subscriptions when activation fails", async () => {
|
||||
const bundle = dataUrl(`
|
||||
export function activate(ctx) {
|
||||
ctx.subscriptions.push({
|
||||
dispose() {
|
||||
globalThis.__partialActivationDisposed = ctx.pluginId;
|
||||
},
|
||||
});
|
||||
throw new Error("activation failed");
|
||||
}
|
||||
`);
|
||||
|
||||
const { registry, failures } = await loadPlugins(
|
||||
[entry({ id: "dev.acme.partial", displayName: "Partial", bundleUrl: bundle })],
|
||||
gateways,
|
||||
);
|
||||
|
||||
expect(registry.list()).toEqual([]);
|
||||
expect(failures).toEqual([
|
||||
{ pluginId: "dev.acme.partial", reason: "activation failed" },
|
||||
]);
|
||||
expect((globalThis as Record<string, unknown>).__partialActivationDisposed).toBe(
|
||||
"dev.acme.partial",
|
||||
);
|
||||
});
|
||||
|
||||
it("only loads what the catalog contains — a disabled/absent plugin is simply never in it", async () => {
|
||||
// The backend contract (carnet §1.3) filters the catalog to
|
||||
// `enabled && !pendingUninstall` before the loader ever sees it; the
|
||||
|
||||
Reference in New Issue
Block a user