import type { App, PluginManifest } from "@/deps.ts"; export interface ObsidianCommunityPluginManager { enabledPlugins: ReadonlySet; manifests: PluginManifest[]; loadPlugin(pluginId: string): Promise; unloadPlugin(pluginId: string): Promise; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null; } function isPluginManifest(value: unknown): value is PluginManifest { return ( isRecord(value) && typeof value.id === "string" && typeof value.name === "string" && (value.dir === undefined || typeof value.dir === "string") ); } function isStringSet(value: unknown): value is ReadonlySet { if (!(value instanceof Set)) { return false; } const entries = value as ReadonlySet; for (const entry of entries) { if (typeof entry !== "string") { return false; } } return true; } async function invokeLifecycleMethod( manager: Record, methodName: "loadPlugin" | "unloadPlugin", pluginId: string ): Promise { const method = manager[methodName]; if (typeof method !== "function") { throw new TypeError(`Obsidian does not expose ${methodName}`); } const result: unknown = Reflect.apply(method, manager, [pluginId]); await result; } export function getObsidianCommunityPluginManager(app: App): ObsidianCommunityPluginManager { const managerValue: unknown = Reflect.get(app, "plugins"); if (!isRecord(managerValue) || !isRecord(managerValue.manifests) || !isStringSet(managerValue.enabledPlugins)) { throw new TypeError("Obsidian does not expose the community plug-in manager"); } const manifests = Object.values(managerValue.manifests).filter(isPluginManifest); return { enabledPlugins: managerValue.enabledPlugins, manifests, loadPlugin: async (pluginId) => await invokeLifecycleMethod(managerValue, "loadPlugin", pluginId), unloadPlugin: async (pluginId) => await invokeLifecycleMethod(managerValue, "unloadPlugin", pluginId), }; }