Skip to content

Commit ff5525c

Browse files
authored
Merge pull request #153 from objectstack-ai/copilot/remove-objectql-plugin-types
2 parents 26d5321 + 8ede2cd commit ff5525c

File tree

15 files changed

+844
-123
lines changed

15 files changed

+844
-123
lines changed

examples/showcase/enterprise-erp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@objectql/cli": "workspace:*",
4444
"@objectql/driver-sql": "workspace:*",
4545
"@objectql/platform-node": "workspace:*",
46+
"@objectstack/spec": "^0.2.0",
4647
"@types/jest": "^30.0.0",
4748
"jest": "^30.2.0",
4849
"ts-jest": "^29.4.6",

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 type { PluginDefinition, PluginContextData } from '@objectstack/spec';
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/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
},
2424
"dependencies": {
2525
"@objectql/types": "workspace:*",
26-
"@objectstack/spec": "^0.1.2",
26+
"@objectstack/spec": "^0.2.0",
2727
"@objectstack/runtime": "^0.1.1",
2828
"@objectstack/objectql": "^0.1.1",
2929
"js-yaml": "^4.1.0",

packages/foundation/core/src/app.ts

Lines changed: 103 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import {
1414
ObjectQLContextOptions,
1515
IObjectQL,
1616
ObjectQLConfig,
17-
ObjectQLPlugin,
1817
HookName,
1918
HookHandler,
2019
HookContext,
2120
ActionHandler,
2221
ActionContext,
2322
LoaderPlugin
2423
} from '@objectql/types';
24+
import type { PluginDefinition } from '@objectstack/spec';
2525
import { ObjectRepository } from './repository';
2626
// import { createDriverFromConnection } from './driver'; // REMOVE THIS
2727

@@ -37,7 +37,7 @@ export class ObjectQL implements IObjectQL {
3737
private remotes: string[] = [];
3838
private hooks: Record<string, HookEntry[]> = {};
3939
private actions: Record<string, ActionEntry> = {};
40-
private pluginsList: ObjectQLPlugin[] = [];
40+
private pluginsList: PluginDefinition[] = [];
4141

4242
// Store config for lazy loading in init()
4343
private config: ObjectQLConfig;
@@ -63,7 +63,7 @@ export class ObjectQL implements IObjectQL {
6363
}
6464
}
6565
}
66-
use(plugin: ObjectQLPlugin) {
66+
use(plugin: PluginDefinition) {
6767
this.pluginsList.push(plugin);
6868
}
6969

@@ -214,32 +214,111 @@ export class ObjectQL implements IObjectQL {
214214
}
215215
}
216216

