diff --git a/content/docs/references/system/data-engine.mdx b/content/docs/references/system/data-engine.mdx
new file mode 100644
index 000000000..9c7c50f3b
--- /dev/null
+++ b/content/docs/references/system/data-engine.mdx
@@ -0,0 +1,55 @@
+---
+title: Data Engine
+description: Data Engine protocol schemas
+---
+
+# Data Engine
+
+
+**Source:** `packages/spec/src/system/data-engine.zod.ts`
+
+
+## TypeScript Usage
+
+```typescript
+import { DataEngineSchema, DataEngineFilterSchema, DataEngineQueryOptionsSchema } from '@objectstack/spec/system';
+import type { DataEngine, DataEngineFilter, DataEngineQueryOptions } from '@objectstack/spec/system';
+
+// Validate data
+const result = DataEngineSchema.parse(data);
+```
+
+---
+
+## DataEngine
+
+Data Engine Interface
+
+### Properties
+
+| Property | Type | Required | Description |
+| :--- | :--- | :--- | :--- |
+
+---
+
+## DataEngineFilter
+
+Data Engine query filter conditions
+
+---
+
+## DataEngineQueryOptions
+
+Query options for IDataEngine.find() operations
+
+### Properties
+
+| Property | Type | Required | Description |
+| :--- | :--- | :--- | :--- |
+| **filter** | `Record` | optional | Data Engine query filter conditions |
+| **select** | `string[]` | optional | |
+| **sort** | `Record>` | optional | |
+| **limit** | `number` | optional | |
+| **skip** | `number` | optional | |
+| **top** | `number` | optional | |
+
diff --git a/content/docs/references/system/index.mdx b/content/docs/references/system/index.mdx
index fa6bcc251..dd4607dd2 100644
--- a/content/docs/references/system/index.mdx
+++ b/content/docs/references/system/index.mdx
@@ -10,6 +10,7 @@ This section contains all protocol schemas for the system layer of ObjectStack.
+
diff --git a/content/docs/references/system/meta.json b/content/docs/references/system/meta.json
index 5d6a36d7a..09e989f8b 100644
--- a/content/docs/references/system/meta.json
+++ b/content/docs/references/system/meta.json
@@ -3,6 +3,7 @@
"pages": [
"audit",
"context",
+ "data-engine",
"datasource",
"driver",
"events",
diff --git a/packages/objectql/src/index.ts b/packages/objectql/src/index.ts
index 0c301d294..1aecef495 100644
--- a/packages/objectql/src/index.ts
+++ b/packages/objectql/src/index.ts
@@ -1,6 +1,7 @@
import { QueryAST, HookContext } from '@objectstack/spec/data';
import { ObjectStackManifest } from '@objectstack/spec/system';
import { DriverInterface, DriverOptions } from '@objectstack/spec/system';
+import { IDataEngine, DataEngineQueryOptions } from '@objectstack/spec/system';
import { SchemaRegistry } from './registry';
// Export Registry for consumers
@@ -20,8 +21,10 @@ export interface PluginContext {
/**
* ObjectQL Engine
+ *
+ * Implements the IDataEngine interface for data persistence.
*/
-export class ObjectQL {
+export class ObjectQL implements IDataEngine {
private drivers = new Map();
private defaultDriver: string | null = null;
@@ -211,27 +214,57 @@ export class ObjectQL {
}
// ============================================
- // Data Access Methods
+ // Data Access Methods (IDataEngine Interface)
// ============================================
- async find(object: string, query: any = {}, options?: DriverOptions) {
+ /**
+ * Find records matching a query (IDataEngine interface)
+ *
+ * @param object - Object name
+ * @param query - Query options (IDataEngine format)
+ * @returns Promise resolving to array of records
+ */
+ async find(object: string, query?: DataEngineQueryOptions): Promise {
const driver = this.getDriver(object);
- // Normalize QueryAST
- let ast: QueryAST;
- if (query.where || query.fields || query.orderBy || query.limit) {
- ast = { object, ...query } as QueryAST;
- } else {
- ast = { object, where: query } as QueryAST;
+ // Convert DataEngineQueryOptions to QueryAST
+ let ast: QueryAST = { object };
+
+ if (query) {
+ // Map DataEngineQueryOptions to QueryAST
+ if (query.filter) {
+ ast.where = query.filter;
+ }
+ if (query.select) {
+ ast.fields = query.select;
+ }
+ if (query.sort) {
+ // Convert sort Record to orderBy array
+ // sort: { createdAt: -1, name: 'asc' } => orderBy: [{ field: 'createdAt', order: 'desc' }, { field: 'name', order: 'asc' }]
+ ast.orderBy = Object.entries(query.sort).map(([field, order]) => ({
+ field,
+ order: (order === -1 || order === 'desc') ? 'desc' : 'asc'
+ }));
+ }
+ // Handle both limit and top (top takes precedence)
+ if (query.top !== undefined) {
+ ast.limit = query.top;
+ } else if (query.limit !== undefined) {
+ ast.limit = query.limit;
+ }
+ if (query.skip !== undefined) {
+ ast.offset = query.skip;
+ }
}
+ // Set default limit if not specified
if (ast.limit === undefined) ast.limit = 100;
// Trigger Before Hook
const hookContext: HookContext = {
object,
event: 'beforeFind',
- input: { ast, options }, // Hooks can modify AST here
+ input: { ast, options: undefined },
ql: this
};
await this.triggerHooks('beforeFind', hookContext);
@@ -275,7 +308,14 @@ export class ObjectQL {
return driver.findOne(object, ast, options);
}
- async insert(object: string, data: Record, options?: DriverOptions) {
+ /**
+ * Insert a new record (IDataEngine interface)
+ *
+ * @param object - Object name
+ * @param data - Data to insert
+ * @returns Promise resolving to the created record
+ */
+ async insert(object: string, data: any): Promise {
const driver = this.getDriver(object);
// 1. Get Schema
@@ -290,7 +330,7 @@ export class ObjectQL {
const hookContext: HookContext = {
object,
event: 'beforeInsert',
- input: { data, options },
+ input: { data, options: undefined },
ql: this
};
await this.triggerHooks('beforeInsert', hookContext);
@@ -306,13 +346,21 @@ export class ObjectQL {
return hookContext.result;
}
- async update(object: string, id: string | number, data: Record, options?: DriverOptions) {
+ /**
+ * Update a record by ID (IDataEngine interface)
+ *
+ * @param object - Object name
+ * @param id - Record ID
+ * @param data - Updated data
+ * @returns Promise resolving to the updated record
+ */
+ async update(object: string, id: any, data: any): Promise {
const driver = this.getDriver(object);
const hookContext: HookContext = {
object,
event: 'beforeUpdate',
- input: { id, data, options },
+ input: { id, data, options: undefined },
ql: this
};
await this.triggerHooks('beforeUpdate', hookContext);
@@ -326,13 +374,20 @@ export class ObjectQL {
return hookContext.result;
}
- async delete(object: string, id: string | number, options?: DriverOptions) {
+ /**
+ * Delete a record by ID (IDataEngine interface)
+ *
+ * @param object - Object name
+ * @param id - Record ID
+ * @returns Promise resolving to true if deleted, false otherwise
+ */
+ async delete(object: string, id: any): Promise {
const driver = this.getDriver(object);
const hookContext: HookContext = {
object,
event: 'beforeDelete',
- input: { id, options },
+ input: { id, options: undefined },
ql: this
};
await this.triggerHooks('beforeDelete', hookContext);
@@ -343,6 +398,7 @@ export class ObjectQL {
hookContext.result = result;
await this.triggerHooks('afterDelete', hookContext);
+ // Driver.delete() already returns boolean per DriverInterface spec
return hookContext.result;
}
}
diff --git a/packages/runtime/src/interfaces/data-engine.ts b/packages/runtime/src/interfaces/data-engine.ts
index afe3c4e02..458567615 100644
--- a/packages/runtime/src/interfaces/data-engine.ts
+++ b/packages/runtime/src/interfaces/data-engine.ts
@@ -1,122 +1,8 @@
/**
* IDataEngine - Standard Data Engine Interface
*
- * Abstract interface for data persistence capabilities.
- * This allows plugins to interact with data engines without knowing
- * the underlying implementation (SQL, MongoDB, Memory, etc.).
- *
- * Follows Dependency Inversion Principle - plugins depend on this interface,
- * not on concrete database implementations.
- */
-
-/**
- * Query filter conditions
- */
-export interface QueryFilter {
- [field: string]: any;
-}
-
-/**
- * Query options for find operations
+ * Re-exports the data engine interface from the spec package.
+ * This provides backward compatibility for imports from @objectstack/runtime.
*/
-export interface QueryOptions {
- /** Filter conditions */
- filter?: QueryFilter;
- /** Fields to select */
- select?: string[];
- /** Sort order */
- sort?: Record;
- /** Limit number of results (alternative name for top, used by some drivers) */
- limit?: number;
- /** Skip number of results (for pagination) */
- skip?: number;
- /** Maximum number of results (OData-style, takes precedence over limit if both specified) */
- top?: number;
-}
-/**
- * IDataEngine - Data persistence capability interface
- *
- * Defines the contract for data engine implementations.
- * Concrete implementations (ObjectQL, Prisma, TypeORM) should implement this interface.
- */
-export interface IDataEngine {
- /**
- * Insert a new record
- *
- * @param objectName - Name of the object/table (e.g., 'user', 'order')
- * @param data - Data to insert
- * @returns Promise resolving to the created record (including generated ID)
- *
- * @example
- * ```ts
- * const user = await engine.insert('user', {
- * name: 'John Doe',
- * email: 'john@example.com'
- * });
- * console.log(user.id); // Auto-generated ID
- * ```
- */
- insert(objectName: string, data: any): Promise;
-
- /**
- * Find records matching a query
- *
- * @param objectName - Name of the object/table
- * @param query - Query conditions (optional)
- * @returns Promise resolving to an array of matching records
- *
- * @example
- * ```ts
- * // Find all users
- * const allUsers = await engine.find('user');
- *
- * // Find with filter
- * const activeUsers = await engine.find('user', {
- * filter: { status: 'active' }
- * });
- *
- * // Find with limit and sort
- * const recentUsers = await engine.find('user', {
- * sort: { createdAt: -1 },
- * limit: 10
- * });
- * ```
- */
- find(objectName: string, query?: QueryOptions): Promise;
-
- /**
- * Update a record by ID
- *
- * @param objectName - Name of the object/table
- * @param id - Record ID
- * @param data - Updated data (partial update)
- * @returns Promise resolving to the updated record
- *
- * @example
- * ```ts
- * const updatedUser = await engine.update('user', '123', {
- * name: 'Jane Doe',
- * email: 'jane@example.com'
- * });
- * ```
- */
- update(objectName: string, id: any, data: any): Promise;
-
- /**
- * Delete a record by ID
- *
- * @param objectName - Name of the object/table
- * @param id - Record ID
- * @returns Promise resolving to true if deleted, false otherwise
- *
- * @example
- * ```ts
- * const deleted = await engine.delete('user', '123');
- * if (deleted) {
- * console.log('User deleted successfully');
- * }
- * ```
- */
- delete(objectName: string, id: any): Promise;
-}
+export type { IDataEngine, DataEngineQueryOptions, DataEngineFilter } from '@objectstack/spec/system';
diff --git a/packages/runtime/src/objectql-plugin.ts b/packages/runtime/src/objectql-plugin.ts
index 480c0d520..68beed75d 100644
--- a/packages/runtime/src/objectql-plugin.ts
+++ b/packages/runtime/src/objectql-plugin.ts
@@ -44,9 +44,13 @@ export class ObjectQLPlugin implements Plugin {
* Init phase - Register ObjectQL as a service
*/
async init(ctx: PluginContext) {
- // Register ObjectQL engine as a service
+ // Register ObjectQL engine as 'objectql' service (legacy name)
ctx.registerService('objectql', this.ql);
- ctx.logger.log('[ObjectQLPlugin] ObjectQL engine registered as service');
+
+ // Register ObjectQL engine as 'data-engine' service (IDataEngine interface)
+ ctx.registerService('data-engine', this.ql);
+
+ ctx.logger.log('[ObjectQLPlugin] ObjectQL engine registered as services: objectql, data-engine');
}
/**
diff --git a/packages/runtime/src/test-interfaces.ts b/packages/runtime/src/test-interfaces.ts
index 47e3f280f..74735dfb1 100644
--- a/packages/runtime/src/test-interfaces.ts
+++ b/packages/runtime/src/test-interfaces.ts
@@ -5,7 +5,7 @@
* and IDataEngine interfaces without depending on concrete implementations.
*/
-import { IHttpServer, IDataEngine, RouteHandler, IHttpRequest, IHttpResponse, Middleware, QueryOptions } from './index.js';
+import { IHttpServer, IDataEngine, RouteHandler, IHttpRequest, IHttpResponse, Middleware, DataEngineQueryOptions } from './index.js';
/**
* Example: Mock HTTP Server Plugin
@@ -77,7 +77,7 @@ class MockDataEngine implements IDataEngine {
return record;
}
- async find(objectName: string, query?: QueryOptions): Promise {
+ async find(objectName: string, query?: DataEngineQueryOptions): Promise {
const objectStore = this.store.get(objectName);
if (!objectStore) {
return [];
diff --git a/packages/spec/json-schema/system/DataEngine.json b/packages/spec/json-schema/system/DataEngine.json
new file mode 100644
index 000000000..d587be050
--- /dev/null
+++ b/packages/spec/json-schema/system/DataEngine.json
@@ -0,0 +1,12 @@
+{
+ "$ref": "#/definitions/DataEngine",
+ "definitions": {
+ "DataEngine": {
+ "type": "object",
+ "properties": {},
+ "additionalProperties": false,
+ "description": "Data Engine Interface"
+ }
+ },
+ "$schema": "http://json-schema.org/draft-07/schema#"
+}
\ No newline at end of file
diff --git a/packages/spec/json-schema/system/DataEngineFilter.json b/packages/spec/json-schema/system/DataEngineFilter.json
new file mode 100644
index 000000000..afc2c88f4
--- /dev/null
+++ b/packages/spec/json-schema/system/DataEngineFilter.json
@@ -0,0 +1,11 @@
+{
+ "$ref": "#/definitions/DataEngineFilter",
+ "definitions": {
+ "DataEngineFilter": {
+ "type": "object",
+ "additionalProperties": {},
+ "description": "Data Engine query filter conditions"
+ }
+ },
+ "$schema": "http://json-schema.org/draft-07/schema#"
+}
\ No newline at end of file
diff --git a/packages/spec/json-schema/system/DataEngineQueryOptions.json b/packages/spec/json-schema/system/DataEngineQueryOptions.json
new file mode 100644
index 000000000..0111673b3
--- /dev/null
+++ b/packages/spec/json-schema/system/DataEngineQueryOptions.json
@@ -0,0 +1,48 @@
+{
+ "$ref": "#/definitions/DataEngineQueryOptions",
+ "definitions": {
+ "DataEngineQueryOptions": {
+ "type": "object",
+ "properties": {
+ "filter": {
+ "type": "object",
+ "additionalProperties": {},
+ "description": "Data Engine query filter conditions"
+ },
+ "select": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "sort": {
+ "type": "object",
+ "additionalProperties": {
+ "type": [
+ "number",
+ "string"
+ ],
+ "enum": [
+ 1,
+ -1,
+ "asc",
+ "desc"
+ ]
+ }
+ },
+ "limit": {
+ "type": "number"
+ },
+ "skip": {
+ "type": "number"
+ },
+ "top": {
+ "type": "number"
+ }
+ },
+ "additionalProperties": false,
+ "description": "Query options for IDataEngine.find() operations"
+ }
+ },
+ "$schema": "http://json-schema.org/draft-07/schema#"
+}
\ No newline at end of file
diff --git a/packages/spec/src/system/data-engine.zod.ts b/packages/spec/src/system/data-engine.zod.ts
new file mode 100644
index 000000000..39c156f28
--- /dev/null
+++ b/packages/spec/src/system/data-engine.zod.ts
@@ -0,0 +1,107 @@
+import { z } from 'zod';
+
+/**
+ * Data Engine Protocol
+ *
+ * Defines the standard interface for data persistence engines.
+ * This allows different data engines (ObjectQL, Prisma, TypeORM, etc.)
+ * to be used interchangeably through a common interface.
+ *
+ * Following the Dependency Inversion Principle - plugins depend on this interface,
+ * not on concrete database implementations.
+ */
+
+/**
+ * Data Engine Query filter conditions
+ * Simple key-value filter structure for IDataEngine.find() operations
+ */
+export const DataEngineFilterSchema = z.record(z.any()).describe('Data Engine query filter conditions');
+
+/**
+ * Query options for IDataEngine.find() operations
+ */
+export const DataEngineQueryOptionsSchema = z.object({
+ /** Filter conditions */
+ filter: DataEngineFilterSchema.optional(),
+ /** Fields to select */
+ select: z.array(z.string()).optional(),
+ /** Sort order */
+ sort: z.record(z.union([z.literal(1), z.literal(-1), z.literal('asc'), z.literal('desc')])).optional(),
+ /** Limit number of results (alternative name for top, used by some drivers) */
+ limit: z.number().optional(),
+ /** Skip number of results (for pagination) */
+ skip: z.number().optional(),
+ /** Maximum number of results (OData-style, takes precedence over limit if both specified) */
+ top: z.number().optional(),
+}).describe('Query options for IDataEngine.find() operations');
+
+/**
+ * Data Engine Interface Schema
+ *
+ * Defines the contract for data engine implementations.
+ */
+export const DataEngineSchema = z.object({
+ /**
+ * Insert a new record
+ *
+ * @param objectName - Name of the object/table (e.g., 'user', 'order')
+ * @param data - Data to insert
+ * @returns Promise resolving to the created record (including generated ID)
+ */
+ insert: z.function()
+ .args(z.string(), z.any())
+ .returns(z.promise(z.any()))
+ .describe('Insert a new record'),
+
+ /**
+ * Find records matching a query
+ *
+ * @param objectName - Name of the object/table
+ * @param query - Query conditions (optional)
+ * @returns Promise resolving to an array of matching records
+ */
+ find: z.function()
+ .args(z.string())
+ .returns(z.promise(z.array(z.any())))
+ .describe('Find records matching a query'),
+
+ /**
+ * Update a record by ID
+ *
+ * @param objectName - Name of the object/table
+ * @param id - Record ID
+ * @param data - Updated data (partial update)
+ * @returns Promise resolving to the updated record
+ */
+ update: z.function()
+ .args(z.string(), z.any(), z.any())
+ .returns(z.promise(z.any()))
+ .describe('Update a record by ID'),
+
+ /**
+ * Delete a record by ID
+ *
+ * @param objectName - Name of the object/table
+ * @param id - Record ID
+ * @returns Promise resolving to true if deleted, false otherwise
+ */
+ delete: z.function()
+ .args(z.string(), z.any())
+ .returns(z.promise(z.boolean()))
+ .describe('Delete a record by ID'),
+}).describe('Data Engine Interface');
+
+/**
+ * TypeScript types derived from schemas
+ */
+export type DataEngineFilter = z.infer;
+export type DataEngineQueryOptions = z.infer;
+
+// Define the TypeScript interface manually for better type safety
+// Zod function schema doesn't handle optional parameters well
+export interface IDataEngine {
+ insert(objectName: string, data: any): Promise;
+ find(objectName: string, query?: DataEngineQueryOptions): Promise;
+ update(objectName: string, id: any, data: any): Promise;
+ delete(objectName: string, id: any): Promise;
+}
diff --git a/packages/spec/src/system/index.ts b/packages/spec/src/system/index.ts
index dcb9e3324..f441582fb 100644
--- a/packages/spec/src/system/index.ts
+++ b/packages/spec/src/system/index.ts
@@ -26,5 +26,9 @@ export * from './datasource.zod';
export * from './driver.zod';
export * from './driver/mongo.zod';
export * from './driver/postgres.zod';
+
+// Data Engine Protocol
+export * from './data-engine.zod';
+
// Note: Auth, Identity, Policy, Role, Organization moved to @objectstack/spec/auth
// Note: Territory moved to @objectstack/spec/permission
diff --git a/test-dataengine-interface.ts b/test-dataengine-interface.ts
new file mode 100644
index 000000000..d0334a86e
--- /dev/null
+++ b/test-dataengine-interface.ts
@@ -0,0 +1,162 @@
+/**
+ * Test Script for IDataEngine Interface Compliance
+ *
+ * This script validates:
+ * 1. ObjectQL implements IDataEngine interface
+ * 2. Data engine service registration works
+ * 3. IDataEngine methods are callable
+ */
+
+import { ObjectKernel } from './packages/runtime/src/mini-kernel.js';
+import { ObjectQLPlugin } from './packages/runtime/src/objectql-plugin.js';
+import { DriverPlugin } from './packages/runtime/src/driver-plugin.js';
+import { ObjectQL } from './packages/objectql/src/index.js';
+import type { IDataEngine } from './packages/spec/src/system/data-engine.zod.js';
+
+// Mock driver for testing
+class MockDriver {
+ name = 'mock-driver';
+ version = '1.0.0';
+
+ async connect() {
+ console.log('[MockDriver] Connected');
+ }
+
+ async disconnect() {
+ console.log('[MockDriver] Disconnected');
+ }
+
+ async find(object: string, query: any) {
+ console.log(`[MockDriver] find(${object})`);
+ return [{ id: '1', name: 'Test Record' }];
+ }
+
+ async findOne(object: string, query: any) {
+ console.log(`[MockDriver] findOne(${object})`);
+ return { id: '1', name: 'Test Record' };
+ }
+
+ async create(object: string, data: any) {
+ console.log(`[MockDriver] create(${object})`, data);
+ return { id: '123', ...data };
+ }
+
+ async update(object: string, id: any, data: any) {
+ console.log(`[MockDriver] update(${object}, ${id})`, data);
+ return { id, ...data };
+ }
+
+ async delete(object: string, id: any) {
+ console.log(`[MockDriver] delete(${object}, ${id})`);
+ return true; // Return boolean as per DriverInterface
+ }
+}
+
+async function testDataEngineService() {
+ console.log('\n=== Test 1: IDataEngine Service Registration ===');
+
+ const kernel = new ObjectKernel();
+ kernel.use(new ObjectQLPlugin());
+ kernel.use(new DriverPlugin(new MockDriver() as any, 'mock'));
+
+ await kernel.bootstrap();
+
+ // Verify data-engine service is registered
+ try {
+ const engine = kernel.getService('data-engine');
+ console.log('✅ data-engine service registered');
+ console.log('Service type:', engine.constructor.name);
+ } catch (e: any) {
+ throw new Error(`FAILED: data-engine service not found: ${e.message}`);
+ }
+
+ // Verify objectql service is still available (backward compatibility)
+ try {
+ const ql = kernel.getService('objectql');
+ console.log('✅ objectql service still available (backward compatibility)');
+ } catch (e: any) {
+ throw new Error(`FAILED: objectql service not found: ${e.message}`);
+ }
+}
+
+async function testDataEngineInterface() {
+ console.log('\n=== Test 2: IDataEngine Interface Methods ===');
+
+ const kernel = new ObjectKernel();
+ kernel.use(new ObjectQLPlugin());
+ kernel.use(new DriverPlugin(new MockDriver() as any, 'mock'));
+
+ await kernel.bootstrap();
+
+ const engine = kernel.getService('data-engine');
+
+ // Test insert
+ console.log('\nTesting insert...');
+ const created = await engine.insert('test_object', { name: 'John Doe', email: 'john@example.com' });
+ console.log('✅ insert() returned:', created);
+
+ // Test find with QueryOptions
+ console.log('\nTesting find with QueryOptions...');
+ const results = await engine.find('test_object', {
+ filter: { status: 'active' },
+ limit: 10,
+ sort: { createdAt: -1 }
+ });
+ console.log('✅ find() returned:', results.length, 'records');
+
+ // Test find without query (all records)
+ console.log('\nTesting find without query...');
+ const allResults = await engine.find('test_object');
+ console.log('✅ find() without query returned:', allResults.length, 'records');
+
+ // Test update
+ console.log('\nTesting update...');
+ const updated = await engine.update('test_object', '123', { name: 'Jane Doe' });
+ console.log('✅ update() returned:', updated);
+
+ // Test delete
+ console.log('\nTesting delete...');
+ const deleted = await engine.delete('test_object', '123');
+ console.log('✅ delete() returned boolean:', deleted === true || deleted === false);
+
+ if (typeof deleted !== 'boolean') {
+ throw new Error(`FAILED: delete() should return boolean, got ${typeof deleted}`);
+ }
+}
+
+async function testBackwardCompatibility() {
+ console.log('\n=== Test 3: Backward Compatibility ===');
+
+ const kernel = new ObjectKernel();
+ kernel.use(new ObjectQLPlugin());
+ kernel.use(new DriverPlugin(new MockDriver() as any, 'mock'));
+
+ await kernel.bootstrap();
+
+ // Both services should point to the same instance
+ const engine = kernel.getService('data-engine');
+ const ql = kernel.getService('objectql');
+
+ if (engine !== ql) {
+ throw new Error('FAILED: data-engine and objectql should be the same instance');
+ }
+
+ console.log('✅ data-engine and objectql services are the same instance');
+}
+
+async function runAllTests() {
+ console.log('🧪 Starting IDataEngine Interface Tests...\n');
+
+ try {
+ await testDataEngineService();
+ await testDataEngineInterface();
+ await testBackwardCompatibility();
+
+ console.log('\n✅ All tests passed!\n');
+ } catch (error) {
+ console.error('\n❌ Test failed:', error);
+ process.exit(1);
+ }
+}
+
+runAllTests();
diff --git a/test-objectql-plugin.ts b/test-objectql-plugin.ts
index 335eab288..2d5dd0c78 100644
--- a/test-objectql-plugin.ts
+++ b/test-objectql-plugin.ts
@@ -7,7 +7,7 @@
* 3. Multiple plugins with ObjectQL work
*/
-import { ObjectKernel, ObjectQLPlugin, ObjectQL, SchemaRegistry } from '../packages/runtime/src';
+import { ObjectKernel, ObjectQLPlugin, ObjectQL, SchemaRegistry } from './packages/runtime/src/index.js';
async function testPluginBasedRegistration() {
console.log('\n=== Test 1: Plugin-based ObjectQL Registration ===');
@@ -39,7 +39,7 @@ async function testMissingObjectQL() {
kernel.getService('objectql');
throw new Error('FAILED: Should have thrown error for missing ObjectQL');
} catch (e: any) {
- if (e.message.includes('Service not found')) {
+ if (e.message.includes('not found')) {
console.log('✅ Correctly throws error when ObjectQL service is not registered');
} else {
throw e;