Files

45 lines
1.9 KiB
TypeScript

/**
* Builds the `ActivePlugin` shape (docs/plans/plugins/PLUGIN-API.md)
* for a plugin the operator just enabled, so the settings page can trigger a
* runtime `loadPlugins` call without a full page reload. Mirrors the
* backend's boot-payload mapping in
* apps/backend/internal/backendapp/helpers.go (bundleActivePlugins /
* pluginStyleURLs) — keep both in sync.
*/
import type { ActivePlugin } from "./types";
import type { PluginRecord } from "@/lib/types/plugins";
/**
* Returns the `ActivePlugin` for a plugin record, or null when the plugin
* declares no UI bundle (nothing to load).
*
* `bundleUrl` carries the installed `version` as a `?v=` query param so an
* updated plugin resolves to a *different* module specifier. Without this,
* `unloadPlugin(id, { evictCache: true })` (see `lib/plugins/host.ts`) drops
* the cached bundle registration, but a same-tab re-`import()` of the same
* URL returns the browser's already-evaluated ES module without re-running
* its top-level `registerKandevPlugin()` — leaving the plugin active but
* unregistered. Keying the URL on version keeps a plain unchanged
* enable/disable cycle or reload cache-friendly (identical URL, browser
* cache hit) while forcing a real re-import + re-execution whenever the
* version actually changes.
*/
export function toActivePlugin(record: PluginRecord): ActivePlugin | null {
const bundle = record.ui?.bundle;
if (!bundle) return null;
return {
id: record.id,
name: record.display_name,
bundleUrl: `/api/plugins/${record.id}/bundle?v=${encodeURIComponent(record.version)}`,
styleUrls: pluginStyleURLs(record),
...(record.repository_providers ? { repositoryProviderIds: record.repository_providers } : {}),
};
}
function pluginStyleURLs(record: PluginRecord): string[] | undefined {
const styles = record.ui?.styles;
if (!styles || styles.length === 0) return undefined;
return styles.map((style) => `/api/plugins/${record.id}/ui${style}`);
}