@objectql/core 4.2.0 → 4.2.2

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 (116) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +5 -2
  4. package/dist/app.d.ts +61 -52
  5. package/dist/app.js +100 -435
  6. package/dist/app.js.map +1 -1
  7. package/dist/index.d.ts +14 -20
  8. package/dist/index.js +21 -20
  9. package/dist/index.js.map +1 -1
  10. package/dist/kernel-factory.d.ts +38 -0
  11. package/dist/kernel-factory.js +38 -0
  12. package/dist/kernel-factory.js.map +1 -0
  13. package/dist/plugin.d.ts +9 -16
  14. package/dist/plugin.js +71 -48
  15. package/dist/plugin.js.map +1 -1
  16. package/dist/repository.d.ts +4 -31
  17. package/dist/repository.js +7 -283
  18. package/dist/repository.js.map +1 -1
  19. package/dist/util.js +4 -2
  20. package/dist/util.js.map +1 -1
  21. package/package.json +14 -12
  22. package/src/app.ts +156 -539
  23. package/src/index.ts +18 -42
  24. package/src/kernel-factory.ts +47 -0
  25. package/src/plugin.ts +77 -85
  26. package/src/repository.ts +4 -320
  27. package/src/util.ts +5 -3
  28. package/test/__mocks__/@objectstack/core.ts +250 -1
  29. package/test/__mocks__/@objectstack/objectql.ts +273 -0
  30. package/test/__mocks__/@objectstack/runtime.ts +14 -5
  31. package/test/introspection.test.ts +1 -1
  32. package/test/mock-driver.ts +5 -5
  33. package/test/optimizations.test.ts +2 -2
  34. package/test/plugin-integration.test.ts +1 -3
  35. package/test/utils.ts +6 -6
  36. package/tsconfig.json +3 -1
  37. package/tsconfig.tsbuildinfo +1 -1
  38. package/dist/ai/index.d.ts +0 -8
  39. package/dist/ai/index.js +0 -25
  40. package/dist/ai/index.js.map +0 -1
  41. package/dist/ai/registry.d.ts +0 -23
  42. package/dist/ai/registry.js +0 -78
  43. package/dist/ai/registry.js.map +0 -1
  44. package/dist/gateway.d.ts +0 -37
  45. package/dist/gateway.js +0 -93
  46. package/dist/gateway.js.map +0 -1
  47. package/dist/optimizations/CompiledHookManager.d.ts +0 -56
  48. package/dist/optimizations/CompiledHookManager.js +0 -170
  49. package/dist/optimizations/CompiledHookManager.js.map +0 -1
  50. package/dist/optimizations/DependencyGraph.d.ts +0 -82
  51. package/dist/optimizations/DependencyGraph.js +0 -211
  52. package/dist/optimizations/DependencyGraph.js.map +0 -1
  53. package/dist/optimizations/GlobalConnectionPool.d.ts +0 -89
  54. package/dist/optimizations/GlobalConnectionPool.js +0 -193
  55. package/dist/optimizations/GlobalConnectionPool.js.map +0 -1
  56. package/dist/optimizations/LazyMetadataLoader.d.ts +0 -75
  57. package/dist/optimizations/LazyMetadataLoader.js +0 -149
  58. package/dist/optimizations/LazyMetadataLoader.js.map +0 -1
  59. package/dist/optimizations/OptimizedMetadataRegistry.d.ts +0 -26
  60. package/dist/optimizations/OptimizedMetadataRegistry.js +0 -117
  61. package/dist/optimizations/OptimizedMetadataRegistry.js.map +0 -1
  62. package/dist/optimizations/OptimizedValidationEngine.d.ts +0 -73
  63. package/dist/optimizations/OptimizedValidationEngine.js +0 -141
  64. package/dist/optimizations/OptimizedValidationEngine.js.map +0 -1
  65. package/dist/optimizations/QueryCompiler.d.ts +0 -51
  66. package/dist/optimizations/QueryCompiler.js +0 -216
  67. package/dist/optimizations/QueryCompiler.js.map +0 -1
  68. package/dist/optimizations/SQLQueryOptimizer.d.ts +0 -96
  69. package/dist/optimizations/SQLQueryOptimizer.js +0 -265
  70. package/dist/optimizations/SQLQueryOptimizer.js.map +0 -1
  71. package/dist/optimizations/index.d.ts +0 -32
  72. package/dist/optimizations/index.js +0 -44
  73. package/dist/optimizations/index.js.map +0 -1
  74. package/dist/protocol.d.ts +0 -191
  75. package/dist/protocol.js +0 -272
  76. package/dist/protocol.js.map +0 -1
  77. package/dist/query/filter-translator.d.ts +0 -24
  78. package/dist/query/filter-translator.js +0 -38
  79. package/dist/query/filter-translator.js.map +0 -1
  80. package/dist/query/index.d.ts +0 -22
  81. package/dist/query/index.js +0 -39
  82. package/dist/query/index.js.map +0 -1
  83. package/dist/query/query-analyzer.d.ts +0 -186
  84. package/dist/query/query-analyzer.js +0 -348
  85. package/dist/query/query-analyzer.js.map +0 -1
  86. package/dist/query/query-builder.d.ts +0 -27
  87. package/dist/query/query-builder.js +0 -69
  88. package/dist/query/query-builder.js.map +0 -1
  89. package/dist/query/query-service.d.ts +0 -151
  90. package/dist/query/query-service.js +0 -272
  91. package/dist/query/query-service.js.map +0 -1
  92. package/src/ai/index.ts +0 -9
  93. package/src/ai/registry.ts +0 -81
  94. package/src/gateway.ts +0 -105
  95. package/src/optimizations/CompiledHookManager.ts +0 -193
  96. package/src/optimizations/DependencyGraph.ts +0 -255
  97. package/src/optimizations/GlobalConnectionPool.ts +0 -251
  98. package/src/optimizations/LazyMetadataLoader.ts +0 -180
  99. package/src/optimizations/OptimizedMetadataRegistry.ts +0 -132
  100. package/src/optimizations/OptimizedValidationEngine.ts +0 -172
  101. package/src/optimizations/QueryCompiler.ts +0 -242
  102. package/src/optimizations/SQLQueryOptimizer.ts +0 -329
  103. package/src/optimizations/index.ts +0 -34
  104. package/src/protocol.ts +0 -304
  105. package/src/query/filter-translator.ts +0 -41
  106. package/src/query/index.ts +0 -24
  107. package/src/query/query-analyzer.ts +0 -532
  108. package/src/query/query-builder.ts +0 -64
  109. package/src/query/query-service.ts +0 -397
  110. package/test/ai-registry.test.ts +0 -42
  111. package/test/app.test.ts +0 -578
  112. package/test/filter-syntax.test.ts +0 -233
  113. package/test/gateway.test.ts +0 -88
  114. package/test/protocol.test.ts +0 -143
  115. package/test/repository-validation.test.ts +0 -351
  116. package/test/repository.test.ts +0 -151
