@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +34 -0
- package/README.md +5 -2
- package/dist/app.d.ts +61 -52
- package/dist/app.js +100 -435
- package/dist/app.js.map +1 -1
- package/dist/index.d.ts +14 -20
- package/dist/index.js +21 -20
- package/dist/index.js.map +1 -1
- package/dist/kernel-factory.d.ts +38 -0
- package/dist/kernel-factory.js +38 -0
- package/dist/kernel-factory.js.map +1 -0
- package/dist/plugin.d.ts +9 -16
- package/dist/plugin.js +71 -48
- package/dist/plugin.js.map +1 -1
- package/dist/repository.d.ts +4 -31
- package/dist/repository.js +7 -283
- package/dist/repository.js.map +1 -1
- package/dist/util.js +4 -2
- package/dist/util.js.map +1 -1
- package/package.json +14 -12
- package/src/app.ts +156 -539
- package/src/index.ts +18 -42
- package/src/kernel-factory.ts +47 -0
- package/src/plugin.ts +77 -85
- package/src/repository.ts +4 -320
- package/src/util.ts +5 -3
- package/test/__mocks__/@objectstack/core.ts +250 -1
- package/test/__mocks__/@objectstack/objectql.ts +273 -0
- package/test/__mocks__/@objectstack/runtime.ts +14 -5
- package/test/introspection.test.ts +1 -1
- package/test/mock-driver.ts +5 -5
- package/test/optimizations.test.ts +2 -2
- package/test/plugin-integration.test.ts +1 -3
- package/test/utils.ts +6 -6
- package/tsconfig.json +3 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/ai/index.d.ts +0 -8
- package/dist/ai/index.js +0 -25
- package/dist/ai/index.js.map +0 -1
- package/dist/ai/registry.d.ts +0 -23
- package/dist/ai/registry.js +0 -78
- package/dist/ai/registry.js.map +0 -1
- package/dist/gateway.d.ts +0 -37
- package/dist/gateway.js +0 -93
- package/dist/gateway.js.map +0 -1
- package/dist/optimizations/CompiledHookManager.d.ts +0 -56
- package/dist/optimizations/CompiledHookManager.js +0 -170
- package/dist/optimizations/CompiledHookManager.js.map +0 -1
- package/dist/optimizations/DependencyGraph.d.ts +0 -82
- package/dist/optimizations/DependencyGraph.js +0 -211
- package/dist/optimizations/DependencyGraph.js.map +0 -1
- package/dist/optimizations/GlobalConnectionPool.d.ts +0 -89
- package/dist/optimizations/GlobalConnectionPool.js +0 -193
- package/dist/optimizations/GlobalConnectionPool.js.map +0 -1
- package/dist/optimizations/LazyMetadataLoader.d.ts +0 -75
- package/dist/optimizations/LazyMetadataLoader.js +0 -149
- package/dist/optimizations/LazyMetadataLoader.js.map +0 -1
- package/dist/optimizations/OptimizedMetadataRegistry.d.ts +0 -26
- package/dist/optimizations/OptimizedMetadataRegistry.js +0 -117
- package/dist/optimizations/OptimizedMetadataRegistry.js.map +0 -1
- package/dist/optimizations/OptimizedValidationEngine.d.ts +0 -73
- package/dist/optimizations/OptimizedValidationEngine.js +0 -141
- package/dist/optimizations/OptimizedValidationEngine.js.map +0 -1
- package/dist/optimizations/QueryCompiler.d.ts +0 -51
- package/dist/optimizations/QueryCompiler.js +0 -216
- package/dist/optimizations/QueryCompiler.js.map +0 -1
- package/dist/optimizations/SQLQueryOptimizer.d.ts +0 -96
- package/dist/optimizations/SQLQueryOptimizer.js +0 -265
- package/dist/optimizations/SQLQueryOptimizer.js.map +0 -1
- package/dist/optimizations/index.d.ts +0 -32
- package/dist/optimizations/index.js +0 -44
- package/dist/optimizations/index.js.map +0 -1
- package/dist/protocol.d.ts +0 -191
- package/dist/protocol.js +0 -272
- package/dist/protocol.js.map +0 -1
- package/dist/query/filter-translator.d.ts +0 -24
- package/dist/query/filter-translator.js +0 -38
- package/dist/query/filter-translator.js.map +0 -1
- package/dist/query/index.d.ts +0 -22
- package/dist/query/index.js +0 -39
- package/dist/query/index.js.map +0 -1
- package/dist/query/query-analyzer.d.ts +0 -186
- package/dist/query/query-analyzer.js +0 -348
- package/dist/query/query-analyzer.js.map +0 -1
- package/dist/query/query-builder.d.ts +0 -27
- package/dist/query/query-builder.js +0 -69
- package/dist/query/query-builder.js.map +0 -1
- package/dist/query/query-service.d.ts +0 -151
- package/dist/query/query-service.js +0 -272
- package/dist/query/query-service.js.map +0 -1
- package/src/ai/index.ts +0 -9
- package/src/ai/registry.ts +0 -81
- package/src/gateway.ts +0 -105
- package/src/optimizations/CompiledHookManager.ts +0 -193
- package/src/optimizations/DependencyGraph.ts +0 -255
- package/src/optimizations/GlobalConnectionPool.ts +0 -251
- package/src/optimizations/LazyMetadataLoader.ts +0 -180
- package/src/optimizations/OptimizedMetadataRegistry.ts +0 -132
- package/src/optimizations/OptimizedValidationEngine.ts +0 -172
- package/src/optimizations/QueryCompiler.ts +0 -242
- package/src/optimizations/SQLQueryOptimizer.ts +0 -329
- package/src/optimizations/index.ts +0 -34
- package/src/protocol.ts +0 -304
- package/src/query/filter-translator.ts +0 -41
- package/src/query/index.ts +0 -24
- package/src/query/query-analyzer.ts +0 -532
- package/src/query/query-builder.ts +0 -64
- package/src/query/query-service.ts +0 -397
- package/test/ai-registry.test.ts +0 -42
- package/test/app.test.ts +0 -578
- package/test/filter-syntax.test.ts +0 -233
- package/test/gateway.test.ts +0 -88
- package/test/protocol.test.ts +0 -143
- package/test/repository-validation.test.ts +0 -351
- package/test/repository.test.ts +0 -151
|
@@ -1,329 +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
|
-
* Index metadata
|
|
11
|
-
*/
|
|
12
|
-
export interface IndexMetadata {
|
|
13
|
-
name: string;
|
|
14
|
-
fields: string[];
|
|
15
|
-
unique: boolean;
|
|
16
|
-
type?: 'btree' | 'hash' | 'fulltext';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Schema with index information
|
|
21
|
-
*/
|
|
22
|
-
export interface SchemaWithIndexes {
|
|
23
|
-
name: string;
|
|
24
|
-
fields: Record<string, any>;
|
|
25
|
-
indexes?: IndexMetadata[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Query AST for optimization
|
|
30
|
-
*/
|
|
31
|
-
export interface OptimizableQueryAST {
|
|
32
|
-
object: string;
|
|
33
|
-
fields?: string[];
|
|
34
|
-
filters?: any;
|
|
35
|
-
sort?: Array<{ field: string; order: 'asc' | 'desc' }>;
|
|
36
|
-
joins?: Array<{ type: 'left' | 'inner'; table: string; on: any }>;
|
|
37
|
-
limit?: number;
|
|
38
|
-
offset?: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* SQL Query Optimizer
|
|
43
|
-
*
|
|
44
|
-
* Improvement: SQL-aware optimization with index hints and join reordering.
|
|
45
|
-
* Analyzes query patterns and schema to generate optimal SQL.
|
|
46
|
-
*
|
|
47
|
-
* Expected: 2-5x faster queries on large datasets
|
|
48
|
-
*/
|
|
49
|
-
export class SQLQueryOptimizer {
|
|
50
|
-
private schemas = new Map<string, SchemaWithIndexes>();
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Register a schema with index information
|
|
54
|
-
*/
|
|
55
|
-
registerSchema(schema: SchemaWithIndexes): void {
|
|
56
|
-
this.schemas.set(schema.name, schema);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Check if a field has an index
|
|
61
|
-
*/
|
|
62
|
-
private hasIndex(objectName: string, fieldName: string): boolean {
|
|
63
|
-
const schema = this.schemas.get(objectName);
|
|
64
|
-
if (!schema || !schema.indexes) return false;
|
|
65
|
-
|
|
66
|
-
return schema.indexes.some(index =>
|
|
67
|
-
index.fields.includes(fieldName)
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Get the best index for given filter fields
|
|
73
|
-
*/
|
|
74
|
-
private getBestIndex(objectName: string, filterFields: string[]): IndexMetadata | null {
|
|
75
|
-
const schema = this.schemas.get(objectName);
|
|
76
|
-
if (!schema || !schema.indexes) return null;
|
|
77
|
-
|
|
78
|
-
// Find indexes that cover the filter fields
|
|
79
|
-
const candidateIndexes = schema.indexes.filter(index => {
|
|
80
|
-
// Check if index covers any of the filter fields
|
|
81
|
-
return filterFields.some(field => index.fields.includes(field));
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (candidateIndexes.length === 0) return null;
|
|
85
|
-
|
|
86
|
-
// Prefer indexes that match more filter fields
|
|
87
|
-
candidateIndexes.sort((a, b) => {
|
|
88
|
-
const aMatches = a.fields.filter(f => filterFields.includes(f)).length;
|
|
89
|
-
const bMatches = b.fields.filter(f => filterFields.includes(f)).length;
|
|
90
|
-
return bMatches - aMatches;
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return candidateIndexes[0];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Extract filter fields from filter conditions
|
|
98
|
-
*/
|
|
99
|
-
private extractFilterFields(filters: any): string[] {
|
|
100
|
-
const fields: string[] = [];
|
|
101
|
-
|
|
102
|
-
const extract = (obj: any) => {
|
|
103
|
-
if (!obj || typeof obj !== 'object') return;
|
|
104
|
-
|
|
105
|
-
for (const key of Object.keys(obj)) {
|
|
106
|
-
if (!key.startsWith('$')) {
|
|
107
|
-
fields.push(key);
|
|
108
|
-
}
|
|
109
|
-
if (typeof obj[key] === 'object') {
|
|
110
|
-
extract(obj[key]);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
extract(filters);
|
|
116
|
-
return [...new Set(fields)]; // Remove duplicates
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Optimize join type based on query characteristics
|
|
121
|
-
*
|
|
122
|
-
* Note: This method expects filter fields in one of two formats:
|
|
123
|
-
* 1. Table-prefixed: "tableName.fieldName" (e.g., "accounts.type")
|
|
124
|
-
* 2. Simple field name: "fieldName" (checked against joined table schema)
|
|
125
|
-
*
|
|
126
|
-
* Multi-level qualification (e.g., "schema.table.field") is not currently supported.
|
|
127
|
-
*/
|
|
128
|
-
private optimizeJoinType(
|
|
129
|
-
join: { type: 'left' | 'inner'; table: string; on: any },
|
|
130
|
-
ast: OptimizableQueryAST
|
|
131
|
-
): 'left' | 'inner' {
|
|
132
|
-
// If we're filtering on the joined table, we can use INNER JOIN
|
|
133
|
-
if (ast.filters) {
|
|
134
|
-
const filterFields = this.extractFilterFields(ast.filters);
|
|
135
|
-
|
|
136
|
-
// Check if any filter field references the joined table
|
|
137
|
-
const hasFilterOnJoinTable = filterFields.some(field => {
|
|
138
|
-
// Handle table-prefixed fields like "accounts.type"
|
|
139
|
-
if (field.includes('.')) {
|
|
140
|
-
const parts = field.split('.');
|
|
141
|
-
// Only consider the first part as table name for simplicity
|
|
142
|
-
// This assumes format: "table.field" not "schema.table.field"
|
|
143
|
-
if (parts.length === 2 && parts[0] === join.table) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
// If more than 2 parts or table doesn't match, skip this field
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Also check if the field exists in the joined table schema (non-prefixed)
|
|
151
|
-
const schema = this.schemas.get(join.table);
|
|
152
|
-
if (schema) {
|
|
153
|
-
return schema.fields[field] !== undefined;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return false;
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
if (hasFilterOnJoinTable) {
|
|
160
|
-
return 'inner'; // Convert LEFT to INNER for better performance
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return join.type;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Optimize a query AST to SQL
|
|
169
|
-
*/
|
|
170
|
-
optimize(ast: OptimizableQueryAST): string {
|
|
171
|
-
const schema = this.schemas.get(ast.object);
|
|
172
|
-
if (!schema) {
|
|
173
|
-
// Fallback to basic SQL generation
|
|
174
|
-
return this.generateBasicSQL(ast);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
let sql = 'SELECT ';
|
|
178
|
-
|
|
179
|
-
// Fields
|
|
180
|
-
if (ast.fields && ast.fields.length > 0) {
|
|
181
|
-
sql += ast.fields.join(', ');
|
|
182
|
-
} else {
|
|
183
|
-
sql += '*';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
sql += ` FROM ${ast.object}`;
|
|
187
|
-
|
|
188
|
-
// Index hints
|
|
189
|
-
if (ast.filters) {
|
|
190
|
-
const filterFields = this.extractFilterFields(ast.filters);
|
|
191
|
-
const bestIndex = this.getBestIndex(ast.object, filterFields);
|
|
192
|
-
|
|
193
|
-
if (bestIndex) {
|
|
194
|
-
// Add index hint for MySQL/MariaDB
|
|
195
|
-
sql += ` USE INDEX (${bestIndex.name})`;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Optimized joins
|
|
200
|
-
if (ast.joins && ast.joins.length > 0) {
|
|
201
|
-
for (const join of ast.joins) {
|
|
202
|
-
const optimizedType = this.optimizeJoinType(join, ast);
|
|
203
|
-
sql += ` ${optimizedType.toUpperCase()} JOIN ${join.table}`;
|
|
204
|
-
|
|
205
|
-
// Simplified join condition
|
|
206
|
-
if (typeof join.on === 'string') {
|
|
207
|
-
sql += ` ON ${join.on}`;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// WHERE clause
|
|
213
|
-
if (ast.filters) {
|
|
214
|
-
sql += ' WHERE ' + this.filtersToSQL(ast.filters);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// ORDER BY
|
|
218
|
-
if (ast.sort && ast.sort.length > 0) {
|
|
219
|
-
sql += ' ORDER BY ';
|
|
220
|
-
sql += ast.sort.map(s => `${s.field} ${s.order.toUpperCase()}`).join(', ');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// LIMIT and OFFSET
|
|
224
|
-
if (ast.limit !== undefined) {
|
|
225
|
-
sql += ` LIMIT ${ast.limit}`;
|
|
226
|
-
}
|
|
227
|
-
if (ast.offset !== undefined) {
|
|
228
|
-
sql += ` OFFSET ${ast.offset}`;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return sql;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Convert filter object to SQL WHERE clause
|
|
236
|
-
*/
|
|
237
|
-
private filtersToSQL(filters: any): string {
|
|
238
|
-
if (typeof filters === 'string') {
|
|
239
|
-
return filters;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Simplified filter conversion
|
|
243
|
-
const conditions: string[] = [];
|
|
244
|
-
|
|
245
|
-
for (const [key, value] of Object.entries(filters)) {
|
|
246
|
-
if (key === '$and') {
|
|
247
|
-
const subconditions = (value as any[]).map(f => this.filtersToSQL(f));
|
|
248
|
-
conditions.push(`(${subconditions.join(' AND ')})`);
|
|
249
|
-
} else if (key === '$or') {
|
|
250
|
-
const subconditions = (value as any[]).map(f => this.filtersToSQL(f));
|
|
251
|
-
conditions.push(`(${subconditions.join(' OR ')})`);
|
|
252
|
-
} else if (!key.startsWith('$')) {
|
|
253
|
-
// Field condition
|
|
254
|
-
if (typeof value === 'object' && value !== null) {
|
|
255
|
-
for (const [op, val] of Object.entries(value)) {
|
|
256
|
-
switch (op) {
|
|
257
|
-
case '$eq':
|
|
258
|
-
conditions.push(`${key} = '${val}'`);
|
|
259
|
-
break;
|
|
260
|
-
case '$ne':
|
|
261
|
-
conditions.push(`${key} != '${val}'`);
|
|
262
|
-
break;
|
|
263
|
-
case '$gt':
|
|
264
|
-
conditions.push(`${key} > '${val}'`);
|
|
265
|
-
break;
|
|
266
|
-
case '$gte':
|
|
267
|
-
conditions.push(`${key} >= '${val}'`);
|
|
268
|
-
break;
|
|
269
|
-
case '$lt':
|
|
270
|
-
conditions.push(`${key} < '${val}'`);
|
|
271
|
-
break;
|
|
272
|
-
case '$lte':
|
|
273
|
-
conditions.push(`${key} <= '${val}'`);
|
|
274
|
-
break;
|
|
275
|
-
case '$in':
|
|
276
|
-
const inValues = (val as any[]).map(v => `'${v}'`).join(', ');
|
|
277
|
-
conditions.push(`${key} IN (${inValues})`);
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
} else {
|
|
282
|
-
conditions.push(`${key} = '${value}'`);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return conditions.join(' AND ');
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Fallback: generate basic SQL without optimizations
|
|
292
|
-
*/
|
|
293
|
-
private generateBasicSQL(ast: OptimizableQueryAST): string {
|
|
294
|
-
let sql = 'SELECT ';
|
|
295
|
-
|
|
296
|
-
if (ast.fields && ast.fields.length > 0) {
|
|
297
|
-
sql += ast.fields.join(', ');
|
|
298
|
-
} else {
|
|
299
|
-
sql += '*';
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
sql += ` FROM ${ast.object}`;
|
|
303
|
-
|
|
304
|
-
if (ast.filters) {
|
|
305
|
-
sql += ' WHERE ' + this.filtersToSQL(ast.filters);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (ast.sort && ast.sort.length > 0) {
|
|
309
|
-
sql += ' ORDER BY ';
|
|
310
|
-
sql += ast.sort.map(s => `${s.field} ${s.order.toUpperCase()}`).join(', ');
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (ast.limit !== undefined) {
|
|
314
|
-
sql += ` LIMIT ${ast.limit}`;
|
|
315
|
-
}
|
|
316
|
-
if (ast.offset !== undefined) {
|
|
317
|
-
sql += ` OFFSET ${ast.offset}`;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return sql;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Clear all registered schemas
|
|
325
|
-
*/
|
|
326
|
-
clearSchemas(): void {
|
|
327
|
-
this.schemas.clear();
|
|
328
|
-
}
|
|
329
|
-
}
|
|
@@ -1,34 +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
|
-
* Kernel Optimizations Module
|
|
11
|
-
*
|
|
12
|
-
* This module contains 10 key optimizations for the ObjectQL kernel:
|
|
13
|
-
*
|
|
14
|
-
* 1. OptimizedMetadataRegistry - O(k) package uninstall with secondary indexes
|
|
15
|
-
* 2. QueryCompiler - LRU cache for compiled query plans
|
|
16
|
-
* 3. CompiledHookManager - Pre-compiled hook pipelines
|
|
17
|
-
* 4. GlobalConnectionPool - Kernel-level connection pooling
|
|
18
|
-
* 5. OptimizedValidationEngine - Compiled validation rules
|
|
19
|
-
* 6. LazyMetadataLoader - On-demand metadata loading with predictive preload
|
|
20
|
-
* 7. DependencyGraph - DAG-based dependency resolution
|
|
21
|
-
* 8. SQLQueryOptimizer - SQL-aware query optimization with index hints
|
|
22
|
-
*
|
|
23
|
-
* Note: TypeScript type generation optimization (#7) and memory-mapped storage (#10)
|
|
24
|
-
* are not included in this release for compatibility and complexity reasons.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
export { OptimizedMetadataRegistry } from './OptimizedMetadataRegistry';
|
|
28
|
-
export { QueryCompiler, type CompiledQuery } from './QueryCompiler';
|
|
29
|
-
export { CompiledHookManager, type Hook } from './CompiledHookManager';
|
|
30
|
-
export { GlobalConnectionPool, type Connection, type PoolLimits } from './GlobalConnectionPool';
|
|
31
|
-
export { OptimizedValidationEngine, type ValidatorFunction, type ValidationSchema } from './OptimizedValidationEngine';
|
|
32
|
-
export { LazyMetadataLoader, type ObjectMetadata, type MetadataLoader } from './LazyMetadataLoader';
|
|
33
|
-
export { DependencyGraph, type DependencyEdge, type DependencyType } from './DependencyGraph';
|
|
34
|
-
export { SQLQueryOptimizer, type IndexMetadata, type SchemaWithIndexes, type OptimizableQueryAST } from './SQLQueryOptimizer';
|
package/src/protocol.ts
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectStack Protocol Implementation
|
|
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 { ObjectStackProtocol } from '@objectstack/spec/api';
|
|
10
|
-
import { IObjectQL } from '@objectql/types';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Bridges the ObjectStack Protocol (Specification) to the ObjectQL Engine (Implementation)
|
|
14
|
-
*/
|
|
15
|
-
export class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
16
|
-
constructor(private engine: IObjectQL) {}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get API Discovery Document
|
|
20
|
-
*/
|
|
21
|
-
async getDiscovery(args: any): Promise<any> {
|
|
22
|
-
return {
|
|
23
|
-
name: 'ObjectQL Engine',
|
|
24
|
-
version: '4.0.0',
|
|
25
|
-
protocols: ['rest', 'graphql', 'json-rpc', 'odata'],
|
|
26
|
-
auth: {
|
|
27
|
-
type: 'bearer',
|
|
28
|
-
url: '/auth/token'
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Get Metadata Types (e.g., ['object', 'action'])
|
|
35
|
-
*/
|
|
36
|
-
async getMetaTypes(args: any): Promise<{ types: string[] }> {
|
|
37
|
-
let types = ['object'];
|
|
38
|
-
if (this.engine.metadata && typeof this.engine.metadata.getTypes === 'function') {
|
|
39
|
-
types = this.engine.metadata.getTypes();
|
|
40
|
-
}
|
|
41
|
-
return { types };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Get Metadata Items for a Type
|
|
46
|
-
*/
|
|
47
|
-
async getMetaItems(args: { type: string }): Promise<{ type: string; items: any[] }> {
|
|
48
|
-
const { type } = args;
|
|
49
|
-
let items: any[] = [];
|
|
50
|
-
if (this.engine.metadata && typeof this.engine.metadata.list === 'function') {
|
|
51
|
-
items = this.engine.metadata.list(type);
|
|
52
|
-
}
|
|
53
|
-
return { type, items };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get Metadata Item
|
|
58
|
-
*/
|
|
59
|
-
async getMetaItem(args: { type: string; name: string }): Promise<any> {
|
|
60
|
-
const { type, name } = args;
|
|
61
|
-
if (this.engine.metadata && typeof this.engine.metadata.get === 'function') {
|
|
62
|
-
return this.engine.metadata.get(type, name);
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Get Cached Metadata Item
|
|
69
|
-
*/
|
|
70
|
-
async getMetaItemCached(args: { type: string; name: string }): Promise<any> {
|
|
71
|
-
// Fallback to non-cached version for now
|
|
72
|
-
return this.getMetaItem(args);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Save Metadata Item
|
|
77
|
-
*/
|
|
78
|
-
async saveMetaItem(args: { type: string; name: string; item?: any }): Promise<{ success: boolean; message?: string }> {
|
|
79
|
-
const { type, name, item } = args;
|
|
80
|
-
if (this.engine.metadata && typeof this.engine.metadata.register === 'function') {
|
|
81
|
-
const data = { ...(item || {}), name }; // Ensure name is in data
|
|
82
|
-
this.engine.metadata.register(type, data);
|
|
83
|
-
return { success: true };
|
|
84
|
-
}
|
|
85
|
-
throw new Error('Engine does not support saving metadata');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get UI View
|
|
90
|
-
*/
|
|
91
|
-
async getUiView(args: { object: string; type: 'list' | 'form' }): Promise<any> {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Find Data Records
|
|
97
|
-
*/
|
|
98
|
-
async findData(args: { object: string; query?: any }): Promise<any> {
|
|
99
|
-
const { object, query } = args;
|
|
100
|
-
|
|
101
|
-
// Use direct kernel method if available (preferred)
|
|
102
|
-
if (typeof (this.engine as any).find === 'function') {
|
|
103
|
-
const result = await (this.engine as any).find(object, query || {});
|
|
104
|
-
return result;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Fallback to createContext (if engine is IObjectQL)
|
|
108
|
-
if (typeof (this.engine as any).createContext === 'function') {
|
|
109
|
-
const ctx = (this.engine as any).createContext({ isSystem: true });
|
|
110
|
-
try {
|
|
111
|
-
const repo = ctx.object(object);
|
|
112
|
-
return await repo.find(query || {});
|
|
113
|
-
} catch (error: any) {
|
|
114
|
-
throw new Error(`Data access failed: ${error.message}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
throw new Error('Engine does not support find operation');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Count Data Records
|
|
123
|
-
*/
|
|
124
|
-
async countData(args: { object: string; query?: any }): Promise<number> {
|
|
125
|
-
const { object, query } = args;
|
|
126
|
-
// Basic fallback
|
|
127
|
-
const result = await this.findData(args);
|
|
128
|
-
return Array.isArray(result) ? result.length : (result.value ? result.value.length : 0);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Get Single Data Record
|
|
134
|
-
*/
|
|
135
|
-
async getData(args: { object: string; id: string }): Promise<any> {
|
|
136
|
-
const { object, id } = args;
|
|
137
|
-
|
|
138
|
-
if (typeof (this.engine as any).get === 'function') {
|
|
139
|
-
return await (this.engine as any).get(object, id);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (typeof (this.engine as any).createContext === 'function') {
|
|
143
|
-
const ctx = (this.engine as any).createContext({ isSystem: true });
|
|
144
|
-
try {
|
|
145
|
-
const repo = ctx.object(object);
|
|
146
|
-
return await repo.findOne(id);
|
|
147
|
-
} catch (error: any) {
|
|
148
|
-
throw new Error(`Data retrieval failed: ${error.message}`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
throw new Error('Engine does not support get operation');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Create Data Record
|
|
157
|
-
*/
|
|
158
|
-
async createData(args: { object: string; data: any }): Promise<any> {
|
|
159
|
-
const { object, data } = args;
|
|
160
|
-
|
|
161
|
-
if (typeof (this.engine as any).create === 'function') {
|
|
162
|
-
return await (this.engine as any).create(object, data);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (typeof (this.engine as any).createContext === 'function') {
|
|
166
|
-
const ctx = (this.engine as any).createContext({ isSystem: true });
|
|
167
|
-
try {
|
|
168
|
-
const repo = ctx.object(object);
|
|
169
|
-
// Protocol expects returned data
|
|
170
|
-
return await repo.create(data);
|
|
171
|
-
} catch (error: any) {
|
|
172
|
-
throw new Error(`Data creation failed: ${error.message}`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
throw new Error('Engine does not support create operation');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Update Data Record
|
|
181
|
-
*/
|
|
182
|
-
async updateData(args: { object: string; id: string; data: any }): Promise<any> {
|
|
183
|
-
const { object, id, data } = args;
|
|
184
|
-
|
|
185
|
-
if (typeof (this.engine as any).update === 'function') {
|
|
186
|
-
return await (this.engine as any).update(object, id, data);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (typeof (this.engine as any).createContext === 'function') {
|
|
190
|
-
const ctx = (this.engine as any).createContext({ isSystem: true });
|
|
191
|
-
try {
|
|
192
|
-
const repo = ctx.object(object);
|
|
193
|
-
return await repo.update(id, data);
|
|
194
|
-
} catch (error: any) {
|
|
195
|
-
throw new Error(`Data update failed: ${error.message}`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
throw new Error('Engine does not support update operation');
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Delete Data Record
|
|
204
|
-
*/
|
|
205
|
-
async deleteData(args: { object: string; id: string }): Promise<{ object: string; id: string; success: boolean }> {
|
|
206
|
-
const { object, id } = args;
|
|
207
|
-
|
|
208
|
-
if (typeof (this.engine as any).delete === 'function') {
|
|
209
|
-
const success = await (this.engine as any).delete(object, id);
|
|
210
|
-
return { object, id, success: !!success };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (typeof (this.engine as any).createContext === 'function') {
|
|
214
|
-
const ctx = (this.engine as any).createContext({ isSystem: true });
|
|
215
|
-
try {
|
|
216
|
-
const repo = ctx.object(object);
|
|
217
|
-
await repo.delete(id);
|
|
218
|
-
return { object, id, success: true };
|
|
219
|
-
} catch (error: any) {
|
|
220
|
-
throw new Error(`Data deletion failed: ${error.message}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
throw new Error('Engine does not support delete operation');
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Create Many Data Records
|
|
229
|
-
*/
|
|
230
|
-
async createManyData(args: { object: string; records: any[]; options?: any }): Promise<any> {
|
|
231
|
-
throw new Error('createManyData not implemented');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Update Many Data Records
|
|
236
|
-
*/
|
|
237
|
-
async updateManyData(args: { object: string; records: { id: string; data: any }[]; options?: any }): Promise<any> {
|
|
238
|
-
throw new Error('updateManyData not implemented');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Delete Many Data Records
|
|
243
|
-
*/
|
|
244
|
-
async deleteManyData(args: { object: string; ids: string[]; options?: any }): Promise<any> {
|
|
245
|
-
throw new Error('deleteManyData not implemented');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Batch Operations
|
|
250
|
-
*/
|
|
251
|
-
async batchData(args: { object: string; request: { operation: 'create' | 'update' | 'delete' | 'upsert'; records: any[] } }): Promise<any> {
|
|
252
|
-
throw new Error('batchData not implemented');
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Execute Action/Operation
|
|
257
|
-
*/
|
|
258
|
-
async performAction(args: { object: string; id?: string; action: string; args?: any }): Promise<any> {
|
|
259
|
-
// Not implemented in this shim yet
|
|
260
|
-
throw new Error('Action execution not implemented in protocol shim');
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* Analytics Query - Execute analytics query
|
|
265
|
-
*/
|
|
266
|
-
async analyticsQuery(args: any): Promise<any> {
|
|
267
|
-
throw new Error('analyticsQuery not implemented');
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get Analytics Metadata
|
|
272
|
-
*/
|
|
273
|
-
async getAnalyticsMeta(args: any): Promise<any> {
|
|
274
|
-
throw new Error('getAnalyticsMeta not implemented');
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Trigger Automation
|
|
279
|
-
*/
|
|
280
|
-
async triggerAutomation(args: { trigger: string; payload: Record<string, any> }): Promise<{ success: boolean; jobId?: string; result?: any }> {
|
|
281
|
-
throw new Error('triggerAutomation not implemented');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* List Spaces (Hub/Workspace Management)
|
|
286
|
-
*/
|
|
287
|
-
async listSpaces(args: any): Promise<any> {
|
|
288
|
-
throw new Error('listSpaces not implemented');
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Create Space (Hub/Workspace Management)
|
|
293
|
-
*/
|
|
294
|
-
async createSpace(args: any): Promise<any> {
|
|
295
|
-
throw new Error('createSpace not implemented');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Install Plugin (Hub/Extension Management)
|
|
300
|
-
*/
|
|
301
|
-
async installPlugin(args: any): Promise<any> {
|
|
302
|
-
throw new Error('installPlugin not implemented');
|
|
303
|
-
}
|
|
304
|
-
}
|