@vaultgradient/pipequery-lang 1.12.2 → 1.13.0

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.
@@ -1,2093 +1,18 @@
1
- // src/engine/types.ts
2
- var AGGREGATE_NAMES = /* @__PURE__ */ new Set([
3
- "sum",
4
- "avg",
5
- "min",
6
- "max",
7
- "count",
8
- "median",
9
- "stddev",
10
- "var",
11
- "percentile",
12
- "skew",
13
- "kurt",
14
- "vwap",
15
- "wavg",
16
- "drawdown",
17
- "pct",
18
- "sharpe",
19
- "calmar",
20
- "sortino",
21
- "info_ratio",
22
- "distinct_count",
23
- "sum_abs",
24
- "abs_sum",
25
- "first_value",
26
- "last_value"
27
- ]);
28
- var TWO_ARG_AGGREGATES = /* @__PURE__ */ new Set([
29
- "percentile",
30
- "vwap",
31
- "wavg",
32
- "info_ratio"
33
- ]);
34
- var DataWeaveError = class extends Error {
35
- position;
36
- line;
37
- column;
38
- constructor(message, position, line, column) {
39
- super(message);
40
- this.name = "DataWeaveError";
41
- this.position = position;
42
- this.line = line;
43
- this.column = column;
44
- }
45
- };
46
- var LexerError = class extends DataWeaveError {
47
- constructor(message, position, line, column) {
48
- super(message, position, line, column);
49
- this.name = "LexerError";
50
- }
51
- };
52
- var ParseError = class extends DataWeaveError {
53
- constructor(message, position, line, column) {
54
- super(message, position, line, column);
55
- this.name = "ParseError";
56
- }
57
- };
58
- var RuntimeError = class extends Error {
59
- constructor(message) {
60
- super(message);
61
- this.name = "RuntimeError";
62
- }
63
- };
64
-
65
- // src/engine/lexer.ts
66
- var KEYWORDS = {
67
- as: "AS",
68
- asc: "ASC",
69
- desc: "DESC",
70
- true: "BOOLEAN",
71
- false: "BOOLEAN",
72
- null: "NULL"
73
- };
74
- function tokenize(source) {
75
- const tokens = [];
76
- let pos = 0;
77
- let line = 1;
78
- let column = 1;
79
- function advance() {
80
- const ch = source[pos];
81
- pos++;
82
- if (ch === "\n") {
83
- line++;
84
- column = 1;
85
- } else {
86
- column++;
87
- }
88
- return ch;
89
- }
90
- function peek() {
91
- return source[pos];
92
- }
93
- function peekNext() {
94
- return source[pos + 1];
95
- }
96
- function addToken(type, value, startPos, startLine, startCol) {
97
- tokens.push({ type, value, position: startPos, line: startLine, column: startCol });
98
- }
99
- function error(msg) {
100
- throw new LexerError(msg, pos, line, column);
101
- }
102
- while (pos < source.length) {
103
- const startPos = pos;
104
- const startLine = line;
105
- const startCol = column;
106
- const ch = peek();
107
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
108
- advance();
109
- continue;
110
- }
111
- if (ch === "|") {
112
- advance();
113
- addToken("PIPE", "|", startPos, startLine, startCol);
114
- continue;
115
- }
116
- if (ch === "(") {
117
- advance();
118
- addToken("LPAREN", "(", startPos, startLine, startCol);
119
- continue;
120
- }
121
- if (ch === ")") {
122
- advance();
123
- addToken("RPAREN", ")", startPos, startLine, startCol);
124
- continue;
125
- }
126
- if (ch === ",") {
127
- advance();
128
- addToken("COMMA", ",", startPos, startLine, startCol);
129
- continue;
130
- }
131
- if (ch === ".") {
132
- advance();
133
- addToken("DOT", ".", startPos, startLine, startCol);
134
- continue;
135
- }
136
- if (ch === "+") {
137
- advance();
138
- addToken("PLUS", "+", startPos, startLine, startCol);
139
- continue;
140
- }
141
- if (ch === "-") {
142
- advance();
143
- addToken("MINUS", "-", startPos, startLine, startCol);
144
- continue;
145
- }
146
- if (ch === "*") {
147
- advance();
148
- addToken("STAR", "*", startPos, startLine, startCol);
149
- continue;
150
- }
151
- if (ch === "/") {
152
- advance();
153
- addToken("SLASH", "/", startPos, startLine, startCol);
154
- continue;
155
- }
156
- if (ch === "%") {
157
- advance();
158
- addToken("MOD", "%", startPos, startLine, startCol);
159
- continue;
160
- }
161
- if (ch === ">" && peekNext() === "=") {
162
- advance();
163
- advance();
164
- addToken("GTE", ">=", startPos, startLine, startCol);
165
- continue;
166
- }
167
- if (ch === ">") {
168
- advance();
169
- addToken("GT", ">", startPos, startLine, startCol);
170
- continue;
171
- }
172
- if (ch === "<" && peekNext() === "=") {
173
- advance();
174
- advance();
175
- addToken("LTE", "<=", startPos, startLine, startCol);
176
- continue;
177
- }
178
- if (ch === "<") {
179
- advance();
180
- addToken("LT", "<", startPos, startLine, startCol);
181
- continue;
182
- }
183
- if (ch === "=" && peekNext() === "=") {
184
- advance();
185
- advance();
186
- addToken("EQ", "==", startPos, startLine, startCol);
187
- continue;
188
- }
189
- if (ch === "!" && peekNext() === "=") {
190
- advance();
191
- advance();
192
- addToken("NEQ", "!=", startPos, startLine, startCol);
193
- continue;
194
- }
195
- if (ch === "!") {
196
- advance();
197
- addToken("NOT", "!", startPos, startLine, startCol);
198
- continue;
199
- }
200
- if (ch === "&" && peekNext() === "&") {
201
- advance();
202
- advance();
203
- addToken("AND", "&&", startPos, startLine, startCol);
204
- continue;
205
- }
206
- if (ch === "|" && peekNext() === "|") {
207
- advance();
208
- advance();
209
- addToken("OR", "||", startPos, startLine, startCol);
210
- continue;
211
- }
212
- if (ch >= "0" && ch <= "9") {
213
- const start = pos;
214
- while (pos < source.length && peek() >= "0" && peek() <= "9") advance();
215
- if (pos < source.length && peek() === "." && pos + 1 < source.length && source[pos + 1] >= "0" && source[pos + 1] <= "9") {
216
- advance();
217
- while (pos < source.length && peek() >= "0" && peek() <= "9") advance();
218
- }
219
- addToken("NUMBER", source.slice(start, pos), startPos, startLine, startCol);
220
- continue;
221
- }
222
- if (ch === '"' || ch === "'") {
223
- const quote = ch;
224
- advance();
225
- const start = pos;
226
- let value = "";
227
- while (pos < source.length && peek() !== quote) {
228
- if (peek() === "\\" && pos + 1 < source.length) {
229
- advance();
230
- const escaped = advance();
231
- if (escaped === "n") value += "\n";
232
- else if (escaped === "t") value += " ";
233
- else value += escaped;
234
- } else {
235
- value += advance();
236
- }
237
- }
238
- if (pos >= source.length) {
239
- error(`Unterminated string starting at line ${startLine}, column ${startCol}`);
240
- }
241
- advance();
242
- void start;
243
- addToken("STRING", value, startPos, startLine, startCol);
244
- continue;
245
- }
246
- if (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_") {
247
- const start = pos;
248
- while (pos < source.length && (peek() >= "a" && peek() <= "z" || peek() >= "A" && peek() <= "Z" || peek() >= "0" && peek() <= "9" || peek() === "_")) {
249
- advance();
250
- }
251
- const word = source.slice(start, pos);
252
- const keywordType = KEYWORDS[word];
253
- if (keywordType) {
254
- addToken(keywordType, word, startPos, startLine, startCol);
255
- } else {
256
- addToken("IDENTIFIER", word, startPos, startLine, startCol);
257
- }
258
- continue;
259
- }
260
- error(`Unexpected character '${ch}'`);
261
- }
262
- addToken("EOF", "", pos, line, column);
263
- return tokens;
264
- }
265
-
266
- // src/engine/parser.ts
267
- function parse(tokens) {
268
- let pos = 0;
269
- function peek() {
270
- return tokens[pos];
271
- }
272
- function advance() {
273
- const token = tokens[pos];
274
- pos++;
275
- return token;
276
- }
277
- function expect(type, context) {
278
- const token = peek();
279
- if (token.type !== type) {
280
- const ctx = context ? ` ${context}` : "";
281
- throw new ParseError(
282
- `Expected ${type} but found ${token.type} ('${token.value}')${ctx}`,
283
- token.position,
284
- token.line,
285
- token.column
286
- );
287
- }
288
- return advance();
289
- }
290
- function match(type) {
291
- if (peek().type === type) {
292
- advance();
293
- return true;
294
- }
295
- return false;
296
- }
297
- function parseExpression() {
298
- let expr = parseOrExpr();
299
- if (peek().type === "AS") {
300
- advance();
301
- const nameToken = expect("IDENTIFIER", 'after "as"');
302
- expr = { kind: "AliasExpr", expression: expr, alias: nameToken.value };
303
- }
304
- return expr;
305
- }
306
- function parseOrExpr() {
307
- let left = parseAndExpr();
308
- while (peek().type === "OR") {
309
- advance();
310
- const right = parseAndExpr();
311
- left = { kind: "BinaryExpr", operator: "||", left, right };
312
- }
313
- return left;
314
- }
315
- function parseAndExpr() {
316
- let left = parseEqualityExpr();
317
- while (peek().type === "AND") {
318
- advance();
319
- const right = parseEqualityExpr();
320
- left = { kind: "BinaryExpr", operator: "&&", left, right };
321
- }
322
- return left;
323
- }
324
- function parseEqualityExpr() {
325
- let left = parseComparisonExpr();
326
- while (peek().type === "EQ" || peek().type === "NEQ") {
327
- const op = advance().value;
328
- const right = parseComparisonExpr();
329
- left = { kind: "BinaryExpr", operator: op, left, right };
330
- }
331
- return left;
332
- }
333
- function parseComparisonExpr() {
334
- let left = parseAdditiveExpr();
335
- while (peek().type === "GT" || peek().type === "GTE" || peek().type === "LT" || peek().type === "LTE") {
336
- const op = advance().value;
337
- const right = parseAdditiveExpr();
338
- left = { kind: "BinaryExpr", operator: op, left, right };
339
- }
340
- return left;
341
- }
342
- function parseAdditiveExpr() {
343
- let left = parseMultiplicativeExpr();
344
- while (peek().type === "PLUS" || peek().type === "MINUS") {
345
- const op = advance().value;
346
- const right = parseMultiplicativeExpr();
347
- left = { kind: "BinaryExpr", operator: op, left, right };
348
- }
349
- return left;
350
- }
351
- function parseMultiplicativeExpr() {
352
- let left = parseUnaryExpr();
353
- while (peek().type === "STAR" || peek().type === "SLASH" || peek().type === "MOD") {
354
- const op = advance().value;
355
- const right = parseUnaryExpr();
356
- left = { kind: "BinaryExpr", operator: op, left, right };
357
- }
358
- return left;
359
- }
360
- function parseUnaryExpr() {
361
- if (peek().type === "NOT") {
362
- advance();
363
- const operand = parseUnaryExpr();
364
- return { kind: "UnaryExpr", operator: "!", operand };
365
- }
366
- if (peek().type === "MINUS") {
367
- advance();
368
- const operand = parseUnaryExpr();
369
- return { kind: "UnaryExpr", operator: "-", operand };
370
- }
371
- return parsePrimaryExpr();
372
- }
373
- function parsePrimaryExpr() {
374
- const token = peek();
375
- if (token.type === "NUMBER") {
376
- advance();
377
- return { kind: "NumberLiteral", value: Number(token.value) };
378
- }
379
- if (token.type === "STRING") {
380
- advance();
381
- return { kind: "StringLiteral", value: token.value };
382
- }
383
- if (token.type === "BOOLEAN") {
384
- advance();
385
- return { kind: "BooleanLiteral", value: token.value === "true" };
386
- }
387
- if (token.type === "NULL") {
388
- advance();
389
- return { kind: "NullLiteral" };
390
- }
391
- if (token.type === "IDENTIFIER") {
392
- advance();
393
- const name = token.value;
394
- if (peek().type === "LPAREN") {
395
- advance();
396
- const args = [];
397
- if (peek().type !== "RPAREN") {
398
- args.push(parseExpression());
399
- while (match("COMMA")) {
400
- args.push(parseExpression());
401
- }
402
- }
403
- expect("RPAREN", "after function arguments");
404
- return { kind: "FunctionCall", name, args };
405
- }
406
- const path = [name];
407
- while (peek().type === "DOT") {
408
- advance();
409
- const next = expect("IDENTIFIER", 'after "."');
410
- path.push(next.value);
411
- }
412
- return { kind: "FieldAccess", path };
413
- }
414
- if (token.type === "LPAREN") {
415
- advance();
416
- const expr = parseExpression();
417
- expect("RPAREN", "after grouped expression");
418
- return expr;
419
- }
420
- throw new ParseError(
421
- `Unexpected token ${token.type} ('${token.value}')`,
422
- token.position,
423
- token.line,
424
- token.column
425
- );
426
- }
427
- function isAggregateExpression(expr) {
428
- if (expr.kind === "AliasExpr") return isAggregateExpression(expr.expression);
429
- if (expr.kind === "FunctionCall") return AGGREGATE_NAMES.has(expr.name);
430
- return false;
431
- }
432
- function parseOperation() {
433
- const nameToken = expect("IDENTIFIER", 'after "|"');
434
- const name = nameToken.value;
435
- expect("LPAREN", `after "${name}"`);
436
- let op;
437
- switch (name) {
438
- case "where": {
439
- const condition = parseExpression();
440
- op = { kind: "WhereOp", condition };
441
- break;
442
- }
443
- case "select": {
444
- const fields = [];
445
- if (peek().type !== "RPAREN") {
446
- fields.push(parseExpression());
447
- while (match("COMMA")) {
448
- fields.push(parseExpression());
449
- }
450
- }
451
- op = { kind: "SelectOp", fields };
452
- break;
453
- }
454
- case "sort": {
455
- const criteria = [];
456
- if (peek().type !== "RPAREN") {
457
- const expr = parseOrExpr();
458
- let direction = "asc";
459
- if (peek().type === "ASC") {
460
- advance();
461
- direction = "asc";
462
- } else if (peek().type === "DESC") {
463
- advance();
464
- direction = "desc";
465
- }
466
- criteria.push({ expression: expr, direction });
467
- while (match("COMMA")) {
468
- const e = parseOrExpr();
469
- let d = "asc";
470
- if (peek().type === "ASC") {
471
- advance();
472
- d = "asc";
473
- } else if (peek().type === "DESC") {
474
- advance();
475
- d = "desc";
476
- }
477
- criteria.push({ expression: e, direction: d });
478
- }
479
- }
480
- op = { kind: "SortOp", criteria };
481
- break;
482
- }
483
- case "groupBy": {
484
- const keys = [];
485
- if (peek().type !== "RPAREN") {
486
- keys.push(parseOrExpr());
487
- while (match("COMMA")) {
488
- keys.push(parseOrExpr());
489
- }
490
- }
491
- op = { kind: "GroupByOp", keys };
492
- break;
493
- }
494
- case "join": {
495
- const rightToken = expect("IDENTIFIER", "for join source");
496
- const right = rightToken.value;
497
- expect("COMMA", "after join source");
498
- const condition = parseExpression();
499
- op = { kind: "JoinOp", right, condition };
500
- break;
501
- }
502
- case "first": {
503
- const numToken = expect("NUMBER", "for first count");
504
- op = { kind: "FirstOp", count: Number(numToken.value) };
505
- break;
506
- }
507
- case "last": {
508
- const numToken = expect("NUMBER", "for last count");
509
- op = { kind: "LastOp", count: Number(numToken.value) };
510
- break;
511
- }
512
- case "distinct": {
513
- const fields = [];
514
- if (peek().type !== "RPAREN") {
515
- fields.push(parseOrExpr());
516
- while (match("COMMA")) {
517
- fields.push(parseOrExpr());
518
- }
519
- }
520
- op = { kind: "DistinctOp", fields: fields.length > 0 ? fields : void 0 };
521
- break;
522
- }
523
- case "flatten": {
524
- let field;
525
- if (peek().type !== "RPAREN") {
526
- field = parseOrExpr();
527
- }
528
- op = { kind: "FlattenOp", field };
529
- break;
530
- }
531
- case "transpose": {
532
- let headerField;
533
- if (peek().type !== "RPAREN") {
534
- headerField = parseOrExpr();
535
- }
536
- op = { kind: "TransposeOp", headerField };
537
- break;
538
- }
539
- case "rollup": {
540
- const keys = [];
541
- const aggregates = [];
542
- let seenAggregate = false;
543
- if (peek().type !== "RPAREN") {
544
- const expr = parseExpression();
545
- if (isAggregateExpression(expr)) {
546
- seenAggregate = true;
547
- aggregates.push(expr);
548
- } else {
549
- keys.push(expr);
550
- }
551
- while (match("COMMA")) {
552
- const e = parseExpression();
553
- if (isAggregateExpression(e)) {
554
- seenAggregate = true;
555
- aggregates.push(e);
556
- } else if (seenAggregate) {
557
- const t = peek();
558
- throw new ParseError(
559
- "rollup: group keys must come before aggregate expressions",
560
- t.position,
561
- t.line,
562
- t.column
563
- );
564
- } else {
565
- keys.push(e);
566
- }
567
- }
568
- }
569
- if (aggregates.length === 0) {
570
- const t = peek();
571
- throw new ParseError(
572
- "rollup: at least one aggregate expression is required",
573
- t.position,
574
- t.line,
575
- t.column
576
- );
577
- }
578
- op = { kind: "RollupOp", keys, aggregates };
579
- break;
580
- }
581
- case "pivot": {
582
- const pivotField = parseOrExpr();
583
- expect("COMMA", "after pivot field");
584
- const aggregates = [];
585
- aggregates.push(parseExpression());
586
- while (match("COMMA")) {
587
- aggregates.push(parseExpression());
588
- }
589
- op = { kind: "PivotOp", pivotField, aggregates };
590
- break;
591
- }
592
- case "map": {
593
- const fields = [];
594
- if (peek().type !== "RPAREN") {
595
- fields.push(parseExpression());
596
- while (match("COMMA")) {
597
- fields.push(parseExpression());
598
- }
599
- }
600
- op = { kind: "MapOp", fields };
601
- break;
602
- }
603
- case "reduce": {
604
- const initial = parseExpression();
605
- expect("COMMA", "after reduce initial value");
606
- const accumulator = parseExpression();
607
- op = { kind: "ReduceOp", initial, accumulator };
608
- break;
609
- }
610
- default: {
611
- if (AGGREGATE_NAMES.has(name)) {
612
- if (TWO_ARG_AGGREGATES.has(name)) {
613
- const args = [];
614
- args.push(parseOrExpr());
615
- expect("COMMA", `after first argument of ${name}`);
616
- args.push(parseOrExpr());
617
- op = { kind: "AggregateOp", function: name, args };
618
- } else {
619
- let field;
620
- if (peek().type !== "RPAREN") {
621
- field = parseOrExpr();
622
- }
623
- op = { kind: "AggregateOp", function: name, field };
624
- }
625
- break;
626
- }
627
- throw new ParseError(
628
- `Unknown operation "${name}"`,
629
- nameToken.position,
630
- nameToken.line,
631
- nameToken.column
632
- );
633
- }
634
- }
635
- expect("RPAREN", `after ${name} arguments`);
636
- return op;
637
- }
638
- function parsePipeline() {
639
- const sourceToken = expect("IDENTIFIER", "at start of pipeline");
640
- const operations = [];
641
- while (peek().type === "PIPE") {
642
- advance();
643
- operations.push(parseOperation());
644
- }
645
- if (peek().type !== "EOF") {
646
- const t = peek();
647
- throw new ParseError(
648
- `Unexpected token ${t.type} ('${t.value}') after pipeline`,
649
- t.position,
650
- t.line,
651
- t.column
652
- );
653
- }
654
- return { kind: "Pipeline", source: sourceToken.value, operations };
655
- }
656
- return parsePipeline();
657
- }
658
-
659
- // src/engine/runtime.ts
660
- var accessorCache = /* @__PURE__ */ new Map();
661
- function getFieldAccessor(path) {
662
- const key = path.join(".");
663
- let fn = accessorCache.get(key);
664
- if (fn) return fn;
665
- if (path.length === 1) {
666
- const field = path[0];
667
- fn = (obj) => obj[field];
668
- } else {
669
- const segments = [...path];
670
- fn = (obj) => {
671
- let val = obj;
672
- for (const segment of segments) {
673
- if (val == null) return void 0;
674
- val = val[segment];
675
- }
676
- return val;
677
- };
678
- }
679
- accessorCache.set(key, fn);
680
- return fn;
681
- }
682
- function compareValues(a, b) {
683
- if (a === b) return 0;
684
- if (a == null && b == null) return 0;
685
- if (a == null) return -1;
686
- if (b == null) return 1;
687
- if (typeof a === "number" && typeof b === "number") return a - b;
688
- return String(a).localeCompare(String(b));
689
- }
690
- function groupByFn(data, keyFns) {
691
- const groups = /* @__PURE__ */ new Map();
692
- if (keyFns.length === 1) {
693
- const { fn, name } = keyFns[0];
694
- for (const row of data) {
695
- const val = fn(row);
696
- const groupKey = String(val);
697
- let group = groups.get(groupKey);
698
- if (!group) {
699
- group = { keys: { [name]: val }, rows: [] };
700
- groups.set(groupKey, group);
701
- }
702
- group.rows.push(row);
703
- }
704
- } else {
705
- for (const row of data) {
706
- const keys = {};
707
- let groupKey = "";
708
- for (let i = 0; i < keyFns.length; i++) {
709
- const { fn, name } = keyFns[i];
710
- const val = fn(row);
711
- keys[name] = val;
712
- groupKey += (i > 0 ? "\0" : "") + String(val);
713
- }
714
- let group = groups.get(groupKey);
715
- if (!group) {
716
- group = { keys, rows: [] };
717
- groups.set(groupKey, group);
718
- }
719
- group.rows.push(row);
720
- }
721
- }
722
- return Array.from(groups.values());
723
- }
724
- function nestedLoopJoin(left, right, conditionFn) {
725
- const result = [];
726
- for (const l of left) {
727
- for (const r of right) {
728
- const combined = { ...l, ...r };
729
- if (conditionFn(combined)) {
730
- result.push(combined);
731
- }
732
- }
733
- }
734
- return result;
735
- }
736
- function hashJoin(left, right, leftKeyFn, rightKeyFn) {
737
- const hashMap = /* @__PURE__ */ new Map();
738
- for (const r of right) {
739
- const key = rightKeyFn(r);
740
- let bucket = hashMap.get(key);
741
- if (!bucket) {
742
- bucket = [];
743
- hashMap.set(key, bucket);
744
- }
745
- bucket.push(r);
746
- }
747
- const result = [];
748
- for (const l of left) {
749
- const matches = hashMap.get(leftKeyFn(l));
750
- if (matches) {
751
- for (const r of matches) {
752
- result.push({ ...l, ...r });
753
- }
754
- }
755
- }
756
- return result;
757
- }
758
- function distinctFn(data, keyFn) {
759
- const seen = /* @__PURE__ */ new Set();
760
- return data.filter((row) => {
761
- const key = keyFn ? keyFn(row) : JSON.stringify(row);
762
- if (seen.has(key)) return false;
763
- seen.add(key);
764
- return true;
765
- });
766
- }
767
- function transposeFn(data, headerFn, headerFieldName) {
768
- if (data.length === 0) return [];
769
- const colNames = data.map(
770
- (row, i) => headerFn ? String(headerFn(row)) : `col_${i}`
771
- );
772
- const allFields = /* @__PURE__ */ new Set();
773
- for (const row of data) {
774
- for (const key of Object.keys(row)) {
775
- if (key !== "_group" && key !== headerFieldName) {
776
- allFields.add(key);
777
- }
778
- }
779
- }
780
- const results = [];
781
- for (const field of allFields) {
782
- const row = { _field: field };
783
- for (let i = 0; i < data.length; i++) {
784
- row[colNames[i]] = data[i][field] ?? null;
785
- }
786
- results.push(row);
787
- }
788
- return results;
789
- }
790
- function rollupFn(data, keyFns, aggFns) {
791
- const results = [];
792
- const numKeys = keyFns.length;
793
- for (let level = 0; level <= numKeys; level++) {
794
- const activeKeys = keyFns.slice(0, numKeys - level);
795
- const groups = groupByFn(data, activeKeys);
796
- for (const group of groups) {
797
- const row = {};
798
- for (const { name } of activeKeys) {
799
- row[name] = group.keys[name];
800
- }
801
- for (let i = numKeys - level; i < numKeys; i++) {
802
- row[keyFns[i].name] = null;
803
- }
804
- const groupedRow = { ...row, _group: group.rows };
805
- for (const { fn, name } of aggFns) {
806
- row[name] = fn(groupedRow);
807
- }
808
- row._rollupLevel = level;
809
- results.push(row);
810
- }
811
- }
812
- return results;
813
- }
814
- function defaultForAggregate(name) {
815
- if (name === "min" || name === "max") return null;
816
- return 0;
817
- }
818
- function pivotFlatFn(data, pivotFn, aggCompilers, singleAggregate) {
819
- const pivotGroups = /* @__PURE__ */ new Map();
820
- for (const row of data) {
821
- const pivotVal = String(pivotFn(row));
822
- if (!pivotGroups.has(pivotVal)) pivotGroups.set(pivotVal, []);
823
- pivotGroups.get(pivotVal).push(row);
824
- }
825
- const result = {};
826
- for (const [pivotVal, groupRows] of pivotGroups) {
827
- const syntheticRow = { _group: groupRows };
828
- const safeName = pivotVal.replace(/\s+/g, "_");
829
- for (const { fn, name } of aggCompilers) {
830
- const colName = singleAggregate ? safeName : `${name}_${safeName}`;
831
- result[colName] = fn(syntheticRow);
832
- }
833
- }
834
- return [result];
835
- }
836
- function pivotGroupedFn(data, pivotFn, aggCompilers, singleAggregate) {
837
- const allPivotValues = /* @__PURE__ */ new Set();
838
- for (const groupRow of data) {
839
- const groupData = groupRow._group;
840
- for (const row of groupData) {
841
- allPivotValues.add(String(pivotFn(row)));
842
- }
843
- }
844
- return data.map((groupRow) => {
845
- const groupData = groupRow._group;
846
- const result = {};
847
- for (const key of Object.keys(groupRow)) {
848
- if (key !== "_group") result[key] = groupRow[key];
849
- }
850
- const subGroups = /* @__PURE__ */ new Map();
851
- for (const row of groupData) {
852
- const pivotVal = String(pivotFn(row));
853
- if (!subGroups.has(pivotVal)) subGroups.set(pivotVal, []);
854
- subGroups.get(pivotVal).push(row);
855
- }
856
- for (const pivotVal of allPivotValues) {
857
- const subGroupRows = subGroups.get(pivotVal) || [];
858
- const syntheticRow = { _group: subGroupRows };
859
- const safeName = pivotVal.replace(/\s+/g, "_");
860
- for (const { fn, name } of aggCompilers) {
861
- const colName = singleAggregate ? safeName : `${name}_${safeName}`;
862
- result[colName] = subGroupRows.length > 0 ? fn(syntheticRow) : defaultForAggregate(name);
863
- }
864
- }
865
- return result;
866
- });
867
- }
868
- function computeWindowValues(data, windowDefs) {
869
- const n = data.length;
870
- const results = Array.from({ length: n }, () => ({}));
871
- for (const def of windowDefs) {
872
- switch (def.name) {
873
- case "row_number":
874
- case "running_count":
875
- for (let i = 0; i < n; i++) results[i][def.id] = i + 1;
876
- break;
877
- case "running_sum": {
878
- let acc = 0;
879
- for (let i = 0; i < n; i++) {
880
- acc += Number(def.fieldFn(data[i]));
881
- results[i][def.id] = acc;
882
- }
883
- break;
884
- }
885
- case "running_avg": {
886
- let acc = 0;
887
- for (let i = 0; i < n; i++) {
888
- acc += Number(def.fieldFn(data[i]));
889
- results[i][def.id] = acc / (i + 1);
890
- }
891
- break;
892
- }
893
- case "running_min": {
894
- let min = Infinity;
895
- for (let i = 0; i < n; i++) {
896
- const val = Number(def.fieldFn(data[i]));
897
- if (val < min) min = val;
898
- results[i][def.id] = min;
899
- }
900
- break;
901
- }
902
- case "running_max": {
903
- let max = -Infinity;
904
- for (let i = 0; i < n; i++) {
905
- const val = Number(def.fieldFn(data[i]));
906
- if (val > max) max = val;
907
- results[i][def.id] = max;
908
- }
909
- break;
910
- }
911
- case "lag": {
912
- const offset = def.offset;
913
- for (let i = 0; i < n; i++) {
914
- results[i][def.id] = i >= offset ? def.fieldFn(data[i - offset]) : null;
915
- }
916
- break;
917
- }
918
- case "lead": {
919
- const offset = def.offset;
920
- for (let i = 0; i < n; i++) {
921
- results[i][def.id] = i + offset < n ? def.fieldFn(data[i + offset]) : null;
922
- }
923
- break;
924
- }
925
- }
926
- }
927
- return results;
928
- }
929
-
930
- // src/engine/compiler.ts
931
- function computeMedian(values) {
932
- if (values.length === 0) return 0;
933
- const sorted = [...values].sort((a, b) => a - b);
934
- const mid = Math.floor(sorted.length / 2);
935
- return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
936
- }
937
- function computePercentile(values, p) {
938
- if (values.length === 0) return 0;
939
- const sorted = [...values].sort((a, b) => a - b);
940
- const idx = p / 100 * (sorted.length - 1);
941
- const lo = Math.floor(idx);
942
- const hi = Math.ceil(idx);
943
- if (lo === hi) return sorted[lo];
944
- return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);
945
- }
946
- function computeVariance(values) {
947
- if (values.length === 0) return 0;
948
- const mean = values.reduce((a, b) => a + b, 0) / values.length;
949
- return values.reduce((acc, v) => acc + (v - mean) ** 2, 0) / values.length;
950
- }
951
- function computeStddev(values) {
952
- return Math.sqrt(computeVariance(values));
953
- }
954
- function computeSkew(values) {
955
- const n = values.length;
956
- if (n < 3) return 0;
957
- const mean = values.reduce((a, b) => a + b, 0) / n;
958
- const sd = computeStddev(values);
959
- if (sd === 0) return 0;
960
- return values.reduce((acc, v) => acc + ((v - mean) / sd) ** 3, 0) / n;
961
- }
962
- function computeKurt(values) {
963
- const n = values.length;
964
- if (n < 4) return 0;
965
- const mean = values.reduce((a, b) => a + b, 0) / n;
966
- const sd = computeStddev(values);
967
- if (sd === 0) return 0;
968
- return values.reduce((acc, v) => acc + ((v - mean) / sd) ** 4, 0) / n - 3;
969
- }
970
- function computeDrawdown(values) {
971
- if (values.length === 0) return 0;
972
- let peak = values[0];
973
- let maxDd = 0;
974
- for (const v of values) {
975
- if (v > peak) peak = v;
976
- const dd = peak === 0 ? 0 : (v - peak) / peak;
977
- if (dd < maxDd) maxDd = dd;
978
- }
979
- return maxDd;
980
- }
981
- function computeDownsideDeviation(values, target = 0) {
982
- if (values.length === 0) return 0;
983
- const downside = values.filter((v) => v < target).map((v) => (v - target) ** 2);
984
- if (downside.length === 0) return 0;
985
- return Math.sqrt(downside.reduce((a, b) => a + b, 0) / values.length);
986
- }
987
- var WINDOW_FUNCTION_NAMES = /* @__PURE__ */ new Set([
988
- "running_sum",
989
- "running_avg",
990
- "running_count",
991
- "running_min",
992
- "running_max",
993
- "row_number",
994
- "lag",
995
- "lead"
996
- ]);
997
- var windowIdCounter = 0;
998
- function compileExpression(expr, grouped = false) {
999
- switch (expr.kind) {
1000
- case "NumberLiteral": {
1001
- const v = expr.value;
1002
- return () => v;
1003
- }
1004
- case "StringLiteral": {
1005
- const v = expr.value;
1006
- return () => v;
1007
- }
1008
- case "BooleanLiteral": {
1009
- const v = expr.value;
1010
- return () => v;
1011
- }
1012
- case "NullLiteral":
1013
- return () => null;
1014
- case "FieldAccess": {
1015
- const accessor = getFieldAccessor(expr.path);
1016
- return accessor;
1017
- }
1018
- case "BinaryExpr": {
1019
- const leftFn = compileExpression(expr.left, grouped);
1020
- const rightFn = compileExpression(expr.right, grouped);
1021
- const op = expr.operator;
1022
- switch (op) {
1023
- case ">":
1024
- return (row) => leftFn(row) > rightFn(row);
1025
- case ">=":
1026
- return (row) => leftFn(row) >= rightFn(row);
1027
- case "<":
1028
- return (row) => leftFn(row) < rightFn(row);
1029
- case "<=":
1030
- return (row) => leftFn(row) <= rightFn(row);
1031
- case "==":
1032
- return (row) => leftFn(row) === rightFn(row);
1033
- case "!=":
1034
- return (row) => leftFn(row) !== rightFn(row);
1035
- case "&&":
1036
- return (row) => leftFn(row) && rightFn(row);
1037
- case "||":
1038
- return (row) => leftFn(row) || rightFn(row);
1039
- case "+":
1040
- return (row) => leftFn(row) + rightFn(row);
1041
- case "-":
1042
- return (row) => leftFn(row) - rightFn(row);
1043
- case "*":
1044
- return (row) => leftFn(row) * rightFn(row);
1045
- case "/":
1046
- return (row) => leftFn(row) / rightFn(row);
1047
- case "%":
1048
- return (row) => leftFn(row) % rightFn(row);
1049
- }
1050
- break;
1051
- }
1052
- case "UnaryExpr": {
1053
- const operandFn = compileExpression(expr.operand, grouped);
1054
- if (expr.operator === "!") return (row) => !operandFn(row);
1055
- return (row) => -operandFn(row);
1056
- }
1057
- case "FunctionCall":
1058
- return compileFunctionCall(expr, grouped);
1059
- case "AliasExpr":
1060
- return compileExpression(expr.expression, grouped);
1061
- }
1062
- throw new RuntimeError(`Unknown expression kind: ${expr.kind}`);
1063
- }
1064
- function compileFunctionCall(expr, grouped) {
1065
- const { name, args } = expr;
1066
- if (grouped && AGGREGATE_NAMES.has(name)) {
1067
- return compileGroupAggregate(name, args);
1068
- }
1069
- if (WINDOW_FUNCTION_NAMES.has(name)) {
1070
- const id = `_w${windowIdCounter++}`;
1071
- const fieldFn = args.length > 0 ? compileExpression(args[0], false) : void 0;
1072
- const offset = args.length > 1 ? args[1].value || 1 : 1;
1073
- const fn = ((row) => {
1074
- const ctx = row._windowCtx;
1075
- return ctx?.[id];
1076
- });
1077
- fn._windowDef = { id, name, fieldFn, offset };
1078
- return fn;
1079
- }
1080
- switch (name) {
1081
- case "if": {
1082
- if (args.length < 2 || args.length > 3) {
1083
- throw new RuntimeError(`if() expects 2 or 3 arguments, got ${args.length}`);
1084
- }
1085
- const condFn = compileExpression(args[0], grouped);
1086
- const thenFn = compileExpression(args[1], grouped);
1087
- const elseFn = args.length === 3 ? compileExpression(args[2], grouped) : () => null;
1088
- return (row) => condFn(row) ? thenFn(row) : elseFn(row);
1089
- }
1090
- case "coalesce": {
1091
- const fns = args.map((a) => compileExpression(a, grouped));
1092
- return (row) => {
1093
- for (const fn of fns) {
1094
- const val = fn(row);
1095
- if (val != null) return val;
1096
- }
1097
- return null;
1098
- };
1099
- }
1100
- case "lower": {
1101
- const fn = compileExpression(args[0], grouped);
1102
- return (row) => String(fn(row)).toLowerCase();
1103
- }
1104
- case "upper": {
1105
- const fn = compileExpression(args[0], grouped);
1106
- return (row) => String(fn(row)).toUpperCase();
1107
- }
1108
- case "len": {
1109
- const fn = compileExpression(args[0], grouped);
1110
- return (row) => {
1111
- const val = fn(row);
1112
- if (Array.isArray(val)) return val.length;
1113
- if (typeof val === "string") return val.length;
1114
- return 0;
1115
- };
1116
- }
1117
- case "abs": {
1118
- const fn = compileExpression(args[0], grouped);
1119
- return (row) => Math.abs(fn(row));
1120
- }
1121
- case "round": {
1122
- const fn = compileExpression(args[0], grouped);
1123
- const precFn = args.length > 1 ? compileExpression(args[1], grouped) : () => 0;
1124
- return (row) => {
1125
- const val = fn(row);
1126
- const prec = precFn(row);
1127
- const factor = Math.pow(10, prec);
1128
- return Math.round(val * factor) / factor;
1129
- };
1130
- }
1131
- case "concat": {
1132
- const fns = args.map((a) => compileExpression(a, grouped));
1133
- return (row) => fns.map((fn) => String(fn(row))).join("");
1134
- }
1135
- // ── String functions ────────────────────────────────────
1136
- case "contains": {
1137
- if (args.length !== 2) throw new RuntimeError(`contains() expects 2 arguments, got ${args.length}`);
1138
- const strFn = compileExpression(args[0], grouped);
1139
- const searchFn = compileExpression(args[1], grouped);
1140
- return (row) => String(strFn(row)).includes(String(searchFn(row)));
1141
- }
1142
- case "startsWith": {
1143
- if (args.length !== 2) throw new RuntimeError(`startsWith() expects 2 arguments, got ${args.length}`);
1144
- const strFn = compileExpression(args[0], grouped);
1145
- const prefixFn = compileExpression(args[1], grouped);
1146
- return (row) => String(strFn(row)).startsWith(String(prefixFn(row)));
1147
- }
1148
- case "endsWith": {
1149
- if (args.length !== 2) throw new RuntimeError(`endsWith() expects 2 arguments, got ${args.length}`);
1150
- const strFn = compileExpression(args[0], grouped);
1151
- const suffixFn = compileExpression(args[1], grouped);
1152
- return (row) => String(strFn(row)).endsWith(String(suffixFn(row)));
1153
- }
1154
- case "trim": {
1155
- if (args.length !== 1) throw new RuntimeError(`trim() expects 1 argument, got ${args.length}`);
1156
- const fn = compileExpression(args[0], grouped);
1157
- return (row) => String(fn(row)).trim();
1158
- }
1159
- case "substring": {
1160
- if (args.length < 2 || args.length > 3) throw new RuntimeError(`substring() expects 2 or 3 arguments, got ${args.length}`);
1161
- const strFn = compileExpression(args[0], grouped);
1162
- const startFn = compileExpression(args[1], grouped);
1163
- const lenFn = args.length === 3 ? compileExpression(args[2], grouped) : void 0;
1164
- return (row) => {
1165
- const s = String(strFn(row));
1166
- const start = Number(startFn(row));
1167
- if (lenFn) {
1168
- return s.substring(start, start + Number(lenFn(row)));
1169
- }
1170
- return s.substring(start);
1171
- };
1172
- }
1173
- case "replace": {
1174
- if (args.length !== 3) throw new RuntimeError(`replace() expects 3 arguments, got ${args.length}`);
1175
- const strFn = compileExpression(args[0], grouped);
1176
- const searchFn = compileExpression(args[1], grouped);
1177
- const replaceFn = compileExpression(args[2], grouped);
1178
- return (row) => String(strFn(row)).replaceAll(String(searchFn(row)), String(replaceFn(row)));
1179
- }
1180
- // Non-grouped aggregates used as functions return NaN or undefined — they're
1181
- // only meaningful as pipeline-level AggregateOps or in grouped selects
1182
- default:
1183
- throw new RuntimeError(`Unknown function: ${name}`);
1184
- }
1185
- }
1186
- function compileGroupAggregate(name, args) {
1187
- const fieldFn = args.length > 0 ? compileExpression(args[0], false) : void 0;
1188
- const secondFn = args.length > 1 ? compileExpression(args[1], false) : void 0;
1189
- const extractValues = (row) => row._group.map((r) => Number(fieldFn(r)));
1190
- switch (name) {
1191
- // ── Core ───────────────────────────────────────────────
1192
- case "sum":
1193
- return (row) => {
1194
- const group = row._group;
1195
- return group.reduce((acc, r) => acc + Number(fieldFn(r)), 0);
1196
- };
1197
- case "avg":
1198
- return (row) => {
1199
- const group = row._group;
1200
- if (group.length === 0) return 0;
1201
- const total = group.reduce((acc, r) => acc + Number(fieldFn(r)), 0);
1202
- return total / group.length;
1203
- };
1204
- case "min":
1205
- return (row) => {
1206
- const group = row._group;
1207
- if (group.length === 0) return null;
1208
- return group.reduce((min, r) => {
1209
- const val = fieldFn(r);
1210
- return val < min ? val : min;
1211
- }, fieldFn(group[0]));
1212
- };
1213
- case "max":
1214
- return (row) => {
1215
- const group = row._group;
1216
- if (group.length === 0) return null;
1217
- return group.reduce((max, r) => {
1218
- const val = fieldFn(r);
1219
- return val > max ? val : max;
1220
- }, fieldFn(group[0]));
1221
- };
1222
- case "count":
1223
- return (row) => {
1224
- const group = row._group;
1225
- return group.length;
1226
- };
1227
- // ── Statistical ───────────────────────────────────────
1228
- case "median":
1229
- return (row) => computeMedian(extractValues(row));
1230
- case "stddev":
1231
- return (row) => computeStddev(extractValues(row));
1232
- case "var":
1233
- return (row) => computeVariance(extractValues(row));
1234
- case "percentile":
1235
- return (row) => computePercentile(extractValues(row), Number(secondFn(row)));
1236
- case "skew":
1237
- return (row) => computeSkew(extractValues(row));
1238
- case "kurt":
1239
- return (row) => computeKurt(extractValues(row));
1240
- // ── Finance ───────────────────────────────────────────
1241
- case "vwap":
1242
- return (row) => {
1243
- const group = row._group;
1244
- let sumPV = 0, sumV = 0;
1245
- for (const r of group) {
1246
- const p = Number(fieldFn(r));
1247
- const v = Number(secondFn(r));
1248
- sumPV += p * v;
1249
- sumV += v;
1250
- }
1251
- return sumV === 0 ? 0 : sumPV / sumV;
1252
- };
1253
- case "wavg":
1254
- return (row) => {
1255
- const group = row._group;
1256
- let sumWV = 0, sumW = 0;
1257
- for (const r of group) {
1258
- const val = Number(fieldFn(r));
1259
- const w = Number(secondFn(r));
1260
- sumWV += val * w;
1261
- sumW += w;
1262
- }
1263
- return sumW === 0 ? 0 : sumWV / sumW;
1264
- };
1265
- case "drawdown":
1266
- return (row) => computeDrawdown(extractValues(row));
1267
- // ── Ratios ────────────────────────────────────────────
1268
- case "pct":
1269
- return (row) => {
1270
- const group = row._group;
1271
- return group.reduce((acc, r) => acc + Number(fieldFn(r)), 0);
1272
- };
1273
- case "sharpe":
1274
- return (row) => {
1275
- const vals = extractValues(row);
1276
- if (vals.length === 0) return 0;
1277
- const mean = vals.reduce((a, b) => a + b, 0) / vals.length;
1278
- const sd = computeStddev(vals);
1279
- return sd === 0 ? 0 : mean / sd;
1280
- };
1281
- case "calmar":
1282
- return (row) => {
1283
- const vals = extractValues(row);
1284
- if (vals.length === 0) return 0;
1285
- const mean = vals.reduce((a, b) => a + b, 0) / vals.length;
1286
- const dd = Math.abs(computeDrawdown(vals));
1287
- return dd === 0 ? 0 : mean / dd;
1288
- };
1289
- case "sortino":
1290
- return (row) => {
1291
- const vals = extractValues(row);
1292
- if (vals.length === 0) return 0;
1293
- const mean = vals.reduce((a, b) => a + b, 0) / vals.length;
1294
- const dsd = computeDownsideDeviation(vals, 0);
1295
- return dsd === 0 ? 0 : mean / dsd;
1296
- };
1297
- case "info_ratio":
1298
- return (row) => {
1299
- const group = row._group;
1300
- const excess = group.map((r) => Number(fieldFn(r)) - Number(secondFn(r)));
1301
- if (excess.length === 0) return 0;
1302
- const mean = excess.reduce((a, b) => a + b, 0) / excess.length;
1303
- const sd = computeStddev(excess);
1304
- return sd === 0 ? 0 : mean / sd;
1305
- };
1306
- // ── Counting ──────────────────────────────────────────
1307
- case "distinct_count":
1308
- return (row) => {
1309
- const group = row._group;
1310
- return new Set(group.map((r) => fieldFn(r))).size;
1311
- };
1312
- case "sum_abs":
1313
- return (row) => {
1314
- const group = row._group;
1315
- return group.reduce((acc, r) => acc + Math.abs(Number(fieldFn(r))), 0);
1316
- };
1317
- case "abs_sum":
1318
- return (row) => {
1319
- const group = row._group;
1320
- return Math.abs(group.reduce((acc, r) => acc + Number(fieldFn(r)), 0));
1321
- };
1322
- // ── Range ─────────────────────────────────────────────
1323
- case "first_value":
1324
- return (row) => {
1325
- const group = row._group;
1326
- return group.length > 0 ? fieldFn(group[0]) : null;
1327
- };
1328
- case "last_value":
1329
- return (row) => {
1330
- const group = row._group;
1331
- return group.length > 0 ? fieldFn(group[group.length - 1]) : null;
1332
- };
1333
- default:
1334
- throw new RuntimeError(`Unknown aggregate function: ${name}`);
1335
- }
1336
- }
1337
- function resolveFieldName(expr) {
1338
- switch (expr.kind) {
1339
- case "AliasExpr":
1340
- return expr.alias;
1341
- case "FieldAccess":
1342
- return expr.path[expr.path.length - 1];
1343
- case "FunctionCall":
1344
- return expr.name;
1345
- default:
1346
- return "value";
1347
- }
1348
- }
1349
- function compileOperation(op, isGrouped) {
1350
- switch (op.kind) {
1351
- case "WhereOp": {
1352
- const condFn = compileExpression(op.condition);
1353
- return {
1354
- fn: (data) => data.filter((row) => condFn(row)),
1355
- producesGrouped: false
1356
- };
1357
- }
1358
- case "SelectOp":
1359
- return compileSelectOp(op, isGrouped);
1360
- case "SortOp": {
1361
- const criteria = op.criteria.map((c) => ({
1362
- fn: compileExpression(c.expression),
1363
- dir: c.direction === "desc" ? -1 : 1
1364
- }));
1365
- return {
1366
- fn: (data) => {
1367
- return [...data].sort((a, b) => {
1368
- for (const { fn, dir } of criteria) {
1369
- const cmp = compareValues(fn(a), fn(b));
1370
- if (cmp !== 0) return cmp * dir;
1371
- }
1372
- return 0;
1373
- });
1374
- },
1375
- producesGrouped: false
1376
- };
1377
- }
1378
- case "GroupByOp": {
1379
- const keyFns = op.keys.map((k) => {
1380
- const fn = compileExpression(k);
1381
- const name = k.kind === "FieldAccess" ? k.path[k.path.length - 1] : "key";
1382
- return { fn, name };
1383
- });
1384
- return {
1385
- fn: (data) => {
1386
- const groups = groupByFn(data, keyFns);
1387
- return groups.map((g) => ({ ...g.keys, _group: g.rows }));
1388
- },
1389
- producesGrouped: true
1390
- };
1391
- }
1392
- case "JoinOp": {
1393
- const rightSource = op.right;
1394
- if (op.condition.kind === "BinaryExpr" && op.condition.operator === "==" && op.condition.left.kind === "FieldAccess" && op.condition.right.kind === "FieldAccess") {
1395
- const leftKeyFn = compileExpression(op.condition.left);
1396
- const rightKeyFn = compileExpression(op.condition.right);
1397
- return {
1398
- fn: (data, context) => {
1399
- const rightData = context[rightSource];
1400
- if (!rightData) {
1401
- throw new RuntimeError(`Join source "${rightSource}" not found in context`);
1402
- }
1403
- return hashJoin(data, rightData, leftKeyFn, rightKeyFn);
1404
- },
1405
- producesGrouped: false
1406
- };
1407
- }
1408
- const condFn = compileExpression(op.condition);
1409
- return {
1410
- fn: (data, context) => {
1411
- const rightData = context[rightSource];
1412
- if (!rightData) {
1413
- throw new RuntimeError(`Join source "${rightSource}" not found in context`);
1414
- }
1415
- return nestedLoopJoin(data, rightData, condFn);
1416
- },
1417
- producesGrouped: false
1418
- };
1419
- }
1420
- case "FirstOp": {
1421
- const count = op.count;
1422
- return {
1423
- fn: (data) => data.slice(0, count),
1424
- producesGrouped: false
1425
- };
1426
- }
1427
- case "LastOp": {
1428
- const count = op.count;
1429
- return {
1430
- fn: (data) => data.slice(-count),
1431
- producesGrouped: false
1432
- };
1433
- }
1434
- case "DistinctOp": {
1435
- const keyFn = op.fields ? (() => {
1436
- const fns = op.fields.map((f) => compileExpression(f));
1437
- return (row) => JSON.stringify(fns.map((fn) => fn(row)));
1438
- })() : void 0;
1439
- return {
1440
- fn: (data) => distinctFn(data, keyFn),
1441
- producesGrouped: false
1442
- };
1443
- }
1444
- case "FlattenOp": {
1445
- if (op.field) {
1446
- const fieldFn = compileExpression(op.field);
1447
- return {
1448
- fn: (data) => data.flatMap((row) => {
1449
- const val = fieldFn(row);
1450
- return Array.isArray(val) ? val : [val];
1451
- }),
1452
- producesGrouped: false
1453
- };
1454
- }
1455
- return {
1456
- fn: (data) => data.flat(),
1457
- producesGrouped: false
1458
- };
1459
- }
1460
- case "TransposeOp": {
1461
- const headerFn = op.headerField ? compileExpression(op.headerField) : void 0;
1462
- const headerFieldName = op.headerField ? resolveFieldName(op.headerField) : void 0;
1463
- return {
1464
- fn: (data) => transposeFn(data, headerFn, headerFieldName),
1465
- producesGrouped: false
1466
- };
1467
- }
1468
- case "RollupOp": {
1469
- const keyFns = op.keys.map((k) => {
1470
- const fn = compileExpression(k);
1471
- const name = resolveFieldName(k);
1472
- return { fn, name };
1473
- });
1474
- const aggFns = op.aggregates.map((a) => {
1475
- const fn = compileExpression(a, true);
1476
- const name = resolveFieldName(a);
1477
- return { fn, name };
1478
- });
1479
- const pctCols = /* @__PURE__ */ new Set();
1480
- for (const a of op.aggregates) {
1481
- const expr = a.kind === "AliasExpr" ? a.expression : a;
1482
- if (expr.kind === "FunctionCall" && expr.name === "pct") {
1483
- pctCols.add(resolveFieldName(a));
1484
- }
1485
- }
1486
- return {
1487
- fn: (data) => {
1488
- const results = rollupFn(data, keyFns, aggFns);
1489
- if (pctCols.size > 0) {
1490
- const numKeys = keyFns.length;
1491
- const grandRow = results.find((r) => r._rollupLevel === numKeys);
1492
- for (const col of pctCols) {
1493
- const total = Number(grandRow?.[col]) || 1;
1494
- for (const row of results) {
1495
- row[col] = Number(row[col]) / total * 100;
1496
- }
1497
- }
1498
- if (pctCols.size === aggFns.length) {
1499
- return results.filter((r) => r._rollupLevel !== numKeys);
1500
- }
1501
- }
1502
- return results;
1503
- },
1504
- producesGrouped: false
1505
- };
1506
- }
1507
- case "PivotOp": {
1508
- const pivotFn = compileExpression(op.pivotField);
1509
- const aggCompilers = op.aggregates.map((a) => ({
1510
- fn: compileExpression(a, true),
1511
- // grouped = true
1512
- name: resolveFieldName(a)
1513
- }));
1514
- const singleAggregate = aggCompilers.length === 1;
1515
- return {
1516
- fn: (data) => {
1517
- if (isGrouped) {
1518
- return pivotGroupedFn(data, pivotFn, aggCompilers, singleAggregate);
1519
- }
1520
- return pivotFlatFn(data, pivotFn, aggCompilers, singleAggregate);
1521
- },
1522
- producesGrouped: false
1523
- };
1524
- }
1525
- case "MapOp": {
1526
- const fieldCompilers = op.fields.map((field) => {
1527
- const name = resolveFieldName(field);
1528
- const fn = compileExpression(field, isGrouped);
1529
- return { name, fn };
1530
- });
1531
- return {
1532
- fn: (data) => data.map((row) => {
1533
- const result = { ...row };
1534
- for (const { name, fn } of fieldCompilers) {
1535
- result[name] = fn(row);
1536
- }
1537
- return result;
1538
- }),
1539
- producesGrouped: isGrouped
1540
- };
1541
- }
1542
- case "ReduceOp": {
1543
- const initialFn = compileExpression(op.initial);
1544
- const accFn = compileExpression(op.accumulator);
1545
- return {
1546
- fn: (data) => {
1547
- let acc = initialFn({});
1548
- for (const row of data) {
1549
- acc = accFn({ ...row, _acc: acc });
1550
- }
1551
- return acc;
1552
- },
1553
- producesGrouped: false
1554
- };
1555
- }
1556
- case "AggregateOp": {
1557
- const argFns = op.args?.map((a) => compileExpression(a));
1558
- const primaryFn = argFns ? argFns[0] : op.field ? compileExpression(op.field) : void 0;
1559
- const secondFn = argFns ? argFns[1] : void 0;
1560
- const aggName = op.function;
1561
- return {
1562
- fn: (data) => {
1563
- const vals = () => data.map((row) => Number(primaryFn(row)));
1564
- switch (aggName) {
1565
- // ── Core ──────────────────────────────────────
1566
- case "count":
1567
- return data.length;
1568
- case "sum":
1569
- return data.reduce((acc, row) => acc + Number(primaryFn(row)), 0);
1570
- case "avg": {
1571
- if (data.length === 0) return 0;
1572
- const total = data.reduce((acc, row) => acc + Number(primaryFn(row)), 0);
1573
- return total / data.length;
1574
- }
1575
- case "min":
1576
- return data.reduce((min, row) => {
1577
- const val = primaryFn(row);
1578
- return val < min ? val : min;
1579
- }, primaryFn(data[0]));
1580
- case "max":
1581
- return data.reduce((max, row) => {
1582
- const val = primaryFn(row);
1583
- return val > max ? val : max;
1584
- }, primaryFn(data[0]));
1585
- // ── Statistical ───────────────────────────────
1586
- case "median":
1587
- return computeMedian(vals());
1588
- case "stddev":
1589
- return computeStddev(vals());
1590
- case "var":
1591
- return computeVariance(vals());
1592
- case "percentile":
1593
- return computePercentile(vals(), Number(secondFn(data[0])));
1594
- case "skew":
1595
- return computeSkew(vals());
1596
- case "kurt":
1597
- return computeKurt(vals());
1598
- // ── Finance ───────────────────────────────────
1599
- case "vwap": {
1600
- let sumPV = 0, sumV = 0;
1601
- for (const row of data) {
1602
- const p = Number(primaryFn(row));
1603
- const v = Number(secondFn(row));
1604
- sumPV += p * v;
1605
- sumV += v;
1606
- }
1607
- return sumV === 0 ? 0 : sumPV / sumV;
1608
- }
1609
- case "wavg": {
1610
- let sumWV = 0, sumW = 0;
1611
- for (const row of data) {
1612
- const val = Number(primaryFn(row));
1613
- const w = Number(secondFn(row));
1614
- sumWV += val * w;
1615
- sumW += w;
1616
- }
1617
- return sumW === 0 ? 0 : sumWV / sumW;
1618
- }
1619
- case "drawdown":
1620
- return computeDrawdown(vals());
1621
- // ── Ratios ────────────────────────────────────
1622
- case "pct": {
1623
- return data.reduce((acc, row) => acc + Number(primaryFn(row)), 0);
1624
- }
1625
- case "sharpe": {
1626
- const v = vals();
1627
- if (v.length === 0) return 0;
1628
- const mean = v.reduce((a, b) => a + b, 0) / v.length;
1629
- const sd = computeStddev(v);
1630
- return sd === 0 ? 0 : mean / sd;
1631
- }
1632
- case "calmar": {
1633
- const v = vals();
1634
- if (v.length === 0) return 0;
1635
- const mean = v.reduce((a, b) => a + b, 0) / v.length;
1636
- const dd = Math.abs(computeDrawdown(v));
1637
- return dd === 0 ? 0 : mean / dd;
1638
- }
1639
- case "sortino": {
1640
- const v = vals();
1641
- if (v.length === 0) return 0;
1642
- const mean = v.reduce((a, b) => a + b, 0) / v.length;
1643
- const dsd = computeDownsideDeviation(v, 0);
1644
- return dsd === 0 ? 0 : mean / dsd;
1645
- }
1646
- case "info_ratio": {
1647
- const excess = data.map((row) => Number(primaryFn(row)) - Number(secondFn(row)));
1648
- if (excess.length === 0) return 0;
1649
- const mean = excess.reduce((a, b) => a + b, 0) / excess.length;
1650
- const sd = computeStddev(excess);
1651
- return sd === 0 ? 0 : mean / sd;
1652
- }
1653
- // ── Counting ──────────────────────────────────
1654
- case "distinct_count":
1655
- return new Set(data.map((row) => primaryFn(row))).size;
1656
- case "sum_abs":
1657
- return data.reduce((acc, row) => acc + Math.abs(Number(primaryFn(row))), 0);
1658
- case "abs_sum":
1659
- return Math.abs(data.reduce((acc, row) => acc + Number(primaryFn(row)), 0));
1660
- // ── Range ─────────────────────────────────────
1661
- case "first_value":
1662
- return data.length > 0 ? primaryFn(data[0]) : null;
1663
- case "last_value":
1664
- return data.length > 0 ? primaryFn(data[data.length - 1]) : null;
1665
- }
1666
- },
1667
- producesGrouped: false
1668
- };
1669
- }
1670
- }
1671
- }
1672
- function compileSelectOp(op, isGrouped) {
1673
- const fieldCompilers = op.fields.map((field) => {
1674
- const name = resolveFieldName(field);
1675
- const fn = compileExpression(field, isGrouped);
1676
- return { name, fn };
1677
- });
1678
- const windowDefs = [];
1679
- for (const { fn } of fieldCompilers) {
1680
- const wDef = fn._windowDef;
1681
- if (wDef) windowDefs.push(wDef);
1682
- }
1683
- if (windowDefs.length > 0) {
1684
- return {
1685
- fn: (data) => {
1686
- const windowValues = computeWindowValues(data, windowDefs);
1687
- return data.map((row, i) => {
1688
- const enrichedRow = { ...row, _windowCtx: windowValues[i] };
1689
- const result = {};
1690
- for (const { name, fn } of fieldCompilers) {
1691
- result[name] = fn(enrichedRow);
1692
- }
1693
- return result;
1694
- });
1695
- },
1696
- producesGrouped: false
1697
- };
1698
- }
1699
- return {
1700
- fn: (data) => {
1701
- return data.map((row) => {
1702
- const result = {};
1703
- for (const { name, fn } of fieldCompilers) {
1704
- result[name] = fn(row);
1705
- }
1706
- return result;
1707
- });
1708
- },
1709
- producesGrouped: false
1710
- };
1711
- }
1712
- function compile(ast, source) {
1713
- const compiledOps = [];
1714
- let isGrouped = false;
1715
- for (const op of ast.operations) {
1716
- const { fn, producesGrouped } = compileOperation(op, isGrouped);
1717
- compiledOps.push(fn);
1718
- isGrouped = producesGrouped;
1719
- }
1720
- const sourceName = ast.source;
1721
- const queryFn = (context) => {
1722
- const sourceData = context[sourceName];
1723
- if (!sourceData) {
1724
- throw new RuntimeError(`Source "${sourceName}" not found in context`);
1725
- }
1726
- if (!Array.isArray(sourceData)) {
1727
- throw new RuntimeError(`Source "${sourceName}" is not an array`);
1728
- }
1729
- let data = sourceData;
1730
- for (const opFn of compiledOps) {
1731
- if (!Array.isArray(data)) {
1732
- return data;
1733
- }
1734
- data = opFn(data, context);
1735
- }
1736
- return data;
1737
- };
1738
- const compiled = queryFn;
1739
- Object.defineProperty(compiled, "source", { value: source, writable: false });
1740
- Object.defineProperty(compiled, "ast", { value: ast, writable: false });
1741
- return compiled;
1742
- }
1743
-
1744
- // src/engine/cache.ts
1745
- var QueryCache = class {
1746
- cache = /* @__PURE__ */ new Map();
1747
- maxSize;
1748
- constructor(maxSize = 128) {
1749
- this.maxSize = maxSize;
1750
- }
1751
- get(key) {
1752
- const value = this.cache.get(key);
1753
- if (value) {
1754
- this.cache.delete(key);
1755
- this.cache.set(key, value);
1756
- }
1757
- return value;
1758
- }
1759
- set(key, value) {
1760
- if (this.cache.has(key)) {
1761
- this.cache.delete(key);
1762
- } else if (this.cache.size >= this.maxSize) {
1763
- const firstKey = this.cache.keys().next().value;
1764
- if (firstKey !== void 0) {
1765
- this.cache.delete(firstKey);
1766
- }
1767
- }
1768
- this.cache.set(key, value);
1769
- }
1770
- clear() {
1771
- this.cache.clear();
1772
- }
1773
- get size() {
1774
- return this.cache.size;
1775
- }
1776
- };
1777
-
1778
- // src/engine/live.ts
1779
- var OPERATION_KEYWORDS = /* @__PURE__ */ new Set([
1780
- "where",
1781
- "select",
1782
- "sort",
1783
- "groupBy",
1784
- "join",
1785
- "first",
1786
- "last",
1787
- "distinct",
1788
- "map",
1789
- "reduce",
1790
- "rollup",
1791
- "pivot",
1792
- "flatten",
1793
- "transpose"
1794
- ]);
1795
- function normalizeExpression(expression) {
1796
- const trimmed = expression.trimStart();
1797
- const match = trimmed.match(/^(\w+)\s*\(/);
1798
- if (match && OPERATION_KEYWORDS.has(match[1])) {
1799
- return `_data | ${trimmed}`;
1800
- }
1801
- return expression;
1802
- }
1803
- var LiveQuery = class {
1804
- _index;
1805
- _keyFields;
1806
- _sourceName;
1807
- _context;
1808
- _compiled;
1809
- _throttle;
1810
- _pendingTimer;
1811
- _executing;
1812
- _dirtyDuringExec;
1813
- _result;
1814
- _stats;
1815
- _subscribers;
1816
- _batchDepth;
1817
- _batchDirty;
1818
- _disposed;
1819
- constructor(data, expression, options) {
1820
- this._keyFields = Array.isArray(options.key) ? options.key : [options.key];
1821
- this._throttle = options.throttle ?? 0;
1822
- this._pendingTimer = null;
1823
- this._executing = false;
1824
- this._dirtyDuringExec = false;
1825
- this._subscribers = /* @__PURE__ */ new Set();
1826
- this._batchDepth = 0;
1827
- this._batchDirty = false;
1828
- this._disposed = false;
1829
- this._result = void 0;
1830
- this._stats = {
1831
- patchMs: 0,
1832
- executionMs: 0,
1833
- totalMs: 0,
1834
- rowCount: 0,
1835
- resultCount: 0,
1836
- patchCount: 0,
1837
- tick: 0
1838
- };
1839
- if (Array.isArray(data)) {
1840
- this._sourceName = "_data";
1841
- this._compiled = compile2(normalizeExpression(expression));
1842
- this._context = { _data: [] };
1843
- this._index = /* @__PURE__ */ new Map();
1844
- this._buildIndex(data);
1845
- } else {
1846
- this._sourceName = options.source ?? Object.keys(data)[0];
1847
- this._compiled = compile2(expression);
1848
- this._context = { ...data };
1849
- this._index = /* @__PURE__ */ new Map();
1850
- const sourceData = data[this._sourceName];
1851
- if (Array.isArray(sourceData)) {
1852
- this._buildIndex(sourceData);
1853
- }
1854
- }
1855
- this._executeSync();
1856
- }
1857
- // ── Public getters ─────────────────────────────────────────────
1858
- get result() {
1859
- return this._result;
1860
- }
1861
- get stats() {
1862
- return { ...this._stats };
1863
- }
1864
- get size() {
1865
- return this._index.size;
1866
- }
1867
- // ── Subscription ───────────────────────────────────────────────
1868
- subscribe(fn) {
1869
- this._guardDisposed();
1870
- this._subscribers.add(fn);
1871
- return () => {
1872
- this._subscribers.delete(fn);
1873
- };
1874
- }
1875
- // ── Patching ───────────────────────────────────────────────────
1876
- patch(changed, removals) {
1877
- this._guardDisposed();
1878
- const patchStart = performance.now();
1879
- for (const row of changed) {
1880
- const mapKey = this._rowKey(row);
1881
- this._index.set(mapKey, row);
1882
- }
1883
- if (removals) {
1884
- for (const removal of removals) {
1885
- const mapKey = this._removalKey(removal);
1886
- this._index.delete(mapKey);
1887
- }
1888
- }
1889
- this._stats.patchMs = performance.now() - patchStart;
1890
- this._stats.patchCount++;
1891
- if (this._batchDepth > 0) {
1892
- this._batchDirty = true;
1893
- return;
1894
- }
1895
- if (this._executing) {
1896
- this._dirtyDuringExec = true;
1897
- return;
1898
- }
1899
- this._scheduleExecution();
1900
- }
1901
- // ── Reset ──────────────────────────────────────────────────────
1902
- reset(data) {
1903
- this._guardDisposed();
1904
- this._index.clear();
1905
- this._buildIndex(data);
1906
- this._stats.patchMs = 0;
1907
- this._scheduleExecution();
1908
- }
1909
- // ── Query change ───────────────────────────────────────────────
1910
- setQuery(expression) {
1911
- this._guardDisposed();
1912
- const normalized = this._sourceName === "_data" ? normalizeExpression(expression) : expression;
1913
- this._compiled = compile2(normalized);
1914
- this._stats.patchMs = 0;
1915
- this._scheduleExecution();
1916
- }
1917
- // ── Batching ───────────────────────────────────────────────────
1918
- beginBatch() {
1919
- this._guardDisposed();
1920
- this._batchDepth++;
1921
- }
1922
- endBatch() {
1923
- this._guardDisposed();
1924
- if (this._batchDepth <= 0) {
1925
- throw new RuntimeError("LiveQuery: endBatch() called without matching beginBatch()");
1926
- }
1927
- this._batchDepth--;
1928
- if (this._batchDepth === 0 && this._batchDirty) {
1929
- this._batchDirty = false;
1930
- this._scheduleExecution();
1931
- }
1932
- }
1933
- // ── Cleanup ────────────────────────────────────────────────────
1934
- dispose() {
1935
- if (this._pendingTimer !== null) {
1936
- clearTimeout(this._pendingTimer);
1937
- this._pendingTimer = null;
1938
- }
1939
- this._subscribers.clear();
1940
- this._index.clear();
1941
- this._result = void 0;
1942
- this._disposed = true;
1943
- }
1944
- // ── Private: key helpers ───────────────────────────────────────
1945
- _rowKey(row) {
1946
- if (this._keyFields.length === 1) {
1947
- const val = row[this._keyFields[0]];
1948
- if (val == null) {
1949
- throw new RuntimeError(
1950
- `LiveQuery: row is missing key field "${this._keyFields[0]}"`
1951
- );
1952
- }
1953
- return String(val);
1954
- }
1955
- let key = "";
1956
- for (let i = 0; i < this._keyFields.length; i++) {
1957
- const val = row[this._keyFields[i]];
1958
- if (val == null) {
1959
- throw new RuntimeError(
1960
- `LiveQuery: row is missing key field "${this._keyFields[i]}"`
1961
- );
1962
- }
1963
- if (i > 0) key += "\0";
1964
- key += String(val);
1965
- }
1966
- return key;
1967
- }
1968
- _removalKey(removal) {
1969
- if (typeof removal === "string") {
1970
- return removal;
1971
- }
1972
- return removal.map(String).join("\0");
1973
- }
1974
- // ── Private: index ─────────────────────────────────────────────
1975
- _buildIndex(rows) {
1976
- for (const row of rows) {
1977
- const key = this._rowKey(row);
1978
- this._index.set(key, row);
1979
- }
1980
- }
1981
- // ── Private: execution scheduling ──────────────────────────────
1982
- _scheduleExecution() {
1983
- if (this._pendingTimer !== null) return;
1984
- if (this._throttle === 0) {
1985
- this._pendingTimer = setTimeout(() => this._onTimer(), 0);
1986
- } else {
1987
- this._pendingTimer = setTimeout(() => this._onTimer(), this._throttle);
1988
- }
1989
- }
1990
- _onTimer() {
1991
- this._pendingTimer = null;
1992
- if (this._disposed) return;
1993
- this._executeSync();
1994
- if (this._dirtyDuringExec) {
1995
- this._dirtyDuringExec = false;
1996
- this._scheduleExecution();
1997
- }
1998
- }
1999
- _executeSync() {
2000
- this._executing = true;
2001
- this._dirtyDuringExec = false;
2002
- const rows = Array.from(this._index.values());
2003
- this._context[this._sourceName] = rows;
2004
- const execStart = performance.now();
2005
- this._result = this._compiled(this._context);
2006
- const executionMs = performance.now() - execStart;
2007
- this._stats.executionMs = executionMs;
2008
- this._stats.totalMs = this._stats.patchMs + executionMs;
2009
- this._stats.rowCount = rows.length;
2010
- this._stats.resultCount = Array.isArray(this._result) ? this._result.length : this._result != null ? 1 : 0;
2011
- this._stats.tick++;
2012
- this._executing = false;
2013
- this._notify();
2014
- }
2015
- _notify() {
2016
- const result = this._result;
2017
- const stats = { ...this._stats };
2018
- for (const fn of this._subscribers) {
2019
- try {
2020
- fn(result, stats);
2021
- } catch (err) {
2022
- console.error("LiveQuery subscriber error:", err);
2023
- }
2024
- }
2025
- }
2026
- _guardDisposed() {
2027
- if (this._disposed) {
2028
- throw new RuntimeError("LiveQuery: instance has been disposed");
2029
- }
2030
- }
2031
- };
2032
- function liveQuery(data, expression, options) {
2033
- return new LiveQuery(data, expression, options);
2034
- }
2035
-
2036
- // src/engine/index.ts
2037
- var defaultCache = new QueryCache(128);
2038
- var OPERATION_KEYWORDS2 = /* @__PURE__ */ new Set([
2039
- "where",
2040
- "select",
2041
- "sort",
2042
- "groupBy",
2043
- "join",
2044
- "first",
2045
- "last",
2046
- "distinct",
2047
- "map",
2048
- "reduce",
2049
- "rollup",
2050
- "pivot",
2051
- "flatten",
2052
- "transpose"
2053
- ]);
2054
- function normalizeExpression2(expression) {
2055
- const trimmed = expression.trimStart();
2056
- const match = trimmed.match(/^(\w+)\s*\(/);
2057
- if (match && OPERATION_KEYWORDS2.has(match[1])) {
2058
- return `_data | ${trimmed}`;
2059
- }
2060
- return expression;
2061
- }
2062
- function compile2(expression, useCache = true) {
2063
- if (useCache) {
2064
- const cached = defaultCache.get(expression);
2065
- if (cached) return cached;
2066
- }
2067
- const tokens = tokenize(expression);
2068
- const ast = parse(tokens);
2069
- const fn = compile(ast, expression);
2070
- if (useCache) {
2071
- defaultCache.set(expression, fn);
2072
- }
2073
- return fn;
2074
- }
2075
- function query(context, expression) {
2076
- if (Array.isArray(context)) {
2077
- const normalized = normalizeExpression2(expression);
2078
- const fn2 = compile2(normalized);
2079
- return fn2({ _data: context });
2080
- }
2081
- const fn = compile2(expression);
2082
- return fn(context);
2083
- }
2084
- function parseQuery(expression) {
2085
- const tokens = tokenize(expression);
2086
- return parse(tokens);
2087
- }
2088
- function clearCache() {
2089
- defaultCache.clear();
2090
- }
1
+ import {
2
+ DataWeaveError,
3
+ LexerError,
4
+ LiveQuery,
5
+ ParseError,
6
+ RuntimeError,
7
+ clearCache,
8
+ compile,
9
+ compileAst,
10
+ liveQuery,
11
+ parseQuery,
12
+ printExpression,
13
+ printQuery,
14
+ query
15
+ } from "../chunk-KNVFXND5.js";
2091
16
  export {
2092
17
  DataWeaveError,
2093
18
  LexerError,
@@ -2095,8 +20,11 @@ export {
2095
20
  ParseError,
2096
21
  RuntimeError,
2097
22
  clearCache,
2098
- compile2 as compile,
23
+ compile,
24
+ compileAst,
2099
25
  liveQuery,
2100
26
  parseQuery,
27
+ printExpression,
28
+ printQuery,
2101
29
  query
2102
30
  };