@objectql/core 3.0.1 → 4.0.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.
- package/CHANGELOG.md +17 -3
- package/README.md +31 -9
- package/dist/ai-agent.d.ts +4 -3
- package/dist/ai-agent.js +10 -3
- package/dist/ai-agent.js.map +1 -1
- package/dist/app.d.ts +29 -6
- package/dist/app.js +117 -58
- package/dist/app.js.map +1 -1
- package/dist/formula-engine.d.ts +7 -0
- package/dist/formula-engine.js +9 -2
- package/dist/formula-engine.js.map +1 -1
- package/dist/formula-plugin.d.ts +52 -0
- package/dist/formula-plugin.js +107 -0
- package/dist/formula-plugin.js.map +1 -0
- package/dist/index.d.ts +16 -3
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +89 -0
- package/dist/plugin.js +136 -0
- package/dist/plugin.js.map +1 -0
- package/dist/query/filter-translator.d.ts +39 -0
- package/dist/query/filter-translator.js +135 -0
- package/dist/query/filter-translator.js.map +1 -0
- package/dist/query/index.d.ts +22 -0
- package/dist/query/index.js +39 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/query-analyzer.d.ts +188 -0
- package/dist/query/query-analyzer.js +349 -0
- package/dist/query/query-analyzer.js.map +1 -0
- package/dist/query/query-builder.d.ts +29 -0
- package/dist/query/query-builder.js +71 -0
- package/dist/query/query-builder.js.map +1 -0
- package/dist/query/query-service.d.ts +152 -0
- package/dist/query/query-service.js +268 -0
- package/dist/query/query-service.js.map +1 -0
- package/dist/repository.d.ts +23 -2
- package/dist/repository.js +81 -14
- package/dist/repository.js.map +1 -1
- package/dist/util.d.ts +7 -0
- package/dist/util.js +18 -3
- package/dist/util.js.map +1 -1
- package/dist/validator-plugin.d.ts +56 -0
- package/dist/validator-plugin.js +106 -0
- package/dist/validator-plugin.js.map +1 -0
- package/dist/validator.d.ts +7 -0
- package/dist/validator.js +10 -8
- package/dist/validator.js.map +1 -1
- package/jest.config.js +16 -0
- package/package.json +7 -5
- package/src/ai-agent.ts +8 -0
- package/src/app.ts +136 -72
- package/src/formula-engine.ts +8 -0
- package/src/formula-plugin.ts +141 -0
- package/src/index.ts +28 -3
- package/src/plugin.ts +224 -0
- package/src/query/filter-translator.ts +148 -0
- package/src/query/index.ts +24 -0
- package/src/query/query-analyzer.ts +537 -0
- package/src/query/query-builder.ts +81 -0
- package/src/query/query-service.ts +393 -0
- package/src/repository.ts +101 -18
- package/src/util.ts +19 -3
- package/src/validator-plugin.ts +140 -0
- package/src/validator.ts +12 -5
- package/test/__mocks__/@objectstack/runtime.ts +255 -0
- package/test/app.test.ts +23 -35
- package/test/filter-syntax.test.ts +233 -0
- package/test/formula-engine.test.ts +8 -0
- package/test/formula-integration.test.ts +8 -0
- package/test/formula-plugin.test.ts +197 -0
- package/test/introspection.test.ts +8 -0
- package/test/mock-driver.ts +8 -0
- package/test/plugin-integration.test.ts +213 -0
- package/test/repository-validation.test.ts +8 -0
- package/test/repository.test.ts +8 -0
- package/test/util.test.ts +9 -1
- package/test/utils.ts +8 -0
- package/test/validator-plugin.test.ts +126 -0
- package/test/validator.test.ts +8 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/action.d.ts +0 -7
- package/dist/action.js +0 -23
- package/dist/action.js.map +0 -1
- package/dist/hook.d.ts +0 -8
- package/dist/hook.js +0 -25
- package/dist/hook.js.map +0 -1
- package/dist/object.d.ts +0 -3
- package/dist/object.js +0 -28
- package/dist/object.js.map +0 -1
- package/src/action.ts +0 -40
- package/src/hook.ts +0 -42
- package/src/object.ts +0 -26
- package/test/action.test.ts +0 -276
- package/test/hook.test.ts +0 -343
- package/test/object.test.ts +0 -183
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectQL Plugin
|
|
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 type { RuntimePlugin, RuntimeContext } from '@objectql/runtime';
|
|
10
|
+
import type { ObjectStackKernel } from '@objectql/runtime';
|
|
11
|
+
import { ValidatorPlugin, ValidatorPluginConfig } from './validator-plugin';
|
|
12
|
+
import { FormulaPlugin, FormulaPluginConfig } from './formula-plugin';
|
|
13
|
+
import { QueryService } from './query/query-service';
|
|
14
|
+
import { QueryAnalyzer } from './query/query-analyzer';
|
|
15
|
+
import type { Driver } from '@objectql/types';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Extended ObjectStack Kernel with ObjectQL services
|
|
19
|
+
*/
|
|
20
|
+
interface ExtendedKernel extends ObjectStackKernel {
|
|
21
|
+
queryService?: QueryService;
|
|
22
|
+
queryAnalyzer?: QueryAnalyzer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for the ObjectQL Plugin
|
|
27
|
+
*/
|
|
28
|
+
export interface ObjectQLPluginConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Enable repository pattern for data access
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
enableRepository?: boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Enable validation engine
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
enableValidator?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Validator plugin configuration
|
|
43
|
+
* Only used if enableValidator is not false
|
|
44
|
+
*/
|
|
45
|
+
validatorConfig?: ValidatorPluginConfig;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Enable formula engine
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
enableFormulas?: boolean;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Formula plugin configuration
|
|
55
|
+
* Only used if enableFormulas is not false
|
|
56
|
+
*/
|
|
57
|
+
formulaConfig?: FormulaPluginConfig;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Enable AI integration
|
|
61
|
+
* @default true
|
|
62
|
+
*/
|
|
63
|
+
enableAI?: boolean;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Enable query service and analyzer
|
|
67
|
+
* @default true
|
|
68
|
+
*/
|
|
69
|
+
enableQueryService?: boolean;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Datasources for query service
|
|
73
|
+
* Required if enableQueryService is true
|
|
74
|
+
*/
|
|
75
|
+
datasources?: Record<string, Driver>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* ObjectQL Plugin
|
|
80
|
+
*
|
|
81
|
+
* Implements the RuntimePlugin interface from @objectql/runtime
|
|
82
|
+
* to provide ObjectQL's enhanced features (Repository, Validator, Formula, AI)
|
|
83
|
+
* on top of the ObjectStack kernel.
|
|
84
|
+
*/
|
|
85
|
+
export class ObjectQLPlugin implements RuntimePlugin {
|
|
86
|
+
name = '@objectql/core';
|
|
87
|
+
version = '4.0.0';
|
|
88
|
+
|
|
89
|
+
constructor(private config: ObjectQLPluginConfig = {}) {
|
|
90
|
+
// Set defaults
|
|
91
|
+
this.config = {
|
|
92
|
+
enableRepository: true,
|
|
93
|
+
enableValidator: true,
|
|
94
|
+
enableFormulas: true,
|
|
95
|
+
enableAI: true,
|
|
96
|
+
enableQueryService: true,
|
|
97
|
+
...config
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Install the plugin into the kernel
|
|
103
|
+
* This is called during kernel initialization
|
|
104
|
+
*/
|
|
105
|
+
async install(ctx: RuntimeContext): Promise<void> {
|
|
106
|
+
console.log(`[${this.name}] Installing plugin...`);
|
|
107
|
+
|
|
108
|
+
const kernel = ctx.engine as ExtendedKernel;
|
|
109
|
+
|
|
110
|
+
// Register QueryService and QueryAnalyzer if enabled
|
|
111
|
+
if (this.config.enableQueryService !== false && this.config.datasources) {
|
|
112
|
+
const queryService = new QueryService(
|
|
113
|
+
this.config.datasources,
|
|
114
|
+
kernel.metadata
|
|
115
|
+
);
|
|
116
|
+
kernel.queryService = queryService;
|
|
117
|
+
|
|
118
|
+
const queryAnalyzer = new QueryAnalyzer(
|
|
119
|
+
queryService,
|
|
120
|
+
kernel.metadata
|
|
121
|
+
);
|
|
122
|
+
kernel.queryAnalyzer = queryAnalyzer;
|
|
123
|
+
|
|
124
|
+
console.log(`[${this.name}] QueryService and QueryAnalyzer registered`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Register components based on configuration
|
|
128
|
+
if (this.config.enableRepository !== false) {
|
|
129
|
+
await this.registerRepository(ctx.engine);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Install validator plugin if enabled
|
|
133
|
+
if (this.config.enableValidator !== false) {
|
|
134
|
+
const validatorPlugin = new ValidatorPlugin(this.config.validatorConfig || {});
|
|
135
|
+
await validatorPlugin.install(ctx);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Install formula plugin if enabled
|
|
139
|
+
if (this.config.enableFormulas !== false) {
|
|
140
|
+
const formulaPlugin = new FormulaPlugin(this.config.formulaConfig || {});
|
|
141
|
+
await formulaPlugin.install(ctx);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (this.config.enableAI !== false) {
|
|
145
|
+
await this.registerAI(ctx.engine);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
console.log(`[${this.name}] Plugin installed successfully`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Called when the kernel starts
|
|
153
|
+
* This is the initialization phase
|
|
154
|
+
*/
|
|
155
|
+
async onStart(ctx: RuntimeContext): Promise<void> {
|
|
156
|
+
console.log(`[${this.name}] Starting plugin...`);
|
|
157
|
+
// Additional startup logic can be added here
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Register the Repository pattern
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
private async registerRepository(kernel: ObjectStackKernel): Promise<void> {
|
|
165
|
+
if (!this.config.datasources) {
|
|
166
|
+
console.log(`[${this.name}] No datasources configured, skipping repository registration`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const datasources = this.config.datasources;
|
|
171
|
+
|
|
172
|
+
// Helper function to get the driver for an object
|
|
173
|
+
const getDriver = (objectName: string): Driver => {
|
|
174
|
+
const objectConfig = kernel.metadata.get<any>('object', objectName);
|
|
175
|
+
const datasourceName = objectConfig?.datasource || 'default';
|
|
176
|
+
const driver = datasources[datasourceName];
|
|
177
|
+
if (!driver) {
|
|
178
|
+
throw new Error(`Datasource '${datasourceName}' not found for object '${objectName}'`);
|
|
179
|
+
}
|
|
180
|
+
return driver;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// Override kernel CRUD methods to use drivers
|
|
184
|
+
kernel.create = async (objectName: string, data: any): Promise<any> => {
|
|
185
|
+
const driver = getDriver(objectName);
|
|
186
|
+
return await driver.create(objectName, data, {});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
kernel.update = async (objectName: string, id: string, data: any): Promise<any> => {
|
|
190
|
+
const driver = getDriver(objectName);
|
|
191
|
+
return await driver.update(objectName, id, data, {});
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
kernel.delete = async (objectName: string, id: string): Promise<boolean> => {
|
|
195
|
+
const driver = getDriver(objectName);
|
|
196
|
+
const result = await driver.delete(objectName, id, {});
|
|
197
|
+
return !!result;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
kernel.find = async (objectName: string, query: any): Promise<{ value: any[]; count: number }> => {
|
|
201
|
+
const driver = getDriver(objectName);
|
|
202
|
+
const value = await driver.find(objectName, query);
|
|
203
|
+
const count = value.length;
|
|
204
|
+
return { value, count };
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
kernel.get = async (objectName: string, id: string): Promise<any> => {
|
|
208
|
+
const driver = getDriver(objectName);
|
|
209
|
+
return await driver.findOne(objectName, id);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
console.log(`[${this.name}] Repository pattern registered`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Register AI integration
|
|
217
|
+
* @private
|
|
218
|
+
*/
|
|
219
|
+
private async registerAI(kernel: ObjectStackKernel): Promise<void> {
|
|
220
|
+
// TODO: Implement AI registration
|
|
221
|
+
// For now, this is a placeholder to establish the structure
|
|
222
|
+
console.log(`[${this.name}] AI integration registered`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
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 type { Filter } from '@objectql/types';
|
|
10
|
+
import { Data } from '@objectstack/spec';
|
|
11
|
+
type FilterNode = Data.FilterNode;
|
|
12
|
+
import { ObjectQLError } from '@objectql/types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Filter Translator
|
|
16
|
+
*
|
|
17
|
+
* Translates ObjectQL Filter (FilterCondition) to ObjectStack FilterNode format.
|
|
18
|
+
* Converts modern object-based syntax to legacy array-based syntax for backward compatibility.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* Input: { age: { $gte: 18 }, $or: [{ status: "active" }, { role: "admin" }] }
|
|
22
|
+
* Output: [["age", ">=", 18], "or", [["status", "=", "active"], "or", ["role", "=", "admin"]]]
|
|
23
|
+
*/
|
|
24
|
+
export class FilterTranslator {
|
|
25
|
+
/**
|
|
26
|
+
* Translate filters from ObjectQL format to ObjectStack FilterNode format
|
|
27
|
+
*/
|
|
28
|
+
translate(filters?: Filter): FilterNode | undefined {
|
|
29
|
+
if (!filters) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Backward compatibility: if it's already an array (old format), pass through
|
|
34
|
+
if (Array.isArray(filters)) {
|
|
35
|
+
return filters as unknown as FilterNode;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If it's an empty object, return undefined
|
|
39
|
+
if (typeof filters === 'object' && Object.keys(filters).length === 0) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return this.convertToNode(filters);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Recursively converts FilterCondition to FilterNode array format
|
|
48
|
+
*/
|
|
49
|
+
private convertToNode(filter: Filter): FilterNode {
|
|
50
|
+
const nodes: any[] = [];
|
|
51
|
+
|
|
52
|
+
// Process logical operators first
|
|
53
|
+
if (filter.$and) {
|
|
54
|
+
const andNodes = filter.$and.map((f: Filter) => this.convertToNode(f));
|
|
55
|
+
nodes.push(...this.interleaveWithOperator(andNodes, 'and'));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (filter.$or) {
|
|
59
|
+
const orNodes = filter.$or.map((f: Filter) => this.convertToNode(f));
|
|
60
|
+
if (nodes.length > 0) {
|
|
61
|
+
nodes.push('and');
|
|
62
|
+
}
|
|
63
|
+
nodes.push(...this.interleaveWithOperator(orNodes, 'or'));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Note: $not operator is not currently supported in the legacy FilterNode format
|
|
67
|
+
if (filter.$not) {
|
|
68
|
+
throw new ObjectQLError({
|
|
69
|
+
code: 'UNSUPPORTED_OPERATOR',
|
|
70
|
+
message: '$not operator is not supported. Use $ne for field negation instead.'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Process field conditions
|
|
75
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
76
|
+
if (field.startsWith('$')) {
|
|
77
|
+
continue; // Skip logical operators (already processed)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (nodes.length > 0) {
|
|
81
|
+
nodes.push('and');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Handle field value
|
|
85
|
+
if (value === null || value === undefined) {
|
|
86
|
+
nodes.push([field, '=', value]);
|
|
87
|
+
} else if (typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
|
|
88
|
+
// Explicit operators - multiple operators on same field are AND-ed together
|
|
89
|
+
const entries = Object.entries(value);
|
|
90
|
+
for (let i = 0; i < entries.length; i++) {
|
|
91
|
+
const [op, opValue] = entries[i];
|
|
92
|
+
|
|
93
|
+
// Add 'and' before each operator (except the very first node)
|
|
94
|
+
if (nodes.length > 0 || i > 0) {
|
|
95
|
+
nodes.push('and');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const legacyOp = this.mapOperatorToLegacy(op);
|
|
99
|
+
nodes.push([field, legacyOp, opValue]);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// Implicit equality
|
|
103
|
+
nodes.push([field, '=', value]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Return as FilterNode (type assertion for backward compatibility)
|
|
108
|
+
return (nodes.length === 1 ? nodes[0] : nodes) as unknown as FilterNode;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Interleaves filter nodes with a logical operator
|
|
113
|
+
*/
|
|
114
|
+
private interleaveWithOperator(nodes: FilterNode[], operator: string): any[] {
|
|
115
|
+
if (nodes.length === 0) return [];
|
|
116
|
+
if (nodes.length === 1) return [nodes[0]];
|
|
117
|
+
|
|
118
|
+
const result: any[] = [nodes[0]];
|
|
119
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
120
|
+
result.push(operator, nodes[i]);
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Maps modern $-prefixed operators to legacy format
|
|
127
|
+
*/
|
|
128
|
+
private mapOperatorToLegacy(operator: string): string {
|
|
129
|
+
const mapping: Record<string, string> = {
|
|
130
|
+
'$eq': '=',
|
|
131
|
+
'$ne': '!=',
|
|
132
|
+
'$gt': '>',
|
|
133
|
+
'$gte': '>=',
|
|
134
|
+
'$lt': '<',
|
|
135
|
+
'$lte': '<=',
|
|
136
|
+
'$in': 'in',
|
|
137
|
+
'$nin': 'nin',
|
|
138
|
+
'$contains': 'contains',
|
|
139
|
+
'$startsWith': 'startswith',
|
|
140
|
+
'$endsWith': 'endswith',
|
|
141
|
+
'$null': 'is_null',
|
|
142
|
+
'$exist': 'is_not_null',
|
|
143
|
+
'$between': 'between',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return mapping[operator] || operator.replace('$', '');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
* Query Module
|
|
11
|
+
*
|
|
12
|
+
* This module contains ObjectQL's query-specific functionality:
|
|
13
|
+
* - FilterTranslator: Converts ObjectQL filters to ObjectStack FilterNode
|
|
14
|
+
* - QueryBuilder: Builds ObjectStack QueryAST from ObjectQL UnifiedQuery
|
|
15
|
+
* - QueryService: Executes queries via drivers with profiling support
|
|
16
|
+
* - QueryAnalyzer: Provides query performance analysis and optimization suggestions
|
|
17
|
+
*
|
|
18
|
+
* These are the core components that differentiate ObjectQL from generic runtime systems.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export * from './filter-translator';
|
|
22
|
+
export * from './query-builder';
|
|
23
|
+
export * from './query-service';
|
|
24
|
+
export * from './query-analyzer';
|