@@ -268,6 +268,92 @@ export class ObjectQL implements IDataEngine {
268268 this . logger . debug ( 'Registered Kind' , { kind : kind . name || kind . type , from : id } ) ;
269269 }
270270 }
271+
272+ // 7. Recursively register nested plugins
273+ if ( Array . isArray ( manifest . plugins ) && manifest . plugins . length > 0 ) {
274+ this . logger . debug ( 'Processing nested plugins' , { id, count : manifest . plugins . length } ) ;
275+ for ( const plugin of manifest . plugins ) {
276+ if ( plugin && typeof plugin === 'object' ) {
277+ const pluginName = plugin . name || plugin . id || 'unnamed-plugin' ;
278+ this . logger . debug ( 'Registering nested plugin' , { pluginName, parentId : id } ) ;
279+ this . registerPlugin ( plugin , id , namespace ) ;
280+ }
281+ }
282+ }
283+ }
284+
285+ /**
286+ * Register a nested plugin's metadata (objects, actions, views, etc.)
287+ *
288+ * Unlike registerApp(), this does NOT call SchemaRegistry.installPackage()
289+ * because plugins are not formal manifests — they are lightweight config
290+ * bundles with objects, actions, triggers, and navigation.
291+ *
292+ * @param plugin - The plugin config object
293+ * @param parentId - The parent package ID (for ownership tracking)
294+ * @param parentNamespace - The parent package's namespace (for FQN resolution)
295+ */
296+ private registerPlugin ( plugin : any , parentId : string , parentNamespace ?: string ) {
297+ const pluginName = plugin . name || plugin . id || 'unnamed' ;
298+ const pluginNamespace = plugin . namespace || parentNamespace ;
299+
300+ // Use parentId as the owning package for namespace consistency.
301+ // The parent package already claimed the namespace — nested plugins
302+ // contribute objects UNDER the parent's ownership.
303+ const ownerId = parentId ;
304+
305+ // Register objects (supports both Array and Map formats)
306+ if ( plugin . objects ) {
307+ try {
308+ if ( Array . isArray ( plugin . objects ) ) {
309+ this . logger . debug ( 'Registering plugin objects (Array)' , { pluginName, count : plugin . objects . length } ) ;
310+ for ( const objDef of plugin . objects ) {
311+ const fqn = SchemaRegistry . registerObject ( objDef , ownerId , pluginNamespace , 'own' ) ;
312+ this . logger . debug ( 'Registered Object' , { fqn, from : pluginName } ) ;
313+ }
314+ } else {
315+ const entries = Object . entries ( plugin . objects ) ;
316+ this . logger . debug ( 'Registering plugin objects (Map)' , { pluginName, count : entries . length } ) ;
317+ for ( const [ name , objDef ] of entries ) {
318+ ( objDef as any ) . name = name ;
319+ const fqn = SchemaRegistry . registerObject ( objDef as any , ownerId , pluginNamespace , 'own' ) ;
320+ this . logger . debug ( 'Registered Object' , { fqn, from : pluginName } ) ;
321+ }
322+ }
323+ } catch ( err : any ) {
324+ this . logger . warn ( 'Failed to register plugin objects' , { pluginName, error : err . message } ) ;
325+ }
326+ }
327+
328+ // Register plugin as app if it has navigation (for sidebar display)
329+ if ( plugin . name && plugin . navigation ) {
330+ try {
331+ SchemaRegistry . registerApp ( plugin , ownerId ) ;
332+ this . logger . debug ( 'Registered plugin-as-app' , { app : plugin . name , from : pluginName } ) ;
333+ } catch ( err : any ) {
334+ this . logger . warn ( 'Failed to register plugin as app' , { pluginName, error : err . message } ) ;
335+ }
336+ }
337+
338+ // Register metadata arrays (actions, views, triggers, etc.)
339+ const metadataArrayKeys = [
340+ 'actions' , 'views' , 'pages' , 'dashboards' , 'reports' , 'themes' ,
341+ 'flows' , 'workflows' , 'approvals' , 'webhooks' ,
342+ 'roles' , 'permissions' , 'profiles' , 'sharingRules' , 'policies' ,
343+ 'agents' , 'ragPipelines' , 'apis' ,
344+ 'hooks' , 'mappings' , 'analyticsCubes' , 'connectors' ,
345+ ] ;
346+ for ( const key of metadataArrayKeys ) {
347+ const items = ( plugin as any ) [ key ] ;
348+ if ( Array . isArray ( items ) && items . length > 0 ) {
349+ for ( const item of items ) {
350+ const itemName = item . name || item . id ;
351+ if ( itemName ) {
352+ SchemaRegistry . registerItem ( key , item , 'name' as any , ownerId ) ;
353+ }
354+ }
355+ }
356+ }
271357 }
272358
273359 /**
0 commit comments