From 46086af026ba21568eeedf6c36acd2244d8aa3c3 Mon Sep 17 00:00:00 2001 From: Blomios Date: Sat, 1 Aug 2026 00:05:57 +0200 Subject: [PATCH] fix(plugins): support export default { activate }, cleanup partiel et erreurs visibles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../plugins/PluginRuntimeProvider.tsx | 20 +++++- .../src/features/plugins/PluginsPanel.tsx | 6 +- .../src/features/plugins/plugins.test.tsx | 63 +++++++++++++++++++ frontend/src/plugins/runtime/loader.test.ts | 46 ++++++++++++++ frontend/src/plugins/runtime/loader.ts | 16 +++-- 5 files changed, 144 insertions(+), 7 deletions(-) diff --git a/frontend/src/features/plugins/PluginRuntimeProvider.tsx b/frontend/src/features/plugins/PluginRuntimeProvider.tsx index 026ee17..2ff26b5 100644 --- a/frontend/src/features/plugins/PluginRuntimeProvider.tsx +++ b/frontend/src/features/plugins/PluginRuntimeProvider.tsx @@ -38,6 +38,13 @@ const EMPTY_PLUGIN_RUNTIME: PluginRuntimeContextValue = { const PluginRuntimeContext = createContext(EMPTY_PLUGIN_RUNTIME); +function describeError(e: unknown): string { + if (e && typeof e === "object" && "message" in e) { + return String((e as { message: unknown }).message); + } + return String(e); +} + interface PluginRuntimeProviderProps { children: ReactNode; /** Test/Storybook escape hatch — skips the gateway fetch and uses this value as-is. */ @@ -72,10 +79,19 @@ export function PluginRuntimeProvider({ children, value: injected }: PluginRunti if (cancelled) return; setValue({ registry: result.registry, failures: result.failures, loading: false }); }) - .catch(() => { + .catch((e: unknown) => { // No plugin gateway / catalog fetch failed: run with zero plugins // rather than blocking the app (full-trust plugins are additive). - if (!cancelled) setValue((prev) => ({ ...prev, loading: false })); + if (!cancelled) { + setValue((prev) => ({ + ...prev, + failures: [ + ...prev.failures, + { pluginId: "", reason: describeError(e) }, + ], + loading: false, + })); + } }); return () => { cancelled = true; diff --git a/frontend/src/features/plugins/PluginsPanel.tsx b/frontend/src/features/plugins/PluginsPanel.tsx index 35e3d7f..6b1c6e4 100644 --- a/frontend/src/features/plugins/PluginsPanel.tsx +++ b/frontend/src/features/plugins/PluginsPanel.tsx @@ -167,7 +167,11 @@ export function PluginsPanel() { )}
- {p.enabled ? ( + {p.lifecycleState === "invalid" ? ( + + ) : p.enabled ? (