Skip to content

Commit b45df71

Browse files
Copilothotlong
andcommitted
fix: improve type safety in plugin interfaces per code review
- Use unknown[] instead of any for logger args in PluginContext - Type kernel.use() parameter as AppMetadataPlugin | Record<string, unknown> - Use PluginContext type in install() method and all example plugins - Import PluginContext in CRMPlugin, TodoPlugin, KitchenSinkPlugin Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent e6f950d commit b45df71

File tree

6 files changed

+44
-9
lines changed

6 files changed

+44
-9
lines changed

ROADMAP.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ObjectUI Development Roadmap
22

3-
> **Last Updated:** March 2, 2026
3+
> **Last Updated:** March 4, 2026
44
> **Current Version:** v0.5.x
55
> **Spec Version:** @objectstack/spec v3.2.0
66
> **Client Version:** @objectstack/client v3.2.0
@@ -1164,6 +1164,38 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th
11641164
- [ ] Conflict resolution on reconnection wired into Console flow
11651165
- [ ] Optimistic updates with TransactionManager state application
11661166

1167+
### P2.6 Plugin Modularization & Dynamic Management
1168+
1169+
> **Status:** Phase 1 complete — Plugin class standard, install/uninstall API, example plugin classes.
1170+
1171+
Plugin architecture refactoring to support true modular development, plugin isolation, and dynamic plugin install/uninstall at runtime.
1172+
1173+
**Phase 1 — Plugin Class Standard & Example Plugins ✅**
1174+
- [x] Define `AppMetadataPlugin` interface in `@object-ui/types` (name, version, type, description, init/start/stop/getConfig)
1175+
- [x] Define `PluginContext` interface for lifecycle hook context (logger, kernel)
1176+
- [x] Add `install(plugin)` / `uninstall(pluginName)` convenience methods to `PluginSystem` in `@object-ui/core`
1177+
- [x] Create `CRMPlugin` with full lifecycle (init/start/stop/getConfig) — `examples/crm/plugin.ts`
1178+
- [x] Create `TodoPlugin``examples/todo/plugin.ts`
1179+
- [x] Create `KitchenSinkPlugin``examples/kitchen-sink/plugin.ts`
1180+
- [x] Update `package.json` exports for all example apps (`./plugin` entry point)
1181+
- [x] Refactor root `objectstack.config.ts` to use plugin-based config collection via `getConfig()`
1182+
- [x] Unit tests for `install()` / `uninstall()` (5 new tests, 18 total in PluginSystem)
1183+
1184+
**Phase 2 — Dynamic Plugin Loading (Planned)**
1185+
- [ ] Hot-reload / lazy loading of plugins for development
1186+
- [ ] Runtime plugin discovery and loading from registry
1187+
- [ ] Plugin dependency graph visualization in Console
1188+
1189+
**Phase 3 — Plugin Identity & Isolation (Planned)**
1190+
- [ ] Preserve origin plugin metadata on objects, actions, dashboards for runtime inspection
1191+
- [ ] Per-plugin i18n namespace support
1192+
- [ ] Per-plugin permissions and data isolation
1193+
1194+
**Phase 4 — Cross-Repo Plugin Ecosystem (Planned)**
1195+
- [ ] Plugin marketplace / registry for third-party plugins
1196+
- [ ] Plugin publish/validate tooling (spec v3.0.9 `PluginBuildOptions`, `PluginPublishOptions`)
1197+
- [ ] Cross-repo plugin loading from npm packages
1198+
11671199
---
11681200

11691201
## 🔮 P3 — Future Vision (Deferred)

examples/crm/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* import { crmConfig } from '@object-ui/example-crm/plugin';
1919
*/
2020

21+
import type { PluginContext } from '@object-ui/types';
2122
import config from './objectstack.config';
2223

2324
/** Raw CRM stack configuration for direct merging */
@@ -39,7 +40,7 @@ export class CRMPlugin {
3940
// No initialization needed
4041
}
4142

42-
async start(ctx: any) {
43+
async start(ctx: PluginContext) {
4344
const logger = ctx.logger || console;
4445

4546
try {

examples/kitchen-sink/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* import { kitchenSinkConfig } from '@object-ui/example-kitchen-sink/plugin';
1919
*/
2020

21+
import type { PluginContext } from '@object-ui/types';
2122
import config from './objectstack.config';
2223

2324
/** Raw Kitchen Sink stack configuration for direct merging */
@@ -39,7 +40,7 @@ export class KitchenSinkPlugin {
3940
// No initialization needed
4041
}
4142

42-
async start(ctx: any) {
43+
async start(ctx: PluginContext) {
4344
const logger = ctx.logger || console;
4445

4546
try {

examples/todo/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* import { todoConfig } from '@object-ui/example-todo/plugin';
1919
*/
2020

21+
import type { PluginContext } from '@object-ui/types';
2122
import config from './objectstack.config';
2223

2324
/** Raw Todo stack configuration for direct merging */
@@ -39,7 +40,7 @@ export class TodoPlugin {
3940
// No initialization needed
4041
}
4142

42-
async start(ctx: any) {
43+
async start(ctx: PluginContext) {
4344
const logger = ctx.logger || console;
4445

4546
try {

packages/core/src/registry/PluginSystem.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import type { Registry } from './Registry.js';
10-
import type { PluginScope, PluginScopeConfig, AppMetadataPlugin } from '@object-ui/types';
10+
import type { PluginScope, PluginScopeConfig, AppMetadataPlugin, PluginContext } from '@object-ui/types';
1111
import { PluginScopeImpl } from './PluginScopeImpl.js';
1212

1313
export interface PluginDefinition {
@@ -169,7 +169,7 @@ export class PluginSystem {
169169
* @param registry - The component registry
170170
* @param ctx - Optional context passed to the plugin's start() hook
171171
*/
172-
async install(plugin: AppMetadataPlugin, registry: Registry, ctx?: Record<string, any>): Promise<void> {
172+
async install(plugin: AppMetadataPlugin, registry: Registry, ctx?: PluginContext): Promise<void> {
173173
if (this.loaded.has(plugin.name)) {
174174
console.warn(`Plugin "${plugin.name}" is already installed. Skipping.`);
175175
return;

packages/types/src/plugin-scope.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,10 @@ export type PluginEventHandler = (data?: any) => void;
189189
*/
190190
export interface PluginContext {
191191
/** Logger instance (falls back to console) */
192-
logger?: { info: (...args: any[]) => void; warn: (...args: any[]) => void; error: (...args: any[]) => void };
192+
logger?: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void };
193193
/** Kernel/runtime reference for plugin registration */
194-
kernel?: { use?: (plugin: any) => Promise<void> | void };
195-
[key: string]: any;
194+
kernel?: { use?: (plugin: AppMetadataPlugin | Record<string, unknown>) => Promise<void> | void };
195+
[key: string]: unknown;
196196
}
197197

198198
/**

0 commit comments

Comments
 (0)