@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
@@ -0,0 +1,440 @@
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 { describe, it, expect, beforeEach } from '@jest/globals';
10
+ import {
11
+ OptimizedMetadataRegistry,
12
+ QueryCompiler,
13
+ CompiledHookManager,
14
+ GlobalConnectionPool,
15
+ OptimizedValidationEngine,
16
+ LazyMetadataLoader,
17
+ DependencyGraph,
18
+ SQLQueryOptimizer
19
+ } from '../src/optimizations';
20
+
21
+ describe('Kernel Optimizations', () => {
22
+ describe('OptimizedMetadataRegistry', () => {
23
+ let registry: OptimizedMetadataRegistry;
24
+
25
+ beforeEach(() => {
26
+ registry = new OptimizedMetadataRegistry();
27
+ });
28
+
29
+ it('should register and retrieve metadata', () => {
30
+ const item = { name: 'user', package: 'crm', fields: {} };
31
+ registry.register('object', item);
32
+
33
+ const retrieved = registry.get('object', 'user');
34
+ expect(retrieved).toEqual(item);
35
+ });
36
+
37
+ it('should unregister package with O(k) complexity', () => {
38
+ // Register multiple items in the same package
39
+ registry.register('object', { name: 'user', package: 'crm' });
40
+ registry.register('object', { name: 'account', package: 'crm' });
41
+ registry.register('object', { name: 'task', package: 'todo' });
42
+
43
+ // Unregister the 'crm' package
44
+ registry.unregisterPackage('crm');
45
+
46
+ // Verify items are removed
47
+ expect(registry.get('object', 'user')).toBeUndefined();
48
+ expect(registry.get('object', 'account')).toBeUndefined();
49
+
50
+ // Verify other package items remain
51
+ expect(registry.get('object', 'task')).toBeDefined();
52
+ });
53
+
54
+ it('should list items by type', () => {
55
+ registry.register('object', { name: 'user', package: 'crm' });
56
+ registry.register('object', { name: 'account', package: 'crm' });
57
+
58
+ const objects = registry.list('object');
59
+ expect(objects.length).toBe(2);
60
+ });
61
+ });
62
+
63
+ describe('QueryCompiler', () => {
64
+ let compiler: QueryCompiler;
65
+
66
+ beforeEach(() => {
67
+ compiler = new QueryCompiler(10);
68
+ });
69
+
70
+ it('should compile and cache queries', () => {
71
+ const ast = {
72
+ filters: { status: 'active' },
73
+ sort: [{ field: 'created', order: 'desc' as const }]
74
+ };
75
+
76
+ const compiled1 = compiler.compile('user', ast);
77
+ const compiled2 = compiler.compile('user', ast);
78
+
79
+ // Should return same cached instance
80
+ expect(compiled1).toBe(compiled2);
81
+ });
82
+
83
+ it('should detect indexable fields', () => {
84
+ const ast = {
85
+ filters: { status: 'active', email: 'test@example.com' }
86
+ };
87
+
88
+ const compiled = compiler.compile('user', ast);
89
+ expect(compiled.plan.useIndex).toContain('status');
90
+ expect(compiled.plan.useIndex).toContain('email');
91
+ });
92
+
93
+ it('should cache different queries separately', () => {
94
+ const ast1 = { filters: { status: 'active' } };
95
+ const ast2 = { filters: { status: 'inactive' } };
96
+
97
+ const compiled1 = compiler.compile('user', ast1);
98
+ const compiled2 = compiler.compile('user', ast2);
99
+
100
+ // Should be different instances
101
+ expect(compiled1).not.toBe(compiled2);
102
+ });
103
+
104
+ it('should clear cache', () => {
105
+ const ast = { filters: { status: 'active' } };
106
+ compiler.compile('user', ast);
107
+
108
+ compiler.clearCache();
109
+
110
+ const compiled = compiler.compile('user', ast);
111
+ expect(compiled).toBeDefined();
112
+ });
113
+ });
114
+
115
+ describe('CompiledHookManager', () => {
116
+ let hookManager: CompiledHookManager;
117
+
118
+ beforeEach(() => {
119
+ hookManager = new CompiledHookManager();
120
+ });
121
+
122
+ it('should register and run hooks', async () => {
123
+ let executed = false;
124
+ const handler = async () => { executed = true; };
125
+
126
+ hookManager.registerHook('beforeCreate', 'user', handler);
127
+ await hookManager.runHooks('beforeCreate', 'user', {});
128
+
129
+ expect(executed).toBe(true);
130
+ });
131
+
132
+ it('should expand wildcard patterns', async () => {
133
+ let count = 0;
134
+ const handler = async () => { count++; };
135
+
136
+ hookManager.registerHook('before*', 'user', handler);
137
+
138
+ await hookManager.runHooks('beforeCreate', 'user', {});
139
+ await hookManager.runHooks('beforeUpdate', 'user', {});
140
+
141
+ expect(count).toBe(2);
142
+ });
143
+
144
+ it('should handle wildcard object names', async () => {
145
+ let count = 0;
146
+ const handler = async () => { count++; };
147
+
148
+ hookManager.registerHook('beforeCreate', '*', handler);
149
+
150
+ await hookManager.runHooks('beforeCreate', 'user', {});
151
+ await hookManager.runHooks('beforeCreate', 'account', {});
152
+
153
+ expect(count).toBe(2);
154
+ });
155
+
156
+ it('should remove package hooks', async () => {
157
+ let executed = false;
158
+ const handler = async () => { executed = true; };
159
+
160
+ hookManager.registerHook('beforeCreate', 'user', handler, 'crm');
161
+ hookManager.removePackage('crm');
162
+
163
+ await hookManager.runHooks('beforeCreate', 'user', {});
164
+
165
+ expect(executed).toBe(false);
166
+ });
167
+ });
168
+
169
+ describe('GlobalConnectionPool', () => {
170
+ let pool: GlobalConnectionPool;
171
+
172
+ beforeEach(() => {
173
+ pool = new GlobalConnectionPool({ total: 5, perDriver: 3 });
174
+ });
175
+
176
+ it('should acquire and release connections', async () => {
177
+ const conn = await pool.acquire('postgres');
178
+ expect(conn).toBeDefined();
179
+ expect(conn.driverName).toBe('postgres');
180
+
181
+ await pool.release(conn);
182
+ const stats = pool.getStats();
183
+ expect(stats.driverStats.postgres.idle).toBe(1);
184
+ });
185
+
186
+ it('should enforce per-driver limits', async () => {
187
+ const conns = [];
188
+ for (let i = 0; i < 3; i++) {
189
+ conns.push(await pool.acquire('postgres'));
190
+ }
191
+
192
+ // Should queue the next request
193
+ const promise = pool.acquire('postgres');
194
+ const stats = pool.getStats();
195
+ expect(stats.waitQueueSize).toBe(1);
196
+
197
+ // Release one connection
198
+ await pool.release(conns[0]);
199
+
200
+ // Now the queued request should complete
201
+ const conn = await promise;
202
+ expect(conn).toBeDefined();
203
+ });
204
+
205
+ it('should track connection statistics', async () => {
206
+ await pool.acquire('postgres');
207
+ await pool.acquire('postgres');
208
+
209
+ const stats = pool.getStats();
210
+ expect(stats.totalConnections).toBe(2);
211
+ expect(stats.driverStats.postgres.active).toBe(2);
212
+ });
213
+ });
214
+
215
+ describe('OptimizedValidationEngine', () => {
216
+ let engine: OptimizedValidationEngine;
217
+
218
+ beforeEach(() => {
219
+ engine = new OptimizedValidationEngine();
220
+ });
221
+
222
+ it('should compile and validate schemas', () => {
223
+ const schema = {
224
+ type: 'object',
225
+ properties: {
226
+ name: { type: 'string', required: true, minLength: 3 },
227
+ age: { type: 'number', minimum: 0, maximum: 120 }
228
+ }
229
+ };
230
+
231
+ engine.compile('user', schema);
232
+
233
+ const result = engine.validate('user', {
234
+ name: 'John',
235
+ age: 30
236
+ });
237
+
238
+ expect(result.valid).toBe(true);
239
+ });
240
+
241
+ it('should detect validation errors', () => {
242
+ const schema = {
243
+ type: 'object',
244
+ properties: {
245
+ email: { type: 'string', pattern: '^[^@]+@[^@]+\\.[^@]+$' }
246
+ }
247
+ };
248
+
249
+ engine.compile('user', schema);
250
+
251
+ const result = engine.validate('user', {
252
+ email: 'invalid-email'
253
+ });
254
+
255
+ expect(result.valid).toBe(false);
256
+ expect(result.errors).toBeDefined();
257
+ });
258
+
259
+ it('should validate enum values', () => {
260
+ const schema = {
261
+ type: 'string',
262
+ enum: ['active', 'inactive', 'pending']
263
+ };
264
+
265
+ engine.compile('status', schema);
266
+
267
+ const validResult = engine.validate('status', 'active');
268
+ expect(validResult.valid).toBe(true);
269
+
270
+ const invalidResult = engine.validate('status', 'unknown');
271
+ expect(invalidResult.valid).toBe(false);
272
+ });
273
+ });
274
+
275
+ describe('LazyMetadataLoader', () => {
276
+ let loader: LazyMetadataLoader;
277
+ let loadCount: number;
278
+
279
+ beforeEach(() => {
280
+ loadCount = 0;
281
+ loader = new LazyMetadataLoader(async (objectName) => {
282
+ loadCount++;
283
+ return {
284
+ name: objectName,
285
+ fields: { id: { type: 'string' } },
286
+ relatedObjects: objectName === 'user' ? ['account'] : []
287
+ };
288
+ });
289
+ });
290
+
291
+ it('should load metadata on-demand', async () => {
292
+ const metadata = await loader.get('user');
293
+ expect(metadata.name).toBe('user');
294
+ expect(loadCount).toBe(1);
295
+ });
296
+
297
+ it('should cache loaded metadata', async () => {
298
+ await loader.get('user');
299
+ await loader.get('user');
300
+
301
+ // Should only load once
302
+ expect(loadCount).toBe(1);
303
+ });
304
+
305
+ it('should predictively preload related objects', async () => {
306
+ await loader.get('user');
307
+
308
+ // Give time for async preload
309
+ await new Promise(resolve => setTimeout(resolve, 100));
310
+
311
+ // Account should have been preloaded
312
+ expect(loader.isLoaded('account')).toBe(true);
313
+ });
314
+
315
+ it('should invalidate cache', async () => {
316
+ await loader.get('user');
317
+ loader.invalidate('user');
318
+
319
+ await loader.get('user');
320
+ expect(loadCount).toBe(2);
321
+ });
322
+ });
323
+
324
+ describe('DependencyGraph', () => {
325
+ let graph: DependencyGraph;
326
+
327
+ beforeEach(() => {
328
+ graph = new DependencyGraph();
329
+ });
330
+
331
+ it('should build dependency graph', () => {
332
+ graph.addDependency('account', 'contact', 'lookup', 'account_id');
333
+ graph.addDependency('account', 'opportunity', 'lookup', 'account_id');
334
+
335
+ const dependents = graph.getDependents('account');
336
+ expect(dependents).toContain('contact');
337
+ expect(dependents).toContain('opportunity');
338
+ });
339
+
340
+ it('should compute topological sort', () => {
341
+ graph.addDependency('account', 'contact', 'lookup', 'account_id');
342
+ graph.addDependency('contact', 'task', 'lookup', 'contact_id');
343
+
344
+ const sorted = graph.topologicalSort(['account', 'contact', 'task']);
345
+
346
+ // Task should come before contact, contact before account
347
+ expect(sorted.indexOf('task')).toBeLessThan(sorted.indexOf('contact'));
348
+ expect(sorted.indexOf('contact')).toBeLessThan(sorted.indexOf('account'));
349
+ });
350
+
351
+ it('should detect circular dependencies', () => {
352
+ graph.addDependency('a', 'b', 'lookup', 'a_id');
353
+ graph.addDependency('b', 'c', 'lookup', 'b_id');
354
+ graph.addDependency('c', 'a', 'lookup', 'c_id');
355
+
356
+ expect(graph.hasCircularDependency()).toBe(true);
357
+ });
358
+
359
+ it('should get cascade delete order', () => {
360
+ graph.addDependency('account', 'contact', 'master_detail', 'account_id');
361
+ graph.addDependency('contact', 'task', 'master_detail', 'contact_id');
362
+
363
+ const deleteOrder = graph.getCascadeDeleteOrder('account');
364
+
365
+ // Should delete in order: task -> contact -> account
366
+ expect(deleteOrder).toContain('task');
367
+ expect(deleteOrder).toContain('contact');
368
+ expect(deleteOrder).toContain('account');
369
+ });
370
+ });
371
+
372
+ describe('SQLQueryOptimizer', () => {
373
+ let optimizer: SQLQueryOptimizer;
374
+
375
+ beforeEach(() => {
376
+ optimizer = new SQLQueryOptimizer();
377
+
378
+ // Register schema with indexes
379
+ optimizer.registerSchema({
380
+ name: 'users',
381
+ fields: {
382
+ id: { type: 'string' },
383
+ email: { type: 'string' },
384
+ status: { type: 'string' }
385
+ },
386
+ indexes: [
387
+ { name: 'idx_email', fields: ['email'], unique: true },
388
+ { name: 'idx_status', fields: ['status'], unique: false }
389
+ ]
390
+ });
391
+ });
392
+
393
+ it('should generate basic SQL', () => {
394
+ const sql = optimizer.optimize({
395
+ object: 'users',
396
+ fields: ['id', 'email'],
397
+ filters: { status: 'active' }
398
+ });
399
+
400
+ expect(sql).toContain('SELECT id, email');
401
+ expect(sql).toContain('FROM users');
402
+ expect(sql).toContain('WHERE');
403
+ });
404
+
405
+ it('should add index hints', () => {
406
+ const sql = optimizer.optimize({
407
+ object: 'users',
408
+ filters: { status: 'active' }
409
+ });
410
+
411
+ expect(sql).toContain('USE INDEX (idx_status)');
412
+ });
413
+
414
+ it('should optimize join types', () => {
415
+ const sql = optimizer.optimize({
416
+ object: 'users',
417
+ joins: [
418
+ { type: 'left', table: 'accounts', on: 'users.account_id = accounts.id' }
419
+ ],
420
+ filters: { 'accounts.type': 'premium' }
421
+ });
422
+
423
+ // Should convert LEFT to INNER when filtering on joined table
424
+ expect(sql).toContain('INNER JOIN');
425
+ });
426
+
427
+ it('should handle sorting and limits', () => {
428
+ const sql = optimizer.optimize({
429
+ object: 'users',
430
+ sort: [{ field: 'created_at', order: 'desc' }],
431
+ limit: 10,
432
+ offset: 20
433
+ });
434
+
435
+ expect(sql).toContain('ORDER BY created_at DESC');
436
+ expect(sql).toContain('LIMIT 10');
437
+ expect(sql).toContain('OFFSET 20');
438
+ });
439
+ });
440
+ });
@@ -7,12 +7,12 @@
7
7
  */
