@objectql/core 3.0.1 → 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.
Files changed (97) hide show
  1. package/IMPLEMENTATION_STATUS.md +364 -0
  2. package/README.md +31 -9
  3. package/RUNTIME_INTEGRATION.md +391 -0
  4. package/dist/ai-agent.d.ts +4 -3
  5. package/dist/ai-agent.js +10 -3
  6. package/dist/ai-agent.js.map +1 -1
  7. package/dist/app.d.ts +29 -6
  8. package/dist/app.js +117 -58
  9. package/dist/app.js.map +1 -1
  10. package/dist/formula-engine.d.ts +7 -0
  11. package/dist/formula-engine.js +9 -2
  12. package/dist/formula-engine.js.map +1 -1
  13. package/dist/formula-plugin.d.ts +52 -0
  14. package/dist/formula-plugin.js +107 -0
  15. package/dist/formula-plugin.js.map +1 -0
  16. package/dist/index.d.ts +13 -3
  17. package/dist/index.js +14 -3
  18. package/dist/index.js.map +1 -1
  19. package/dist/plugin.d.ts +89 -0
  20. package/dist/plugin.js +99 -0
  21. package/dist/plugin.js.map +1 -0
  22. package/dist/query/filter-translator.d.ts +37 -0
  23. package/dist/query/filter-translator.js +135 -0
  24. package/dist/query/filter-translator.js.map +1 -0
  25. package/dist/query/index.d.ts +22 -0
  26. package/dist/query/index.js +39 -0
  27. package/dist/query/index.js.map +1 -0
  28. package/dist/query/query-analyzer.d.ts +186 -0
  29. package/dist/query/query-analyzer.js +349 -0
  30. package/dist/query/query-analyzer.js.map +1 -0
  31. package/dist/query/query-builder.d.ts +27 -0
  32. package/dist/query/query-builder.js +71 -0
  33. package/dist/query/query-builder.js.map +1 -0
  34. package/dist/query/query-service.d.ts +150 -0
  35. package/dist/query/query-service.js +268 -0
  36. package/dist/query/query-service.js.map +1 -0
  37. package/dist/repository.d.ts +23 -2
  38. package/dist/repository.js +62 -13
  39. package/dist/repository.js.map +1 -1
  40. package/dist/util.d.ts +7 -0
  41. package/dist/util.js +18 -3
  42. package/dist/util.js.map +1 -1
  43. package/dist/validator-plugin.d.ts +56 -0
  44. package/dist/validator-plugin.js +106 -0
  45. package/dist/validator-plugin.js.map +1 -0
  46. package/dist/validator.d.ts +7 -0
  47. package/dist/validator.js +10 -8
  48. package/dist/validator.js.map +1 -1
  49. package/jest.config.js +16 -0
  50. package/package.json +8 -5
  51. package/src/ai-agent.ts +8 -0
  52. package/src/app.ts +136 -72
  53. package/src/formula-engine.ts +8 -0
  54. package/src/formula-plugin.ts +141 -0
  55. package/src/index.ts +25 -3
  56. package/src/plugin.ts +179 -0
  57. package/src/query/filter-translator.ts +147 -0
  58. package/src/query/index.ts +24 -0
  59. package/src/query/query-analyzer.ts +535 -0
  60. package/src/query/query-builder.ts +80 -0
  61. package/src/query/query-service.ts +392 -0
  62. package/src/repository.ts +81 -17
  63. package/src/util.ts +19 -3
  64. package/src/validator-plugin.ts +140 -0
  65. package/src/validator.ts +12 -5
  66. package/test/__mocks__/@objectstack/runtime.ts +255 -0
  67. package/test/app.test.ts +23 -35
  68. package/test/filter-syntax.test.ts +233 -0
  69. package/test/formula-engine.test.ts +8 -0
  70. package/test/formula-integration.test.ts +8 -0
  71. package/test/formula-plugin.test.ts +197 -0
  72. package/test/introspection.test.ts +8 -0
  73. package/test/mock-driver.ts +8 -0
  74. package/test/plugin-integration.test.ts +213 -0
  75. package/test/repository-validation.test.ts +8 -0
  76. package/test/repository.test.ts +8 -0
  77. package/test/util.test.ts +9 -1
  78. package/test/utils.ts +8 -0
  79. package/test/validator-plugin.test.ts +126 -0
  80. package/test/validator.test.ts +8 -0
  81. package/tsconfig.json +9 -0
  82. package/tsconfig.tsbuildinfo +1 -1
  83. package/dist/action.d.ts +0 -7
  84. package/dist/action.js +0 -23
  85. package/dist/action.js.map +0 -1
  86. package/dist/hook.d.ts +0 -8
  87. package/dist/hook.js +0 -25
  88. package/dist/hook.js.map +0 -1
  89. package/dist/object.d.ts +0 -3
  90. package/dist/object.js +0 -28
  91. package/dist/object.js.map +0 -1
  92. package/src/action.ts +0 -40
  93. package/src/hook.ts +0 -42
  94. package/src/object.ts +0 -26
  95. package/test/action.test.ts +0 -276
  96. package/test/hook.test.ts +0 -343
  97. package/test/object.test.ts +0 -183
