flowquery 1.0.32 → 1.0.34

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 (103) hide show
  1. package/dist/compute/flowquery.d.ts +43 -0
  2. package/dist/compute/flowquery.d.ts.map +1 -0
  3. package/dist/compute/flowquery.js +30 -0
  4. package/dist/compute/flowquery.js.map +1 -0
  5. package/dist/compute/runner.d.ts +0 -21
  6. package/dist/compute/runner.d.ts.map +1 -1
  7. package/dist/compute/runner.js.map +1 -1
  8. package/dist/flowquery.min.js +1 -1
  9. package/dist/index.browser.d.ts +1 -1
  10. package/dist/index.browser.d.ts.map +1 -1
  11. package/dist/index.browser.js +10 -10
  12. package/dist/index.browser.js.map +1 -1
  13. package/dist/index.node.d.ts +4 -4
  14. package/dist/index.node.d.ts.map +1 -1
  15. package/dist/index.node.js +13 -13
  16. package/dist/index.node.js.map +1 -1
  17. package/dist/parsing/context.d.ts +1 -0
  18. package/dist/parsing/context.d.ts.map +1 -1
  19. package/dist/parsing/context.js +5 -0
  20. package/dist/parsing/context.js.map +1 -1
  21. package/dist/parsing/expressions/operator.d.ts +2 -2
  22. package/dist/parsing/expressions/operator.d.ts.map +1 -1
  23. package/dist/parsing/expressions/operator.js +6 -1
  24. package/dist/parsing/expressions/operator.js.map +1 -1
  25. package/dist/parsing/functions/function_factory.d.ts +2 -0
  26. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  27. package/dist/parsing/functions/function_factory.js +2 -0
  28. package/dist/parsing/functions/function_factory.js.map +1 -1
  29. package/dist/parsing/functions/to_lower.d.ts +7 -0
  30. package/dist/parsing/functions/to_lower.d.ts.map +1 -0
  31. package/dist/parsing/functions/to_lower.js +37 -0
  32. package/dist/parsing/functions/to_lower.js.map +1 -0
  33. package/dist/parsing/functions/to_string.d.ts +7 -0
  34. package/dist/parsing/functions/to_string.d.ts.map +1 -0
  35. package/dist/parsing/functions/to_string.js +44 -0
  36. package/dist/parsing/functions/to_string.js.map +1 -0
  37. package/dist/parsing/operations/match.d.ts +5 -1
  38. package/dist/parsing/operations/match.d.ts.map +1 -1
  39. package/dist/parsing/operations/match.js +25 -1
  40. package/dist/parsing/operations/match.js.map +1 -1
  41. package/dist/parsing/operations/union.d.ts +36 -0
  42. package/dist/parsing/operations/union.d.ts.map +1 -0
  43. package/dist/parsing/operations/union.js +121 -0
  44. package/dist/parsing/operations/union.js.map +1 -0
  45. package/dist/parsing/operations/union_all.d.ts +10 -0
  46. package/dist/parsing/operations/union_all.d.ts.map +1 -0
  47. package/dist/parsing/operations/union_all.js +17 -0
  48. package/dist/parsing/operations/union_all.js.map +1 -0
  49. package/dist/parsing/parser.d.ts +2 -3
  50. package/dist/parsing/parser.d.ts.map +1 -1
  51. package/dist/parsing/parser.js +72 -24
  52. package/dist/parsing/parser.js.map +1 -1
  53. package/dist/parsing/parser_state.d.ts +13 -0
  54. package/dist/parsing/parser_state.d.ts.map +1 -0
  55. package/dist/parsing/parser_state.js +27 -0
  56. package/dist/parsing/parser_state.js.map +1 -0
  57. package/dist/tokenization/keyword.d.ts +4 -1
  58. package/dist/tokenization/keyword.d.ts.map +1 -1
  59. package/dist/tokenization/keyword.js +3 -0
  60. package/dist/tokenization/keyword.js.map +1 -1
  61. package/dist/tokenization/token.d.ts +6 -0
  62. package/dist/tokenization/token.d.ts.map +1 -1
  63. package/dist/tokenization/token.js +18 -0
  64. package/dist/tokenization/token.js.map +1 -1
  65. package/docs/flowquery.min.js +1 -1
  66. package/flowquery-py/pyproject.toml +1 -1
  67. package/flowquery-py/src/__init__.py +2 -0
  68. package/flowquery-py/src/compute/__init__.py +2 -1
  69. package/flowquery-py/src/compute/flowquery.py +68 -0
  70. package/flowquery-py/src/graph/node.py +1 -1
  71. package/flowquery-py/src/parsing/functions/__init__.py +4 -0
  72. package/flowquery-py/src/parsing/functions/to_lower.py +35 -0
  73. package/flowquery-py/src/parsing/functions/to_string.py +41 -0
  74. package/flowquery-py/src/parsing/operations/__init__.py +4 -0
  75. package/flowquery-py/src/parsing/operations/match.py +24 -2
  76. package/flowquery-py/src/parsing/operations/union.py +115 -0
  77. package/flowquery-py/src/parsing/operations/union_all.py +17 -0
  78. package/flowquery-py/src/parsing/parser.py +68 -24
  79. package/flowquery-py/src/parsing/parser_state.py +26 -0
  80. package/flowquery-py/src/tokenization/keyword.py +3 -0
  81. package/flowquery-py/src/tokenization/token.py +21 -0
  82. package/flowquery-py/tests/compute/test_runner.py +587 -1
  83. package/flowquery-py/tests/parsing/test_parser.py +82 -0
  84. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  85. package/package.json +1 -1
  86. package/src/compute/flowquery.ts +46 -0
  87. package/src/compute/runner.ts +0 -24
  88. package/src/index.browser.ts +17 -14
  89. package/src/index.node.ts +21 -18
  90. package/src/parsing/context.ts +6 -0
  91. package/src/parsing/expressions/operator.ts +8 -3
  92. package/src/parsing/functions/function_factory.ts +2 -0
  93. package/src/parsing/functions/to_lower.ts +25 -0
  94. package/src/parsing/functions/to_string.ts +32 -0
  95. package/src/parsing/operations/match.ts +24 -1
  96. package/src/parsing/operations/union.ts +114 -0
  97. package/src/parsing/operations/union_all.ts +16 -0
  98. package/src/parsing/parser.ts +74 -23
  99. package/src/parsing/parser_state.ts +25 -0
  100. package/src/tokenization/keyword.ts +3 -0
  101. package/src/tokenization/token.ts +24 -0
  102. package/tests/compute/runner.test.ts +507 -0
  103. package/tests/parsing/parser.test.ts +76 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowquery",
