@objectql/core 4.0.2 → 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 (98) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +4 -4
  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/query-analyzer.js.map +1 -1
  41. package/dist/query/query-builder.d.ts +6 -1
  42. package/dist/query/query-builder.js +21 -5
  43. package/dist/query/query-builder.js.map +1 -1
  44. package/dist/query/query-service.js.map +1 -1
  45. package/dist/repository.d.ts +2 -0
  46. package/dist/repository.js +15 -9
  47. package/dist/repository.js.map +1 -1
  48. package/jest.config.js +3 -3
  49. package/package.json +8 -5
  50. package/src/app.ts +173 -47
  51. package/src/index.ts +8 -9
  52. package/src/optimizations/CompiledHookManager.ts +185 -0
  53. package/src/optimizations/DependencyGraph.ts +255 -0
  54. package/src/optimizations/GlobalConnectionPool.ts +251 -0
  55. package/src/optimizations/LazyMetadataLoader.ts +180 -0
  56. package/src/optimizations/OptimizedMetadataRegistry.ts +132 -0
  57. package/src/optimizations/OptimizedValidationEngine.ts +172 -0
  58. package/src/optimizations/QueryCompiler.ts +242 -0
  59. package/src/optimizations/SQLQueryOptimizer.ts +329 -0
  60. package/src/optimizations/index.ts +34 -0
  61. package/src/plugin.ts +51 -28
  62. package/src/query/query-analyzer.ts +1 -1
  63. package/src/query/query-builder.ts +21 -7
  64. package/src/query/query-service.ts +1 -1
  65. package/src/repository.ts +25 -13
  66. package/test/__mocks__/@objectstack/runtime.ts +8 -8
  67. package/test/app.test.ts +9 -7
  68. package/test/optimizations.test.ts +440 -0
  69. package/test/plugin-integration.test.ts +30 -19
  70. package/tsconfig.json +4 -6
  71. package/tsconfig.tsbuildinfo +1 -1
  72. package/dist/ai-agent.d.ts +0 -176
  73. package/dist/ai-agent.js +0 -722
  74. package/dist/ai-agent.js.map +0 -1
  75. package/dist/formula-engine.d.ts +0 -102
  76. package/dist/formula-engine.js +0 -433
  77. package/dist/formula-engine.js.map +0 -1
  78. package/dist/formula-plugin.d.ts +0 -52
  79. package/dist/formula-plugin.js +0 -107
  80. package/dist/formula-plugin.js.map +0 -1
  81. package/dist/validator-plugin.d.ts +0 -56
  82. package/dist/validator-plugin.js +0 -106
  83. package/dist/validator-plugin.js.map +0 -1
  84. package/dist/validator.d.ts +0 -80
  85. package/dist/validator.js +0 -625
  86. package/dist/validator.js.map +0 -1
  87. package/src/ai-agent.ts +0 -868
  88. package/src/formula-engine.ts +0 -572
  89. package/src/formula-plugin.ts +0 -141
  90. package/src/validator-plugin.ts +0 -140
  91. package/src/validator.ts +0 -743
  92. package/test/formula-engine.test.ts +0 -725
  93. package/test/formula-integration.test.ts +0 -286
  94. package/test/formula-plugin.test.ts +0 -197
  95. package/test/formula-spec-compliance.test.ts +0 -258
  96. package/test/validation-spec-compliance.test.ts +0 -440
  97. package/test/validator-plugin.test.ts +0 -126
  98. package/test/validator.test.ts +0 -440
