Skip to content

Commit 4dfbede

Browse files
Copilothuangyiirene
andcommitted
Remove backward compatibility, use only @objectstack/spec plugin types
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
1 parent f576069 commit 4dfbede

8 files changed

Lines changed: 57 additions & 158 deletions

File tree

examples/showcase/enterprise-erp/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ObjectQL } from '@objectql/core';
1010
import { SqlDriver } from '@objectql/driver-sql';
1111
import { ObjectLoader } from '@objectql/platform-node';
1212
import * as path from 'path';
13-
import { AuditLogPlugin } from './plugins/audit/audit.plugin';
13+
import AuditLogPlugin from './plugins/audit/audit.plugin';
1414

1515
async function main() {
1616
console.log("🚀 Starting Enterprise ERP Showcase...");
@@ -26,7 +26,7 @@ async function main() {
2626
});
2727

2828
// Register Plugin
29-
app.use(new AuditLogPlugin());
29+
app.use(AuditLogPlugin);
3030

3131
const loader = new ObjectLoader(app.metadata);
3232
await loader.load(path.join(__dirname));

examples/showcase/enterprise-erp/src/plugins/audit/audit.plugin.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,26 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
import { ObjectQLPlugin, IObjectQL, MutationHookContext } from '@objectql/types';
9+
import { PluginDefinition, PluginContextData } from '@objectql/types';
1010