217+
/**
218+
* Create a PluginContext from the current IObjectQL instance.
219+
* This adapts the IObjectQL interface to the PluginContext expected by @objectstack/spec plugins.
220+
*
221+
* **Current Implementation Status:**
222+
* - ✅ ql.object() - Fully functional, provides repository interface for data access
223+
* - ❌ ql.query() - Not implemented, throws error with guidance
224+
* - ❌ os.getCurrentUser() - Stub, returns null
225+
* - ❌ os.getConfig() - Stub, returns null
226+
* - ✅ logger - Functional, logs to console with [Plugin] prefix
227+
* - ❌ storage - Stub, no persistence implemented
228+
* - ✅ i18n - Basic fallback implementation
229+
* - ✅ metadata - Direct access to MetadataRegistry
230+
* - ❌ events - Empty object, event bus not implemented
231+
* - ❌ app.router - Stub methods, no actual routing
232+
* - ❌ app.scheduler - Not implemented (optional in spec)
233+
*
234+
* @private
235+
* @returns Minimal PluginContext adapter for current plugin system capabilities
236+
*/
237+
private createPluginContext(): import('@objectstack/spec').PluginContextData {
238+
// TODO: Implement full PluginContext conversion
239+
// For now, provide a minimal adapter that maps IObjectQL to PluginContext
240+
return {
241+
ql: {
242+
object: (name: string) => {
243+
// Return a repository-like interface
244+
// Cast to ObjectQL to access createContext
245+
return (this as ObjectQL).createContext({}).object(name);
246+
},
247+
query: async (soql: string) => {
248+
// TODO: Implement SOQL query execution
249+
// This requires implementing a SOQL parser and converter
250+
// For now, throw a descriptive error to guide users
251+
throw new Error(
252+
'SOQL queries are not yet supported in plugin context adapter. ' +
253+
'Please use context.ql.object(name).find() instead for data access.'
254+
);
255+
}
256+
},
257+
os: {
258+
getCurrentUser: async () => {
259+
// TODO: Get from context
260+
return null;
261+
},
262+
getConfig: async (key: string) => {
263+
// TODO: Implement config access
264+
return null;
265+
}
266+
},
267+
logger: {
268+
debug: (...args: any[]) => console.debug('[Plugin]', ...args),
269+
info: (...args: any[]) => console.info('[Plugin]', ...args),
270+
warn: (...args: any[]) => console.warn('[Plugin]', ...args),
271+
error: (...args: any[]) => console.error('[Plugin]', ...args),
272+
},
273+
storage: {
274+
get: async (key: string) => {
275+
// TODO: Implement plugin storage
276+
return null;
277+
},
278+
set: async (key: string, value: any) => {
279+
// TODO: Implement plugin storage
280+
},
281+
delete: async (key: string) => {
282+
// TODO: Implement plugin storage
283+
}
284+
},
285+
i18n: {
286+
t: (key: string, params?: any) => key, // Fallback: return key
287+
getLocale: () => 'en'
288+
},
289+
metadata: this.metadata,
290+
events: {
291+
// TODO: Implement event bus
292+
},
293+
app: {
294+
router: {
295+
get: (path: string, handler: (...args: unknown[]) => unknown, ...args: unknown[]) => {
296+
// TODO: Implement router registration
297+
},
298+
post: (path: string, handler: (...args: unknown[]) => unknown, ...args: unknown[]) => {
299+
// TODO: Implement router registration
300+
},
301+
use: (path: string | undefined, handler: (...args: unknown[]) => unknown, ...args: unknown[]) => {
302+
// TODO: Implement middleware registration
303+
}
304+
},
305+
scheduler: undefined // Optional in spec
306+
}
307+
};
308+
}
309+
217310
async init() {
218311
// 0. Init Plugins (This allows plugins to register custom loaders)
219312
for (const plugin of this.pluginsList) {
220-
console.log(`Initializing plugin '${plugin.name}'...`);
313+
const pluginId = plugin.id || 'unknown';
221314

222-
let app: IObjectQL = this;
223-
const pkgName = (plugin as any)._packageName;
224-
225-
if (pkgName) {
226-
app = new Proxy(this, {
227-
get(target, prop) {
228-
if (prop === 'on') {
229-
return (event: HookName, obj: string, handler: HookHandler) =>
230-
target.on(event, obj, handler, pkgName);
231-
}
232-
if (prop === 'registerAction') {
233-
return (obj: string, act: string, handler: ActionHandler) =>
234-
target.registerAction(obj, act, handler, pkgName);
235-
}
236-
const value = (target as any)[prop];
237-
return typeof value === 'function' ? value.bind(target) : value;
238-
}
239-
});
315+
console.log(`Initializing plugin '${pluginId}'...`);
316+
317+
// Call onEnable hook if it exists
318+
if (plugin.onEnable) {
319+
const context = this.createPluginContext();
320+
await plugin.onEnable(context);
240321
}
241-
242-
await plugin.setup(app);
243322
}
244323

245324
// Packages, Presets, Source, Objects loading logic removed from Core.

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

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
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, HookContext, ActionContext, Metadata } from '@objectql/types';
12+
import type { PluginDefinition } from '@objectstack/spec';
1213

1314
const todoObject: ObjectConfig = {
1415
name: 'todo',
@@ -65,9 +66,9 @@ describe('ObjectQL App', () => {
6566
});
6667

6768
it('should accept plugin instances', () => {
68-
const mockPlugin: ObjectQLPlugin = {
69-
name: 'test-plugin',
70-
setup: jest.fn()
69+
const mockPlugin: PluginDefinition = {
70+
id: 'test-plugin',
71+
onEnable: jest.fn()
7172
};
7273
const app = new ObjectQL({
7374
datasources: {},
@@ -291,10 +292,10 @@ describe('ObjectQL App', () => {
291292

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

300301
const app = new ObjectQL({
@@ -303,43 +304,19 @@ describe('ObjectQL App', () => {
303304
});
304305

305306
await app.init();
306-
expect(setupFn).toHaveBeenCalledWith(app);
307+
expect(onEnableFn).toHaveBeenCalled();
307308
});
308309

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

315316
const app = new ObjectQL({ datasources: {} });
316317
app.use(mockPlugin);
317318
expect(app).toBeDefined();
318319
});
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-
});
343320
});
344321

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

packages/foundation/platform-node/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"dependencies": {
2323
"@objectql/types": "workspace:*",
2424
"@objectql/core": "workspace:*",
25+
"@objectstack/spec": "^0.2.0",
2526
"fast-glob": "^3.3.2",
2627
"js-yaml": "^4.1.1"
2728
},

0 commit comments

Comments
 (0)