Skip to content

Commit 3b8351c

Browse files
Copilothuangyiirene
andcommitted
docs: Update documentation for Phase 3 plugin architecture and kernel integration
Co-authored-by: huangyiirene <7665279+huangyiirene@users.noreply.github.com>
1 parent 478ea26 commit 3b8351c

6 files changed

Lines changed: 229 additions & 37 deletions

File tree

packages/foundation/core/README.md

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
# @objectql/core
22

3-
The core ORM and runtime engine for ObjectQL. This package handles object querying, CRUD operations, database driver coordination, transaction management, and **metadata-driven validation**.
3+
The core ORM and runtime engine for ObjectQL. This package handles object querying, CRUD operations, database driver coordination, transaction management, and **metadata-driven validation**. As of version 4.0.0, it wraps the **ObjectStackKernel** for plugin architecture and lifecycle management.
44

55
## Features
66

7-
- **Unified Query Language**: A generic way to query data across different databases (SQL, Mongo, etc.).
8-
- **Repository Pattern**: `ObjectRepository` for managing object records.
9-
- **Driver Agnostic**: Abstraction layer for database drivers.
10-
- **Dynamic Schema**: Loads object definitions from metadata.
11-
- **Hooks & Actions**: Runtime logic injection.
12-
- **Validation Engine**: Metadata-driven validation with field-level, cross-field, and state machine rules.
7+
- **Plugin Architecture**: Built on top of `@objectstack/runtime` with kernel-based plugin system
8+
- **Unified Query Language**: A generic way to query data across different databases (SQL, Mongo, etc.)
9+
- **Repository Pattern**: `ObjectRepository` for managing object records
10+
- **Driver Agnostic**: Abstraction layer for database drivers
11+
- **Dynamic Schema**: Loads object definitions from metadata
12+
- **Hooks & Actions**: Runtime logic injection
13+
- **Validation Engine**: Metadata-driven validation with field-level, cross-field, and state machine rules
14+
- **Formula Engine**: Computed fields with dynamic formulas
15+
- **AI Integration**: Built-in AI agent capabilities
1316

1417
## Installation
1518

1619
```bash
17-
npm install @objectql/core @objectql/types
20+
npm install @objectql/core @objectql/types @objectstack/runtime @objectstack/spec
1821
```
1922

23+
## Architecture
24+
25+
ObjectQL now wraps the `ObjectStackKernel` from `@objectstack/runtime`, providing:
26+
27+
- **Kernel-based lifecycle management**: Initialization, startup, and shutdown
28+
- **Plugin system**: Extensible architecture with `ObjectQLPlugin`
29+
- **Enhanced features**: Repository, Validator, Formula, and AI capabilities as plugins
30+
31+
See [RUNTIME_INTEGRATION.md](./RUNTIME_INTEGRATION.md) for detailed architecture documentation.
32+
2033
## Usage
2134

2235
### Basic Setup
@@ -31,7 +44,7 @@ const objectql = new ObjectQL({
3144
}
3245
});
3346

34-
await objectql.init();
47+
await objectql.init(); // Initializes the kernel and all plugins
3548

3649
// Use context for operations
3750
const ctx = objectql.createContext({ userId: 'u-1' });
@@ -40,6 +53,15 @@ const projects = await ctx.object('project').find({
4053
});
4154
```
4255

56+
### Accessing the Kernel
57+
58+
For advanced use cases, you can access the underlying kernel:
59+
60+
```typescript
61+
const kernel = objectql.getKernel();
62+
// Use kernel methods if needed
63+
```
64+
4365
### Validation System
4466

4567
The validation system allows you to define validation rules in your object metadata and execute them programmatically.

packages/foundation/core/RUNTIME_INTEGRATION.md

Lines changed: 105 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,63 @@ This document explains the integration of `@objectstack/runtime` and `@objectsta
44

55
## Overview
66

7-
As of version 3.0.1, ObjectQL core natively uses the ObjectStack runtime packages:
7+
As of version 4.0.0, ObjectQL core uses the ObjectStack runtime packages with plugin architecture:
88

9-
- **@objectstack/spec@0.1.2**: Protocol specification with standard `DriverInterface`
10-
- **@objectstack/objectql@0.1.1**: Core ObjectQL engine with driver management
11-
- **@objectstack/runtime@0.1.1**: Runtime kernel with application lifecycle orchestration
9+
- **@objectstack/spec@0.2.0**: Protocol specification with standard `DriverInterface`
10+
- **@objectstack/objectql@0.2.0**: Core ObjectQL engine with driver management
11+
- **@objectstack/runtime@0.2.0**: Runtime kernel with application lifecycle orchestration and plugin system
1212

1313
## Architecture
1414

1515
### Package Relationship
1616

1717
```
1818
@objectql/core (this package)
19+
├── Wraps ObjectStackKernel from @objectstack/runtime
20+
├── Implements ObjectQLPlugin for enhanced features
1921
├── Uses @objectstack/objectql for driver management
2022
├── Natively uses @objectstack/spec.DriverInterface (no wrapper)
2123
└── Re-exports types from @objectstack/runtime
2224
```
2325

