@objectql/core 4.0.1 → 4.0.3

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 (103) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +32 -0
  3. package/README.md +14 -12
  4. package/dist/app.d.ts +9 -6
  5. package/dist/app.js +151 -29
  6. package/dist/app.js.map +1 -1
  7. package/dist/index.d.ts +6 -9
  8. package/dist/index.js +2 -5
  9. package/dist/index.js.map +1 -1
  10. package/dist/optimizations/CompiledHookManager.d.ts +55 -0
  11. package/dist/optimizations/CompiledHookManager.js +164 -0
  12. package/dist/optimizations/CompiledHookManager.js.map +1 -0
  13. package/dist/optimizations/DependencyGraph.d.ts +82 -0
  14. package/dist/optimizations/DependencyGraph.js +211 -0
  15. package/dist/optimizations/DependencyGraph.js.map +1 -0
  16. package/dist/optimizations/GlobalConnectionPool.d.ts +89 -0
  17. package/dist/optimizations/GlobalConnectionPool.js +193 -0
  18. package/dist/optimizations/GlobalConnectionPool.js.map +1 -0
  19. package/dist/optimizations/LazyMetadataLoader.d.ts +75 -0
  20. package/dist/optimizations/LazyMetadataLoader.js +149 -0
  21. package/dist/optimizations/LazyMetadataLoader.js.map +1 -0
  22. package/dist/optimizations/OptimizedMetadataRegistry.d.ts +26 -0
  23. package/dist/optimizations/OptimizedMetadataRegistry.js +117 -0
  24. package/dist/optimizations/OptimizedMetadataRegistry.js.map +1 -0
  25. package/dist/optimizations/OptimizedValidationEngine.d.ts +73 -0
  26. package/dist/optimizations/OptimizedValidationEngine.js +141 -0
  27. package/dist/optimizations/OptimizedValidationEngine.js.map +1 -0
  28. package/dist/optimizations/QueryCompiler.d.ts +51 -0
  29. package/dist/optimizations/QueryCompiler.js +216 -0
  30. package/dist/optimizations/QueryCompiler.js.map +1 -0
  31. package/dist/optimizations/SQLQueryOptimizer.d.ts +96 -0
  32. package/dist/optimizations/SQLQueryOptimizer.js +265 -0
  33. package/dist/optimizations/SQLQueryOptimizer.js.map +1 -0
  34. package/dist/optimizations/index.d.ts +32 -0
  35. package/dist/optimizations/index.js +44 -0
  36. package/dist/optimizations/index.js.map +1 -0
  37. package/dist/plugin.d.ts +6 -7
  38. package/dist/plugin.js +39 -22
  39. package/dist/plugin.js.map +1 -1
  40. package/dist/query/filter-translator.d.ts +6 -18
  41. package/dist/query/filter-translator.js +6 -103
  42. package/dist/query/filter-translator.js.map +1 -1
  43. package/dist/query/query-analyzer.js +24 -25
  44. package/dist/query/query-analyzer.js.map +1 -1
  45. package/dist/query/query-builder.d.ts +9 -3
  46. package/dist/query/query-builder.js +25 -35
  47. package/dist/query/query-builder.js.map +1 -1
  48. package/dist/query/query-service.d.ts +2 -2
  49. package/dist/query/query-service.js +5 -5
  50. package/dist/query/query-service.js.map +1 -1
  51. package/dist/repository.d.ts +2 -0
  52. package/dist/repository.js +24 -17
  53. package/dist/repository.js.map +1 -1
  54. package/jest.config.js +3 -3
  55. package/package.json +8 -5
  56. package/src/app.ts +173 -47
  57. package/src/index.ts +7 -8
  58. package/src/optimizations/CompiledHookManager.ts +185 -0
  59. package/src/optimizations/DependencyGraph.ts +255 -0
  60. package/src/optimizations/GlobalConnectionPool.ts +251 -0
  61. package/src/optimizations/LazyMetadataLoader.ts +180 -0
  62. package/src/optimizations/OptimizedMetadataRegistry.ts +132 -0
  63. package/src/optimizations/OptimizedValidationEngine.ts +172 -0
  64. package/src/optimizations/QueryCompiler.ts +242 -0
  65. package/src/optimizations/SQLQueryOptimizer.ts +329 -0
  66. package/src/optimizations/index.ts +34 -0
  67. package/src/plugin.ts +51 -28
  68. package/src/query/filter-translator.ts +8 -115
  69. package/src/query/query-analyzer.ts +25 -29
  70. package/src/query/query-builder.ts +26 -43
  71. package/src/query/query-service.ts +6 -6
  72. package/src/repository.ts +35 -22
  73. package/test/__mocks__/@objectstack/runtime.ts +8 -8
  74. package/test/app.test.ts +11 -8
  75. package/test/optimizations.test.ts +440 -0
  76. package/test/plugin-integration.test.ts +30 -19
  77. package/tsconfig.json +4 -6
  78. package/tsconfig.tsbuildinfo +1 -1
  79. package/dist/ai-agent.d.ts +0 -176
  80. package/dist/ai-agent.js +0 -722
  81. package/dist/ai-agent.js.map +0 -1
  82. package/dist/formula-engine.d.ts +0 -102
  83. package/dist/formula-engine.js +0 -433
  84. package/dist/formula-engine.js.map +0 -1
  85. package/dist/formula-plugin.d.ts +0 -52
  86. package/dist/formula-plugin.js +0 -107
  87. package/dist/formula-plugin.js.map +0 -1
  88. package/dist/validator-plugin.d.ts +0 -56
  89. package/dist/validator-plugin.js +0 -106
  90. package/dist/validator-plugin.js.map +0 -1
  91. package/dist/validator.d.ts +0 -80
  92. package/dist/validator.js +0 -625
  93. package/dist/validator.js.map +0 -1
  94. package/src/ai-agent.ts +0 -868
  95. package/src/formula-engine.ts +0 -572
  96. package/src/formula-plugin.ts +0 -141
  97. package/src/validator-plugin.ts +0 -140
  98. package/src/validator.ts +0 -743
  99. package/test/formula-engine.test.ts +0 -725
  100. package/test/formula-integration.test.ts +0 -286
  101. package/test/formula-plugin.test.ts +0 -197
  102. package/test/validator-plugin.test.ts +0 -126
  103. package/test/validator.test.ts +0 -440
