@objectstack/runtime 0.4.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +196 -81
- package/dist/app-manifest-plugin.d.ts +19 -0
- package/dist/app-manifest-plugin.js +33 -0
- package/dist/driver-plugin.d.ts +23 -0
- package/dist/driver-plugin.js +31 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +9 -5
- package/dist/test-interfaces.d.ts +7 -0
- package/dist/test-interfaces.js +138 -0
- package/package.json +6 -4
- package/src/app-manifest-plugin.ts +48 -0
- package/src/driver-plugin.ts +40 -0
- package/src/index.ts +12 -5
- package/src/test-interfaces.ts +170 -0
- package/dist/kernel.d.ts +0 -147
- package/dist/kernel.js +0 -173
- package/dist/objectql-plugin.d.ts +0 -27
- package/dist/objectql-plugin.js +0 -41
- package/dist/protocol.d.ts +0 -68
- package/dist/protocol.js +0 -108
- package/dist/types.d.ts +0 -9
- package/dist/types.js +0 -1
- package/src/kernel.ts +0 -203
- package/src/objectql-plugin.ts +0 -47
- package/src/protocol.ts +0 -129
- package/src/types.ts +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @objectstack/runtime
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b2df5f7: Unified version bump to 0.5.0
|
|
8
|
+
|
|
9
|
+
- Standardized all package versions to 0.5.0 across the monorepo
|
|
10
|
+
- Fixed driver-memory package.json paths for proper module resolution
|
|
11
|
+
- Ensured all packages are in sync for the 0.5.0 release
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [b2df5f7]
|
|
16
|
+
- @objectstack/spec@0.6.0
|
|
17
|
+
- @objectstack/objectql@0.6.0
|
|
18
|
+
- @objectstack/types@0.6.0
|
|
19
|
+
- @objectstack/core@0.6.0
|
|
20
|
+
|
|
3
21
|
## 0.4.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# @objectstack/runtime
|
|
2
2
|
|
|
3
|
-
ObjectStack
|
|
3
|
+
ObjectStack Standard System Library
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
The runtime package provides the
|
|
7
|
+
The runtime package provides the **Standard Library** for the ObjectStack Operating System. It bridges the pure **ObjectKernel** (from `@objectstack/core`) with the **Data Engine** (`@objectstack/objectql`) and provides essential infrastructure adapters.
|
|
8
|
+
|
|
9
|
+
### Architecture Highlights
|
|
10
|
+
|
|
11
|
+
- **Standard Library**: Contains essential plugins (`AppManifestPlugin`, `DriverPlugin`)
|
|
12
|
+
- **Core Integration**: Re-exports `ObjectKernel` for convenience
|
|
13
|
+
- **Capability Contracts**: Abstract interfaces for HTTP server and data persistence
|
|
8
14
|
|
|
9
15
|
## Installation
|
|
10
16
|
|
|
@@ -12,26 +18,28 @@ The runtime package provides the `ObjectStackKernel` - the central orchestrator
|
|
|
12
18
|
npm install @objectstack/runtime
|
|
13
19
|
```
|
|
14
20
|
|
|
15
|
-
##
|
|
21
|
+
## Quick Start
|
|
16
22
|
|
|
17
|
-
### Basic Setup
|
|
23
|
+
### Basic Setup (Recommended)
|
|
18
24
|
|
|
19
25
|
```typescript
|
|
20
|
-
import {
|
|
26
|
+
import { ObjectKernel } from '@objectstack/core';
|
|
27
|
+
import { ObjectQLPlugin, DriverPlugin, AppManifestPlugin } from '@objectstack/runtime';
|
|
21
28
|
import { InMemoryDriver } from '@objectstack/driver-memory';
|
|
22
29
|
|
|
23
|
-
const kernel = new
|
|
30
|
+
const kernel = new ObjectKernel();
|
|
31
|
+
|
|
32
|
+
kernel
|
|
24
33
|
// Register ObjectQL engine
|
|
25
|
-
new ObjectQLPlugin()
|
|
34
|
+
.use(new ObjectQLPlugin())
|
|
26
35
|
|
|
27
36
|
// Add database driver
|
|
28
|
-
new InMemoryDriver(),
|
|
37
|
+
.use(new DriverPlugin(new InMemoryDriver(), 'memory'))
|
|
29
38
|
|
|
30
39
|
// Add your app configurations
|
|
31
|
-
// appConfig
|
|
32
|
-
]);
|
|
40
|
+
// .use(new AppManifestPlugin(appConfig));
|
|
33
41
|
|
|
34
|
-
await kernel.
|
|
42
|
+
await kernel.bootstrap();
|
|
35
43
|
```
|
|
36
44
|
|
|
37
45
|
### Custom ObjectQL Instance
|
|
@@ -39,7 +47,7 @@ await kernel.start();
|
|
|
39
47
|
If you have a separate ObjectQL implementation or need custom configuration:
|
|
40
48
|
|
|
41
49
|
```typescript
|
|
42
|
-
import {
|
|
50
|
+
import { ObjectKernel, ObjectQLPlugin, DriverPlugin, ObjectQL } from '@objectstack/runtime';
|
|
43
51
|
|
|
44
52
|
// Create custom ObjectQL instance
|
|
45
53
|
const customQL = new ObjectQL({
|
|
@@ -52,111 +60,218 @@ customQL.registerHook('beforeInsert', async (ctx) => {
|
|
|
52
60
|
console.log(`Inserting into ${ctx.object}`);
|
|
53
61
|
});
|
|
54
62
|
|
|
55
|
-
const kernel = new
|
|
63
|
+
const kernel = new ObjectKernel();
|
|
64
|
+
|
|
65
|
+
kernel
|
|
56
66
|
// Use your custom ObjectQL instance
|
|
57
|
-
new ObjectQLPlugin(customQL)
|
|
67
|
+
.use(new ObjectQLPlugin(customQL))
|
|
58
68
|
|
|
59
|
-
//
|
|
60
|
-
|
|
69
|
+
// Add driver
|
|
70
|
+
.use(new DriverPlugin(new InMemoryDriver(), 'memory'));
|
|
71
|
+
|
|
72
|
+
await kernel.bootstrap();
|
|
61
73
|
|
|
62
|
-
|
|
74
|
+
// Access ObjectQL via service registry
|
|
75
|
+
const objectql = kernel.getService('objectql');
|
|
63
76
|
```
|
|
64
77
|
|
|
65
|
-
|
|
78
|
+
## Architecture
|
|
79
|
+
|
|
80
|
+
### ObjectKernel (MiniKernel)
|
|
81
|
+
|
|
82
|
+
The kernel provides:
|
|
83
|
+
- **Plugin Lifecycle Management**: init → start → destroy phases
|
|
84
|
+
- **Service Registry**: Dependency injection container
|
|
85
|
+
- **Event/Hook System**: Inter-plugin communication
|
|
86
|
+
- **Dependency Resolution**: Topological sort for plugin dependencies
|
|
87
|
+
|
|
88
|
+
### Built-in Plugins
|
|
66
89
|
|
|
67
|
-
|
|
90
|
+
#### ObjectQLPlugin
|
|
91
|
+
Registers the ObjectQL data engine as a service.
|
|
68
92
|
|
|
69
93
|
```typescript
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// ... other plugins
|
|
74
|
-
]);
|
|
94
|
+
new ObjectQLPlugin() // Default instance
|
|
95
|
+
new ObjectQLPlugin(customQL) // Custom instance
|
|
96
|
+
new ObjectQLPlugin(undefined, { env: 'prod' }) // With context
|
|
75
97
|
```
|
|
76
98
|
|
|
77
|
-
|
|
99
|
+
**Services**: `'objectql'`
|
|
78
100
|
|
|
79
|
-
|
|
101
|
+
#### DriverPlugin
|
|
102
|
+
Registers a data driver with ObjectQL.
|
|
80
103
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
- Coordinating the ObjectQL engine
|
|
85
|
-
- Handling data operations
|
|
104
|
+
```typescript
|
|
105
|
+
new DriverPlugin(driver, 'driver-name')
|
|
106
|
+
```
|
|
86
107
|
|
|
87
|
-
|
|
108
|
+
**Dependencies**: `['com.objectstack.engine.objectql']`
|
|
88
109
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
110
|
+
#### AppManifestPlugin
|
|
111
|
+
Wraps ObjectStack app manifests (objectstack.config.ts) as plugins.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
new AppManifestPlugin(appConfig)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Dependencies**: `['com.objectstack.engine.objectql']`
|
|
94
118
|
|
|
95
119
|
## API Reference
|
|
96
120
|
|
|
97
|
-
###
|
|
121
|
+
### Capability Contract Interfaces
|
|
122
|
+
|
|
123
|
+
#### IHttpServer
|
|
124
|
+
|
|
125
|
+
Abstract interface for HTTP server capabilities. Allows plugins to work with any HTTP framework (Express, Fastify, Hono, etc.) without tight coupling.
|
|
98
126
|
|
|
99
|
-
#### Constructor
|
|
100
127
|
```typescript
|
|
101
|
-
|
|
128
|
+
import { IHttpServer, IHttpRequest, IHttpResponse } from '@objectstack/runtime';
|
|
129
|
+
|
|
130
|
+
// In your HTTP server plugin
|
|
131
|
+
class MyHttpServerPlugin implements Plugin {
|
|
132
|
+
name = 'http-server';
|
|
133
|
+
|
|
134
|
+
async init(ctx: PluginContext) {
|
|
135
|
+
const server: IHttpServer = createMyServer(); // Express, Hono, etc.
|
|
136
|
+
ctx.registerService('http-server', server);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// In your API plugin
|
|
141
|
+
class MyApiPlugin implements Plugin {
|
|
142
|
+
name = 'api';
|
|
143
|
+
dependencies = ['http-server'];
|
|
144
|
+
|
|
145
|
+
async start(ctx: PluginContext) {
|
|
146
|
+
const server = ctx.getService<IHttpServer>('http-server');
|
|
147
|
+
|
|
148
|
+
// Register routes - works with any HTTP framework
|
|
149
|
+
server.get('/api/users', async (req, res) => {
|
|
150
|
+
res.json({ users: [] });
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
102
154
|
```
|
|
103
155
|
|
|
156
|
+
**Interface Methods:**
|
|
157
|
+
- `get(path, handler)` - Register GET route
|
|
158
|
+
- `post(path, handler)` - Register POST route
|
|
159
|
+
- `put(path, handler)` - Register PUT route
|
|
160
|
+
- `delete(path, handler)` - Register DELETE route
|
|
161
|
+
- `patch(path, handler)` - Register PATCH route
|
|
162
|
+
- `use(path, handler?)` - Register middleware
|
|
163
|
+
- `listen(port)` - Start server
|
|
164
|
+
- `close()` - Stop server (optional)
|
|
165
|
+
|
|
166
|
+
#### IDataEngine
|
|
167
|
+
|
|
168
|
+
Abstract interface for data persistence. Allows plugins to work with any data layer (ObjectQL, Prisma, TypeORM, etc.) without tight coupling.
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { IDataEngine } from '@objectstack/runtime';
|
|
172
|
+
|
|
173
|
+
// In your data plugin
|
|
174
|
+
class MyDataPlugin implements Plugin {
|
|
175
|
+
name = 'data';
|
|
176
|
+
|
|
177
|
+
async init(ctx: PluginContext) {
|
|
178
|
+
const engine: IDataEngine = createMyDataEngine(); // ObjectQL, Prisma, etc.
|
|
179
|
+
ctx.registerService('data-engine', engine);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// In your business logic plugin
|
|
184
|
+
class MyBusinessPlugin implements Plugin {
|
|
185
|
+
name = 'business';
|
|
186
|
+
dependencies = ['data'];
|
|
187
|
+
|
|
188
|
+
async start(ctx: PluginContext) {
|
|
189
|
+
const engine = ctx.getService<IDataEngine>('data-engine');
|
|
190
|
+
|
|
191
|
+
// CRUD operations - works with any data layer
|
|
192
|
+
const user = await engine.insert('user', { name: 'John' });
|
|
193
|
+
const users = await engine.find('user', { filter: { active: true } });
|
|
194
|
+
await engine.update('user', user.id, { name: 'Jane' });
|
|
195
|
+
await engine.delete('user', user.id);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Interface Methods:**
|
|
201
|
+
- `insert(objectName, data)` - Create a record
|
|
202
|
+
- `find(objectName, query?)` - Query records
|
|
203
|
+
- `update(objectName, id, data)` - Update a record
|
|
204
|
+
- `delete(objectName, id)` - Delete a record
|
|
205
|
+
|
|
206
|
+
### ObjectKernel
|
|
207
|
+
|
|
104
208
|
#### Methods
|
|
105
|
-
- `
|
|
106
|
-
- `
|
|
107
|
-
- `
|
|
108
|
-
- `
|
|
109
|
-
- `
|
|
110
|
-
- `
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
### ObjectQLPlugin
|
|
115
|
-
|
|
116
|
-
#### Constructor
|
|
209
|
+
- `use(plugin: Plugin)`: Register a plugin
|
|
210
|
+
- `bootstrap()`: Initialize and start all plugins
|
|
211
|
+
- `shutdown()`: Stop all plugins in reverse order
|
|
212
|
+
- `getService<T>(name: string)`: Get a service from registry
|
|
213
|
+
- `isRunning()`: Check if kernel is running
|
|
214
|
+
- `getState()`: Get current kernel state
|
|
215
|
+
|
|
216
|
+
### Plugin Interface
|
|
217
|
+
|
|
117
218
|
```typescript
|
|
118
|
-
|
|
219
|
+
interface Plugin {
|
|
220
|
+
name: string; // Unique identifier
|
|
221
|
+
version?: string; // Plugin version
|
|
222
|
+
dependencies?: string[]; // Required plugin names
|
|
223
|
+
|
|
224
|
+
init(ctx: PluginContext): Promise<void>; // Register services
|
|
225
|
+
start?(ctx: PluginContext): Promise<void>; // Execute business logic
|
|
226
|
+
destroy?(): Promise<void>; // Cleanup
|
|
227
|
+
}
|
|
119
228
|
```
|
|
120
229
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
230
|
+
### PluginContext
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
interface PluginContext {
|
|
234
|
+
registerService(name: string, service: any): void;
|
|
235
|
+
getService<T>(name: string): T;
|
|
236
|
+
hook(name: string, handler: Function): void;
|
|
237
|
+
trigger(name: string, ...args: any[]): Promise<void>;
|
|
238
|
+
logger: Console;
|
|
239
|
+
getKernel?(): any;
|
|
240
|
+
}
|
|
241
|
+
```
|
|
124
242
|
|
|
125
243
|
## Examples
|
|
126
244
|
|
|
127
245
|
See the `examples/` directory for complete examples:
|
|
128
246
|
- `examples/host/` - Full server setup with Hono
|
|
129
247
|
- `examples/msw-react-crud/` - Browser-based setup with MSW
|
|
130
|
-
- `
|
|
248
|
+
- `test-mini-kernel.ts` - Comprehensive kernel test suite
|
|
249
|
+
- `packages/runtime/src/test-interfaces.ts` - Capability contract interface examples
|
|
131
250
|
|
|
132
|
-
##
|
|
251
|
+
## Benefits of MiniKernel
|
|
133
252
|
|
|
134
|
-
|
|
253
|
+
1. **True Modularity**: Each plugin is independent and reusable
|
|
254
|
+
2. **Capability Contracts**: Plugins depend on interfaces, not implementations
|
|
255
|
+
3. **Testability**: Mock services easily in tests
|
|
256
|
+
4. **Flexibility**: Load plugins conditionally, swap implementations
|
|
257
|
+
5. **Extensibility**: Add new plugins without modifying kernel
|
|
258
|
+
6. **Clear Dependencies**: Explicit dependency declarations
|
|
259
|
+
7. **Better Architecture**: Separation of concerns with Dependency Inversion
|
|
135
260
|
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
const kernel = new ObjectStackKernel([appConfig, driver]);
|
|
139
|
-
```
|
|
261
|
+
## Best Practices
|
|
140
262
|
|
|
141
|
-
**
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
driver
|
|
147
|
-
]);
|
|
148
|
-
```
|
|
263
|
+
1. **Keep plugins focused**: One responsibility per plugin
|
|
264
|
+
2. **Use services**: Share functionality via service registry
|
|
265
|
+
3. **Declare dependencies**: Make plugin requirements explicit
|
|
266
|
+
4. **Use hooks**: Decouple plugins with event system
|
|
267
|
+
5. **Handle errors**: Implement proper error handling in lifecycle methods
|
|
149
268
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
appConfig,
|
|
156
|
-
driver
|
|
157
|
-
]);
|
|
158
|
-
```
|
|
269
|
+
## Documentation
|
|
270
|
+
|
|
271
|
+
- [MiniKernel Guide](../../MINI_KERNEL_GUIDE.md) - Complete API documentation and patterns
|
|
272
|
+
- [MiniKernel Architecture](../../MINI_KERNEL_ARCHITECTURE.md) - Architecture diagrams and flows
|
|
273
|
+
- [MiniKernel Implementation](../../MINI_KERNEL_IMPLEMENTATION.md) - Implementation details
|
|
159
274
|
|
|
160
275
|
## License
|
|
161
276
|
|
|
162
|
-
|
|
277
|
+
Apache-2.0
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin, PluginContext } from '@objectstack/core';
|
|
2
|
+
/**
|
|
3
|
+
* AppManifestPlugin
|
|
4
|
+
*
|
|
5
|
+
* Adapts a static Manifest JSON into a dynamic Kernel Service.
|
|
6
|
+
* This allows the ObjectQL Engine to "discover" this app during its start phase.
|
|
7
|
+
*
|
|
8
|
+
* Flow:
|
|
9
|
+
* 1. AppPlugin registers `app.<id>` service (init phase)
|
|
10
|
+
* 2. ObjectQL Engine discovers `app.*` services (start phase)
|
|
11
|
+
*/
|
|
12
|
+
export declare class AppManifestPlugin implements Plugin {
|
|
13
|
+
name: string;
|
|
14
|
+
version?: string;
|
|
15
|
+
private manifest;
|
|
16
|
+
constructor(manifest: any);
|
|
17
|
+
init(ctx: PluginContext): Promise<void>;
|
|
18
|
+
start(ctx: PluginContext): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppManifestPlugin
|
|
3
|
+
*
|
|
4
|
+
* Adapts a static Manifest JSON into a dynamic Kernel Service.
|
|
5
|
+
* This allows the ObjectQL Engine to "discover" this app during its start phase.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. AppPlugin registers `app.<id>` service (init phase)
|
|
9
|
+
* 2. ObjectQL Engine discovers `app.*` services (start phase)
|
|
10
|
+
*/
|
|
11
|
+
export class AppManifestPlugin {
|
|
12
|
+
constructor(manifest) {
|
|
13
|
+
this.manifest = manifest;
|
|
14
|
+
// Support both direct manifest (legacy) and Stack Definition (nested manifest)
|
|
15
|
+
const sys = manifest.manifest || manifest;
|
|
16
|
+
const appId = sys.id || sys.name || 'unnamed-app';
|
|
17
|
+
this.name = `plugin.app.${appId}`; // Unique plugin name
|
|
18
|
+
this.version = sys.version;
|
|
19
|
+
}
|
|
20
|
+
async init(ctx) {
|
|
21
|
+
// Support both direct manifest (legacy) and Stack Definition (nested manifest)
|
|
22
|
+
const sys = this.manifest.manifest || this.manifest;
|
|
23
|
+
const appId = sys.id || sys.name;
|
|
24
|
+
ctx.logger.log(`[AppManifestPlugin] Registering App Service: ${appId}`);
|
|
25
|
+
// Register the app manifest as a service
|
|
26
|
+
const serviceName = `app.${appId}`;
|
|
27
|
+
ctx.registerService(serviceName, this.manifest);
|
|
28
|
+
}
|
|
29
|
+
async start(ctx) {
|
|
30
|
+
// No logic needed here.
|
|
31
|
+
// Logic is inverted: The Engine will pull data from this service.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Plugin, PluginContext } from '@objectstack/core';
|
|
2
|
+
/**
|
|
3
|
+
* Driver Plugin
|
|
4
|
+
*
|
|
5
|
+
* Generic plugin wrapper for ObjectQL drivers.
|
|
6
|
+
* Registers a driver with the ObjectQL engine.
|
|
7
|
+
*
|
|
8
|
+
* Dependencies: None (Registers service for ObjectQL to discover)
|
|
9
|
+
* Services: driver.{name}
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const memoryDriver = new InMemoryDriver();
|
|
13
|
+
* const driverPlugin = new DriverPlugin(memoryDriver, 'memory');
|
|
14
|
+
* kernel.use(driverPlugin);
|
|
15
|
+
*/
|
|
16
|
+
export declare class DriverPlugin implements Plugin {
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
private driver;
|
|
20
|
+
constructor(driver: any, driverName?: string);
|
|
21
|
+
init(ctx: PluginContext): Promise<void>;
|
|
22
|
+
start(ctx: PluginContext): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver Plugin
|
|
3
|
+
*
|
|
4
|
+
* Generic plugin wrapper for ObjectQL drivers.
|
|
5
|
+
* Registers a driver with the ObjectQL engine.
|
|
6
|
+
*
|
|
7
|
+
* Dependencies: None (Registers service for ObjectQL to discover)
|
|
8
|
+
* Services: driver.{name}
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const memoryDriver = new InMemoryDriver();
|
|
12
|
+
* const driverPlugin = new DriverPlugin(memoryDriver, 'memory');
|
|
13
|
+
* kernel.use(driverPlugin);
|
|
14
|
+
*/
|
|
15
|
+
export class DriverPlugin {
|
|
16
|
+
constructor(driver, driverName) {
|
|
17
|
+
this.version = '1.0.0';
|
|
18
|
+
this.driver = driver;
|
|
19
|
+
this.name = `com.objectstack.driver.${driverName || driver.name || 'unknown'}`;
|
|
20
|
+
}
|
|
21
|
+
async init(ctx) {
|
|
22
|
+
// Register driver as a service instead of directly to objectql
|
|
23
|
+
const serviceName = `driver.${this.driver.name || 'unknown'}`;
|
|
24
|
+
ctx.registerService(serviceName, this.driver);
|
|
25
|
+
ctx.logger.log(`[DriverPlugin] Registered driver service: ${serviceName}`);
|
|
26
|
+
}
|
|
27
|
+
async start(ctx) {
|
|
28
|
+
// Drivers don't need start phase, initialization happens in init
|
|
29
|
+
ctx.logger.log(`[DriverPlugin] Driver ready: ${this.driver.name || 'unknown'}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { ObjectQL, SchemaRegistry } from '@objectstack/objectql';
|
|
2
|
-
export {
|
|
3
|
-
export { ObjectQLPlugin } from '
|
|
4
|
-
export {
|
|
5
|
-
export
|
|
1
|
+
export { ObjectQL, SchemaRegistry, ObjectStackProtocolImplementation } from '@objectstack/objectql';
|
|
2
|
+
export { ObjectKernel } from '@objectstack/core';
|
|
3
|
+
export { ObjectQLPlugin } from '@objectstack/objectql';
|
|
4
|
+
export { DriverPlugin } from './driver-plugin';
|
|
5
|
+
export { AppManifestPlugin } from './app-manifest-plugin';
|
|
6
|
+
export * from '@objectstack/core';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Export core engine
|
|
2
|
-
export { ObjectQL, SchemaRegistry } from '@objectstack/objectql';
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
export
|
|
2
|
+
export { ObjectQL, SchemaRegistry, ObjectStackProtocolImplementation } from '@objectstack/objectql';
|
|
3
|
+
// Export Kernels
|
|
4
|
+
export { ObjectKernel } from '@objectstack/core';
|
|
5
|
+
// Export Plugins
|
|
6
|
+
export { ObjectQLPlugin } from '@objectstack/objectql';
|
|
7
|
+
export { DriverPlugin } from './driver-plugin';
|
|
8
|
+
export { AppManifestPlugin } from './app-manifest-plugin';
|
|
9
|
+
// Export Types
|
|
10
|
+
export * from '@objectstack/core';
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test file to verify capability contract interfaces
|
|
3
|
+
*
|
|
4
|
+
* This file demonstrates how plugins can implement the IHttpServer
|
|
5
|
+
* and IDataEngine interfaces without depending on concrete implementations.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Example: Mock HTTP Server Plugin
|
|
9
|
+
*
|
|
10
|
+
* Shows how a plugin can implement the IHttpServer interface
|
|
11
|
+
* without depending on Express, Fastify, or any specific framework.
|
|
12
|
+
*/
|
|
13
|
+
class MockHttpServer {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.routes = new Map();
|
|
16
|
+
}
|
|
17
|
+
get(path, handler) {
|
|
18
|
+
this.routes.set(`GET:${path}`, { method: 'GET', handler });
|
|
19
|
+
console.log(`✅ Registered GET ${path}`);
|
|
20
|
+
}
|
|
21
|
+
post(path, handler) {
|
|
22
|
+
this.routes.set(`POST:${path}`, { method: 'POST', handler });
|
|
23
|
+
console.log(`✅ Registered POST ${path}`);
|
|
24
|
+
}
|
|
25
|
+
put(path, handler) {
|
|
26
|
+
this.routes.set(`PUT:${path}`, { method: 'PUT', handler });
|
|
27
|
+
console.log(`✅ Registered PUT ${path}`);
|
|
28
|
+
}
|
|
29
|
+
delete(path, handler) {
|
|
30
|
+
this.routes.set(`DELETE:${path}`, { method: 'DELETE', handler });
|
|
31
|
+
console.log(`✅ Registered DELETE ${path}`);
|
|
32
|
+
}
|
|
33
|
+
patch(path, handler) {
|
|
34
|
+
this.routes.set(`PATCH:${path}`, { method: 'PATCH', handler });
|
|
35
|
+
console.log(`✅ Registered PATCH ${path}`);
|
|
36
|
+
}
|
|
37
|
+
use(path, handler) {
|
|
38
|
+
console.log(`✅ Registered middleware`);
|
|
39
|
+
}
|
|
40
|
+
async listen(port) {
|
|
41
|
+
console.log(`✅ Mock HTTP server listening on port ${port}`);
|
|
42
|
+
}
|
|
43
|
+
async close() {
|
|
44
|
+
console.log(`✅ Mock HTTP server closed`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Example: Mock Data Engine Plugin
|
|
49
|
+
*
|
|
50
|
+
* Shows how a plugin can implement the IDataEngine interface
|
|
51
|
+
* without depending on ObjectQL, Prisma, or any specific database.
|
|
52
|
+
*/
|
|
53
|
+
class MockDataEngine {
|
|
54
|
+
constructor() {
|
|
55
|
+
this.store = new Map();
|
|
56
|
+
this.idCounter = 0;
|
|
57
|
+
}
|
|
58
|
+
async insert(objectName, data) {
|
|
59
|
+
if (!this.store.has(objectName)) {
|
|
60
|
+
this.store.set(objectName, new Map());
|
|
61
|
+
}
|
|
62
|
+
const id = `${objectName}_${++this.idCounter}`;
|
|
63
|
+
const record = { id, ...data };
|
|
64
|
+
this.store.get(objectName).set(id, record);
|
|
65
|
+
console.log(`✅ Inserted into ${objectName}:`, record);
|
|
66
|
+
return record;
|
|
67
|
+
}
|
|
68
|
+
async find(objectName, query) {
|
|
69
|
+
const objectStore = this.store.get(objectName);
|
|
70
|
+
if (!objectStore) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const results = Array.from(objectStore.values());
|
|
74
|
+
console.log(`✅ Found ${results.length} records in ${objectName}`);
|
|
75
|
+
return results;
|
|
76
|
+
}
|
|
77
|
+
async update(objectName, id, data) {
|
|
78
|
+
const objectStore = this.store.get(objectName);
|
|
79
|
+
if (!objectStore || !objectStore.has(id)) {
|
|
80
|
+
throw new Error(`Record ${id} not found in ${objectName}`);
|
|
81
|
+
}
|
|
82
|
+
const existing = objectStore.get(id);
|
|
83
|
+
const updated = { ...existing, ...data };
|
|
84
|
+
objectStore.set(id, updated);
|
|
85
|
+
console.log(`✅ Updated ${objectName}/${id}:`, updated);
|
|
86
|
+
return updated;
|
|
87
|
+
}
|
|
88
|
+
async delete(objectName, id) {
|
|
89
|
+
const objectStore = this.store.get(objectName);
|
|
90
|
+
if (!objectStore) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const deleted = objectStore.delete(id);
|
|
94
|
+
console.log(`✅ Deleted ${objectName}/${id}: ${deleted}`);
|
|
95
|
+
return deleted;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Test the interfaces
|
|
100
|
+
*/
|
|
101
|
+
async function testInterfaces() {
|
|
102
|
+
console.log('\n=== Testing IHttpServer Interface ===\n');
|
|
103
|
+
const httpServer = new MockHttpServer();
|
|
104
|
+
// Register routes using the interface
|
|
105
|
+
httpServer.get('/api/users', async (req, res) => {
|
|
106
|
+
res.json({ users: [] });
|
|
107
|
+
});
|
|
108
|
+
httpServer.post('/api/users', async (req, res) => {
|
|
109
|
+
res.status(201).json({ id: 1, ...req.body });
|
|
110
|
+
});
|
|
111
|
+
await httpServer.listen(3000);
|
|
112
|
+
console.log('\n=== Testing IDataEngine Interface ===\n');
|
|
113
|
+
const dataEngine = new MockDataEngine();
|
|
114
|
+
// Use the data engine interface
|
|
115
|
+
const user1 = await dataEngine.insert('user', {
|
|
116
|
+
name: 'John Doe',
|
|
117
|
+
email: 'john@example.com'
|
|
118
|
+
});
|
|
119
|
+
const user2 = await dataEngine.insert('user', {
|
|
120
|
+
name: 'Jane Smith',
|
|
121
|
+
email: 'jane@example.com'
|
|
122
|
+
});
|
|
123
|
+
const users = await dataEngine.find('user');
|
|
124
|
+
console.log(`Found ${users.length} users after inserts`);
|
|
125
|
+
const updatedUser = await dataEngine.update('user', user1.id, {
|
|
126
|
+
name: 'John Updated'
|
|
127
|
+
});
|
|
128
|
+
console.log(`Updated user:`, updatedUser);
|
|
129
|
+
const deleted = await dataEngine.delete('user', user2.id);
|
|
130
|
+
console.log(`Delete result: ${deleted}`);
|
|
131
|
+
console.log('\n✅ All interface tests passed!\n');
|
|
132
|
+
if (httpServer.close) {
|
|
133
|
+
await httpServer.close();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Run tests
|
|
137
|
+
testInterfaces().catch(console.error);
|
|
138
|
+
export {};
|