@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
@@ -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
- formula: '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
- formula: '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
- formula: '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
- formula: '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
- formula: `
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
- formula: '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
- });
@@ -1,126 +0,0 @@
1
- /**
2
- * ObjectQL Validator 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 { ValidatorPlugin } from '../src/validator-plugin';
10
- import { ObjectStackKernel } from '@objectql/runtime';
11
-
12
- describe('ValidatorPlugin', () => {
13
- let plugin: ValidatorPlugin;
14
- let mockKernel: any;
15
-
16
- beforeEach(() => {
17
- // Create a mock kernel with middleware support
18
- mockKernel = {
19
- use: jest.fn(),
20
- };
21
- plugin = new ValidatorPlugin();
22
- });
23
-
24
- describe('Plugin Metadata', () => {
25
- it('should have correct name and version', () => {
26
- expect(plugin.name).toBe('@objectql/validator');
27
- expect(plugin.version).toBe('4.0.0');
28
- });
29
- });
30
-
31
- describe('Constructor', () => {
32
- it('should create plugin with default config', () => {
33
- const defaultPlugin = new ValidatorPlugin();
34
- expect(defaultPlugin).toBeDefined();
35
- });
36
-
37
- it('should create plugin with custom config', () => {
38
- const customPlugin = new ValidatorPlugin({
39
- language: 'zh-CN',
40
- enableQueryValidation: false,
41
- enableMutationValidation: true,
42
- });
43
- expect(customPlugin).toBeDefined();
44
- });
45
-
46
- it('should accept language options', () => {
47
- const customPlugin = new ValidatorPlugin({
48
- language: 'fr',
49
- languageFallback: ['en', 'zh-CN'],
50
- });
51
- expect(customPlugin).toBeDefined();
52
- });
53
- });
54
-
55
- describe('Installation', () => {
56
- it('should install successfully with mock kernel', async () => {
57
- const ctx = { engine: mockKernel };
58
- await plugin.install(ctx);
59
-
60
- // Verify that middleware hooks were registered
61
- expect(mockKernel.use).toHaveBeenCalled();
62
- });
63
-
64
- it('should register query validation when enabled', async () => {
65
- const pluginWithQuery = new ValidatorPlugin({ enableQueryValidation: true });
66
- const ctx = { engine: mockKernel };
67
-
68
- await pluginWithQuery.install(ctx);
69
-
70
- // Check that use was called (for query validation)
71
- expect(mockKernel.use).toHaveBeenCalledWith('beforeQuery', expect.any(Function));
72
- });
73
-
74
- it('should register mutation validation when enabled', async () => {
75
- const pluginWithMutation = new ValidatorPlugin({ enableMutationValidation: true });
76
- const ctx = { engine: mockKernel };
77
-
78
- await pluginWithMutation.install(ctx);
79
-
80
- // Check that use was called (for mutation validation)
81
- expect(mockKernel.use).toHaveBeenCalledWith('beforeMutation', expect.any(Function));
82
- });
83
-
84
- it('should not register query validation when disabled', async () => {
85
- const pluginNoQuery = new ValidatorPlugin({ enableQueryValidation: false });
86
- const ctx = { engine: mockKernel };
87
-
88
- await pluginNoQuery.install(ctx);
89
-
90
- // Should not have registered beforeQuery hook
91
- const beforeQueryCalls = mockKernel.use.mock.calls.filter(
92
- (call: any[]) => call[0] === 'beforeQuery'
93
- );
94
- expect(beforeQueryCalls.length).toBe(0);
95
- });
96
-
97
- it('should not register mutation validation when disabled', async () => {
98
- const pluginNoMutation = new ValidatorPlugin({ enableMutationValidation: false });
99
- const ctx = { engine: mockKernel };
100
-
101
- await pluginNoMutation.install(ctx);
102
-
103
- // Should not have registered beforeMutation hook
104
- const beforeMutationCalls = mockKernel.use.mock.calls.filter(
105
- (call: any[]) => call[0] === 'beforeMutation'
106
- );
107
- expect(beforeMutationCalls.length).toBe(0);
108
- });
109
-
110
- it('should handle kernel without middleware support', async () => {
111
- const kernelNoMiddleware = {};
112
- const ctx = { engine: kernelNoMiddleware };
113
-
114
- // Should not throw error
115
- await expect(plugin.install(ctx)).resolves.not.toThrow();
116
- });
117
- });
118
-
119
- describe('Validator Access', () => {
120
- it('should expose validator instance', () => {
121
- const validator = plugin.getValidator();
122
- expect(validator).toBeDefined();
123
- expect(typeof validator.validate).toBe('function');
124
- });
125
- });
126
- });