flowquery 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +2 -0
- package/.github/workflows/release.yml +24 -9
- package/dist/compute/runner.js +75 -0
- package/dist/compute/runner.js.map +1 -0
- package/dist/flowquery.min.js +1 -0
- package/dist/index.browser.js +119 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/index.node.js +123 -0
- package/dist/index.node.js.map +1 -0
- package/dist/io/command_line.js +102 -0
- package/dist/io/command_line.js.map +1 -0
- package/dist/parsing/alias.js +23 -0
- package/dist/parsing/alias.js.map +1 -0
- package/dist/parsing/alias_option.js +11 -0
- package/dist/parsing/alias_option.js.map +1 -0
- package/dist/parsing/ast_node.js +145 -0
- package/dist/parsing/ast_node.js.map +1 -0
- package/dist/parsing/base_parser.js +92 -0
- package/dist/parsing/base_parser.js.map +1 -0
- package/dist/parsing/components/csv.js +13 -0
- package/dist/parsing/components/csv.js.map +1 -0
- package/dist/parsing/components/from.js +16 -0
- package/dist/parsing/components/from.js.map +1 -0
- package/dist/parsing/components/headers.js +16 -0
- package/dist/parsing/components/headers.js.map +1 -0
- package/dist/parsing/components/json.js +13 -0
- package/dist/parsing/components/json.js.map +1 -0
- package/dist/parsing/components/null.js +13 -0
- package/dist/parsing/components/null.js.map +1 -0
- package/dist/parsing/components/post.js +13 -0
- package/dist/parsing/components/post.js.map +1 -0
- package/dist/parsing/components/text.js +13 -0
- package/dist/parsing/components/text.js.map +1 -0
- package/dist/parsing/context.js +47 -0
- package/dist/parsing/context.js.map +1 -0
- package/dist/parsing/data_structures/associative_array.js +45 -0
- package/dist/parsing/data_structures/associative_array.js.map +1 -0
- package/dist/parsing/data_structures/json_array.js +35 -0
- package/dist/parsing/data_structures/json_array.js.map +1 -0
- package/dist/parsing/data_structures/key_value_pair.js +41 -0
- package/dist/parsing/data_structures/key_value_pair.js.map +1 -0
- package/dist/parsing/data_structures/lookup.js +44 -0
- package/dist/parsing/data_structures/lookup.js.map +1 -0
- package/dist/parsing/data_structures/range_lookup.js +40 -0
- package/dist/parsing/data_structures/range_lookup.js.map +1 -0
- package/dist/parsing/expressions/expression.js +142 -0
- package/dist/parsing/expressions/expression.js.map +1 -0
- package/dist/parsing/expressions/f_string.js +29 -0
- package/dist/parsing/expressions/f_string.js.map +1 -0
- package/dist/parsing/expressions/identifier.js +26 -0
- package/dist/parsing/expressions/identifier.js.map +1 -0
- package/dist/parsing/expressions/number.js +41 -0
- package/dist/parsing/expressions/number.js.map +1 -0
- package/dist/parsing/expressions/operator.js +180 -0
- package/dist/parsing/expressions/operator.js.map +1 -0
- package/dist/parsing/expressions/reference.js +45 -0
- package/dist/parsing/expressions/reference.js.map +1 -0
- package/dist/parsing/expressions/string.js +34 -0
- package/dist/parsing/expressions/string.js.map +1 -0
- package/dist/parsing/functions/aggregate_function.js +58 -0
- package/dist/parsing/functions/aggregate_function.js.map +1 -0
- package/dist/parsing/functions/async_function.js +119 -0
- package/dist/parsing/functions/async_function.js.map +1 -0
- package/dist/parsing/functions/avg.js +43 -0
- package/dist/parsing/functions/avg.js.map +1 -0
- package/dist/parsing/functions/collect.js +52 -0
- package/dist/parsing/functions/collect.js.map +1 -0
- package/dist/parsing/functions/function.js +59 -0
- package/dist/parsing/functions/function.js.map +1 -0
- package/dist/parsing/functions/function_factory.js +259 -0
- package/dist/parsing/functions/function_factory.js.map +1 -0
- package/dist/parsing/functions/function_metadata.js +159 -0
- package/dist/parsing/functions/function_metadata.js.map +1 -0
- package/dist/parsing/functions/functions.js +47 -0
- package/dist/parsing/functions/functions.js.map +1 -0
- package/dist/parsing/functions/join.js +29 -0
- package/dist/parsing/functions/join.js.map +1 -0
- package/dist/parsing/functions/predicate_function.js +37 -0
- package/dist/parsing/functions/predicate_function.js.map +1 -0
- package/dist/parsing/functions/predicate_function_factory.js +19 -0
- package/dist/parsing/functions/predicate_function_factory.js.map +1 -0
- package/dist/parsing/functions/predicate_sum.js +33 -0
- package/dist/parsing/functions/predicate_sum.js.map +1 -0
- package/dist/parsing/functions/rand.js +17 -0
- package/dist/parsing/functions/rand.js.map +1 -0
- package/dist/parsing/functions/range.js +22 -0
- package/dist/parsing/functions/range.js.map +1 -0
- package/dist/parsing/functions/reducer_element.js +12 -0
- package/dist/parsing/functions/reducer_element.js.map +1 -0
- package/dist/parsing/functions/replace.js +23 -0
- package/dist/parsing/functions/replace.js.map +1 -0
- package/dist/parsing/functions/round.js +21 -0
- package/dist/parsing/functions/round.js.map +1 -0
- package/dist/parsing/functions/size.js +21 -0
- package/dist/parsing/functions/size.js.map +1 -0
- package/dist/parsing/functions/split.js +29 -0
- package/dist/parsing/functions/split.js.map +1 -0
- package/dist/parsing/functions/stringify.js +29 -0
- package/dist/parsing/functions/stringify.js.map +1 -0
- package/dist/parsing/functions/sum.js +38 -0
- package/dist/parsing/functions/sum.js.map +1 -0
- package/dist/parsing/functions/to_json.js +21 -0
- package/dist/parsing/functions/to_json.js.map +1 -0
- package/dist/parsing/functions/value_holder.js +16 -0
- package/dist/parsing/functions/value_holder.js.map +1 -0
- package/dist/parsing/logic/case.js +27 -0
- package/dist/parsing/logic/case.js.map +1 -0
- package/dist/parsing/logic/else.js +16 -0
- package/dist/parsing/logic/else.js.map +1 -0
- package/dist/parsing/logic/end.js +13 -0
- package/dist/parsing/logic/end.js.map +1 -0
- package/dist/parsing/logic/then.js +16 -0
- package/dist/parsing/logic/then.js.map +1 -0
- package/dist/parsing/logic/when.js +16 -0
- package/dist/parsing/logic/when.js.map +1 -0
- package/dist/parsing/operations/aggregated_return.js +35 -0
- package/dist/parsing/operations/aggregated_return.js.map +1 -0
- package/dist/parsing/operations/aggregated_with.js +41 -0
- package/dist/parsing/operations/aggregated_with.js.map +1 -0
- package/dist/parsing/operations/group_by.js +139 -0
- package/dist/parsing/operations/group_by.js.map +1 -0
- package/dist/parsing/operations/limit.js +38 -0
- package/dist/parsing/operations/limit.js.map +1 -0
- package/dist/parsing/operations/load.js +174 -0
- package/dist/parsing/operations/load.js.map +1 -0
- package/dist/parsing/operations/operation.js +81 -0
- package/dist/parsing/operations/operation.js.map +1 -0
- package/dist/parsing/operations/projection.js +21 -0
- package/dist/parsing/operations/projection.js.map +1 -0
- package/dist/parsing/operations/return.js +60 -0
- package/dist/parsing/operations/return.js.map +1 -0
- package/dist/parsing/operations/unwind.js +46 -0
- package/dist/parsing/operations/unwind.js.map +1 -0
- package/dist/parsing/operations/where.js +53 -0
- package/dist/parsing/operations/where.js.map +1 -0
- package/dist/parsing/operations/with.js +36 -0
- package/dist/parsing/operations/with.js.map +1 -0
- package/dist/parsing/parser.js +812 -0
- package/dist/parsing/parser.js.map +1 -0
- package/dist/parsing/token_to_node.js +121 -0
- package/dist/parsing/token_to_node.js.map +1 -0
- package/dist/tokenization/keyword.js +46 -0
- package/dist/tokenization/keyword.js.map +1 -0
- package/dist/tokenization/operator.js +28 -0
- package/dist/tokenization/operator.js.map +1 -0
- package/dist/tokenization/string_walker.js +165 -0
- package/dist/tokenization/string_walker.js.map +1 -0
- package/dist/tokenization/symbol.js +18 -0
- package/dist/tokenization/symbol.js.map +1 -0
- package/dist/tokenization/token.js +484 -0
- package/dist/tokenization/token.js.map +1 -0
- package/dist/tokenization/token_mapper.js +55 -0
- package/dist/tokenization/token_mapper.js.map +1 -0
- package/dist/tokenization/token_type.js +19 -0
- package/dist/tokenization/token_type.js.map +1 -0
- package/dist/tokenization/tokenizer.js +220 -0
- package/dist/tokenization/tokenizer.js.map +1 -0
- package/dist/tokenization/trie.js +111 -0
- package/dist/tokenization/trie.js.map +1 -0
- package/dist/utils/object_utils.js +19 -0
- package/dist/utils/object_utils.js.map +1 -0
- package/dist/utils/string_utils.js +110 -0
- package/dist/utils/string_utils.js.map +1 -0
- package/docs/flowquery.min.js +1 -1
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +22 -4
- package/src/compute/runner.ts +45 -0
- package/src/index.browser.ts +118 -0
- package/src/index.node.ts +141 -0
- package/src/parsing/functions/async_function.ts +95 -0
- package/src/parsing/functions/function_factory.ts +230 -1
- package/src/parsing/functions/function_metadata.ts +238 -0
- package/src/parsing/functions/functions.ts +43 -0
- package/src/parsing/operations/load.ts +51 -2
- package/src/parsing/parser.ts +45 -4
- package/tests/parsing/function_plugins.test.ts +369 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema definition for function arguments and outputs.
|
|
3
|
+
* Compatible with JSON Schema for LLM consumption.
|
|
4
|
+
*/
|
|
5
|
+
export interface ParameterSchema {
|
|
6
|
+
/** The parameter name */
|
|
7
|
+
name: string;
|
|
8
|
+
/** Description of the parameter */
|
|
9
|
+
description: string;
|
|
10
|
+
/** JSON Schema type: string, number, boolean, object, array, null */
|
|
11
|
+
type: "string" | "number" | "boolean" | "object" | "array" | "null" | string;
|
|
12
|
+
/** Whether the parameter is required (default: true) */
|
|
13
|
+
required?: boolean;
|
|
14
|
+
/** Default value if not provided */
|
|
15
|
+
default?: any;
|
|
16
|
+
/** For arrays, the schema of items */
|
|
17
|
+
items?: Omit<ParameterSchema, 'name' | 'required' | 'default'>;
|
|
18
|
+
/** For objects, the properties schema */
|
|
19
|
+
properties?: Record<string, Omit<ParameterSchema, 'name' | 'required'>>;
|
|
20
|
+
/** Enum of allowed values */
|
|
21
|
+
enum?: any[];
|
|
22
|
+
/** Example value */
|
|
23
|
+
example?: any;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schema definition for function output.
|
|
28
|
+
*/
|
|
29
|
+
export interface OutputSchema {
|
|
30
|
+
/** Description of the output */
|
|
31
|
+
description: string;
|
|
32
|
+
/** JSON Schema type */
|
|
33
|
+
type: "string" | "number" | "boolean" | "object" | "array" | "null" | string;
|
|
34
|
+
/** For arrays, the schema of items */
|
|
35
|
+
items?: Omit<OutputSchema, 'description'>;
|
|
36
|
+
/** For objects, the properties schema */
|
|
37
|
+
properties?: Record<string, Omit<ParameterSchema, 'name' | 'required'>>;
|
|
38
|
+
/** Example output value */
|
|
39
|
+
example?: any;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Metadata for a registered function, designed for LLM consumption.
|
|
44
|
+
*/
|
|
45
|
+
export interface FunctionMetadata {
|
|
46
|
+
/** The function name */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Human-readable description of what the function does */
|
|
49
|
+
description: string;
|
|
50
|
+
/** Category for grouping functions (e.g., "aggregation", "string", "data") */
|
|
51
|
+
category?: string;
|
|
52
|
+
/** Array of parameter schemas */
|
|
53
|
+
parameters: ParameterSchema[];
|
|
54
|
+
/** Output schema */
|
|
55
|
+
output: OutputSchema;
|
|
56
|
+
/** Example usage in FlowQuery syntax */
|
|
57
|
+
examples?: string[];
|
|
58
|
+
/** Whether this is an async data provider for LOAD operations */
|
|
59
|
+
isAsyncProvider?: boolean;
|
|
60
|
+
/** Additional notes or caveats */
|
|
61
|
+
notes?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Options for registering a sync function with metadata.
|
|
66
|
+
*/
|
|
67
|
+
export interface RegisterFunctionOptions {
|
|
68
|
+
/** Factory function that creates the Function instance */
|
|
69
|
+
factory: () => any;
|
|
70
|
+
/** Function metadata for documentation */
|
|
71
|
+
metadata: FunctionMetadata;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Options for registering an async data provider with metadata.
|
|
76
|
+
*/
|
|
77
|
+
export interface RegisterAsyncProviderOptions {
|
|
78
|
+
/** Async generator or function that returns data */
|
|
79
|
+
provider: (...args: any[]) => AsyncGenerator<any, void, unknown> | Promise<any>;
|
|
80
|
+
/** Function metadata for documentation */
|
|
81
|
+
metadata: FunctionMetadata;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Built-in function metadata definitions.
|
|
86
|
+
*/
|
|
87
|
+
export const BUILTIN_FUNCTION_METADATA: FunctionMetadata[] = [
|
|
88
|
+
{
|
|
89
|
+
name: "sum",
|
|
90
|
+
description: "Calculates the sum of numeric values across grouped rows",
|
|
91
|
+
category: "aggregation",
|
|
92
|
+
parameters: [
|
|
93
|
+
{ name: "value", description: "Numeric value to sum", type: "number" }
|
|
94
|
+
],
|
|
95
|
+
output: { description: "Sum of all values", type: "number", example: 150 },
|
|
96
|
+
examples: ["WITH [1, 2, 3] AS nums UNWIND nums AS n RETURN sum(n)"]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "avg",
|
|
100
|
+
description: "Calculates the average of numeric values across grouped rows",
|
|
101
|
+
category: "aggregation",
|
|
102
|
+
parameters: [
|
|
103
|
+
{ name: "value", description: "Numeric value to average", type: "number" }
|
|
104
|
+
],
|
|
105
|
+
output: { description: "Average of all values", type: "number", example: 50 },
|
|
106
|
+
examples: ["WITH [10, 20, 30] AS nums UNWIND nums AS n RETURN avg(n)"]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: "collect",
|
|
110
|
+
description: "Collects values into an array across grouped rows",
|
|
111
|
+
category: "aggregation",
|
|
112
|
+
parameters: [
|
|
113
|
+
{ name: "value", description: "Value to collect", type: "any" }
|
|
114
|
+
],
|
|
115
|
+
output: { description: "Array of collected values", type: "array", example: [1, 2, 3] },
|
|
116
|
+
examples: ["WITH [1, 2, 3] AS nums UNWIND nums AS n RETURN collect(n)"]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "range",
|
|
120
|
+
description: "Generates an array of sequential integers",
|
|
121
|
+
category: "generator",
|
|
122
|
+
parameters: [
|
|
123
|
+
{ name: "start", description: "Starting number (inclusive)", type: "number" },
|
|
124
|
+
{ name: "end", description: "Ending number (inclusive)", type: "number" }
|
|
125
|
+
],
|
|
126
|
+
output: { description: "Array of integers from start to end", type: "array", items: { type: "number" }, example: [1, 2, 3, 4, 5] },
|
|
127
|
+
examples: ["WITH range(1, 5) AS nums RETURN nums"]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "rand",
|
|
131
|
+
description: "Generates a random number between 0 and 1",
|
|
132
|
+
category: "generator",
|
|
133
|
+
parameters: [],
|
|
134
|
+
output: { description: "Random number between 0 and 1", type: "number", example: 0.7234 },
|
|
135
|
+
examples: ["WITH rand() AS r RETURN r"]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "round",
|
|
139
|
+
description: "Rounds a number to the nearest integer",
|
|
140
|
+
category: "math",
|
|
141
|
+
parameters: [
|
|
142
|
+
{ name: "value", description: "Number to round", type: "number" }
|
|
143
|
+
],
|
|
144
|
+
output: { description: "Rounded integer", type: "number", example: 4 },
|
|
145
|
+
examples: ["WITH 3.7 AS n RETURN round(n)"]
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "split",
|
|
149
|
+
description: "Splits a string into an array by a delimiter",
|
|
150
|
+
category: "string",
|
|
151
|
+
parameters: [
|
|
152
|
+
{ name: "text", description: "String to split", type: "string" },
|
|
153
|
+
{ name: "delimiter", description: "Delimiter to split by", type: "string" }
|
|
154
|
+
],
|
|
155
|
+
output: { description: "Array of string parts", type: "array", items: { type: "string" }, example: ["a", "b", "c"] },
|
|
156
|
+
examples: ["WITH 'a,b,c' AS s RETURN split(s, ',')"]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "join",
|
|
160
|
+
description: "Joins an array of strings with a delimiter",
|
|
161
|
+
category: "string",
|
|
162
|
+
parameters: [
|
|
163
|
+
{ name: "array", description: "Array of values to join", type: "array" },
|
|
164
|
+
{ name: "delimiter", description: "Delimiter to join with", type: "string" }
|
|
165
|
+
],
|
|
166
|
+
output: { description: "Joined string", type: "string", example: "a,b,c" },
|
|
167
|
+
examples: ["WITH ['a', 'b', 'c'] AS arr RETURN join(arr, ',')"]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "replace",
|
|
171
|
+
description: "Replaces occurrences of a pattern in a string",
|
|
172
|
+
category: "string",
|
|
173
|
+
parameters: [
|
|
174
|
+
{ name: "text", description: "Source string", type: "string" },
|
|
175
|
+
{ name: "pattern", description: "Pattern to find", type: "string" },
|
|
176
|
+
{ name: "replacement", description: "Replacement string", type: "string" }
|
|
177
|
+
],
|
|
178
|
+
output: { description: "String with replacements", type: "string", example: "hello world" },
|
|
179
|
+
examples: ["WITH 'hello there' AS s RETURN replace(s, 'there', 'world')"]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "stringify",
|
|
183
|
+
description: "Converts a value to its JSON string representation",
|
|
184
|
+
category: "conversion",
|
|
185
|
+
parameters: [
|
|
186
|
+
{ name: "value", description: "Value to stringify", type: "any" }
|
|
187
|
+
],
|
|
188
|
+
output: { description: "JSON string", type: "string", example: "{\"a\":1}" },
|
|
189
|
+
examples: ["WITH {a: 1} AS obj RETURN stringify(obj)"]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "tojson",
|
|
193
|
+
description: "Parses a JSON string into an object",
|
|
194
|
+
category: "conversion",
|
|
195
|
+
parameters: [
|
|
196
|
+
{ name: "text", description: "JSON string to parse", type: "string" }
|
|
197
|
+
],
|
|
198
|
+
output: { description: "Parsed object or array", type: "object", example: { a: 1 } },
|
|
199
|
+
examples: ["WITH '{\"a\": 1}' AS s RETURN tojson(s)"]
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "size",
|
|
203
|
+
description: "Returns the length of an array or string",
|
|
204
|
+
category: "utility",
|
|
205
|
+
parameters: [
|
|
206
|
+
{ name: "value", description: "Array or string to measure", type: "array" }
|
|
207
|
+
],
|
|
208
|
+
output: { description: "Length of the input", type: "number", example: 3 },
|
|
209
|
+
examples: ["WITH [1, 2, 3] AS arr RETURN size(arr)"]
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "functions",
|
|
213
|
+
description: "Lists all registered functions with their metadata. Useful for discovering available functions and their documentation. Results include name, description, parameters, output schema, and usage examples.",
|
|
214
|
+
category: "introspection",
|
|
215
|
+
parameters: [
|
|
216
|
+
{ name: "category", description: "Optional category to filter by (e.g., 'aggregation', 'string', 'math')", type: "string", required: false }
|
|
217
|
+
],
|
|
218
|
+
output: {
|
|
219
|
+
description: "Array of function metadata objects",
|
|
220
|
+
type: "array",
|
|
221
|
+
items: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
name: { description: "Function name", type: "string" },
|
|
225
|
+
description: { description: "What the function does", type: "string" },
|
|
226
|
+
category: { description: "Function category", type: "string" },
|
|
227
|
+
parameters: { description: "Array of parameter definitions", type: "array" },
|
|
228
|
+
output: { description: "Output schema", type: "object" },
|
|
229
|
+
examples: { description: "Usage examples", type: "array" }
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
examples: [
|
|
234
|
+
"WITH functions() AS funcs RETURN funcs",
|
|
235
|
+
"WITH functions('aggregation') AS funcs UNWIND funcs AS f RETURN f.name, f.description"
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Function from "./function";
|
|
2
|
+
import FunctionFactory from "./function_factory";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Built-in function that lists all registered functions with their metadata.
|
|
6
|
+
*
|
|
7
|
+
* Can be used in FlowQuery to discover available functions:
|
|
8
|
+
* - `WITH functions() AS funcs RETURN funcs` - returns all functions
|
|
9
|
+
* - `WITH functions('aggregation') AS funcs RETURN funcs` - returns functions in a category
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```
|
|
13
|
+
* WITH functions() AS funcs
|
|
14
|
+
* UNWIND funcs AS func
|
|
15
|
+
* RETURN func.name, func.description
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
class Functions extends Function {
|
|
19
|
+
constructor() {
|
|
20
|
+
super("functions");
|
|
21
|
+
this._expectedParameterCount = null; // 0 or 1 parameter
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public value(): any {
|
|
25
|
+
const children = this.getChildren();
|
|
26
|
+
|
|
27
|
+
if (children.length === 0) {
|
|
28
|
+
// Return all functions
|
|
29
|
+
return FunctionFactory.listFunctions();
|
|
30
|
+
} else if (children.length === 1) {
|
|
31
|
+
// Filter by category
|
|
32
|
+
const category = children[0].value();
|
|
33
|
+
if (typeof category === 'string') {
|
|
34
|
+
return FunctionFactory.listFunctions({ category });
|
|
35
|
+
}
|
|
36
|
+
throw new Error("functions() category parameter must be a string");
|
|
37
|
+
} else {
|
|
38
|
+
throw new Error("functions() takes 0 or 1 parameters");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default Functions;
|
|
@@ -3,12 +3,14 @@ import CSV from "../components/csv";
|
|
|
3
3
|
import {default as _JSON} from "../components/json";
|
|
4
4
|
import Text from "../components/text";
|
|
5
5
|
import Function from "../functions/function";
|
|
6
|
+
import AsyncFunction from "../functions/async_function";
|
|
6
7
|
import AssociativeArray from "../data_structures/associative_array";
|
|
7
8
|
import Reference from "../expressions/reference";
|
|
8
9
|
import Expression from "../expressions/expression";
|
|
9
10
|
import Headers from "../components/headers";
|
|
10
11
|
import Post from "../components/post";
|
|
11
12
|
import Lookup from "../data_structures/lookup";
|
|
13
|
+
import From from "../components/from";
|
|
12
14
|
|
|
13
15
|
class Load extends Operation {
|
|
14
16
|
private _value: any = null;
|
|
@@ -18,6 +20,29 @@ class Load extends Operation {
|
|
|
18
20
|
public get type(): _JSON | CSV | Text {
|
|
19
21
|
return this.children[0] as _JSON | CSV | Text;
|
|
20
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Gets the From component which contains either a URL expression or an AsyncFunction.
|
|
26
|
+
*/
|
|
27
|
+
public get fromComponent(): From {
|
|
28
|
+
return this.children[1] as From;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Checks if the data source is an async function.
|
|
33
|
+
*/
|
|
34
|
+
public get isAsyncFunction(): boolean {
|
|
35
|
+
return this.fromComponent.firstChild() instanceof AsyncFunction;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Gets the async function if the source is a function, otherwise null.
|
|
40
|
+
*/
|
|
41
|
+
public get asyncFunction(): AsyncFunction | null {
|
|
42
|
+
const child = this.fromComponent.firstChild();
|
|
43
|
+
return child instanceof AsyncFunction ? child : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
21
46
|
public get from(): string {
|
|
22
47
|
return this.children[1].value() as string;
|
|
23
48
|
}
|
|
@@ -56,7 +81,22 @@ class Load extends Operation {
|
|
|
56
81
|
...(payload !== null ? {"body": JSON.stringify(payload.value())} : {})
|
|
57
82
|
};
|
|
58
83
|
}
|
|
59
|
-
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Loads data from an async function source.
|
|
87
|
+
*/
|
|
88
|
+
private async loadFromFunction(): Promise<void> {
|
|
89
|
+
const asyncFunc = this.asyncFunction!;
|
|
90
|
+
for await (const item of asyncFunc.execute()) {
|
|
91
|
+
this._value = item;
|
|
92
|
+
await this.next?.run();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Loads data from a URL source (original behavior).
|
|
98
|
+
*/
|
|
99
|
+
private async loadFromUrl(): Promise<void> {
|
|
60
100
|
const result = await fetch(this.from, this.options());
|
|
61
101
|
let data: any = null;
|
|
62
102
|
if(this.type instanceof _JSON) {
|
|
@@ -77,11 +117,20 @@ class Load extends Operation {
|
|
|
77
117
|
await this.next?.run();
|
|
78
118
|
}
|
|
79
119
|
}
|
|
120
|
+
|
|
121
|
+
public async load(): Promise<any> {
|
|
122
|
+
if (this.isAsyncFunction) {
|
|
123
|
+
await this.loadFromFunction();
|
|
124
|
+
} else {
|
|
125
|
+
await this.loadFromUrl();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
80
128
|
public async run(): Promise<void> {
|
|
81
129
|
try {
|
|
82
130
|
await this.load();
|
|
83
131
|
} catch(e) {
|
|
84
|
-
|
|
132
|
+
const source = this.isAsyncFunction ? this.asyncFunction?.name : this.from;
|
|
133
|
+
throw new Error(`Failed to load data from ${source}. Error: ${e}`);
|
|
85
134
|
}
|
|
86
135
|
}
|
|
87
136
|
public value(): any {
|
package/src/parsing/parser.ts
CHANGED
|
@@ -4,6 +4,7 @@ import ASTNode from "./ast_node";
|
|
|
4
4
|
import BaseParser from "./base_parser";
|
|
5
5
|
import FunctionFactory from "./functions/function_factory";
|
|
6
6
|
import Function from "./functions/function";
|
|
7
|
+
import AsyncFunction from "./functions/async_function";
|
|
7
8
|
import AssociativeArray from "./data_structures/associative_array";
|
|
8
9
|
import JSONArray from "./data_structures/json_array";
|
|
9
10
|
import KeyValuePair from "./data_structures/key_value_pair";
|
|
@@ -218,11 +219,19 @@ class Parser extends BaseParser {
|
|
|
218
219
|
this.expectAndSkipWhitespaceAndComments();
|
|
219
220
|
const from = new From();
|
|
220
221
|
load.addChild(from);
|
|
221
|
-
|
|
222
|
-
if
|
|
223
|
-
|
|
222
|
+
|
|
223
|
+
// Check if the source is an async function
|
|
224
|
+
const asyncFunc = this.parseAsyncFunction();
|
|
225
|
+
if (asyncFunc !== null) {
|
|
226
|
+
from.addChild(asyncFunc);
|
|
227
|
+
} else {
|
|
228
|
+
const expression = this.parseExpression();
|
|
229
|
+
if(expression === null) {
|
|
230
|
+
throw new Error('Expected expression or async function');
|
|
231
|
+
}
|
|
232
|
+
from.addChild(expression);
|
|
224
233
|
}
|
|
225
|
-
|
|
234
|
+
|
|
226
235
|
this.expectAndSkipWhitespaceAndComments();
|
|
227
236
|
if(this.token.isHeaders()) {
|
|
228
237
|
const headers = new Headers();
|
|
@@ -569,6 +578,38 @@ class Parser extends BaseParser {
|
|
|
569
578
|
return func;
|
|
570
579
|
}
|
|
571
580
|
|
|
581
|
+
/**
|
|
582
|
+
* Parses an async function call for use in LOAD operations.
|
|
583
|
+
* Only matches if the identifier is registered as an async data provider.
|
|
584
|
+
*
|
|
585
|
+
* @returns An AsyncFunction node if a registered async function is found, otherwise null
|
|
586
|
+
*/
|
|
587
|
+
private parseAsyncFunction(): AsyncFunction | null {
|
|
588
|
+
if(!this.token.isIdentifier()) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
if(this.token.value === null) {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
// Only parse as async function if it's registered as an async provider
|
|
595
|
+
if(!FunctionFactory.isAsyncProvider(this.token.value)) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
if(!this.peek()?.isLeftParenthesis()) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
const asyncFunc = new AsyncFunction(this.token.value);
|
|
602
|
+
this.setNextToken(); // skip function name
|
|
603
|
+
this.setNextToken(); // skip left parenthesis
|
|
604
|
+
this.skipWhitespaceAndComments();
|
|
605
|
+
asyncFunc.parameters = Array.from(this.parseExpressions(AliasOption.NOT_ALLOWED));
|
|
606
|
+
this.skipWhitespaceAndComments();
|
|
607
|
+
if(!this.token.isRightParenthesis()) {
|
|
608
|
+
throw new Error('Expected right parenthesis');
|
|
609
|
+
}
|
|
610
|
+
this.setNextToken();
|
|
611
|
+
return asyncFunc;
|
|
612
|
+
}
|
|
572
613
|
private parsePredicateFunction(): PredicateFunction | null {
|
|
573
614
|
if(!this.ahead([Token.IDENTIFIER(""), Token.LEFT_PARENTHESIS, Token.IDENTIFIER(""), Token.IN])) {
|
|
574
615
|
return null;
|