Skip to content

Commit 00321c7

Browse files
Copilothotlong
andcommitted
refactor: Address code review feedback - improve type safety and plugin detection
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 7d35ed8 commit 00321c7

4 files changed

Lines changed: 45 additions & 18 deletions

File tree

packages/runtime/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Export core engine
22
export { ObjectQL, SchemaRegistry } from '@objectstack/objectql';
33
export { ObjectStackKernel } from './kernel';
4-
export { ObjectQLPlugin } from './objectql-plugin';
4+
export { ObjectQLPlugin, OBJECTQL_PLUGIN_MARKER } from './objectql-plugin';
55
export { ObjectStackRuntimeProtocol } from './protocol';
66

77
export * from './types';

packages/runtime/src/kernel.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ServiceObject } from '@objectstack/spec/data';
22
import { SchemaRegistry, ObjectQL } from '@objectstack/objectql';
3+
import { OBJECTQL_PLUGIN_MARKER } from './objectql-plugin';
34

45
/**
56
* ObjectStack Kernel (Microkernel)
@@ -8,16 +9,16 @@ import { SchemaRegistry, ObjectQL } from '@objectstack/objectql';
89
* plugins, and the core ObjectQL engine.
910
*/
1011
export class ObjectStackKernel {
11-
public ql!: ObjectQL; // Will be set by ObjectQLPlugin or fallback initialization
12+
public ql?: ObjectQL; // Will be set by ObjectQLPlugin or fallback initialization
1213
private plugins: any[];
1314

1415
constructor(plugins: any[] = []) {
1516
this.plugins = plugins;
1617

17-
// Check if any plugin provides ObjectQL via install method
18-
// If not, initialize it as a fallback for backward compatibility
18+
// Check if any plugin provides ObjectQL via the plugin marker
19+
// This is more robust than string matching on name
1920
const hasObjectQLPlugin = plugins.some(p =>
20-
p && typeof p === 'object' && 'install' in p && p.name?.includes('objectql')
21+
p && typeof p === 'object' && OBJECTQL_PLUGIN_MARKER in p
2122
);
2223

2324
if (!hasObjectQLPlugin) {
@@ -29,6 +30,17 @@ export class ObjectStackKernel {
2930
}
3031
}
3132

33+
/**
34+
* Ensure ObjectQL engine is initialized
35+
* @throws Error if ObjectQL is not available
36+
*/
37+
private ensureObjectQL(): ObjectQL {
38+
if (!this.ql) {
39+
throw new Error('[Kernel] ObjectQL engine not initialized. Ensure ObjectQLPlugin is registered or kernel is properly initialized.');
40+
}
41+
return this.ql;
42+
}
43+
3244
async start() {
3345
console.log('[Kernel] Starting...');
3446

@@ -64,13 +76,13 @@ export class ObjectStackKernel {
6476
// @ts-ignore
6577
const { InMemoryDriver } = await import('@objectstack/driver-memory');
6678
const driver = new InMemoryDriver();
67-
this.ql.registerDriver(driver);
79+
this.ensureObjectQL().registerDriver(driver);
6880
} catch (e) {
6981
// Ignore if not present
7082
}
7183

7284
// 2. Initialize Engine
73-
await this.ql.init();
85+
await this.ensureObjectQL().init();
7486

7587

7688
// 3. Seed Data
@@ -112,11 +124,11 @@ export class ObjectStackKernel {
112124
for (const seed of app.data) {
113125
try {
114126
// Check if data exists
115-
const existing = await this.ql.find(seed.object, { top: 1 });
127+
const existing = await this.ensureObjectQL().find(seed.object, { top: 1 });
116128
if (existing.length === 0) {
117129
console.log(`[Kernel] Inserting ${seed.records.length} records into ${seed.object}`);
118130
for (const record of seed.records) {
119-
await this.ql.insert(seed.object, record);
131+
await this.ensureObjectQL().insert(seed.object, record);
120132
}
121133
}
122134
} catch (e) {
@@ -134,30 +146,30 @@ export class ObjectStackKernel {
134146
// Forward methods to ObjectQL
135147
async find(objectName: string, query: any) {
136148
this.ensureSchema(objectName);
137-
const results = await this.ql.find(objectName, { top: 100 });
149+
const results = await this.ensureObjectQL().find(objectName, { top: 100 });
138150
return { value: results, count: results.length };
139151
}
140152

141153
async get(objectName: string, id: string) {
142154
this.ensureSchema(objectName);
143155
// Find One
144-
const results = await this.ql.find(objectName, { top: 1 }); // Mock implementation
156+
const results = await this.ensureObjectQL().find(objectName, { top: 1 }); // Mock implementation
145157
return results[0];
146158
}
147159

148160
async create(objectName: string, data: any) {
149161
this.ensureSchema(objectName);
150-
return this.ql.insert(objectName, data);
162+
return this.ensureObjectQL().insert(objectName, data);
151163
}
152164

153165
async update(objectName: string, id: string, data: any) {
154166
this.ensureSchema(objectName);
155-
return this.ql.update(objectName, id, data);
167+
return this.ensureObjectQL().update(objectName, id, data);
156168
}
157169

158170
async delete(objectName: string, id: string) {
159171
this.ensureSchema(objectName);
160-
return this.ql.delete(objectName, id);
172+
return this.ensureObjectQL().delete(objectName, id);
161173
}
162174

163175
// [New Methods for ObjectUI]
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { ObjectQL } from '@objectstack/objectql';
22
import { RuntimePlugin, RuntimeContext } from '@objectstack/types';
33

4+
/**
5+
* Symbol to identify ObjectQL plugins
6+
*/
7+
export const OBJECTQL_PLUGIN_MARKER = Symbol('objectql-plugin');
8+
49
/**
510
* ObjectQL Engine Plugin
611
*
@@ -10,21 +15,30 @@ import { RuntimePlugin, RuntimeContext } from '@objectstack/types';
1015
export class ObjectQLPlugin implements RuntimePlugin {
1116
name = 'com.objectstack.engine.objectql';
1217

18+
// Mark this as an ObjectQL plugin for reliable detection
19+
readonly [OBJECTQL_PLUGIN_MARKER] = true;
20+
1321
private ql: ObjectQL;
1422

1523
constructor(ql?: ObjectQL, hostContext?: Record<string, any>) {
1624
// Allow passing existing ObjectQL instance or create a new one
17-
this.ql = ql || new ObjectQL(hostContext || {
18-
env: process.env.NODE_ENV || 'development'
19-
});
25+
// Note: If 'ql' is provided, 'hostContext' is ignored
26+
// To create a new instance with custom context, pass only hostContext
27+
if (ql) {
28+
this.ql = ql;
29+
} else {
30+
this.ql = new ObjectQL(hostContext || {
31+
env: process.env.NODE_ENV || 'development'
32+
});
33+
}
2034
}
2135

2236
/**
2337
* Install the ObjectQL engine into the kernel
2438
*/
2539
async install(ctx: RuntimeContext) {
2640
// Attach the ObjectQL engine to the kernel
27-
(ctx.engine as any).ql = this.ql;
41+
ctx.engine.ql = this.ql;
2842
console.log('[ObjectQLPlugin] ObjectQL engine registered');
2943
}
3044
}

packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
export interface IKernel {
44
// We can add specific methods here that plugins are allowed to call
55
// forcing a stricter contract than exposing the whole class.
6+
ql?: any; // ObjectQL instance (optional to support initialization phase)
67
start(): Promise<void>;
78
// ... expose other needed public methods
89
[key: string]: any;

0 commit comments

Comments
 (0)