@@ -1,286 +0,0 @@
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
- /**
10
- * Formula Integration Tests
11
- *
12
- * Tests formula evaluation within repository queries
13
- */
14
-
15
- import { ObjectQL } from '../src/app';
16
- import { MockDriver } from './mock-driver';
17
-
18
- describe('Formula Integration', () => {
19
- let app: ObjectQL;
20
- let mockDriver: MockDriver;
21
-
22
- beforeEach(async () => {
23
- mockDriver = new MockDriver();
24
-
25
- app = new ObjectQL({
26
- datasources: {
27
- default: mockDriver
28
- }
29
- });
30
-
31
- // Register an object with formula fields
32
- app.registerObject({
33
- name: 'contact',
34
- fields: {
35
- first_name: {
36
- type: 'text',
37
- required: true,
38
- },
39
- last_name: {
40
- type: 'text',
41
- required: true,
42
- },
43
- full_name: {
44
- type: 'formula',
45
- expression: 'first_name + " " + last_name',
46
- data_type: 'text',
47
- label: 'Full Name',
48
- },
49
- quantity: {
50
- type: 'number',
51
- },
52
- unit_price: {
53
- type: 'currency',
54
- },
55
- total: {
56
- type: 'formula',
57
- expression: 'quantity * unit_price',
58
- data_type: 'currency',
59
- label: 'Total',
60
- },
61
- is_active: {
62
- type: 'boolean',
63
- },
64
- status_label: {
65
- type: 'formula',
66
- expression: 'is_active ? "Active" : "Inactive"',
67
- data_type: 'text',
68
- label: 'Status',
69
- },
70
- },
71
- });
72
-
73
- await app.init();
74
- });
75
-
76
- describe('Formula Evaluation in Queries', () => {
77
- it('should evaluate formula fields in find results', async () => {
78
- // Setup mock data
79
- mockDriver.setMockData('contact', [
80
- {
81
- _id: '1',
82
- first_name: 'John',
83
- last_name: 'Doe',
84
- quantity: 10,
85
- unit_price: 25.5,
86
- is_active: true,
87
- },
88
- {
89
- _id: '2',
90
- first_name: 'Jane',
91
- last_name: 'Smith',
92
- quantity: 5,
93
- unit_price: 30,
94
- is_active: false,
95
- },
96
- ]);
97
-
98
- const ctx = app.createContext({ isSystem: true });
99
- const results = await ctx.object('contact').find({});
100
-
101
- expect(results).toHaveLength(2);
102
-
103
- // Check first record
104
- expect(results[0].full_name).toBe('John Doe');
105
- expect(results[0].total).toBe(255); // 10 * 25.5
106
- expect(results[0].status_label).toBe('Active');
107
-
108
- // Check second record
109
- expect(results[1].full_name).toBe('Jane Smith');
110
- expect(results[1].total).toBe(150); // 5 * 30
111
- expect(results[1].status_label).toBe('Inactive');
112
- });
113
-
114
- it('should evaluate formula fields in findOne result', async () => {
115
- mockDriver.setMockData('contact', [
116
- {
117
- _id: '1',
118
- first_name: 'John',
119
- last_name: 'Doe',
120
- quantity: 10,
121
- unit_price: 25.5,
122
- is_active: true,
123
- },
124
- ]);
125
-
126
- const ctx = app.createContext({ isSystem: true });
127
- const result = await ctx.object('contact').findOne('1');
128
-
129
- expect(result).toBeDefined();
130
- expect(result.full_name).toBe('John Doe');
131
- expect(result.total).toBe(255);
132
- expect(result.status_label).toBe('Active');
133
- });
134
-
135
- it('should handle null values in formulas', async () => {
136
- mockDriver.setMockData('contact', [
137
- {
138
- _id: '1',
139
- first_name: 'John',
140
- last_name: 'Doe',
141
- quantity: null,
142
- unit_price: 25.5,
143
- is_active: true,
144
- },
145
- ]);
146
-
147
- const ctx = app.createContext({ isSystem: true });
148
- const result = await ctx.object('contact').findOne('1');
149
-
150
- expect(result).toBeDefined();
151
- expect(result.full_name).toBe('John Doe');
152
- // In JavaScript, null * number = 0 (null is coerced to 0)
153
- expect(result.total).toBe(0);
154
- });
155
- });
156
-
157
- describe('Complex Formula Examples', () => {
158
- beforeEach(async () => {
159
- // Register an object with more complex formulas
160
- app.registerObject({
161
- name: 'order',
162
- fields: {
163
- subtotal: { type: 'currency' },
164
- discount_rate: { type: 'percent' },
165
- tax_rate: { type: 'percent' },
166
- final_price: {
167
- type: 'formula',
168
- expression: 'subtotal * (1 - discount_rate / 100) * (1 + tax_rate / 100)',
169
- data_type: 'currency',
170
- label: 'Final Price',
171
- },
172
- created_at: { type: 'date' },
173
- status: { type: 'select', options: ['draft', 'confirmed', 'shipped'] },
174
- risk_level: {
175
- type: 'formula',
176
- expression: `
177
- if (subtotal > 10000) {
178
- return 'High';
179
- } else if (subtotal > 1000) {
180
- return 'Medium';
181
- } else {
182
- return 'Low';
183
- }
184
- `,
185
- data_type: 'text',
186
- },
187
- },
188
- });
189
-
190
- await app.init();
191
- });
192
-
193
- it('should calculate complex financial formulas', async () => {
194
- mockDriver.setMockData('order', [
195
- {
196
- _id: '1',
197
- subtotal: 5000,
198
- discount_rate: 10,
199
- tax_rate: 8,
200
- status: 'confirmed',
201
- created_at: new Date('2026-01-01'),
202
- },
203
- ]);
204
-
205
- const ctx = app.createContext({ isSystem: true });
206
- const result = await ctx.object('order').findOne('1');
207
-
208
- expect(result).toBeDefined();
209
- expect(result.final_price).toBeCloseTo(4860, 1);
210
- expect(result.risk_level).toBe('Medium');
211
- });
212
-
213
- it('should handle conditional logic in formulas', async () => {
214
- mockDriver.setMockData('order', [
215
- {
216
- _id: '1',
217
- subtotal: 500,
218
- discount_rate: 0,
219
- tax_rate: 0,
220
- status: 'draft',
221
- created_at: new Date('2026-01-01'),
222
- },
223
- {
224
- _id: '2',
225
- subtotal: 5000,
226
- discount_rate: 0,
227
- tax_rate: 0,
228
- status: 'confirmed',
229
- created_at: new Date('2026-01-01'),
230
- },
231
- {
232
- _id: '3',
233
- subtotal: 15000,
234
- discount_rate: 0,
235
- tax_rate: 0,
236
- status: 'shipped',
237
- created_at: new Date('2026-01-01'),
238
- },
239
- ]);
240
-
241
- const ctx = app.createContext({ isSystem: true });
242
- const results = await ctx.object('order').find({});
243
-
244
- expect(results[0].risk_level).toBe('Low');
245
- expect(results[1].risk_level).toBe('Medium');
246
- expect(results[2].risk_level).toBe('High');
247
- });
248
- });
249
-
250
- describe('Formula Error Handling', () => {
251
- beforeEach(async () => {
252
- app.registerObject({
253
- name: 'product',
254
- fields: {
255
- name: { type: 'text' },
256
- price: { type: 'currency' },
257
- invalid_formula: {
258
- type: 'formula',
259
- expression: 'nonexistent_field * 2',
260
- data_type: 'number',
261
- },
262
- },
263
- });
264
-
265
- await app.init();
266
- });
267
-
268
- it('should handle formula evaluation errors gracefully', async () => {
269
- mockDriver.setMockData('product', [
270
- {
271
- _id: '1',
272
- name: 'Widget',
273
- price: 100,
274
- },
275
- ]);
276
-
277
- const ctx = app.createContext({ isSystem: true });
278
- const result = await ctx.object('product').findOne('1');
279
-
280
- expect(result).toBeDefined();
281
- expect(result.name).toBe('Widget');
282
- // Formula failed, should be null
283
- expect(result.invalid_formula).toBeNull();
284
- });
285
- });
286
- });
@@ -1,197 +0,0 @@
1
- /**
2
- * ObjectQL Formula Plugin Tests
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 { FormulaPlugin } from '../src/formula-plugin';
10
- import { ObjectStackKernel } from '@objectql/runtime';
11
-
12
- describe('FormulaPlugin', () => {
13
- let plugin: FormulaPlugin;
14
- let mockKernel: any;
15
-
16
- beforeEach(() => {
17
- // Create a mock kernel with middleware and formula provider support
18
- mockKernel = {
19
- use: jest.fn(),
20
- registerFormulaProvider: jest.fn(),
21
- };
22
- plugin = new FormulaPlugin();
23
- });
24
-
25
- describe('Plugin Metadata', () => {
26
- it('should have correct name and version', () => {
27
- expect(plugin.name).toBe('@objectql/formulas');
28
- expect(plugin.version).toBe('4.0.0');
29
- });
30
- });
31
-
32
- describe('Constructor', () => {
33
- it('should create plugin with default config', () => {
34
- const defaultPlugin = new FormulaPlugin();
35
- expect(defaultPlugin).toBeDefined();
36
- });
37
-
38
- it('should create plugin with custom config', () => {
39
- const customPlugin = new FormulaPlugin({
40
- enable_cache: true,
41
- cache_ttl: 600,
42
- autoEvaluateOnQuery: false,
43
- });
44
- expect(customPlugin).toBeDefined();
45
- });
46
-
47
- it('should accept formula engine config', () => {
48
- const customPlugin = new FormulaPlugin({
49
- enable_monitoring: true,
50
- max_execution_time: 5000,
51
- });
52
- expect(customPlugin).toBeDefined();
53
- });
54
- });
55
-
56
- describe('Installation', () => {
57
- it('should install successfully with mock kernel', async () => {
58
- const ctx = { engine: mockKernel };
59
- await plugin.install(ctx);
60
-
61
- // Verify that formula provider was registered
62
- expect(mockKernel.registerFormulaProvider).toHaveBeenCalled();
63
- });
64
-
65
- it('should register formula provider with evaluate function', async () => {
66
- const ctx = { engine: mockKernel };
67
- await plugin.install(ctx);
68
-
69
- expect(mockKernel.registerFormulaProvider).toHaveBeenCalledWith(
70
- expect.objectContaining({
71
- evaluate: expect.any(Function),
72
- validate: expect.any(Function),
73
- extractMetadata: expect.any(Function),
74
- })
75
- );
76
- });
77
-
78
- it('should register formula middleware when auto-evaluation is enabled', async () => {
79
- const pluginWithAuto = new FormulaPlugin({ autoEvaluateOnQuery: true });
80
- const ctx = { engine: mockKernel };
81
-
82
- await pluginWithAuto.install(ctx);
83
-
84
- // Check that middleware was registered
85
- expect(mockKernel.use).toHaveBeenCalledWith('afterQuery', expect.any(Function));
86
- });
87
-
88
- it('should not register formula middleware when auto-evaluation is disabled', async () => {
89
- const pluginNoAuto = new FormulaPlugin({ autoEvaluateOnQuery: false });
90
- const ctx = { engine: mockKernel };
91
-
92
- await pluginNoAuto.install(ctx);
93
-
94
- // Should not have registered afterQuery hook
95
- const afterQueryCalls = mockKernel.use.mock.calls.filter(
96
- (call: any[]) => call[0] === 'afterQuery'
97
- );
98
- expect(afterQueryCalls.length).toBe(0);
99
- });
100
-
101
- it('should handle kernel without registerFormulaProvider', async () => {
102
- const kernelNoProvider = {
103
- use: jest.fn(),
104
- };
105
- const ctx = { engine: kernelNoProvider };
106
-
107
- // Should not throw error and should set formulaEngine property
108
- await expect(plugin.install(ctx)).resolves.not.toThrow();
109
- expect((kernelNoProvider as any).formulaEngine).toBeDefined();
110
- });
111
-
112
- it('should handle kernel without middleware support', async () => {
113
- const kernelNoMiddleware = {
114
- registerFormulaProvider: jest.fn(),
115
- };
116
- const ctx = { engine: kernelNoMiddleware };
117
-
118
- // Should not throw error
119
- await expect(plugin.install(ctx)).resolves.not.toThrow();
120
- });
121
- });
122
-
123
- describe('Formula Provider', () => {
124
- it('should provide evaluate function that works', async () => {
125
- const ctx = { engine: mockKernel };
126
- await plugin.install(ctx);
127
-
128
- // Get the registered provider
129
- const provider = mockKernel.registerFormulaProvider.mock.calls[0][0];
130
-
131
- // Test the evaluate function
132
- const result = provider.evaluate('1 + 1', {
133
- record: {},
134
- system: {
135
- today: new Date(),
136
- now: new Date(),
137
- year: 2026,
138
- month: 1,
139
- day: 22,
140
- hour: 12,
141
- minute: 0,
142
- second: 0,
143
- },
144
- current_user: {},
145
- is_new: false,
146
- });
147
-
148
- expect(result).toBeDefined();
149
- expect(result.success).toBe(true);
150
- // The result is coerced to 'text' by default, so it's a string "2"
151
- expect(result.value).toBe('2');
152
- });
153
-
154
- it('should provide validate function that works', async () => {
155
- const ctx = { engine: mockKernel };
156
- await plugin.install(ctx);
157
-
158
- // Get the registered provider
159
- const provider = mockKernel.registerFormulaProvider.mock.calls[0][0];
160
-
161
- // Test the validate function
162
- const result = provider.validate('quantity * price');
163
-
164
- expect(result).toBeDefined();
165
- expect(result.valid).toBe(true);
166
- expect(result.errors.length).toBe(0);
167
- });
168
-
169
- it('should provide extractMetadata function that works', async () => {
170
- const ctx = { engine: mockKernel };
171
- await plugin.install(ctx);
172
-
173
- // Get the registered provider
174
- const provider = mockKernel.registerFormulaProvider.mock.calls[0][0];
175
-
176
- // Test the extractMetadata function
177
- const metadata = provider.extractMetadata('total', 'quantity * price', 'number');
178
-
179
- expect(metadata).toBeDefined();
180
- expect(metadata.field_name).toBe('total');
181
- expect(metadata.expression).toBe('quantity * price');
182
- expect(metadata.data_type).toBe('number');
183
- expect(metadata.dependencies).toContain('quantity');
184
- expect(metadata.dependencies).toContain('price');
185
- });
186
- });
187
-
188
- describe('Engine Access', () => {
189
- it('should expose formula engine instance', () => {
190
- const engine = plugin.getEngine();
191
- expect(engine).toBeDefined();
192
- expect(typeof engine.evaluate).toBe('function');
193
- expect(typeof engine.validate).toBe('function');
194
- expect(typeof engine.extractMetadata).toBe('function');
195
- });
196
- });
197
- });