Skip to content

Commit 5e7bd68

Browse files
authored
Merge pull request #1027 from objectstack-ai/copilot/remove-core-composestacks-implementation
2 parents 19745da + c7965e8 commit 5e7bd68

File tree

8 files changed

+190
-478
lines changed

8 files changed

+190
-478
lines changed

ROADMAP.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th
11661166

11671167
### P2.6 Plugin Modularization & Dynamic Management
11681168

1169-
> **Status:** Phase 1 complete — Plugin class standard, install/uninstall API, example plugin classes. Phase 1.5 complete — composeStacks, plugin isolation, duplicate merge removal.
1169+
> **Status:** Phase 1 complete — Plugin class standard, install/uninstall API, example plugin classes. Phase 1.5 complete — composeStacks unified to `@objectstack/spec`, plugin isolation, duplicate merge removal.
11701170
11711171
Plugin architecture refactoring to support true modular development, plugin isolation, and dynamic plugin install/uninstall at runtime.
11721172

@@ -1188,11 +1188,10 @@ Plugin architecture refactoring to support true modular development, plugin isol
11881188
- [x] Remove duplicate `mergeActionsIntoObjects()` from root config and console shared config
11891189
- [x] Remove duplicate `mergeViewsIntoObjects()` from root config and console shared config (moved into `composeStacks`)
11901190
- [x] Refactor root `objectstack.config.ts` and `apps/console/objectstack.shared.ts` to use `composeStacks()`
1191-
- [x] Unit tests for `composeStacks()` (15 tests covering merging, dedup, views, actions, cross-stack, conflict detection)
11921191
- [x] Eliminate `defineStack()` double-pass hack — single `composeStacks()` call produces final config with runtime properties (listViews, actions) preserved. `defineStack()` Zod validation stripped these fields, requiring a second `composeStacks` pass to restore them.
11931192
- [x] Use `composed.apps` unified flow in console shared config — replaced manual `[...crmApps, ...(todoConfig.apps || []), ...]` spreading with CRM navigation patch applied to composed output
11941193
- [x] Use `composed.reports` in console shared config — replaced `...(crmConfig.reports || [])` with `...(composed.reports || [])` to include reports from all stacks
1195-
- [x] **composeStacks responsibility split:** `@object-ui/core` composeStacks handles runtime mapping (merging views→objects listViews, actions→objects) while `@objectstack/spec` composeStacks handles protocol-level composition (broader field concatenation, manifest selection, i18n). ObjectUI apps use the core version for single-pass config with runtime properties preserved.
1194+
- [x] **composeStacks unified to `@objectstack/spec`:** Removed `@object-ui/core` composeStacks implementation and its 15-test suite (coverage now lives upstream in `@objectstack/spec`). All config composition now uses `composeStacks` from `@objectstack/spec` (protocol-level: object dedup, array concatenation, actions→objects mapping, manifest selection, i18n). Runtime-specific `mergeViewsIntoObjects` adapter extracted to `@object-ui/core` and applied post-composition at call sites. A deprecated re-export of `composeStacks` is kept in `@object-ui/core` for backward compatibility.
11961195