11-
export class AuditLogPlugin implements ObjectQLPlugin {
12-
name = 'audit-log';
11+
const AuditLogPlugin: PluginDefinition = {
12+
id: 'audit-log',
13+
14+
onEnable: async (context: PluginContextData) => {
15+
console.log('[AuditLogPlugin] Enabling...');
1316

14-
setup(app: IObjectQL) {
15-
console.log('[AuditLogPlugin] Setting up...');
16-
17-
// 1. Listen to all 'afterCreate' events
18-
app.on('afterCreate', '*', async (ctx) => {
19-
// Narrow down context type or use assertion since 'afterCreate' is Mutation
20-
const mutationCtx = ctx as MutationHookContext;
21-
const userId = mutationCtx.user?.id || 'Guest';
22-
console.log(`[Audit] Created ${mutationCtx.objectName} (ID: ${mutationCtx.id}) by User ${userId}`);
23-
});
24-
25-
// 2. Listen to all 'afterDelete' events
26-
app.on('afterDelete', '*', async (ctx) => {
27-
const mutationCtx = ctx as MutationHookContext;
28-
console.log(`[Audit] Deleted ${mutationCtx.objectName} (ID: ${mutationCtx.id})`);
29-
});
17+
// TODO: Register event handlers using the new context API
18+
// The PluginContextData provides:
19+
// - context.events for event handling
20+
// - context.ql for data access
21+
// - context.logger for logging
22+
23+
// For now, we'll just log that the plugin is enabled
24+
context.logger.info('[AuditLogPlugin] Plugin enabled');
25+
26+
// Note: The new plugin system uses context.events instead of app.on()
27+
// This will need to be implemented when the events API is available
3028
}
31-
}
29+
};
30+
31+
export default AuditLogPlugin;

examples/showcase/enterprise-erp/src/plugins/audit/index.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,5 @@
77
*/
88

99
// Export the plugin for external usage
10-
export * from './audit.plugin';
11-
12-
// Make it the default export as well for easier consumption
13-
import { AuditLogPlugin } from './audit.plugin';
10+
import AuditLogPlugin from './audit.plugin';
1411
export default AuditLogPlugin;

packages/foundation/core/src/app.ts

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
ObjectQLContextOptions,
1515
IObjectQL,
1616
ObjectQLConfig,
17-
ObjectQLPlugin,
1817
PluginDefinition,
1918
HookName,
2019
HookHandler,
@@ -38,7 +37,7 @@ export class ObjectQL implements IObjectQL {
3837
private remotes: string[] = [];
3938
private hooks: Record<string, HookEntry[]> = {};
4039
private actions: Record<string, ActionEntry> = {};
41-
private pluginsList: Array<ObjectQLPlugin | PluginDefinition> = [];
40+
private pluginsList: PluginDefinition[] = [];
4241

4342
// Store config for lazy loading in init()
4443
private config: ObjectQLConfig;
@@ -64,7 +63,7 @@ export class ObjectQL implements IObjectQL {
6463
}
6564
}
6665
}
67-
use(plugin: ObjectQLPlugin | PluginDefinition) {
66+
use(plugin: PluginDefinition) {
6867
this.pluginsList.push(plugin);
6968
}
7069

@@ -221,7 +220,7 @@ export class ObjectQL implements IObjectQL {
221220
*
222221
* @private
223222
*/
224-
private createPluginContext(app: IObjectQL): import('@objectstack/spec').PluginContextData {
223+
private createPluginContext(): import('@objectstack/spec').PluginContextData {
225224
// TODO: Implement full PluginContext conversion
226225
// For now, provide a minimal adapter that maps IObjectQL to PluginContext
227226
return {
@@ -273,7 +272,7 @@ export class ObjectQL implements IObjectQL {
273272
t: (key: string, params?: any) => key, // Fallback: return key
274273
getLocale: () => 'en'
275274
},
276-
metadata: app.metadata,
275+
metadata: this.metadata,
277276
events: {
278277
// TODO: Implement event bus
279278
},
@@ -297,46 +296,14 @@ export class ObjectQL implements IObjectQL {
297296
async init() {
298297
// 0. Init Plugins (This allows plugins to register custom loaders)
299298
for (const plugin of this.pluginsList) {
300-
// Type guard: check if it's a legacy plugin or new PluginDefinition
301-
const isLegacyPlugin = 'setup' in plugin && typeof plugin.setup === 'function';
302-
const pluginId = isLegacyPlugin ? (plugin as ObjectQLPlugin).name : (plugin as PluginDefinition).id || 'unknown';
299+
const pluginId = plugin.id || 'unknown';
303300

304301
console.log(`Initializing plugin '${pluginId}'...`);
305302

306-
let app: IObjectQL = this;
307-
const pkgName = (plugin as any)._packageName;
308-
309-
if (pkgName) {
310-
app = new Proxy(this, {
311-
get(target, prop) {
312-
if (prop === 'on') {
313-
return (event: HookName, obj: string, handler: HookHandler) =>
314-
target.on(event, obj, handler, pkgName);
315-
}
316-
if (prop === 'registerAction') {
317-
return (obj: string, act: string, handler: ActionHandler) =>
318-
target.registerAction(obj, act, handler, pkgName);
319-
}
320-
const value = (target as any)[prop];
321-
return typeof value === 'function' ? value.bind(target) : value;
322-
}
323-
});
324-
}
325-
326-
if (isLegacyPlugin) {
327-
// Legacy plugin with setup() method
328-
await (plugin as ObjectQLPlugin).setup(app);
329-
} else {
330-
// New plugin with lifecycle hooks
331-
const newPlugin = plugin as PluginDefinition;
332-
333-
// Call onEnable hook if it exists
334-
if (newPlugin.onEnable) {
335-
// TODO: Build proper PluginContext from IObjectQL
336-
// For now, we'll create a minimal context adapter
337-
const context = this.createPluginContext(app);
338-
await newPlugin.onEnable(context);
339-
}
303+
// Call onEnable hook if it exists
304+
if (plugin.onEnable) {
305+
const context = this.createPluginContext();
306+
await plugin.onEnable(context);
340307
}
341308
}
342309

packages/foundation/core/test/app.test.ts

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import { ObjectQL } from '../src/app';
1010
import { MockDriver } from './mock-driver';
11-
import { ObjectConfig, ObjectQLPlugin, HookContext, ActionContext, Metadata } from '@objectql/types';
11+
import { ObjectConfig, PluginDefinition, HookContext, ActionContext, Metadata } from '@objectql/types';
1212

1313
const todoObject: ObjectConfig = {
1414
name: 'todo',
@@ -65,9 +65,9 @@ describe('ObjectQL App', () => {
6565
});
6666

6767
it('should accept plugin instances', () => {
68-
const mockPlugin: ObjectQLPlugin = {
69-
name: 'test-plugin',
70-
setup: jest.fn()
68+
const mockPlugin: PluginDefinition = {
69+
id: 'test-plugin',
70+
onEnable: jest.fn()
7171
};
7272
const app = new ObjectQL({
7373
datasources: {},
@@ -291,10 +291,10 @@ describe('ObjectQL App', () => {
291291

292292
describe('Plugin System', () => {
293293
it('should initialize plugins on init', async () => {
294-
const setupFn = jest.fn();
295-
const mockPlugin: ObjectQLPlugin = {
296-
name: 'test-plugin',
297-
setup: setupFn
294+
const onEnableFn = jest.fn();
295+
const mockPlugin: PluginDefinition = {
296+
id: 'test-plugin',
297+
onEnable: onEnableFn
298298
};
299299

300300
const app = new ObjectQL({
@@ -303,43 +303,19 @@ describe('ObjectQL App', () => {
303303
});
304304

305305
await app.init();
306-
expect(setupFn).toHaveBeenCalledWith(app);
306+
expect(onEnableFn).toHaveBeenCalled();
307307
});
308308

309309
it('should use plugin method', () => {
310-
const mockPlugin: ObjectQLPlugin = {
311-
name: 'test-plugin',
312-
setup: jest.fn()
310+
const mockPlugin: PluginDefinition = {
311+
id: 'test-plugin',
312+
onEnable: jest.fn()
313313
};
314314

315315
const app = new ObjectQL({ datasources: {} });
316316
app.use(mockPlugin);
317317
expect(app).toBeDefined();
318318
});
319-
320-
it('should provide package-scoped proxy for plugins', async () => {
321-
let capturedApp: any;
322-
const mockPlugin: ObjectQLPlugin = {
323-
name: 'test-plugin',
324-
setup: async (app) => {
325-
capturedApp = app;
326-
}
327-
};
328-
(mockPlugin as any)._packageName = 'test-package';
329-
330-
const app = new ObjectQL({
331-
datasources: {},
332-
plugins: [mockPlugin]
333-
});
334-
app.registerObject(todoObject);
335-
336-
await app.init();
337-
338-
// Test proxied methods
339-
const handler = jest.fn();
340-
capturedApp.on('beforeCreate', 'todo', handler);
341-
capturedApp.registerAction('todo', 'test', handler);
342-
});
343319
});
344320

345321
describe('Initialization', () => {

packages/foundation/platform-node/src/plugin.ts

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
import { ObjectQLPlugin, PluginDefinition } from '@objectql/types';
9+
import { PluginDefinition } from '@objectql/types';
1010

11-
export function loadPlugin(packageName: string): ObjectQLPlugin | PluginDefinition {
11+
export function loadPlugin(packageName: string): PluginDefinition {
1212
let mod: any;
1313
try {
1414
const modulePath = require.resolve(packageName, { paths: [process.cwd()] });
@@ -17,13 +17,8 @@ export function loadPlugin(packageName: string): ObjectQLPlugin | PluginDefiniti
1717
throw new Error(`Failed to resolve plugin '${packageName}': ${e}`);
1818
}
1919

20-
// Helper to check if candidate is a legacy ObjectQLPlugin
21-
const isLegacyPlugin = (candidate: any): candidate is ObjectQLPlugin => {
22-
return candidate && typeof candidate.setup === 'function' && candidate.name;
23-
};
24-
25-
// Helper to check if candidate is a new PluginDefinition
26-
const isNewPlugin = (candidate: any): candidate is PluginDefinition => {
20+
// Helper to check if candidate is a PluginDefinition
21+
const isPlugin = (candidate: any): candidate is PluginDefinition => {
2722
return candidate && (
2823
// Check for any lifecycle method
2924
typeof candidate.onEnable === 'function' ||
@@ -39,33 +34,25 @@ export function loadPlugin(packageName: string): ObjectQLPlugin | PluginDefiniti
3934
};
4035

4136
// Helper to find plugin instance
42-
const findPlugin = (candidate: any): ObjectQLPlugin | PluginDefinition | undefined => {
37+
const findPlugin = (candidate: any): PluginDefinition | undefined => {
4338
if (!candidate) return undefined;
4439

45-
// 1. Check if it's a new PluginDefinition
46-
if (isNewPlugin(candidate)) {
40+
// 1. Check if it's a PluginDefinition
41+
if (isPlugin(candidate)) {
4742
return candidate;
4843
}
4944

50-
// 2. Try treating as Class (legacy)
45+
// 2. Try treating as Class
5146
if (typeof candidate === 'function') {
5247
try {
5348
const inst = new candidate();
54-
if (isLegacyPlugin(inst)) {
55-
return inst;
56-
}
57-
if (isNewPlugin(inst)) {
49+
if (isPlugin(inst)) {
5850
return inst;
5951
}
6052
} catch (e) {
6153
// Not a constructor or instantiation failed
6254
}
6355
}
64-
65-
// 3. Try treating as Instance (legacy)
66-
if (isLegacyPlugin(candidate)) {
67-
return candidate;
68-
}
6956

7057
return undefined;
7158
};
@@ -86,6 +73,6 @@ export function loadPlugin(packageName: string): ObjectQLPlugin | PluginDefiniti
8673
return instance;
8774
} else {
8875
console.error(`[PluginLoader] Failed to find plugin in '${packageName}'. Exports:`, Object.keys(mod));
89-
throw new Error(`Plugin '${packageName}' must export a PluginDefinition (with lifecycle hooks) or legacy ObjectQLPlugin (with setup method).`);
76+
throw new Error(`Plugin '${packageName}' must export a PluginDefinition with lifecycle hooks (onEnable, onDisable, etc.).`);
9077
}
9178
}

packages/foundation/types/src/config.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import { MetadataRegistry } from "./registry";
1010
import { Driver } from "./driver";
1111
import { ObjectConfig } from "./object";
12-
import { ObjectQLPlugin, PluginDefinition } from "./plugin";
12+
import { PluginDefinition } from "./plugin";
1313

1414
export interface ObjectQLConfig {
1515
registry?: MetadataRegistry;
@@ -40,11 +40,10 @@ export interface ObjectQLConfig {
4040
/**
4141
* List of plugins to load.
4242
* Can be:
43-
* - An instance of ObjectQLPlugin (legacy, deprecated)
44-
* - An instance of PluginDefinition (from @objectstack/spec, recommended)
43+
* - An instance of PluginDefinition (from @objectstack/spec)
4544
* - A package name string
4645
*/
47-
plugins?: (ObjectQLPlugin | PluginDefinition | string)[];
46+
plugins?: (PluginDefinition | string)[];
4847
/**
4948
* List of remote ObjectQL instances to connect to.
5049
* e.g. ["http://user-service:3000", "http://order-service:3000"]

packages/foundation/types/src/plugin.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,9 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
import { IObjectQL } from './app';
10-
119
// Import plugin types from @objectstack/spec (the protocol standard)
1210
export type {
1311
PluginContextData,
1412
PluginLifecycleHooks,
1513
PluginDefinition,
1614
} from '@objectstack/spec';
17-
18-
/**
19-
* @deprecated Legacy plugin interface. Use PluginDefinition from @objectstack/spec instead.
20-
*
21-
* This interface is maintained for backward compatibility with existing plugins.
22-
* New plugins should implement PluginDefinition with lifecycle hooks (onEnable, onDisable, etc.)
23-
* instead of the legacy setup() method.
24-
*
25-
* @example Migration from legacy to new:
26-
* // Old way (deprecated):
27-
* export class MyPlugin implements ObjectQLPlugin {
28-
* name = 'my-plugin';
29-
* setup(app: IObjectQL) { ... }
30-
* }
31-
*
32-
* // New way (recommended):
33-
* export default {
34-
* id: 'my-plugin',
35-
* onEnable: async (context) => { ... }
36-
* } satisfies PluginDefinition;
37-
*/
38-
export interface ObjectQLPlugin {
39-
name: string;
40-
setup(app: IObjectQL): void | Promise<void>;
41-
}

0 commit comments

Comments
 (0)