package/src/repository.ts CHANGED
@@ -1,322 +1,6 @@
1
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.
2
+ * @deprecated This file is dead code. Delete it.
3
+ * ObjectRepository class now comes from @objectstack/objectql.
4
+ * Re-exported via index.ts for backward compatibility.
7
5
  */
8
-
9
- import { ObjectQLContext, IObjectQL, ObjectConfig, Driver, UnifiedQuery, ActionContext, HookAPI, RetrievalHookContext, MutationHookContext, UpdateHookContext, ValidationContext, ValidationError, ValidationRuleResult, FormulaContext, Filter, QueryAST } from '@objectql/types';
10
- import type { ObjectKernel } from '@objectstack/runtime';
11
- import { Data } from '@objectstack/spec';
12
- import { z } from 'zod';
13
- type SortNode = z.infer<typeof Data.SortNodeSchema>;
14
-
15
- export class ObjectRepository {
16
-
17
- constructor(
18
- private objectName: string,
19
- private context: ObjectQLContext,
20
- private app: IObjectQL
21
- ) {
22
- }
23
-
24
- private getDriver(): Driver {
25
- const obj = this.getSchema();
26
- const datasourceName = obj.datasource || 'default';
27
- return this.app.datasource(datasourceName);
28
- }
29
-
30
- private getKernel(): ObjectKernel {
31
- return this.app.getKernel();
32
- }
33
-
34
- private getOptions(extra: Record<string, unknown> = {}) {
35
- return {
36
- transaction: this.context.transactionHandle,
37
- ...extra
38
- };
39
- }
40
-
41
- getSchema(): ObjectConfig {
42
- const obj = this.app.getObject(this.objectName);
43
- if (!obj) {
44
- throw new Error(`Object '${this.objectName}' not found`);
45
- }
46
- return obj;
47
- }
48
-
49
- private getHookAPI(): HookAPI {
50
- return {
51
- find: (obj, q) => this.context.object(obj).find(q),
52
- findOne: (obj, id) => this.context.object(obj).findOne(id),
53
- count: (obj, q) => this.context.object(obj).count(q),
54
- create: (obj, data) => this.context.object(obj).create(data),
55
- update: (obj, id, data) => this.context.object(obj).update(id, data),
56
- delete: (obj, id) => this.context.object(obj).delete(id)
57
- };
58
- }
59
-
60
- private getUserFromContext() {
61
- if (!this.context.userId) {
62
- return undefined;
63
- }
64
- // Construct user object from context, including relevant properties
65
- return {
66
- id: this.context.userId,
67
- spaceId: this.context.spaceId,
68
- roles: this.context.roles,
69
- isSystem: this.context.isSystem
70
- };
71
- }
72
-
73
-
74
- async find(query: UnifiedQuery = {}): Promise<any[]> {
75
- const hookCtx: RetrievalHookContext = {
76
- ...this.context,
77
- objectName: this.objectName,
78
- operation: 'find',
79
- api: this.getHookAPI(),
80
- user: this.getUserFromContext(),
81
- state: {},
82
- query
83
- };
84
- await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
85
-
86
- // Execute via kernel (delegates to QueryService)
87
- const kernelResult = await (this.getKernel() as any).find(this.objectName, hookCtx.query || {});
88
- const results = kernelResult.value;
89
-
90
- // Formula evaluation moved to FormulaPlugin hook
91
- hookCtx.result = results;
92
- await this.app.triggerHook('afterFind', this.objectName, hookCtx);
93
-
94
- return hookCtx.result as any[];
95
- }
96
-
97
- async findOne(idOrQuery: string | number | UnifiedQuery): Promise<any> {
98
- if (typeof idOrQuery === 'string' || typeof idOrQuery === 'number') {
99
- const hookCtx: RetrievalHookContext = {
100
- ...this.context,
101
- objectName: this.objectName,
102
- operation: 'find',
103
- api: this.getHookAPI(),
104
- user: this.getUserFromContext(),
105
- state: {},
106
- query: { _id: idOrQuery }
107
- };
108
- await this.app.triggerHook('beforeFind', this.objectName, hookCtx);
109
-
110
- // Use kernel.get() for direct ID lookup
111
- const result = await (this.getKernel() as any).get(this.objectName, String(idOrQuery));
112
-
113
- // Formula evaluation moved to FormulaPlugin hook
114
- hookCtx.result = result;
115
- await this.app.triggerHook('afterFind', this.objectName, hookCtx);
116
- return hookCtx.result;
117
- } else {
118
- const results = await this.find(idOrQuery);
119
- return results[0] || null;
120
- }
121
- }
122
-
123
- async count(filters: any): Promise<number> {
124
- // Normalize filters to UnifiedQuery format
125
- // If filters is an array, wrap it in a query object
126
- // If filters is already a UnifiedQuery (has UnifiedQuery-specific properties), use it as-is
127
- let query: UnifiedQuery;
128
- if (Array.isArray(filters)) {
129
- query = { where: filters };
130
- } else if (filters && typeof filters === 'object' && (filters.fields || filters.orderBy || filters.limit !== undefined || filters.offset !== undefined)) {
131
- // It's already a UnifiedQuery object
132
- query = filters;
133
- } else if (filters) {
134
- // It's a raw filter object, wrap it
135
- query = { where: filters };
136
- } else {
137
- query = {};
138
- }
139
-
140
- const hookCtx: RetrievalHookContext = {
141
- ...this.context,
142
- objectName: this.objectName,
143
- operation: 'count',
144
- api: this.getHookAPI(),
145
- user: this.getUserFromContext(),
146
- state: {},
147
- query
148
- };
149
- await this.app.triggerHook('beforeCount', this.objectName, hookCtx);
150
-
151
- // Execute via kernel (delegates to QueryService)
152
- let result: number;
153
- const kernel = this.getKernel() as any;
154
- if (typeof kernel.count === 'function') {
155
- result = await kernel.count(this.objectName, hookCtx.query || {});
156
- } else {
157
- // Fallback to driver if kernel doesn't support count
158
- result = await this.getDriver().count(this.objectName, hookCtx.query || {});
159
- }
160
-
161
- hookCtx.result = result;
162
- await this.app.triggerHook('afterCount', this.objectName, hookCtx);
163
- return hookCtx.result as number;
164
- }
165
-
166
- async create(doc: any): Promise<any> {
167
- const hookCtx: MutationHookContext = {
168
- ...this.context,
169
- objectName: this.objectName,
170
- operation: 'create',
171
- state: {},
172
- api: this.getHookAPI(),
173
- user: this.getUserFromContext(),
174
- data: doc
175
- };
176
- await this.app.triggerHook('beforeCreate', this.objectName, hookCtx);
177
- const finalDoc = hookCtx.data || doc;
178
-
179
- if (this.context.userId) finalDoc.created_by = this.context.userId;
180
- if (this.context.spaceId) finalDoc.space_id = this.context.spaceId;
181
-
182
- // Validation moved to ValidatorPlugin hook
183
-
184
- // Execute via kernel
185
- const result = await (this.getKernel() as any).create(this.objectName, finalDoc, this.getOptions());
186
-
187
- hookCtx.result = result;
188
- await this.app.triggerHook('afterCreate', this.objectName, hookCtx);
189
- return hookCtx.result;
190
- }
191
-
192
- async update(id: string | number, doc: any, options?: any): Promise<any> {
193
- const previousData = await this.findOne(id);
194
- const hookCtx: UpdateHookContext = {
195
- ...this.context,
196
- objectName: this.objectName,
197
- operation: 'update',
198
- state: {},
199
- api: this.getHookAPI(),
200
- user: this.getUserFromContext(),
201
- id,
202
- data: doc,
203
- previousData,
204
- isModified: (field) => hookCtx.data ? Object.prototype.hasOwnProperty.call(hookCtx.data, field) : false
205
- };
206
- await this.app.triggerHook('beforeUpdate', this.objectName, hookCtx);
207
-
208
- // Validation moved to ValidatorPlugin hook
209
-
210
- // Execute via kernel
211
- const result = await (this.getKernel() as any).update(this.objectName, String(id), hookCtx.data, this.getOptions());
212
-
213
- hookCtx.result = result;
214
- await this.app.triggerHook('afterUpdate', this.objectName, hookCtx);
215
- return hookCtx.result;
216
- }
217
-
218
- async delete(id: string | number): Promise<any> {
219
- const previousData = await this.findOne(id);
220
- const hookCtx: MutationHookContext = {
221
- ...this.context,
222
- objectName: this.objectName,
223
- operation: 'delete',
224
- state: {},
225
- api: this.getHookAPI(),
226
- user: this.getUserFromContext(),
227
- id,
228
- previousData
229
- };
230
- await this.app.triggerHook('beforeDelete', this.objectName, hookCtx);
231
-
232
- // Execute via kernel
233
- const result = await (this.getKernel() as any).delete(this.objectName, String(id), this.getOptions());
234
-
235
- hookCtx.result = result;
236
- await this.app.triggerHook('afterDelete', this.objectName, hookCtx);
237
- return hookCtx.result;
238
- }
239
-
240
- async aggregate(query: any): Promise<any> {
241
- const driver = this.getDriver();
242
- if (!driver.aggregate) throw new Error("Driver does not support aggregate");
243
-
244
- return driver.aggregate(this.objectName, query, this.getOptions());
245
- }
246
-
247
- async distinct(field: string, filters?: any): Promise<any[]> {
248
- const driver = this.getDriver();
249
- if (!driver.distinct) throw new Error("Driver does not support distinct");
250
-
251
- return driver.distinct(this.objectName, field, filters, this.getOptions());
252
- }
253
-
254
- async findOneAndUpdate(filters: any, update: any, options?: any): Promise<any> {
255
- const driver = this.getDriver();
256
- if (!driver.findOneAndUpdate) throw new Error("Driver does not support findOneAndUpdate");
257
- return driver.findOneAndUpdate(this.objectName, filters, update, this.getOptions(options));
258
- }
259
-
260
- async createMany(data: any[]): Promise<any> {
261
- const driver = this.getDriver();
262
-
263
- // Always use fallback to ensure validation and hooks are executed
264
- // This maintains ObjectQL's metadata-driven architecture where
265
- // validation rules, hooks, and permissions are enforced consistently
266
- const results = [];
267
- for (const item of data) {
268
- results.push(await this.create(item));
269
- }
270
- return results;
271
- }
272
-
273
- async updateMany(filters: any, data: any): Promise<any> {
274
- // Find all matching records and update them individually
275
- // to ensure validation and hooks are executed
276
- const records = await this.find({ where: filters });
277
- let count = 0;
278
- for (const record of records) {
279
- if (record && record._id) {
280
- await this.update(record._id, data);
281
- count++;
282
- }
283
- }
284
- return count;
285
- }
286
-
287
- async deleteMany(filters: any): Promise<any> {
288
- // Find all matching records and delete them individually
289
- // to ensure hooks are executed
290
- const records = await this.find({ where: filters });
291
- let count = 0;
292
- for (const record of records) {
293
- if (record && record._id) {
294
- await this.delete(record._id);
295
- count++;
296
- }
297
- }
298
- return count;
299
- }
300
-
301
- async execute(actionName: string, id: string | number | undefined, params: any): Promise<any> {
302
- const api: HookAPI = {
303
- find: (obj, q) => this.context.object(obj).find(q),
304
- findOne: (obj, id) => this.context.object(obj).findOne(id),
305
- count: (obj, q) => this.context.object(obj).count(q),
306
- create: (obj, data) => this.context.object(obj).create(data),
307
- update: (obj, id, data) => this.context.object(obj).update(id, data),
308
- delete: (obj, id) => this.context.object(obj).delete(id)
309
- };
310
-
311
- const ctx: ActionContext = {
312
- ...this.context,
313
- objectName: this.objectName,
314
- actionName,
315
- id,
316
- input: params,
317
- api,
318
- user: this.getUserFromContext()
319
- };
320
- return await this.app.executeAction(this.objectName, actionName, ctx);
321
- }
322
- }
6
+ export { ObjectRepository } from '@objectstack/objectql';
package/src/util.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
 