11971196
**Phase 2 — Dynamic Plugin Loading (Planned)**
11981197
- [ ] Hot-reload / lazy loading of plugins for development
@@ -1242,7 +1241,7 @@ Plugin architecture refactoring to support true modular development, plugin isol
12421241
- [x] **P1: Chart Widget Server-Side Aggregation** — Fixed chart widgets (bar/line/area/pie/donut/scatter) downloading all raw data and aggregating client-side. Added optional `aggregate()` method to `DataSource` interface (`AggregateParams`, `AggregateResult` types) enabling server-side grouping/aggregation via analytics API (e.g. `GET /api/v1/analytics/{resource}?category=…&metric=…&agg=…`). `ObjectChart` now prefers `dataSource.aggregate()` when available, falling back to `dataSource.find()` + client-side aggregation for backward compatibility. Implemented `aggregate()` in `ValueDataSource` (in-memory), `ApiDataSource` (HTTP), and `ObjectStackAdapter` (analytics API with client-side fallback). Only detail widgets (grid/table/list) continue to fetch full data. 9 new tests.
12431242
- [x] **P1: Spec-Aligned CRM I18n** — Fixed CRM internationalization not taking effect on the console. Root cause: CRM metadata used plain string labels instead of spec-aligned `I18nLabel` objects. Fix: (1) Updated CRM app/dashboard/navigation metadata to use `I18nLabel` objects (`{ key, defaultValue }`) per spec. (2) Updated `NavigationItem` and `NavigationArea` types to support I18nLabel. (3) Added `resolveLabel()` helper in NavigationRenderer. (4) Updated `resolveI18nLabel()` to accept `t()` function for translation. (5) Added `loadLanguage` callback in I18nProvider for API-based translation loading. (6) Added `/api/v1/i18n/:lang` endpoint to mock server. Console contains zero CRM-specific code.
12441243
- [x] **P0: Opportunity List View & ObjectDef Column Enrichment** — Fixed ObjectGrid not using objectDef field metadata for type-aware rendering when columns are `string[]` or `ListColumn[]` without full options. (1) Schema resolution always fetches full schema from DataSource for field type metadata. (2) String[] column path enriched with objectDef types, options (with colors), currency, precision for proper CurrencyCellRenderer, SelectCellRenderer (colored badges), PercentCellRenderer, DateCellRenderer. (3) ListColumn[] fieldMeta deep-merged with objectDef field properties (select options with colors, currency code, precision). (4) Opportunity view columns upgraded from bare `string[]` to `ListColumn[]` with explicit types, alignment, and summary aggregation. 9 new tests.
1245-
- [x] **P1: Actions Merge into Object Definitions** — Fixed action buttons never showing in Console/Studio because example object definitions lacked `actions` field. Initially added `mergeActionsIntoObjects()` helper with longest-prefix name matching. Later refactored: all actions now declare explicit `objectName`, and merging is handled by `composeStacks()` in `@object-ui/core`. Created todo task actions (6: complete, start, clone, defer, set_reminder, assign) and kitchen-sink showcase actions (3: change_status, assign_owner, archive). All CRM/Todo/Kitchen Sink objects now serve `actions` in metadata. Fixes #840.
1244+
- [x] **P1: Actions Merge into Object Definitions** — Fixed action buttons never showing in Console/Studio because example object definitions lacked `actions` field. Initially added `mergeActionsIntoObjects()` helper with longest-prefix name matching. Later refactored: all actions now declare explicit `objectName`, and merging is handled by `composeStacks()` from `@objectstack/spec`. Created todo task actions (6: complete, start, clone, defer, set_reminder, assign) and kitchen-sink showcase actions (3: change_status, assign_owner, archive). All CRM/Todo/Kitchen Sink objects now serve `actions` in metadata. Fixes #840.
12461245
- [x] **P1: Unified Debug/Metadata Entry — Remove Redundant Metadata Button** — Removed the visible `<MetadataToggle>` button from RecordDetailView, DashboardView, PageView, and ReportView headers. End users no longer see a "</> Metadata" button that had no practical purpose. The MetadataInspector panel is now only accessible via `?__debug` URL parameter (auto-opens when debug mode is active). ObjectView retains its admin-only Design Tools menu entry for metadata inspection. This unifies the debug entry point and improves end-user UX by removing redundant UI elements.
12471246

12481247
### Ecosystem & Marketplace

apps/console/objectstack.shared.ts

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ObjectStackDefinition } from '@objectstack/spec';
2-
import { composeStacks } from '@object-ui/core';
2+
import { composeStacks } from '@objectstack/spec';
3+
import { mergeViewsIntoObjects } from '@object-ui/core';
34
import crmConfigImport from '@object-ui/example-crm/objectstack.config';
45
import todoConfigImport from '@object-ui/example-todo/objectstack.config';
56
import kitchenSinkConfigImport from '@object-ui/example-kitchen-sink/objectstack.config';
@@ -17,14 +18,20 @@ const crmConfig = resolveDefault<ObjectStackDefinition>(crmConfigImport);
1718
const todoConfig = resolveDefault<ObjectStackDefinition>(todoConfigImport);
1819
const kitchenSinkConfig = resolveDefault<ObjectStackDefinition>(kitchenSinkConfigImport);
1920