3
- "version": "1.0.32",
3
+ "version": "1.0.34",
4
4
  "description": "A declarative query language for data processing pipelines.",
5
5
  "main": "dist/index.node.js",
6
6
  "types": "dist/index.node.d.ts",
@@ -0,0 +1,46 @@
1
+ import Function from "../parsing/functions/function";
2
+ import { FunctionMetadata } from "../parsing/functions/function_metadata";
3
+ import Runner from "./runner";
4
+
5
+ /**
6
+ * FlowQuery is the public API surface for the FlowQuery library.
7
+ *
8
+ * It extends {@link Runner} with extensibility features such as function
9
+ * listing and plugin registration, keeping the Runner focused on execution.
10
+ *
11
+ * The static members are assigned dynamically in
12
+ * {@link ../index.browser.ts | index.browser.ts} and
13
+ * {@link ../index.node.ts | index.node.ts}.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const fq = new FlowQuery("WITH 1 as x RETURN x");
18
+ * await fq.run();
19
+ * console.log(fq.results); // [{ x: 1 }]
20
+ *
21
+ * // List all registered functions
22
+ * const functions = FlowQuery.listFunctions();
23
+ * ```
24
+ */
25
+ class FlowQuery extends Runner {
26
+ /**
27
+ * List all registered functions with their metadata.
28
+ */
29
+ static listFunctions: (options?: {
30
+ category?: string;
31
+ asyncOnly?: boolean;
32
+ syncOnly?: boolean;
33
+ }) => FunctionMetadata[];
34
+
35
+ /**
36
+ * Get metadata for a specific function.
37
+ */
38
+ static getFunctionMetadata: (name: string) => FunctionMetadata | undefined;
39
+
40
+ /**
41
+ * Base Function class for creating custom plugin functions.
42
+ */
43
+ static Function: typeof Function;
44
+ }
45
+
46
+ export default FlowQuery;
@@ -1,6 +1,4 @@
1
1
  import ASTNode from "../parsing/ast_node";
