@objectstack/runtime 0.4.1 → 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 +28 -0
- package/README.md +277 -0
- 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 -4
- package/dist/index.js +9 -4
- 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 -4
- package/src/test-interfaces.ts +170 -0
- package/dist/kernel.d.ts +0 -142
- package/dist/kernel.js +0 -157
- 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 -182
- package/src/protocol.ts +0 -129
- package/src/types.ts +0 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
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
|
+
|
|
21
|
+
## 0.4.2
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Unify all package versions to 0.4.2
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
- @objectstack/spec@0.4.2
|
|
28
|
+
- @objectstack/objectql@0.4.2
|
|
29
|
+
- @objectstack/types@0.4.2
|
|
30
|
+
|
|
3
31
|
## 0.4.1
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# @objectstack/runtime
|
|
2
|
+
|
|
3
|
+
ObjectStack Standard System Library
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
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
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @objectstack/runtime
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Basic Setup (Recommended)
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { ObjectKernel } from '@objectstack/core';
|
|
27
|
+
import { ObjectQLPlugin, DriverPlugin, AppManifestPlugin } from '@objectstack/runtime';
|
|
28
|
+
import { InMemoryDriver } from '@objectstack/driver-memory';
|
|
29
|
+
|
|
30
|
+
const kernel = new ObjectKernel();
|
|
31
|
+
|
|
32
|
+
kernel
|
|
33
|
+
// Register ObjectQL engine
|
|
34
|
+
.use(new ObjectQLPlugin())
|
|
35
|
+
|
|
36
|
+
// Add database driver
|
|
37
|
+
.use(new DriverPlugin(new InMemoryDriver(), 'memory'))
|
|
38
|
+
|
|
39
|
+
// Add your app configurations
|
|
40
|
+
// .use(new AppManifestPlugin(appConfig));
|
|
41
|
+
|
|
42
|
+
await kernel.bootstrap();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Custom ObjectQL Instance
|
|
46
|
+
|
|
47
|
+
If you have a separate ObjectQL implementation or need custom configuration:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { ObjectKernel, ObjectQLPlugin, DriverPlugin, ObjectQL } from '@objectstack/runtime';
|
|
51
|
+
|
|
52
|
+
// Create custom ObjectQL instance
|
|
53
|
+
const customQL = new ObjectQL({
|
|
54
|
+
env: 'production',
|
|
55
|
+
customConfig: true
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Pre-configure with custom hooks
|
|
59
|
+
customQL.registerHook('beforeInsert', async (ctx) => {
|
|
60
|
+
console.log(`Inserting into ${ctx.object}`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const kernel = new ObjectKernel();
|
|
64
|
+
|
|
65
|
+
kernel
|
|
66
|
+
// Use your custom ObjectQL instance
|
|
67
|
+
.use(new ObjectQLPlugin(customQL))
|
|
68
|
+
|
|
69
|
+
// Add driver
|
|
70
|
+
.use(new DriverPlugin(new InMemoryDriver(), 'memory'));
|
|
71
|
+
|
|
72
|
+
await kernel.bootstrap();
|
|
73
|
+
|
|
74
|
+
// Access ObjectQL via service registry
|
|
75
|
+
const objectql = kernel.getService('objectql');
|
|
76
|
+
```
|
|
77
|
+
|
|
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
|
|
89
|
+
|
|
90
|
+
#### ObjectQLPlugin
|
|
91
|
+
Registers the ObjectQL data engine as a service.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
new ObjectQLPlugin() // Default instance
|
|
95
|
+
new ObjectQLPlugin(customQL) // Custom instance
|
|
96
|
+
new ObjectQLPlugin(undefined, { env: 'prod' }) // With context
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Services**: `'objectql'`
|
|
100
|
+
|
|
101
|
+
#### DriverPlugin
|
|
102
|
+
Registers a data driver with ObjectQL.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
new DriverPlugin(driver, 'driver-name')
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Dependencies**: `['com.objectstack.engine.objectql']`
|
|
109
|
+
|
|
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']`
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
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.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
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
|
+
}
|
|
154
|
+
```
|
|
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
|
+
|
|
208
|
+
#### Methods
|
|
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
|
+
|
|
218
|
+
```typescript
|
|
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
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
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
|
+
```
|
|
242
|
+
|
|
243
|
+
## Examples
|
|
244
|
+
|
|
245
|
+
See the `examples/` directory for complete examples:
|
|
246
|
+
- `examples/host/` - Full server setup with Hono
|
|
247
|
+
- `examples/msw-react-crud/` - Browser-based setup with MSW
|
|
248
|
+
- `test-mini-kernel.ts` - Comprehensive kernel test suite
|
|
249
|
+
- `packages/runtime/src/test-interfaces.ts` - Capability contract interface examples
|
|
250
|
+
|
|
251
|
+
## Benefits of MiniKernel
|
|
252
|
+
|
|
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
|
|
260
|
+
|
|
261
|
+
## Best Practices
|
|
262
|
+
|
|
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
|
|
268
|
+
|
|
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
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
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,4 +1,6 @@
|
|
|
1
|
-
export { ObjectQL, SchemaRegistry } from '@objectstack/objectql';
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
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,5 +1,10 @@
|
|
|
1
1
|
// Export core engine
|
|
2
|
-
export { ObjectQL, SchemaRegistry } from '@objectstack/objectql';
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
|
|
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 {};
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "ObjectStack Core Runtime & Query Engine",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
7
8
|
"dependencies": {
|
|
8
|
-
"@objectstack/
|
|
9
|
-
"@objectstack/
|
|
10
|
-
"@objectstack/
|
|
9
|
+
"@objectstack/core": "0.6.0",
|
|
10
|
+
"@objectstack/objectql": "0.6.0",
|
|
11
|
+
"@objectstack/types": "0.6.0",
|
|
12
|
+
"@objectstack/spec": "0.6.0"
|
|
11
13
|
},
|
|
12
14
|
"devDependencies": {
|
|
13
15
|
"typescript": "^5.0.0"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Plugin, PluginContext } from '@objectstack/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AppManifestPlugin
|
|
5
|
+
*
|
|
6
|
+
* Adapts a static Manifest JSON into a dynamic Kernel Service.
|
|
7
|
+
* This allows the ObjectQL Engine to "discover" this app during its start phase.
|
|
8
|
+
*
|
|
9
|
+
* Flow:
|
|
10
|
+
* 1. AppPlugin registers `app.<id>` service (init phase)
|
|
11
|
+
* 2. ObjectQL Engine discovers `app.*` services (start phase)
|
|
12
|
+
*/
|
|
13
|
+
export class AppManifestPlugin implements Plugin {
|
|
14
|
+
name: string;
|
|
15
|
+
version?: string;
|
|
16
|
+
|
|
17
|
+
// Dependencies removed: This plugin produces data. It doesn't need to consume the engine to register itself.
|
|
18
|
+
// Making it dependency-free allows it to initialize BEFORE the engine if needed.
|
|
19
|
+
|
|
20
|
+
private manifest: any;
|
|
21
|
+
|
|
22
|
+
constructor(manifest: any) {
|
|
23
|
+
this.manifest = manifest;
|
|
24
|
+
// Support both direct manifest (legacy) and Stack Definition (nested manifest)
|
|
25
|
+
const sys = manifest.manifest || manifest;
|
|
26
|
+
const appId = sys.id || sys.name || 'unnamed-app';
|
|
27
|
+
|
|
28
|
+
this.name = `plugin.app.${appId}`; // Unique plugin name
|
|
29
|
+
this.version = sys.version;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async init(ctx: PluginContext) {
|
|
33
|
+
// Support both direct manifest (legacy) and Stack Definition (nested manifest)
|
|
34
|
+
const sys = this.manifest.manifest || this.manifest;
|
|
35
|
+
const appId = sys.id || sys.name;
|
|
36
|
+
|
|
37
|
+
ctx.logger.log(`[AppManifestPlugin] Registering App Service: ${appId}`);
|
|
38
|
+
|
|
39
|
+
// Register the app manifest as a service
|
|
40
|
+
const serviceName = `app.${appId}`;
|
|
41
|
+
ctx.registerService(serviceName, this.manifest);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async start(ctx: PluginContext) {
|
|
45
|
+
// No logic needed here.
|
|
46
|
+
// Logic is inverted: The Engine will pull data from this service.
|
|
47
|
+
}
|
|
48
|
+
}
|