20-
// Single-pass composition: composeStacks handles object deduplication (override),
21-
// views→objects mapping, and actions→objects assignment via objectName.
22-
// No defineStack() validation pass — it would strip runtime properties (listViews,
23-
// actions) from objects, requiring a double-pass hack to restore them.
24-
const composed = composeStacks(
25-
[crmConfig, todoConfig, kitchenSinkConfig] as Record<string, any>[],
26-
{ objectConflict: 'override' },
27-
);
21+
const allConfigs = [crmConfig, todoConfig, kitchenSinkConfig];
22+
23+
// Aggregate seed data from all manifest.data arrays (spec selects one manifest,
24+
// so we collect data from all stacks before composing).
25+
const allData = allConfigs.flatMap((c: any) => c.manifest?.data || c.data || []);
26+
27+
// Protocol-level composition via @objectstack/spec: handles object dedup,
28+
// array concatenation, actions→objects mapping, and manifest selection.
29+
const composed = composeStacks(allConfigs as any[], { objectConflict: 'override' }) as any;
30+
31+
// Adapter: merge views[].listViews into object definitions for the runtime.
32+
if (composed.objects && composed.views) {
33+
composed.objects = mergeViewsIntoObjects(composed.objects, composed.views);
34+
}
2835

2936
// Patch CRM App Navigation to include Report using a supported navigation type
3037
// (type: 'url' passes schema validation while still routing correctly via React Router)
@@ -80,7 +87,7 @@ export const sharedConfig = {
8087
version: '0.1.0',
8188
type: 'app',
8289
name: '@object-ui/console',
83-
data: composed.manifest.data,
90+
data: allData,
8491
},
8592
plugins: [],
8693
datasources: [

objectstack.config.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import { ObjectQLPlugin } from '@objectstack/objectql';
1919
import { InMemoryDriver } from '@objectstack/driver-memory';
2020
import { HonoServerPlugin } from '@objectstack/plugin-hono-server';
2121
import { ConsolePlugin } from '@object-ui/console';
22-
import { composeStacks } from '@object-ui/core';
22+
import { composeStacks } from '@objectstack/spec';
23+
import { mergeViewsIntoObjects } from '@object-ui/core';
2324
import { CRMPlugin } from './examples/crm/plugin';
2425
import { TodoPlugin } from './examples/todo/plugin';
2526
import { KitchenSinkPlugin } from './examples/kitchen-sink/plugin';
@@ -33,11 +34,18 @@ const allConfigs = plugins.map((p) => {
3334
return (raw as any).default || raw;
3435
});
3536

36-
// Single-pass composition: composeStacks handles object deduplication,
37-
// views→objects mapping, and actions→objects assignment via objectName.
38-
// No defineStack() validation pass needed — it would strip runtime properties
39-
// (listViews, actions) from objects, requiring a second merge pass to restore them.
40-
const composed = composeStacks(allConfigs, { objectConflict: 'override' });
37+
// Aggregate seed data from all manifest.data arrays (spec selects one manifest,
38+
// so we collect data from all stacks before composing).
39+
const allData = allConfigs.flatMap((c: any) => c.manifest?.data || c.data || []);
40+
41+
// Protocol-level composition via @objectstack/spec: handles object dedup,
42+
// array concatenation, actions→objects mapping, and manifest selection.
43+
const composed = composeStacks(allConfigs as any[], { objectConflict: 'override' }) as any;
44+
45+
// Adapter: merge views[].listViews into object definitions for the runtime.
46+
if (composed.objects && composed.views) {
47+
composed.objects = mergeViewsIntoObjects(composed.objects, composed.views);
48+
}
4149

4250
const mergedApp = {
4351
...composed,
@@ -47,7 +55,7 @@ const mergedApp = {
4755
version: '0.0.0',
4856
description: 'ObjectUI monorepo development workspace',
4957
type: 'app',
50-
data: composed.manifest.data,
58+
data: allData,
5159
},
5260
};
5361

packages/core/src/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,13 @@ export * from './data-scope/index.js';
2626
export * from './errors/index.js';
2727
export * from './utils/debug.js';
2828
export * from './utils/debug-collector.js';
29-
export * from './utils/compose-stacks.js';
29+
export * from './utils/merge-views-into-objects.js';
3030
export * from './protocols/index.js';
31+
32+
/**
33+
* @deprecated Import `composeStacks` from `@objectstack/spec` instead.
34+
*
35+
* This re-export is kept only for backward compatibility and will be removed
36+
* in the next major version of `@object-ui/core`.
37+
*/
38+
export { composeStacks } from '@objectstack/spec';

0 commit comments

Comments
 (0)