@@ -9,7 +9,6 @@
9
9
  import type { UnifiedQuery, ObjectConfig, MetadataRegistry } from '@objectql/types';
10
10
  import { Data } from '@objectstack/spec';
11
11
  type QueryAST = Data.QueryAST;
12
- type FilterNode = Data.FilterNode;
13
12
  import { QueryService, QueryOptions } from './query-service';
14
13
 
15
14
  /**
@@ -167,10 +166,10 @@ export class QueryAnalyzer {
167
166
  // Build the QueryAST (without executing)
168
167
  const ast: QueryAST = {
169
168
  object: objectName,
170
- filters: query.filters as any, // FilterCondition is compatible with FilterNode
171
- sort: query.sort as any, // Will be converted to SortNode[] format
172
- top: query.limit, // Changed from limit to top (QueryAST uses 'top')
173
- skip: query.skip,
169
+ where: query.where as any, // FilterCondition format
170
+ orderBy: query.orderBy as any, // Will be converted to SortNode[] format
171
+ limit: query.limit,
172
+ offset: query.offset,
174
173
  fields: query.fields
175
174
  };
176
175
 
@@ -273,7 +272,7 @@ export class QueryAnalyzer {
273
272
  * @private
274
273
  */
275
274
  private getSchema(objectName: string): ObjectConfig {
276
- const obj = this.metadata.get<ObjectConfig>('object', objectName);
275
+ const obj = this.metadata.get('object', objectName);
277
276
  if (!obj) {
278
277
  throw new Error(`Object '${objectName}' not found`);
279
278
  }
@@ -287,7 +286,7 @@ export class QueryAnalyzer {
287
286
  private findApplicableIndexes(schema: ObjectConfig, query: UnifiedQuery): string[] {
288
287
  const indexes: string[] = [];
289
288
 
290
- if (!schema.indexes || !query.filters) {
289
+ if (!schema.indexes || !query.where) {
291
290
  return indexes;
292
291
  }
293
292
 
@@ -316,7 +315,7 @@ export class QueryAnalyzer {
316
315
  }
317
316
  };
318
317
 
319
- extractFieldsFromFilter(query.filters);
318
+ extractFieldsFromFilter(query.where);
320
319
 
321
320
  // Check which indexes could be used
322
321
  const indexesArray = Array.isArray(schema.indexes) ? schema.indexes : Object.values(schema.indexes || {});
@@ -343,7 +342,7 @@ export class QueryAnalyzer {
343
342
  const warnings: string[] = [];
344
343
 
345
344
  // Warning: No filters (full table scan)
346
- if (!query.filters || query.filters.length === 0) {
345
+ if (!query.where || Object.keys(query.where).length === 0) {
347
346
  warnings.push('No filters specified - this will scan all records');
348
347
  }
349
348
 
@@ -361,7 +360,7 @@ export class QueryAnalyzer {
361
360
  }
362
361
 
363
362
  // Warning: Complex filters without indexes
364
- if (query.filters && query.filters.length > 5) {
363
+ if (query.where && Object.keys(query.where).length > 5) {
365
364
  const indexes = this.findApplicableIndexes(schema, query);
366
365
  if (indexes.length === 0) {
367
366
  warnings.push('Complex filters without matching indexes - consider adding indexes');
@@ -388,10 +387,8 @@ export class QueryAnalyzer {
388
387
  }
389
388
 
390
389
  // Suggest adding indexes
391
- if (query.filters && query.filters.length > 0 && indexes.length === 0) {
392
- const filterFields = query.filters
393
- .filter((f: any) => Array.isArray(f) && f.length >= 1)
394
- .map((f: any) => String(f[0]));
390
+ if (query.where && Object.keys(query.where).length > 0 && indexes.length === 0) {
391
+ const filterFields = Object.keys(query.where).filter(k => !k.startsWith('$'));
395
392
 
396
393
  if (filterFields.length > 0) {
397
394
  suggestions.push(`Consider adding an index on: ${filterFields.join(', ')}`);
@@ -404,10 +401,9 @@ export class QueryAnalyzer {
404
401
  }
405
402
 
406
403
  // Suggest composite index for multiple filters
407
- if (query.filters && query.filters.length > 1 && indexes.length < 2) {
408
- const filterFields = query.filters
409
- .filter((f: any) => Array.isArray(f) && f.length >= 1)
410
- .map((f: any) => String(f[0]))
404
+ if (query.where && Object.keys(query.where).length > 1 && indexes.length < 2) {
405
+ const filterFields = Object.keys(query.where)
406
+ .filter(k => !k.startsWith('$'))
411
407
  .slice(0, 3); // Top 3 fields
412
408
 
413
409
  if (filterFields.length > 1) {
@@ -429,21 +425,20 @@ export class QueryAnalyzer {
429
425
  complexity += 10;
430
426
 
431
427
  // Filters add complexity
432
- if (query.filters) {
433
- complexity += query.filters.length * 5;
428
+ if (query.where) {
429
+ const filterCount = Object.keys(query.where).length;
430
+ complexity += filterCount * 5;
434
431
 
435
- // Nested filters (OR conditions) add more
436
- const hasNestedFilters = query.filters.some((f: any) =>
437
- Array.isArray(f) && Array.isArray(f[0])
438
- );
439
- if (hasNestedFilters) {
432
+ // Nested filters (OR/AND conditions) add more
433
+ const hasLogicalOps = query.where.$and || query.where.$or;
434
+ if (hasLogicalOps) {
440
435
  complexity += 15;
441
436
  }
442
437
  }
443
438
 
444
439
  // Sorting adds complexity
445
- if (query.sort && query.sort.length > 0) {
446
- complexity += query.sort.length * 3;
440
+ if (query.orderBy && query.orderBy.length > 0) {
441
+ complexity += query.orderBy.length * 3;
447
442
  }
448
443
 
449
444
  // Field selection reduces complexity slightly
@@ -469,13 +464,14 @@ export class QueryAnalyzer {
469
464
  // from the database (row count, index selectivity, etc.)
470
465
 
471
466
  // Default to unknown
472
- if (!query.filters || query.filters.length === 0) {
467
+ if (!query.where || Object.keys(query.where).length === 0) {
473
468
  return -1; // Unknown, full scan
474
469
  }
475
470
 
476
471
  // Very rough estimate based on filter count
477
472
  const baseEstimate = 1000;
478
- const filterReduction = Math.pow(0.5, query.filters.length);
473
+ const filterCount = Object.keys(query.where).length;
474
+ const filterReduction = Math.pow(0.5, filterCount);
479
475
  const estimated = Math.floor(baseEstimate * filterReduction);
480
476
 
481
477
  // Apply limit if present
@@ -8,14 +8,23 @@
8
8
 
9
9
  import type { UnifiedQuery } from '@objectql/types';
10
10
  import { Data } from '@objectstack/spec';
11
- type QueryAST = Data.QueryAST;
11
+
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
+ }
19
+
12
20
  import { FilterTranslator } from './filter-translator';
13
21
 
14
22
  /**
15
23
  * Query Builder
16
24
  *
17
25
  * Builds ObjectStack QueryAST from ObjectQL UnifiedQuery.
18
- * This is the central query construction module for ObjectQL.
26
+ * Since UnifiedQuery now uses the standard protocol format directly,
27
+ * this is now a simple pass-through with object name injection.
19
28
  */
20
29
  export class QueryBuilder {
21
30
  private filterTranslator: FilterTranslator;
@@ -28,53 +37,27 @@ export class QueryBuilder {
28
37
  * Build a QueryAST from a UnifiedQuery
29
38
  *
30
39
  * @param objectName - Target object name
31
- * @param query - ObjectQL UnifiedQuery
40
+ * @param query - ObjectQL UnifiedQuery (now in standard QueryAST format)
32
41
  * @returns ObjectStack QueryAST
33
42
  */
34
43
  build(objectName: string, query: UnifiedQuery): QueryAST {
44
+ // UnifiedQuery now uses the same format as QueryAST
45
+ // Just add the object name and pass through
35
46
  const ast: QueryAST = {
36
- object: objectName,
47
+ object: objectName
37
48
  };
38
49
 
39
- // Map fields
40
- if (query.fields) {
41
- ast.fields = query.fields;
42
- }
43
-
44
- // Map filters using FilterTranslator
45
- if (query.filters) {
46
- ast.filters = this.filterTranslator.translate(query.filters);
47
- }
48
-
49
- // Map sort
50
- if (query.sort) {
51
- ast.sort = query.sort.map(([field, order]) => ({
52
- field,
53
- order: order as 'asc' | 'desc'
54
- }));
55
- }
56
-
57
- // Map pagination
58
- if (query.limit !== undefined) {
59
- ast.top = query.limit;
60
- }
61
- if (query.skip !== undefined) {
62
- ast.skip = query.skip;
63
- }
64
-
65
- // Map groupBy
66
- if (query.groupBy) {
67
- ast.groupBy = query.groupBy;
68
- }
69
-
70
- // Map aggregations
71
- if (query.aggregate) {
72
- ast.aggregations = query.aggregate.map(agg => ({
73
- function: agg.func as any,
74
- field: agg.field,
75
- alias: agg.alias || `${agg.func}_${agg.field}`
76
- }));
77
- }
50
+ // Map UnifiedQuery properties to QueryAST
51
+ if (query.fields) ast.fields = query.fields;
52
+ if (query.where) ast.where = this.filterTranslator.translate(query.where);
53
+ if (query.orderBy) ast.orderBy = query.orderBy;
54
+ if (query.offset !== undefined) ast.offset = query.offset;
55
+ if (query.limit !== undefined) ast.top = query.limit; // UnifiedQuery uses 'limit', QueryAST uses 'top'
56
+ if (query.expand) ast.expand = query.expand;
57
+ if (query.groupBy) ast.groupBy = query.groupBy;
58
+ if (query.aggregations) ast.aggregations = query.aggregations;
59
+ if (query.having) ast.having = query.having;
60
+ if (query.distinct) ast.distinct = query.distinct;
78
61
 
79
62
  return ast;
80
63
  }
@@ -130,7 +130,7 @@ export class QueryService {
130
130
  * @private
131
131
  */
132
132
  private getSchema(objectName: string): ObjectConfig {
133
- const obj = this.metadata.get<ObjectConfig>('object', objectName);
133
+ const obj = this.metadata.get('object', objectName);
134
134
  if (!obj) {
135
135
  throw new Error(`Object '${objectName}' not found in metadata`);
136
136
  }
@@ -232,7 +232,7 @@ export class QueryService {
232
232
  } else if (driver.executeQuery) {
233
233
  // Fallback to query with ID filter
234
234
  const query: UnifiedQuery = {
235
- filters: [['_id', '=', id]]
235
+ where: { _id: id }
236
236
  };
237
237
  const ast = this.buildQueryAST(objectName, query);
238
238
  const queryResult = await driver.executeQuery(ast, driverOptions);
@@ -256,19 +256,19 @@ export class QueryService {
256
256
  * Execute a count query
257
257
  *
258
258
  * @param objectName - The object to query
259
- * @param filters - Optional filters
259
+ * @param where - Optional filter condition
260
260
  * @param options - Query execution options
261
261
  * @returns Count of matching records
262
262
  */
263
263
  async count(
264
264
  objectName: string,
265
- filters?: Filter[],
265
+ where?: Filter,
266
266
  options: QueryOptions = {}
267
267
  ): Promise<QueryResult<number>> {
268
268
  const driver = this.getDriver(objectName);
269
269
  const startTime = options.profile ? Date.now() : 0;
270
270
 
271
- const query: UnifiedQuery = filters ? { filters } : {};
271
+ const query: UnifiedQuery = where ? { where } : {};
272
272
  const ast = this.buildQueryAST(objectName, query);
273
273
 
274
274
  const driverOptions = {
@@ -280,7 +280,7 @@ export class QueryService {
280
280
 
281
281
  if (driver.count) {
282
282
  // Legacy driver interface
283
- count = await driver.count(objectName, filters || [], driverOptions);
283
+ count = await driver.count(objectName, where || {}, driverOptions);
284
284
  } else if (driver.executeQuery) {
285
285
  // Use executeQuery and count results
286
286
  // Note: This is inefficient for large datasets
package/src/repository.ts CHANGED
@@ -7,26 +7,33 @@
7
7
  */
8
8
 
9
9
  import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext, ValidationContext, ValidationError, ValidationRuleResult, FormulaContext, Filter } from '@objectql/types';
10
- import type { ObjectStackKernel } from '@objectql/runtime';
10
+ import type { ObjectKernel } from '@objectstack/runtime';
11
11
  import { Data } from '@objectstack/spec';
12
12
  type QueryAST = Data.QueryAST;
13
- type FilterNode = Data.FilterNode;
14
13
  type SortNode = Data.SortNode;
15
- import { Validator } from './validator';
16
- import { FormulaEngine } from './formula-engine';
14
+ import { Validator } from '@objectql/plugin-validator';
15
+ import { FormulaEngine } from '@objectql/plugin-formula';
17
16
  import { QueryBuilder } from './query';
17
+ import { QueryCompiler } from './optimizations/QueryCompiler';
18
18
 
19
19
  /**
20
20
  * Extended ObjectStack Kernel with optional ObjectQL plugin capabilities.
21
21
  * These properties are attached by ValidatorPlugin and FormulaPlugin during installation.
22
22
  */
23
- interface ExtendedKernel extends ObjectStackKernel {
23
+ interface ExtendedKernel extends ObjectKernel {
24
24
  validator?: Validator;
25
25
  formulaEngine?: FormulaEngine;
26
+ create?: (objectName: string, data: any) => Promise<any>;
27
+ update?: (objectName: string, id: string, data: any) => Promise<any>;
28
+ delete?: (objectName: string, id: string) => Promise<any>;
29
+ find?: (objectName: string, query: any) => Promise<any>;
30
+ get?: (objectName: string, id: string) => Promise<any>;
26
31
  }
27
32
 
28
33
  export class ObjectRepository {
29
34
  private queryBuilder: QueryBuilder;
35
+ // Shared query compiler for caching compiled queries
36
+ private static queryCompiler = new QueryCompiler(1000);
30
37
 
31
38
  constructor(
32
39
  private objectName: string,
@@ -68,7 +75,7 @@ export class ObjectRepository {
68
75
  return this.app.datasource(datasourceName);
69
76
  }
70
77
 
71
- private getKernel(): ObjectStackKernel {
78
+ private getKernel(): ObjectKernel {
72
79
  return this.app.getKernel();
73
80
  }
74
81
 
@@ -81,9 +88,13 @@ export class ObjectRepository {
81
88
 
82
89
  /**
83
90
  * Translates ObjectQL UnifiedQuery to ObjectStack QueryAST format
91
+ * Uses query compiler for caching and optimization
84
92
  */
85
93
  private buildQueryAST(query: UnifiedQuery): QueryAST {
86
- return this.queryBuilder.build(this.objectName, query);
94
+ const ast = this.queryBuilder.build(this.objectName, query);
95
+ // Use query compiler to cache and optimize the AST
96
+ const compiled = ObjectRepository.queryCompiler.compile(this.objectName, ast);
97
+ return compiled.ast;
87
98
  }
88
99
 
89
100
  getSchema(): ObjectConfig {
@@ -225,9 +236,11 @@ export class ObjectRepository {
225
236
 
226
237
  // Evaluate each formula field
227
238
  for (const [fieldName, fieldConfig] of Object.entries(schema.fields)) {
228
- if (fieldConfig.type === 'formula' && fieldConfig.formula) {
239
+ const formulaExpression = fieldConfig.expression;
240
+
241
+ if (fieldConfig.type === 'formula' && formulaExpression) {
229
242
  const result = this.getFormulaEngine().evaluate(
230
- fieldConfig.formula,
243
+ formulaExpression,
231
244
  formulaContext,
232
245
  fieldConfig.data_type || 'text',
233
246
  { strict: true }
@@ -240,14 +253,14 @@ export class ObjectRepository {
240
253
  record[fieldName] = null;
241
254
  // Formula evaluation should not throw here, but we need observability
242
255
  // This logging is intentionally minimal and side-effect free
243
- // eslint-disable-next-line no-console
256
+
244
257
  console.error(
245
258
  '[ObjectQL][FormulaEngine] Formula evaluation failed',
246
259
  {
247
260
  objectName: this.objectName,
248
261
  fieldName,
249
262
  recordId: formulaContext.record_id,
250
- formula: fieldConfig.formula,
263
+ expression: formulaExpression,
251
264
  error: result.error,
252
265
  stack: result.stack,
253
266
  }
@@ -273,7 +286,7 @@ export class ObjectRepository {
273
286
 
274
287
  // Build QueryAST and execute via kernel
275
288
  const ast = this.buildQueryAST(hookCtx.query || {});
276
- const kernelResult = await this.getKernel().find(this.objectName, ast);
289
+ const kernelResult = await (this.getKernel() as any).find(this.objectName, ast);
277
290
  const results = kernelResult.value;
278
291
 
279
292
  // Evaluate formulas for each result
@@ -299,7 +312,7 @@ export class ObjectRepository {
299
312
  await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
300
313
 
301
314
  // Use kernel.get() for direct ID lookup
302
- const result = await this.getKernel().get(this.objectName, String(idOrQuery));
315
+ const result = await (this.getKernel() as any).get(this.objectName, String(idOrQuery));
303
316
 
304
317
  // Evaluate formulas if result exists
305
318
  const resultWithFormulas = result ? this.evaluateFormulas(result) : result;
@@ -319,13 +332,13 @@ export class ObjectRepository {
319
332
  // If filters is already a UnifiedQuery (has UnifiedQuery-specific properties), use it as-is
320
333
  let query: UnifiedQuery;
321
334
  if (Array.isArray(filters)) {
322
- query = { filters };
323
- } else if (filters && typeof filters === 'object' && (filters.fields || filters.sort || filters.limit !== undefined || filters.skip !== undefined)) {
335
+ query = { where: filters };
336
+ } else if (filters && typeof filters === 'object' && (filters.fields || filters.orderBy || filters.limit !== undefined || filters.offset !== undefined)) {
324
337
  // It's already a UnifiedQuery object
325
338
  query = filters;
326
339
  } else if (filters) {
327
340
  // It's a raw filter object, wrap it
328
- query = { filters };
341
+ query = { where: filters };
329
342
  } else {
330
343
  query = {};
331
344
  }
@@ -343,7 +356,7 @@ export class ObjectRepository {
343
356
 
344
357
  // Build QueryAST and execute via kernel to get count
345
358
  const ast = this.buildQueryAST(hookCtx.query || {});
346
- const kernelResult = await this.getKernel().find(this.objectName, ast);
359
+ const kernelResult = await (this.getKernel() as any).find(this.objectName, ast);
347
360
  const result = kernelResult.count;
348
361
 
349
362
  hookCtx.result = result;
@@ -371,7 +384,7 @@ export class ObjectRepository {
371
384
  await this.validateRecord('create', finalDoc);
372
385
 
373
386
  // Execute via kernel
374
- const result = await this.getKernel().create(this.objectName, finalDoc);
387
+ const result = await (this.getKernel() as any).create(this.objectName, finalDoc, this.getOptions());
375
388
 
376
389
  hookCtx.result = result;
377
390
  await this.app.triggerHook('afterCreate', this.objectName, hookCtx);
@@ -398,7 +411,7 @@ export class ObjectRepository {
398
411
  await this.validateRecord('update', hookCtx.data, previousData);
399
412
 
400
413
  // Execute via kernel
401
- const result = await this.getKernel().update(this.objectName, String(id), hookCtx.data);
414
+ const result = await (this.getKernel() as any).update(this.objectName, String(id), hookCtx.data, this.getOptions());
402
415
 
403
416
  hookCtx.result = result;
404
417
  await this.app.triggerHook('afterUpdate', this.objectName, hookCtx);
@@ -420,7 +433,7 @@ export class ObjectRepository {
420
433
  await this.app.triggerHook('beforeDelete', this.objectName, hookCtx);
421
434
 
422
435
  // Execute via kernel
423
- const result = await this.getKernel().delete(this.objectName, String(id));
436
+ const result = await (this.getKernel() as any).delete(this.objectName, String(id), this.getOptions());
424
437
 
425
438
  hookCtx.result = result;
426
439
  await this.app.triggerHook('afterDelete', this.objectName, hookCtx);
@@ -463,7 +476,7 @@ export class ObjectRepository {
463
476
  async updateMany(filters: any, data: any): Promise<any> {
464
477
  // Find all matching records and update them individually
465
478
  // to ensure validation and hooks are executed
466
- const records = await this.find({ filters });
479
+ const records = await this.find({ where: filters });
467
480
  let count = 0;
468
481
  for (const record of records) {
469
482
  if (record && record._id) {
@@ -477,7 +490,7 @@ export class ObjectRepository {
477
490
  async deleteMany(filters: any): Promise<any> {
478
491
  // Find all matching records and delete them individually
479
492
  // to ensure hooks are executed
480
- const records = await this.find({ filters });
493
+ const records = await this.find({ where: filters });
481
494
  let count = 0;
482
495
  for (const record of records) {
483
496
  if (record && record._id) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Mock for @objectql/runtime
2
+ * Mock for @objectstack/runtime
3
3
  * This mock is needed because the npm package has issues with Jest
4
4
  * and we want to focus on testing ObjectQL's logic, not the kernel integration.
5
5
  *
@@ -123,7 +123,7 @@ class MockActionManager {
123
123
  }
124
124
  }
125
125
 
126
- export class ObjectStackKernel {
126
+ export class ObjectKernel {
127
127
  public ql: unknown = null;
128
128
  public metadata: MockMetadataRegistry;
129
129
  public hooks: MockHookManager;
@@ -242,14 +242,14 @@ export class ObjectStackKernel {
242
242
  }
243
243
  }
244
244
 
245
- export class ObjectStackRuntimeProtocol {}
245
+ export class ObjectStackProtocolImplementation {}
246
246
 
247
- export interface RuntimeContext {
248
- engine: ObjectStackKernel;
247
+ export interface any {
248
+ engine: ObjectKernel;
249
249
  }
250
250
 
251
- export interface RuntimePlugin {
251
+ export interface ObjectQLPlugin {
252
252
  name: string;
253
- install?: (ctx: RuntimeContext) => void | Promise<void>;
254
- onStart?: (ctx: RuntimeContext) => void | Promise<void>;
253
+ install?: (ctx: any) => void | Promise<void>;
254
+ onStart?: (ctx: any) => void | Promise<void>;
255
255
  }
package/test/app.test.ts CHANGED
@@ -9,7 +9,8 @@
9
9
  import { ObjectQL } from '../src/app';
10
10
  import { MockDriver } from './mock-driver';
11
11
  import { ObjectConfig, HookContext, ActionContext, Metadata } from '@objectql/types';
12
- import type { PluginDefinition } from '@objectstack/spec';
12
+ import type { Kernel } from '@objectstack/spec';
13
+ type PluginDefinition = Kernel.PluginDefinition;
13
14
 
14
15
  const todoObject: ObjectConfig = {
15
16
  name: 'todo',
@@ -268,6 +269,8 @@ describe('ObjectQL App', () => {
268
269
  const entry: Metadata = {
269
270
  type: 'object',
270
271
  id: 'todo',
272
+ // @ts-expect-error - SchemaRegistry typing limitation
273
+ name: 'todo', // Ensure SchemaRegistry keys it correctly
271
274
  package: 'test-package',
272
275
  content: obj
273
276
  };
@@ -292,12 +295,12 @@ describe('ObjectQL App', () => {
292
295
 
293
296
  describe('Plugin System', () => {
294
297
  it('should initialize runtime plugins on init', async () => {
295
- const installFn = jest.fn();
296
- const onStartFn = jest.fn();
298
+ const initFn = jest.fn();
299
+ const startFn = jest.fn();
297
300
  const mockPlugin = {
298
301
  name: 'test-plugin',
299
- install: installFn,
300
- onStart: onStartFn
302
+ init: initFn,
303
+ start: startFn
301
304
  };
302
305
 
303
306
  const app = new ObjectQL({
@@ -306,14 +309,14 @@ describe('ObjectQL App', () => {
306
309
  });
307
310
 
308
311
  await app.init();
309
- expect(installFn).toHaveBeenCalled();
310
- expect(onStartFn).toHaveBeenCalled();
312
+ expect(initFn).toHaveBeenCalled();
313
+ expect(startFn).toHaveBeenCalled();
311
314
  });
312
315
 
313
316
  it('should use plugin method', () => {
314
317
  const mockPlugin = {
315
318
  name: 'test-plugin',
316
- install: jest.fn()
319
+ init: jest.fn()
317
320
  };
318
321
 
319
322
  const app = new ObjectQL({ datasources: {} });