8
8
 
9
9
  import { ObjectQLPlugin } from '../src/plugin';
10
- import { ValidatorPlugin } from '../src/validator-plugin';
11
- import { FormulaPlugin } from '../src/formula-plugin';
10
+ import { ValidatorPlugin } from '@objectql/plugin-validator';
11
+ import { FormulaPlugin } from '@objectql/plugin-formula';
12
12
 
13
13
  // Mock the sub-plugins
14
- jest.mock('../src/validator-plugin');
15
- jest.mock('../src/formula-plugin');
14
+ jest.mock('@objectql/plugin-validator');
15
+ jest.mock('@objectql/plugin-formula');
16
16
 
17
17
  describe('ObjectQLPlugin Integration', () => {
18
18
  let plugin: ObjectQLPlugin;
@@ -29,7 +29,7 @@ describe('ObjectQLPlugin Integration', () => {
29
29
  registerFormulaProvider: jest.fn(),
30
30
  };
31
31
 
32
- mockContext = { engine: mockKernel };
32
+ mockContext = { app: mockKernel };
33
33
 
34
34
  // Setup mock implementations
35
35
  (ValidatorPlugin as jest.Mock).mockImplementation(() => ({
@@ -45,7 +45,7 @@ describe('ObjectQLPlugin Integration', () => {
45
45
  it('should have correct name and version', () => {
46
46
  plugin = new ObjectQLPlugin();
47
47
  expect(plugin.name).toBe('@objectql/core');
48
- expect(plugin.version).toBe('4.0.0');
48
+ expect(plugin.version).toBe('4.0.2');
49
49
  });
50
50
  });
51
51
 
@@ -89,32 +89,36 @@ describe('ObjectQLPlugin Integration', () => {
89
89
  describe('Installation - Conditional Plugin Loading', () => {
90
90
  it('should install validator plugin when enabled', async () => {
91
91
  plugin = new ObjectQLPlugin({ enableValidator: true });
92
- await plugin.install(mockContext);
92
+ const runtimeContext = { engine: mockContext.app };
93
+ await plugin.install(runtimeContext);
93
94
 
94
95
  expect(ValidatorPlugin).toHaveBeenCalled();
95
96
  const validatorInstance = (ValidatorPlugin as jest.Mock).mock.results[0].value;
96
- expect(validatorInstance.install).toHaveBeenCalledWith(mockContext);
97
+ expect(validatorInstance.install).toHaveBeenCalledWith(runtimeContext);
97
98
  });
98
99
 
99
100
  it('should not install validator plugin when disabled', async () => {
100
101
  plugin = new ObjectQLPlugin({ enableValidator: false });
101
- await plugin.install(mockContext);
102
+ const runtimeContext = { engine: mockContext.app };
103
+ await plugin.install(runtimeContext);
102
104
 
103
105
  expect(ValidatorPlugin).not.toHaveBeenCalled();
104
106
  });
105
107
 
106
108
  it('should install formula plugin when enabled', async () => {
107
109
  plugin = new ObjectQLPlugin({ enableFormulas: true });
108
- await plugin.install(mockContext);
110
+ const runtimeContext = { engine: mockContext.app };
111
+ await plugin.install(runtimeContext);
109
112
 
110
113
  expect(FormulaPlugin).toHaveBeenCalled();
111
114
  const formulaInstance = (FormulaPlugin as jest.Mock).mock.results[0].value;
112
- expect(formulaInstance.install).toHaveBeenCalledWith(mockContext);
115
+ expect(formulaInstance.install).toHaveBeenCalledWith(runtimeContext);
113
116
  });
114
117
 
115
118
  it('should not install formula plugin when disabled', async () => {
116
119
  plugin = new ObjectQLPlugin({ enableFormulas: false });
117
- await plugin.install(mockContext);
120
+ const runtimeContext = { engine: mockContext.app };
121
+ await plugin.install(runtimeContext);
118
122
 
119
123
  expect(FormulaPlugin).not.toHaveBeenCalled();
120
124
  });
@@ -130,7 +134,8 @@ describe('ObjectQLPlugin Integration', () => {
130
134
  validatorConfig,
131
135
  });
132
136
 
133
- await plugin.install(mockContext);
137
+ const runtimeContext = { engine: mockContext.app };
138
+ await plugin.install(runtimeContext);
134
139
 
135
140
  expect(ValidatorPlugin).toHaveBeenCalledWith(validatorConfig);
136
141
  });
@@ -146,7 +151,8 @@ describe('ObjectQLPlugin Integration', () => {
146
151
  formulaConfig,
147
152
  });
148
153
 
149
- await plugin.install(mockContext);
154
+ const runtimeContext = { engine: mockContext.app };
155
+ await plugin.install(runtimeContext);
150
156
 
151
157
  expect(FormulaPlugin).toHaveBeenCalledWith(formulaConfig);
152
158
  });
@@ -157,7 +163,8 @@ describe('ObjectQLPlugin Integration', () => {
157
163
  enableFormulas: true,
158
164
  });
159
165
 
160
- await plugin.install(mockContext);
166
+ const runtimeContext = { engine: mockContext.app };
167
+ await plugin.install(runtimeContext);
161
168
 
162
169
  expect(ValidatorPlugin).toHaveBeenCalled();
163
170
  expect(FormulaPlugin).toHaveBeenCalled();
@@ -171,7 +178,8 @@ describe('ObjectQLPlugin Integration', () => {
171
178
  enableAI: false,
172
179
  });
173
180
 
174
- await plugin.install(mockContext);
181
+ const runtimeContext = { engine: mockContext.app };
182
+ await plugin.install(runtimeContext);
175
183
 
176
184
  expect(ValidatorPlugin).not.toHaveBeenCalled();
177
185
  expect(FormulaPlugin).not.toHaveBeenCalled();
@@ -183,15 +191,17 @@ describe('ObjectQLPlugin Integration', () => {
183
191
  plugin = new ObjectQLPlugin();
184
192
  expect(typeof plugin.onStart).toBe('function');
185
193
 
194
+ const runtimeContext = { engine: mockContext.app };
186
195
  // Should not throw when called
187
- await expect(plugin.onStart(mockContext)).resolves.not.toThrow();
196
+ await expect(plugin.onStart(runtimeContext)).resolves.not.toThrow();
188
197
  });
189
198
  });
190
199
 
191
200
  describe('Default Configuration', () => {
192
201
  it('should enable all features by default', async () => {
193
202
  plugin = new ObjectQLPlugin();
194
- await plugin.install(mockContext);
203
+ const runtimeContext = { engine: mockContext.app };
204
+ await plugin.install(runtimeContext);
195
205
 
196
206
  // Validator and Formula should be installed by default
197
207
  expect(ValidatorPlugin).toHaveBeenCalled();
@@ -203,7 +213,8 @@ describe('ObjectQLPlugin Integration', () => {
203
213
  // Explicitly not setting enableValidator or enableFormulas
204
214
  });
205
215
 
206
- await plugin.install(mockContext);
216
+ const runtimeContext = { engine: mockContext.app };
217
+ await plugin.install(runtimeContext);
207
218
 
208
219
  // Both should still be installed
209
220
  expect(ValidatorPlugin).toHaveBeenCalled();
package/tsconfig.json CHANGED
@@ -7,13 +7,11 @@
7
7
  "include": ["src/**/*"],
8
8
  "exclude": [
9
9
  "node_modules",
10
- "dist",
11
- // Exclude external @objectstack/objectql package that has type incompatibilities
12
- // with our stub packages during migration phase
13
- "../../../node_modules/@objectstack+objectql"
10
+ "dist"
14
11
  ],
15
12
  "references": [
16
- { "path": "../../objectstack/runtime" },
17
- { "path": "../types" }
13
+ { "path": "../types" },
14
+ { "path": "../plugin-validator" },
15
+ { "path": "../plugin-formula" }
18
16
  ]
19
17
  }