@hypequery/clickhouse 1.6.2 → 2.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/README-CLI.md +43 -88
- package/README.md +84 -253
- package/dist/cli/bin.js +16 -8
- package/dist/core/adapters/clickhouse-adapter.d.ts.map +1 -1
- package/dist/core/adapters/clickhouse-adapter.js +3 -2
- package/dist/core/cache/cache-manager.d.ts.map +1 -1
- package/dist/core/cache/cache-manager.js +5 -3
- package/dist/core/connection.d.ts +6 -6
- package/dist/core/connection.js +9 -9
- package/dist/core/cross-filter.js +1 -1
- package/dist/core/dialects/clickhouse-dialect.d.ts +2 -2
- package/dist/core/dialects/clickhouse-dialect.d.ts.map +1 -1
- package/dist/core/dialects/clickhouse-dialect.js +39 -22
- package/dist/core/dialects/sql-dialect.d.ts +2 -2
- package/dist/core/dialects/sql-dialect.d.ts.map +1 -1
- package/dist/core/env/auto-client.d.ts.map +1 -1
- package/dist/core/env/auto-client.js +1 -1
- package/dist/core/features/aggregations.d.ts +27 -84
- package/dist/core/features/aggregations.d.ts.map +1 -1
- package/dist/core/features/aggregations.js +59 -7
- package/dist/core/features/analytics.d.ts +5 -870
- package/dist/core/features/analytics.d.ts.map +1 -1
- package/dist/core/features/analytics.js +15 -13
- package/dist/core/features/cross-filtering.d.ts +1 -1
- package/dist/core/features/cross-filtering.d.ts.map +1 -1
- package/dist/core/features/cross-filtering.js +28 -73
- package/dist/core/features/executor.d.ts +1 -1
- package/dist/core/features/executor.d.ts.map +1 -1
- package/dist/core/features/executor.js +9 -11
- package/dist/core/features/filtering.d.ts +5 -91
- package/dist/core/features/filtering.d.ts.map +1 -1
- package/dist/core/features/filtering.js +63 -77
- package/dist/core/features/joins.d.ts +2 -19
- package/dist/core/features/joins.d.ts.map +1 -1
- package/dist/core/features/joins.js +16 -5
- package/dist/core/features/query-modifiers.d.ts +10 -109
- package/dist/core/features/query-modifiers.d.ts.map +1 -1
- package/dist/core/features/query-modifiers.js +64 -18
- package/dist/core/formatters/sql-formatter.d.ts +16 -5
- package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
- package/dist/core/formatters/sql-formatter.js +197 -93
- package/dist/core/join-relationships.d.ts +22 -5
- package/dist/core/join-relationships.d.ts.map +1 -1
- package/dist/core/join-relationships.js +1 -1
- package/dist/core/query-builder.d.ts +64 -12
- package/dist/core/query-builder.d.ts.map +1 -1
- package/dist/core/query-builder.js +213 -153
- package/dist/core/query-node.d.ts +7 -0
- package/dist/core/query-node.d.ts.map +1 -0
- package/dist/core/query-node.js +80 -0
- package/dist/core/tests/integration/setup.d.ts +3 -10
- package/dist/core/tests/integration/setup.d.ts.map +1 -1
- package/dist/core/tests/integration/setup.js +30 -249
- package/dist/core/types/select-types.d.ts +3 -0
- package/dist/core/types/select-types.d.ts.map +1 -1
- package/dist/core/utils/connection-endpoint.d.ts +3 -0
- package/dist/core/utils/connection-endpoint.d.ts.map +1 -0
- package/dist/core/utils/connection-endpoint.js +9 -0
- package/dist/core/utils/filter-application.d.ts +15 -0
- package/dist/core/utils/filter-application.d.ts.map +1 -0
- package/dist/core/utils/filter-application.js +32 -0
- package/dist/core/utils/query-config-compat.d.ts +48 -0
- package/dist/core/utils/query-config-compat.d.ts.map +1 -0
- package/dist/core/utils/query-config-compat.js +137 -0
- package/dist/core/utils/relation-application.d.ts +9 -0
- package/dist/core/utils/relation-application.d.ts.map +1 -0
- package/dist/core/utils/relation-application.js +19 -0
- package/dist/core/utils/relation-validation.d.ts +6 -0
- package/dist/core/utils/relation-validation.d.ts.map +1 -0
- package/dist/core/utils/relation-validation.js +29 -0
- package/dist/core/utils/sql-expressions.d.ts +14 -0
- package/dist/core/utils/sql-expressions.d.ts.map +1 -1
- package/dist/core/utils/sql-expressions.js +40 -0
- package/dist/core/utils/tuple-filter-validation.d.ts +3 -0
- package/dist/core/utils/tuple-filter-validation.d.ts.map +1 -0
- package/dist/core/utils/tuple-filter-validation.js +16 -0
- package/dist/index.d.ts +2 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -8
- package/dist/types/base.d.ts +89 -22
- package/dist/types/base.d.ts.map +1 -1
- package/dist/types/filters.d.ts +9 -5
- package/dist/types/filters.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/core/tests/integration/test-data.json +0 -190
- package/dist/migrations/config/index.d.ts +0 -3
- package/dist/migrations/config/index.d.ts.map +0 -1
- package/dist/migrations/config/index.js +0 -1
- package/dist/migrations/config/types.d.ts +0 -45
- package/dist/migrations/config/types.d.ts.map +0 -1
- package/dist/migrations/config/types.js +0 -28
- package/dist/migrations/diff/diff.d.ts +0 -11
- package/dist/migrations/diff/diff.d.ts.map +0 -1
- package/dist/migrations/diff/diff.js +0 -240
- package/dist/migrations/diff/index.d.ts +0 -3
- package/dist/migrations/diff/index.d.ts.map +0 -1
- package/dist/migrations/diff/index.js +0 -1
- package/dist/migrations/diff/types.d.ts +0 -74
- package/dist/migrations/diff/types.d.ts.map +0 -1
- package/dist/migrations/diff/types.js +0 -1
- package/dist/migrations/plan/index.d.ts +0 -3
- package/dist/migrations/plan/index.d.ts.map +0 -1
- package/dist/migrations/plan/index.js +0 -1
- package/dist/migrations/plan/plan.d.ts +0 -12
- package/dist/migrations/plan/plan.d.ts.map +0 -1
- package/dist/migrations/plan/plan.js +0 -416
- package/dist/migrations/plan/types.d.ts +0 -93
- package/dist/migrations/plan/types.d.ts.map +0 -1
- package/dist/migrations/plan/types.js +0 -1
- package/dist/migrations/schema/column.d.ts +0 -71
- package/dist/migrations/schema/column.d.ts.map +0 -1
- package/dist/migrations/schema/column.js +0 -123
- package/dist/migrations/schema/define.d.ts +0 -24
- package/dist/migrations/schema/define.d.ts.map +0 -1
- package/dist/migrations/schema/define.js +0 -47
- package/dist/migrations/schema/index.d.ts +0 -4
- package/dist/migrations/schema/index.d.ts.map +0 -1
- package/dist/migrations/schema/index.js +0 -2
- package/dist/migrations/schema/types.d.ts +0 -74
- package/dist/migrations/schema/types.d.ts.map +0 -1
- package/dist/migrations/schema/types.js +0 -1
- package/dist/migrations/snapshot/index.d.ts +0 -3
- package/dist/migrations/snapshot/index.d.ts.map +0 -1
- package/dist/migrations/snapshot/index.js +0 -1
- package/dist/migrations/snapshot/serialize.d.ts +0 -21
- package/dist/migrations/snapshot/serialize.d.ts.map +0 -1
- package/dist/migrations/snapshot/serialize.js +0 -127
- package/dist/migrations/snapshot/types.d.ts +0 -47
- package/dist/migrations/snapshot/types.d.ts.map +0 -1
- package/dist/migrations/snapshot/types.js +0 -1
- package/dist/migrations/sql/index.d.ts +0 -4
- package/dist/migrations/sql/index.d.ts.map +0 -1
- package/dist/migrations/sql/index.js +0 -2
- package/dist/migrations/sql/render.d.ts +0 -10
- package/dist/migrations/sql/render.d.ts.map +0 -1
- package/dist/migrations/sql/render.js +0 -347
- package/dist/migrations/sql/types.d.ts +0 -53
- package/dist/migrations/sql/types.d.ts.map +0 -1
- package/dist/migrations/sql/types.js +0 -1
- package/dist/migrations/sql/write.d.ts +0 -10
- package/dist/migrations/sql/write.d.ts.map +0 -1
- package/dist/migrations/sql/write.js +0 -35
|
@@ -9,8 +9,22 @@ import { QueryModifiersFeature } from './features/query-modifiers.js';
|
|
|
9
9
|
import { FilterValidator } from './validators/filter-validator.js';
|
|
10
10
|
import { createPredicateBuilder, } from './utils/predicate-builder.js';
|
|
11
11
|
import { CrossFilteringFeature } from './features/cross-filtering.js';
|
|
12
|
+
import { cloneSelectQueryNode, createSelectQueryNode, transformSelectQueryNode, } from './query-node.js';
|
|
12
13
|
import { executeWithCache } from './cache/cache-manager.js';
|
|
13
14
|
import { mergeCacheOptionsPartial, initializeCacheRuntime } from './cache/utils.js';
|
|
15
|
+
import { normalizeFilterApplication } from './utils/filter-application.js';
|
|
16
|
+
import { toLegacyQueryConfig } from './utils/query-config-compat.js';
|
|
17
|
+
import { applyRelationPath, resolveRelationPath } from './utils/relation-application.js';
|
|
18
|
+
const ADVANCED_IN_OPERATORS = new Set([
|
|
19
|
+
'globalIn',
|
|
20
|
+
'globalNotIn',
|
|
21
|
+
'inSubquery',
|
|
22
|
+
'globalInSubquery',
|
|
23
|
+
'inTable',
|
|
24
|
+
'globalInTable',
|
|
25
|
+
'inTuple',
|
|
26
|
+
'globalInTuple',
|
|
27
|
+
]);
|
|
14
28
|
/**
|
|
15
29
|
* Type guard to check if a config is a client-based configuration.
|
|
16
30
|
*/
|
|
@@ -23,7 +37,7 @@ export function isClientConfig(config) {
|
|
|
23
37
|
*/
|
|
24
38
|
export class QueryBuilder {
|
|
25
39
|
static relationships;
|
|
26
|
-
|
|
40
|
+
query;
|
|
27
41
|
tableName;
|
|
28
42
|
state;
|
|
29
43
|
aggregations;
|
|
@@ -37,8 +51,15 @@ export class QueryBuilder {
|
|
|
37
51
|
adapter;
|
|
38
52
|
dialect;
|
|
39
53
|
cacheOptions;
|
|
54
|
+
queryTransforms = [];
|
|
40
55
|
constructor(tableName, state, runtime, adapter, dialect) {
|
|
41
56
|
this.tableName = tableName;
|
|
57
|
+
this.query = createSelectQueryNode({
|
|
58
|
+
from: {
|
|
59
|
+
kind: 'table',
|
|
60
|
+
name: tableName,
|
|
61
|
+
},
|
|
62
|
+
});
|
|
42
63
|
this.state = state;
|
|
43
64
|
this.runtime = runtime;
|
|
44
65
|
this.adapter = adapter;
|
|
@@ -51,31 +72,98 @@ export class QueryBuilder {
|
|
|
51
72
|
this.modifiers = new QueryModifiersFeature(this);
|
|
52
73
|
this.crossFiltering = new CrossFilteringFeature(this);
|
|
53
74
|
}
|
|
54
|
-
fork(state,
|
|
75
|
+
fork(state, query) {
|
|
76
|
+
return this.transition(state, query);
|
|
77
|
+
}
|
|
78
|
+
transition(state, query) {
|
|
55
79
|
const builder = new QueryBuilder(this.tableName, state, this.runtime, this.adapter, this.dialect);
|
|
56
|
-
builder.
|
|
57
|
-
builder.cacheOptions = this.cacheOptions;
|
|
80
|
+
builder.query = cloneSelectQueryNode(query);
|
|
81
|
+
builder.cacheOptions = this.cacheOptions ? { ...this.cacheOptions } : undefined;
|
|
82
|
+
builder.queryTransforms = [...this.queryTransforms];
|
|
83
|
+
return builder;
|
|
84
|
+
}
|
|
85
|
+
cloneMutable() {
|
|
86
|
+
return this.fork(this.state, this.query);
|
|
87
|
+
}
|
|
88
|
+
assignQuery(builder, query) {
|
|
89
|
+
builder.query = cloneSelectQueryNode(query);
|
|
58
90
|
return builder;
|
|
59
91
|
}
|
|
92
|
+
updateQuery(updater) {
|
|
93
|
+
return this.assignQuery(this.cloneMutable(), updater(this.query));
|
|
94
|
+
}
|
|
95
|
+
withAliasesState(aliases) {
|
|
96
|
+
return {
|
|
97
|
+
...this.state,
|
|
98
|
+
aliases,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
buildSelectState(output) {
|
|
102
|
+
return {
|
|
103
|
+
...this.state,
|
|
104
|
+
output,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
buildSelectQuery(selections) {
|
|
108
|
+
return {
|
|
109
|
+
...this.query,
|
|
110
|
+
select: selections.map(selection => ({ kind: 'selection', selection })),
|
|
111
|
+
orderBy: this.query.orderBy?.map(({ column, direction }) => ({
|
|
112
|
+
kind: 'order-by-item',
|
|
113
|
+
column: String(column),
|
|
114
|
+
direction,
|
|
115
|
+
})),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
createDetachedBuilder() {
|
|
119
|
+
const builder = new QueryBuilder(this.tableName, this.state, this.runtime, this.adapter, this.dialect);
|
|
120
|
+
builder.query = createSelectQueryNode();
|
|
121
|
+
builder.cacheOptions = this.cacheOptions ? { ...this.cacheOptions } : undefined;
|
|
122
|
+
builder.queryTransforms = [...this.queryTransforms];
|
|
123
|
+
return builder;
|
|
124
|
+
}
|
|
125
|
+
runDraftCallback(seed, callback) {
|
|
126
|
+
let current = seed;
|
|
127
|
+
// Group callbacks expect fluent chaining, so the draft keeps rebinding
|
|
128
|
+
// method calls to the latest immutable builder instance produced so far.
|
|
129
|
+
const draft = new Proxy(seed, {
|
|
130
|
+
get: (_target, prop, receiver) => {
|
|
131
|
+
const value = Reflect.get(current, prop, receiver);
|
|
132
|
+
if (typeof value !== 'function') {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
return (...args) => {
|
|
136
|
+
const result = value.apply(current, args);
|
|
137
|
+
if (result instanceof QueryBuilder) {
|
|
138
|
+
current = result;
|
|
139
|
+
return draft;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
callback(draft);
|
|
146
|
+
return current;
|
|
147
|
+
}
|
|
60
148
|
debug() {
|
|
61
149
|
console.log('Current Type:', {
|
|
62
150
|
state: this.state,
|
|
63
|
-
|
|
151
|
+
query: this.query
|
|
64
152
|
});
|
|
65
153
|
return this;
|
|
66
154
|
}
|
|
67
155
|
cache(options) {
|
|
156
|
+
const next = this.cloneMutable();
|
|
68
157
|
if (options === false) {
|
|
69
|
-
|
|
70
|
-
return
|
|
158
|
+
next.cacheOptions = { mode: 'no-store', ttlMs: 0, staleTtlMs: 0, cacheTimeMs: 0 };
|
|
159
|
+
return next;
|
|
71
160
|
}
|
|
72
|
-
|
|
73
|
-
return
|
|
161
|
+
next.cacheOptions = mergeCacheOptionsPartial(next.cacheOptions, options);
|
|
162
|
+
return next;
|
|
74
163
|
}
|
|
75
164
|
// --- Analytics Helper: Add a CTE.
|
|
76
165
|
withCTE(alias, subquery) {
|
|
77
|
-
this.
|
|
78
|
-
return this;
|
|
166
|
+
return this.updateQuery(() => this.analytics.addCTE(alias, subquery));
|
|
79
167
|
}
|
|
80
168
|
// --- Analytics Helper: Add a scalar WITH alias.
|
|
81
169
|
withScalar(alias, expressionBuilder) {
|
|
@@ -84,17 +172,15 @@ export class QueryBuilder {
|
|
|
84
172
|
}
|
|
85
173
|
const expression = expressionBuilder(createPredicateBuilder());
|
|
86
174
|
const nextConfig = this.analytics.addScalar(alias, expression);
|
|
175
|
+
const nextScalars = {
|
|
176
|
+
...this.state.scalars,
|
|
177
|
+
[alias]: undefined,
|
|
178
|
+
};
|
|
87
179
|
const nextState = {
|
|
88
180
|
...this.state,
|
|
89
|
-
scalars:
|
|
90
|
-
...this.state.scalars,
|
|
91
|
-
[alias]: undefined,
|
|
92
|
-
},
|
|
181
|
+
scalars: nextScalars,
|
|
93
182
|
};
|
|
94
|
-
|
|
95
|
-
builder.config = { ...nextConfig };
|
|
96
|
-
builder.cacheOptions = this.cacheOptions;
|
|
97
|
-
return builder;
|
|
183
|
+
return this.transition(nextState, nextConfig);
|
|
98
184
|
}
|
|
99
185
|
/**
|
|
100
186
|
* Groups results by a time interval using a specified ClickHouse function.
|
|
@@ -109,42 +195,35 @@ export class QueryBuilder {
|
|
|
109
195
|
* @returns The current QueryBuilder instance.
|
|
110
196
|
*/
|
|
111
197
|
groupByTimeInterval(column, interval, method = 'toStartOfInterval') {
|
|
112
|
-
this.
|
|
113
|
-
return this;
|
|
198
|
+
return this.updateQuery(() => this.analytics.addTimeInterval(String(column), interval, method, this.dialect));
|
|
114
199
|
}
|
|
115
200
|
// --- Analytics Helper: Add a raw SQL fragment.
|
|
116
201
|
raw(sql) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return this;
|
|
202
|
+
return this.updateQuery(query => ({
|
|
203
|
+
...query,
|
|
204
|
+
having: [...(query.having || []), { kind: 'having', expression: sql }],
|
|
205
|
+
}));
|
|
122
206
|
}
|
|
123
207
|
// --- Analytics Helper: Add query settings.
|
|
124
208
|
settings(opts) {
|
|
125
|
-
this.
|
|
126
|
-
|
|
209
|
+
return this.updateQuery(() => this.analytics.addSettings(opts));
|
|
210
|
+
}
|
|
211
|
+
final() {
|
|
212
|
+
return this.updateQuery(query => ({
|
|
213
|
+
...query,
|
|
214
|
+
from: {
|
|
215
|
+
kind: 'table',
|
|
216
|
+
name: this.tableName,
|
|
217
|
+
final: true,
|
|
218
|
+
},
|
|
219
|
+
}));
|
|
127
220
|
}
|
|
128
221
|
applyCrossFilters(crossFilter) {
|
|
129
|
-
|
|
130
|
-
this.config = this.crossFiltering.applyCrossFilters(normalized);
|
|
131
|
-
return this;
|
|
222
|
+
return this.crossFiltering.applyCrossFilters(crossFilter);
|
|
132
223
|
}
|
|
133
224
|
select(columnsOrAsterisk) {
|
|
134
225
|
if (columnsOrAsterisk === '*') {
|
|
135
|
-
|
|
136
|
-
...this.state,
|
|
137
|
-
output: {}
|
|
138
|
-
};
|
|
139
|
-
const nextConfig = {
|
|
140
|
-
...this.config,
|
|
141
|
-
select: ['*'],
|
|
142
|
-
orderBy: this.config.orderBy?.map(({ column, direction }) => ({
|
|
143
|
-
column: String(column),
|
|
144
|
-
direction
|
|
145
|
-
}))
|
|
146
|
-
};
|
|
147
|
-
return this.fork(nextState, nextConfig);
|
|
226
|
+
return this.fork(this.buildSelectState({}), this.buildSelectQuery(['*']));
|
|
148
227
|
}
|
|
149
228
|
const columns = columnsOrAsterisk;
|
|
150
229
|
const processedColumns = columns.map(col => {
|
|
@@ -153,19 +232,7 @@ export class QueryBuilder {
|
|
|
153
232
|
}
|
|
154
233
|
return String(col);
|
|
155
234
|
});
|
|
156
|
-
|
|
157
|
-
...this.state,
|
|
158
|
-
output: {}
|
|
159
|
-
};
|
|
160
|
-
const nextConfig = {
|
|
161
|
-
...this.config,
|
|
162
|
-
select: processedColumns,
|
|
163
|
-
orderBy: this.config.orderBy?.map(({ column, direction }) => ({
|
|
164
|
-
column: String(column),
|
|
165
|
-
direction
|
|
166
|
-
}))
|
|
167
|
-
};
|
|
168
|
-
return this.fork(nextState, nextConfig);
|
|
235
|
+
return this.fork(this.buildSelectState({}), this.buildSelectQuery(processedColumns));
|
|
169
236
|
}
|
|
170
237
|
selectConst(...columns) {
|
|
171
238
|
return this.select(columns);
|
|
@@ -185,13 +252,13 @@ export class QueryBuilder {
|
|
|
185
252
|
max(column, alias) {
|
|
186
253
|
return this.applyAggregation(column, alias, 'max', (col, finalAlias) => this.aggregations.max(col, finalAlias));
|
|
187
254
|
}
|
|
255
|
+
countDistinct(column, alias) {
|
|
256
|
+
return this.applyAggregation(column, alias, 'countDistinct', (col, finalAlias) => this.aggregations.countDistinct(col, finalAlias));
|
|
257
|
+
}
|
|
188
258
|
applyAggregation(column, alias, suffix, updater) {
|
|
189
259
|
const columnName = String(column);
|
|
190
260
|
const finalAlias = (alias || `${columnName}_${suffix}`);
|
|
191
|
-
const nextState = {
|
|
192
|
-
...this.state,
|
|
193
|
-
output: {}
|
|
194
|
-
};
|
|
261
|
+
const nextState = this.buildSelectState({});
|
|
195
262
|
const nextConfig = updater(columnName, finalAlias);
|
|
196
263
|
return this.fork(nextState, nextConfig);
|
|
197
264
|
}
|
|
@@ -256,11 +323,7 @@ export class QueryBuilder {
|
|
|
256
323
|
return;
|
|
257
324
|
}
|
|
258
325
|
// Skip validation for advanced IN operators - they handle their own validation
|
|
259
|
-
|
|
260
|
-
'globalIn', 'globalNotIn', 'inSubquery', 'globalInSubquery',
|
|
261
|
-
'inTable', 'globalInTable', 'inTuple', 'globalInTuple'
|
|
262
|
-
];
|
|
263
|
-
if (advancedInOperators.includes(operator)) {
|
|
326
|
+
if (ADVANCED_IN_OPERATORS.has(operator) || operator === 'isNull' || operator === 'isNotNull') {
|
|
264
327
|
return;
|
|
265
328
|
}
|
|
266
329
|
const columnName = String(column);
|
|
@@ -270,46 +333,25 @@ export class QueryBuilder {
|
|
|
270
333
|
const columnType = baseColumns[columnName];
|
|
271
334
|
FilterValidator.validateFilterCondition({ column: columnName, operator, value }, columnType);
|
|
272
335
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
this.
|
|
277
|
-
return this;
|
|
336
|
+
applyFilter(clause, conjunction, columnOrColumns, operator, value) {
|
|
337
|
+
const normalized = normalizeFilterApplication(clause, conjunction, columnOrColumns, operator, value, builder => builder(createPredicateBuilder()));
|
|
338
|
+
if (normalized.kind === 'expression') {
|
|
339
|
+
return this.updateQuery(() => this.filtering.addExpressionCondition(clause, conjunction, normalized.expression));
|
|
278
340
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const columns = columnOrColumns;
|
|
285
|
-
this.validateFilterValue(columns, operator, value);
|
|
286
|
-
this.config = this.filtering.addCondition('AND', columns.map(String), operator, value);
|
|
287
|
-
return this;
|
|
288
|
-
}
|
|
289
|
-
const column = columnOrColumns;
|
|
290
|
-
this.validateFilterValue(column, operator, value);
|
|
291
|
-
this.config = this.filtering.addCondition('AND', String(column), operator, value);
|
|
292
|
-
return this;
|
|
341
|
+
this.validateFilterValue(normalized.validationTarget, normalized.operator, normalized.value);
|
|
342
|
+
return this.updateQuery(() => this.filtering.addCondition(clause, conjunction, normalized.column, normalized.operator, normalized.value));
|
|
343
|
+
}
|
|
344
|
+
where(columnOrColumns, operator, value) {
|
|
345
|
+
return this.applyFilter('where', 'AND', columnOrColumns, operator, value);
|
|
293
346
|
}
|
|
294
347
|
orWhere(columnOrColumns, operator, value) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
if (Array.isArray(columnOrColumns) && (operator === 'inTuple' || operator === 'globalInTuple')) {
|
|
304
|
-
const columns = columnOrColumns;
|
|
305
|
-
this.validateFilterValue(columns, operator, value);
|
|
306
|
-
this.config = this.filtering.addCondition('OR', columns.map(String), operator, value);
|
|
307
|
-
return this;
|
|
308
|
-
}
|
|
309
|
-
const column = columnOrColumns;
|
|
310
|
-
this.validateFilterValue(column, operator, value);
|
|
311
|
-
this.config = this.filtering.addCondition('OR', String(column), operator, value);
|
|
312
|
-
return this;
|
|
348
|
+
return this.applyFilter('where', 'OR', columnOrColumns, operator, value);
|
|
349
|
+
}
|
|
350
|
+
prewhere(columnOrColumns, operator, value) {
|
|
351
|
+
return this.applyFilter('prewhere', 'AND', columnOrColumns, operator, value);
|
|
352
|
+
}
|
|
353
|
+
orPrewhere(columnOrColumns, operator, value) {
|
|
354
|
+
return this.applyFilter('prewhere', 'OR', columnOrColumns, operator, value);
|
|
313
355
|
}
|
|
314
356
|
/**
|
|
315
357
|
* Creates a parenthesized group of WHERE conditions joined with AND/OR operators.
|
|
@@ -323,10 +365,8 @@ export class QueryBuilder {
|
|
|
323
365
|
* ```
|
|
324
366
|
*/
|
|
325
367
|
whereGroup(callback) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
this.config = this.filtering.endWhereGroup();
|
|
329
|
-
return this;
|
|
368
|
+
const groupBuilder = this.runDraftCallback(this.createDetachedBuilder(), callback);
|
|
369
|
+
return this.updateQuery(() => this.filtering.addGroup('where', 'AND', groupBuilder.getQueryNode().where));
|
|
330
370
|
}
|
|
331
371
|
/**
|
|
332
372
|
* Creates a parenthesized group of WHERE conditions joined with OR operator.
|
|
@@ -340,10 +380,8 @@ export class QueryBuilder {
|
|
|
340
380
|
* ```
|
|
341
381
|
*/
|
|
342
382
|
orWhereGroup(callback) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
this.config = this.filtering.endWhereGroup();
|
|
346
|
-
return this;
|
|
383
|
+
const groupBuilder = this.runDraftCallback(this.createDetachedBuilder(), callback);
|
|
384
|
+
return this.updateQuery(() => this.filtering.addGroup('where', 'OR', groupBuilder.getQueryNode().where));
|
|
347
385
|
}
|
|
348
386
|
/**
|
|
349
387
|
* Adds a GROUP BY clause.
|
|
@@ -356,16 +394,23 @@ export class QueryBuilder {
|
|
|
356
394
|
*/
|
|
357
395
|
groupBy(columns) {
|
|
358
396
|
const normalized = Array.isArray(columns) ? columns.map(String) : String(columns);
|
|
359
|
-
this.
|
|
360
|
-
|
|
397
|
+
return this.updateQuery(() => this.modifiers.addGroupBy(normalized));
|
|
398
|
+
}
|
|
399
|
+
arrayJoin(column) {
|
|
400
|
+
return this.updateQuery(() => this.modifiers.addArrayJoin('ARRAY', String(column)));
|
|
401
|
+
}
|
|
402
|
+
leftArrayJoin(column) {
|
|
403
|
+
return this.updateQuery(() => this.modifiers.addArrayJoin('LEFT ARRAY', String(column)));
|
|
361
404
|
}
|
|
362
405
|
limit(count) {
|
|
363
|
-
this.
|
|
364
|
-
|
|
406
|
+
return this.updateQuery(() => this.modifiers.addLimit(count));
|
|
407
|
+
}
|
|
408
|
+
limitBy(count, by) {
|
|
409
|
+
const normalized = Array.isArray(by) ? by.map(String) : String(by);
|
|
410
|
+
return this.updateQuery(() => this.modifiers.addLimitBy(count, normalized));
|
|
365
411
|
}
|
|
366
412
|
offset(count) {
|
|
367
|
-
this.
|
|
368
|
-
return this;
|
|
413
|
+
return this.updateQuery(() => this.modifiers.addOffset(count));
|
|
369
414
|
}
|
|
370
415
|
/**
|
|
371
416
|
* Adds an ORDER BY clause.
|
|
@@ -378,8 +423,7 @@ export class QueryBuilder {
|
|
|
378
423
|
* ```
|
|
379
424
|
*/
|
|
380
425
|
orderBy(column, direction = 'ASC') {
|
|
381
|
-
this.
|
|
382
|
-
return this;
|
|
426
|
+
return this.updateQuery(() => this.modifiers.addOrderBy(String(column), direction));
|
|
383
427
|
}
|
|
384
428
|
/**
|
|
385
429
|
* Adds a HAVING clause for filtering grouped results.
|
|
@@ -391,12 +435,31 @@ export class QueryBuilder {
|
|
|
391
435
|
* ```
|
|
392
436
|
*/
|
|
393
437
|
having(condition, parameters) {
|
|
394
|
-
this.
|
|
395
|
-
return this;
|
|
438
|
+
return this.updateQuery(() => this.modifiers.addHaving(condition, parameters));
|
|
396
439
|
}
|
|
397
440
|
distinct() {
|
|
398
|
-
this.
|
|
399
|
-
|
|
441
|
+
return this.updateQuery(() => this.modifiers.setDistinct());
|
|
442
|
+
}
|
|
443
|
+
withTotals() {
|
|
444
|
+
return this.updateQuery(() => this.modifiers.setWithTotals());
|
|
445
|
+
}
|
|
446
|
+
whereNull(column) {
|
|
447
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'AND', String(column), 'isNull', null));
|
|
448
|
+
}
|
|
449
|
+
whereNotNull(column) {
|
|
450
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'AND', String(column), 'isNotNull', null));
|
|
451
|
+
}
|
|
452
|
+
orWhereNull(column) {
|
|
453
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'OR', String(column), 'isNull', null));
|
|
454
|
+
}
|
|
455
|
+
orWhereNotNull(column) {
|
|
456
|
+
return this.updateQuery(() => this.filtering.addCondition('where', 'OR', String(column), 'isNotNull', null));
|
|
457
|
+
}
|
|
458
|
+
prewhereNull(column) {
|
|
459
|
+
return this.updateQuery(() => this.filtering.addCondition('prewhere', 'AND', String(column), 'isNull', null));
|
|
460
|
+
}
|
|
461
|
+
prewhereNotNull(column) {
|
|
462
|
+
return this.updateQuery(() => this.filtering.addCondition('prewhere', 'AND', String(column), 'isNotNull', null));
|
|
400
463
|
}
|
|
401
464
|
whereBetween(column, [min, max]) {
|
|
402
465
|
if (min === null || max === null) {
|
|
@@ -417,46 +480,43 @@ export class QueryBuilder {
|
|
|
417
480
|
return this.applyJoin('FULL', table, leftColumn, rightColumn, alias);
|
|
418
481
|
}
|
|
419
482
|
applyJoin(type, table, leftColumn, rightColumn, alias) {
|
|
420
|
-
const
|
|
421
|
-
...this.state,
|
|
422
|
-
|
|
423
|
-
|
|
483
|
+
const nextAliases = (alias
|
|
484
|
+
? { ...this.state.aliases, [alias]: table }
|
|
485
|
+
: this.state.aliases);
|
|
486
|
+
const nextState = this.withAliasesState(nextAliases);
|
|
424
487
|
const nextConfig = this.joins.addJoin(type, table, String(leftColumn), rightColumn, alias);
|
|
425
|
-
return this.
|
|
488
|
+
return this.transition(nextState, nextConfig);
|
|
426
489
|
}
|
|
427
|
-
|
|
490
|
+
/**
|
|
491
|
+
* @deprecated Prefer `getQueryNode()` for inspection or `toQueryNode()` for the
|
|
492
|
+
* transformed query tree used during compilation.
|
|
493
|
+
*/
|
|
428
494
|
getConfig() {
|
|
429
|
-
return this.
|
|
495
|
+
return toLegacyQueryConfig(this.getQueryNode());
|
|
496
|
+
}
|
|
497
|
+
toQueryNode() {
|
|
498
|
+
return transformSelectQueryNode(this.query, this.queryTransforms);
|
|
499
|
+
}
|
|
500
|
+
getQueryNode() {
|
|
501
|
+
return cloneSelectQueryNode(this.query);
|
|
430
502
|
}
|
|
431
503
|
static setJoinRelationships(relationships) {
|
|
432
504
|
this.relationships = relationships;
|
|
433
505
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
if (!relationships) {
|
|
440
|
-
throw new Error('Join relationships have not been initialized. Call QueryBuilder.setJoinRelationships first.');
|
|
441
|
-
}
|
|
442
|
-
const path = relationships.get(name);
|
|
443
|
-
if (!path) {
|
|
444
|
-
throw new Error(`Join relationship '${name}' not found`);
|
|
445
|
-
}
|
|
446
|
-
const applyJoin = (joinPath) => {
|
|
506
|
+
withRelation(nameOrPath, options) {
|
|
507
|
+
const next = this.cloneMutable();
|
|
508
|
+
const { path, label } = resolveRelationPath(nameOrPath, QueryBuilder.relationships);
|
|
509
|
+
next.query = applyRelationPath(next.query, path, options, (currentQuery, joinPath, relationOptions) => {
|
|
510
|
+
next.query = currentQuery;
|
|
447
511
|
const type = options?.type || joinPath.type || 'INNER';
|
|
448
|
-
const alias =
|
|
512
|
+
const alias = relationOptions?.alias || joinPath.alias;
|
|
513
|
+
const leftColumn = String(joinPath.leftColumn);
|
|
514
|
+
const leftSource = String(joinPath.from);
|
|
449
515
|
const table = String(joinPath.to);
|
|
450
516
|
const rightColumn = `${table}.${joinPath.rightColumn}`;
|
|
451
|
-
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
path.forEach(applyJoin);
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
applyJoin(path);
|
|
458
|
-
}
|
|
459
|
-
return this;
|
|
517
|
+
return next.joins.addJoin(type, table, leftColumn, rightColumn, alias, leftSource);
|
|
518
|
+
}, label);
|
|
519
|
+
return next;
|
|
460
520
|
}
|
|
461
521
|
}
|
|
462
522
|
export function createQueryBuilder(config) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExprNode, QueryConfig, SelectQueryNode } from '../types/index.js';
|
|
2
|
+
export declare function cloneExprNode(expr?: ExprNode): ExprNode | undefined;
|
|
3
|
+
export declare function createSelectQueryNode<TOutput, TSchema>(config?: QueryConfig<TOutput, TSchema>): SelectQueryNode<TOutput, TSchema>;
|
|
4
|
+
export declare function cloneSelectQueryNode<TOutput, TSchema>(query: SelectQueryNode<TOutput, TSchema>): SelectQueryNode<TOutput, TSchema>;
|
|
5
|
+
export type QueryNodeTransform<TOutput, TSchema> = (query: SelectQueryNode<TOutput, TSchema>) => SelectQueryNode<TOutput, TSchema>;
|
|
6
|
+
export declare function transformSelectQueryNode<TOutput, TSchema>(query: SelectQueryNode<TOutput, TSchema>, transforms: ReadonlyArray<QueryNodeTransform<TOutput, TSchema>>): SelectQueryNode<TOutput, TSchema>;
|
|
7
|
+
//# sourceMappingURL=query-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-node.d.ts","sourceRoot":"","sources":["../../src/core/query-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,QAAQ,EACR,WAAW,EACX,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAY3B,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAgCnE;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EACpD,MAAM,GAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAM,GACzC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CA+BnC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EACnD,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,GACvC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAEnC;AAED,MAAM,MAAM,kBAAkB,CAAC,OAAO,EAAE,OAAO,IAAI,CACjD,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,KACrC,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAEvC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,EACvD,KAAK,EAAE,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,EACxC,UAAU,EAAE,aAAa,CAAC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,GAC9D,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,CAKnC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function cloneConditionValue(value) {
|
|
2
|
+
if (typeof value === 'string') {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
return value.map(item => Array.isArray(item) ? item.map(inner => ({ ...inner })) : { ...item });
|
|
7
|
+
}
|
|
8
|
+
return { ...value };
|
|
9
|
+
}
|
|
10
|
+
export function cloneExprNode(expr) {
|
|
11
|
+
if (!expr)
|
|
12
|
+
return undefined;
|
|
13
|
+
switch (expr.kind) {
|
|
14
|
+
case 'condition':
|
|
15
|
+
return { ...expr, value: cloneConditionValue(expr.value) };
|
|
16
|
+
case 'raw':
|
|
17
|
+
return {
|
|
18
|
+
...expr,
|
|
19
|
+
parameters: expr.parameters.map(parameter => ({ ...parameter })),
|
|
20
|
+
};
|
|
21
|
+
case 'group':
|
|
22
|
+
return {
|
|
23
|
+
...expr,
|
|
24
|
+
expression: cloneExprNode(expr.expression),
|
|
25
|
+
};
|
|
26
|
+
case 'logical':
|
|
27
|
+
return {
|
|
28
|
+
...expr,
|
|
29
|
+
conditions: expr.conditions.map(condition => cloneExprNode(condition)),
|
|
30
|
+
};
|
|
31
|
+
case 'sequence':
|
|
32
|
+
return {
|
|
33
|
+
...expr,
|
|
34
|
+
items: expr.items.map(item => ({
|
|
35
|
+
...item,
|
|
36
|
+
expression: cloneExprNode(item.expression),
|
|
37
|
+
})),
|
|
38
|
+
};
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(`Unsupported expression kind: ${String(expr.kind)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function createSelectQueryNode(config = {}) {
|
|
44
|
+
return {
|
|
45
|
+
kind: 'select-query',
|
|
46
|
+
from: config.from ? { ...config.from } : undefined,
|
|
47
|
+
select: config.select ? config.select.map(item => ({ ...item })) : undefined,
|
|
48
|
+
arrayJoins: config.arrayJoins ? config.arrayJoins.map(item => ({ ...item })) : undefined,
|
|
49
|
+
prewhere: cloneExprNode(config.prewhere),
|
|
50
|
+
where: cloneExprNode(config.where),
|
|
51
|
+
groupBy: config.groupBy ? config.groupBy.map(item => ({ ...item })) : undefined,
|
|
52
|
+
withTotals: config.withTotals,
|
|
53
|
+
having: config.having
|
|
54
|
+
? config.having.map(item => ({
|
|
55
|
+
...item,
|
|
56
|
+
parameters: item.parameters?.map(parameter => ({ ...parameter })),
|
|
57
|
+
}))
|
|
58
|
+
: undefined,
|
|
59
|
+
limitBy: config.limitBy
|
|
60
|
+
? {
|
|
61
|
+
...config.limitBy,
|
|
62
|
+
by: [...config.limitBy.by],
|
|
63
|
+
}
|
|
64
|
+
: undefined,
|
|
65
|
+
limit: config.limit,
|
|
66
|
+
offset: config.offset,
|
|
67
|
+
distinct: config.distinct,
|
|
68
|
+
orderBy: config.orderBy ? config.orderBy.map(item => ({ ...item })) : undefined,
|
|
69
|
+
joins: config.joins ? config.joins.map(item => ({ ...item })) : undefined,
|
|
70
|
+
ctes: config.ctes ? config.ctes.map(item => ({ ...item })) : undefined,
|
|
71
|
+
unionQueries: config.unionQueries ? [...config.unionQueries] : undefined,
|
|
72
|
+
settings: config.settings ? { ...config.settings } : undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function cloneSelectQueryNode(query) {
|
|
76
|
+
return createSelectQueryNode(query);
|
|
77
|
+
}
|
|
78
|
+
export function transformSelectQueryNode(query, transforms) {
|
|
79
|
+
return transforms.reduce((current, transform) => transform(current), cloneSelectQueryNode(query));
|
|
80
|
+
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
host: string;
|
|
3
|
-
user: string;
|
|
4
|
-
password: string;
|
|
5
|
-
database: string;
|
|
6
|
-
};
|
|
1
|
+
import { CLICKHOUSE_CONTAINER_NAME, TEST_CONNECTION_CONFIG, TEST_DATA } from '../../../../../../testing/clickhouse/harness.mjs';
|
|
7
2
|
export declare const initializeTestConnection: () => Promise<{
|
|
8
3
|
cache: import("../../cache/controller.js").CacheController;
|
|
9
4
|
adapter: import("../../adapters/database-adapter.js").DatabaseAdapter;
|
|
@@ -12,10 +7,7 @@ export declare const initializeTestConnection: () => Promise<{
|
|
|
12
7
|
table<TableName extends never>(tableName: TableName): import("../../query-builder.js").SelectQB<import("../../types/builder-state.js").SchemaDefinition<unknown>, TableName, import("../../../index.js").TableRecord<import("../../types/builder-state.js").SchemaDefinition<unknown>[TableName]>, TableName>;
|
|
13
8
|
}>;
|
|
14
9
|
export declare const ensureConnectionInitialized: () => import("@clickhouse/client").ClickHouseClient | import("@clickhouse/client-web").ClickHouseClient;
|
|
15
|
-
export declare const isDockerAvailable: () => Promise<boolean>;
|
|
16
|
-
export declare const isDockerComposeAvailable: () => Promise<boolean>;
|
|
17
10
|
export declare const isContainerRunning: (containerName: string) => Promise<boolean>;
|
|
18
|
-
export declare const isClickHouseReady: () => Promise<boolean>;
|
|
19
11
|
export declare const startClickHouseContainer: () => Promise<void>;
|
|
20
12
|
export declare const waitForClickHouse: (maxAttempts?: number, retryInterval?: number) => Promise<void>;
|
|
21
13
|
export declare const stopClickHouseContainer: () => Promise<void>;
|
|
@@ -27,6 +19,7 @@ export interface TestSchemaType {
|
|
|
27
19
|
price: number;
|
|
28
20
|
created_at: string;
|
|
29
21
|
is_active: boolean;
|
|
22
|
+
tags: string[];
|
|
30
23
|
}>;
|
|
31
24
|
users: Array<{
|
|
32
25
|
id: number;
|
|
@@ -45,6 +38,6 @@ export interface TestSchemaType {
|
|
|
45
38
|
created_at: string;
|
|
46
39
|
}>;
|
|
47
40
|
}
|
|
48
|
-
export
|
|
41
|
+
export { CLICKHOUSE_CONTAINER_NAME, TEST_CONNECTION_CONFIG, TEST_DATA };
|
|
49
42
|
export declare const setupTestDatabase: () => Promise<void>;
|
|
50
43
|
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACtB,SAAS,EASV,MAAM,kDAAkD,CAAC;AA4B1D,eAAO,MAAM,wBAAwB;;;;;;EAyBpC,CAAC;AAGF,eAAO,MAAM,2BAA2B,yGAevC,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,OAAO,CAE/E,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CAO7D,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAC5B,oBAAgB,EAChB,sBAAoB,KACnB,OAAO,CAAC,IAAI,CAOd,CAAC;AAGF,eAAO,MAAM,uBAAuB,QAAa,OAAO,CAAC,IAAI,CAU5D,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;QACnB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAED,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC;AAKxE,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAuBtD,CAAC"}
|