@visactor/vbi 0.4.15 → 0.4.17

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 (66) hide show
  1. package/dist/builder/{sub-builders → features}/chart-type/chart-type-builder.d.ts +1 -1
  2. package/dist/builder/{sub-builders → features}/dimensions/dim-builder.d.ts +11 -11
  3. package/dist/builder/{sub-builders → features}/dimensions/dim-node-builder.d.ts +5 -1
  4. package/dist/builder/features/dimensions/dimension-utils.d.ts +4 -0
  5. package/dist/builder/{sub-builders/havingFilters → features/havingFilter}/having-builder.d.ts +15 -15
  6. package/dist/builder/{sub-builders/havingFilters → features/havingFilter}/having-group-builder.d.ts +4 -3
  7. package/dist/builder/{sub-builders/havingFilters → features/havingFilter}/having-node-builder.d.ts +2 -2
  8. package/dist/builder/features/havingFilter/having-utils.d.ts +9 -0
  9. package/dist/builder/features/havingFilter/index.d.ts +3 -0
  10. package/dist/builder/features/index.d.ts +9 -0
  11. package/dist/builder/features/limit/index.d.ts +1 -0
  12. package/dist/builder/features/limit/limit-builder.d.ts +31 -0
  13. package/dist/builder/features/locale/index.d.ts +1 -0
  14. package/dist/builder/features/locale/locale-builder.d.ts +31 -0
  15. package/dist/builder/{sub-builders → features}/measures/mea-builder.d.ts +10 -10
  16. package/dist/builder/{sub-builders → features}/measures/mea-node-builder.d.ts +5 -1
  17. package/dist/builder/features/measures/measure-utils.d.ts +4 -0
  18. package/dist/builder/features/theme/index.d.ts +1 -0
  19. package/dist/builder/features/theme/theme-builder.d.ts +31 -0
  20. package/dist/builder/features/undo-manager/index.d.ts +1 -0
  21. package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/index.d.ts +1 -1
  22. package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/where-builder.d.ts +14 -14
  23. package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/where-group-builder.d.ts +2 -1
  24. package/dist/builder/{sub-builders/whereFilters → features/whereFilter}/where-node-builder.d.ts +6 -1
  25. package/dist/builder/features/whereFilter/where-utils.d.ts +9 -0
  26. package/dist/builder/index.d.ts +2 -3
  27. package/dist/builder/modules/apply-update.d.ts +2 -0
  28. package/dist/builder/modules/build-vquery.d.ts +4 -0
  29. package/dist/builder/modules/build-vseed.d.ts +8 -0
  30. package/dist/builder/modules/build.d.ts +3 -0
  31. package/dist/builder/modules/create-builder-features.d.ts +14 -0
  32. package/dist/builder/modules/encode-state-as-update.d.ts +2 -0
  33. package/dist/builder/modules/get-schema.d.ts +5 -0
  34. package/dist/builder/modules/index.d.ts +8 -0
  35. package/dist/builder/modules/is-empty.d.ts +2 -0
  36. package/dist/builder/vbi-builder.d.ts +13 -17
  37. package/dist/index.cjs +921 -598
  38. package/dist/index.d.ts +3 -2
  39. package/dist/index.js +904 -599
  40. package/dist/types/builder/VBIInterface.d.ts +7 -5
  41. package/dist/types/dsl/dimensions/dimensions.d.ts +2 -0
  42. package/dist/types/dsl/{havingFilters → havingFilter}/having.d.ts +10 -6
  43. package/dist/types/dsl/index.d.ts +4 -5
  44. package/dist/types/dsl/measures/measures.d.ts +18 -2
  45. package/dist/types/dsl/vbi/vbi.d.ts +42 -4
  46. package/dist/types/dsl/{whereFilters → whereFilter}/filters.d.ts +10 -6
  47. package/dist/utils/filter-guards.d.ts +5 -0
  48. package/dist/utils/index.d.ts +1 -0
  49. package/dist/vbi/create-vbi.d.ts +14 -0
  50. package/dist/vbi/from/from-vbi-dsl-input.d.ts +3 -0
  51. package/dist/vbi/from/set-base-dsl-fields.d.ts +3 -0
  52. package/dist/vbi/generate-empty-dsl.d.ts +3 -0
  53. package/dist/vbi/normalize/ensure-having-group.d.ts +2 -0
  54. package/dist/vbi/normalize/ensure-where-group.d.ts +2 -0
  55. package/dist/vbi/normalize/ensure-y-array.d.ts +3 -0
  56. package/dist/vbi/normalize/to-y-map.d.ts +3 -0
  57. package/dist/vbi/normalize/types.d.ts +5 -0
  58. package/dist/vbi.d.ts +14 -0
  59. package/package.json +5 -5
  60. package/dist/builder/sub-builders/havingFilters/index.d.ts +0 -3
  61. package/dist/builder/sub-builders/index.d.ts +0 -5
  62. package/dist/builder/vbi.d.ts +0 -16
  63. /package/dist/builder/{sub-builders → features}/chart-type/index.d.ts +0 -0
  64. /package/dist/builder/{sub-builders → features}/dimensions/index.d.ts +0 -0
  65. /package/dist/builder/{sub-builders → features}/measures/index.d.ts +0 -0
  66. /package/dist/builder/{undo-manager.d.ts → features/undo-manager/undo-manager.d.ts} +0 -0
package/dist/index.js CHANGED
@@ -1,89 +1,247 @@
1
1
  import { Array as external_yjs_Array, Doc, Map as external_yjs_Map, UndoManager, applyUpdate, encodeStateAsUpdate } from "yjs";
2
+ import { pipe } from "remeda";
2
3
  import { ChartTypeEnum, findTreeNodesBy, preorderTraverse } from "@visactor/vseed";
3
4
  import { v4 } from "uuid";
4
- import { pipe } from "remeda";
5
- import { z } from "zod";
6
- class DimensionNodeBuilder {
7
- yMap;
8
- constructor(yMap){
9
- this.yMap = yMap;
10
- }
11
- getField() {
12
- return this.yMap.get('field');
13
- }
14
- setAlias(alias) {
15
- this.yMap.set('alias', alias);
16
- return this;
17
- }
18
- toJson() {
19
- return this.yMap.toJSON();
20
- }
5
+ const connectorMap = new Map();
6
+ const registerConnector = (id, connector)=>{
7
+ connectorMap.set(id, connector);
8
+ };
9
+ const getConnector = async (id)=>{
10
+ const connector = connectorMap.get(id);
11
+ if (!connector) throw new Error(`connector ${id} not registered`);
12
+ if ('function' == typeof connector) return connector();
13
+ return connector;
14
+ };
15
+ const applyUpdateToDoc = (doc, update, transactionOrigin)=>{
16
+ applyUpdate(doc, update, transactionOrigin);
17
+ };
18
+ const encodeDocStateAsUpdate = (doc, targetStateVector)=>encodeStateAsUpdate(doc, targetStateVector);
19
+ const buildVBIDSL = (dsl)=>dsl.toJSON();
20
+ const buildSelect = (queryDSL, context)=>{
21
+ const { vbiDSL } = context;
22
+ const measures = vbiDSL.measures;
23
+ const dimensions = vbiDSL.dimensions;
24
+ const result = {
25
+ ...queryDSL
26
+ };
27
+ const measureNodes = measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure));
28
+ const measureSelects = measureNodes.map((measure)=>({
29
+ field: measure.field,
30
+ alias: measure.alias,
31
+ aggr: measure.aggregate
32
+ }));
33
+ const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
34
+ const dimensionSelects = dimensionNodes.map((dimension)=>({
35
+ field: dimension.field,
36
+ alias: dimension.alias
37
+ }));
38
+ result.select = measureSelects.concat(dimensionSelects);
39
+ return result;
40
+ };
41
+ const buildGroupBy = (queryDSL, context)=>{
42
+ const result = {
43
+ ...queryDSL
44
+ };
45
+ const { vbiDSL } = context;
46
+ const dimensions = vbiDSL.dimensions;
47
+ const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
48
+ result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
49
+ return result;
50
+ };
51
+ const buildWhere = (queryDSL, context)=>{
52
+ const { vbiDSL } = context;
53
+ const whereFilter = vbiDSL.whereFilter;
54
+ if (!whereFilter || 0 === whereFilter.conditions.length) return queryDSL;
55
+ const result = {
56
+ ...queryDSL
57
+ };
58
+ result.where = mapGroupToCondition(whereFilter);
59
+ return result;
60
+ };
61
+ function isWhereGroup(clause) {
62
+ return 'op' in clause && 'conditions' in clause;
21
63
  }
