Skip to content

Add namespace support to ComponentRegistry to prevent naming conflicts#290

Merged
hotlong merged 9 commits intomainfrom
copilot/add-namespacing-to-components
Jan 31, 2026
Merged

Add namespace support to ComponentRegistry to prevent naming conflicts#290
hotlong merged 9 commits intomainfrom
copilot/add-namespacing-to-components

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 31, 2026

ComponentRegistry currently uses a flat namespace for 100+ components, risking conflicts when multiple plugins register components with identical names (e.g., grid from both plugin-grid and plugin-aggrid).

Changes

Core Registry

  • Added namespace optional field to ComponentMeta
  • Registry stores components under both namespace:type and type keys for backward compatibility
  • Lookup with explicit namespace returns only from that namespace; without namespace returns from non-namespaced key
  • Non-namespaced registrations emit deprecation warnings

Component Registrations

  • All 14 plugins updated to use plugin-specific namespaces (plugin-grid, plugin-aggrid, etc.)
  • UI components use ui namespace
  • Field components migrated from field: prefix to namespace: 'field' parameter

Testing

  • Added 24 comprehensive tests for namespace behavior
  • All 426 existing tests remain passing

Example

// Before - conflict risk
ComponentRegistry.register('grid', GridComponent);
ComponentRegistry.register('grid', AgGridComponent);  // Overwrites!

// After - isolated namespaces
ComponentRegistry.register('grid', GridComponent, { namespace: 'plugin-grid' });
ComponentRegistry.register('grid', AgGridComponent, { namespace: 'plugin-aggrid' });

// Explicit lookup
ComponentRegistry.get('grid', 'plugin-grid')    // → GridComponent
ComponentRegistry.get('grid', 'plugin-aggrid')  // → AgGridComponent

// Backward compatible - returns last registered
ComponentRegistry.get('grid')  // → AgGridComponent

Migration

Non-namespaced registrations continue working with deprecation warnings. Full type will be namespace:type format. Plan to remove non-namespaced support in v2.0.

Original prompt

3.1 ⚠️ 组件命名空间管理

优先级:高
当前评分:2/5

问题: 所有 100+ 组件共享 ComponentRegistry 中的单一扁平命名空间。随着项目规模扩大,存在名称冲突的风险。

示例:

ComponentRegistry.register('grid', GridComponent); // plugin-grid
ComponentRegistry.register('grid', AgGridComponent); // plugin-aggrid (冲突!)
影响:

插件冲突
调试困难
行为不可预测
建议:

方案 1:命名空间化的组件类型(推荐)

// 之前
type: 'grid'

// 之后
type: 'plugin-grid:grid' // 或 '@grid/table'
type: 'ui:button'
type: 'field:text'
实现:

// core/src/registry/ComponentRegistry.ts
export class ComponentRegistry {
private static components = new Map<string, ComponentDefinition>();

static register(
type: string,
component: React.ComponentType,
options?: {
namespace?: string; // 新增
...
}
) {
const fullType = options?.namespace
? ${options.namespace}:${type}
: type;
this.components.set(fullType, { component, ...options });
}

static get(type: string, namespace?: string): React.ComponentType {
// 首先尝试命名空间化的类型,回退到全局
const namespacedType = namespace ? ${namespace}:${type} : null;
return this.components.get(namespacedType) ?? this.components.get(type);
}
}
迁移路径:

向 ComponentMeta 添加 namespace 字段
更新所有插件注册
弃用非命名空间类型(记录警告)
在 v2.0 中移除非命名空间支持


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Jan 31, 2026 3:34am
objectui-components Ready Ready Preview, Comment Jan 31, 2026 3:34am
objectui-crm-app Error Error Jan 31, 2026 3:34am

Request Review

