11import { defineStack } from '@objectstack/spec' ;
22import type { ObjectStackDefinition } from '@objectstack/spec' ;
3+ import { composeStacks } from '@object-ui/core' ;
34import crmConfigImport from '@object-ui/example-crm/objectstack.config' ;
45import todoConfigImport from '@object-ui/example-todo/objectstack.config' ;
56import kitchenSinkConfigImport from '@object-ui/example-kitchen-sink/objectstack.config' ;
@@ -36,84 +37,13 @@ if (crmApps.length > 0) {
3637 }
3738}
3839
39- // ---------------------------------------------------------------------------
40- // Merge stack-level views into object definitions.
41- // The @objectstack /spec defines views at the stack level (views[].listViews),
42- // but the runtime protocol serves objects without listViews. This helper
43- // merges listViews from the views array into the corresponding objects so
44- // the console can render the correct view type when switching views.
45- // ---------------------------------------------------------------------------
46- function mergeViewsIntoObjects ( objects : any [ ] , configs : any [ ] ) : any [ ] {
47- // Collect all listViews grouped by object name
48- const viewsByObject : Record < string , Record < string , any > > = { } ;
49- for ( const config of configs ) {
50- if ( ! Array . isArray ( config . views ) ) continue ;
51- for ( const view of config . views ) {
52- if ( ! view . listViews ) continue ;
53- for ( const [ viewName , listView ] of Object . entries ( view . listViews as Record < string , any > ) ) {
54- const objectName = listView ?. data ?. object ;
55- if ( ! objectName ) continue ;
56- if ( ! viewsByObject [ objectName ] ) viewsByObject [ objectName ] = { } ;
57- viewsByObject [ objectName ] [ viewName ] = listView ;
58- }
59- }
60- }
61-
62- // Merge into objects
63- return objects . map ( ( obj : any ) => {
64- const views = viewsByObject [ obj . name ] ;
65- if ( ! views ) return obj ;
66- return { ...obj , listViews : { ...( obj . listViews || { } ) , ...views } } ;
67- } ) ;
68- }
69-
70- // ---------------------------------------------------------------------------
71- // Merge stack-level actions into object definitions.
72- // Actions declared at the stack level (actions[]) need to be merged into
73- // individual object definitions so the runtime protocol (and Console/Studio)
74- // can render action buttons directly from objectDef.actions.
75- // Matching uses explicit objectName on the action, or longest object-name
76- // prefix of the action name (e.g. "account_send_email" → "account").
77- // ---------------------------------------------------------------------------
78- function mergeActionsIntoObjects ( objects : any [ ] , configs : any [ ] ) : any [ ] {
79- const allActions : any [ ] = [ ] ;
80- for ( const config of configs ) {
81- if ( Array . isArray ( config . actions ) ) {
82- allActions . push ( ...config . actions ) ;
83- }
84- }
85- if ( allActions . length === 0 ) return objects ;
86-
87- // Sort object names longest-first so "order_item" matches before "order"
88- const objectNames = objects . map ( ( o : any ) => o . name as string )
89- . filter ( Boolean )
90- . sort ( ( a , b ) => b . length - a . length ) ;
91-
92- const actionsByObject : Record < string , any [ ] > = { } ;
93- for ( const action of allActions ) {
94- let target : string | undefined = action . objectName ;
95- if ( ! target ) {
96- for ( const name of objectNames ) {
97- if ( action . name . startsWith ( name + '_' ) ) {
98- target = name ;
99- break ;
100- }
101- }
102- }
103- if ( target ) {
104- if ( ! actionsByObject [ target ] ) actionsByObject [ target ] = [ ] ;
105- actionsByObject [ target ] . push ( action ) ;
106- }
107- }
108-
109- return objects . map ( ( obj : any ) => {
110- const actions = actionsByObject [ obj . name ] ;
111- if ( ! actions ) return obj ;
112- return { ...obj , actions : [ ...( obj . actions || [ ] ) , ...actions ] } ;
113- } ) ;
114- }
115-
116- const allConfigs = [ crmConfig , todoConfig , kitchenSinkConfig ] ;
40+ // Compose all example stacks into a single merged definition.
41+ // composeStacks handles object deduplication (override), views→objects mapping,
42+ // and actions→objects assignment via objectName.
43+ const composed = composeStacks (
44+ [ crmConfig , todoConfig , kitchenSinkConfig ] as Record < string , any > [ ] ,
45+ { objectConflict : 'override' } ,
46+ ) ;
11747
11848export const sharedConfig = {
11949 // ============================================================================
@@ -125,29 +55,15 @@ export const sharedConfig = {
12555 description : 'ObjectStack Console' ,
12656
12757 // ============================================================================
128- // Merged Stack Configuration (CRM + Todo + Kitchen Sink + Mock Metadata )
58+ // Merged Stack Configuration (CRM + Todo + Kitchen Sink)
12959 // ============================================================================
130- objects : mergeActionsIntoObjects (
131- mergeViewsIntoObjects (
132- [
133- ...( crmConfig . objects || [ ] ) ,
134- ...( todoConfig . objects || [ ] ) ,
135- ...( kitchenSinkConfig . objects || [ ] ) ,
136- ] ,
137- allConfigs ,
138- ) ,
139- allConfigs ,
140- ) ,
60+ objects : composed . objects ,
14161 apps : [
14262 ...crmApps ,
14363 ...( todoConfig . apps || [ ] ) ,
14464 ...( kitchenSinkConfig . apps || [ ] ) ,
14565 ] ,
146- dashboards : [
147- ...( crmConfig . dashboards || [ ] ) ,
148- ...( todoConfig . dashboards || [ ] ) ,
149- ...( kitchenSinkConfig . dashboards || [ ] )
150- ] ,
66+ dashboards : composed . dashboards ,
15167 reports : [
15268 ...( crmConfig . reports || [ ] ) ,
15369 // Manually added report since CRM config validation prevents it
@@ -165,21 +81,13 @@ export const sharedConfig = {
16581 ]
16682 }
16783 ] ,
168- pages : [
169- ...( crmConfig . pages || [ ] ) ,
170- ...( todoConfig . pages || [ ] ) ,
171- ...( kitchenSinkConfig . pages || [ ] )
172- ] ,
84+ pages : composed . pages ,
17385 manifest : {
17486 id : 'com.objectui.console' ,
17587 version : '0.1.0' ,
17688 type : 'app' ,
17789 name : '@object-ui/console' ,
178- data : [
179- ...( crmConfig . manifest ?. data || [ ] ) ,
180- ...( todoConfig . manifest ?. data || [ ] ) ,
181- ...( kitchenSinkConfig . manifest ?. data || [ ] )
182- ]
90+ data : composed . manifest . data ,
18391 } ,
18492 plugins : [ ] ,
18593 datasources : [
@@ -191,14 +99,16 @@ export const sharedConfig = {
19199 ]
192100} ;
193101
102+ const allConfigs = [ crmConfig , todoConfig , kitchenSinkConfig ] ;
103+
194104// defineStack() validates the config but strips non-standard properties like
195- // listViews and actions from objects. Re-merge after validation so the runtime
196- // protocol serves objects with their view and action definitions.
105+ // listViews and actions from objects. Re-compose after validation so the
106+ // runtime protocol serves objects with their view and action definitions.
197107const validated = defineStack ( sharedConfig as Parameters < typeof defineStack > [ 0 ] ) ;
198108export default {
199109 ...validated ,
200- objects : mergeActionsIntoObjects (
201- mergeViewsIntoObjects ( validated . objects || [ ] , allConfigs ) ,
202- allConfigs ,
203- ) ,
110+ objects : composeStacks ( [
111+ { objects : validated . objects || [ ] } ,
112+ ... allConfigs . map ( ( cfg : any ) => ( { views : cfg . views || [ ] , actions : cfg . actions || [ ] } ) ) ,
113+ ] ) . objects ,
204114} ;
0 commit comments