22
- class DimensionsBuilder {
23
- dsl;
24
- constructor(_doc, dsl){
25
- this.dsl = dsl;
26
- }
27
- add(field, callback) {
28
- const dimension = {
29
- alias: field,
30
- field
31
- };
32
- const yMap = new external_yjs_Map();
33
- for (const [key, value] of Object.entries(dimension))yMap.set(key, value);
34
- this.dsl.get('dimensions').push([
35
- yMap
36
- ]);
37
- const node = new DimensionNodeBuilder(yMap);
38
- callback(node);
39
- return this;
40
- }
41
- remove(field) {
42
- const dimensions = this.dsl.get('dimensions');
43
- const index = dimensions.toArray().findIndex((item)=>item.get('field') === field);
44
- if (-1 !== index) this.dsl.get('dimensions').delete(index, 1);
45
- return this;
46
- }
47
- update(field, callback) {
48
- const dimensions = this.dsl.get('dimensions');
49
- const index = dimensions.toArray().findIndex((item)=>item.get('field') === field);
50
- if (-1 === index) throw new Error(`Dimension with field "${field}" not found`);
51
- const dimensionYMap = dimensions.get(index);
52
- const node = new DimensionNodeBuilder(dimensionYMap);
53
- callback(node);
54
- return this;
55
- }
56
- find(field) {
57
- const dimensions = this.dsl.get('dimensions');
58
- const index = dimensions.toArray().findIndex((item)=>item.get('field') === field);
59
- if (-1 === index) return;
60
- return new DimensionNodeBuilder(dimensions.get(index));
61
- }
62
- findAll() {
63
- const dimensions = this.dsl.get('dimensions');
64
- return dimensions.toArray().map((yMap)=>new DimensionNodeBuilder(yMap));
65
- }
66
- toJson() {
67
- return this.dsl.get('dimensions').toJSON();
68
- }
69
- observe(callback) {
70
- this.dsl.get('dimensions').observe(callback);
71
- return ()=>{
72
- this.dsl.get('dimensions').unobserve(callback);
73
- };
74
- }
75
- static isDimensionNode(node) {
76
- return 'alias' in node && !('children' in node);
64
+ function mapClauseToCondition(clause) {
65
+ if (isWhereGroup(clause)) return [
66
+ mapGroupToCondition(clause)
67
+ ];
68
+ return mapFilterToCondition(clause);
69
+ }
70
+ function mapGroupToCondition(group) {
71
+ return {
72
+ op: group.op,
73
+ conditions: group.conditions.flatMap(mapClauseToCondition)
74
+ };
75
+ }
76
+ function mapFilterToCondition(filter) {
77
+ if ('between' === filter.op || 'not between' === filter.op) return handleBetweenFilter(filter);
78
+ return handleSimpleFilter(filter);
79
+ }
80
+ function handleBetweenFilter(filter) {
81
+ const value = normalizeBetweenValue(filter.value);
82
+ const lowerCondition = void 0 !== value.min && null !== value.min && '' !== value.min ? {
83
+ field: filter.field,
84
+ op: '<' === value.leftOp ? '>' : '>=',
85
+ value: value.min
86
+ } : void 0;
87
+ const upperCondition = void 0 !== value.max && null !== value.max && '' !== value.max ? {
88
+ field: filter.field,
89
+ op: '<' === value.rightOp ? '<' : '<=',
90
+ value: value.max
91
+ } : void 0;
92
+ if ('not between' === filter.op) {
93
+ const outsideConditions = [
94
+ lowerCondition && invertLowerBound(lowerCondition),
95
+ upperCondition && invertUpperBound(upperCondition)
96
+ ].filter(Boolean);
97
+ if (outsideConditions.length <= 1) return outsideConditions;
98
+ return [
99
+ {
100
+ op: 'or',
101
+ conditions: outsideConditions
102
+ }
103
+ ];
77
104
  }
78
- static isDimensionGroup(node) {
79
- return 'children' in node;
105
+ return [
106
+ lowerCondition,
107
+ upperCondition
108
+ ].filter(Boolean);
109
+ }
110
+ function normalizeBetweenValue(value) {
111
+ if (Array.isArray(value)) return {
112
+ min: value[0],
113
+ max: value[1],
114
+ leftOp: '<=',
115
+ rightOp: '<='
116
+ };
117
+ if ('object' == typeof value && null !== value) return value;
118
+ return {};
119
+ }
120
+ function invertLowerBound(condition) {
121
+ return {
122
+ field: condition.field,
123
+ op: '>' === condition.op ? '<=' : '<',
124
+ value: condition.value
125
+ };
126
+ }
127
+ function invertUpperBound(condition) {
128
+ return {
129
+ field: condition.field,
130
+ op: '<' === condition.op ? '>=' : '>',
131
+ value: condition.value
132
+ };
133
+ }
134
+ function handleSimpleFilter(filter) {
135
+ let mappedOp = filter.op ?? '=';
136
+ const value = filter.value;
137
+ if (Array.isArray(value)) {
138
+ if ('=' === mappedOp) mappedOp = 'in';
139
+ if ('!=' === mappedOp) mappedOp = 'not in';
80
140
  }
141
+ return [
142
+ {
143
+ field: filter.field,
144
+ op: mappedOp,
145
+ value
146
+ }
147
+ ];
148
+ }
149
+ const buildHaving = (queryDSL, context)=>{
150
+ const { vbiDSL } = context;
151
+ const havingFilter = vbiDSL.havingFilter;
152
+ if (!havingFilter || 0 === havingFilter.conditions.length) return queryDSL;
153
+ const result = {
154
+ ...queryDSL
155
+ };
156
+ result.having = {
157
+ op: havingFilter.op,
158
+ conditions: havingFilter.conditions.flatMap(buildHaving_mapClauseToCondition)
159
+ };
160
+ return result;
161
+ };
162
+ function isHavingGroup(clause) {
163
+ return 'op' in clause && 'conditions' in clause;
164
+ }
165
+ function buildHaving_mapClauseToCondition(clause) {
166
+ if (isHavingGroup(clause)) return [
167
+ buildHaving_mapGroupToCondition(clause)
168
+ ];
169
+ return buildHaving_mapFilterToCondition(clause);
170
+ }
171
+ function buildHaving_mapGroupToCondition(group) {
172
+ return {
173
+ op: group.op,
174
+ conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
175
+ };
176
+ }
177
+ function buildHaving_mapFilterToCondition(filter) {
178
+ const mappedOp = filter.op ?? '=';
179
+ return [
180
+ {
181
+ field: filter.field,
182
+ op: mappedOp,
183
+ value: filter.value
184
+ }
185
+ ];
81
186
  }
187
+ const buildLimit = (queryDSL, context)=>{
188
+ const result = {
189
+ ...queryDSL
190
+ };
191
+ const limit = context.vbiDSL.limit ?? 1000;
192
+ result.limit = limit;
193
+ return result;
194
+ };
195
+ const buildVQuery = (vbiDSL, builder)=>{
196
+ const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
197
+ vbiDSL,
198
+ builder
199
+ });
200
+ return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildLimit));
201
+ };
202
+ const buildVQueryDSL = (dsl, builder)=>{
203
+ const vbiDSL = buildVBIDSL(dsl);
204
+ return buildVQuery(vbiDSL, builder);
205
+ };
206
+ const buildVSeedDSL = async ({ vbiDSL, queryDSL })=>{
207
+ const connectorId = vbiDSL.connectorId;
208
+ const connector = await getConnector(connectorId);
209
+ const schema = await connector.discoverSchema();
210
+ const queryResult = await connector.query({
211
+ queryDSL,
212
+ schema,
213
+ connectorId
214
+ });
215
+ return {
216
+ chartType: vbiDSL.chartType,
217
+ dataset: queryResult.dataset,
218
+ theme: vbiDSL.theme,
219
+ locale: vbiDSL.locale
220
+ };
221
+ };
222
+ const getCollectionLength = (value)=>{
223
+ if (value instanceof external_yjs_Array) return value.length;
224
+ if (Array.isArray(value)) return value.length;
225
+ return 0;
226
+ };
227
+ const isEmptyVBIDSL = (dsl)=>{
228
+ const dimensionsLength = getCollectionLength(dsl.get('dimensions'));
229
+ const measuresLength = getCollectionLength(dsl.get('measures'));
230
+ return 0 === dimensionsLength && 0 === measuresLength;
231
+ };
232
+ const getBuilderSchema = async (dsl)=>{
233
+ const connectorId = dsl.get('connectorId');
234
+ const connector = await getConnector(connectorId);
235
+ return connector.discoverSchema();
236
+ };
82
237
  class MeasureNodeBuilder {
83
238
  yMap;
84
239
  constructor(yMap){
85
240
  this.yMap = yMap;
86
241
  }
242
+ getId() {
243
+ return this.yMap.get('id');
244
+ }
87
245
  getField() {
88
246
  return this.yMap.get('field');
89
247
  }
@@ -99,17 +257,50 @@ class MeasureNodeBuilder {
99
257
  this.yMap.set('aggregate', aggregate);
100
258
  return this;
101
259
  }
102
- toJson() {
260
+ toJSON() {
103
261
  return this.yMap.toJSON();
104
262
  }
105
263
  }
264
+ const id_id = {
265
+ uuid: ()=>v4()
266
+ };
267
+ function isVBIFilter(clause) {
268
+ return 'field' in clause;
269
+ }
270
+ function isVBIWhereGroup(clause) {
271
+ return 'conditions' in clause;
272
+ }
273
+ function isVBIHavingFilter(clause) {
274
+ return 'field' in clause;
275
+ }
276
+ function isVBIHavingGroup(clause) {
277
+ return 'conditions' in clause;
278
+ }
279
+ const getOrCreateMeasures = (dsl)=>{
280
+ const measures = dsl.get('measures');
281
+ if (measures instanceof external_yjs_Array) return measures;
282
+ const yMeasures = new external_yjs_Array();
283
+ dsl.set('measures', yMeasures);
284
+ return yMeasures;
285
+ };
286
+ const normalizeMeasureNodeIds = (measures)=>{
287
+ measures.toArray().forEach((item)=>{
288
+ if (item instanceof external_yjs_Map && 'string' == typeof item.get('field') && !item.get('id')) item.set('id', id_id.uuid());
289
+ });
290
+ };
291
+ const locateMeasureIndexById = (measures, measureId)=>measures.toArray().findIndex((item)=>item.get('id') === measureId);
106
292
  class MeasuresBuilder {
107
293
  dsl;
108
- constructor(_doc, dsl){
294
+ constructor(doc, dsl){
109
295
  this.dsl = dsl;
296
+ doc.transact(()=>{
297
+ const measures = getOrCreateMeasures(this.dsl);
298
+ normalizeMeasureNodeIds(measures);
299
+ });
110
300
  }
111
301
  add(field, callback) {
112
302
  const measure = {
303
+ id: id_id.uuid(),
113
304
  alias: field,
114
305
  field,
115
306
  encoding: 'yAxis',
@@ -119,45 +310,50 @@ class MeasuresBuilder {
119
310
  };
120
311
  const yMap = new external_yjs_Map();
121
312
  for (const [key, value] of Object.entries(measure))yMap.set(key, value);
122
- this.dsl.get('measures').push([
313
+ const measures = getOrCreateMeasures(this.dsl);
314
+ measures.push([
123
315
  yMap
124
316
  ]);
125
317
  const node = new MeasureNodeBuilder(yMap);
126
318
  callback(node);
127
319
  return this;
128
320
  }
129
- remove(field) {
130
- const measures = this.dsl.get('measures');
131
- const index = measures.toArray().findIndex((item)=>item.get('field') === field);
132
- if (-1 !== index) this.dsl.get('measures').delete(index, 1);
321
+ remove(id) {
322
+ const measures = getOrCreateMeasures(this.dsl);
323
+ const index = locateMeasureIndexById(measures, id);
324
+ if (-1 !== index) measures.delete(index, 1);
133
325
  return this;
134
326
  }
135
- update(field, callback) {
136
- const measures = this.dsl.get('measures');
137
- const index = measures.toArray().findIndex((item)=>item.get('field') === field);
138
- if (-1 === index) throw new Error(`Measure with field "${field}" not found`);
327
+ update(id, callback) {
328
+ const measures = getOrCreateMeasures(this.dsl);
329
+ const index = locateMeasureIndexById(measures, id);
330
+ if (-1 === index) throw new Error(`Measure with id "${id}" not found`);
139
331
  const measureYMap = measures.get(index);
140
332
  const node = new MeasureNodeBuilder(measureYMap);
141
333
  callback(node);
142
334
  return this;
143
335
  }
144
- find(field) {
145
- const measures = this.dsl.get('measures');
146
- const index = measures.toArray().findIndex((item)=>item.get('field') === field);
147
- if (-1 === index) return;
148
- return new MeasureNodeBuilder(measures.get(index));
336
+ find(predicate) {
337
+ const measures = getOrCreateMeasures(this.dsl);
338
+ const items = measures.toArray();
339
+ for(let index = 0; index < items.length; index++){
340
+ const node = new MeasureNodeBuilder(items[index]);
341
+ if (predicate(node, index)) return node;
342
+ }
149
343
  }
150
344
  findAll() {
151
- const measures = this.dsl.get('measures');
345
+ const measures = getOrCreateMeasures(this.dsl);
152
346
  return measures.toArray().map((yMap)=>new MeasureNodeBuilder(yMap));
153
347
  }
154
- toJson() {
155
- return this.dsl.get('measures').toJSON();
348
+ toJSON() {
349
+ const measures = getOrCreateMeasures(this.dsl);
350
+ return measures.toJSON();
156
351
  }
157
352
  observe(callback) {
158
- this.dsl.get('measures').observe(callback);
353
+ const measures = getOrCreateMeasures(this.dsl);
354
+ measures.observe(callback);
159
355
  return ()=>{
160
- this.dsl.get('measures').unobserve(callback);
356
+ measures.unobserve(callback);
161
357
  };
162
358
  }
163
359
  static isMeasureNode(node) {
@@ -167,10 +363,7 @@ class MeasuresBuilder {
167
363
  return 'children' in node;
168
364
  }
169
365
  }
170
- const id_id = {
171
- uuid: ()=>v4()
172
- };
173
- class HavingFiltersNodeBuilder {
366
+ class DimensionNodeBuilder {
174
367
  yMap;
175
368
  constructor(yMap){
176
369
  this.yMap = yMap;
@@ -181,26 +374,210 @@ class HavingFiltersNodeBuilder {
181
374
  getField() {
182
375
  return this.yMap.get('field');
183
376
  }
184
- getOperator() {
185
- return this.yMap.get('op');
186
- }
187
- setValue(value) {
188
- this.yMap.set('value', value);
189
- return this;
190
- }
191
- setOperator(operator) {
192
- this.yMap.set('op', operator);
377
+ setAlias(alias) {
378
+ this.yMap.set('alias', alias);
193
379
  return this;
194
380
  }
195
- toJson() {
381
+ toJSON() {
196
382
  return this.yMap.toJSON();
197
383
  }
198
384
  }
199
- class HavingGroupBuilder {
200
- yMap;
201
- constructor(yMap){
202
- this.yMap = yMap;
203
- }
385
+ const getOrCreateDimensions = (dsl)=>{
386
+ const dimensions = dsl.get('dimensions');
387
+ if (dimensions instanceof external_yjs_Array) return dimensions;
388
+ const yDimensions = new external_yjs_Array();
389
+ dsl.set('dimensions', yDimensions);
390
+ return yDimensions;
391
+ };
392
+ const normalizeDimensionNodeIds = (dimensions)=>{
393
+ dimensions.toArray().forEach((item)=>{
394
+ if (item instanceof external_yjs_Map && 'string' == typeof item.get('field') && !item.get('id')) item.set('id', id_id.uuid());
395
+ });
396
+ };
397
+ const locateDimensionIndexById = (dimensions, dimensionId)=>dimensions.toArray().findIndex((item)=>item.get('id') === dimensionId);
398
+ class DimensionsBuilder {
399
+ dsl;
400
+ constructor(doc, dsl){
401
+ this.dsl = dsl;
402
+ doc.transact(()=>{
403
+ const dimensions = getOrCreateDimensions(this.dsl);
404
+ normalizeDimensionNodeIds(dimensions);
405
+ });
406
+ }
407
+ add(field, callback) {
408
+ const dimension = {
409
+ id: id_id.uuid(),
410
+ alias: field,
411
+ field
412
+ };
413
+ const yMap = new external_yjs_Map();
414
+ for (const [key, value] of Object.entries(dimension))yMap.set(key, value);
415
+ const dimensions = getOrCreateDimensions(this.dsl);
416
+ dimensions.push([
417
+ yMap
418
+ ]);
419
+ const node = new DimensionNodeBuilder(yMap);
420
+ callback(node);
421
+ return this;
422
+ }
423
+ remove(id) {
424
+ const dimensions = getOrCreateDimensions(this.dsl);
425
+ const index = locateDimensionIndexById(dimensions, id);
426
+ if (-1 !== index) dimensions.delete(index, 1);
427
+ return this;
428
+ }
429
+ update(id, callback) {
430
+ const dimensions = getOrCreateDimensions(this.dsl);
431
+ const index = locateDimensionIndexById(dimensions, id);
432
+ if (-1 === index) throw new Error(`Dimension with id "${id}" not found`);
433
+ const dimensionYMap = dimensions.get(index);
434
+ const node = new DimensionNodeBuilder(dimensionYMap);
435
+ callback(node);
436
+ return this;
437
+ }
438
+ find(predicate) {
439
+ const dimensions = getOrCreateDimensions(this.dsl);
440
+ const items = dimensions.toArray();
441
+ for(let index = 0; index < items.length; index++){
442
+ const node = new DimensionNodeBuilder(items[index]);
443
+ if (predicate(node, index)) return node;
444
+ }
445
+ }
446
+ findAll() {
447
+ const dimensions = getOrCreateDimensions(this.dsl);
448
+ return dimensions.toArray().map((yMap)=>new DimensionNodeBuilder(yMap));
449
+ }
450
+ toJSON() {
451
+ const dimensions = getOrCreateDimensions(this.dsl);
452
+ return dimensions.toJSON();
453
+ }
454
+ observe(callback) {
455
+ const dimensions = getOrCreateDimensions(this.dsl);
456
+ dimensions.observe(callback);
457
+ return ()=>{
458
+ dimensions.unobserve(callback);
459
+ };
460
+ }
461
+ static isDimensionNode(node) {
462
+ return 'alias' in node && !('children' in node);
463
+ }
464
+ static isDimensionGroup(node) {
465
+ return 'children' in node;
466
+ }
467
+ }
468
+ class ChartTypeBuilder {
469
+ dsl;
470
+ constructor(_doc, dsl){
471
+ this.dsl = dsl;
472
+ }
473
+ observe(callback) {
474
+ const wrapper = (e, trans)=>{
475
+ if (e.keysChanged.has('chartType')) callback(e, trans);
476
+ };
477
+ this.dsl.observe(wrapper);
478
+ return ()=>{
479
+ this.dsl.unobserve(wrapper);
480
+ };
481
+ }
482
+ changeChartType(chartType) {
483
+ this.dsl.set('chartType', chartType);
484
+ }
485
+ getChartType() {
486
+ return this.dsl.get('chartType') || 'table';
487
+ }
488
+ toJSON() {
489
+ return this.dsl.get('chartType') || 'table';
490
+ }
491
+ getAvailableChartTypes() {
492
+ return [
493
+ ChartTypeEnum.Table,
494
+ ChartTypeEnum.PivotTable,
495
+ ChartTypeEnum.Line,
496
+ ChartTypeEnum.Column,
497
+ ChartTypeEnum.ColumnPercent,
498
+ ChartTypeEnum.ColumnParallel,
499
+ ChartTypeEnum.BarPercent,
500
+ ChartTypeEnum.BarParallel,
501
+ ChartTypeEnum.Area,
502
+ ChartTypeEnum.AreaPercent,
503
+ ChartTypeEnum.DualAxis,
504
+ ChartTypeEnum.Scatter,
505
+ ChartTypeEnum.Rose,
506
+ ChartTypeEnum.RoseParallel,
507
+ ChartTypeEnum.Pie,
508
+ ChartTypeEnum.Donut,
509
+ ChartTypeEnum.Radar,
510
+ ChartTypeEnum.Funnel,
511
+ ChartTypeEnum.Heatmap,
512
+ ChartTypeEnum.Boxplot,
513
+ ChartTypeEnum.Histogram
514
+ ];
515
+ }
516
+ }
517
+ function createWhereGroup(op = 'and', groupId = 'root') {
518
+ const yMap = new external_yjs_Map();
519
+ yMap.set('id', groupId);
520
+ yMap.set('op', op);
521
+ yMap.set('conditions', new external_yjs_Array());
522
+ return yMap;
523
+ }
524
+ function where_utils_isWhereGroup(yMap) {
525
+ return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
526
+ }
527
+ function findEntry(collection, entryId) {
528
+ const items = collection.toArray();
529
+ for(let index = 0; index < items.length; index++){
530
+ const item = items[index];
531
+ if (item.get('id') === entryId) return {
532
+ collection,
533
+ index,
534
+ item
535
+ };
536
+ if (where_utils_isWhereGroup(item)) {
537
+ const nestedCollection = item.get('conditions');
538
+ const nestedMatch = findEntry(nestedCollection, entryId);
539
+ if (nestedMatch) return nestedMatch;
540
+ }
541
+ }
542
+ }
543
+ class WhereFilterNodeBuilder {
544
+ yMap;
545
+ constructor(yMap){
546
+ this.yMap = yMap;
547
+ }
548
+ getId() {
549
+ return this.yMap.get('id');
550
+ }
551
+ getField() {
552
+ return this.yMap.get('field');
553
+ }
554
+ setField(field) {
555
+ this.yMap.set('field', field);
556
+ return this;
557
+ }
558
+ getOperator() {
559
+ return this.yMap.get('op');
560
+ }
561
+ setOperator(operator) {
562
+ this.yMap.set('op', operator);
563
+ return this;
564
+ }
565
+ setValue(value) {
566
+ this.yMap.set('value', value);
567
+ return this;
568
+ }
569
+ toJSON() {
570
+ return this.yMap.toJSON();
571
+ }
572
+ }
573
+ class WhereGroupBuilder {
574
+ yMap;
575
+ constructor(yMap){
576
+ this.yMap = yMap;
577
+ }
578
+ getConditions() {
579
+ return this.yMap.get('conditions');
580
+ }
204
581
  getId() {
205
582
  return this.yMap.get('id');
206
583
  }
@@ -215,29 +592,24 @@ class HavingGroupBuilder {
215
592
  const yMap = new external_yjs_Map();
216
593
  yMap.set('id', id_id.uuid());
217
594
  yMap.set('field', field);
218
- const conditions = this.yMap.get('conditions');
219
- conditions.push([
595
+ this.getConditions().push([
220
596
  yMap
221
597
  ]);
222
- const node = new HavingFiltersNodeBuilder(yMap);
598
+ const node = new WhereFilterNodeBuilder(yMap);
223
599
  callback(node);
224
600
  return this;
225
601
  }
226
602
  addGroup(op, callback) {
227
- const yMap = new external_yjs_Map();
228
- yMap.set('id', id_id.uuid());
229
- yMap.set('op', op);
230
- yMap.set('conditions', new external_yjs_Array());
231
- const conditions = this.yMap.get('conditions');
232
- conditions.push([
603
+ const yMap = createWhereGroup(op, id_id.uuid());
604
+ this.getConditions().push([
233
605
  yMap
234
606
  ]);
235
- const group = new HavingGroupBuilder(yMap);
607
+ const group = new WhereGroupBuilder(yMap);
236
608
  callback(group);
237
609
  return this;
238
610
  }
239
611
  remove(idOrIndex) {
240
- const conditions = this.yMap.get('conditions');
612
+ const conditions = this.getConditions();
241
613
  if ('number' == typeof idOrIndex) {
242
614
  if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
243
615
  } else {
@@ -247,154 +619,146 @@ class HavingGroupBuilder {
247
619
  return this;
248
620
  }
249
621
  clear() {
250
- const conditions = this.yMap.get('conditions');
622
+ const conditions = this.getConditions();
251
623
  conditions.delete(0, conditions.length);
252
624
  return this;
253
625
  }
254
- toJson() {
626
+ toJSON() {
255
627
  return this.yMap.toJSON();
256
628
  }
257
629
  }
258
- class HavingFiltersBuilder {
259
- dsl;
260
- doc;
630
+ class WhereFilterBuilder {
631
+ whereFilter;
261
632
  constructor(doc, dsl){
262
- this.doc = doc;
263
- this.dsl = dsl;
264
- if (!this.dsl.get('havingFilters')) this.doc.transact(()=>{
265
- this.dsl.set('havingFilters', new external_yjs_Array());
633
+ doc.transact(()=>{
634
+ const existingWhereFilter = dsl.get('whereFilter');
635
+ if (existingWhereFilter instanceof external_yjs_Map) this.whereFilter = existingWhereFilter;
636
+ else {
637
+ this.whereFilter = createWhereGroup();
638
+ dsl.set('whereFilter', this.whereFilter);
639
+ }
640
+ if (!(this.whereFilter.get('conditions') instanceof external_yjs_Array)) this.whereFilter.set('conditions', new external_yjs_Array());
641
+ if (!this.whereFilter.get('id')) this.whereFilter.set('id', 'root');
642
+ if (!this.whereFilter.get('op')) this.whereFilter.set('op', 'and');
266
643
  });
267
644
  }
645
+ getConditions() {
646
+ return this.whereFilter.get('conditions');
647
+ }
268
648
  add(field, callback) {
269
649
  const yMap = new external_yjs_Map();
270
650
  yMap.set('id', id_id.uuid());
271
651
  yMap.set('field', field);
272
- this.dsl.get('havingFilters').push([
652
+ this.getConditions().push([
273
653
  yMap
274
654
  ]);
275
- const node = new HavingFiltersNodeBuilder(yMap);
655
+ const node = new WhereFilterNodeBuilder(yMap);
276
656
  callback(node);
277
657
  return this;
278
658
  }
279
659
  addGroup(op, callback) {
280
- const yMap = new external_yjs_Map();
281
- yMap.set('id', id_id.uuid());
282
- yMap.set('op', op);
283
- yMap.set('conditions', new external_yjs_Array());
284
- this.dsl.get('havingFilters').push([
660
+ const yMap = createWhereGroup(op, id_id.uuid());
661
+ this.getConditions().push([
285
662
  yMap
286
663
  ]);
287
- const group = new HavingGroupBuilder(yMap);
664
+ const group = new WhereGroupBuilder(yMap);
288
665
  callback(group);
289
666
  return this;
290
667
  }
291
668
  update(id, callback) {
292
- const havingFilters = this.dsl.get('havingFilters');
293
- const index = havingFilters.toArray().findIndex((item)=>item.get('id') === id);
294
- if (-1 === index) throw new Error(`Having filter with id ${id} not found`);
295
- const filterYMap = havingFilters.get(index);
296
- const node = new HavingFiltersNodeBuilder(filterYMap);
669
+ const conditions = this.getConditions();
670
+ const match = findEntry(conditions, id);
671
+ if (!match) throw new Error(`Where filter with id ${id} not found`);
672
+ if (!WhereFilterBuilder.isNode(match.item)) throw new Error(`Item with id ${id} is not a filter`);
673
+ const filterYMap = match.item;
674
+ const node = new WhereFilterNodeBuilder(filterYMap);
297
675
  callback(node);
298
676
  return this;
299
677
  }
300
678
  updateGroup(id, callback) {
301
- const havingFilters = this.dsl.get('havingFilters');
302
- const index = havingFilters.toArray().findIndex((item)=>item.get('id') === id);
303
- if (-1 === index) throw new Error(`Having group with id ${id} not found`);
304
- const yMap = havingFilters.get(index);
305
- if (!HavingFiltersBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
306
- const group = new HavingGroupBuilder(yMap);
679
+ const conditions = this.getConditions();
680
+ const match = findEntry(conditions, id);
681
+ if (!match) throw new Error(`Where group with id ${id} not found`);
682
+ const yMap = match.item;
683
+ if (!WhereFilterBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
684
+ const group = new WhereGroupBuilder(yMap);
307
685
  callback(group);
308
686
  return this;
309
687
  }
310
688
  remove(idOrIndex) {
311
- const havingFilters = this.dsl.get('havingFilters');
689
+ const conditions = this.getConditions();
312
690
  if ('number' == typeof idOrIndex) {
313
- if (idOrIndex >= 0 && idOrIndex < havingFilters.length) havingFilters.delete(idOrIndex, 1);
691
+ if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
314
692
  } else {
315
- const index = havingFilters.toArray().findIndex((item)=>item.get('id') === idOrIndex);
316
- if (-1 !== index) havingFilters.delete(index, 1);
693
+ const match = findEntry(conditions, idOrIndex);
694
+ if (match) match.collection.delete(match.index, 1);
317
695
  }
318
696
  return this;
319
697
  }
320
- find(id) {
321
- const havingFilters = this.dsl.get('havingFilters');
322
- const yMap = havingFilters.toArray().find((item)=>item.get('id') === id);
323
- if (!yMap) return;
324
- if (HavingFiltersBuilder.isGroup(yMap)) return new HavingGroupBuilder(yMap);
325
- return new HavingFiltersNodeBuilder(yMap);
698
+ find(predicate) {
699
+ const traverse = (collection)=>{
700
+ const items = collection.toArray();
701
+ for(let index = 0; index < items.length; index++){
702
+ const yMap = items[index];
703
+ const entry = WhereFilterBuilder.isGroup(yMap) ? new WhereGroupBuilder(yMap) : new WhereFilterNodeBuilder(yMap);
704
+ if (predicate(entry, index)) return entry;
705
+ if (WhereFilterBuilder.isGroup(yMap)) {
706
+ const nestedCollection = yMap.get('conditions');
707
+ const nestedMatch = traverse(nestedCollection);
708
+ if (nestedMatch) return nestedMatch;
709
+ }
710
+ }
711
+ };
712
+ return traverse(this.getConditions());
326
713
  }
327
714
  clear() {
328
- const havingFilters = this.dsl.get('havingFilters');
329
- havingFilters.delete(0, havingFilters.length);
715
+ const conditions = this.getConditions();
716
+ conditions.delete(0, conditions.length);
330
717
  return this;
331
718
  }
332
- toJson() {
333
- return this.dsl.get('havingFilters').toJSON();
719
+ toJSON() {
720
+ return this.whereFilter.toJSON();
334
721
  }
335
722
  observe(callback) {
336
- this.dsl.get('havingFilters').observe(callback);
723
+ this.whereFilter.observeDeep(callback);
337
724
  return ()=>{
338
- this.dsl.get('havingFilters').unobserve(callback);
725
+ this.whereFilter.unobserveDeep(callback);
339
726
  };
340
727
  }
341
728
  static isGroup(yMap) {
342
- return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
729
+ return where_utils_isWhereGroup(yMap);
343
730
  }
344
731
  static isNode(yMap) {
345
732
  return void 0 !== yMap.get('field');
346
733
  }
347
734
  }
348
- class ChartTypeBuilder {
349
- dsl;
350
- constructor(_doc, dsl){
351
- this.dsl = dsl;
352
- }
353
- observe(callback) {
354
- const wrapper = (e, trans)=>{
355
- if (e.keysChanged.has('chartType')) callback(e, trans);
356
- };
357
- this.dsl.observe(wrapper);
358
- return ()=>{
359
- this.dsl.unobserve(wrapper);
735
+ function createHavingGroup(op = 'and', groupId = 'root') {
736
+ const yMap = new external_yjs_Map();
737
+ yMap.set('id', groupId);
738
+ yMap.set('op', op);
739
+ yMap.set('conditions', new external_yjs_Array());
740
+ return yMap;
741
+ }
742
+ function having_utils_isHavingGroup(yMap) {
743
+ return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
744
+ }
745
+ function having_utils_findEntry(collection, entryId) {
746
+ const items = collection.toArray();
747
+ for(let index = 0; index < items.length; index++){
748
+ const item = items[index];
749
+ if (item.get('id') === entryId) return {
750
+ collection,
751
+ index,
752
+ item
360
753
  };
361
- }
362
- changeChartType(chartType) {
363
- this.dsl.set('chartType', chartType);
364
- }
365
- getChartType() {
366
- return this.dsl.get('chartType') || 'table';
367
- }
368
- toJson() {
369
- return this.dsl.get('chartType') || 'table';
370
- }
371
- getAvailableChartTypes() {
372
- return [
373
- ChartTypeEnum.Table,
374
- ChartTypeEnum.PivotTable,
375
- ChartTypeEnum.Line,
376
- ChartTypeEnum.Column,
377
- ChartTypeEnum.ColumnPercent,
378
- ChartTypeEnum.ColumnParallel,
379
- ChartTypeEnum.BarPercent,
380
- ChartTypeEnum.BarParallel,
381
- ChartTypeEnum.Area,
382
- ChartTypeEnum.AreaPercent,
383
- ChartTypeEnum.DualAxis,
384
- ChartTypeEnum.Scatter,
385
- ChartTypeEnum.Rose,
386
- ChartTypeEnum.RoseParallel,
387
- ChartTypeEnum.Pie,
388
- ChartTypeEnum.Donut,
389
- ChartTypeEnum.Radar,
390
- ChartTypeEnum.Funnel,
391
- ChartTypeEnum.Heatmap,
392
- ChartTypeEnum.Boxplot,
393
- ChartTypeEnum.Histogram
394
- ];
754
+ if (having_utils_isHavingGroup(item)) {
755
+ const nestedCollection = item.get('conditions');
756
+ const nestedMatch = having_utils_findEntry(nestedCollection, entryId);
757
+ if (nestedMatch) return nestedMatch;
758
+ }
395
759
  }
396
760
  }
397
- class WhereFilterNodeBuilder {
761
+ class HavingFilterNodeBuilder {
398
762
  yMap;
399
763
  constructor(yMap){
400
764
  this.yMap = yMap;
@@ -408,23 +772,26 @@ class WhereFilterNodeBuilder {
408
772
  getOperator() {
409
773
  return this.yMap.get('op');
410
774
  }
411
- setOperator(operator) {
412
- this.yMap.set('op', operator);
413
- return this;
414
- }
415
775
  setValue(value) {
416
776
  this.yMap.set('value', value);
417
777
  return this;
418
778
  }
419
- toJson() {
779
+ setOperator(operator) {
780
+ this.yMap.set('op', operator);
781
+ return this;
782
+ }
783
+ toJSON() {
420
784
  return this.yMap.toJSON();
421
785
  }
422
786
  }
423
- class WhereGroupBuilder {
787
+ class HavingGroupBuilder {
424
788
  yMap;
425
789
  constructor(yMap){
426
790
  this.yMap = yMap;
427
791
  }
792
+ getConditions() {
793
+ return this.yMap.get('conditions');
794
+ }
428
795
  getId() {
429
796
  return this.yMap.get('id');
430
797
  }
@@ -439,29 +806,24 @@ class WhereGroupBuilder {
439
806
  const yMap = new external_yjs_Map();
440
807
  yMap.set('id', id_id.uuid());
441
808
  yMap.set('field', field);
442
- const conditions = this.yMap.get('conditions');
443
- conditions.push([
809
+ this.getConditions().push([
444
810
  yMap
445
811
  ]);
446
- const node = new WhereFilterNodeBuilder(yMap);
812
+ const node = new HavingFilterNodeBuilder(yMap);
447
813
  callback(node);
448
814
  return this;
449
815
  }
450
816
  addGroup(op, callback) {
451
- const yMap = new external_yjs_Map();
452
- yMap.set('id', id_id.uuid());
453
- yMap.set('op', op);
454
- yMap.set('conditions', new external_yjs_Array());
455
- const conditions = this.yMap.get('conditions');
456
- conditions.push([
817
+ const yMap = createHavingGroup(op, id_id.uuid());
818
+ this.getConditions().push([
457
819
  yMap
458
820
  ]);
459
- const group = new WhereGroupBuilder(yMap);
821
+ const group = new HavingGroupBuilder(yMap);
460
822
  callback(group);
461
823
  return this;
462
824
  }
463
825
  remove(idOrIndex) {
464
- const conditions = this.yMap.get('conditions');
826
+ const conditions = this.getConditions();
465
827
  if ('number' == typeof idOrIndex) {
466
828
  if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
467
829
  } else {
@@ -471,104 +833,191 @@ class WhereGroupBuilder {
471
833
  return this;
472
834
  }
473
835
  clear() {
474
- const conditions = this.yMap.get('conditions');
836
+ const conditions = this.getConditions();
475
837
  conditions.delete(0, conditions.length);
476
838
  return this;
477
839
  }
478
- toJson() {
840
+ toJSON() {
479
841
  return this.yMap.toJSON();
480
842
  }
481
843
  }
482
- class WhereFiltersBuilder {
483
- dsl;
484
- doc;
844
+ class HavingFilterBuilder {
845
+ havingFilter;
485
846
  constructor(doc, dsl){
486
- this.doc = doc;
487
- this.dsl = dsl;
488
- if (!this.dsl.get('whereFilters')) this.doc.transact(()=>{
489
- this.dsl.set('whereFilters', new external_yjs_Array());
847
+ doc.transact(()=>{
848
+ const existingHavingFilter = dsl.get('havingFilter');
849
+ if (existingHavingFilter instanceof external_yjs_Map) this.havingFilter = existingHavingFilter;
850
+ else {
851
+ this.havingFilter = createHavingGroup();
852
+ dsl.set('havingFilter', this.havingFilter);
853
+ }
854
+ if (!(this.havingFilter.get('conditions') instanceof external_yjs_Array)) this.havingFilter.set('conditions', new external_yjs_Array());
855
+ if (!this.havingFilter.get('id')) this.havingFilter.set('id', 'root');
856
+ if (!this.havingFilter.get('op')) this.havingFilter.set('op', 'and');
490
857
  });
491
858
  }
859
+ getConditions() {
860
+ return this.havingFilter.get('conditions');
861
+ }
492
862
  add(field, callback) {
493
863
  const yMap = new external_yjs_Map();
494
864
  yMap.set('id', id_id.uuid());
495
865
  yMap.set('field', field);
496
- this.dsl.get('whereFilters').push([
866
+ this.getConditions().push([
497
867
  yMap
498
868
  ]);
499
- const node = new WhereFilterNodeBuilder(yMap);
869
+ const node = new HavingFilterNodeBuilder(yMap);
500
870
  callback(node);
501
871
  return this;
502
872
  }
503
873
  addGroup(op, callback) {
504
- const yMap = new external_yjs_Map();
505
- yMap.set('id', id_id.uuid());
506
- yMap.set('op', op);
507
- yMap.set('conditions', new external_yjs_Array());
508
- this.dsl.get('whereFilters').push([
874
+ const yMap = createHavingGroup(op, id_id.uuid());
875
+ this.getConditions().push([
509
876
  yMap
510
877
  ]);
511
- const group = new WhereGroupBuilder(yMap);
878
+ const group = new HavingGroupBuilder(yMap);
512
879
  callback(group);
513
880
  return this;
514
881
  }
515
882
  update(id, callback) {
516
- const whereFilters = this.dsl.get('whereFilters');
517
- const index = whereFilters.toArray().findIndex((item)=>item.get('id') === id);
518
- if (-1 === index) throw new Error(`Where filter with id ${id} not found`);
519
- const filterYMap = whereFilters.get(index);
520
- const node = new WhereFilterNodeBuilder(filterYMap);
883
+ const conditions = this.getConditions();
884
+ const match = having_utils_findEntry(conditions, id);
885
+ if (!match) throw new Error(`Having filter with id ${id} not found`);
886
+ if (!HavingFilterBuilder.isNode(match.item)) throw new Error(`Item with id ${id} is not a filter`);
887
+ const filterYMap = match.item;
888
+ const node = new HavingFilterNodeBuilder(filterYMap);
521
889
  callback(node);
522
890
  return this;
523
891
  }
524
892
  updateGroup(id, callback) {
525
- const whereFilters = this.dsl.get('whereFilters');
526
- const index = whereFilters.toArray().findIndex((item)=>item.get('id') === id);
527
- if (-1 === index) throw new Error(`Where group with id ${id} not found`);
528
- const yMap = whereFilters.get(index);
529
- if (!WhereFiltersBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
530
- const group = new WhereGroupBuilder(yMap);
893
+ const conditions = this.getConditions();
894
+ const match = having_utils_findEntry(conditions, id);
895
+ if (!match) throw new Error(`Having group with id ${id} not found`);
896
+ const yMap = match.item;
897
+ if (!HavingFilterBuilder.isGroup(yMap)) throw new Error(`Item with id ${id} is not a group`);
898
+ const group = new HavingGroupBuilder(yMap);
531
899
  callback(group);
532
900
  return this;
533
901
  }
534
902
  remove(idOrIndex) {
535
- const whereFilters = this.dsl.get('whereFilters');
903
+ const conditions = this.getConditions();
536
904
  if ('number' == typeof idOrIndex) {
537
- if (idOrIndex >= 0 && idOrIndex < whereFilters.length) whereFilters.delete(idOrIndex, 1);
905
+ if (idOrIndex >= 0 && idOrIndex < conditions.length) conditions.delete(idOrIndex, 1);
538
906
  } else {
539
- const index = whereFilters.toArray().findIndex((item)=>item.get('id') === idOrIndex);
540
- if (-1 !== index) whereFilters.delete(index, 1);
907
+ const match = having_utils_findEntry(conditions, idOrIndex);
908
+ if (match) match.collection.delete(match.index, 1);
541
909
  }
542
910
  return this;
543
911
  }
544
- find(id) {
545
- const whereFilters = this.dsl.get('whereFilters');
546
- const yMap = whereFilters.toArray().find((item)=>item.get('id') === id);
547
- if (!yMap) return;
548
- if (WhereFiltersBuilder.isGroup(yMap)) return new WhereGroupBuilder(yMap);
549
- return new WhereFilterNodeBuilder(yMap);
912
+ find(predicate) {
913
+ const traverse = (collection)=>{
914
+ const items = collection.toArray();
915
+ for(let index = 0; index < items.length; index++){
916
+ const yMap = items[index];
917
+ const entry = HavingFilterBuilder.isGroup(yMap) ? new HavingGroupBuilder(yMap) : new HavingFilterNodeBuilder(yMap);
918
+ if (predicate(entry, index)) return entry;
919
+ if (HavingFilterBuilder.isGroup(yMap)) {
920
+ const nestedCollection = yMap.get('conditions');
921
+ const nestedMatch = traverse(nestedCollection);
922
+ if (nestedMatch) return nestedMatch;
923
+ }
924
+ }
925
+ };
926
+ return traverse(this.getConditions());
550
927
  }
551
928
  clear() {
552
- const whereFilters = this.dsl.get('whereFilters');
553
- whereFilters.delete(0, whereFilters.length);
929
+ const conditions = this.getConditions();
930
+ conditions.delete(0, conditions.length);
554
931
  return this;
555
932
  }
556
- toJson() {
557
- return this.dsl.get('whereFilters').toJSON();
933
+ toJSON() {
934
+ return this.havingFilter.toJSON();
558
935
  }
559
936
  observe(callback) {
560
- this.dsl.get('whereFilters').observe(callback);
937
+ this.havingFilter.observeDeep(callback);
561
938
  return ()=>{
562
- this.dsl.get('whereFilters').unobserve(callback);
939
+ this.havingFilter.unobserveDeep(callback);
563
940
  };
564
941
  }
565
942
  static isGroup(yMap) {
566
- return void 0 !== yMap.get('op') && void 0 !== yMap.get('conditions');
943
+ return having_utils_isHavingGroup(yMap);
567
944
  }
568
945
  static isNode(yMap) {
569
946
  return void 0 !== yMap.get('field');
570
947
  }
571
948
  }
949
+ class ThemeBuilder {
950
+ dsl;
951
+ constructor(_doc, dsl){
952
+ this.dsl = dsl;
953
+ }
954
+ observe(callback) {
955
+ const wrapper = (e, trans)=>{
956
+ if (e.keysChanged.has('theme')) callback(e, trans);
957
+ };
958
+ this.dsl.observe(wrapper);
959
+ return ()=>{
960
+ this.dsl.unobserve(wrapper);
961
+ };
962
+ }
963
+ setTheme(theme) {
964
+ this.dsl.set('theme', theme);
965
+ }
966
+ getTheme() {
967
+ return this.dsl.get('theme') || 'light';
968
+ }
969
+ toJSON() {
970
+ return this.getTheme();
971
+ }
972
+ }
973
+ class LocaleBuilder {
974
+ dsl;
975
+ constructor(_doc, dsl){
976
+ this.dsl = dsl;
977
+ }
978
+ observe(callback) {
979
+ const wrapper = (e, trans)=>{
980
+ if (e.keysChanged.has('locale')) callback(e, trans);
981
+ };
982
+ this.dsl.observe(wrapper);
983
+ return ()=>{
984
+ this.dsl.unobserve(wrapper);
985
+ };
986
+ }
987
+ setLocale(locale) {
988
+ this.dsl.set('locale', locale);
989
+ }
990
+ getLocale() {
991
+ return this.dsl.get('locale') || 'zh-CN';
992
+ }
993
+ toJSON() {
994
+ return this.getLocale();
995
+ }
996
+ }
997
+ class LimitBuilder {
998
+ dsl;
999
+ constructor(_doc, dsl){
1000
+ this.dsl = dsl;
1001
+ }
1002
+ observe(callback) {
1003
+ const wrapper = (e, trans)=>{
1004
+ if (e.keysChanged.has('limit')) callback(e, trans);
1005
+ };
1006
+ this.dsl.observe(wrapper);
1007
+ return ()=>{
1008
+ this.dsl.unobserve(wrapper);
1009
+ };
1010
+ }
1011
+ setLimit(limit) {
1012
+ this.dsl.set('limit', limit);
1013
+ }
1014
+ getLimit() {
1015
+ return this.dsl.get('limit');
1016
+ }
1017
+ toJSON() {
1018
+ return this.getLimit();
1019
+ }
1020
+ }
572
1021
  class undo_manager_UndoManager {
573
1022
  manager;
574
1023
  constructor(scope){
@@ -590,346 +1039,202 @@ class undo_manager_UndoManager {
590
1039
  this.manager.clear(clearUndoStack, clearRedoStack);
591
1040
  }
592
1041
  }
593
- const buildSelect = (queryDSL, context)=>{
594
- const { vbiDSL } = context;
595
- const measures = vbiDSL.measures;
596
- const dimensions = vbiDSL.dimensions;
597
- const result = {
598
- ...queryDSL
599
- };
600
- const measureNodes = measures.filter((measure)=>MeasuresBuilder.isMeasureNode(measure));
601
- const measureSelects = measureNodes.map((measure)=>({
602
- field: measure.field,
603
- alias: measure.alias,
604
- aggr: measure.aggregate
605
- }));
606
- const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
607
- const dimensionSelects = dimensionNodes.map((dimension)=>({
608
- field: dimension.field,
609
- alias: dimension.alias
610
- }));
611
- result.select = measureSelects.concat(dimensionSelects);
612
- return result;
613
- };
614
- const buildGroupBy = (queryDSL, context)=>{
615
- const result = {
616
- ...queryDSL
617
- };
618
- const { vbiDSL } = context;
619
- const dimensions = vbiDSL.dimensions;
620
- const dimensionNodes = dimensions.filter((dimension)=>DimensionsBuilder.isDimensionNode(dimension));
621
- result.groupBy = dimensionNodes.map((dimension)=>dimension.field);
622
- return result;
623
- };
624
- const buildWhere = (queryDSL, context)=>{
625
- const { vbiDSL } = context;
626
- const whereFilters = vbiDSL.whereFilters || [];
627
- if (0 === whereFilters.length) return queryDSL;
628
- const result = {
629
- ...queryDSL
630
- };
631
- result.where = {
632
- op: 'and',
633
- conditions: whereFilters.flatMap(mapClauseToCondition)
634
- };
635
- return result;
636
- };
637
- function isWhereGroup(clause) {
638
- return 'op' in clause && 'conditions' in clause;
639
- }
640
- function mapClauseToCondition(clause) {
641
- if (isWhereGroup(clause)) return [
642
- mapGroupToCondition(clause)
643
- ];
644
- return mapFilterToCondition(clause);
645
- }
646
- function mapGroupToCondition(group) {
647
- return {
648
- op: group.op,
649
- conditions: group.conditions.flatMap(mapClauseToCondition)
650
- };
651
- }
652
- function mapFilterToCondition(filter) {
653
- if ('between' === filter.op) return handleBetweenFilter(filter);
654
- return handleSimpleFilter(filter);
655
- }
656
- function handleBetweenFilter(filter) {
657
- const conditions = [];
658
- const value = filter.value;
659
- if (void 0 !== value.min && null !== value.min && '' !== value.min) conditions.push({
660
- field: filter.field,
661
- op: '<' === value.leftOp ? '>' : '>=',
662
- value: value.min
663
- });
664
- if (void 0 !== value.max && null !== value.max && '' !== value.max) conditions.push({
665
- field: filter.field,
666
- op: '<' === value.rightOp ? '<' : '<=',
667
- value: value.max
1042
+ const createBuilderFeatures = (doc, dsl)=>({
1043
+ undoManager: new undo_manager_UndoManager(dsl),
1044
+ chartType: new ChartTypeBuilder(doc, dsl),
1045
+ measures: new MeasuresBuilder(doc, dsl),
1046
+ dimensions: new DimensionsBuilder(doc, dsl),
1047
+ havingFilter: new HavingFilterBuilder(doc, dsl),
1048
+ whereFilter: new WhereFilterBuilder(doc, dsl),
1049
+ theme: new ThemeBuilder(doc, dsl),
1050
+ locale: new LocaleBuilder(doc, dsl),
1051
+ limit: new LimitBuilder(doc, dsl)
668
1052
  });
669
- return conditions;
670
- }
671
- function handleSimpleFilter(filter) {
672
- let mappedOp = filter.op ?? '=';
673
- const value = filter.value;
674
- if (Array.isArray(value)) {
675
- if ('=' === mappedOp) mappedOp = 'in';
676
- if ('!=' === mappedOp) mappedOp = 'not in';
677
- }
678
- return [
679
- {
680
- field: filter.field,
681
- op: mappedOp,
682
- value
683
- }
684
- ];
685
- }
686
- const buildHaving = (queryDSL, context)=>{
687
- const { vbiDSL } = context;
688
- const havingFilters = vbiDSL.havingFilters || [];
689
- if (0 === havingFilters.length) return queryDSL;
690
- const result = {
691
- ...queryDSL
692
- };
693
- result.having = {
694
- op: 'and',
695
- conditions: havingFilters.flatMap(buildHaving_mapClauseToCondition)
696
- };
697
- return result;
698
- };
699
- function isHavingGroup(clause) {
700
- return 'op' in clause && 'conditions' in clause;
701
- }
702
- function buildHaving_mapClauseToCondition(clause) {
703
- if (isHavingGroup(clause)) return [
704
- buildHaving_mapGroupToCondition(clause)
705
- ];
706
- return buildHaving_mapFilterToCondition(clause);
707
- }
708
- function buildHaving_mapGroupToCondition(group) {
709
- return {
710
- op: group.op,
711
- conditions: group.conditions.flatMap(buildHaving_mapClauseToCondition)
712
- };
713
- }
714
- function buildHaving_mapFilterToCondition(filter) {
715
- const mappedOp = filter.op ?? '=';
716
- return [
717
- {
718
- field: filter.field,
719
- op: mappedOp,
720
- value: filter.value
721
- }
722
- ];
723
- }
724
- const buildLimit = (queryDSL, context)=>{
725
- const result = {
726
- ...queryDSL
727
- };
728
- const limit = context.vbiDSL.limit ?? 1000;
729
- result.limit = limit;
730
- return result;
731
- };
732
- const buildVQuery = (vbiDSL, builder)=>{
733
- const wrapper = (processor)=>(queryDSL)=>processor(queryDSL, {
734
- vbiDSL,
735
- builder
736
- });
737
- return pipe({}, wrapper(buildSelect), wrapper(buildGroupBy), wrapper(buildWhere), wrapper(buildHaving), wrapper(buildLimit));
738
- };
739
- const connectorMap = new Map();
740
- const registerConnector = (id, connector)=>{
741
- connectorMap.set(id, connector);
742
- };
743
- const getConnector = async (id)=>{
744
- const connector = connectorMap.get(id);
745
- if (!connector) throw new Error(`connector ${id} not registered`);
746
- if ('function' == typeof connector) return connector();
747
- return connector;
748
- };
749
1053
  class VBIBuilder {
750
1054
  doc;
751
1055
  dsl;
752
- undoManager;
753
1056
  chartType;
754
1057
  measures;
755
1058
  dimensions;
756
- havingFilters;
757
- whereFilters;
1059
+ havingFilter;
1060
+ whereFilter;
1061
+ theme;
1062
+ locale;
1063
+ limit;
1064
+ undoManager;
758
1065
  constructor(doc){
759
1066
  this.doc = doc;
760
1067
  this.dsl = doc.getMap('dsl');
761
- this.undoManager = new undo_manager_UndoManager(this.dsl);
762
- this.chartType = new ChartTypeBuilder(doc, this.dsl);
763
- this.measures = new MeasuresBuilder(doc, this.dsl);
764
- this.dimensions = new DimensionsBuilder(doc, this.dsl);
765
- this.havingFilters = new HavingFiltersBuilder(doc, this.dsl);
766
- this.whereFilters = new WhereFiltersBuilder(doc, this.dsl);
767
- }
768
- applyUpdate(update) {
769
- applyUpdate(this.doc, update);
770
- }
771
- encodeStateAsUpdate(targetStateVector) {
772
- return encodeStateAsUpdate(this.doc, targetStateVector);
773
- }
1068
+ const features = createBuilderFeatures(doc, this.dsl);
1069
+ this.undoManager = features.undoManager;
1070
+ this.chartType = features.chartType;
1071
+ this.measures = features.measures;
1072
+ this.dimensions = features.dimensions;
1073
+ this.havingFilter = features.havingFilter;
1074
+ this.whereFilter = features.whereFilter;
1075
+ this.theme = features.theme;
1076
+ this.locale = features.locale;
1077
+ this.limit = features.limit;
1078
+ }
1079
+ applyUpdate = (update, transactionOrigin)=>applyUpdateToDoc(this.doc, update, transactionOrigin);
1080
+ encodeStateAsUpdate = (targetStateVector)=>encodeDocStateAsUpdate(this.doc, targetStateVector);
774
1081
  buildVSeed = async ()=>{
775
1082
  const vbiDSL = this.build();
776
- const connectorId = vbiDSL.connectorId;
777
- const connector = await getConnector(vbiDSL.connectorId);
778
1083
  const queryDSL = this.buildVQuery();
779
- const schema = await connector.discoverSchema();
780
- const queryResult = await connector.query({
781
- queryDSL,
782
- schema,
783
- connectorId
1084
+ return buildVSeedDSL({
1085
+ vbiDSL,
1086
+ queryDSL
784
1087
  });
785
- return {
786
- chartType: vbiDSL.chartType,
787
- dataset: queryResult.dataset,
788
- theme: vbiDSL.theme,
789
- locale: vbiDSL.locale
790
- };
791
- };
792
- buildVQuery = ()=>{
793
- const vbiDSL = this.build();
794
- return buildVQuery(vbiDSL, this);
795
1088
  };
796
- build = ()=>this.dsl.toJSON();
797
- getSchema = async ()=>{
798
- const connectorId = this.dsl.get('connectorId');
799
- const con = await getConnector(connectorId);
800
- const result = await con.discoverSchema();
801
- return result;
802
- };
803
- setLimit(limit) {
804
- this.dsl.set('limit', limit);
805
- return this;
806
- }
807
- setLocale(locale) {
808
- this.dsl.set('locale', locale);
809
- return this;
1089
+ buildVQuery = ()=>buildVQueryDSL(this.dsl, this);
1090
+ build = ()=>buildVBIDSL(this.dsl);
1091
+ isEmpty = ()=>isEmptyVBIDSL(this.dsl);
1092
+ getSchema = async ()=>getBuilderSchema(this.dsl);
1093
+ }
1094
+ const shouldEnsureIdForObject = (obj, ensureId)=>{
1095
+ if (true === ensureId) return true;
1096
+ if ('field' === ensureId) return 'string' == typeof obj.field;
1097
+ return false;
1098
+ };
1099
+ const toYMap = (obj, ensureId = false)=>{
1100
+ const yMap = new external_yjs_Map();
1101
+ if (shouldEnsureIdForObject(obj, ensureId) && !obj.id) yMap.set('id', id_id.uuid());
1102
+ for (const [key, value] of Object.entries(obj)){
1103
+ if (('conditions' === key || 'children' === key) && Array.isArray(value)) {
1104
+ const yArr = new external_yjs_Array();
1105
+ for (const child of value){
1106
+ if (child instanceof external_yjs_Map) {
1107
+ yArr.push([
1108
+ child
1109
+ ]);
1110
+ continue;
1111
+ }
1112
+ if ('object' == typeof child && null !== child) {
1113
+ yArr.push([
1114
+ toYMap(child, ensureId)
1115
+ ]);
1116
+ continue;
1117
+ }
1118
+ yArr.push([
1119
+ child
1120
+ ]);
1121
+ }
1122
+ yMap.set(key, yArr);
1123
+ continue;
1124
+ }
1125
+ yMap.set(key, value);
810
1126
  }
811
- setTheme(theme) {
812
- this.dsl.set('theme', theme);
813
- return this;
1127
+ return yMap;
1128
+ };
1129
+ const ensureYArray = (arr, ensureId = false)=>{
1130
+ if (!arr) return new external_yjs_Array();
1131
+ if (arr instanceof external_yjs_Array) return arr;
1132
+ const yArr = new external_yjs_Array();
1133
+ for (const item of arr){
1134
+ if (item instanceof external_yjs_Map) {
1135
+ yArr.push([
1136
+ item
1137
+ ]);
1138
+ continue;
1139
+ }
1140
+ if ('object' == typeof item && null !== item) {
1141
+ yArr.push([
1142
+ toYMap(item, ensureId)
1143
+ ]);
1144
+ continue;
1145
+ }
1146
+ yArr.push([
1147
+ item
1148
+ ]);
814
1149
  }
815
- }
1150
+ return yArr;
1151
+ };
1152
+ const getDefaultWhereFilter = ()=>({
1153
+ id: 'root',
1154
+ op: 'and',
1155
+ conditions: []
1156
+ });
1157
+ const isFilterGroupInput = (value)=>'object' == typeof value && null !== value;
1158
+ const ensureWhereGroup = (whereFilter)=>{
1159
+ const sourceWhereFilter = whereFilter instanceof external_yjs_Map || isFilterGroupInput(whereFilter) ? whereFilter : getDefaultWhereFilter();
1160
+ const whereGroup = sourceWhereFilter instanceof external_yjs_Map ? sourceWhereFilter : createWhereGroup();
1161
+ if (sourceWhereFilter instanceof external_yjs_Map) {
1162
+ if (!(whereGroup.get('conditions') instanceof external_yjs_Array)) whereGroup.set('conditions', new external_yjs_Array());
1163
+ if (!whereGroup.get('id')) whereGroup.set('id', 'root');
1164
+ if (!whereGroup.get('op')) whereGroup.set('op', 'and');
1165
+ return whereGroup;
1166
+ }
1167
+ whereGroup.set('id', sourceWhereFilter.id ?? 'root');
1168
+ whereGroup.set('op', sourceWhereFilter.op ?? 'and');
1169
+ whereGroup.set('conditions', ensureYArray(sourceWhereFilter.conditions, true));
1170
+ return whereGroup;
1171
+ };
1172
+ const getDefaultHavingFilter = ()=>({
1173
+ id: 'root',
1174
+ op: 'and',
1175
+ conditions: []
1176
+ });
1177
+ const ensure_having_group_isFilterGroupInput = (value)=>'object' == typeof value && null !== value;
1178
+ const ensureHavingGroup = (havingFilter)=>{
1179
+ const sourceHavingFilter = havingFilter instanceof external_yjs_Map || ensure_having_group_isFilterGroupInput(havingFilter) ? havingFilter : getDefaultHavingFilter();
1180
+ const havingGroup = sourceHavingFilter instanceof external_yjs_Map ? sourceHavingFilter : createHavingGroup();
1181
+ if (sourceHavingFilter instanceof external_yjs_Map) {
1182
+ if (!(havingGroup.get('conditions') instanceof external_yjs_Array)) havingGroup.set('conditions', new external_yjs_Array());
1183
+ if (!havingGroup.get('id')) havingGroup.set('id', 'root');
1184
+ if (!havingGroup.get('op')) havingGroup.set('op', 'and');
1185
+ return havingGroup;
1186
+ }
1187
+ havingGroup.set('id', sourceHavingFilter.id ?? 'root');
1188
+ havingGroup.set('op', sourceHavingFilter.op ?? 'and');
1189
+ havingGroup.set('conditions', ensureYArray(sourceHavingFilter.conditions, true));
1190
+ return havingGroup;
1191
+ };
1192
+ const setBaseDSLFields = (dsl, vbi)=>{
1193
+ if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId);
1194
+ if (vbi.chartType) dsl.set('chartType', vbi.chartType);
1195
+ if (vbi.theme) dsl.set('theme', vbi.theme);
1196
+ if (vbi.limit) dsl.set('limit', vbi.limit);
1197
+ if (vbi.locale) dsl.set('locale', vbi.locale);
1198
+ if (void 0 !== vbi.version) dsl.set('version', vbi.version);
1199
+ };
1200
+ const fromVBIDSLInput = (vbi)=>{
1201
+ const doc = new Doc();
1202
+ const dsl = doc.getMap('dsl');
1203
+ doc.transact(()=>{
1204
+ setBaseDSLFields(dsl, vbi);
1205
+ dsl.set('whereFilter', ensureWhereGroup(vbi.whereFilter));
1206
+ dsl.set('havingFilter', ensureHavingGroup(vbi.havingFilter));
1207
+ dsl.set('measures', ensureYArray(vbi.measures, 'field'));
1208
+ dsl.set('dimensions', ensureYArray(vbi.dimensions, 'field'));
1209
+ });
1210
+ return new VBIBuilder(doc);
1211
+ };
1212
+ const generateEmptyDSL = (connectorId)=>({
1213
+ connectorId,
1214
+ chartType: 'table',
1215
+ measures: [],
1216
+ dimensions: [],
1217
+ whereFilter: {
1218
+ id: 'root',
1219
+ op: 'and',
1220
+ conditions: []
1221
+ },
1222
+ havingFilter: {
1223
+ id: 'root',
1224
+ op: 'and',
1225
+ conditions: []
1226
+ },
1227
+ theme: 'light',
1228
+ locale: 'zh-CN',
1229
+ version: 0
1230
+ });
816
1231
  const createVBI = ()=>({
817
1232
  connectorMap: connectorMap,
818
1233
  registerConnector: registerConnector,
819
1234
  getConnector: getConnector,
820
- generateEmptyDSL: (connectorId)=>({
821
- connectorId: connectorId,
822
- chartType: 'table',
823
- measures: [],
824
- dimensions: [],
825
- whereFilters: [],
826
- havingFilters: [],
827
- theme: 'light',
828
- locale: 'zh-CN',
829
- version: 0
830
- }),
831
- from: (vbi)=>{
832
- const doc = new Doc();
833
- const dsl = doc.getMap('dsl');
834
- doc.transact(()=>{
835
- if (vbi.connectorId) dsl.set('connectorId', vbi.connectorId);
836
- if (vbi.chartType) dsl.set('chartType', vbi.chartType);
837
- if (vbi.theme) dsl.set('theme', vbi.theme);
838
- if (vbi.limit) dsl.set('limit', vbi.limit);
839
- if (vbi.locale) dsl.set('locale', vbi.locale);
840
- if (vbi.version) dsl.set('version', vbi.version);
841
- const toYMap = (obj, ensureId = false)=>{
842
- const yMap = new external_yjs_Map();
843
- if (ensureId && !obj.id) yMap.set('id', id_id.uuid());
844
- for (const [key, value] of Object.entries(obj))if ('conditions' === key && Array.isArray(value)) {
845
- const yArr = new external_yjs_Array();
846
- value.forEach((child)=>{
847
- if (child instanceof external_yjs_Map) yArr.push([
848
- child
849
- ]);
850
- else if ('object' == typeof child && null !== child) yArr.push([
851
- toYMap(child, true)
852
- ]);
853
- else yArr.push([
854
- child
855
- ]);
856
- });
857
- yMap.set(key, yArr);
858
- } else yMap.set(key, value);
859
- return yMap;
860
- };
861
- const ensureYArray = (arr, ensureId = false)=>{
862
- if (!arr) return new external_yjs_Array();
863
- if (arr instanceof external_yjs_Array) return arr;
864
- const yArr = new external_yjs_Array();
865
- arr.forEach((item)=>{
866
- if (item instanceof external_yjs_Map) yArr.push([
867
- item
868
- ]);
869
- else if ('object' == typeof item && null !== item) yArr.push([
870
- toYMap(item, ensureId)
871
- ]);
872
- else yArr.push([
873
- item
874
- ]);
875
- });
876
- return yArr;
877
- };
878
- dsl.set('whereFilters', ensureYArray(vbi.whereFilters, true));
879
- dsl.set('havingFilters', ensureYArray(vbi.havingFilters, true));
880
- dsl.set('measures', ensureYArray(vbi.measures));
881
- dsl.set('dimensions', ensureYArray(vbi.dimensions));
882
- });
883
- return new VBIBuilder(doc);
884
- }
1235
+ generateEmptyDSL: generateEmptyDSL,
1236
+ from: fromVBIDSLInput,
1237
+ create: fromVBIDSLInput
885
1238
  });
886
1239
  const VBI = createVBI();
887
- const zVBIFilter = z.object({
888
- id: z.string(),
889
- field: z.string(),
890
- op: z.string().optional(),
891
- value: z.any().optional()
892
- });
893
- const zVBIWhereGroup = z.lazy(()=>z.object({
894
- id: z.string(),
895
- op: z["enum"]([
896
- 'and',
897
- 'or'
898
- ]),
899
- conditions: z.array(zVBIWhereClause)
900
- }));
901
- const zVBIWhereClause = z.lazy(()=>z.union([
902
- zVBIFilter,
903
- zVBIWhereGroup
904
- ]));
905
- function isVBIFilter(clause) {
906
- return 'field' in clause;
907
- }
908
- function isVBIWhereGroup(clause) {
909
- return 'conditions' in clause;
910
- }
911
- const zVBIHavingFilter = z.object({
912
- id: z.string(),
913
- field: z.string(),
914
- op: z.string().optional(),
915
- value: z.any().optional()
916
- });
917
- const zVBIHavingGroup = z.lazy(()=>z.object({
918
- id: z.string(),
919
- op: z["enum"]([
920
- 'and',
921
- 'or'
922
- ]),
923
- conditions: z.array(zVBIHavingClause)
924
- }));
925
- const zVBIHavingClause = z.lazy(()=>z.union([
926
- zVBIHavingFilter,
927
- zVBIHavingGroup
928
- ]));
929
- function isVBIHavingFilter(clause) {
930
- return 'field' in clause;
931
- }
932
- function isVBIHavingGroup(clause) {
933
- return 'conditions' in clause;
934
- }
935
- export { ChartTypeBuilder, DimensionsBuilder, MeasuresBuilder, VBI, VBIBuilder, buildVQuery, findTreeNodesBy, id_id as id, isVBIFilter, isVBIHavingFilter, isVBIHavingGroup, isVBIWhereGroup, preorderTraverse };
1240
+ export { ChartTypeBuilder, DimensionsBuilder, HavingFilterBuilder, LimitBuilder, LocaleBuilder, MeasuresBuilder, ThemeBuilder, undo_manager_UndoManager as UndoManager, VBI, VBIBuilder, WhereFilterBuilder, buildVQuery, findTreeNodesBy, id_id as id, isVBIFilter, isVBIHavingFilter, isVBIHavingGroup, isVBIWhereGroup, preorderTraverse };