9
- import { ObjectConfig, FieldConfig, FieldType, IntrospectedSchema, IntrospectedColumn, IntrospectedTable } from '@objectql/types';
9
+ import { ObjectConfig, FieldConfig, FieldType, IntrospectedSchema } from '@objectql/types';
10
10
 
11
11
  export function toTitleCase(str: string): string {
12
12
  return str
@@ -118,7 +118,8 @@ export function convertIntrospectedSchemaToObjects(
118
118
  reference_to: foreignKey.referencedTable,
119
119
  label: toTitleCase(column.name),
120
120
  required: !column.nullable,
121
- searchable: false
121
+ searchable: false,
122
+ sortable: false
122
123
  };
123
124
  } else {
124
125
  // Regular field
@@ -131,7 +132,8 @@ export function convertIntrospectedSchemaToObjects(
131
132
  type: fieldType,
132
133
  label: toTitleCase(column.name),
133
134
  required: !column.nullable,
134
- searchable: false
135
+ searchable: false,
136
+ sortable: false
135
137
  };
136
138
 
137
139
  // Add unique constraint
@@ -22,6 +22,255 @@ export const createLogger = jest.fn(() => ({
22
22
  fatal: jest.fn(),
23
23
  }));
24
24
 
25
- export const ObjectKernel = jest.fn();
25
+ class MockMetadataRegistry {
26
+ private store = new Map<string, Map<string, any>>();
27
+
28
+ register(type: string, nameOrConfig: string | any, config?: any): void {
29
+ if (!this.store.has(type)) {
30
+ this.store.set(type, new Map());
31
+ }
32
+ const typeMap = this.store.get(type)!;
33
+ let name: string;
34
+ let item: any;
35
+ if (config) {
36
+ name = nameOrConfig as string;
37
+ item = config;
38
+ } else {
39
+ item = nameOrConfig;
40
+ name = item.id || item.name;
41
+ }
42
+ typeMap.set(name, item);
43
+ }
44
+
45
+ get<T = any>(type: string, id: string): T | undefined {
46
+ const typeMap = this.store.get(type);
47
+ const item = typeMap?.get(id);
48
+ return item?.content as T;
49
+ }
50
+
51
+ list<T = any>(type: string): T[] {
52
+ const typeMap = this.store.get(type);
53
+ if (!typeMap) return [];
54
+ return Array.from(typeMap.values()).map(item => item.content as T);
55
+ }
56
+
57
+ unregister(type: string, id: string): boolean {
58
+ const typeMap = this.store.get(type);
59
+ if (!typeMap) return false;
60
+ return typeMap.delete(id);
61
+ }
62
+
63
+ getTypes(): string[] {
64
+ return Array.from(this.store.keys());
65
+ }
66
+
67
+ getEntry(type: string, id: string): any | undefined {
68
+ const typeMap = this.store.get(type);
69
+ return typeMap ? typeMap.get(id) : undefined;
70
+ }
71
+
72
+ unregisterPackage(packageName: string): void {
73
+ for (const [_type, typeMap] of this.store.entries()) {
74
+ const toDelete: string[] = [];
75
+ for (const [id, item] of typeMap.entries()) {
76
+ if (item.packageName === packageName || item.package === packageName) {
77
+ toDelete.push(id);
78
+ }
79
+ }
80
+ toDelete.forEach(id => typeMap.delete(id));
81
+ }
82
+ }
83
+ }
84
+
85
+ class MockHookManager {
86
+ removePackage(_packageName: string): void {
87
+ // Mock implementation
88
+ }
89
+ clear(): void {
90
+ // Mock implementation
91
+ }
92
+ }
93
+
94
+ class MockActionManager {
95
+ removePackage(_packageName: string): void {
96
+ // Mock implementation
97
+ }
98
+ clear(): void {
99
+ // Mock implementation
100
+ }
101
+ }
102
+
103
+ export class ObjectKernel {
104
+ public ql: unknown = null;
105
+ public metadata: MockMetadataRegistry;
106
+ public hooks: MockHookManager;
107
+ public actions: MockActionManager;
108
+ private plugins: any[] = [];
109
+ private driver: any = null;
110
+
111
+ constructor(plugins: any[] = []) {
112
+ this.plugins = plugins;
113
+ this.metadata = new MockMetadataRegistry();
114
+ this.hooks = new MockHookManager();
115
+ this.actions = new MockActionManager();
116
+ }
117
+
118
+ setDriver(driver: any): void {
119
+ this.driver = driver;
120
+ }
121
+
122
+ async start(): Promise<void> {
123
+ for (const plugin of this.plugins) {
124
+ if (plugin.install) {
125
+ await plugin.install({ engine: this });
126
+ }
127
+ }
128
+ for (const plugin of this.plugins) {
129
+ if (plugin.onStart) {
130
+ await plugin.onStart({ engine: this });
131
+ }
132
+ }
133
+ }
134
+
135
+ async stop(): Promise<void> {
136
+ for (const plugin of this.plugins) {
137
+ if (plugin.onStop) {
138
+ await plugin.onStop({ engine: this });
139
+ }
140
+ }
141
+ }
142
+
143
+ getDriver(): any {
144
+ const driver = this.plugins.find(p =>
145
+ p.constructor.name?.includes('Driver') ||
146
+ (typeof p.find === 'function' && typeof p.create === 'function')
147
+ );
148
+ return driver || this.driver;
149
+ }
150
+
151
+ async seed(): Promise<void> {
152
+ // Mock implementation
153
+ }
154
+
155
+ async find(objectName: string, query: any): Promise<{ value: Record<string, any>[]; count: number }> {
156
+ const driver = this.getDriver();
157
+ if (driver) {
158
+ const normalizedQuery: any = {};
159
+ if (query.filters) {
160
+ normalizedQuery.where = query.filters;
161
+ } else if (query.filter) {
162
+ normalizedQuery.where = query.filter;
163
+ } else if (query.where) {
164
+ normalizedQuery.where = query.where;
165
+ }
166
+ if (query.fields) {
167
+ normalizedQuery.fields = query.fields;
168
+ }
169
+ if (query.sort) {
170
+ if (Array.isArray(query.sort) && query.sort.length > 0) {
171
+ const firstItem = query.sort[0];
172
+ if (firstItem !== undefined) {
173
+ if (Array.isArray(firstItem)) {
174
+ normalizedQuery.orderBy = query.sort.map((s: any) => ({
175
+ field: s[0],
176
+ order: s[1]
177
+ }));
178
+ } else if (typeof firstItem === 'object') {
179
+ normalizedQuery.orderBy = query.sort.flatMap((s: any) =>
180
+ Object.entries(s).map(([field, order]) => ({
181
+ field,
182
+ order: (order === -1 || order === 'desc' || order === 'DESC') ? 'desc' : 'asc'
183
+ }))
184
+ );
185
+ }
186
+ }
187
+ } else if (query.sort) {
188
+ normalizedQuery.orderBy = query.sort;
189
+ }
190
+ } else if (query.orderBy) {
191
+ normalizedQuery.orderBy = query.orderBy;
192
+ }
193
+ if (query.limit !== undefined) {
194
+ normalizedQuery.limit = query.limit;
195
+ } else if (query.top !== undefined) {
196
+ normalizedQuery.limit = query.top;
197
+ }
198
+ if (query.offset !== undefined) {
199
+ normalizedQuery.offset = query.offset;
200
+ } else if (query.skip !== undefined) {
201
+ normalizedQuery.offset = query.skip;
202
+ }
203
+ if (query.aggregations) {
204
+ normalizedQuery.aggregate = query.aggregations.map((agg: any) => ({
205
+ func: agg.function,
206
+ field: agg.field,
207
+ alias: agg.alias
208
+ }));
209
+ }
210
+ if (query.groupBy) {
211
+ normalizedQuery.groupBy = query.groupBy;
212
+ }
213
+ const results = await driver.find(objectName, normalizedQuery, {});
214
+ return { value: results, count: results.length };
215
+ }
216
+ return { value: [], count: 0 };
217
+ }
218
+
219
+ async get(objectName: string, id: string): Promise<Record<string, any>> {
220
+ const driver = this.getDriver();
221
+ if (driver) {
222
+ return await driver.findOne(objectName, id, {}, {});
223
+ }
224
+ return {};
225
+ }
226
+
227
+ async create(objectName: string, data: any): Promise<Record<string, any>> {
228
+ const driver = this.getDriver();
229
+ if (driver) {
230
+ return await driver.create(objectName, data, {});
231
+ }
232
+ return data;
233
+ }
234
+
235
+ async update(objectName: string, id: string, data: any): Promise<Record<string, any>> {
236
+ const driver = this.getDriver();
237
+ if (driver) {
238
+ return await driver.update(objectName, id, data, {});
239
+ }
240
+ return data;
241
+ }
242
+
243
+ async delete(objectName: string, id: string): Promise<boolean> {
244
+ const driver = this.getDriver();
245
+ if (driver) {
246
+ const result = await driver.delete(objectName, id, {});
247
+ return result > 0;
248
+ }
249
+ return true;
250
+ }
251
+
252
+ getMetadata(_objectName: string): any {
253
+ return {};
254
+ }
255
+
256
+ getView(_objectName: string, _viewType?: 'list' | 'form'): any {
257
+ return null;
258
+ }
259
+ }
260
+
261
+ export class ObjectStackProtocolImplementation {}
262
+
263
+ export interface PluginContext {
264
+ engine: ObjectKernel;
265
+ }
266
+
267
+ export interface ObjectQLPlugin {
268
+ name: string;
269
+ install?: (ctx: PluginContext) => void | Promise<void>;
270
+ onStart?: (ctx: PluginContext) => void | Promise<void>;
271
+ }
272
+
273
+ export { MockMetadataRegistry as MetadataRegistry };
274
+
26
275
  export const LiteKernel = jest.fn();
27
276
  export const createApiRegistryPlugin = jest.fn();