@objectql/core 4.1.0 → 4.2.1

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 (107) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +42 -0
  3. package/dist/app.d.ts +61 -51
  4. package/dist/app.js +101 -431
  5. package/dist/app.js.map +1 -1
  6. package/dist/index.d.ts +4 -11
  7. package/dist/index.js +13 -9
  8. package/dist/index.js.map +1 -1
  9. package/dist/kernel-factory.d.ts +38 -0
  10. package/dist/kernel-factory.js +38 -0
  11. package/dist/kernel-factory.js.map +1 -0
  12. package/dist/plugin.d.ts +10 -16
  13. package/dist/plugin.js +104 -58
  14. package/dist/plugin.js.map +1 -1
  15. package/dist/repository.d.ts +4 -31
  16. package/dist/repository.js +7 -275
  17. package/dist/repository.js.map +1 -1
  18. package/dist/util.js +4 -2
  19. package/dist/util.js.map +1 -1
  20. package/package.json +19 -11
  21. package/src/app.ts +157 -531
  22. package/src/index.ts +8 -23
  23. package/src/kernel-factory.ts +47 -0
  24. package/src/plugin.ts +116 -94
  25. package/src/repository.ts +4 -313
  26. package/src/util.ts +5 -3
  27. package/test/__mocks__/@objectstack/core.ts +250 -1
  28. package/test/__mocks__/@objectstack/objectql.ts +127 -0
  29. package/test/__mocks__/@objectstack/runtime.ts +14 -5
  30. package/test/introspection.test.ts +1 -1
  31. package/test/mock-driver.ts +5 -5
  32. package/test/optimizations.test.ts +2 -2
  33. package/test/plugin-integration.test.ts +4 -5
  34. package/test/utils.ts +6 -6
  35. package/tsconfig.json +3 -1
  36. package/tsconfig.tsbuildinfo +1 -1
  37. package/dist/gateway.d.ts +0 -36
  38. package/dist/gateway.js +0 -89
  39. package/dist/gateway.js.map +0 -1
  40. package/dist/optimizations/CompiledHookManager.d.ts +0 -55
  41. package/dist/optimizations/CompiledHookManager.js +0 -164
  42. package/dist/optimizations/CompiledHookManager.js.map +0 -1
  43. package/dist/optimizations/DependencyGraph.d.ts +0 -82
  44. package/dist/optimizations/DependencyGraph.js +0 -211
  45. package/dist/optimizations/DependencyGraph.js.map +0 -1
  46. package/dist/optimizations/GlobalConnectionPool.d.ts +0 -89
  47. package/dist/optimizations/GlobalConnectionPool.js +0 -193
  48. package/dist/optimizations/GlobalConnectionPool.js.map +0 -1
  49. package/dist/optimizations/LazyMetadataLoader.d.ts +0 -75
  50. package/dist/optimizations/LazyMetadataLoader.js +0 -149
  51. package/dist/optimizations/LazyMetadataLoader.js.map +0 -1
  52. package/dist/optimizations/OptimizedMetadataRegistry.d.ts +0 -26
  53. package/dist/optimizations/OptimizedMetadataRegistry.js +0 -117
  54. package/dist/optimizations/OptimizedMetadataRegistry.js.map +0 -1
  55. package/dist/optimizations/OptimizedValidationEngine.d.ts +0 -73
  56. package/dist/optimizations/OptimizedValidationEngine.js +0 -141
  57. package/dist/optimizations/OptimizedValidationEngine.js.map +0 -1
  58. package/dist/optimizations/QueryCompiler.d.ts +0 -51
  59. package/dist/optimizations/QueryCompiler.js +0 -216
  60. package/dist/optimizations/QueryCompiler.js.map +0 -1
  61. package/dist/optimizations/SQLQueryOptimizer.d.ts +0 -96
  62. package/dist/optimizations/SQLQueryOptimizer.js +0 -265
  63. package/dist/optimizations/SQLQueryOptimizer.js.map +0 -1
  64. package/dist/optimizations/index.d.ts +0 -32
  65. package/dist/optimizations/index.js +0 -44
  66. package/dist/optimizations/index.js.map +0 -1
  67. package/dist/protocol.d.ts +0 -180
  68. package/dist/protocol.js +0 -260
  69. package/dist/protocol.js.map +0 -1
  70. package/dist/query/filter-translator.d.ts +0 -27
  71. package/dist/query/filter-translator.js +0 -38
  72. package/dist/query/filter-translator.js.map +0 -1
  73. package/dist/query/index.d.ts +0 -22
  74. package/dist/query/index.js +0 -39
  75. package/dist/query/index.js.map +0 -1
  76. package/dist/query/query-analyzer.d.ts +0 -188
  77. package/dist/query/query-analyzer.js +0 -348
  78. package/dist/query/query-analyzer.js.map +0 -1
  79. package/dist/query/query-builder.d.ts +0 -35
  80. package/dist/query/query-builder.js +0 -61
  81. package/dist/query/query-builder.js.map +0 -1
  82. package/dist/query/query-service.d.ts +0 -153
  83. package/dist/query/query-service.js +0 -272
  84. package/dist/query/query-service.js.map +0 -1
  85. package/jest.config.js +0 -29
  86. package/src/gateway.ts +0 -101
  87. package/src/optimizations/CompiledHookManager.ts +0 -185
  88. package/src/optimizations/DependencyGraph.ts +0 -255
  89. package/src/optimizations/GlobalConnectionPool.ts +0 -251
  90. package/src/optimizations/LazyMetadataLoader.ts +0 -180
  91. package/src/optimizations/OptimizedMetadataRegistry.ts +0 -132
  92. package/src/optimizations/OptimizedValidationEngine.ts +0 -172
  93. package/src/optimizations/QueryCompiler.ts +0 -242
  94. package/src/optimizations/SQLQueryOptimizer.ts +0 -329
  95. package/src/optimizations/index.ts +0 -34
  96. package/src/protocol.ts +0 -291
  97. package/src/query/filter-translator.ts +0 -41
  98. package/src/query/index.ts +0 -24
  99. package/src/query/query-analyzer.ts +0 -533
  100. package/src/query/query-builder.ts +0 -64
  101. package/src/query/query-service.ts +0 -398
  102. package/test/app.test.ts +0 -578
  103. package/test/filter-syntax.test.ts +0 -233
  104. package/test/gateway.test.ts +0 -88
  105. package/test/protocol.test.ts +0 -143
  106. package/test/repository-validation.test.ts +0 -351
  107. package/test/repository.test.ts +0 -151
