diff --git a/packages/core/src/database/entities/plugin.entity.ts b/packages/core/src/database/entities/plugin.entity.ts index 9ad1a50..ef9c834 100644 --- a/packages/core/src/database/entities/plugin.entity.ts +++ b/packages/core/src/database/entities/plugin.entity.ts @@ -43,6 +43,7 @@ export class Plugin { hooks?: string[]; permissions?: string[]; dependencies?: Record; + peerDependencies?: Record; settings?: Record; entryPoint?: string; }; diff --git a/packages/core/src/modules/plugins/installed-plugins.service.ts b/packages/core/src/modules/plugins/installed-plugins.service.ts index 49816ac..830fb80 100644 --- a/packages/core/src/modules/plugins/installed-plugins.service.ts +++ b/packages/core/src/modules/plugins/installed-plugins.service.ts @@ -40,7 +40,7 @@ export class InstalledPluginsService { applicationId: string, pluginId: string, ownerId: string, - ): Promise { + ): Promise { // Verify app ownership const app = await this.appRepo.findOne({ where: { id: applicationId, ownerId }, @@ -97,8 +97,31 @@ export class InstalledPluginsService { pluginId, }); + // Peer dependency check (warning-only, never throws or blocks) + const peerDeps = plugin.manifest?.peerDependencies ?? {}; + const peerWarnings: string[] = []; + if (Object.keys(peerDeps).length > 0) { + const activeInstalled = await this.installedRepo.find({ + where: { applicationId, status: InstalledPluginStatus.ACTIVE }, + relations: ["plugin"], + }); + const activeSlugs = new Set( + activeInstalled.map((i) => i.plugin?.slug).filter(Boolean), + ); + for (const peerSlug of Object.keys(peerDeps)) { + if (!activeSlugs.has(peerSlug)) { + peerWarnings.push(peerSlug); + } + } + if (peerWarnings.length > 0) { + this.logger.warn( + `Plugin "${plugin.name}" installed on app "${app.name}" with missing peer plugins: ${peerWarnings.join(", ")}`, + ); + } + } + this.logger.log(`Plugin ${plugin.name} installed on app ${app.name}`); - return result; + return { ...result, peerWarnings }; } async uninstall( diff --git a/packages/plugins/sdk/src/index.ts b/packages/plugins/sdk/src/index.ts index 4dbe89c..6ada9ce 100644 --- a/packages/plugins/sdk/src/index.ts +++ b/packages/plugins/sdk/src/index.ts @@ -28,6 +28,8 @@ export interface PluginManifest { author?: string; permissions?: string[]; dependencies?: Record; + /** Slugs of plugins this plugin works best alongside. Missing peers trigger a warning but never block install. */ + peerDependencies?: Record; } export interface PluginContext {