@@ -0,0 +1,80 @@
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
+ import type { UnifiedQuery } from '@objectql/types';
10
+ import type { QueryAST } from '@objectstack/spec';
11
+ import { FilterTranslator } from './filter-translator';
12
+
13
+ /**
14
+ * Query Builder
15
+ *
16
+ * Builds ObjectStack QueryAST from ObjectQL UnifiedQuery.
17
+ * This is the central query construction module for ObjectQL.
18
+ */
19
+ export class QueryBuilder {
20
+ private filterTranslator: FilterTranslator;
21
+
22
+ constructor() {
23
+ this.filterTranslator = new FilterTranslator();
24
+ }
25
+
26
+ /**
27
+ * Build a QueryAST from a UnifiedQuery
28
+ *
29
+ * @param objectName - Target object name
30
+ * @param query - ObjectQL UnifiedQuery
31
+ * @returns ObjectStack QueryAST
32
+ */
33
+ build(objectName: string, query: UnifiedQuery): QueryAST {
34
+ const ast: QueryAST = {
35
+ object: objectName,
36
+ };
37
+
38
+ // Map fields
39
+ if (query.fields) {
40
+ ast.fields = query.fields;
41
+ }
42
+
43
+ // Map filters using FilterTranslator
44
+ if (query.filters) {
45
+ ast.filters = this.filterTranslator.translate(query.filters);
46
+ }
47
+
48
+ // Map sort
49
+ if (query.sort) {
50
+ ast.sort = query.sort.map(([field, order]) => ({
51
+ field,
52
+ order: order as 'asc' | 'desc'
53
+ }));
54
+ }
55
+
56
+ // Map pagination
57
+ if (query.limit !== undefined) {
58
+ ast.top = query.limit;
59
+ }
60
+ if (query.skip !== undefined) {
61
+ ast.skip = query.skip;
62
+ }
63
+
64
+ // Map groupBy
65
+ if (query.groupBy) {
66
+ ast.groupBy = query.groupBy;
67
+ }
68
+
69
+ // Map aggregations
70
+ if (query.aggregate) {
71
+ ast.aggregations = query.aggregate.map(agg => ({
72
+ function: agg.func as any,
73
+ field: agg.field,
74
+ alias: agg.alias || `${agg.func}_${agg.field}`
75
+ }));
76
+ }
77
+
78
+ return ast;
79
+ }
80
+ }
@@ -0,0 +1,392 @@
1
+ /**
2
+ * ObjectQL Query Service
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
+ Driver,
11
+ ObjectConfig,
12
+ UnifiedQuery,
13
+ Filter,
14
+ MetadataRegistry
15
+ } from '@objectql/types';
16
+ import type { QueryAST } from '@objectstack/spec';
17
+ import { QueryBuilder } from './query-builder';
18
+
19
+ /**
20
+ * Options for query execution
21
+ */
22
+ export interface QueryOptions {
23
+ /**
24
+ * Transaction handle for transactional queries
25
+ */
26
+ transaction?: any;
27
+
28
+ /**
29
+ * Skip validation (for system operations)
30
+ */
31
+ skipValidation?: boolean;
32
+
33
+ /**
34
+ * Include profiling information
35
+ */
36
+ profile?: boolean;
37
+
38
+ /**
39
+ * Custom driver options
40
+ */
41
+ driverOptions?: Record<string, unknown>;
42
+ }
43
+
44
+ /**
45
+ * Result of a query execution with optional profiling data
46
+ */
47
+ export interface QueryResult<T = any> {
48
+ /**
49
+ * The query results
50
+ */
51
+ value: T;
52
+
53
+ /**
54
+ * Total count (for paginated queries)
55
+ */
56
+ count?: number;
57
+
58
+ /**
59
+ * Profiling information (if profile option was enabled)
60
+ */
61
+ profile?: QueryProfile;
62
+ }
63
+
64
+ /**
65
+ * Profiling information for a query
66
+ */
67
+ export interface QueryProfile {
68
+ /**
69
+ * Execution time in milliseconds
70
+ */
71
+ executionTime: number;
72
+
73
+ /**
74
+ * Number of rows scanned
75
+ */
76
+ rowsScanned?: number;
77
+
78
+ /**
79
+ * Whether an index was used
80
+ */
81
+ indexUsed?: boolean;
82
+
83
+ /**
84
+ * The generated QueryAST
85
+ */
86
+ ast?: QueryAST;
87
+ }
88
+
89
+ /**
90
+ * Query Service
91
+ *
92
+ * Handles all query execution logic, separating query concerns from
93
+ * the repository pattern. This service is responsible for:
94
+ * - Building QueryAST from UnifiedQuery
95
+ * - Executing queries via drivers
96
+ * - Optional query profiling and analysis
97
+ *
98
+ * The QueryService is registered as a service in the ObjectQLPlugin
99
+ * and can be used by Repository for all read operations.
100
+ */
101
+ export class QueryService {
102
+ private queryBuilder: QueryBuilder;
103
+
104
+ constructor(
105
+ private datasources: Record<string, Driver>,
106
+ private metadata: MetadataRegistry
107
+ ) {
108
+ this.queryBuilder = new QueryBuilder();
109
+ }
110
+
111
+ /**
112
+ * Get the driver for a specific object
113
+ * @private
114
+ */
115
+ private getDriver(objectName: string): Driver {
116
+ const obj = this.getSchema(objectName);
117
+ const datasourceName = obj.datasource || 'default';
118
+ const driver = this.datasources[datasourceName];
119
+
120
+ if (!driver) {
121
+ throw new Error(`Datasource '${datasourceName}' not found for object '${objectName}'`);
122
+ }
123
+
124
+ return driver;
125
+ }
126
+
127
+ /**
128
+ * Get the schema for an object
129
+ * @private
130
+ */
131
+ private getSchema(objectName: string): ObjectConfig {
132
+ const obj = this.metadata.get<ObjectConfig>('object', objectName);
133
+ if (!obj) {
134
+ throw new Error(`Object '${objectName}' not found in metadata`);
135
+ }
136
+ return obj;
137
+ }
138
+
139
+ /**
140
+ * Build QueryAST from UnifiedQuery
141
+ * @private
142
+ */
143
+ private buildQueryAST(objectName: string, query: UnifiedQuery): QueryAST {
144
+ return this.queryBuilder.build(objectName, query);
145
+ }
146
+
147
+ /**
148
+ * Execute a find query
149
+ *
150
+ * @param objectName - The object to query
151
+ * @param query - The unified query
152
+ * @param options - Query execution options
153
+ * @returns Array of matching records
154
+ */
155
+ async find(
156
+ objectName: string,
157
+ query: UnifiedQuery = {},
158
+ options: QueryOptions = {}
159
+ ): Promise<QueryResult<any[]>> {
160
+ const driver = this.getDriver(objectName);
161
+ const startTime = options.profile ? Date.now() : 0;
162
+
163
+ // Build QueryAST
164
+ const ast = this.buildQueryAST(objectName, query);
165
+
166
+ // Execute query via driver
167
+ const driverOptions = {
168
+ transaction: options.transaction,
169
+ ...options.driverOptions
170
+ };
171
+
172
+ let results: any[];
173
+ let count: number | undefined;
174
+
175
+ if (driver.find) {
176
+ // Legacy driver interface
177
+ const result: any = await driver.find(objectName, query, driverOptions);
178
+ results = Array.isArray(result) ? result : (result?.value || []);
179
+ count = (typeof result === 'object' && !Array.isArray(result) && result?.count !== undefined) ? result.count : undefined;
180
+ } else if (driver.executeQuery) {
181
+ // New DriverInterface
182
+ const result = await driver.executeQuery(ast, driverOptions);
183
+ results = result.value || [];
184
+ count = result.count;
185
+ } else {
186
+ throw new Error(`Driver does not support query execution`);
187
+ }
188
+
189
+ const executionTime = options.profile ? Date.now() - startTime : 0;
190
+
191
+ return {
192
+ value: results,
193
+ count,
194
+ profile: options.profile ? {
195
+ executionTime,
196
+ ast,
197
+ rowsScanned: results.length,
198
+ } : undefined
199
+ };
200
+ }
201
+
202
+ /**
203
+ * Execute a findOne query by ID
204
+ *
205
+ * @param objectName - The object to query
206
+ * @param id - The record ID
207
+ * @param options - Query execution options
208
+ * @returns The matching record or undefined
209
+ */
210
+ async findOne(
211
+ objectName: string,
212
+ id: string | number,
213
+ options: QueryOptions = {}
214
+ ): Promise<QueryResult<any>> {
215
+ const driver = this.getDriver(objectName);
216
+ const startTime = options.profile ? Date.now() : 0;
217
+
218
+ const driverOptions = {
219
+ transaction: options.transaction,
220
+ ...options.driverOptions
221
+ };
222
+
223
+ let result: any;
224
+
225
+ if (driver.findOne) {
226
+ // Legacy driver interface
227
+ result = await driver.findOne(objectName, id, driverOptions);
228
+ } else if (driver.get) {
229
+ // Alternative method name
230
+ result = await driver.get(objectName, String(id), driverOptions);
231
+ } else if (driver.executeQuery) {
232
+ // Fallback to query with ID filter
233
+ const query: UnifiedQuery = {
234
+ filters: [['_id', '=', id]]
235
+ };
236
+ const ast = this.buildQueryAST(objectName, query);
237
+ const queryResult = await driver.executeQuery(ast, driverOptions);
238
+ result = queryResult.value?.[0];
239
+ } else {
240
+ throw new Error(`Driver does not support findOne operation`);
241
+ }
242
+
243
+ const executionTime = options.profile ? Date.now() - startTime : 0;
244
+
245
+ return {
246
+ value: result,
247
+ profile: options.profile ? {
248
+ executionTime,
249
+ rowsScanned: result ? 1 : 0,
250
+ } : undefined
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Execute a count query
256
+ *
257
+ * @param objectName - The object to query
258
+ * @param filters - Optional filters
259
+ * @param options - Query execution options
260
+ * @returns Count of matching records
261
+ */
262
+ async count(
263
+ objectName: string,
264
+ filters?: Filter[],
265
+ options: QueryOptions = {}
266
+ ): Promise<QueryResult<number>> {
267
+ const driver = this.getDriver(objectName);
268
+ const startTime = options.profile ? Date.now() : 0;
269
+
270
+ const query: UnifiedQuery = filters ? { filters } : {};
271
+ const ast = this.buildQueryAST(objectName, query);
272
+
273
+ const driverOptions = {
274
+ transaction: options.transaction,
275
+ ...options.driverOptions
276
+ };
277
+
278
+ let count: number;
279
+
280
+ if (driver.count) {
281
+ // Legacy driver interface
282
+ count = await driver.count(objectName, filters || [], driverOptions);
283
+ } else if (driver.executeQuery) {
284
+ // Use executeQuery and count results
285
+ // Note: This is inefficient for large datasets
286
+ // Ideally, driver should support count-specific optimization
287
+ const result = await driver.executeQuery(ast, driverOptions);
288
+ count = result.count ?? result.value?.length ?? 0;
289
+ } else {
290
+ throw new Error(`Driver does not support count operation`);
291
+ }
292
+
293
+ const executionTime = options.profile ? Date.now() - startTime : 0;
294
+
295
+ return {
296
+ value: count,
297
+ profile: options.profile ? {
298
+ executionTime,
299
+ ast,
300
+ } : undefined
301
+ };
302
+ }
303
+
304
+ /**
305
+ * Execute an aggregate query
306
+ *
307
+ * @param objectName - The object to query
308
+ * @param query - The aggregation query using UnifiedQuery format
309
+ * @param options - Query execution options
310
+ * @returns Aggregation results
311
+ */
312
+ async aggregate(
313
+ objectName: string,
314
+ query: UnifiedQuery,
315
+ options: QueryOptions = {}
316
+ ): Promise<QueryResult<any[]>> {
317
+ const driver = this.getDriver(objectName);
318
+ const startTime = options.profile ? Date.now() : 0;
319
+
320
+ const driverOptions = {
321
+ transaction: options.transaction,
322
+ ...options.driverOptions
323
+ };
324
+
325
+ let results: any[];
326
+
327
+ if (driver.aggregate) {
328
+ // Driver supports aggregation
329
+ results = await driver.aggregate(objectName, query, driverOptions);
330
+ } else {
331
+ // Driver doesn't support aggregation
332
+ throw new Error(`Driver does not support aggregate operations. Consider using a driver that supports aggregation.`);
333
+ }
334
+
335
+ const executionTime = options.profile ? Date.now() - startTime : 0;
336
+
337
+ return {
338
+ value: results,
339
+ profile: options.profile ? {
340
+ executionTime,
341
+ rowsScanned: results.length,
342
+ } : undefined
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Execute a direct SQL/query passthrough
348
+ *
349
+ * This bypasses ObjectQL's query builder and executes raw queries.
350
+ * Use with caution as it bypasses security and validation.
351
+ *
352
+ * @param objectName - The object (determines which datasource to use)
353
+ * @param queryString - Raw query string (SQL, MongoDB query, etc.)
354
+ * @param params - Query parameters (for parameterized queries)
355
+ * @param options - Query execution options
356
+ * @returns Query results
357
+ */
358
+ async directQuery(
359
+ objectName: string,
360
+ queryString: string,
361
+ params?: any[],
362
+ options: QueryOptions = {}
363
+ ): Promise<QueryResult<any>> {
364
+ const driver = this.getDriver(objectName);
365
+ const startTime = options.profile ? Date.now() : 0;
366
+
367
+ const driverOptions = {
368
+ transaction: options.transaction,
369
+ ...options.driverOptions
370
+ };
371
+
372
+ let results: any;
373
+
374
+ if (driver.directQuery) {
375
+ results = await driver.directQuery(queryString, params);
376
+ } else if (driver.query) {
377
+ // Alternative method name
378
+ results = await driver.query(queryString, params);
379
+ } else {
380
+ throw new Error(`Driver does not support direct query execution`);
381
+ }
382
+
383
+ const executionTime = options.profile ? Date.now() - startTime : 0;
384
+
385
+ return {
386
+ value: results,
387
+ profile: options.profile ? {
388
+ executionTime,
389
+ } : undefined
390
+ };
391
+ }
392
+ }
package/src/repository.ts CHANGED
@@ -1,18 +1,62 @@
1
- import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext, ValidationContext, ValidationError, ValidationRuleResult, FormulaContext } from '@objectql/types';
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
+ import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext, ValidationContext, ValidationError, ValidationRuleResult, FormulaContext, Filter } from '@objectql/types';
10
+ import type { ObjectStackKernel } from '@objectstack/runtime';
11
+ import type { QueryAST, FilterNode, SortNode } from '@objectstack/spec';
2
12
  import { Validator } from './validator';
3
13
  import { FormulaEngine } from './formula-engine';
14
+ import { QueryBuilder } from './query';
15
+
16
+ /**
17
+ * Extended ObjectStack Kernel with optional ObjectQL plugin capabilities.
18
+ * These properties are attached by ValidatorPlugin and FormulaPlugin during installation.
19
+ */
20
+ interface ExtendedKernel extends ObjectStackKernel {
21
+ validator?: Validator;
22
+ formulaEngine?: FormulaEngine;
23
+ }
4
24
 
5
25
  export class ObjectRepository {
6
- private validator: Validator;
7
- private formulaEngine: FormulaEngine;
26
+ private queryBuilder: QueryBuilder;
8
27
 
9
28
  constructor(
10
29
  private objectName: string,
11
30
  private context: ObjectQLContext,
12
31
  private app: IObjectQL
13
32
  ) {
14
- this.validator = new Validator();
15
- this.formulaEngine = new FormulaEngine();
33
+ this.queryBuilder = new QueryBuilder();
34
+ }
35
+
36
+ /**
37
+ * Get validator instance from kernel (via plugin)
38
+ * Falls back to creating a new instance if not available
39
+ */
40
+ private getValidator(): Validator {
41
+ const kernel = this.getKernel() as ExtendedKernel;
42
+ if (kernel.validator) {
43
+ return kernel.validator;
44
+ }
45
+ // Fallback for backward compatibility
46
+ return new Validator();
47
+ }
48
+
49
+ /**
50
+ * Get formula engine instance from kernel (via plugin)
51
+ * Falls back to creating a new instance if not available
52
+ */
53
+ private getFormulaEngine(): FormulaEngine {
54
+ const kernel = this.getKernel() as ExtendedKernel;
55
+ if (kernel.formulaEngine) {
56
+ return kernel.formulaEngine;
57
+ }
58
+ // Fallback for backward compatibility
59
+ return new FormulaEngine();
16
60
  }
17
61
 
18
62
  private getDriver(): Driver {
@@ -21,13 +65,24 @@ export class ObjectRepository {
21
65
  return this.app.datasource(datasourceName);
22
66
  }
23
67
 
24
- private getOptions(extra: any = {}) {
68
+ private getKernel(): ObjectStackKernel {
69
+ return this.app.getKernel();
70
+ }
71
+
72
+ private getOptions(extra: Record<string, unknown> = {}) {
25
73
  return {
26
74
  transaction: this.context.transactionHandle,
27
75
  ...extra
28
76
  };
29
77
  }
30
78
 
79
+ /**
80
+ * Translates ObjectQL UnifiedQuery to ObjectStack QueryAST format
81
+ */
82
+ private buildQueryAST(query: UnifiedQuery): QueryAST {
83
+ return this.queryBuilder.build(this.objectName, query);
84
+ }
85
+
31
86
  getSchema(): ObjectConfig {
32
87
  const obj = this.app.getObject(this.objectName);
33
88
  if (!obj) {
@@ -82,7 +137,7 @@ export class ObjectRepository {
82
137
  }
83
138
 
84
139
  const value = record[fieldName];
85
- const fieldResults = await this.validator.validateField(
140
+ const fieldResults = await this.getValidator().validateField(
86
141
  fieldName,
87
142
  fieldConfig,
88
143
  value,
@@ -121,7 +176,7 @@ export class ObjectRepository {
121
176
  changedFields,
122
177
  };
123
178
 
124
- const result = await this.validator.validate(schema.validation.rules, validationContext);
179
+ const result = await this.getValidator().validate(schema.validation.rules, validationContext);
125
180
  allResults.push(...result.results);
126
181
  }
127
182
 
@@ -168,7 +223,7 @@ export class ObjectRepository {
168
223
  // Evaluate each formula field
169
224
  for (const [fieldName, fieldConfig] of Object.entries(schema.fields)) {
170
225
  if (fieldConfig.type === 'formula' && fieldConfig.formula) {
171
- const result = this.formulaEngine.evaluate(
226
+ const result = this.getFormulaEngine().evaluate(
172
227
  fieldConfig.formula,
173
228
  formulaContext,
174
229
  fieldConfig.data_type || 'text',
@@ -213,11 +268,13 @@ export class ObjectRepository {
213
268
  };
214
269
  await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
215
270
 
216
- // TODO: Apply basic filters like spaceId
217
- const results = await this.getDriver().find(this.objectName, hookCtx.query || {}, this.getOptions());
271
+ // Build QueryAST and execute via kernel
272
+ const ast = this.buildQueryAST(hookCtx.query || {});
273
+ const kernelResult = await this.getKernel().find(this.objectName, ast);
274
+ const results = kernelResult.value;
218
275
 
219
276
  // Evaluate formulas for each result
220
- const resultsWithFormulas = results.map(record => this.evaluateFormulas(record));
277
+ const resultsWithFormulas = results.map((record: any) => this.evaluateFormulas(record));
221
278
 
222
279
  hookCtx.result = resultsWithFormulas;
223
280
  await this.app.triggerHook('afterFind', this.objectName, hookCtx);
@@ -238,7 +295,8 @@ export class ObjectRepository {
238
295
  };
239
296
  await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
240
297
 
241
- const result = await this.getDriver().findOne(this.objectName, idOrQuery, hookCtx.query, this.getOptions());
298
+ // Use kernel.get() for direct ID lookup
299
+ const result = await this.getKernel().get(this.objectName, String(idOrQuery));
242
300
 
243
301
  // Evaluate formulas if result exists
244
302
  const resultWithFormulas = result ? this.evaluateFormulas(result) : result;
@@ -264,7 +322,10 @@ export class ObjectRepository {
264
322
  };
265
323
  await this.app.triggerHook('beforeCount', this.objectName, hookCtx);
266
324
 
267
- const result = await this.getDriver().count(this.objectName, hookCtx.query, this.getOptions());
325
+ // Build QueryAST and execute via kernel to get count
326
+ const ast = this.buildQueryAST(hookCtx.query || {});
327
+ const kernelResult = await this.getKernel().find(this.objectName, ast);
328
+ const result = kernelResult.count;
268
329
 
269
330
  hookCtx.result = result;
270
331
  await this.app.triggerHook('afterCount', this.objectName, hookCtx);
@@ -290,7 +351,8 @@ export class ObjectRepository {
290
351
  // Validate the record before creating
291
352
  await this.validateRecord('create', finalDoc);
292
353
 
293
- const result = await this.getDriver().create(this.objectName, finalDoc, this.getOptions());
354
+ // Execute via kernel
355
+ const result = await this.getKernel().create(this.objectName, finalDoc);
294
356
 
295
357
  hookCtx.result = result;
296
358
  await this.app.triggerHook('afterCreate', this.objectName, hookCtx);
@@ -316,7 +378,8 @@ export class ObjectRepository {
316
378
  // Validate the update
317
379
  await this.validateRecord('update', hookCtx.data, previousData);
318
380
 
319
- const result = await this.getDriver().update(this.objectName, id, hookCtx.data, this.getOptions(options));
381
+ // Execute via kernel
382
+ const result = await this.getKernel().update(this.objectName, String(id), hookCtx.data);
320
383
 
321
384
  hookCtx.result = result;
322
385
  await this.app.triggerHook('afterUpdate', this.objectName, hookCtx);
@@ -337,7 +400,8 @@ export class ObjectRepository {
337
400
  };
338
401
  await this.app.triggerHook('beforeDelete', this.objectName, hookCtx);
339
402
 
340
- const result = await this.getDriver().delete(this.objectName, id, this.getOptions());
403
+ // Execute via kernel
404
+ const result = await this.getKernel().delete(this.objectName, String(id));
341
405
 
342
406
  hookCtx.result = result;
343
407
  await this.app.triggerHook('afterDelete', this.objectName, hookCtx);