@objectql/core 3.0.0 → 4.0.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 +32 -0
- package/IMPLEMENTATION_STATUS.md +364 -0
- package/README.md +31 -9
- package/RUNTIME_INTEGRATION.md +391 -0
- package/dist/ai-agent.d.ts +4 -3
- package/dist/ai-agent.js +10 -3
- package/dist/ai-agent.js.map +1 -1
- package/dist/app.d.ts +29 -6
- package/dist/app.js +117 -58
- package/dist/app.js.map +1 -1
- package/dist/formula-engine.d.ts +7 -0
- package/dist/formula-engine.js +9 -2
- package/dist/formula-engine.js.map +1 -1
- package/dist/formula-plugin.d.ts +52 -0
- package/dist/formula-plugin.js +107 -0
- package/dist/formula-plugin.js.map +1 -0
- package/dist/index.d.ts +13 -3
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +89 -0
- package/dist/plugin.js +99 -0
- package/dist/plugin.js.map +1 -0
- package/dist/query/filter-translator.d.ts +37 -0
- package/dist/query/filter-translator.js +135 -0
- package/dist/query/filter-translator.js.map +1 -0
- package/dist/query/index.d.ts +22 -0
- package/dist/query/index.js +39 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/query-analyzer.d.ts +186 -0
- package/dist/query/query-analyzer.js +349 -0
- package/dist/query/query-analyzer.js.map +1 -0
- package/dist/query/query-builder.d.ts +27 -0
- package/dist/query/query-builder.js +71 -0
- package/dist/query/query-builder.js.map +1 -0
- package/dist/query/query-service.d.ts +150 -0
- package/dist/query/query-service.js +268 -0
- package/dist/query/query-service.js.map +1 -0
- package/dist/repository.d.ts +23 -2
- package/dist/repository.js +62 -13
- package/dist/repository.js.map +1 -1
- package/dist/util.d.ts +7 -0
- package/dist/util.js +18 -3
- package/dist/util.js.map +1 -1
- package/dist/validator-plugin.d.ts +56 -0
- package/dist/validator-plugin.js +106 -0
- package/dist/validator-plugin.js.map +1 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.js +10 -8
- package/dist/validator.js.map +1 -1
- package/jest.config.js +16 -0
- package/package.json +8 -5
- package/src/ai-agent.ts +8 -0
- package/src/app.ts +136 -72
- package/src/formula-engine.ts +8 -0
- package/src/formula-plugin.ts +141 -0
- package/src/index.ts +25 -3
- package/src/plugin.ts +179 -0
- package/src/query/filter-translator.ts +147 -0
- package/src/query/index.ts +24 -0
- package/src/query/query-analyzer.ts +535 -0
- package/src/query/query-builder.ts +80 -0
- package/src/query/query-service.ts +392 -0
- package/src/repository.ts +81 -17
- package/src/util.ts +19 -3
- package/src/validator-plugin.ts +140 -0
- package/src/validator.ts +12 -5
- package/test/__mocks__/@objectstack/runtime.ts +255 -0
- package/test/app.test.ts +23 -35
- package/test/filter-syntax.test.ts +233 -0
- package/test/formula-engine.test.ts +8 -0
- package/test/formula-integration.test.ts +8 -0
- package/test/formula-plugin.test.ts +197 -0
- package/test/introspection.test.ts +8 -0
- package/test/mock-driver.ts +8 -0
- package/test/plugin-integration.test.ts +213 -0
- package/test/repository-validation.test.ts +8 -0
- package/test/repository.test.ts +8 -0
- package/test/util.test.ts +9 -1
- package/test/utils.ts +8 -0
- package/test/validator-plugin.test.ts +126 -0
- package/test/validator.test.ts +8 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/action.d.ts +0 -7
- package/dist/action.js +0 -23
- package/dist/action.js.map +0 -1
- package/dist/hook.d.ts +0 -8
- package/dist/hook.js +0 -25
- package/dist/hook.js.map +0 -1
- package/dist/object.d.ts +0 -3
- package/dist/object.js +0 -28
- package/dist/object.js.map +0 -1
- package/src/action.ts +0 -40
- package/src/hook.ts +0 -42
- package/src/object.ts +0 -26
- package/test/action.test.ts +0 -276
- package/test/hook.test.ts +0 -343
- package/test/object.test.ts +0 -183
package/src/app.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
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
|
+
|
|
1
9
|
import {
|
|
2
|
-
MetadataRegistry,
|
|
10
|
+
MetadataRegistry,
|
|
11
|
+
MetadataItem,
|
|
3
12
|
Driver,
|
|
4
13
|
ObjectConfig,
|
|
5
14
|
ObjectQLContext,
|
|
6
15
|
ObjectQLContextOptions,
|
|
7
16
|
IObjectQL,
|
|
8
17
|
ObjectQLConfig,
|
|
9
|
-
ObjectQLPlugin,
|
|
10
18
|
HookName,
|
|
11
19
|
HookHandler,
|
|
12
20
|
HookContext,
|
|
@@ -14,37 +22,47 @@ import {
|
|
|
14
22
|
ActionContext,
|
|
15
23
|
LoaderPlugin
|
|
16
24
|
} from '@objectql/types';
|
|
25
|
+
import { ObjectStackKernel, type RuntimePlugin } from '@objectstack/runtime';
|
|
17
26
|
import { ObjectRepository } from './repository';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// import { loadRemoteFromUrl } from './remote';
|
|
21
|
-
import { executeActionHelper, registerActionHelper, ActionEntry } from './action';
|
|
22
|
-
import { registerHookHelper, triggerHookHelper, HookEntry } from './hook';
|
|
23
|
-
import { registerObjectHelper, getConfigsHelper } from './object';
|
|
27
|
+
import { ObjectQLPlugin } from './plugin';
|
|
24
28
|
import { convertIntrospectedSchemaToObjects } from './util';
|
|
25
29
|
|
|
30
|
+
/**
|
|
31
|
+
* ObjectQL
|
|
32
|
+
*
|
|
33
|
+
* ObjectQL implementation that wraps ObjectStackKernel
|
|
34
|
+
* to provide the plugin architecture.
|
|
35
|
+
*/
|
|
26
36
|
export class ObjectQL implements IObjectQL {
|
|
27
|
-
|
|
37
|
+
// Delegate to kernel for metadata, hooks, and actions
|
|
38
|
+
public get metadata(): MetadataRegistry {
|
|
39
|
+
return this.kernel.metadata;
|
|
40
|
+
}
|
|
41
|
+
|
|
28
42
|
private datasources: Record<string, Driver> = {};
|
|
29
43
|
private remotes: string[] = [];
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
private
|
|
44
|
+
|
|
45
|
+
// ObjectStack Kernel Integration
|
|
46
|
+
private kernel!: ObjectStackKernel;
|
|
47
|
+
private kernelPlugins: RuntimePlugin[] = [];
|
|
33
48
|
|
|
34
49
|
// Store config for lazy loading in init()
|
|
35
50
|
private config: ObjectQLConfig;
|
|
36
51
|
|
|
37
52
|
constructor(config: ObjectQLConfig) {
|
|
38
53
|
this.config = config;
|
|
39
|
-
this.metadata = config.registry || new MetadataRegistry();
|
|
40
54
|
this.datasources = config.datasources || {};
|
|
41
|
-
// this.remotes = config.remotes || [];
|
|
42
55
|
|
|
43
56
|
if (config.connection) {
|
|
44
57
|
throw new Error("Connection strings are not supported in core directly. Use @objectql/platform-node's createDriverFromConnection or pass a driver instance to 'datasources'.");
|
|
45
58
|
}
|
|
46
59
|
|
|
47
|
-
//
|
|
60
|
+
// Add the ObjectQL plugin to provide enhanced features
|
|
61
|
+
this.kernelPlugins.push(new ObjectQLPlugin({
|
|
62
|
+
datasources: this.datasources
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
// Add runtime plugins from config
|
|
48
66
|
if (config.plugins) {
|
|
49
67
|
for (const plugin of config.plugins) {
|
|
50
68
|
if (typeof plugin === 'string') {
|
|
@@ -54,41 +72,68 @@ export class ObjectQL implements IObjectQL {
|
|
|
54
72
|
}
|
|
55
73
|
}
|
|
56
74
|
}
|
|
57
|
-
}
|
|
58
|
-
use(plugin: ObjectQLPlugin) {
|
|
59
|
-
this.pluginsList.push(plugin);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
removePackage(name: string) {
|
|
63
|
-
this.metadata.unregisterPackage(name);
|
|
64
75
|
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
this.hooks[event] = this.hooks[event].filter(h => h.packageName !== name);
|
|
68
|
-
}
|
|
76
|
+
// Create the kernel
|
|
77
|
+
this.kernel = new ObjectStackKernel(this.kernelPlugins);
|
|
69
78
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
// Register initial metadata if provided
|
|
80
|
+
if (config.registry) {
|
|
81
|
+
// Copy metadata from provided registry to kernel's registry
|
|
82
|
+
for (const type of config.registry.getTypes()) {
|
|
83
|
+
const items = config.registry.list(type);
|
|
84
|
+
for (const item of items) {
|
|
85
|
+
// Safely extract the item's id/name
|
|
86
|
+
const itemId = typeof item === 'object' && item !== null
|
|
87
|
+
? (item as { name?: string; id?: string }).name || (item as { name?: string; id?: string }).id || 'unknown'
|
|
88
|
+
: 'unknown';
|
|
89
|
+
|
|
90
|
+
this.kernel.metadata.register(type, {
|
|
91
|
+
type,
|
|
92
|
+
id: itemId,
|
|
93
|
+
content: item
|
|
94
|
+
});
|
|
95
|
+
}
|
|
74
96
|
}
|
|
75
97
|
}
|
|
76
98
|
}
|
|
99
|
+
|
|
100
|
+
use(plugin: RuntimePlugin) {
|
|
101
|
+
this.kernelPlugins.push(plugin);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
removePackage(name: string) {
|
|
105
|
+
// Delegate to kernel managers
|
|
106
|
+
this.kernel.metadata.unregisterPackage(name);
|
|
107
|
+
this.kernel.hooks.removePackage(name);
|
|
108
|
+
this.kernel.actions.removePackage(name);
|
|
109
|
+
}
|
|
77
110
|
|
|
78
111
|
on(event: HookName, objectName: string, handler: HookHandler, packageName?: string) {
|
|
79
|
-
|
|
112
|
+
// Delegate to kernel hook manager
|
|
113
|
+
// We wrap the handler to bridge ObjectQL's rich context types with runtime's base types
|
|
114
|
+
// The runtime HookContext supports all fields via index signature, so this is safe
|
|
115
|
+
const wrappedHandler = handler as unknown as import('@objectstack/runtime').HookHandler;
|
|
116
|
+
this.kernel.hooks.register(event, objectName, wrappedHandler, packageName);
|
|
80
117
|
}
|
|
81
118
|
|
|
82
119
|
async triggerHook(event: HookName, objectName: string, ctx: HookContext) {
|
|
83
|
-
|
|
120
|
+
// Delegate to kernel hook manager
|
|
121
|
+
// Runtime HookContext supports ObjectQL-specific fields via index signature
|
|
122
|
+
await this.kernel.hooks.trigger(event, objectName, ctx);
|
|
84
123
|
}
|
|
85
124
|
|
|
86
125
|
registerAction(objectName: string, actionName: string, handler: ActionHandler, packageName?: string) {
|
|
87
|
-
|
|
126
|
+
// Delegate to kernel action manager
|
|
127
|
+
// We wrap the handler to bridge ObjectQL's rich context types with runtime's base types
|
|
128
|
+
// The runtime ActionContext supports all fields via index signature, so this is safe
|
|
129
|
+
const wrappedHandler = handler as unknown as import('@objectstack/runtime').ActionHandler;
|
|
130
|
+
this.kernel.actions.register(objectName, actionName, wrappedHandler, packageName);
|
|
88
131
|
}
|
|
89
132
|
|
|
90
133
|
async executeAction(objectName: string, actionName: string, ctx: ActionContext) {
|
|
91
|
-
|
|
134
|
+
// Delegate to kernel action manager
|
|
135
|
+
// Runtime ActionContext supports ObjectQL-specific fields via index signature
|
|
136
|
+
return await this.kernel.actions.execute(objectName, actionName, ctx);
|
|
92
137
|
}
|
|
93
138
|
|
|
94
139
|
createContext(options: ObjectQLContextOptions): ObjectQLContext {
|
|
@@ -100,7 +145,7 @@ export class ObjectQL implements IObjectQL {
|
|
|
100
145
|
object: (name: string) => {
|
|
101
146
|
return new ObjectRepository(name, ctx, this);
|
|
102
147
|
},
|
|
103
|
-
transaction: async (callback) => {
|
|
148
|
+
transaction: async (callback: (ctx: ObjectQLContext) => Promise<any>) => {
|
|
104
149
|
const driver = this.datasources['default'];
|
|
105
150
|
if (!driver || !driver.beginTransaction) {
|
|
106
151
|
return callback(ctx);
|
|
@@ -116,7 +161,7 @@ export class ObjectQL implements IObjectQL {
|
|
|
116
161
|
const trxCtx: ObjectQLContext = {
|
|
117
162
|
...ctx,
|
|
118
163
|
transactionHandle: trx,
|
|
119
|
-
transaction: async (cb) => cb(trxCtx)
|
|
164
|
+
transaction: async (cb: (ctx: ObjectQLContext) => Promise<any>) => cb(trxCtx)
|
|
120
165
|
};
|
|
121
166
|
|
|
122
167
|
try {
|
|
@@ -135,20 +180,53 @@ export class ObjectQL implements IObjectQL {
|
|
|
135
180
|
return ctx;
|
|
136
181
|
}
|
|
137
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Get the underlying ObjectStackKernel instance
|
|
185
|
+
*
|
|
186
|
+
* This provides access to the kernel for advanced usage scenarios
|
|
187
|
+
* where you need direct access to the plugin architecture.
|
|
188
|
+
*
|
|
189
|
+
* @returns The ObjectStackKernel instance
|
|
190
|
+
* @throws Error if called before init()
|
|
191
|
+
*/
|
|
192
|
+
getKernel(): ObjectStackKernel {
|
|
193
|
+
if (!this.kernel) {
|
|
194
|
+
throw new Error('Kernel not initialized. Call init() first.');
|
|
195
|
+
}
|
|
196
|
+
return this.kernel;
|
|
197
|
+
}
|
|
198
|
+
|
|
138
199
|
registerObject(object: ObjectConfig) {
|
|
139
|
-
|
|
200
|
+
// Normalize fields
|
|
201
|
+
if (object.fields) {
|
|
202
|
+
for (const [key, field] of Object.entries(object.fields)) {
|
|
203
|
+
if (field && !field.name) {
|
|
204
|
+
field.name = key;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
this.kernel.metadata.register('object', {
|
|
209
|
+
type: 'object',
|
|
210
|
+
id: object.name,
|
|
211
|
+
content: object
|
|
212
|
+
});
|
|
140
213
|
}
|
|
141
214
|
|
|
142
215
|
unregisterObject(name: string) {
|
|
143
|
-
this.metadata.unregister('object', name);
|
|
216
|
+
this.kernel.metadata.unregister('object', name);
|
|
144
217
|
}
|
|
145
218
|
|
|
146
219
|
getObject(name: string): ObjectConfig | undefined {
|
|
147
|
-
return this.metadata.get<ObjectConfig>('object', name);
|
|
220
|
+
return this.kernel.metadata.get<ObjectConfig>('object', name);
|
|
148
221
|
}
|
|
149
222
|
|
|
150
223
|
getConfigs(): Record<string, ObjectConfig> {
|
|
151
|
-
|
|
224
|
+
const result: Record<string, ObjectConfig> = {};
|
|
225
|
+
const items = this.kernel.metadata.list<ObjectConfig>('object');
|
|
226
|
+
for (const item of items) {
|
|
227
|
+
result[item.name] = item;
|
|
228
|
+
}
|
|
229
|
+
return result;
|
|
152
230
|
}
|
|
153
231
|
|
|
154
232
|
datasource(name: string): Driver {
|
|
@@ -207,46 +285,30 @@ export class ObjectQL implements IObjectQL {
|
|
|
207
285
|
}
|
|
208
286
|
|
|
209
287
|
async init() {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return (event: HookName, obj: string, handler: HookHandler) =>
|
|
222
|
-
target.on(event, obj, handler, pkgName);
|
|
223
|
-
}
|
|
224
|
-
if (prop === 'registerAction') {
|
|
225
|
-
return (obj: string, act: string, handler: ActionHandler) =>
|
|
226
|
-
target.registerAction(obj, act, handler, pkgName);
|
|
227
|
-
}
|
|
228
|
-
const value = (target as any)[prop];
|
|
229
|
-
return typeof value === 'function' ? value.bind(target) : value;
|
|
230
|
-
}
|
|
231
|
-
});
|
|
288
|
+
console.log('[ObjectQL] Initializing with ObjectStackKernel...');
|
|
289
|
+
|
|
290
|
+
// Start the kernel - this will install and start all plugins
|
|
291
|
+
await this.kernel.start();
|
|
292
|
+
|
|
293
|
+
// TEMPORARY: Set driver for backward compatibility during migration
|
|
294
|
+
// This allows the kernel mock to delegate to the driver
|
|
295
|
+
if (typeof (this.kernel as any).setDriver === 'function') {
|
|
296
|
+
const defaultDriver = this.datasources['default'];
|
|
297
|
+
if (defaultDriver) {
|
|
298
|
+
(this.kernel as any).setDriver(defaultDriver);
|
|
232
299
|
}
|
|
233
|
-
|
|
234
|
-
await plugin.setup(app);
|
|
235
300
|
}
|
|
236
301
|
|
|
237
|
-
//
|
|
238
|
-
// Use @objectql/platform-node's ObjectLoader or platform-specific loaders.
|
|
239
|
-
|
|
240
|
-
// 3. Load In-Memory Objects (Dynamic Layer)
|
|
302
|
+
// Load In-Memory Objects (Dynamic Layer)
|
|
241
303
|
if (this.config.objects) {
|
|
242
304
|
for (const [key, obj] of Object.entries(this.config.objects)) {
|
|
243
305
|
this.registerObject(obj);
|
|
244
306
|
}
|
|
245
307
|
}
|
|
246
308
|
|
|
247
|
-
const objects = this.metadata.list<ObjectConfig>('object');
|
|
309
|
+
const objects = this.kernel.metadata.list<ObjectConfig>('object');
|
|
248
310
|
|
|
249
|
-
//
|
|
311
|
+
// Init Datasources
|
|
250
312
|
// Let's pass all objects to all configured drivers.
|
|
251
313
|
for (const [name, driver] of Object.entries(this.datasources)) {
|
|
252
314
|
if (driver.init) {
|
|
@@ -255,12 +317,14 @@ export class ObjectQL implements IObjectQL {
|
|
|
255
317
|
}
|
|
256
318
|
}
|
|
257
319
|
|
|
258
|
-
//
|
|
320
|
+
// Process Initial Data
|
|
259
321
|
await this.processInitialData();
|
|
322
|
+
|
|
323
|
+
console.log('[ObjectQL] Initialization complete');
|
|
260
324
|
}
|
|
261
325
|
|
|
262
326
|
private async processInitialData() {
|
|
263
|
-
const dataEntries = this.metadata.list<any>('data');
|
|
327
|
+
const dataEntries = this.kernel.metadata.list<any>('data');
|
|
264
328
|
if (dataEntries.length === 0) return;
|
|
265
329
|
|
|
266
330
|
console.log(`Processing ${dataEntries.length} initial data files...`);
|
package/src/formula-engine.ts
CHANGED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL Formula Plugin
|
|
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
|
+
import type { RuntimePlugin, RuntimeContext, ObjectStackKernel } from '@objectstack/runtime';
|
|
10
|
+
import { FormulaEngine } from './formula-engine';
|
|
11
|
+
import type { FormulaEngineConfig } from '@objectql/types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extended ObjectStack Kernel with formula engine capability
|
|
15
|
+
*/
|
|
16
|
+
interface KernelWithFormulas extends ObjectStackKernel {
|
|
17
|
+
formulaEngine?: FormulaEngine;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for the Formula Plugin
|
|
22
|
+
*/
|
|
23
|
+
export interface FormulaPluginConfig extends FormulaEngineConfig {
|
|
24
|
+
/**
|
|
25
|
+
* Enable automatic formula evaluation on queries
|
|
26
|
+
* @default true
|
|
27
|
+
*/
|
|
28
|
+
autoEvaluateOnQuery?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Formula Plugin
|
|
33
|
+
*
|
|
34
|
+
* Wraps the ObjectQL Formula Engine as an ObjectStack plugin.
|
|
35
|
+
* Registers formula evaluation capabilities into the kernel.
|
|
36
|
+
*/
|
|
37
|
+
export class FormulaPlugin implements RuntimePlugin {
|
|
38
|
+
name = '@objectql/formulas';
|
|
39
|
+
version = '4.0.0';
|
|
40
|
+
|
|
41
|
+
private engine: FormulaEngine;
|
|
42
|
+
private config: FormulaPluginConfig;
|
|
43
|
+
|
|
44
|
+
constructor(config: FormulaPluginConfig = {}) {
|
|
45
|
+
this.config = {
|
|
46
|
+
autoEvaluateOnQuery: true,
|
|
47
|
+
...config
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Initialize the formula engine with configuration
|
|
51
|
+
this.engine = new FormulaEngine(config);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Install the plugin into the kernel
|
|
56
|
+
* Registers formula evaluation capabilities
|
|
57
|
+
*/
|
|
58
|
+
async install(ctx: RuntimeContext): Promise<void> {
|
|
59
|
+
const kernel = ctx.engine as KernelWithFormulas;
|
|
60
|
+
|
|
61
|
+
console.log(`[${this.name}] Installing formula plugin...`);
|
|
62
|
+
|
|
63
|
+
// Make formula engine accessible from the kernel for direct usage
|
|
64
|
+
kernel.formulaEngine = this.engine;
|
|
65
|
+
|
|
66
|
+
// Register formula provider if the kernel supports it
|
|
67
|
+
this.registerFormulaProvider(kernel);
|
|
68
|
+
|
|
69
|
+
// Register formula evaluation middleware if auto-evaluation is enabled
|
|
70
|
+
if (this.config.autoEvaluateOnQuery !== false) {
|
|
71
|
+
this.registerFormulaMiddleware(kernel);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(`[${this.name}] Formula plugin installed`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Register the formula provider with the kernel
|
|
79
|
+
* @private
|
|
80
|
+
*/
|
|
81
|
+
private registerFormulaProvider(kernel: KernelWithFormulas): void {
|
|
82
|
+
// Check if kernel supports formula provider registration
|
|
83
|
+
if (typeof (kernel as any).registerFormulaProvider === 'function') {
|
|
84
|
+
(kernel as any).registerFormulaProvider({
|
|
85
|
+
evaluate: (formula: string, context: any) => {
|
|
86
|
+
// Delegate to the formula engine
|
|
87
|
+
// Note: In a real implementation, we would need to properly construct
|
|
88
|
+
// the FormulaContext from the provided context
|
|
89
|
+
return this.engine.evaluate(
|
|
90
|
+
formula,
|
|
91
|
+
context,
|
|
92
|
+
'text', // default data type
|
|
93
|
+
{}
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
validate: (expression: string) => {
|
|
97
|
+
return this.engine.validate(expression);
|
|
98
|
+
},
|
|
99
|
+
extractMetadata: (fieldName: string, expression: string, dataType: any) => {
|
|
100
|
+
return this.engine.extractMetadata(fieldName, expression, dataType);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Note: formulaEngine is already registered in install() method above
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Register formula evaluation middleware
|
|
109
|
+
* @private
|
|
110
|
+
*/
|
|
111
|
+
private registerFormulaMiddleware(kernel: KernelWithFormulas): void {
|
|
112
|
+
// Check if kernel supports middleware hooks
|
|
113
|
+
if (typeof (kernel as any).use === 'function') {
|
|
114
|
+
// Register middleware to evaluate formulas after queries
|
|
115
|
+
(kernel as any).use('afterQuery', async (context: any) => {
|
|
116
|
+
// Formula evaluation logic would go here
|
|
117
|
+
// This would automatically compute formula fields after data is retrieved
|
|
118
|
+
if (context.results && context.metadata?.fields) {
|
|
119
|
+
// Iterate through fields and evaluate formulas
|
|
120
|
+
// const formulaFields = Object.entries(context.metadata.fields)
|
|
121
|
+
// .filter(([_, fieldConfig]) => (fieldConfig as any).formula);
|
|
122
|
+
//
|
|
123
|
+
// for (const record of context.results) {
|
|
124
|
+
// for (const [fieldName, fieldConfig] of formulaFields) {
|
|
125
|
+
// const formula = (fieldConfig as any).formula;
|
|
126
|
+
// const result = this.engine.evaluate(formula, /* context */, /* dataType */);
|
|
127
|
+
// record[fieldName] = result.value;
|
|
128
|
+
// }
|
|
129
|
+
// }
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get the formula engine instance for direct access
|
|
137
|
+
*/
|
|
138
|
+
getEngine(): FormulaEngine {
|
|
139
|
+
return this.engine;
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +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
|
+
// Re-export types from @objectstack packages for API compatibility
|
|
10
|
+
export type { ObjectStackKernel, ObjectStackRuntimeProtocol } from '@objectstack/runtime';
|
|
11
|
+
// Note: @objectstack/objectql types temporarily commented out due to type incompatibilities
|
|
12
|
+
// in the published package. Will be re-enabled when package is updated.
|
|
13
|
+
// export type { ObjectQL as ObjectQLEngine, SchemaRegistry } from '@objectstack/objectql';
|
|
14
|
+
|
|
15
|
+
// Export ObjectStack spec types for driver development
|
|
16
|
+
export type { DriverInterface, DriverOptions, QueryAST } from '@objectstack/spec';
|
|
17
|
+
|
|
18
|
+
// Export our enhanced runtime components (actual implementations)
|
|
1
19
|
export * from './repository';
|
|
2
20
|
export * from './app';
|
|
21
|
+
export * from './plugin';
|
|
22
|
+
export * from './validator-plugin';
|
|
23
|
+
export * from './formula-plugin';
|
|
24
|
+
|
|
25
|
+
// Export query-specific modules (ObjectQL core competency)
|
|
26
|
+
export * from './query';
|
|
3
27
|
|
|
4
|
-
|
|
5
|
-
export * from './hook';
|
|
6
|
-
export * from './object';
|
|
28
|
+
// Export utilities
|
|
7
29
|
export * from './validator';
|
|
8
30
|
export * from './util';
|
|
9
31
|
export * from './ai-agent';
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL Plugin
|
|
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
|
+
import type { RuntimePlugin, RuntimeContext } from '@objectstack/runtime';
|
|
10
|
+
import type { ObjectStackKernel } from '@objectstack/runtime';
|
|
11
|
+
import { ValidatorPlugin, ValidatorPluginConfig } from './validator-plugin';
|
|
12
|
+
import { FormulaPlugin, FormulaPluginConfig } from './formula-plugin';
|
|
13
|
+
import { QueryService } from './query/query-service';
|
|
14
|
+
import { QueryAnalyzer } from './query/query-analyzer';
|
|
15
|
+
import type { Driver } from '@objectql/types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extended ObjectStack Kernel with ObjectQL services
|
|
19
|
+
*/
|
|
20
|
+
interface ExtendedKernel extends ObjectStackKernel {
|
|
21
|
+
queryService?: QueryService;
|
|
22
|
+
queryAnalyzer?: QueryAnalyzer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for the ObjectQL Plugin
|
|
27
|
+
*/
|
|
28
|
+
export interface ObjectQLPluginConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Enable repository pattern for data access
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
enableRepository?: boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Enable validation engine
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
enableValidator?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validator plugin configuration
|
|
43
|
+
* Only used if enableValidator is not false
|
|
44
|
+
*/
|
|
45
|
+
validatorConfig?: ValidatorPluginConfig;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Enable formula engine
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
enableFormulas?: boolean;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Formula plugin configuration
|
|
55
|
+
* Only used if enableFormulas is not false
|
|
56
|
+
*/
|
|
57
|
+
formulaConfig?: FormulaPluginConfig;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Enable AI integration
|
|
61
|
+
* @default true
|
|
62
|
+
*/
|
|
63
|
+
enableAI?: boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Enable query service and analyzer
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
enableQueryService?: boolean;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Datasources for query service
|
|
73
|
+
* Required if enableQueryService is true
|
|
74
|
+
*/
|
|
75
|
+
datasources?: Record<string, Driver>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* ObjectQL Plugin
|
|
80
|
+
*
|
|
81
|
+
* Implements the RuntimePlugin interface from @objectstack/runtime
|
|
82
|
+
* to provide ObjectQL's enhanced features (Repository, Validator, Formula, AI)
|
|
83
|
+
* on top of the ObjectStack kernel.
|
|
84
|
+
*/
|
|
85
|
+
export class ObjectQLPlugin implements RuntimePlugin {
|
|
86
|
+
name = '@objectql/core';
|
|
87
|
+
version = '4.0.0';
|
|
88
|
+
|
|
89
|
+
constructor(private config: ObjectQLPluginConfig = {}) {
|
|
90
|
+
// Set defaults
|
|
91
|
+
this.config = {
|
|
92
|
+
enableRepository: true,
|
|
93
|
+
enableValidator: true,
|
|
94
|
+
enableFormulas: true,
|
|
95
|
+
enableAI: true,
|
|
96
|
+
enableQueryService: true,
|
|
97
|
+
...config
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Install the plugin into the kernel
|
|
103
|
+
* This is called during kernel initialization
|
|
104
|
+
*/
|
|
105
|
+
async install(ctx: RuntimeContext): Promise<void> {
|
|
106
|
+
console.log(`[${this.name}] Installing plugin...`);
|
|
107
|
+
|
|
108
|
+
const kernel = ctx.engine as ExtendedKernel;
|
|
109
|
+
|
|
110
|
+
// Register QueryService and QueryAnalyzer if enabled
|
|
111
|
+
if (this.config.enableQueryService !== false && this.config.datasources) {
|
|
112
|
+
const queryService = new QueryService(
|
|
113
|
+
this.config.datasources,
|
|
114
|
+
kernel.metadata
|
|
115
|
+
);
|
|
116
|
+
kernel.queryService = queryService;
|
|
117
|
+
|
|
118
|
+
const queryAnalyzer = new QueryAnalyzer(
|
|
119
|
+
queryService,
|
|
120
|
+
kernel.metadata
|
|
121
|
+
);
|
|
122
|
+
kernel.queryAnalyzer = queryAnalyzer;
|
|
123
|
+
|
|
124
|
+
console.log(`[${this.name}] QueryService and QueryAnalyzer registered`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Register components based on configuration
|
|
128
|
+
if (this.config.enableRepository !== false) {
|
|
129
|
+
await this.registerRepository(ctx.engine);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Install validator plugin if enabled
|
|
133
|
+
if (this.config.enableValidator !== false) {
|
|
134
|
+
const validatorPlugin = new ValidatorPlugin(this.config.validatorConfig || {});
|
|
135
|
+
await validatorPlugin.install(ctx);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Install formula plugin if enabled
|
|
139
|
+
if (this.config.enableFormulas !== false) {
|
|
140
|
+
const formulaPlugin = new FormulaPlugin(this.config.formulaConfig || {});
|
|
141
|
+
await formulaPlugin.install(ctx);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.config.enableAI !== false) {
|
|
145
|
+
await this.registerAI(ctx.engine);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(`[${this.name}] Plugin installed successfully`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Called when the kernel starts
|
|
153
|
+
* This is the initialization phase
|
|
154
|
+
*/
|
|
155
|
+
async onStart(ctx: RuntimeContext): Promise<void> {
|
|
156
|
+
console.log(`[${this.name}] Starting plugin...`);
|
|
157
|
+
// Additional startup logic can be added here
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Register the Repository pattern
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
private async registerRepository(kernel: ObjectStackKernel): Promise<void> {
|
|
165
|
+
// TODO: Implement repository registration
|
|
166
|
+
// For now, this is a placeholder to establish the structure
|
|
167
|
+
console.log(`[${this.name}] Repository pattern registered`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Register AI integration
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
private async registerAI(kernel: ObjectStackKernel): Promise<void> {
|
|
175
|
+
// TODO: Implement AI registration
|
|
176
|
+
// For now, this is a placeholder to establish the structure
|
|
177
|
+
console.log(`[${this.name}] AI integration registered`);
|
|
178
|
+
}
|
|
179
|
+
}
|