Copilot AI and others added 2 commits January 31, 2026 02:28
- Add namespace field to ComponentMeta type
- Update Registry.register() to support namespace option and construct full type as namespace:type
- Update Registry.get(), getConfig(), and has() to support namespace lookup with fallback to non-namespaced
- Add deprecation warning for non-namespaced registrations
- Add comprehensive tests for namespace functionality (23 tests, all passing)

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- plugin-grid: use 'plugin-grid' and 'view' namespaces
- plugin-aggrid: use 'plugin-aggrid' namespace
- plugin-charts: use 'plugin-charts' and 'chart' namespaces
- plugin-kanban: use 'plugin-kanban' namespace
- plugin-form: use 'plugin-form' namespace
- plugin-view: use 'plugin-view' and 'view' namespaces
- plugin-calendar: use 'plugin-calendar' namespace
- plugin-dashboard: use 'plugin-dashboard' namespace
- plugin-editor: use 'plugin-editor' namespace
- plugin-gantt: use 'plugin-gantt' namespace
- plugin-map: use 'plugin-map' namespace
- plugin-markdown: use 'plugin-markdown' namespace
- plugin-timeline: use 'plugin-timeline' namespace
- plugin-chatbot: use 'plugin-chatbot' namespace

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- components: Add 'ui' namespace to component registrations
- fields: Replace field: prefix with namespace: 'field' parameter
  - All field types now use field:name format via namespace parameter
  - Removed deprecated field: string prefix in favor of namespace approach
  - Maintains backward compatibility through fallback lookup

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Store namespaced components under both full type (namespace:name) and simple name
- This allows non-namespaced lookups to find namespaced components
- When namespace is explicitly provided, only look in that namespace (no fallback)
- Updated all Registry tests to reflect new behavior
- All 24 Registry tests now passing

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
- Fix toast.tsx: Move namespace from toast function call to ComponentRegistry metadata
- Fix fields/index.tsx: Remove duplicate field registrations (lines 871-900)
  - These were overwriting the wrapped versions registered earlier
  - Wrapped versions (with createFieldRenderer) are the correct approach

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add namespacing management for component registry Add namespace support to ComponentRegistry to prevent naming conflicts Jan 31, 2026
Copilot AI requested a review from hotlong January 31, 2026 02:55
@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Gzipped
components (index.js) 1760.39KB 416.50KB
core (index.js) 0.56KB 0.29KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 16.91KB 4.46KB
fields (index.js) 75.70KB 15.43KB
layout (index.js) 12.63KB 3.91KB
plugin-aggrid (AgGridImpl-BQ6tBvrq.js) 5.27KB 1.92KB
plugin-aggrid (ObjectAgGridImpl-CGFeGvOH.js) 11.44KB 3.52KB
plugin-aggrid (index-CLKYMco3.js) 19.20KB 4.89KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-calendar (index.js) 33.16KB 8.29KB
plugin-charts (AdvancedChartImpl-B4uEOmaM.js) 69.51KB 16.23KB
plugin-charts (BarChart-RKJxvg5Y.js) 535.74KB 134.11KB
plugin-charts (ChartImpl-BvFWgBig.js) 8.78KB 3.11KB
plugin-charts (index-DSlUZpyR.js) 12.67KB 3.91KB
plugin-charts (index.js) 0.21KB 0.16KB
plugin-chatbot (index.js) 18.39KB 5.22KB
plugin-dashboard (index.js) 11.99KB 3.82KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 14.46KB 4.65KB
plugin-gantt (index.js) 18.03KB 5.27KB
plugin-grid (index.js) 40.49KB 11.22KB
plugin-kanban (KanbanImpl-kbzXvR7a.js) 76.50KB 20.46KB
plugin-kanban (index-4iNpG2-h.js) 11.89KB 3.68KB
plugin-kanban (index.js) 0.18KB 0.15KB
plugin-map (index.js) 16.78KB 5.12KB
plugin-markdown (MarkdownImpl-9F_xYhwR.js) 256.79KB 64.50KB
plugin-markdown (index-DZS4FNIJ.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-timeline (index.js) 23.93KB 5.96KB
plugin-view (index.js) 16.68KB 4.93KB
react (LazyPluginLoader.js) 1.10KB 0.58KB
react (SchemaRenderer.js) 3.02KB 1.21KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 0.34KB 0.25KB
types (layout.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@github-actions
Copy link
Copy Markdown
Contributor

✅ All checks passed!

  • ✅ Type check passed
  • ✅ Tests passed
  • ✅ Lint check completed

Copilot AI and others added 2 commits January 31, 2026 03:08
Increase wait time from 100ms to 110ms in the second timeout to ensure the total elapsed time exceeds the 200ms TTL, accounting for JavaScript timer precision variability.

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Gzipped
components (index.js) 1760.39KB 416.50KB
core (index.js) 0.56KB 0.29KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 16.91KB 4.46KB
fields (index.js) 75.70KB 15.43KB
layout (index.js) 12.63KB 3.91KB
plugin-aggrid (AgGridImpl-BQ6tBvrq.js) 5.27KB 1.92KB
plugin-aggrid (ObjectAgGridImpl-CGFeGvOH.js) 11.44KB 3.52KB
plugin-aggrid (index-CLKYMco3.js) 19.20KB 4.89KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-calendar (index.js) 33.16KB 8.29KB
plugin-charts (AdvancedChartImpl-B4uEOmaM.js) 69.51KB 16.23KB
plugin-charts (BarChart-RKJxvg5Y.js) 535.74KB 134.11KB
plugin-charts (ChartImpl-BvFWgBig.js) 8.78KB 3.11KB
plugin-charts (index-DSlUZpyR.js) 12.67KB 3.91KB
plugin-charts (index.js) 0.21KB 0.16KB
plugin-chatbot (index.js) 18.39KB 5.22KB
plugin-dashboard (index.js) 11.99KB 3.82KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 14.46KB 4.65KB
plugin-gantt (index.js) 18.03KB 5.27KB
plugin-grid (index.js) 40.49KB 11.22KB
plugin-kanban (KanbanImpl-kbzXvR7a.js) 76.50KB 20.46KB
plugin-kanban (index-4iNpG2-h.js) 11.89KB 3.68KB
plugin-kanban (index.js) 0.18KB 0.15KB
plugin-map (index.js) 16.78KB 5.12KB
plugin-markdown (MarkdownImpl-9F_xYhwR.js) 256.79KB 64.50KB
plugin-markdown (index-DZS4FNIJ.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-timeline (index.js) 23.93KB 5.96KB
plugin-view (index.js) 16.68KB 4.93KB
react (LazyPluginLoader.js) 1.10KB 0.58KB
react (SchemaRenderer.js) 3.02KB 1.21KB
react (index.js) 0.39KB 0.25KB
react (index.test.js) 0.34KB 0.26KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 0.20KB 0.18KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (disclosure.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 0.34KB 0.25KB
types (layout.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@github-actions
Copy link
Copy Markdown
Contributor

✅ All checks passed!

  • ✅ Type check passed
  • ✅ Tests passed
  • ✅ Lint check completed

@hotlong hotlong marked this pull request as ready for review January 31, 2026 03:23
Copilot AI review requested due to automatic review settings January 31, 2026 03:23
@hotlong hotlong merged commit f7fc034 into main Jan 31, 2026
14 of 17 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces optional namespaces to ComponentRegistry registrations to reduce component type naming collisions across plugins, while maintaining backward compatibility for existing un-namespaced lookups.

Changes:

  • Added namespace?: string support to the core Registry and updated lookup APIs to accept an optional namespace.
  • Updated plugin, field, and multiple UI component registrations to pass a namespace in metadata.
  • Added comprehensive unit tests for namespaced registration/lookup behavior and adjusted a cache TTL test to reduce timing flakiness.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/core/src/registry/Registry.ts Adds namespace to ComponentMeta and implements namespaced register/get/has behavior with backward-compatible aliasing and deprecation warnings.
packages/core/src/registry/tests/Registry.test.ts Adds unit tests covering namespaced registration, lookup rules, and backward compatibility behavior.
packages/plugin-view/src/index.tsx Updates plugin-view registrations to use namespacing (including view namespace for the simple view alias).
packages/plugin-timeline/src/renderer.tsx Adds plugin-timeline namespace to the timeline component registration.
packages/plugin-markdown/src/index.tsx Adds plugin-markdown namespace to the markdown component registration.
packages/plugin-map/src/index.tsx Adds plugin-map namespace to the map component registration.
packages/plugin-kanban/src/index.tsx Adds plugin-kanban namespace to the kanban component registration.
packages/plugin-grid/src/index.tsx Adds plugin-grid namespace and migrates the view alias registration to namespace: 'view'.
packages/plugin-gantt/src/index.tsx Adds plugin-gantt namespace to the gantt component registration.
packages/plugin-form/src/index.tsx Adds plugin-form namespace to the object-form component registration.
packages/plugin-editor/src/index.tsx Adds plugin-editor namespace to the code editor component registration.
packages/plugin-dashboard/src/index.tsx Adds plugin-dashboard namespace to dashboard/metric registrations.
packages/plugin-chatbot/src/renderer.tsx Adds plugin-chatbot namespace to chatbot registration.
packages/plugin-charts/src/index.tsx Adds plugin-charts namespace to chart registrations and migrates an alias to a namespaced registration.
packages/plugin-calendar/src/index.tsx Adds plugin-calendar namespace to calendar component registration.
packages/plugin-aggrid/src/index.tsx Adds plugin-aggrid namespace to AG Grid registrations.
packages/fields/src/index.tsx Migrates field registrations to { namespace: 'field' } and removes explicit field:* prefixed registrations.
packages/components/src/renderers/layout/stack.tsx Adds ui namespace to Stack registration.
packages/components/src/renderers/layout/semantic.tsx Adds ui namespace to semantic layout tag registrations.
packages/components/src/renderers/layout/page.tsx Adds ui namespace to Page registration.
packages/components/src/renderers/layout/container.tsx Adds ui namespace to Container registration.
packages/components/src/renderers/layout/card.tsx Adds ui namespace to Card registration.
packages/components/src/renderers/form/input.tsx Adds ui namespace to Input registration.
packages/components/src/renderers/form/button.tsx Adds ui namespace to Button registration.
packages/components/src/renderers/feedback/toaster.tsx Adds ui namespace to Toaster registration.
packages/components/src/renderers/feedback/toast.tsx Adds ui namespace to Toast registration.
packages/components/src/renderers/data-display/statistic.tsx Adds ui namespace to Statistic registration.
packages/components/src/renderers/complex/data-table.tsx Adds ui namespace to DataTable registration.
packages/components/src/renderers/basic/span.tsx Adds ui namespace to Span registration.
packages/components/src/renderers/basic/separator.tsx Adds ui namespace to Separator registration.
packages/components/src/renderers/basic/icon.tsx Adds ui namespace to Icon registration.
packages/components/src/renderers/basic/div.tsx Adds ui namespace to Div registration.
packages/components/src/renderers/basic/button-group.tsx Adds ui namespace to Button Group registration.
packages/data-objectstack/src/cache/MetadataCache.test.ts Tweaks TTL timing to reduce flakiness and updates test comments accordingly.
Comments suppressed due to low confidence (1)

packages/plugin-grid/src/index.tsx:35

  • Registering the view alias as register('grid', ..., { namespace: 'view' }) will also write an un-namespaced grid entry (due to the registry’s backward-compat aliasing). That can overwrite the existing layout grid component registered in @object-ui/components (see packages/components/src/renderers/layout/grid.tsx) depending on import/plugin load order.

To avoid breaking type: 'grid' layouts, consider keeping this alias registered under the fully-qualified key ('view:grid') without creating a global grid alias, or introduce a registry option to disable the un-namespaced alias for specific registrations. Also update the comment below—this plugin now effectively does affect the grid key.

// Alias for view:grid
ComponentRegistry.register('grid', ObjectGridRenderer, {
  namespace: 'view',
  label: 'Data Grid',
  category: 'view'
});

// Note: 'grid' type is handled by @object-ui/components Grid layout component
// This plugin only handles 'object-grid' which integrates with ObjectQL/ObjectStack

Comment on lines +104 to 108
// Access again after another 110ms (total 210ms from first, ensuring TTL has passed)
await new Promise(resolve => setTimeout(resolve, 110));

// Should still be in cache because we're checking timestamp, not last accessed
// Actually, the implementation uses timestamp for expiration, not lastAccessed
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test’s description/comments say the cache uses a creation timestamp for expiration (i.e., TTL does not extend on cache hits), but the test name is still "should update timestamp on cache hit". Rename the test (or adjust the expectation) so the intent matches the behavior being asserted.

Copilot uses AI. Check for mistakes.
Comment on lines 65 to 68
ComponentRegistry.register('input', InputRenderer, {
namespace: 'ui',
label: 'Input Field',
inputs: [
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the base input registration was migrated to { namespace: 'ui' } here, but this file still registers other related input components (e.g., email, password) without a namespace. With the new registry behavior, those registrations will now emit deprecation warnings and can still collide with other packages’ component types.

Either migrate the remaining registrations in this file to the same ui namespace (preferred), or explicitly document/handle them as legacy un-namespaced aliases.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +70
if (!namespace && typeof console !== 'undefined' && console.warn) {
console.warn(
`[ObjectUI] Registering component "${type}" without a namespace is deprecated. ` +
`Please provide a namespace via the meta.namespace option. ` +
`Example: ComponentRegistry.register('${type}', component, { namespace: 'ui' })`
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecation warning currently triggers for any registration that omits meta.namespace, even when the type string is already in namespace:type form (e.g., page:header in @object-ui/layout). This will produce misleading warnings and noisy logs for existing namespaced-type registrations.

Consider treating type strings that already contain : as namespaced (skip the deprecation warning, and optionally parse them into { namespace, type } for consistent handling). Also consider gating this warning to non-production builds to avoid flooding production logs during initialization.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants