@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.
Files changed (56) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/dist/ai/index.d.ts +8 -0
  4. package/dist/ai/index.js +25 -0
  5. package/dist/ai/index.js.map +1 -0
  6. package/dist/ai/registry.d.ts +23 -0
  7. package/dist/ai/registry.js +78 -0
  8. package/dist/ai/registry.js.map +1 -0
  9. package/dist/app.d.ts +1 -0
  10. package/dist/app.js +20 -16
  11. package/dist/app.js.map +1 -1
  12. package/dist/gateway.d.ts +3 -2
  13. package/dist/gateway.js +6 -2
  14. package/dist/gateway.js.map +1 -1
  15. package/dist/index.d.ts +11 -4
  16. package/dist/index.js +11 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/optimizations/CompiledHookManager.d.ts +1 -0
  19. package/dist/optimizations/CompiledHookManager.js +7 -1
  20. package/dist/optimizations/CompiledHookManager.js.map +1 -1
  21. package/dist/plugin.d.ts +3 -2
  22. package/dist/plugin.js +56 -33
  23. package/dist/plugin.js.map +1 -1
  24. package/dist/protocol.d.ts +11 -0
  25. package/dist/protocol.js +12 -0
  26. package/dist/protocol.js.map +1 -1
  27. package/dist/query/filter-translator.d.ts +1 -4
  28. package/dist/query/filter-translator.js.map +1 -1
  29. package/dist/query/query-analyzer.d.ts +1 -3
  30. package/dist/query/query-analyzer.js.map +1 -1
  31. package/dist/query/query-builder.d.ts +1 -9
  32. package/dist/query/query-builder.js +8 -0
  33. package/dist/query/query-builder.js.map +1 -1
  34. package/dist/query/query-service.d.ts +1 -3
  35. package/dist/query/query-service.js.map +1 -1
  36. package/dist/repository.js +9 -1
  37. package/dist/repository.js.map +1 -1
  38. package/package.json +16 -9
  39. package/src/ai/index.ts +9 -0
  40. package/src/ai/registry.ts +81 -0
  41. package/src/app.ts +24 -16
  42. package/src/gateway.ts +7 -3
  43. package/src/index.ts +24 -7
  44. package/src/optimizations/CompiledHookManager.ts +9 -1
  45. package/src/plugin.ts +66 -36
  46. package/src/protocol.ts +13 -0
  47. package/src/query/filter-translator.ts +3 -3
  48. package/src/query/query-analyzer.ts +1 -2
  49. package/src/query/query-builder.ts +8 -8
  50. package/src/query/query-service.ts +1 -2
  51. package/src/repository.ts +11 -4
  52. package/test/ai-registry.test.ts +42 -0
  53. package/test/optimizations.test.ts +1 -1
  54. package/test/plugin-integration.test.ts +3 -2
  55. package/tsconfig.tsbuildinfo +1 -1
  56. 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
- console.log(`Introspecting datasource '${datasourceName}'...`);
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
- console.log(`Discovered ${objects.length} table(s), registering as objects...`);
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
- console.log(`Closing driver '${name}'...`);
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
- console.log('[ObjectQL] Initializing with ObjectKernel...');
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
- console.warn('ObjectKernel does not have start() or bootstrap() method');
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
- console.error(`Failed to initialize plugin ${(plugin as any).name || 'unknown'}:`, error);
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
- console.log(`Initializing driver '${name}'...`);
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
- console.log('[ObjectQL] Initialization complete');
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
- console.log(`Processing ${dataEntries.length} initial data files...`);
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
- console.warn(`Skipping invalid data entry:`, entry);
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
- console.log(`Initialized record for ${objectName}`);
542
+ this.logger.debug(`Initialized record for ${objectName}`);
535
543
  } catch (e: any) {
536
544
  // Ignore duplicate key errors silently-ish
537
- console.warn(`Failed to insert initial data for ${objectName}: ${e.message}`);
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
- console.error(`[ObjectGateway] Error in ${protocol.name}:`, error);
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
- // Note: @objectstack/objectql types temporarily commented out due to type incompatibilities
13
- // in the published package. Will be re-enabled when package is updated.
14
- // export type { ObjectQL as ObjectQLEngine, SchemaRegistry } from '@objectstack/objectql';
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
- export type QueryAST = Data.QueryAST;
19
- export type DriverInterface = Data.DriverInterface;
20
- export type DriverOptions = Data.DriverOptions;
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
- console.error(`Hook execution failed for ${event}:${objectName}`, error);
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
- console.log(`[${this.name}] Installing plugin...`);
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
- console.log(`[${this.name}] Using drivers from kernel:`, Object.keys(datasources));
135
+ this.logger.info('Using drivers from kernel', {
136
+ drivers: Object.keys(datasources),
137
+ });
131
138
  } else {
132
- console.warn(`[${this.name}] No datasources configured and no drivers found in kernel. Repository and QueryService will not be available.`);
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
- console.log(`[${this.name}] QueryService and QueryAnalyzer registered`);
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
- console.log(`[${this.name}] Plugin installed successfully`);
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
- console.log(`[${this.name}] Starting plugin...`);
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
- console.log(`[${this.name}] Repository pattern registered`);
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
- // TODO: Implement AI registration
298
- // For now, this is a placeholder to establish the structure
299
- console.log(`[${this.name}] AI integration registered`);
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
- type FilterCondition = Data.FilterCondition;
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): FilterCondition | undefined {
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 as unknown as FilterCondition;
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 { Data } from '@objectstack/spec';
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 { Data } from '@objectstack/spec';
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 { Data } from '@objectstack/spec';
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
- type QueryAST = Data.QueryAST;
13
- type SortNode = Data.SortNode;
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
- const result = await (this.getKernel() as any).count(this.objectName, hookCtx.query || {});
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);