@objectql/core 4.1.0 → 4.2.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +19 -0
- package/dist/ai/index.d.ts +8 -0
- package/dist/ai/index.js +25 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/registry.d.ts +23 -0
- package/dist/ai/registry.js +78 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +20 -16
- package/dist/app.js.map +1 -1
- package/dist/gateway.d.ts +3 -2
- package/dist/gateway.js +6 -2
- package/dist/gateway.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/optimizations/CompiledHookManager.d.ts +1 -0
- package/dist/optimizations/CompiledHookManager.js +7 -1
- package/dist/optimizations/CompiledHookManager.js.map +1 -1
- package/dist/plugin.d.ts +3 -2
- package/dist/plugin.js +56 -33
- package/dist/plugin.js.map +1 -1
- package/dist/protocol.d.ts +11 -0
- package/dist/protocol.js +12 -0
- package/dist/protocol.js.map +1 -1
- package/dist/query/filter-translator.d.ts +1 -4
- package/dist/query/filter-translator.js.map +1 -1
- package/dist/query/query-analyzer.d.ts +1 -3
- package/dist/query/query-analyzer.js.map +1 -1
- package/dist/query/query-builder.d.ts +1 -9
- package/dist/query/query-builder.js +8 -0
- package/dist/query/query-builder.js.map +1 -1
- package/dist/query/query-service.d.ts +1 -3
- package/dist/query/query-service.js.map +1 -1
- package/dist/repository.js +9 -1
- package/dist/repository.js.map +1 -1
- package/package.json +16 -9
- package/src/ai/index.ts +9 -0
- package/src/ai/registry.ts +81 -0
- package/src/app.ts +24 -16
- package/src/gateway.ts +7 -3
- package/src/index.ts +24 -7
- package/src/optimizations/CompiledHookManager.ts +9 -1
- package/src/plugin.ts +66 -36
- package/src/protocol.ts +13 -0
- package/src/query/filter-translator.ts +3 -3
- package/src/query/query-analyzer.ts +1 -2
- package/src/query/query-builder.ts +8 -8
- package/src/query/query-service.ts +1 -2
- package/src/repository.ts +11 -4
- package/test/ai-registry.test.ts +42 -0
- package/test/optimizations.test.ts +1 -1
- package/test/plugin-integration.test.ts +3 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/jest.config.js +0 -29
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL - AI Runtime Registry
|
|
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 {
|
|
10
|
+
AiRegistry,
|
|
11
|
+
AiModelDefinition,
|
|
12
|
+
ModelRegistry,
|
|
13
|
+
PromptRegistry,
|
|
14
|
+
PromptTemplate,
|
|
15
|
+
} from '@objectql/types';
|
|
16
|
+
|
|
17
|
+
const DEFAULT_PROMPT_VERSION = 'latest';
|
|
18
|
+
|
|
19
|
+
export class InMemoryModelRegistry implements ModelRegistry {
|
|
20
|
+
private readonly models = new Map<string, AiModelDefinition>();
|
|
21
|
+
|
|
22
|
+
register(model: AiModelDefinition): void {
|
|
23
|
+
this.models.set(model.id, model);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get(id: string): AiModelDefinition | undefined {
|
|
27
|
+
return this.models.get(id);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
list(): readonly AiModelDefinition[] {
|
|
31
|
+
return Array.from(this.models.values());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
remove(id: string): void {
|
|
35
|
+
this.models.delete(id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class InMemoryPromptRegistry implements PromptRegistry {
|
|
40
|
+
private readonly prompts = new Map<string, Map<string, PromptTemplate>>();
|
|
41
|
+
|
|
42
|
+
register(template: PromptTemplate): void {
|
|
43
|
+
const version = template.version ?? DEFAULT_PROMPT_VERSION;
|
|
44
|
+
const byId = this.prompts.get(template.id) ?? new Map<string, PromptTemplate>();
|
|
45
|
+
byId.set(version, { ...template, version });
|
|
46
|
+
this.prompts.set(template.id, byId);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get(id: string, version?: string): PromptTemplate | undefined {
|
|
50
|
+
const byId = this.prompts.get(id);
|
|
51
|
+
if (!byId) return undefined;
|
|
52
|
+
if (version) return byId.get(version);
|
|
53
|
+
if (byId.has(DEFAULT_PROMPT_VERSION)) return byId.get(DEFAULT_PROMPT_VERSION);
|
|
54
|
+
return byId.values().next().value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
list(id?: string): readonly PromptTemplate[] {
|
|
58
|
+
if (id) {
|
|
59
|
+
return Array.from(this.prompts.get(id)?.values() ?? []);
|
|
60
|
+
}
|
|
61
|
+
return Array.from(this.prompts.values()).flatMap(map => Array.from(map.values()));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
remove(id: string, version?: string): void {
|
|
65
|
+
if (!version) {
|
|
66
|
+
this.prompts.delete(id);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const byId = this.prompts.get(id);
|
|
70
|
+
if (!byId) return;
|
|
71
|
+
byId.delete(version);
|
|
72
|
+
if (byId.size === 0) {
|
|
73
|
+
this.prompts.delete(id);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const createDefaultAiRegistry = (): AiRegistry => ({
|
|
79
|
+
models: new InMemoryModelRegistry(),
|
|
80
|
+
prompts: new InMemoryPromptRegistry(),
|
|
81
|
+
});
|
package/src/app.ts
CHANGED
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
HookContext,
|
|
21
21
|
ActionHandler,
|
|
22
22
|
ActionContext,
|
|
23
|
-
LoaderPlugin
|
|
23
|
+
LoaderPlugin,
|
|
24
|
+
Logger,
|
|
25
|
+
ConsoleLogger
|
|
24
26
|
} from '@objectql/types';
|
|
25
27
|
import { ObjectKernel, type Plugin } from '@objectstack/runtime';
|
|
26
28
|
import { ObjectQL as RuntimeObjectQL, SchemaRegistry } from '@objectstack/objectql';
|
|
@@ -54,12 +56,16 @@ export class ObjectQL implements IObjectQL {
|
|
|
54
56
|
private hookManager = new CompiledHookManager();
|
|
55
57
|
private localActions = new Map<string, any>();
|
|
56
58
|
|
|
59
|
+
// Structured logger
|
|
60
|
+
private logger: Logger;
|
|
61
|
+
|
|
57
62
|
// Store config for lazy loading in init()
|
|
58
63
|
private config: ObjectQLConfig;
|
|
59
64
|
|
|
60
65
|
constructor(config: ObjectQLConfig) {
|
|
61
66
|
this.config = config;
|
|
62
67
|
this.datasources = config.datasources || {};
|
|
68
|
+
this.logger = config.logger ?? new ConsoleLogger({ name: '@objectql/core', level: 'info' });
|
|
63
69
|
|
|
64
70
|
if (config.connection) {
|
|
65
71
|
throw new Error("Connection strings are not supported in core directly. Use @objectql/platform-node's createDriverFromConnection or pass a driver instance to 'datasources'.");
|
|
@@ -145,20 +151,22 @@ export class ObjectQL implements IObjectQL {
|
|
|
145
151
|
}
|
|
146
152
|
},
|
|
147
153
|
unregisterPackage: (packageName: string) => {
|
|
154
|
+
// Use the official @objectstack/objectql 1.1.0 API for object cleanup
|
|
155
|
+
if (typeof SchemaRegistry.unregisterObjectsByPackage === 'function') {
|
|
156
|
+
SchemaRegistry.unregisterObjectsByPackage(packageName);
|
|
157
|
+
}
|
|
158
|
+
// Also clean up non-object metadata items by package
|
|
148
159
|
const metadata = (SchemaRegistry as any).metadata;
|
|
149
160
|
if (metadata && metadata instanceof Map) {
|
|
150
161
|
for (const [type, collection] of metadata.entries()) {
|
|
151
162
|
if (collection instanceof Map) {
|
|
152
163
|
for (const [key, item] of collection.entries()) {
|
|
153
|
-
// console.log(`[App] Check ${type} ${key} pkg=${(item as any).package}`);
|
|
154
164
|
if ((item as any).package === packageName) {
|
|
155
165
|
collection.delete(key);
|
|
156
166
|
}
|
|
157
167
|
}
|
|
158
168
|
}
|
|
159
169
|
}
|
|
160
|
-
} else {
|
|
161
|
-
console.warn('Metadata is not a Map');
|
|
162
170
|
}
|
|
163
171
|
}
|
|
164
172
|
};
|
|
@@ -385,13 +393,13 @@ export class ObjectQL implements IObjectQL {
|
|
|
385
393
|
throw new Error(`Driver for datasource '${datasourceName}' does not support schema introspection`);
|
|
386
394
|
}
|
|
387
395
|
|
|
388
|
-
|
|
396
|
+
this.logger.info(`Introspecting datasource '${datasourceName}'...`);
|
|
389
397
|
const introspectedSchema = await driver.introspectSchema();
|
|
390
398
|
|
|
391
399
|
// Convert introspected schema to ObjectQL objects
|
|
392
400
|
const objects = convertIntrospectedSchemaToObjects(introspectedSchema, options);
|
|
393
401
|
|
|
394
|
-
|
|
402
|
+
this.logger.info(`Discovered ${objects.length} table(s), registering as objects...`);
|
|
395
403
|
|
|
396
404
|
// Register each discovered object
|
|
397
405
|
for (const obj of objects) {
|
|
@@ -404,14 +412,14 @@ export class ObjectQL implements IObjectQL {
|
|
|
404
412
|
async close() {
|
|
405
413
|
for (const [name, driver] of Object.entries(this.datasources)) {
|
|
406
414
|
if (driver.disconnect) {
|
|
407
|
-
|
|
415
|
+
this.logger.debug(`Closing driver '${name}'...`);
|
|
408
416
|
await driver.disconnect();
|
|
409
417
|
}
|
|
410
418
|
}
|
|
411
419
|
}
|
|
412
420
|
|
|
413
421
|
async init() {
|
|
414
|
-
|
|
422
|
+
this.logger.info('Initializing with ObjectKernel...');
|
|
415
423
|
|
|
416
424
|
// Start the kernel - this will install and start all plugins
|
|
417
425
|
if ((this.kernel as any).start) {
|
|
@@ -419,7 +427,7 @@ export class ObjectQL implements IObjectQL {
|
|
|
419
427
|
} else if ((this.kernel as any).bootstrap) {
|
|
420
428
|
await (this.kernel as any).bootstrap();
|
|
421
429
|
} else {
|
|
422
|
-
|
|
430
|
+
this.logger.warn('ObjectKernel does not have start() or bootstrap() method');
|
|
423
431
|
|
|
424
432
|
// Manually initialize plugins if kernel doesn't support lifecycle
|
|
425
433
|
for (const plugin of this.kernelPlugins) {
|
|
@@ -431,7 +439,7 @@ export class ObjectQL implements IObjectQL {
|
|
|
431
439
|
await (plugin as any).start(this.kernel);
|
|
432
440
|
}
|
|
433
441
|
} catch (error) {
|
|
434
|
-
|
|
442
|
+
this.logger.error(`Failed to initialize plugin ${(plugin as any).name || 'unknown'}`, error as Error);
|
|
435
443
|
// Continue with other plugins even if one fails
|
|
436
444
|
}
|
|
437
445
|
}
|
|
@@ -475,7 +483,7 @@ export class ObjectQL implements IObjectQL {
|
|
|
475
483
|
// Let's pass all objects to all configured drivers.
|
|
476
484
|
for (const [name, driver] of Object.entries(this.datasources)) {
|
|
477
485
|
if (driver.init) {
|
|
478
|
-
|
|
486
|
+
this.logger.debug(`Initializing driver '${name}'...`);
|
|
479
487
|
await driver.init(objects);
|
|
480
488
|
}
|
|
481
489
|
}
|
|
@@ -483,14 +491,14 @@ export class ObjectQL implements IObjectQL {
|
|
|
483
491
|
// Process Initial Data
|
|
484
492
|
await this.processInitialData();
|
|
485
493
|
|
|
486
|
-
|
|
494
|
+
this.logger.info('Initialization complete');
|
|
487
495
|
}
|
|
488
496
|
|
|
489
497
|
private async processInitialData() {
|
|
490
498
|
const dataEntries = (this.kernel as any).metadata.list('data');
|
|
491
499
|
if (dataEntries.length === 0) return;
|
|
492
500
|
|
|
493
|
-
|
|
501
|
+
this.logger.info(`Processing ${dataEntries.length} initial data files...`);
|
|
494
502
|
|
|
495
503
|
// We need a system context to write data
|
|
496
504
|
const ctx = this.createContext({ isSystem: true });
|
|
@@ -514,7 +522,7 @@ export class ObjectQL implements IObjectQL {
|
|
|
514
522
|
}
|
|
515
523
|
|
|
516
524
|
if (!objectName || !records || !Array.isArray(records)) {
|
|
517
|
-
|
|
525
|
+
this.logger.warn('Skipping invalid data entry', { entry: String(entry) });
|
|
518
526
|
continue;
|
|
519
527
|
}
|
|
520
528
|
|
|
@@ -531,10 +539,10 @@ export class ObjectQL implements IObjectQL {
|
|
|
531
539
|
// But without unique keys defined in data, we can't reliably dedup.
|
|
532
540
|
// Let's try to 'create' and catch errors.
|
|
533
541
|
await repo.create(record);
|
|
534
|
-
|
|
542
|
+
this.logger.debug(`Initialized record for ${objectName}`);
|
|
535
543
|
} catch (e: any) {
|
|
536
544
|
// Ignore duplicate key errors silently-ish
|
|
537
|
-
|
|
545
|
+
this.logger.warn(`Failed to insert initial data for ${objectName}: ${e.message}`);
|
|
538
546
|
}
|
|
539
547
|
}
|
|
540
548
|
}
|
package/src/gateway.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { ApiRequest, ApiResponse, GatewayProtocol } from '@objectql/types';
|
|
9
|
+
import { ApiRequest, ApiResponse, GatewayProtocol, Logger, ConsoleLogger } from '@objectql/types';
|
|
10
10
|
|
|
11
11
|
export type RequestTransformer = (request: ApiRequest) => Promise<ApiRequest>;
|
|
12
12
|
export type ResponseTransformer = (response: ApiResponse) => Promise<ApiResponse>;
|
|
@@ -19,9 +19,11 @@ export class ObjectGateway {
|
|
|
19
19
|
private protocols: GatewayProtocol[] = [];
|
|
20
20
|
private requestTransforms: RequestTransformer[] = [];
|
|
21
21
|
private responseTransforms: ResponseTransformer[] = [];
|
|
22
|
+
private logger: Logger;
|
|
22
23
|
|
|
23
|
-
constructor(protocols: GatewayProtocol[] = []) {
|
|
24
|
+
constructor(protocols: GatewayProtocol[] = [], logger?: Logger) {
|
|
24
25
|
this.protocols = protocols;
|
|
26
|
+
this.logger = logger ?? new ConsoleLogger({ name: '@objectql/gateway', level: 'info' });
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -78,7 +80,9 @@ export class ObjectGateway {
|
|
|
78
80
|
try {
|
|
79
81
|
response = await protocol.handle(req);
|
|
80
82
|
} catch (error: any) {
|
|
81
|
-
|
|
83
|
+
this.logger.error(`Error in ${protocol.name}`, error as Error, {
|
|
84
|
+
protocol: protocol.name,
|
|
85
|
+
});
|
|
82
86
|
response = {
|
|
83
87
|
status: 500,
|
|
84
88
|
headers: { 'Content-Type': 'application/json' },
|
package/src/index.ts
CHANGED
|
@@ -9,15 +9,29 @@
|
|
|
9
9
|
// Re-export types from @objectstack packages for API compatibility
|
|
10
10
|
export type { ObjectKernel } from '@objectstack/runtime';
|
|
11
11
|
export type { ObjectStackProtocolImplementation } from '@objectstack/objectql';
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
12
|
+
|
|
13
|
+
// Re-export new @objectstack/objectql 1.1.0 FQN (Fully Qualified Name) utilities
|
|
14
|
+
export {
|
|
15
|
+
computeFQN,
|
|
16
|
+
parseFQN,
|
|
17
|
+
RESERVED_NAMESPACES,
|
|
18
|
+
DEFAULT_OWNER_PRIORITY,
|
|
19
|
+
DEFAULT_EXTENDER_PRIORITY,
|
|
20
|
+
SchemaRegistry,
|
|
21
|
+
} from '@objectstack/objectql';
|
|
22
|
+
export type { ObjectContributor } from '@objectstack/objectql';
|
|
15
23
|
|
|
16
24
|
// Export ObjectStack spec types for driver development
|
|
17
|
-
import { Data, System } from '@objectstack/spec';
|
|
18
|
-
|
|
19
|
-
export
|
|
20
|
-
export type
|
|
25
|
+
import { Data, System, Automation } from '@objectstack/spec';
|
|
26
|
+
import { z } from 'zod';
|
|
27
|
+
export { QueryAST } from '@objectql/types';
|
|
28
|
+
export type DriverInterface = z.infer<typeof Data.DriverInterfaceSchema>;
|
|
29
|
+
export type DriverOptions = z.infer<typeof Data.DriverOptionsSchema>;
|
|
30
|
+
|
|
31
|
+
// Re-export new @objectstack/spec 1.1.0 types
|
|
32
|
+
export type StateMachineConfig = z.infer<typeof Automation.StateMachineSchema>;
|
|
33
|
+
export type ObjectOwnership = z.infer<typeof Data.ObjectOwnershipEnum>;
|
|
34
|
+
export type ObjectExtension = z.infer<typeof Data.ObjectExtensionSchema>;
|
|
21
35
|
|
|
22
36
|
export * from './gateway';
|
|
23
37
|
|
|
@@ -34,3 +48,6 @@ export * from './util';
|
|
|
34
48
|
|
|
35
49
|
// Export kernel optimizations
|
|
36
50
|
export * from './optimizations';
|
|
51
|
+
|
|
52
|
+
// Export AI runtime
|
|
53
|
+
export * from './ai';
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { Logger, ConsoleLogger } from '@objectql/types';
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Hook definition
|
|
11
13
|
*/
|
|
@@ -30,6 +32,9 @@ export class CompiledHookManager {
|
|
|
30
32
|
|
|
31
33
|
// Keep track of all registered hooks for management
|
|
32
34
|
private allHooks = new Map<string, Hook>();
|
|
35
|
+
|
|
36
|
+
// Structured logger
|
|
37
|
+
private logger: Logger = new ConsoleLogger({ name: '@objectql/hook-manager', level: 'warn' });
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Expand a pattern like "before*" to all matching events
|
|
@@ -135,7 +140,10 @@ export class CompiledHookManager {
|
|
|
135
140
|
try {
|
|
136
141
|
return Promise.resolve(hook.handler(context));
|
|
137
142
|
} catch (error) {
|
|
138
|
-
|
|
143
|
+
this.logger.error(`Hook execution failed for ${event}:${objectName}`, error as Error, {
|
|
144
|
+
event,
|
|
145
|
+
objectName,
|
|
146
|
+
});
|
|
139
147
|
return Promise.resolve();
|
|
140
148
|
}
|
|
141
149
|
}));
|
package/src/plugin.ts
CHANGED
|
@@ -7,12 +7,15 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { RuntimePlugin, RuntimeContext } from '@objectql/types';
|
|
10
|
+
import { ConsoleLogger } from '@objectql/types';
|
|
11
|
+
import type { Logger } from '@objectql/types';
|
|
10
12
|
import { ValidatorPlugin, ValidatorPluginConfig } from '@objectql/plugin-validator';
|
|
11
13
|
import { FormulaPlugin, FormulaPluginConfig } from '@objectql/plugin-formula';
|
|
12
14
|
import { QueryService } from './query/query-service';
|
|
13
15
|
import { QueryAnalyzer } from './query/query-analyzer';
|
|
14
16
|
import { ObjectStackProtocolImplementation } from './protocol';
|
|
15
17
|
import type { Driver } from '@objectql/types';
|
|
18
|
+
import { createDefaultAiRegistry } from './ai';
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Extended kernel with ObjectQL services
|
|
@@ -93,6 +96,7 @@ export interface ObjectQLPluginConfig {
|
|
|
93
96
|
export class ObjectQLPlugin implements RuntimePlugin {
|
|
94
97
|
name = '@objectql/core';
|
|
95
98
|
version = '4.0.2';
|
|
99
|
+
private logger: Logger;
|
|
96
100
|
|
|
97
101
|
constructor(private config: ObjectQLPluginConfig = {}, ql?: any) {
|
|
98
102
|
// Set defaults
|
|
@@ -104,6 +108,7 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
104
108
|
enableQueryService: true,
|
|
105
109
|
...config
|
|
106
110
|
};
|
|
111
|
+
this.logger = new ConsoleLogger({ name: '@objectql/core/plugin', level: 'info' });
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
/**
|
|
@@ -111,7 +116,7 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
111
116
|
* This is called during kernel initialization
|
|
112
117
|
*/
|
|
113
118
|
async install(ctx: RuntimeContext): Promise<void> {
|
|
114
|
-
|
|
119
|
+
this.logger.info('Installing plugin...');
|
|
115
120
|
|
|
116
121
|
const kernel = ctx.engine as ExtendedKernel;
|
|
117
122
|
|
|
@@ -127,9 +132,11 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
127
132
|
const driverName = driver.name || (index === 0 ? 'default' : `driver_${index + 1}`);
|
|
128
133
|
datasources![driverName] = driver;
|
|
129
134
|
});
|
|
130
|
-
|
|
135
|
+
this.logger.info('Using drivers from kernel', {
|
|
136
|
+
drivers: Object.keys(datasources),
|
|
137
|
+
});
|
|
131
138
|
} else {
|
|
132
|
-
|
|
139
|
+
this.logger.warn('No datasources configured and no drivers found in kernel. Repository and QueryService will not be available.');
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
@@ -147,7 +154,7 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
147
154
|
);
|
|
148
155
|
kernel.queryAnalyzer = queryAnalyzer;
|
|
149
156
|
|
|
150
|
-
|
|
157
|
+
this.logger.info('QueryService and QueryAnalyzer registered');
|
|
151
158
|
}
|
|
152
159
|
|
|
153
160
|
// Register components based on configuration
|
|
@@ -171,7 +178,23 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
171
178
|
await this.registerAI(kernel);
|
|
172
179
|
}
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
// Register system service aliases
|
|
182
|
+
if (typeof (ctx as any).registerService === 'function') {
|
|
183
|
+
const registerService = (ctx as any).registerService.bind(ctx);
|
|
184
|
+
|
|
185
|
+
// 1. Metadata service
|
|
186
|
+
if (kernel.metadata) {
|
|
187
|
+
registerService('metadata', kernel.metadata);
|
|
188
|
+
this.logger.debug('Registered metadata service alias');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 2. Data service (prefer QueryService, fallback to kernel)
|
|
192
|
+
const dataService = kernel.queryService || kernel;
|
|
193
|
+
registerService('data', dataService);
|
|
194
|
+
this.logger.debug('Registered data service alias');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.logger.info('Plugin installed successfully');
|
|
175
198
|
}
|
|
176
199
|
|
|
177
200
|
/**
|
|
@@ -179,37 +202,11 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
179
202
|
* This is the initialization phase
|
|
180
203
|
*/
|
|
181
204
|
async onStart(ctx: RuntimeContext): Promise<void> {
|
|
182
|
-
|
|
205
|
+
this.logger.debug('Starting plugin...');
|
|
183
206
|
// Additional startup logic can be added here
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
// --- Adapter for @objectstack/core compatibility ---
|
|
187
|
-
async init(kernel: any): Promise<void> {
|
|
188
|
-
// The new core passes the kernel instance directly
|
|
189
|
-
// We wrap it to match the old RuntimeContext interface
|
|
190
|
-
const ctx = {
|
|
191
|
-
engine: kernel,
|
|
192
|
-
getKernel: () => kernel
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
// Register Protocol Service
|
|
196
|
-
// If kernel supports service registration (PluginContext or ExtendedKernel with custom registry)
|
|
197
|
-
if (kernel && typeof kernel.registerService === 'function') {
|
|
198
|
-
console.log(`[${this.name}] Registering protocol service...`);
|
|
199
|
-
const protocolShim = new ObjectStackProtocolImplementation(kernel as any);
|
|
200
|
-
kernel.registerService('protocol', protocolShim);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return this.install(ctx);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async start(kernel: any): Promise<void> {
|
|
207
|
-
const ctx = {
|
|
208
|
-
engine: kernel,
|
|
209
|
-
getKernel: () => kernel
|
|
210
|
-
};
|
|
211
|
-
return this.onStart(ctx);
|
|
212
|
-
}
|
|
213
210
|
// ---------------------------------------------------
|
|
214
211
|
|
|
215
212
|
/**
|
|
@@ -286,7 +283,7 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
286
283
|
return await driver.count(objectName, filters || {}, {});
|
|
287
284
|
};
|
|
288
285
|
|
|
289
|
-
|
|
286
|
+
this.logger.info('Repository pattern registered');
|
|
290
287
|
}
|
|
291
288
|
|
|
292
289
|
/**
|
|
@@ -294,8 +291,41 @@ export class ObjectQLPlugin implements RuntimePlugin {
|
|
|
294
291
|
* @private
|
|
295
292
|
*/
|
|
296
293
|
private async registerAI(kernel: any): Promise<void> {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
if (!(kernel as any).ai) {
|
|
295
|
+
(kernel as any).ai = createDefaultAiRegistry();
|
|
296
|
+
}
|
|
297
|
+
this.logger.debug('AI integration registered');
|
|
300
298
|
}
|
|
299
|
+
|
|
300
|
+
// --- Adapter for @objectstack/core compatibility ---
|
|
301
|
+
init = async (kernel: any): Promise<void> => {
|
|
302
|
+
// The new core passes the kernel instance directly
|
|
303
|
+
// We wrap it to match the old RuntimeContext interface
|
|
304
|
+
const ctx: any = {
|
|
305
|
+
engine: kernel,
|
|
306
|
+
getKernel: () => kernel
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// Register Protocol Service
|
|
310
|
+
// If kernel supports service registration (PluginContext or ExtendedKernel with custom registry)
|
|
311
|
+
if (kernel && typeof kernel.registerService === 'function') {
|
|
312
|
+
this.logger.info('Registering protocol service...');
|
|
313
|
+
const protocolShim = new ObjectStackProtocolImplementation(kernel);
|
|
314
|
+
kernel.registerService('protocol', protocolShim);
|
|
315
|
+
|
|
316
|
+
// Register 'objectql' service for AppPlugin compatibility
|
|
317
|
+
kernel.registerService('objectql', this);
|
|
318
|
+
this.logger.debug('Registered objectql service');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return this.install(ctx);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
start = async (kernel: any): Promise<void> => {
|
|
325
|
+
const ctx: any = {
|
|
326
|
+
engine: kernel,
|
|
327
|
+
getKernel: () => kernel
|
|
328
|
+
};
|
|
329
|
+
return this.onStart(ctx);
|
|
330
|
+
}
|
|
301
331
|
}
|
package/src/protocol.ts
CHANGED
|
@@ -72,6 +72,19 @@ export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
72
72
|
return this.getMetaItem(args);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Save Metadata Item
|
|
77
|
+
*/
|
|
78
|
+
async saveMetaItem(args: { type: string; name: string; item?: any }): Promise<{ success: boolean; message?: string }> {
|
|
79
|
+
const { type, name, item } = args;
|
|
80
|
+
if (this.engine.metadata && typeof this.engine.metadata.register === 'function') {
|
|
81
|
+
const data = { ...(item || {}), name }; // Ensure name is in data
|
|
82
|
+
this.engine.metadata.register(type, data);
|
|
83
|
+
return { success: true };
|
|
84
|
+
}
|
|
85
|
+
throw new Error('Engine does not support saving metadata');
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
/**
|
|
76
89
|
* Get UI View
|
|
77
90
|
*/
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { Filter } from '@objectql/types';
|
|
10
10
|
import { Data } from '@objectstack/spec';
|
|
11
|
-
|
|
11
|
+
import { z } from 'zod';
|
|
12
12
|
import { ObjectQLError } from '@objectql/types';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -25,7 +25,7 @@ export class FilterTranslator {
|
|
|
25
25
|
/**
|
|
26
26
|
* Translate filters from ObjectQL format to ObjectStack FilterCondition format
|
|
27
27
|
*/
|
|
28
|
-
translate(filters?: Filter):
|
|
28
|
+
translate(filters?: Filter): Filter | undefined {
|
|
29
29
|
if (!filters) {
|
|
30
30
|
return undefined;
|
|
31
31
|
}
|
|
@@ -36,6 +36,6 @@ export class FilterTranslator {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// Both ObjectQL Filter and ObjectStack FilterCondition use the same format now
|
|
39
|
-
return filters
|
|
39
|
+
return filters;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { UnifiedQuery, ObjectConfig, MetadataRegistry } from '@objectql/types';
|
|
10
|
-
import {
|
|
11
|
-
type QueryAST = Data.QueryAST;
|
|
10
|
+
import { QueryAST } from '@objectql/types';
|
|
12
11
|
import { QueryService, QueryOptions } from './query-service';
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { UnifiedQuery } from '@objectql/types';
|
|
10
|
-
import {
|
|
9
|
+
import type { UnifiedQuery, QueryAST } from '@objectql/types';
|
|
10
|
+
// import { QueryAST } from './types'; (Removed)
|
|
11
11
|
|
|
12
12
|
// Local QueryAST type extension to include all properties we need
|
|
13
|
-
interface QueryAST extends Data.QueryAST {
|
|
14
|
-
top?: number;
|
|
15
|
-
expand?: Record<string, any>;
|
|
16
|
-
aggregations?: any[];
|
|
17
|
-
having?: any;
|
|
18
|
-
}
|
|
13
|
+
// interface QueryAST extends Data.QueryAST {
|
|
14
|
+
// top?: number;
|
|
15
|
+
// expand?: Record<string, any>;
|
|
16
|
+
// aggregations?: any[];
|
|
17
|
+
// having?: any;
|
|
18
|
+
// }
|
|
19
19
|
|
|
20
20
|
import { FilterTranslator } from './filter-translator';
|
|
21
21
|
|
|
@@ -13,8 +13,7 @@ import type {
|
|
|
13
13
|
Filter,
|
|
14
14
|
MetadataRegistry
|
|
15
15
|
} from '@objectql/types';
|
|
16
|
-
import {
|
|
17
|
-
type QueryAST = Data.QueryAST;
|
|
16
|
+
import { QueryAST } from '@objectql/types';
|
|
18
17
|
import { QueryBuilder } from './query-builder';
|
|
19
18
|
import { QueryCompiler } from '../optimizations/QueryCompiler';
|
|
20
19
|
|
package/src/repository.ts
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext, ValidationContext, ValidationError, ValidationRuleResult, FormulaContext, Filter } from '@objectql/types';
|
|
9
|
+
import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext, ValidationContext, ValidationError, ValidationRuleResult, FormulaContext, Filter, QueryAST } from '@objectql/types';
|
|
10
10
|
import type { ObjectKernel } from '@objectstack/runtime';
|
|
11
11
|
import { Data } from '@objectstack/spec';
|
|
12
|
-
|
|
13
|
-
type SortNode = Data.
|
|
12
|
+
import { z } from 'zod';
|
|
13
|
+
type SortNode = z.infer<typeof Data.SortNodeSchema>;
|
|
14
14
|
|
|
15
15
|
export class ObjectRepository {
|
|
16
16
|
|
|
@@ -149,7 +149,14 @@ export class ObjectRepository {
|
|
|
149
149
|
await this.app.triggerHook('beforeCount', this.objectName, hookCtx);
|
|
150
150
|
|
|
151
151
|
// Execute via kernel (delegates to QueryService)
|
|
152
|
-
|
|
152
|
+
let result: number;
|
|
153
|
+
const kernel = this.getKernel() as any;
|
|
154
|
+
if (typeof kernel.count === 'function') {
|
|
155
|
+
result = await kernel.count(this.objectName, hookCtx.query || {});
|
|
156
|
+
} else {
|
|
157
|
+
// Fallback to driver if kernel doesn't support count
|
|
158
|
+
result = await this.getDriver().count(this.objectName, hookCtx.query || {});
|
|
159
|
+
}
|
|
153
160
|
|
|
154
161
|
hookCtx.result = result;
|
|
155
162
|
await this.app.triggerHook('afterCount', this.objectName, hookCtx);
|