24-
### Driver Management Integration
26+
### Plugin Architecture (v4.0.0)
2527

26-
**Breaking Change (v3.0.1):** The core package now **natively uses** `DriverInterface` from `@objectstack/spec`:
28+
**Breaking Change (v4.0.0):** The core package now **wraps `ObjectStackKernel`** and uses a plugin architecture:
2729

2830
```typescript
29-
import { ObjectQL } from '@objectql/core';
31+
import { ObjectQL, ObjectQLPlugin } from '@objectql/core';
3032
import type { DriverInterface } from '@objectstack/spec';
3133

32-
// Drivers must implement DriverInterface from @objectstack/spec
34+
// ObjectQL now wraps ObjectStackKernel internally
3335
const app = new ObjectQL({
3436
datasources: {
3537
default: myDriver // Must be DriverInterface
3638
}
3739
});
3840

39-
await app.init();
41+
// Access the kernel if needed
42+
const kernel = app.getKernel();
43+
44+
await app.init(); // This calls kernel.start() internally
45+
```
46+
47+
### ObjectQLPlugin
48+
49+
The new `ObjectQLPlugin` class implements the `RuntimePlugin` interface from `@objectstack/runtime`:
50+
51+
```typescript
52+
import { ObjectQLPlugin, ObjectQLPluginConfig } from '@objectql/core';
53+
54+
// Configure the plugin
55+
const plugin = new ObjectQLPlugin({
56+
enableRepository: true,
57+
enableValidator: true,
58+
enableFormulas: true,
59+
enableAI: true
60+
});
61+
62+
// The plugin is automatically registered when you create an ObjectQL instance
63+
const app = new ObjectQL({ datasources: {} });
4064
```
4165

4266
### Type Exports
@@ -76,6 +100,7 @@ The current `ObjectQL` class in this package is a **production-ready, feature-ri
76100
- Repository pattern
77101
- Formula engine
78102
- AI integration
103+
- **Wraps ObjectStackKernel for plugin architecture**
79104
- **Native driver management via @objectstack/objectql**
80105

81106
The `ObjectQLEngine` from `@objectstack/objectql` is a **simpler, lightweight** implementation suitable for:
@@ -84,6 +109,46 @@ The `ObjectQLEngine` from `@objectstack/objectql` is a **simpler, lightweight**
84109
- Simple driver management
85110
- Minimal runtime overhead
86111

112+
### Kernel Integration
113+
114+
ObjectQL now wraps the `ObjectStackKernel` to provide plugin architecture and lifecycle management:
115+
116+
```typescript
117+
// In @objectql/core
118+
import { ObjectStackKernel } from '@objectstack/runtime';
119+
import { ObjectQLPlugin } from './plugin';
120+
121+
export class ObjectQL implements IObjectQL {
122+
private kernel: ObjectStackKernel;
123+
private kernelPlugins: any[] = [];
124+
125+
constructor(config: ObjectQLConfig) {
126+
// Add the ObjectQL plugin to provide enhanced features
127+
this.kernelPlugins.push(new ObjectQLPlugin());
128+
129+
// Create the kernel instance
130+
this.kernel = new ObjectStackKernel(this.kernelPlugins);
131+
}
132+
133+
async init() {
134+
console.log('[ObjectQL] Initializing with ObjectStackKernel...');
135+
136+
// Start the kernel first - this will install and start all plugins
137+
await this.kernel.start();
138+
139+
// Continue with legacy initialization...
140+
}
141+
142+
/**
143+
* Get the underlying ObjectStackKernel instance
144+
* for advanced usage scenarios
145+
*/
146+
getKernel(): ObjectStackKernel {
147+
return this.kernel;
148+
}
149+
}
150+
```
151+
87152
### Driver Management (No Compatibility Layer)
88153

89154
ObjectQL now directly uses drivers conforming to `@objectstack/spec.DriverInterface`:
@@ -174,12 +239,42 @@ app.registerDriver('mydb', new MyCustomDriver(), false);
174239

175240
## Breaking Changes
176241

242+
### v4.0.0: Plugin Architecture
243+
244+
**What Changed:**
245+
- `ObjectQL` now wraps `ObjectStackKernel` from `@objectstack/runtime`
246+
- New `ObjectQLPlugin` class implements `RuntimePlugin` interface
247+
- Initialization process now calls `kernel.start()` which installs and starts all plugins
248+
- Dependencies updated to `@objectstack/*@0.2.0`
249+
- New `getKernel()` method provides access to the underlying kernel
250+
251+
**Migration Guide:**
252+
253+
Your existing code should continue to work without changes:
254+
```typescript
255+
import { ObjectQL } from '@objectql/core';
256+
import { MyDriver } from './my-driver';
257+
258+
const app = new ObjectQL({
259+
datasources: {
260+
default: new MyDriver()
261+
}
262+
});
263+
264+
await app.init(); // Now calls kernel.start() internally
265+
```
266+
267+
If you need kernel access:
268+
```typescript
269+
const kernel = app.getKernel();
270+
// Use kernel methods if needed
271+
```
272+
177273
### v3.0.1: Native DriverInterface Adoption
178274