@@ -1,233 +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
- import { ObjectQL } from '../src/index';
10
- import { MockDriver } from './mock-driver';
11
- import { ObjectConfig } from '@objectql/types';
12
-
13
- /**
14
- * Modern Filter Syntax Tests
15
- *
16
- * Tests the new object-based filter syntax from @objectstack/spec FilterCondition.
17
- * This replaces the old array-based FilterExpression syntax.
18
- *
19
- * Note: These tests verify filter translation logic. Full filter functionality
20
- * is tested in driver integration tests.
21
- */
22
-
23
- const productObject: ObjectConfig = {
24
- name: 'product',
25
- fields: {
26
- name: { type: 'text' },
27
- price: { type: 'number' },
28
- category: { type: 'text' },
29
- status: { type: 'text' }
30
- }
31
- };
32
-
33
- describe('Modern Filter Syntax - Translation', () => {
34
- let app: ObjectQL;
35
- let driver: MockDriver;
36
-
37
- beforeEach(async () => {
38
- driver = new MockDriver();
39
- app = new ObjectQL({
40
- datasources: {
41
- default: driver
42
- },
43
- objects: {
44
- product: productObject
45
- }
46
- });
47
- await app.init();
48
- });
49
-
50
- describe('Filter Translation to Kernel', () => {
51
- it('should accept object-based filter syntax', async () => {
52
- const ctx = app.createContext({ userId: 'test', isSystem: true });
53
- const repo = ctx.object('product');
54
-
55
- // This should not throw - it accepts the new syntax
56
- await expect(repo.find({
57
- filters: { category: 'Electronics' }
58
- })).resolves.toBeDefined();
59
- });
60
-
61
- it('should accept $eq operator', async () => {
62
- const ctx = app.createContext({ userId: 'test', isSystem: true });
63
- const repo = ctx.object('product');
64
-
65
- await expect(repo.find({
66
- filters: { status: { $eq: 'active' } }
67
- })).resolves.toBeDefined();
68
- });
69
-
70
- it('should accept $ne operator', async () => {
71
- const ctx = app.createContext({ userId: 'test', isSystem: true });
72
- const repo = ctx.object('product');
73
-
74
- await expect(repo.find({
75
- filters: { status: { $ne: 'inactive' } }
76
- })).resolves.toBeDefined();
77
- });
78
-
79
- it('should accept comparison operators', async () => {
80
- const ctx = app.createContext({ userId: 'test', isSystem: true });
81
- const repo = ctx.object('product');
82
-
83
- await expect(repo.find({
84
- filters: { price: { $gt: 100 } }
85
- })).resolves.toBeDefined();
86
-
87
- await expect(repo.find({
88
- filters: { price: { $gte: 100 } }
89
- })).resolves.toBeDefined();
90
-
91
- await expect(repo.find({
92
- filters: { price: { $lt: 500 } }
93
- })).resolves.toBeDefined();
94
-
95
- await expect(repo.find({
96
- filters: { price: { $lte: 500 } }
97
- })).resolves.toBeDefined();
98
- });
99
-
100
- it('should accept $in operator', async () => {
101
- const ctx = app.createContext({ userId: 'test', isSystem: true });
102
- const repo = ctx.object('product');
103
-
104
- await expect(repo.find({
105
- filters: { status: { $in: ['active', 'pending'] } }
106
- })).resolves.toBeDefined();
107
- });
108
-
109
- it('should accept $nin operator', async () => {
110
- const ctx = app.createContext({ userId: 'test', isSystem: true });
111
- const repo = ctx.object('product');
112
-
113
- await expect(repo.find({
114
- filters: { status: { $nin: ['inactive', 'deleted'] } }
115
- })).resolves.toBeDefined();
116
- });
117
-
118
- it('should accept $and operator', async () => {
119
- const ctx = app.createContext({ userId: 'test', isSystem: true });
120
- const repo = ctx.object('product');
121
-
122
- await expect(repo.find({
123
- filters: {
124
- $and: [
125
- { category: 'Electronics' },
126
- { status: 'active' }
127
- ]
128
- }
129
- })).resolves.toBeDefined();
130
- });
131
-
132
- it('should accept $or operator', async () => {
133
- const ctx = app.createContext({ userId: 'test', isSystem: true });
134
- const repo = ctx.object('product');
135
-
136
- await expect(repo.find({
137
- filters: {
138
- $or: [
139
- { category: 'Electronics' },
140
- { category: 'Furniture' }
141
- ]
142
- }
143
- })).resolves.toBeDefined();
144
- });
145
-
146
- it('should accept nested logical operators', async () => {
147
- const ctx = app.createContext({ userId: 'test', isSystem: true });
148
- const repo = ctx.object('product');
149
-
150
- await expect(repo.find({
151
- filters: {
152
- $and: [
153
- {
154
- $or: [
155
- { category: 'Electronics' },
156
- { category: 'Furniture' }
157
- ]
158
- },
159
- { status: 'active' }
160
- ]
161
- }
162
- })).resolves.toBeDefined();
163
- });
164
-
165
- it('should accept multiple operators on same field', async () => {
166
- const ctx = app.createContext({ userId: 'test', isSystem: true });
167
- const repo = ctx.object('product');
168
-
169
- await expect(repo.find({
170
- filters: {
171
- price: {
172
- $gte: 100,
173
- $lte: 500
174
- }
175
- }
176
- })).resolves.toBeDefined();
177
- });
178
-
179
- it('should accept mixed implicit and explicit syntax', async () => {
180
- const ctx = app.createContext({ userId: 'test', isSystem: true });
181
- const repo = ctx.object('product');
182
-
183
- await expect(repo.find({
184
- filters: {
185
- category: 'Electronics',
186
- price: { $gte: 100 }
187
- }
188
- })).resolves.toBeDefined();
189
- });
190
- });
191
-
192
- describe('Backward Compatibility', () => {
193
- it('should still support legacy array-based filter syntax', async () => {
194
- const ctx = app.createContext({ userId: 'test', isSystem: true });
195
- const repo = ctx.object('product');
196
-
197
- // Old syntax should still work
198
- await expect(repo.find({
199
- filters: [['category', '=', 'Electronics']] as any
200
- })).resolves.toBeDefined();
201
- });
202
-
203
- it('should support legacy complex filters with logical operators', async () => {
204
- const ctx = app.createContext({ userId: 'test', isSystem: true });
205
- const repo = ctx.object('product');
206
-
207
- await expect(repo.find({
208
- filters: [
209
- ['category', '=', 'Electronics'],
210
- 'and',
211
- ['status', '=', 'active']
212
- ] as any
213
- })).resolves.toBeDefined();
214
- });
215
-
216
- it('should support legacy nested filter groups', async () => {
217
- const ctx = app.createContext({ userId: 'test', isSystem: true });
218
- const repo = ctx.object('product');
219
-
220
- await expect(repo.find({
221
- filters: [
222
- [
223
- ['category', '=', 'Electronics'],
224
- 'or',
225
- ['category', '=', 'Furniture']
226
- ],
227
- 'and',
228
- ['status', '=', 'active']
229
- ] as any
230
- })).resolves.toBeDefined();
231
- });
232
- });
233
- });
@@ -1,88 +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
- import { ObjectGateway } from '../src/gateway';
10
- import { ApiRequest, ApiResponse, GatewayProtocol } from '@objectql/types';
11
-
12
- describe('ObjectGateway', () => {
13
- let gateway: ObjectGateway;
14
- let mockProtocol: GatewayProtocol;
15
-
16
- beforeEach(() => {
17
- gateway = new ObjectGateway();
18
- mockProtocol = {
19
- name: 'mock',
20
- route: jest.fn().mockReturnValue(true),
21
- handle: jest.fn().mockResolvedValue({ status: 200, body: 'ok' })
22
- };
23
- gateway.registerProtocol(mockProtocol);
24
- });
25
-
26
- it('should route request to registered protocol', async () => {
27
- const req: ApiRequest = {
28
- path: '/test',
29
- method: 'GET',
30
- headers: {},
31
- query: {}
32
- };
33
-
34
- const response = await gateway.handle(req);
35
-
36
- expect(mockProtocol.route).toHaveBeenCalledWith(req);
37
- expect(mockProtocol.handle).toHaveBeenCalledWith(req);
38
- expect(response.status).toBe(200);
39
- });
40
-
41
- it('should return 404 if no protocol matches', async () => {
42
- const specializedGateway = new ObjectGateway();
43
- const response = await specializedGateway.handle({
44
- path: '/unknown',
45
- method: 'GET',
46
- headers: {},
47
- query: {}
48
- });
49
-
50
- expect(response.status).toBe(404);
51
- expect(response.body.error.code).toBe('PROTOCOL_NOT_FOUND');
52
- });
53
-
54
- it('should apply request transformers', async () => {
55
- const req: ApiRequest = {
56
- path: '/original',
57
- method: 'GET',
58
- headers: {},
59
- query: {}
60
- };
61
-
62
- gateway.addRequestTransform(async (r) => {
63
- return { ...r, path: '/transformed' };
64
- });
65
-
66
- await gateway.handle(req);
67
-
68
- // Protocol should see the transformed request
69
- expect(mockProtocol.route).toHaveBeenCalledWith(expect.objectContaining({ path: '/transformed' }));
70
- });
71
-
72
- it('should apply response transformers', async () => {
73
- const req: ApiRequest = {
74
- path: '/test',
75
- method: 'GET',
76
- headers: {},
77
- query: {}
78
- };
79
-
80
- gateway.addResponseTransform(async (res) => {
81
- return { ...res, headers: { ...res.headers, 'X-Custom': 'Added' } };
82
- });
83
-
84
- const response = await gateway.handle(req);
85
-
86
- expect(response.headers?.['X-Custom']).toBe('Added');
87
- });
88
- });
@@ -1,143 +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
- import { ObjectStackProtocolImplementation } from '../src/protocol';
10
- import { IObjectQL } from '@objectql/types';
11
-
12
- describe('ObjectStackProtocolImplementation', () => {
13
- let mockEngine: Partial<IObjectQL>;
14
- let protocol: ObjectStackProtocolImplementation;
15
-
16
- beforeEach(() => {
17
- mockEngine = {
18
- metadata: {
19
- getTypes: jest.fn().mockReturnValue(['object']),
20
- list: jest.fn().mockReturnValue([{ name: 'testObject', label: 'Test Object' }]),
21
- get: jest.fn().mockReturnValue({ name: 'testObject', fields: {} }),
22
- } as any,
23
- // Mock kernel-like direct methods
24
- find: jest.fn(),
25
- get: jest.fn(),
26
- create: jest.fn(),
27
- update: jest.fn(),
28
- delete: jest.fn(),
29
- };
30
- protocol = new ObjectStackProtocolImplementation(mockEngine as IObjectQL);
31
- });
32
-
33
- describe('Meta Operations', () => {
34
- it('getMetaTypes should return types from engine metadata', async () => {
35
- const result = await protocol.getMetaTypes({});
36
- expect(result).toEqual({ types: ['object'] });
37
- expect(mockEngine.metadata?.getTypes).toHaveBeenCalled();
38
- });
39
-
40
- it('getMetaItems should return items for a type', async () => {
41
- const result = await protocol.getMetaItems({ type: 'object' });
42
- expect(result).toEqual({
43
- type: 'object',
44
- items: [{ name: 'testObject', label: 'Test Object' }]
45
- });
46
- expect(mockEngine.metadata?.list).toHaveBeenCalledWith('object');
47
- });
48
-
49
- it('getMetaItem should return specific item definition', async () => {
50
- const result = await protocol.getMetaItem({ type: 'object', name: 'testObject' });
51
- expect(result).toEqual({ name: 'testObject', fields: {} });
52
- expect(mockEngine.metadata?.get).toHaveBeenCalledWith('object', 'testObject');
53
- });
54
- });
55
-
56
- describe('Data Operations (Direct Kernel Mode)', () => {
57
- it('findData should delegate to engine.find', async () => {
58
- const mockData = [{ id: '1', name: 'Test' }];
59
- (mockEngine as any).find.mockResolvedValue(mockData);
60
-
61
- const result = await protocol.findData({ object: 'testObject', query: {} });
62
- expect(result).toBe(mockData);
63
- expect((mockEngine as any).find).toHaveBeenCalledWith('testObject', {});
64
- });
65
-
66
- it('getData should delegate to engine.get', async () => {
67
- const mockRecord = { id: '1', name: 'Test' };
68
- (mockEngine as any).get.mockResolvedValue(mockRecord);
69
-
70
- const result = await protocol.getData({ object: 'testObject', id: '1' });
71
- expect(result).toBe(mockRecord);
72
- expect((mockEngine as any).get).toHaveBeenCalledWith('testObject', '1');
73
- });
74
-
75
- it('createData should delegate to engine.create', async () => {
76
- const newData = { name: 'New' };
77
- const createdRecord = { id: '2', ...newData };
78
- (mockEngine as any).create.mockResolvedValue(createdRecord);
79
-
80
- const result = await protocol.createData({ object: 'testObject', data: newData });
81
- expect(result).toBe(createdRecord);
82
- expect((mockEngine as any).create).toHaveBeenCalledWith('testObject', newData);
83
- });
84
-
85
- it('updateData should delegate to engine.update', async () => {
86
- const updateData = { name: 'Updated' };
87
- const updatedRecord = { id: '1', ...updateData };
88
- (mockEngine as any).update.mockResolvedValue(updatedRecord);
89
-
90
- const result = await protocol.updateData({ object: 'testObject', id: '1', data: updateData });
91
- expect(result).toBe(updatedRecord);
92
- expect((mockEngine as any).update).toHaveBeenCalledWith('testObject', '1', updateData);
93
- });
94
-
95
- it('deleteData should delegate to engine.delete', async () => {
96
- (mockEngine as any).delete.mockResolvedValue(true);
97
-
98
- const result = await protocol.deleteData({ object: 'testObject', id: '1' });
99
- expect(result).toEqual({ object: 'testObject', id: '1', success: true });
100
- expect((mockEngine as any).delete).toHaveBeenCalledWith('testObject', '1');
101
- });
102
- });
103
-
104
- describe('Legacy Mode (IObjectQL Context)', () => {
105
- let mockRepo: any;
106
-
107
- beforeEach(() => {
108
- // Remove direct methods to force legacy path
109
- delete (mockEngine as any).find;
110
- delete (mockEngine as any).get;
111
- delete (mockEngine as any).create;
112
- delete (mockEngine as any).update;
113
- delete (mockEngine as any).delete;
114
-
115
- mockRepo = {
116
- find: jest.fn(),
117
- findOne: jest.fn(),
118
- create: jest.fn(),
119
- update: jest.fn(),
120
- delete: jest.fn(),
121
- };
122
-
123
- (mockEngine as any).createContext = jest.fn().mockReturnValue({
124
- object: jest.fn().mockReturnValue(mockRepo)
125
- });
126
- });
127
-
128
- it('findData should use repo.find in legacy mode', async () => {
129
- const mockData = [{ id: '1' }];
130
- mockRepo.find.mockResolvedValue(mockData);
131
-
132
- await protocol.findData({ object: 'testObject' });
133
- expect(mockRepo.find).toHaveBeenCalled();
134
- });
135
-
136
- it('deleteData should use repo.delete in legacy mode', async () => {
137
- mockRepo.delete.mockResolvedValue(true);
138
- const result = await protocol.deleteData({ object: 'testObject', id: '1' });
139
- expect(result.success).toBe(true);
140
- expect(mockRepo.delete).toHaveBeenCalledWith('1');
141
- });
142
- });
143
- });