@@ -15,6 +15,8 @@ final class PluginManager {
1515
1616 private( set) var plugins : [ PluginEntry ] = [ ]
1717
18+ private( set) var needsRestart = false
19+
1820 private( set) var driverPlugins : [ String : any DriverPlugin ] = [ : ]
1921
2022 private( set) var exportPlugins : [ String : any ExportFormatPlugin ] = [ : ]
@@ -53,6 +55,8 @@ final class PluginManager {
5355
5456 loadPlugins ( from: userPluginsDir, source: . userInstalled)
5557
58+ validateDependencies ( )
59+
5660 Self . logger. info ( " Loaded \( self . plugins. count) plugin(s): \( self . driverPlugins. count) driver(s), \( self . exportPlugins. count) export format(s) " )
5761 }
5862
@@ -126,6 +130,7 @@ final class PluginManager {
126130 )
127131
128132 plugins. append ( entry)
133+ validateCapabilityDeclarations ( principalClass, pluginId: bundleId)
129134
130135 if entry. isEnabled {
131136 let instance = principalClass. init ( )
@@ -140,7 +145,12 @@ final class PluginManager {
140145 // MARK: - Capability Registration
141146
142147 private func registerCapabilities( _ instance: any TableProPlugin , pluginId: String ) {
148+ let declared = Set ( type ( of: instance) . capabilities)
149+
143150 if let driver = instance as? any DriverPlugin {
151+ if !declared. contains ( . databaseDriver) {
152+ Self . logger. warning ( " Plugin ' \( pluginId) ' conforms to DriverPlugin but does not declare .databaseDriver capability — registering anyway " )
153+ }
144154 let typeId = type ( of: driver) . databaseTypeId
145155 driverPlugins [ typeId] = driver
146156 for additionalId in type ( of: driver) . additionalDatabaseTypeIds {
@@ -150,12 +160,28 @@ final class PluginManager {
150160 }
151161
152162 if let exportPlugin = instance as? any ExportFormatPlugin {
163+ if !declared. contains ( . exportFormat) {
164+ Self . logger. warning ( " Plugin ' \( pluginId) ' conforms to ExportFormatPlugin but does not declare .exportFormat capability — registering anyway " )
165+ }
153166 let formatId = type ( of: exportPlugin) . formatId
154167 exportPlugins [ formatId] = exportPlugin
155168 Self . logger. debug ( " Registered export plugin ' \( pluginId) ' for format ' \( formatId) ' " )
156169 }
157170 }
158171
172+ private func validateCapabilityDeclarations( _ pluginType: any TableProPlugin . Type , pluginId: String ) {
173+ let declared = Set ( pluginType. capabilities)
174+ let isDriver = pluginType is any DriverPlugin . Type
175+ let isExporter = pluginType is any ExportFormatPlugin . Type
176+
177+ if declared. contains ( . databaseDriver) && !isDriver {
178+ Self . logger. warning ( " Plugin ' \( pluginId) ' declares .databaseDriver but does not conform to DriverPlugin " )
179+ }
180+ if declared. contains ( . exportFormat) && !isExporter {
181+ Self . logger. warning ( " Plugin ' \( pluginId) ' declares .exportFormat but does not conform to ExportFormatPlugin " )
182+ }
183+ }
184+
159185 private func unregisterCapabilities( pluginId: String ) {
160186 driverPlugins = driverPlugins. filter { _, value in
161187 guard let entry = plugins. first ( where: { $0. id == pluginId } ) else { return true }
@@ -200,6 +226,7 @@ final class PluginManager {
200226 }
201227
202228 Self . logger. info ( " Plugin ' \( pluginId) ' \( enabled ? " enabled " : " disabled " ) " )
229+ NotificationCenter . default. post ( name: . pluginStateDidChange, object: nil , userInfo: [ " pluginId " : pluginId] )
203230 }
204231
205232 // MARK: - Install / Uninstall
@@ -292,12 +319,29 @@ final class PluginManager {
292319 disabledPluginIds = disabled
293320
294321 Self . logger. info ( " Uninstalled plugin ' \( id) ' " )
322+ needsRestart = true
323+ }
324+
325+ // MARK: - Dependency Validation
326+
327+ private func validateDependencies( ) {
328+ let loadedIds = Set ( plugins. map ( \. id) )
329+ for plugin in plugins where plugin. isEnabled {
330+ guard let principalClass = plugin. bundle. principalClass as? any TableProPlugin . Type else { continue }
331+ let deps = principalClass. dependencies
332+ for dep in deps {
333+ if !loadedIds. contains ( dep) {
334+ Self . logger. warning ( " Plugin ' \( plugin. id) ' requires ' \( dep) ' which is not installed " )
335+ } else if let depEntry = plugins. first ( where: { $0. id == dep } ) , !depEntry. isEnabled {
336+ Self . logger. warning ( " Plugin ' \( plugin. id) ' requires ' \( dep) ' which is disabled " )
337+ }
338+ }
339+ }
295340 }
296341
297342 // MARK: - Code Signature Verification
298343
299- // TODO: Replace with actual team identifier
300- private static let signingTeamId = " YOURTEAMID "
344+ private static let signingTeamId = " D7HJ5TFYCU "
301345
302346 private func createSigningRequirement( ) -> SecRequirement ? {
303347 var requirement : SecRequirement ?
0 commit comments