179275
**What Changed:**
180276
- `ObjectQLConfig.datasources` now requires `Record<string, DriverInterface>` (from `@objectstack/spec`)
181277
- Removed compatibility wrapper for old `Driver` type
182-
- `app.registerDriver()` now accepts `DriverInterface` instead of legacy `Driver`
183278
- `app.datasource()` now returns `DriverInterface`
184279
- Driver lifecycle is fully managed by ObjectStack engine
185280

packages/foundation/core/jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
testMatch: ['**/test/**/*.test.ts'],
1313
moduleNameMapper: {
1414
'^@objectql/(.*)$': '<rootDir>/../$1/src',
15+
'^@objectstack/runtime$': '<rootDir>/test/__mocks__/@objectstack/runtime.ts',
1516
},
1617
transform: {
1718
'^.+\\.ts$': ['ts-jest', {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Mock for @objectstack/runtime
3+
* This mock is needed because the npm package has issues with Jest
4+
* and we want to focus on testing ObjectQL's logic, not the kernel integration.
5+
*/
6+
7+
export class ObjectStackKernel {
8+
public ql: any = null;
9+
10+
constructor(plugins: any[] = []) {
11+
// Mock implementation
12+
}
13+
14+
async start(): Promise<void> {
15+
// Mock implementation
16+
}
17+
18+
async seed(): Promise<void> {
19+
// Mock implementation
20+
}
21+
22+
async find(objectName: string, query: any): Promise<{ value: Record<string, any>[]; count: number }> {
23+
return { value: [], count: 0 };
24+
}
25+
26+
async get(objectName: string, id: string): Promise<Record<string, any>> {
27+
return {};
28+
}
29+
30+
async create(objectName: string, data: any): Promise<Record<string, any>> {
31+
return data;
32+
}
33+
34+
async update(objectName: string, id: string, data: any): Promise<Record<string, any>> {
35+
return data;
36+
}
37+
38+
async delete(objectName: string, id: string): Promise<boolean> {
39+
return true;
40+
}
41+
42+
getMetadata(objectName: string): any {
43+
return {};
44+
}
45+
46+
getView(objectName: string, viewType?: 'list' | 'form'): any {
47+
return null;
48+
}
49+
}
50+
51+
export class ObjectStackRuntimeProtocol {}
52+
53+
export interface RuntimeContext {
54+
engine: ObjectStackKernel;
55+
}
56+
57+
export interface RuntimePlugin {
58+
name: string;
59+
install?: (ctx: RuntimeContext) => void | Promise<void>;
60+
onStart?: (ctx: RuntimeContext) => void | Promise<void>;
61+
}

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

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

9-
// Mock the ObjectStackKernel before importing ObjectQL
10-
jest.mock('@objectstack/runtime', () => {
11-
return {
12-
ObjectStackKernel: jest.fn().mockImplementation(() => ({
13-
ql: null,
14-
start: jest.fn().mockResolvedValue(undefined),
15-
seed: jest.fn().mockResolvedValue(undefined),
16-
find: jest.fn().mockResolvedValue({ value: [], count: 0 }),
17-
get: jest.fn().mockResolvedValue({}),
18-
create: jest.fn().mockResolvedValue({}),
19-
update: jest.fn().mockResolvedValue({}),
20-
delete: jest.fn().mockResolvedValue(true),
21-
getMetadata: jest.fn().mockReturnValue({}),
22-
getView: jest.fn().mockReturnValue(null),
23-
})),
24-
};
25-
});
26-
279
import { ObjectQL } from '../src/app';
2810
import { MockDriver } from './mock-driver';
2911
import { ObjectConfig, HookContext, ActionContext, Metadata } from '@objectql/types';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* ObjectQL
3+
* Copyright (c) 2026-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/**
10+
* Mock for @objectstack/runtime
11+
* This mock is needed because the npm package has issues with Jest
12+
* and we want to focus on testing ObjectQL's logic, not the kernel integration.
13+
*/
14+
export function setupObjectStackRuntimeMock() {
15+
jest.mock('@objectstack/runtime', () => {
16+
return {
17+
ObjectStackKernel: jest.fn().mockImplementation(() => ({
18+
ql: null,
19+
start: jest.fn().mockResolvedValue(undefined),
20+
seed: jest.fn().mockResolvedValue(undefined),
21+
find: jest.fn().mockResolvedValue({ value: [], count: 0 }),
22+
get: jest.fn().mockResolvedValue({}),
23+
create: jest.fn().mockResolvedValue({}),
24+
update: jest.fn().mockResolvedValue({}),
25+
delete: jest.fn().mockResolvedValue(true),
26+
getMetadata: jest.fn().mockReturnValue({}),
27+
getView: jest.fn().mockReturnValue(null),
28+
})),
29+
};
30+
});
31+
}

0 commit comments

Comments
 (0)