11import type { ObjectStackDefinition } from '@objectstack/spec' ;
2- import { composeStacks } from '@objectstack/spec' ;
32import { mergeViewsIntoObjects } from '@object-ui/core' ;
43import { SETUP_APP_DEFAULTS } from '@objectstack/plugin-setup' ;
54import crmConfigImport from '@object-ui/example-crm/objectstack.config' ;
@@ -15,47 +14,48 @@ function resolveDefault<T>(mod: MaybeDefault<T>): T {
1514 return mod as T ;
1615}
1716
18- const crmConfig = resolveDefault < ObjectStackDefinition > ( crmConfigImport ) ;
19- const todoConfig = resolveDefault < ObjectStackDefinition > ( todoConfigImport ) ;
20- const kitchenSinkConfig = resolveDefault < ObjectStackDefinition > ( kitchenSinkConfigImport ) ;
21-
22- const allConfigs = [ crmConfig , todoConfig , kitchenSinkConfig ] ;
23-
24- // Aggregate seed data from all manifest.data arrays (spec selects one manifest,
25- // so we collect data from all stacks before composing).
26- const allData = allConfigs . flatMap ( ( c : any ) => c . manifest ?. data || c . data || [ ] ) ;
27-
28- // Aggregate i18n bundles from all stacks that declare an i18n section.
29- // Each bundle carries a namespace (e.g. 'crm') and per-language translations.
30- const i18nBundles = allConfigs
31- . map ( ( c : any ) => c . i18n )
32- . filter ( ( i : any ) => i ?. namespace && i ?. translations ) ;
33-
34- // Build the spec `translations` array for the runtime's AppPlugin.
35- // AppPlugin.loadTranslations expects `translations: Array<{ [locale]: data }>`.
36- // Each locale's data is nested under the bundle's namespace so that
37- // both the server-mode (AppPlugin → memory i18n) and MSW-mode (createKernel)
38- // produce the same structure: `{ crm: { objects: { ... } } }`.
39- const specTranslations : Record < string , any > [ ] = i18nBundles . map ( ( bundle : any ) => {
40- const result : Record < string , any > = { } ;
41- for ( const [ locale , data ] of Object . entries ( bundle . translations ) ) {
42- result [ locale ] = { [ bundle . namespace ] : data } ;
17+ /**
18+ * Adapter: prepare a stack config for AppPlugin.
19+ * - Merges stack-level views into object definitions
20+ * - Converts i18n translations to the spec format AppPlugin expects
21+ */
22+ function prepareConfig ( config : any ) {
23+ const result = { ...config } ;
24+ if ( result . objects && result . views ) {
25+ result . objects = mergeViewsIntoObjects ( result . objects , result . views ) ;
26+ }
27+ if ( result . i18n ?. namespace && result . i18n ?. translations ) {
28+ const ns = result . i18n . namespace ;
29+ const converted : Record < string , any > = { } ;
30+ for ( const [ locale , data ] of Object . entries ( result . i18n . translations ) ) {
31+ converted [ locale ] = { [ ns ] : data } ;
32+ }
33+ result . translations = [ converted ] ;
4334 }
4435 return result ;
45- } ) ;
36+ }
4637
47- // Protocol-level composition via @objectstack/spec: handles object dedup,
48- // array concatenation, actions→objects mapping, and manifest selection.
49- const composed = composeStacks ( allConfigs as any [ ] , { objectConflict : 'override' } ) as any ;
38+ const crmConfig = prepareConfig ( resolveDefault < ObjectStackDefinition > ( crmConfigImport ) ) ;
39+ const todoConfig = prepareConfig ( resolveDefault < ObjectStackDefinition > ( todoConfigImport ) ) ;
40+ const kitchenSinkConfig = prepareConfig ( resolveDefault < ObjectStackDefinition > ( kitchenSinkConfigImport ) ) ;
5041
51- // Adapter: merge views[].listViews into object definitions for the runtime.
52- if ( composed . objects && composed . views ) {
53- composed . objects = mergeViewsIntoObjects ( composed . objects , composed . views ) ;
54- }
42+ /**
43+ * Individual prepared configs for per-plugin AppPlugin loading.
44+ * Used by createKernel and server-mode objectstack.config.ts.
45+ */
46+ export const appConfigs = [ crmConfig , todoConfig , kitchenSinkConfig ] ;
47+
48+ // Setup App config for registration via AppPlugin (avoids SetupPlugin timing issue)
49+ export const setupAppConfig = {
50+ apps : [ SETUP_APP_DEFAULTS ] ,
51+ manifest : { id : 'setup' , name : 'setup' } ,
52+ } ;
5553
5654// Patch CRM App Navigation to include Report using a supported navigation type
57- // (type: 'url' passes schema validation while still routing correctly via React Router)
58- const apps = [ ...JSON . parse ( JSON . stringify ( composed . apps || [ ] ) ) , SETUP_APP_DEFAULTS ] ;
55+ const apps = [
56+ ...JSON . parse ( JSON . stringify ( appConfigs . flatMap ( ( c : any ) => c . apps || [ ] ) ) ) ,
57+ SETUP_APP_DEFAULTS ,
58+ ] ;
5959const crmApp = apps . find ( ( a : any ) => a . name === 'crm_app' ) ;
6060if ( crmApp ?. navigation ) {
6161 const dashboardIdx = crmApp . navigation . findIndex ( ( n : any ) => n . id === 'nav_dashboard' ) ;
@@ -69,24 +69,29 @@ if (crmApp?.navigation) {
6969 } ) ;
7070}
7171
72+ // Aggregate i18n bundles from all stacks
73+ const i18nBundles = appConfigs
74+ . map ( ( c : any ) => c . i18n )
75+ . filter ( ( i : any ) => i ?. namespace && i ?. translations ) ;
76+
77+ // Aggregate seed data across all configs
78+ const allData = appConfigs . flatMap ( ( c : any ) => c . manifest ?. data || c . data || [ ] ) ;
79+
80+ /**
81+ * Aggregated sharedConfig for backward compatibility.
82+ * Used by tests that mock objectstack.shared and by components
83+ * that need aggregated metadata (apps, objects, etc.).
84+ */
7285export const sharedConfig = {
73- // ============================================================================
74- // Project Metadata
75- // ============================================================================
76-
7786 name : '@object-ui/console' ,
7887 version : '0.1.0' ,
7988 description : 'ObjectStack Console' ,
80-
81- // ============================================================================
82- // Merged Stack Configuration (CRM + Todo + Kitchen Sink)
83- // ============================================================================
84- objects : composed . objects ,
89+
90+ objects : appConfigs . flatMap ( ( c : any ) => c . objects || [ ] ) ,
8591 apps,
86- dashboards : composed . dashboards ,
92+ dashboards : appConfigs . flatMap ( ( c : any ) => c . dashboards || [ ] ) ,
8793 reports : [
88- ...( composed . reports || [ ] ) ,
89- // Console-specific report not in any example stack
94+ ...appConfigs . flatMap ( ( c : any ) => c . reports || [ ] ) ,
9095 {
9196 name : 'sales_performance_q1' ,
9297 label : 'Q1 Sales Performance' ,
@@ -101,7 +106,7 @@ export const sharedConfig = {
101106 ]
102107 }
103108 ] ,
104- pages : composed . pages ,
109+ pages : appConfigs . flatMap ( ( c : any ) => c . pages || [ ] ) ,
105110 manifest : {
106111 id : 'com.objectui.console' ,
107112 version : '0.1.0' ,
@@ -113,11 +118,6 @@ export const sharedConfig = {
113118 bundles : i18nBundles ,
114119 defaultLocale : 'en' ,
115120 } ,
116- // Spec-format translations array consumed by AppPlugin.loadTranslations()
117- // in real-server mode (pnpm start). Each entry maps locale → namespace-scoped
118- // translation data so the runtime's memory i18n fallback serves the same
119- // structure as the MSW mock handler.
120- translations : specTranslations ,
121121 plugins : [ ] ,
122122 datasources : [
123123 {
0 commit comments