2
- import Function from "../parsing/functions/function";
3
- import { FunctionMetadata } from "../parsing/functions/function_metadata";
4
2
  import Operation from "../parsing/operations/operation";
5
3
  import Parser from "../parsing/parser";
6
4
 
@@ -21,28 +19,6 @@ class Runner {
21
19
  private first: Operation;
22
20
  private last: Operation;
23
21
 
24
- /**
25
- * List all registered functions with their metadata.
26
- * Added dynamically in index.browser.ts / index.node.ts
27
- */
28
- static listFunctions: (options?: {
29
- category?: string;
30
- asyncOnly?: boolean;
31
- syncOnly?: boolean;
32
- }) => FunctionMetadata[];
33
-
34
- /**
35
- * Get metadata for a specific function.
36
- * Added dynamically in index.browser.ts / index.node.ts
37
- */
38
- static getFunctionMetadata: (name: string) => FunctionMetadata | undefined;
39
-
40
- /**
41
- * Base Function class for creating custom plugin functions.
42
- * Added dynamically in index.browser.ts / index.node.ts
43
- */
44
- static Function: typeof Function;
45
-
46
22
  /**
47
23
  * Creates a new Runner instance and parses the FlowQuery statement.
48
24
  *
@@ -1,37 +1,40 @@
1
1
  /**
2
2
  * FlowQuery - A declarative query language for data processing pipelines.
3
- *
3
+ *
4
4
  * This is the main entry point for the FlowQuery in-browser usage.
5
- *
5
+ *
6
6
  * @packageDocumentation
7
7
  */
8
-
9
- import {default as FlowQuery} from "./compute/runner";
8
+ import { default as FlowQuery } from "./compute/flowquery";
9
+ import Function from "./parsing/functions/function";
10
10
  import FunctionFactory from "./parsing/functions/function_factory";
11
- import {
12
- FunctionMetadata,
13
- ParameterSchema,
14
- OutputSchema
11
+ import {
12
+ FunctionMetadata,
13
+ OutputSchema,
14
+ ParameterSchema,
15
15
  } from "./parsing/functions/function_metadata";
16
- import Function from "./parsing/functions/function";
17
16
 
18
17
  /**
19
18
  * List all registered functions with their metadata.
20
- *
19
+ *
21
20
  * @param options - Optional filter options
22
21
  * @returns Array of function metadata
23
22
  */
24
- FlowQuery.listFunctions = function(options?: { category?: string; asyncOnly?: boolean; syncOnly?: boolean }): FunctionMetadata[] {
23
+ FlowQuery.listFunctions = function (options?: {
24
+ category?: string;
25
+ asyncOnly?: boolean;
26
+ syncOnly?: boolean;
27
+ }): FunctionMetadata[] {
25
28
  return FunctionFactory.listFunctions(options);
26
29
  };
27
30
 
28
31
  /**
29
32
  * Get metadata for a specific function.
30
- *
33
+ *
31
34
  * @param name - The function name
32
35
  * @returns Function metadata or undefined
33
36
  */
34
- FlowQuery.getFunctionMetadata = function(name: string): FunctionMetadata | undefined {
37
+ FlowQuery.getFunctionMetadata = function (name: string): FunctionMetadata | undefined {
35
38
  return FunctionFactory.getMetadata(name);
36
39
  };
37
40
 
@@ -40,4 +43,4 @@ FlowQuery.getFunctionMetadata = function(name: string): FunctionMetadata | undef
40
43
  */
41
44
  FlowQuery.Function = Function;
42
45
 
43
- export default FlowQuery;
46
+ export default FlowQuery;
package/src/index.node.ts CHANGED
@@ -1,37 +1,40 @@
1
1
  /**
2
2
  * FlowQuery - A declarative query language for data processing pipelines.
3
- *
3
+ *
4
4
  * This is the main entry point for the FlowQuery Node.js library usage.
5
- *
5
+ *
6
6
  * @packageDocumentation
7
7
  */
8
-
9
- import {default as FlowQuery} from "./compute/runner";
8
+ import { default as FlowQuery } from "./compute/flowquery";
9
+ import Function from "./parsing/functions/function";
10
10
  import FunctionFactory, { AsyncDataProvider } from "./parsing/functions/function_factory";
11
- import {
12
- FunctionMetadata,
13
- ParameterSchema,
14
- OutputSchema
11
+ import {
12
+ FunctionMetadata,
13
+ OutputSchema,
14
+ ParameterSchema,
15
15
  } from "./parsing/functions/function_metadata";
16
- import Function from "./parsing/functions/function";
17
16
 
18
17
  /**
19
18
  * List all registered functions with their metadata.
20
- *
19
+ *
21
20
  * @param options - Optional filter options
22
21
  * @returns Array of function metadata
23
22
  */
24
- FlowQuery.listFunctions = function(options?: { category?: string; asyncOnly?: boolean; syncOnly?: boolean }): FunctionMetadata[] {
23
+ FlowQuery.listFunctions = function (options?: {
24
+ category?: string;
25
+ asyncOnly?: boolean;
26
+ syncOnly?: boolean;
27
+ }): FunctionMetadata[] {
25
28
  return FunctionFactory.listFunctions(options);
26
29
  };
27
30
 
28
31
  /**
29
32
  * Get metadata for a specific function.
30
- *
33
+ *
31
34
  * @param name - The function name
32
35
  * @returns Function metadata or undefined
33
36
  */
34
- FlowQuery.getFunctionMetadata = function(name: string): FunctionMetadata | undefined {
37
+ FlowQuery.getFunctionMetadata = function (name: string): FunctionMetadata | undefined {
35
38
  return FunctionFactory.getMetadata(name);
36
39
  };
37
40
 
@@ -41,12 +44,12 @@ FlowQuery.getFunctionMetadata = function(name: string): FunctionMetadata | undef
41
44
  FlowQuery.Function = Function;
42
45
 
43
46
  export default FlowQuery;
44
- export {
45
- FlowQuery,
46
- Function,
47
- FunctionFactory,
47
+ export {
48
+ FlowQuery,
49
+ Function,
50
+ FunctionFactory,
48
51
  AsyncDataProvider,
49
52
  FunctionMetadata,
50
53
  ParameterSchema,
51
- OutputSchema
54
+ OutputSchema,
52
55
  };
@@ -43,6 +43,12 @@ class Context {
43
43
  public containsType(type: new (...args: any[]) => ASTNode): boolean {
44
44
  return this.nodes.some((v) => v instanceof type);
45
45
  }
46
+
47
+ public clone(): Context {
48
+ const newContext = new Context();
49
+ newContext.nodes = [...this.nodes];
50
+ return newContext;
51
+ }
46
52
  }
47
53
 
48
54
  export default Context;
@@ -43,7 +43,7 @@ abstract class Operator extends ASTNode {
43
43
  public get leftAssociative(): boolean {
44
44
  return this._leftAssociative;
45
45
  }
46
- public abstract value(): number;
46
+ public abstract value(): any;
47
47
  public get lhs(): ASTNode {
48
48
  return this.getChildren()[0];
49
49
  }
@@ -56,8 +56,13 @@ class Add extends Operator {
56
56
  constructor() {
57
57
  super(1, true);
58
58
  }
59
- public value(): number {
60
- return this.lhs.value() + this.rhs.value();
59
+ public value(): any {
60
+ const l = this.lhs.value();
61
+ const r = this.rhs.value();
62
+ if (Array.isArray(l) && Array.isArray(r)) {
63
+ return [...l, ...r];
64
+ }
65
+ return l + r;
61
66
  }
62
67
  }
63
68
 
@@ -27,6 +27,8 @@ import "./stringify";
27
27
  // Import built-in functions to ensure their @FunctionDef decorators run
28
28
  import "./sum";
29
29
  import "./to_json";
30
+ import "./to_lower";
31
+ import "./to_string";
30
32
  import "./type";
31
33
 
32
34
  // Re-export AsyncDataProvider for backwards compatibility
@@ -0,0 +1,25 @@
1
+ import Function from "./function";
2
+ import { FunctionDef } from "./function_metadata";
3
+
4
+ @FunctionDef({
5
+ description: "Converts a string to lowercase",
6
+ category: "scalar",
7
+ parameters: [{ name: "text", description: "String to convert to lowercase", type: "string" }],
8
+ output: { description: "Lowercase string", type: "string", example: "hello world" },
9
+ examples: ["WITH 'Hello World' AS s RETURN toLower(s)", "WITH 'FOO' AS s RETURN toLower(s)"],
10
+ })
11
+ class ToLower extends Function {
12
+ constructor() {
13
+ super("tolower");
14
+ this._expectedParameterCount = 1;
15
+ }
16
+ public value(): any {
17
+ const val = this.getChildren()[0].value();
18
+ if (typeof val !== "string") {
19
+ throw new Error("Invalid argument for toLower function: expected a string");
20
+ }
21
+ return val.toLowerCase();
22
+ }
23
+ }
24
+
25
+ export default ToLower;
@@ -0,0 +1,32 @@
1
+ import Function from "./function";
2
+ import { FunctionDef } from "./function_metadata";
3
+
4
+ @FunctionDef({
5
+ description: "Converts a value to its string representation",
6
+ category: "scalar",
7
+ parameters: [{ name: "value", description: "Value to convert to a string", type: "any" }],
8
+ output: { description: "String representation of the value", type: "string", example: "42" },
9
+ examples: [
10
+ "WITH 42 AS n RETURN toString(n)",
11
+ "WITH true AS b RETURN toString(b)",
12
+ "WITH [1, 2, 3] AS arr RETURN toString(arr)",
13
+ ],
14
+ })
15
+ class ToString extends Function {
16
+ constructor() {
17
+ super("tostring");
18
+ this._expectedParameterCount = 1;
19
+ }
20
+ public value(): any {
21
+ const val = this.getChildren()[0].value();
22
+ if (val === null || val === undefined) {
23
+ return String(val);
24
+ }
25
+ if (typeof val === "object") {
26
+ return JSON.stringify(val);
27
+ }
28
+ return String(val);
29
+ }
30
+ }
31
+
32
+ export default ToString;
@@ -1,30 +1,53 @@
1
+ import Node from "../../graph/node";
1
2
  import Pattern from "../../graph/pattern";
2
3
  import Patterns from "../../graph/patterns";
3
4
  import Operation from "./operation";
4
5
 
5
6
  class Match extends Operation {
6
7
  private _patterns: Patterns | null = null;
8
+ private _optional: boolean = false;
7
9
 
8
- constructor(patterns: Pattern[] = []) {
10
+ constructor(patterns: Pattern[] = [], optional: boolean = false) {
9
11
  super();
10
12
  this._patterns = new Patterns(patterns);
13
+ this._optional = optional;
11
14
  }
12
15
  public get patterns(): Pattern[] {
13
16
  return this._patterns ? this._patterns.patterns : [];
14
17
  }
18
+ public get optional(): boolean {
19
+ return this._optional;
20
+ }
21
+ protected toString(): string {
22
+ return this._optional ? "OptionalMatch" : "Match";
23
+ }
15
24
  /**
16
25
  * Executes the match operation by chaining the patterns together.
17
26
  * After each pattern match, it continues to the next operation in the chain.
27
+ * If optional and no match is found, continues with null values.
18
28
  * @return Promise<void>
19
29
  */
20
30
  public async run(): Promise<void> {
21
31
  await this._patterns!.initialize();
32
+ let matched = false;
22
33
  this._patterns!.toDoNext = async () => {
34
+ matched = true;
23
35
  // Continue to the next operation after all patterns are matched
24
36
  await this.next?.run();
25
37
  };
26
38
  // Kick off the graph pattern traversal
27
39
  await this._patterns!.traverse();
40
+ // For OPTIONAL MATCH: if nothing matched, continue with null values
41
+ if (!matched && this._optional) {
42
+ for (const pattern of this._patterns!.patterns) {
43
+ for (const element of pattern.chain) {
44
+ if (element instanceof Node) {
45
+ element.setValue(null!);
46
+ }
47
+ }
48
+ }
49
+ await this.next?.run();
50
+ }
28
51
  }
29
52
  }
30
53
 
@@ -0,0 +1,114 @@
1
+ import Operation from "./operation";
2
+
3
+ /**
4
+ * Represents a UNION operation that combines results from two sub-queries.
5
+ *
6
+ * UNION merges the results of a left and right query pipeline, removing
7
+ * duplicate rows. Both sides must return the same column names.
8
+ *
9
+ * @example
10
+ * ```
11
+ * WITH 1 AS x RETURN x
12
+ * UNION
13
+ * WITH 2 AS x RETURN x
14
+ * // Results: [{x: 1}, {x: 2}]
15
+ * ```
16
+ */
17
+ class Union extends Operation {
18
+ protected _left: Operation | null = null;
19
+ protected _right: Operation | null = null;
20
+ protected _results: Record<string, any>[] = [];
21
+
22
+ public set left(operation: Operation) {
23
+ this._left = operation;
24
+ }
25
+ public get left(): Operation {
26
+ if (!this._left) {
27
+ throw new Error("Left operation is not set");
28
+ }
29
+ return this._left;
30
+ }
31
+ public set right(operation: Operation) {
32
+ this._right = operation;
33
+ }
34
+ public get right(): Operation {
35
+ if (!this._right) {
36
+ throw new Error("Right operation is not set");
37
+ }
38
+ return this._right;
39
+ }
40
+
41
+ private lastInChain(operation: Operation): Operation {
42
+ let current = operation;
43
+ while (current.next) {
44
+ current = current.next;
45
+ }
46
+ return current;
47
+ }
48
+
49
+ public async initialize(): Promise<void> {
50
+ this._results = [];
51
+ await this.next?.initialize();
52
+ }
53
+
54
+ public async run(): Promise<void> {
55
+ // Execute left pipeline
56
+ await this._left!.initialize();
57
+ await this._left!.run();
58
+ await this._left!.finish();
59
+ const leftLast = this.lastInChain(this._left!);
60
+ const leftResults: Record<string, any>[] = leftLast.results;
61
+
62
+ // Execute right pipeline
63
+ await this._right!.initialize();
64
+ await this._right!.run();
65
+ await this._right!.finish();
66
+ const rightLast = this.lastInChain(this._right!);
67
+ const rightResults: Record<string, any>[] = rightLast.results;
68
+
69
+ // Validate column names match
70
+ if (leftResults.length > 0 && rightResults.length > 0) {
71
+ const leftKeys = Object.keys(leftResults[0]).sort().join(",");
72
+ const rightKeys = Object.keys(rightResults[0]).sort().join(",");
73
+ if (leftKeys !== rightKeys) {
74
+ throw new Error(
75
+ "All sub queries in a UNION must have the same return column names"
76
+ );
77
+ }
78
+ }
79
+
80
+ // Combine results
81
+ this._results = this.combine(leftResults, rightResults);
82
+ }
83
+
84
+ /**
85
+ * Combines results from left and right pipelines.
86
+ * UNION removes duplicates; subclass UnionAll overrides to keep all rows.
87
+ */
88
+ protected combine(
89
+ left: Record<string, any>[],
90
+ right: Record<string, any>[]
91
+ ): Record<string, any>[] {
92
+ const combined = [...left];
93
+ for (const row of right) {
94
+ const serialized = JSON.stringify(row);
95
+ const isDuplicate = combined.some(
96
+ (existing) => JSON.stringify(existing) === serialized
97
+ );
98
+ if (!isDuplicate) {
99
+ combined.push(row);
100
+ }
101
+ }
102
+ return combined;
103
+ }
104
+
105
+ public async finish(): Promise<void> {
106
+ await this.next?.finish();
107
+ }
108
+
109
+ public get results(): Record<string, any>[] {
110
+ return this._results;
111
+ }
112
+ }
113
+
114
+ export default Union;
@@ -0,0 +1,16 @@
1
+ import Union from "./union";
2
+
3
+ /**
4
+ * Represents a UNION ALL operation that concatenates results from two sub-queries
5
+ * without removing duplicates.
6
+ */
7
+ class UnionAll extends Union {
8
+ protected combine(
9
+ left: Record<string, any>[],
10
+ right: Record<string, any>[]
11
+ ): Record<string, any>[] {
12
+ return [...left, ...right];
13
+ }
14
+ }
15
+
16
+ export default UnionAll;