@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/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
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Plugin, PluginContext } from '@objectstack/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Driver Plugin
|
|
5
|
+
*
|
|
6
|
+
* Generic plugin wrapper for ObjectQL drivers.
|
|
7
|
+
* Registers a driver with the ObjectQL engine.
|
|
8
|
+
*
|
|
9
|
+
* Dependencies: None (Registers service for ObjectQL to discover)
|
|
10
|
+
* Services: driver.{name}
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const memoryDriver = new InMemoryDriver();
|
|
14
|
+
* const driverPlugin = new DriverPlugin(memoryDriver, 'memory');
|
|
15
|
+
* kernel.use(driverPlugin);
|
|
16
|
+
*/
|
|
17
|
+
export class DriverPlugin implements Plugin {
|
|
18
|
+
name: string;
|
|
19
|
+
version = '1.0.0';
|
|
20
|
+
// dependencies = ['com.objectstack.engine.objectql']; // Removed: Driver is a producer, not strictly a consumer during init
|
|
21
|
+
|
|
22
|
+
private driver: any;
|
|
23
|
+
|
|
24
|
+
constructor(driver: any, driverName?: string) {
|
|
25
|
+
this.driver = driver;
|
|
26
|
+
this.name = `com.objectstack.driver.${driverName || driver.name || 'unknown'}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async init(ctx: PluginContext) {
|
|
30
|
+
// Register driver as a service instead of directly to objectql
|
|
31
|
+
const serviceName = `driver.${this.driver.name || 'unknown'}`;
|
|
32
|
+
ctx.registerService(serviceName, this.driver);
|
|
33
|
+
ctx.logger.log(`[DriverPlugin] Registered driver service: ${serviceName}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async start(ctx: PluginContext) {
|
|
37
|
+
// Drivers don't need start phase, initialization happens in init
|
|
38
|
+
ctx.logger.log(`[DriverPlugin] Driver ready: ${this.driver.name || 'unknown'}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
// Export core engine
|
|
2
|
-
export { ObjectQL, SchemaRegistry } from '@objectstack/objectql';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export {
|
|
2
|
+
export { ObjectQL, SchemaRegistry, ObjectStackProtocolImplementation } from '@objectstack/objectql';
|
|
3
|
+
|
|
4
|
+
// Export Kernels
|
|
5
|
+
export { ObjectKernel } from '@objectstack/core';
|
|
6
|
+
|
|
7
|
+
// Export Plugins
|
|
8
|
+
export { ObjectQLPlugin } from '@objectstack/objectql';
|
|
9
|
+
export { DriverPlugin } from './driver-plugin';
|
|
10
|
+
export { AppManifestPlugin } from './app-manifest-plugin';
|
|
11
|
+
|
|
12
|
+
// Export Types
|
|
13
|
+
export * from '@objectstack/core';
|
|
6
14
|
|
|
7
|
-
export * from './types';
|
|
8
15
|
|
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
import { IHttpServer, IDataEngine, RouteHandler, IHttpRequest, IHttpResponse, Middleware, DataEngineQueryOptions } from './index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Example: Mock HTTP Server Plugin
|
|
12
|
+
*
|
|
13
|
+
* Shows how a plugin can implement the IHttpServer interface
|
|
14
|
+
* without depending on Express, Fastify, or any specific framework.
|
|
15
|
+
*/
|
|
16
|
+
class MockHttpServer implements IHttpServer {
|
|
17
|
+
private routes: Map<string, { method: string; handler: RouteHandler }> = new Map();
|
|
18
|
+
|
|
19
|
+
get(path: string, handler: RouteHandler): void {
|
|
20
|
+
this.routes.set(`GET:${path}`, { method: 'GET', handler });
|
|
21
|
+
console.log(`✅ Registered GET ${path}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
post(path: string, handler: RouteHandler): void {
|
|
25
|
+
this.routes.set(`POST:${path}`, { method: 'POST', handler });
|
|
26
|
+
console.log(`✅ Registered POST ${path}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
put(path: string, handler: RouteHandler): void {
|
|
30
|
+
this.routes.set(`PUT:${path}`, { method: 'PUT', handler });
|
|
31
|
+
console.log(`✅ Registered PUT ${path}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
delete(path: string, handler: RouteHandler): void {
|
|
35
|
+
this.routes.set(`DELETE:${path}`, { method: 'DELETE', handler });
|
|
36
|
+
console.log(`✅ Registered DELETE ${path}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
patch(path: string, handler: RouteHandler): void {
|
|
40
|
+
this.routes.set(`PATCH:${path}`, { method: 'PATCH', handler });
|
|
41
|
+
console.log(`✅ Registered PATCH ${path}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
use(path: string | Middleware, handler?: Middleware): void {
|
|
45
|
+
console.log(`✅ Registered middleware`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async listen(port: number): Promise<void> {
|
|
49
|
+
console.log(`✅ Mock HTTP server listening on port ${port}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async close(): Promise<void> {
|
|
53
|
+
console.log(`✅ Mock HTTP server closed`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Example: Mock Data Engine Plugin
|
|
59
|
+
*
|
|
60
|
+
* Shows how a plugin can implement the IDataEngine interface
|
|
61
|
+
* without depending on ObjectQL, Prisma, or any specific database.
|
|
62
|
+
*/
|
|
63
|
+
class MockDataEngine implements IDataEngine {
|
|
64
|
+
private store: Map<string, Map<string, any>> = new Map();
|
|
65
|
+
private idCounter = 0;
|
|
66
|
+
|
|
67
|
+
async insert(objectName: string, data: any): Promise<any> {
|
|
68
|
+
if (!this.store.has(objectName)) {
|
|
69
|
+
this.store.set(objectName, new Map());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const id = `${objectName}_${++this.idCounter}`;
|
|
73
|
+
const record = { id, ...data };
|
|
74
|
+
this.store.get(objectName)!.set(id, record);
|
|
75
|
+
|
|
76
|
+
console.log(`✅ Inserted into ${objectName}:`, record);
|
|
77
|
+
return record;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async find(objectName: string, query?: DataEngineQueryOptions): Promise<any[]> {
|
|
81
|
+
const objectStore = this.store.get(objectName);
|
|
82
|
+
if (!objectStore) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const results = Array.from(objectStore.values());
|
|
87
|
+
console.log(`✅ Found ${results.length} records in ${objectName}`);
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async update(objectName: string, id: any, data: any): Promise<any> {
|
|
92
|
+
const objectStore = this.store.get(objectName);
|
|
93
|
+
if (!objectStore || !objectStore.has(id)) {
|
|
94
|
+
throw new Error(`Record ${id} not found in ${objectName}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const existing = objectStore.get(id);
|
|
98
|
+
const updated = { ...existing, ...data };
|
|
99
|
+
objectStore.set(id, updated);
|
|
100
|
+
|
|
101
|
+
console.log(`✅ Updated ${objectName}/${id}:`, updated);
|
|
102
|
+
return updated;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async delete(objectName: string, id: any): Promise<boolean> {
|
|
106
|
+
const objectStore = this.store.get(objectName);
|
|
107
|
+
if (!objectStore) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const deleted = objectStore.delete(id);
|
|
112
|
+
console.log(`✅ Deleted ${objectName}/${id}: ${deleted}`);
|
|
113
|
+
return deleted;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Test the interfaces
|
|
119
|
+
*/
|
|
120
|
+
async function testInterfaces() {
|
|
121
|
+
console.log('\n=== Testing IHttpServer Interface ===\n');
|
|
122
|
+
|
|
123
|
+
const httpServer: IHttpServer = new MockHttpServer();
|
|
124
|
+
|
|
125
|
+
// Register routes using the interface
|
|
126
|
+
httpServer.get('/api/users', async (req, res) => {
|
|
127
|
+
res.json({ users: [] });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
httpServer.post('/api/users', async (req, res) => {
|
|
131
|
+
res.status(201).json({ id: 1, ...req.body });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await httpServer.listen(3000);
|
|
135
|
+
|
|
136
|
+
console.log('\n=== Testing IDataEngine Interface ===\n');
|
|
137
|
+
|
|
138
|
+
const dataEngine: IDataEngine = new MockDataEngine();
|
|
139
|
+
|
|
140
|
+
// Use the data engine interface
|
|
141
|
+
const user1 = await dataEngine.insert('user', {
|
|
142
|
+
name: 'John Doe',
|
|
143
|
+
email: 'john@example.com'
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const user2 = await dataEngine.insert('user', {
|
|
147
|
+
name: 'Jane Smith',
|
|
148
|
+
email: 'jane@example.com'
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const users = await dataEngine.find('user');
|
|
152
|
+
console.log(`Found ${users.length} users after inserts`);
|
|
153
|
+
|
|
154
|
+
const updatedUser = await dataEngine.update('user', user1.id, {
|
|
155
|
+
name: 'John Updated'
|
|
156
|
+
});
|
|
157
|
+
console.log(`Updated user:`, updatedUser);
|
|
158
|
+
|
|
159
|
+
const deleted = await dataEngine.delete('user', user2.id);
|
|
160
|
+
console.log(`Delete result: ${deleted}`);
|
|
161
|
+
|
|
162
|
+
console.log('\n✅ All interface tests passed!\n');
|
|
163
|
+
|
|
164
|
+
if (httpServer.close) {
|
|
165
|
+
await httpServer.close();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Run tests
|
|
170
|
+
testInterfaces().catch(console.error);
|
package/dist/kernel.d.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { ObjectQL } from '@objectstack/objectql';
|
|
2
|
-
/**
|
|
3
|
-
* ObjectStack Kernel (Microkernel)
|
|
4
|
-
*
|
|
5
|
-
* The central container orchestrating the application lifecycle,
|
|
6
|
-
* plugins, and the core ObjectQL engine.
|
|
7
|
-
*/
|
|
8
|
-
export declare class ObjectStackKernel {
|
|
9
|
-
ql?: ObjectQL;
|
|
10
|
-
private plugins;
|
|
11
|
-
constructor(plugins?: any[]);
|
|
12
|
-
/**
|
|
13
|
-
* Ensure ObjectQL engine is initialized
|
|
14
|
-
* @throws Error if ObjectQL is not available
|
|
15
|
-
*/
|
|
16
|
-
private ensureObjectQL;
|
|
17
|
-
start(): Promise<void>;
|
|
18
|
-
seed(): Promise<void>;
|
|
19
|
-
find(objectName: string, query: any): Promise<{
|
|
20
|
-
value: any;
|
|
21
|
-
count: any;
|
|
22
|
-
}>;
|
|
23
|
-
get(objectName: string, id: string): Promise<any>;
|
|
24
|
-
create(objectName: string, data: any): Promise<any>;
|
|
25
|
-
update(objectName: string, id: string, data: any): Promise<any>;
|
|
26
|
-
delete(objectName: string, id: string): Promise<any>;
|
|
27
|
-
getMetadata(objectName: string): {
|
|
28
|
-
fields: Record<string, {
|
|
29
|
-
type: "number" | "boolean" | "code" | "date" | "text" | "textarea" | "email" | "url" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "file" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "json" | "color" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "tags" | "vector";
|
|
30
|
-
required: boolean;
|
|
31
|
-
searchable: boolean;
|
|
32
|
-
multiple: boolean;
|
|
33
|
-
unique: boolean;
|
|
34
|
-
deleteBehavior: "set_null" | "cascade" | "restrict";
|
|
35
|
-
hidden: boolean;
|
|
36
|
-
readonly: boolean;
|
|
37
|
-
encryption: boolean;
|
|
38
|
-
index: boolean;
|
|
39
|
-
externalId: boolean;
|
|
40
|
-
options?: {
|
|
41
|
-
value: string;
|
|
42
|
-
label: string;
|
|
43
|
-
color?: string | undefined;
|
|
44
|
-
default?: boolean | undefined;
|
|
45
|
-
}[] | undefined;
|
|
46
|
-
min?: number | undefined;
|
|
47
|
-
max?: number | undefined;
|
|
48
|
-
formula?: string | undefined;
|
|
49
|
-
label?: string | undefined;
|
|
50
|
-
precision?: number | undefined;
|
|
51
|
-
name?: string | undefined;
|
|
52
|
-
description?: string | undefined;
|
|
53
|
-
format?: string | undefined;
|
|
54
|
-
defaultValue?: any;
|
|
55
|
-
maxLength?: number | undefined;
|
|
56
|
-
minLength?: number | undefined;
|
|
57
|
-
scale?: number | undefined;
|
|
58
|
-
reference?: string | undefined;
|
|
59
|
-
referenceFilters?: string[] | undefined;
|
|
60
|
-
writeRequiresMasterRead?: boolean | undefined;
|
|
61
|
-
expression?: string | undefined;
|
|
62
|
-
summaryOperations?: {
|
|
63
|
-
object: string;
|
|
64
|
-
function: "count" | "sum" | "avg" | "min" | "max";
|
|
65
|
-
field: string;
|
|
66
|
-
} | undefined;
|
|
67
|
-
language?: string | undefined;
|
|
68
|
-
theme?: string | undefined;
|
|
69
|
-
lineNumbers?: boolean | undefined;
|
|
70
|
-
maxRating?: number | undefined;
|
|
71
|
-
allowHalf?: boolean | undefined;
|
|
72
|
-
displayMap?: boolean | undefined;
|
|
73
|
-
allowGeocoding?: boolean | undefined;
|
|
74
|
-
addressFormat?: "us" | "uk" | "international" | undefined;
|
|
75
|
-
colorFormat?: "hex" | "rgb" | "rgba" | "hsl" | undefined;
|
|
76
|
-
allowAlpha?: boolean | undefined;
|
|
77
|
-
presetColors?: string[] | undefined;
|
|
78
|
-
step?: number | undefined;
|
|
79
|
-
showValue?: boolean | undefined;
|
|
80
|
-
marks?: Record<string, string> | undefined;
|
|
81
|
-
barcodeFormat?: "qr" | "ean13" | "ean8" | "code128" | "code39" | "upca" | "upce" | undefined;
|
|
82
|
-
qrErrorCorrection?: "L" | "M" | "Q" | "H" | undefined;
|
|
83
|
-
displayValue?: boolean | undefined;
|
|
84
|
-
allowScanning?: boolean | undefined;
|
|
85
|
-
currencyConfig?: {
|
|
86
|
-
precision: number;
|
|
87
|
-
currencyMode: "dynamic" | "fixed";
|
|
88
|
-
defaultCurrency: string;
|
|
89
|
-
} | undefined;
|
|
90
|
-
vectorConfig?: {
|
|
91
|
-
dimensions: number;
|
|
92
|
-
distanceMetric: "cosine" | "euclidean" | "dotProduct" | "manhattan";
|
|
93
|
-
normalized: boolean;
|
|
94
|
-
indexed: boolean;
|
|
95
|
-
indexType?: "flat" | "hnsw" | "ivfflat" | undefined;
|
|
96
|
-
} | undefined;
|
|
97
|
-
}>;
|
|
98
|
-
name: string;
|
|
99
|
-
active: boolean;
|
|
100
|
-
isSystem: boolean;
|
|
101
|
-
abstract: boolean;
|
|
102
|
-
datasource: string;
|
|
103
|
-
tags?: string[] | undefined;
|
|
104
|
-
label?: string | undefined;
|
|
105
|
-
description?: string | undefined;
|
|
106
|
-
search?: {
|
|
107
|
-
fields: string[];
|
|
108
|
-
displayFields?: string[] | undefined;
|
|
109
|
-
filters?: string[] | undefined;
|
|
110
|
-
} | undefined;
|
|
111
|
-
pluralLabel?: string | undefined;
|
|
112
|
-
icon?: string | undefined;
|
|
113
|
-
tableName?: string | undefined;
|
|
114
|
-
indexes?: {
|
|
115
|
-
fields: string[];
|
|
116
|
-
type?: "hash" | "btree" | "gin" | "gist" | undefined;
|
|
117
|
-
name?: string | undefined;
|
|
118
|
-
unique?: boolean | undefined;
|
|
119
|
-
}[] | undefined;
|
|
120
|
-
validations?: any[] | undefined;
|
|
121
|
-
titleFormat?: string | undefined;
|
|
122
|
-
compactLayout?: string[] | undefined;
|
|
123
|
-
enable?: {
|
|
124
|
-
searchable: boolean;
|
|
125
|
-
trackHistory: boolean;
|
|
126
|
-
apiEnabled: boolean;
|
|
127
|
-
files: boolean;
|
|
128
|
-
feeds: boolean;
|
|
129
|
-
activities: boolean;
|
|
130
|
-
trash: boolean;
|
|
131
|
-
mru: boolean;
|
|
132
|
-
clone: boolean;
|
|
133
|
-
apiMethods?: ("update" | "delete" | "get" | "list" | "create" | "upsert" | "bulk" | "aggregate" | "history" | "search" | "restore" | "purge" | "import" | "export")[] | undefined;
|
|
134
|
-
} | undefined;
|
|
135
|
-
};
|
|
136
|
-
getView(objectName: string, viewType?: 'list' | 'form'): {
|
|
137
|
-
type: string;
|
|
138
|
-
title: string;
|
|
139
|
-
columns: {
|
|
140
|
-
field: string;
|
|
141
|
-
label: string;
|
|
142
|
-
width: number;
|
|
143
|
-
}[];
|
|
144
|
-
actions: string[];
|
|
145
|
-
} | null;
|
|
146
|
-
private ensureSchema;
|
|
147
|
-
}
|
package/dist/kernel.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { SchemaRegistry, ObjectQL } from '@objectstack/objectql';
|
|
2
|
-
/**
|
|
3
|
-
* ObjectStack Kernel (Microkernel)
|
|
4
|
-
*
|
|
5
|
-
* The central container orchestrating the application lifecycle,
|
|
6
|
-
* plugins, and the core ObjectQL engine.
|
|
7
|
-
*/
|
|
8
|
-
export class ObjectStackKernel {
|
|
9
|
-
constructor(plugins = []) {
|
|
10
|
-
this.plugins = plugins;
|
|
11
|
-
// Check if any plugin provides ObjectQL via type: 'objectql'
|
|
12
|
-
// This aligns with the manifest schema that supports objectql as a package type
|
|
13
|
-
const hasObjectQLPlugin = plugins.some(p => p && typeof p === 'object' && p.type === 'objectql');
|
|
14
|
-
if (!hasObjectQLPlugin) {
|
|
15
|
-
// Backward compatibility: Initialize ObjectQL directly if no plugin provides it
|
|
16
|
-
console.warn('[Kernel] No ObjectQL plugin found, using default initialization. Consider using ObjectQLPlugin.');
|
|
17
|
-
this.ql = new ObjectQL({
|
|
18
|
-
env: process.env.NODE_ENV || 'development'
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Ensure ObjectQL engine is initialized
|
|
24
|
-
* @throws Error if ObjectQL is not available
|
|
25
|
-
*/
|
|
26
|
-
ensureObjectQL() {
|
|
27
|
-
if (!this.ql) {
|
|
28
|
-
throw new Error('[Kernel] ObjectQL engine not initialized. Ensure ObjectQLPlugin is registered or kernel is properly initialized.');
|
|
29
|
-
}
|
|
30
|
-
return this.ql;
|
|
31
|
-
}
|
|
32
|
-
async start() {
|
|
33
|
-
console.log('[Kernel] Starting...');
|
|
34
|
-
// 0. Register Provided Plugins
|
|
35
|
-
for (const p of this.plugins) {
|
|
36
|
-
// Check if it is a Runtime Plugin (System Capability)
|
|
37
|
-
if ('onStart' in p || 'install' in p) {
|
|
38
|
-
console.log(`[Kernel] Loading Runtime Plugin: ${p.name}`);
|
|
39
|
-
if (p.install)
|
|
40
|
-
await p.install({ engine: this });
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
// Otherwise treat as App Manifest
|
|
44
|
-
console.log(`[Kernel] Loading App Manifest: ${p.id || p.name}`);
|
|
45
|
-
SchemaRegistry.registerPlugin(p);
|
|
46
|
-
// Register Objects from App/Plugin
|
|
47
|
-
if (p.objects) {
|
|
48
|
-
for (const obj of p.objects) {
|
|
49
|
-
SchemaRegistry.registerObject(obj);
|
|
50
|
-
console.log(`[Kernel] Registered Object: ${obj.name}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// 1. Load Drivers (Default to Memory if none provided in plugins)
|
|
55
|
-
// TODO: Detect driver from plugins. For now, we still hard load memory driver if needed?
|
|
56
|
-
// In strict mode, user should pass driver in plugins array (DriverManifest).
|
|
57
|
-
// check if driver is registered
|
|
58
|
-
// For Backwards Compat / Easy Dev, try dynamic import of memory driver if installed
|
|
59
|
-
try {
|
|
60
|
-
// @ts-ignore
|
|
61
|
-
const { InMemoryDriver } = await import('@objectstack/driver-memory');
|
|
62
|
-
const driver = new InMemoryDriver();
|
|
63
|
-
this.ensureObjectQL().registerDriver(driver);
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
// Ignore if not present
|
|
67
|
-
}
|
|
68
|
-
// 2. Initialize Engine
|
|
69
|
-
await this.ensureObjectQL().init();
|
|
70
|
-
// 3. Seed Data
|
|
71
|
-
await this.seed();
|
|
72
|
-
// 4. Start Runtime Plugins
|
|
73
|
-
for (const p of this.plugins) {
|
|
74
|
-
if (('onStart' in p) && typeof p.onStart === 'function') {
|
|
75
|
-
console.log(`[Kernel] Starting Plugin: ${p.name}`);
|
|
76
|
-
await p.onStart({ engine: this });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
async seed() {
|
|
81
|
-
// If no driver registered yet, this might fail or wait.
|
|
82
|
-
// In real world, we wait for 'ready' event.
|
|
83
|
-
try {
|
|
84
|
-
// Mock System Table
|
|
85
|
-
try {
|
|
86
|
-
// We don't have SystemStatus defined in schema usually, skipping for general engine
|
|
87
|
-
// await this.ql.insert('SystemStatus', { status: 'OK', uptime: 0 });
|
|
88
|
-
}
|
|
89
|
-
catch { }
|
|
90
|
-
// Iterate over all registered plugins/apps and check for 'data' property in manifest
|
|
91
|
-
const plugins = SchemaRegistry.getRegisteredTypes(); // This returns types like 'plugin', 'app'
|
|
92
|
-
// This is a bit hacky because we don't have a direct "getAllManifests" API exposed easily
|
|
93
|
-
// We will iterate known apps for now, or improve Registry API later.
|
|
94
|
-
// Actually, SchemaRegistry.listItems('app') returns the manifests!
|
|
95
|
-
const apps = [...SchemaRegistry.listItems('app'), ...SchemaRegistry.listItems('plugin')];
|
|
96
|
-
for (const appItem of apps) {
|
|
97
|
-
const app = appItem; // Cast to access data prop safely
|
|
98
|
-
if (app.data && Array.isArray(app.data)) {
|
|
99
|
-
console.log(`[Kernel] Seeding data for ${app.name || app.id}...`);
|
|
100
|
-
for (const seed of app.data) {
|
|
101
|
-
try {
|
|
102
|
-
// Check if data exists
|
|
103
|
-
const existing = await this.ensureObjectQL().find(seed.object, { top: 1 });
|
|
104
|
-
if (existing.length === 0) {
|
|
105
|
-
console.log(`[Kernel] Inserting ${seed.records.length} records into ${seed.object}`);
|
|
106
|
-
for (const record of seed.records) {
|
|
107
|
-
await this.ensureObjectQL().insert(seed.object, record);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
catch (e) {
|
|
112
|
-
console.warn(`[Kernel] Failed to seed ${seed.object}`, e);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
catch (e) {
|
|
119
|
-
console.warn('Seed failed (driver might not be ready):', e);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// Forward methods to ObjectQL
|
|
123
|
-
async find(objectName, query) {
|
|
124
|
-
this.ensureSchema(objectName);
|
|
125
|
-
const results = await this.ensureObjectQL().find(objectName, { top: 100 });
|
|
126
|
-
return { value: results, count: results.length };
|
|
127
|
-
}
|
|
128
|
-
async get(objectName, id) {
|
|
129
|
-
this.ensureSchema(objectName);
|
|
130
|
-
// Find One
|
|
131
|
-
const results = await this.ensureObjectQL().find(objectName, { top: 1 }); // Mock implementation
|
|
132
|
-
return results[0];
|
|
133
|
-
}
|
|
134
|
-
async create(objectName, data) {
|
|
135
|
-
this.ensureSchema(objectName);
|
|
136
|
-
return this.ensureObjectQL().insert(objectName, data);
|
|
137
|
-
}
|
|
138
|
-
async update(objectName, id, data) {
|
|
139
|
-
this.ensureSchema(objectName);
|
|
140
|
-
return this.ensureObjectQL().update(objectName, id, data);
|
|
141
|
-
}
|
|
142
|
-
async delete(objectName, id) {
|
|
143
|
-
this.ensureSchema(objectName);
|
|
144
|
-
return this.ensureObjectQL().delete(objectName, id);
|
|
145
|
-
}
|
|
146
|
-
// [New Methods for ObjectUI]
|
|
147
|
-
getMetadata(objectName) {
|
|
148
|
-
return this.ensureSchema(objectName);
|
|
149
|
-
}
|
|
150
|
-
getView(objectName, viewType = 'list') {
|
|
151
|
-
const schema = this.ensureSchema(objectName);
|
|
152
|
-
// Auto-Scaffold Default View
|
|
153
|
-
if (viewType === 'list') {
|
|
154
|
-
return {
|
|
155
|
-
type: 'datagrid',
|
|
156
|
-
title: `${schema.label || objectName} List`,
|
|
157
|
-
columns: Object.keys(schema.fields || {}).map(key => ({
|
|
158
|
-
field: key,
|
|
159
|
-
label: schema.fields?.[key]?.label || key,
|
|
160
|
-
width: 150
|
|
161
|
-
})),
|
|
162
|
-
actions: ['create', 'edit', 'delete']
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
ensureSchema(name) {
|
|
168
|
-
const schema = SchemaRegistry.getObject(name);
|
|
169
|
-
if (!schema)
|
|
170
|
-
throw new Error(`Unknown object: ${name}`);
|
|
171
|
-
return schema;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { ObjectQL } from '@objectstack/objectql';
|
|
2
|
-
import { RuntimePlugin, RuntimeContext } from '@objectstack/types';
|
|
3
|
-
/**
|
|
4
|
-
* ObjectQL Engine Plugin
|
|
5
|
-
*
|
|
6
|
-
* Registers the ObjectQL engine instance with the kernel.
|
|
7
|
-
* This allows users to provide their own ObjectQL implementation or configuration.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* - new ObjectQLPlugin() - Creates new ObjectQL with default settings
|
|
11
|
-
* - new ObjectQLPlugin(existingQL) - Uses existing ObjectQL instance
|
|
12
|
-
* - new ObjectQLPlugin(undefined, { custom: 'context' }) - Creates new ObjectQL with custom context
|
|
13
|
-
*/
|
|
14
|
-
export declare class ObjectQLPlugin implements RuntimePlugin {
|
|
15
|
-
name: string;
|
|
16
|
-
type: "objectql";
|
|
17
|
-
private ql;
|
|
18
|
-
/**
|
|
19
|
-
* @param ql - Existing ObjectQL instance to use (optional)
|
|
20
|
-
* @param hostContext - Host context for new ObjectQL instance (ignored if ql is provided)
|
|
21
|
-
*/
|
|
22
|
-
constructor(ql?: ObjectQL, hostContext?: Record<string, any>);
|
|
23
|
-
/**
|
|
24
|
-
* Install the ObjectQL engine into the kernel
|
|
25
|
-
*/
|
|
26
|
-
install(ctx: RuntimeContext): Promise<void>;
|
|
27
|
-
}
|