@objectql/core 4.2.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 (115) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +23 -0
  3. package/dist/app.d.ts +61 -52
  4. package/dist/app.js +101 -435
  5. package/dist/app.js.map +1 -1
  6. package/dist/index.d.ts +4 -18
  7. package/dist/index.js +11 -18
  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 +9 -16
  13. package/dist/plugin.js +71 -48
  14. package/dist/plugin.js.map +1 -1
  15. package/dist/repository.d.ts +4 -31
  16. package/dist/repository.js +7 -283
  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 +12 -11
  21. package/src/app.ts +157 -539
  22. package/src/index.ts +8 -40
  23. package/src/kernel-factory.ts +47 -0
  24. package/src/plugin.ts +77 -85
  25. package/src/repository.ts +4 -320
  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 +1 -3
  34. package/test/utils.ts +6 -6
  35. package/tsconfig.json +3 -1
  36. package/tsconfig.tsbuildinfo +1 -1
  37. package/dist/ai/index.d.ts +0 -8
  38. package/dist/ai/index.js +0 -25
  39. package/dist/ai/index.js.map +0 -1
  40. package/dist/ai/registry.d.ts +0 -23
  41. package/dist/ai/registry.js +0 -78
  42. package/dist/ai/registry.js.map +0 -1
  43. package/dist/gateway.d.ts +0 -37
  44. package/dist/gateway.js +0 -93
  45. package/dist/gateway.js.map +0 -1
  46. package/dist/optimizations/CompiledHookManager.d.ts +0 -56
  47. package/dist/optimizations/CompiledHookManager.js +0 -170
  48. package/dist/optimizations/CompiledHookManager.js.map +0 -1
  49. package/dist/optimizations/DependencyGraph.d.ts +0 -82
  50. package/dist/optimizations/DependencyGraph.js +0 -211
  51. package/dist/optimizations/DependencyGraph.js.map +0 -1
  52. package/dist/optimizations/GlobalConnectionPool.d.ts +0 -89
  53. package/dist/optimizations/GlobalConnectionPool.js +0 -193
  54. package/dist/optimizations/GlobalConnectionPool.js.map +0 -1
  55. package/dist/optimizations/LazyMetadataLoader.d.ts +0 -75
  56. package/dist/optimizations/LazyMetadataLoader.js +0 -149
  57. package/dist/optimizations/LazyMetadataLoader.js.map +0 -1
  58. package/dist/optimizations/OptimizedMetadataRegistry.d.ts +0 -26
  59. package/dist/optimizations/OptimizedMetadataRegistry.js +0 -117
  60. package/dist/optimizations/OptimizedMetadataRegistry.js.map +0 -1
  61. package/dist/optimizations/OptimizedValidationEngine.d.ts +0 -73
  62. package/dist/optimizations/OptimizedValidationEngine.js +0 -141
  63. package/dist/optimizations/OptimizedValidationEngine.js.map +0 -1
  64. package/dist/optimizations/QueryCompiler.d.ts +0 -51
  65. package/dist/optimizations/QueryCompiler.js +0 -216
  66. package/dist/optimizations/QueryCompiler.js.map +0 -1
  67. package/dist/optimizations/SQLQueryOptimizer.d.ts +0 -96
  68. package/dist/optimizations/SQLQueryOptimizer.js +0 -265
  69. package/dist/optimizations/SQLQueryOptimizer.js.map +0 -1
  70. package/dist/optimizations/index.d.ts +0 -32
  71. package/dist/optimizations/index.js +0 -44
  72. package/dist/optimizations/index.js.map +0 -1
  73. package/dist/protocol.d.ts +0 -191
  74. package/dist/protocol.js +0 -272
  75. package/dist/protocol.js.map +0 -1
  76. package/dist/query/filter-translator.d.ts +0 -24
  77. package/dist/query/filter-translator.js +0 -38
  78. package/dist/query/filter-translator.js.map +0 -1
  79. package/dist/query/index.d.ts +0 -22
  80. package/dist/query/index.js +0 -39
  81. package/dist/query/index.js.map +0 -1
  82. package/dist/query/query-analyzer.d.ts +0 -186
  83. package/dist/query/query-analyzer.js +0 -348
  84. package/dist/query/query-analyzer.js.map +0 -1
  85. package/dist/query/query-builder.d.ts +0 -27
  86. package/dist/query/query-builder.js +0 -69
  87. package/dist/query/query-builder.js.map +0 -1
  88. package/dist/query/query-service.d.ts +0 -151
  89. package/dist/query/query-service.js +0 -272
  90. package/dist/query/query-service.js.map +0 -1
  91. package/src/ai/index.ts +0 -9
  92. package/src/ai/registry.ts +0 -81
  93. package/src/gateway.ts +0 -105
  94. package/src/optimizations/CompiledHookManager.ts +0 -193
  95. package/src/optimizations/DependencyGraph.ts +0 -255
  96. package/src/optimizations/GlobalConnectionPool.ts +0 -251
  97. package/src/optimizations/LazyMetadataLoader.ts +0 -180
  98. package/src/optimizations/OptimizedMetadataRegistry.ts +0 -132
  99. package/src/optimizations/OptimizedValidationEngine.ts +0 -172
  100. package/src/optimizations/QueryCompiler.ts +0 -242
  101. package/src/optimizations/SQLQueryOptimizer.ts +0 -329
  102. package/src/optimizations/index.ts +0 -34
  103. package/src/protocol.ts +0 -304
  104. package/src/query/filter-translator.ts +0 -41
  105. package/src/query/index.ts +0 -24
  106. package/src/query/query-analyzer.ts +0 -532
  107. package/src/query/query-builder.ts +0 -64
  108. package/src/query/query-service.ts +0 -397
  109. package/test/ai-registry.test.ts +0 -42
  110. package/test/app.test.ts +0 -578
  111. package/test/filter-syntax.test.ts +0 -233
  112. package/test/gateway.test.ts +0 -88
  113. package/test/protocol.test.ts +0 -143
  114. package/test/repository-validation.test.ts +0 -351
  115. 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
- });