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.
Files changed (178) hide show
  1. package/.github/workflows/npm-publish.yml +2 -0
  2. package/.github/workflows/release.yml +24 -9
  3. package/dist/compute/runner.js +75 -0
  4. package/dist/compute/runner.js.map +1 -0
  5. package/dist/flowquery.min.js +1 -0
  6. package/dist/index.browser.js +119 -0
  7. package/dist/index.browser.js.map +1 -0
  8. package/dist/index.js +16 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/index.node.js +123 -0
  11. package/dist/index.node.js.map +1 -0
  12. package/dist/io/command_line.js +102 -0
  13. package/dist/io/command_line.js.map +1 -0
  14. package/dist/parsing/alias.js +23 -0
  15. package/dist/parsing/alias.js.map +1 -0
  16. package/dist/parsing/alias_option.js +11 -0
  17. package/dist/parsing/alias_option.js.map +1 -0
  18. package/dist/parsing/ast_node.js +145 -0
  19. package/dist/parsing/ast_node.js.map +1 -0
  20. package/dist/parsing/base_parser.js +92 -0
  21. package/dist/parsing/base_parser.js.map +1 -0
  22. package/dist/parsing/components/csv.js +13 -0
  23. package/dist/parsing/components/csv.js.map +1 -0
  24. package/dist/parsing/components/from.js +16 -0
  25. package/dist/parsing/components/from.js.map +1 -0
  26. package/dist/parsing/components/headers.js +16 -0
  27. package/dist/parsing/components/headers.js.map +1 -0
  28. package/dist/parsing/components/json.js +13 -0
  29. package/dist/parsing/components/json.js.map +1 -0
  30. package/dist/parsing/components/null.js +13 -0
  31. package/dist/parsing/components/null.js.map +1 -0
  32. package/dist/parsing/components/post.js +13 -0
  33. package/dist/parsing/components/post.js.map +1 -0
  34. package/dist/parsing/components/text.js +13 -0
  35. package/dist/parsing/components/text.js.map +1 -0
  36. package/dist/parsing/context.js +47 -0
  37. package/dist/parsing/context.js.map +1 -0
  38. package/dist/parsing/data_structures/associative_array.js +45 -0
  39. package/dist/parsing/data_structures/associative_array.js.map +1 -0
  40. package/dist/parsing/data_structures/json_array.js +35 -0
  41. package/dist/parsing/data_structures/json_array.js.map +1 -0
  42. package/dist/parsing/data_structures/key_value_pair.js +41 -0
  43. package/dist/parsing/data_structures/key_value_pair.js.map +1 -0
  44. package/dist/parsing/data_structures/lookup.js +44 -0
  45. package/dist/parsing/data_structures/lookup.js.map +1 -0
  46. package/dist/parsing/data_structures/range_lookup.js +40 -0
  47. package/dist/parsing/data_structures/range_lookup.js.map +1 -0
  48. package/dist/parsing/expressions/expression.js +142 -0
  49. package/dist/parsing/expressions/expression.js.map +1 -0
  50. package/dist/parsing/expressions/f_string.js +29 -0
  51. package/dist/parsing/expressions/f_string.js.map +1 -0
  52. package/dist/parsing/expressions/identifier.js +26 -0
  53. package/dist/parsing/expressions/identifier.js.map +1 -0
  54. package/dist/parsing/expressions/number.js +41 -0
  55. package/dist/parsing/expressions/number.js.map +1 -0
  56. package/dist/parsing/expressions/operator.js +180 -0
  57. package/dist/parsing/expressions/operator.js.map +1 -0
  58. package/dist/parsing/expressions/reference.js +45 -0
  59. package/dist/parsing/expressions/reference.js.map +1 -0
  60. package/dist/parsing/expressions/string.js +34 -0
  61. package/dist/parsing/expressions/string.js.map +1 -0
  62. package/dist/parsing/functions/aggregate_function.js +58 -0
  63. package/dist/parsing/functions/aggregate_function.js.map +1 -0
  64. package/dist/parsing/functions/async_function.js +119 -0
  65. package/dist/parsing/functions/async_function.js.map +1 -0
  66. package/dist/parsing/functions/avg.js +43 -0
  67. package/dist/parsing/functions/avg.js.map +1 -0
  68. package/dist/parsing/functions/collect.js +52 -0
  69. package/dist/parsing/functions/collect.js.map +1 -0
  70. package/dist/parsing/functions/function.js +59 -0
  71. package/dist/parsing/functions/function.js.map +1 -0
  72. package/dist/parsing/functions/function_factory.js +259 -0
  73. package/dist/parsing/functions/function_factory.js.map +1 -0
  74. package/dist/parsing/functions/function_metadata.js +159 -0
  75. package/dist/parsing/functions/function_metadata.js.map +1 -0
  76. package/dist/parsing/functions/functions.js +47 -0
  77. package/dist/parsing/functions/functions.js.map +1 -0
  78. package/dist/parsing/functions/join.js +29 -0
  79. package/dist/parsing/functions/join.js.map +1 -0
  80. package/dist/parsing/functions/predicate_function.js +37 -0
  81. package/dist/parsing/functions/predicate_function.js.map +1 -0
  82. package/dist/parsing/functions/predicate_function_factory.js +19 -0
  83. package/dist/parsing/functions/predicate_function_factory.js.map +1 -0
  84. package/dist/parsing/functions/predicate_sum.js +33 -0
  85. package/dist/parsing/functions/predicate_sum.js.map +1 -0
  86. package/dist/parsing/functions/rand.js +17 -0
  87. package/dist/parsing/functions/rand.js.map +1 -0
  88. package/dist/parsing/functions/range.js +22 -0
  89. package/dist/parsing/functions/range.js.map +1 -0
  90. package/dist/parsing/functions/reducer_element.js +12 -0
  91. package/dist/parsing/functions/reducer_element.js.map +1 -0
  92. package/dist/parsing/functions/replace.js +23 -0
  93. package/dist/parsing/functions/replace.js.map +1 -0
  94. package/dist/parsing/functions/round.js +21 -0
  95. package/dist/parsing/functions/round.js.map +1 -0
  96. package/dist/parsing/functions/size.js +21 -0
  97. package/dist/parsing/functions/size.js.map +1 -0
  98. package/dist/parsing/functions/split.js +29 -0
  99. package/dist/parsing/functions/split.js.map +1 -0
  100. package/dist/parsing/functions/stringify.js +29 -0
  101. package/dist/parsing/functions/stringify.js.map +1 -0
  102. package/dist/parsing/functions/sum.js +38 -0
  103. package/dist/parsing/functions/sum.js.map +1 -0
  104. package/dist/parsing/functions/to_json.js +21 -0
  105. package/dist/parsing/functions/to_json.js.map +1 -0
  106. package/dist/parsing/functions/value_holder.js +16 -0
  107. package/dist/parsing/functions/value_holder.js.map +1 -0
  108. package/dist/parsing/logic/case.js +27 -0
  109. package/dist/parsing/logic/case.js.map +1 -0
  110. package/dist/parsing/logic/else.js +16 -0
  111. package/dist/parsing/logic/else.js.map +1 -0
  112. package/dist/parsing/logic/end.js +13 -0
  113. package/dist/parsing/logic/end.js.map +1 -0
  114. package/dist/parsing/logic/then.js +16 -0
  115. package/dist/parsing/logic/then.js.map +1 -0
  116. package/dist/parsing/logic/when.js +16 -0
  117. package/dist/parsing/logic/when.js.map +1 -0
  118. package/dist/parsing/operations/aggregated_return.js +35 -0
  119. package/dist/parsing/operations/aggregated_return.js.map +1 -0
  120. package/dist/parsing/operations/aggregated_with.js +41 -0
  121. package/dist/parsing/operations/aggregated_with.js.map +1 -0
  122. package/dist/parsing/operations/group_by.js +139 -0
  123. package/dist/parsing/operations/group_by.js.map +1 -0
  124. package/dist/parsing/operations/limit.js +38 -0
  125. package/dist/parsing/operations/limit.js.map +1 -0
  126. package/dist/parsing/operations/load.js +174 -0
  127. package/dist/parsing/operations/load.js.map +1 -0
  128. package/dist/parsing/operations/operation.js +81 -0
  129. package/dist/parsing/operations/operation.js.map +1 -0
  130. package/dist/parsing/operations/projection.js +21 -0
  131. package/dist/parsing/operations/projection.js.map +1 -0
  132. package/dist/parsing/operations/return.js +60 -0
  133. package/dist/parsing/operations/return.js.map +1 -0
  134. package/dist/parsing/operations/unwind.js +46 -0
  135. package/dist/parsing/operations/unwind.js.map +1 -0
  136. package/dist/parsing/operations/where.js +53 -0
  137. package/dist/parsing/operations/where.js.map +1 -0
  138. package/dist/parsing/operations/with.js +36 -0
  139. package/dist/parsing/operations/with.js.map +1 -0
  140. package/dist/parsing/parser.js +812 -0
  141. package/dist/parsing/parser.js.map +1 -0
  142. package/dist/parsing/token_to_node.js +121 -0
  143. package/dist/parsing/token_to_node.js.map +1 -0
  144. package/dist/tokenization/keyword.js +46 -0
  145. package/dist/tokenization/keyword.js.map +1 -0
  146. package/dist/tokenization/operator.js +28 -0
  147. package/dist/tokenization/operator.js.map +1 -0
  148. package/dist/tokenization/string_walker.js +165 -0
  149. package/dist/tokenization/string_walker.js.map +1 -0
  150. package/dist/tokenization/symbol.js +18 -0
  151. package/dist/tokenization/symbol.js.map +1 -0
  152. package/dist/tokenization/token.js +484 -0
  153. package/dist/tokenization/token.js.map +1 -0
  154. package/dist/tokenization/token_mapper.js +55 -0
  155. package/dist/tokenization/token_mapper.js.map +1 -0
  156. package/dist/tokenization/token_type.js +19 -0
  157. package/dist/tokenization/token_type.js.map +1 -0
  158. package/dist/tokenization/tokenizer.js +220 -0
  159. package/dist/tokenization/tokenizer.js.map +1 -0
  160. package/dist/tokenization/trie.js +111 -0
  161. package/dist/tokenization/trie.js.map +1 -0
  162. package/dist/utils/object_utils.js +19 -0
  163. package/dist/utils/object_utils.js.map +1 -0
  164. package/dist/utils/string_utils.js +110 -0
  165. package/dist/utils/string_utils.js.map +1 -0
  166. package/docs/flowquery.min.js +1 -1
  167. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  168. package/package.json +22 -4
  169. package/src/compute/runner.ts +45 -0
  170. package/src/index.browser.ts +118 -0
  171. package/src/index.node.ts +141 -0
  172. package/src/parsing/functions/async_function.ts +95 -0
  173. package/src/parsing/functions/function_factory.ts +230 -1
  174. package/src/parsing/functions/function_metadata.ts +238 -0
  175. package/src/parsing/functions/functions.ts +43 -0
  176. package/src/parsing/operations/load.ts +51 -2
  177. package/src/parsing/parser.ts +45 -4
  178. package/tests/parsing/function_plugins.test.ts +369 -0
@@ -7,5 +7,123 @@
7
7
  */
8
8
 
9
9
  import {default as FlowQuery} from "./compute/runner";
10
+ import FunctionFactory, { FunctionCreator, AsyncDataProvider } from "./parsing/functions/function_factory";
11
+ import {
12
+ FunctionMetadata,
13
+ ParameterSchema,
14
+ OutputSchema,
15
+ RegisterFunctionOptions,
16
+ RegisterAsyncProviderOptions
17
+ } from "./parsing/functions/function_metadata";
18
+ import Function from "./parsing/functions/function";
19
+
20
+ /**
21
+ * Register a synchronous plugin function.
22
+ *
23
+ * @param name - The function name
24
+ * @param factoryOrOptions - Factory function or options object with metadata
25
+ *
26
+ * @example
27
+ * ```javascript
28
+ * // Simple registration
29
+ * FlowQuery.registerFunction("uppercase", () => new MyUpperCaseFunction());
30
+ *
31
+ * // Registration with metadata for LLM consumption
32
+ * FlowQuery.registerFunction("uppercase", {
33
+ * factory: () => new MyUpperCaseFunction(),
34
+ * metadata: {
35
+ * name: "uppercase",
36
+ * description: "Converts a string to uppercase",
37
+ * category: "string",
38
+ * parameters: [{ name: "text", description: "String to convert", type: "string" }],
39
+ * output: { description: "Uppercase string", type: "string" },
40
+ * examples: ["WITH 'hello' AS s RETURN uppercase(s)"]
41
+ * }
42
+ * });
43
+ * ```
44
+ */
45
+ FlowQuery.registerFunction = function(name: string, factoryOrOptions: FunctionCreator | RegisterFunctionOptions): void {
46
+ FunctionFactory.register(name, factoryOrOptions);
47
+ };
48
+
49
+ /**
50
+ * Unregister a synchronous plugin function.
51
+ *
52
+ * @param name - The function name
53
+ */
54
+ FlowQuery.unregisterFunction = function(name: string): void {
55
+ FunctionFactory.unregister(name);
56
+ };
57
+
58
+ /**
59
+ * Register an async data provider function for use in LOAD operations.
60
+ *
61
+ * @param name - The function name
62
+ * @param providerOrOptions - Async provider or options object with metadata
63
+ *
64
+ * @example
65
+ * ```javascript
66
+ * // Registration with metadata for LLM consumption
67
+ * FlowQuery.registerAsyncProvider("fetchUsers", {
68
+ * provider: async function* (endpoint) {
69
+ * const response = await fetch(endpoint);
70
+ * const data = await response.json();
71
+ * for (const item of data) yield item;
72
+ * },
73
+ * metadata: {
74
+ * name: "fetchUsers",
75
+ * description: "Fetches user data from an API",
76
+ * category: "data",
77
+ * parameters: [{ name: "endpoint", description: "API URL", type: "string" }],
78
+ * output: {
79
+ * description: "User object",
80
+ * type: "object",
81
+ * properties: {
82
+ * id: { description: "User ID", type: "number" },
83
+ * name: { description: "User name", type: "string" }
84
+ * }
85
+ * },
86
+ * examples: ["LOAD JSON FROM fetchUsers('https://api.example.com/users') AS user"]
87
+ * }
88
+ * });
89
+ * ```
90
+ */
91
+ FlowQuery.registerAsyncProvider = function(name: string, providerOrOptions: AsyncDataProvider | RegisterAsyncProviderOptions): void {
92
+ FunctionFactory.registerAsyncProvider(name, providerOrOptions);
93
+ };
94
+
95
+ /**
96
+ * Unregister an async data provider function.
97
+ *
98
+ * @param name - The function name
99
+ */
100
+ FlowQuery.unregisterAsyncProvider = function(name: string): void {
101
+ FunctionFactory.unregisterAsyncProvider(name);
102
+ };
103
+
104
+ /**
105
+ * List all registered functions with their metadata.
106
+ *
107
+ * @param options - Optional filter options
108
+ * @returns Array of function metadata
109
+ */
110
+ FlowQuery.listFunctions = function(options?: { category?: string; asyncOnly?: boolean; syncOnly?: boolean }): FunctionMetadata[] {
111
+ return FunctionFactory.listFunctions(options);
112
+ };
113
+
114
+ /**
115
+ * Get metadata for a specific function.
116
+ *
117
+ * @param name - The function name
118
+ * @returns Function metadata or undefined
119
+ */
120
+ FlowQuery.getFunctionMetadata = function(name: string): FunctionMetadata | undefined {
121
+ return FunctionFactory.getMetadata(name);
122
+ };
123
+
124
+ /**
125
+ * Base Function class for creating custom plugin functions.
126
+ */
127
+ FlowQuery.Function = Function;
10
128
 
11
129
  export default FlowQuery;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * FlowQuery - A declarative query language for data processing pipelines.
3
+ *
4
+ * This is the main entry point for the FlowQuery Node.js library usage.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ import {default as FlowQuery} from "./compute/runner";
10
+ import FunctionFactory, { FunctionCreator, AsyncDataProvider } from "./parsing/functions/function_factory";
11
+ import {
12
+ FunctionMetadata,
13
+ ParameterSchema,
14
+ OutputSchema,
15
+ RegisterFunctionOptions,
16
+ RegisterAsyncProviderOptions
17
+ } from "./parsing/functions/function_metadata";
18
+ import Function from "./parsing/functions/function";
19
+
20
+ /**
21
+ * Register a synchronous plugin function.
22
+ *
23
+ * @param name - The function name
24
+ * @param factoryOrOptions - Factory function or options object with metadata
25
+ *
26
+ * @example
27
+ * ```javascript
28
+ * // Simple registration
29
+ * FlowQuery.registerFunction("uppercase", () => new MyUpperCaseFunction());
30
+ *
31
+ * // Registration with metadata for LLM consumption
32
+ * FlowQuery.registerFunction("uppercase", {
33
+ * factory: () => new MyUpperCaseFunction(),
34
+ * metadata: {
35
+ * name: "uppercase",
36
+ * description: "Converts a string to uppercase",
37
+ * category: "string",
38
+ * parameters: [{ name: "text", description: "String to convert", type: "string" }],
39
+ * output: { description: "Uppercase string", type: "string" },
40
+ * examples: ["WITH 'hello' AS s RETURN uppercase(s)"]
41
+ * }
42
+ * });
43
+ * ```
44
+ */
45
+ FlowQuery.registerFunction = function(name: string, factoryOrOptions: FunctionCreator | RegisterFunctionOptions): void {
46
+ FunctionFactory.register(name, factoryOrOptions);
47
+ };
48
+
49
+ /**
50
+ * Unregister a synchronous plugin function.
51
+ *
52
+ * @param name - The function name
53
+ */
54
+ FlowQuery.unregisterFunction = function(name: string): void {
55
+ FunctionFactory.unregister(name);
56
+ };
57
+
58
+ /**
59
+ * Register an async data provider function for use in LOAD operations.
60
+ *
61
+ * @param name - The function name
62
+ * @param providerOrOptions - Async provider or options object with metadata
63
+ *
64
+ * @example
65
+ * ```javascript
66
+ * // Registration with metadata for LLM consumption
67
+ * FlowQuery.registerAsyncProvider("fetchUsers", {
68
+ * provider: async function* (endpoint) {
69
+ * const response = await fetch(endpoint);
70
+ * const data = await response.json();
71
+ * for (const item of data) yield item;
72
+ * },
73
+ * metadata: {
74
+ * name: "fetchUsers",
75
+ * description: "Fetches user data from an API",
76
+ * category: "data",
77
+ * parameters: [{ name: "endpoint", description: "API URL", type: "string" }],
78
+ * output: {
79
+ * description: "User object",
80
+ * type: "object",
81
+ * properties: {
82
+ * id: { description: "User ID", type: "number" },
83
+ * name: { description: "User name", type: "string" }
84
+ * }
85
+ * },
86
+ * examples: ["LOAD JSON FROM fetchUsers('https://api.example.com/users') AS user"]
87
+ * }
88
+ * });
89
+ * ```
90
+ */
91
+ FlowQuery.registerAsyncProvider = function(name: string, providerOrOptions: AsyncDataProvider | RegisterAsyncProviderOptions): void {
92
+ FunctionFactory.registerAsyncProvider(name, providerOrOptions);
93
+ };
94
+
95
+ /**
96
+ * Unregister an async data provider function.
97
+ *
98
+ * @param name - The function name
99
+ */
100
+ FlowQuery.unregisterAsyncProvider = function(name: string): void {
101
+ FunctionFactory.unregisterAsyncProvider(name);
102
+ };
103
+
104
+ /**
105
+ * List all registered functions with their metadata.
106
+ *
107
+ * @param options - Optional filter options
108
+ * @returns Array of function metadata
109
+ */
110
+ FlowQuery.listFunctions = function(options?: { category?: string; asyncOnly?: boolean; syncOnly?: boolean }): FunctionMetadata[] {
111
+ return FunctionFactory.listFunctions(options);
112
+ };
113
+
114
+ /**
115
+ * Get metadata for a specific function.
116
+ *
117
+ * @param name - The function name
118
+ * @returns Function metadata or undefined
119
+ */
120
+ FlowQuery.getFunctionMetadata = function(name: string): FunctionMetadata | undefined {
121
+ return FunctionFactory.getMetadata(name);
122
+ };
123
+
124
+ /**
125
+ * Base Function class for creating custom plugin functions.
126
+ */
127
+ FlowQuery.Function = Function;
128
+
129
+ export default FlowQuery;
130
+ export {
131
+ FlowQuery,
132
+ Function,
133
+ FunctionFactory,
134
+ FunctionCreator,
135
+ AsyncDataProvider,
136
+ FunctionMetadata,
137
+ ParameterSchema,
138
+ OutputSchema,
139
+ RegisterFunctionOptions,
140
+ RegisterAsyncProviderOptions
141
+ };
@@ -0,0 +1,95 @@
1
+ import ASTNode from "../ast_node";
2
+ import FunctionFactory from "./function_factory";
3
+
4
+ /**
5
+ * Represents an async data provider function call for use in LOAD operations.
6
+ *
7
+ * This class holds the function name and arguments, and provides async iteration
8
+ * over the results from a registered async data provider.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Used in: LOAD JSON FROM myDataSource('arg1', 'arg2') AS data
13
+ * const asyncFunc = new AsyncFunction("myDataSource");
14
+ * asyncFunc.parameters = [arg1Node, arg2Node];
15
+ * for await (const item of asyncFunc.execute()) {
16
+ * console.log(item);
17
+ * }
18
+ * ```
19
+ */
20
+ class AsyncFunction extends ASTNode {
21
+ private _name: string;
22
+
23
+ /**
24
+ * Creates a new AsyncFunction with the given name.
25
+ *
26
+ * @param name - The function name (must be registered as an async provider)
27
+ */
28
+ constructor(name: string) {
29
+ super();
30
+ this._name = name;
31
+ }
32
+
33
+ /**
34
+ * Gets the function name.
35
+ */
36
+ public get name(): string {
37
+ return this._name;
38
+ }
39
+
40
+ /**
41
+ * Sets the function parameters.
42
+ *
43
+ * @param nodes - Array of AST nodes representing the function arguments
44
+ */
45
+ public set parameters(nodes: ASTNode[]) {
46
+ this.children = nodes;
47
+ }
48
+
49
+ /**
50
+ * Evaluates all parameters and returns their values.
51
+ *
52
+ * @returns Array of parameter values
53
+ */
54
+ private getArguments(): any[] {
55
+ return this.children.map(child => child.value());
56
+ }
57
+
58
+ /**
59
+ * Executes the async data provider function and yields results.
60
+ *
61
+ * @yields Data items from the async provider
62
+ * @throws {Error} If the function is not registered as an async provider
63
+ */
64
+ public async *execute(): AsyncGenerator<any, void, unknown> {
65
+ const provider = FunctionFactory.getAsyncProvider(this._name);
66
+ if (!provider) {
67
+ throw new Error(`Async data provider '${this._name}' is not registered`);
68
+ }
69
+
70
+ const args = this.getArguments();
71
+ const result = provider(...args);
72
+
73
+ // Check if result is an async generator or a promise
74
+ if (result && typeof (result as AsyncGenerator).next === 'function') {
75
+ // It's an async generator
76
+ yield* result as AsyncGenerator<any, void, unknown>;
77
+ } else {
78
+ // It's a promise - await and yield the result
79
+ const data = await result;
80
+ if (Array.isArray(data)) {
81
+ for (const item of data) {
82
+ yield item;
83
+ }
84
+ } else {
85
+ yield data;
86
+ }
87
+ }
88
+ }
89
+
90
+ public toString(): string {
91
+ return `AsyncFunction (${this._name})`;
92
+ }
93
+ }
94
+
95
+ export default AsyncFunction;
@@ -10,7 +10,25 @@ import ToJson from "./to_json";
10
10
  import Replace from "./replace";
11
11
  import Stringify from "./stringify";
12
12
  import Size from "./size";
13
+ import Functions from "./functions";
13
14
  import Function from "./function";
15
+ import {
16
+ FunctionMetadata,
17
+ RegisterFunctionOptions,
18
+ RegisterAsyncProviderOptions,
19
+ BUILTIN_FUNCTION_METADATA
20
+ } from "./function_metadata";
21
+
22
+ /**
23
+ * Type for synchronous function factories.
24
+ */
25
+ export type FunctionCreator = () => Function;
26
+
27
+ /**
28
+ * Type for async data provider functions used in LOAD operations.
29
+ * These functions can yield data asynchronously.
30
+ */
31
+ export type AsyncDataProvider = (...args: any[]) => AsyncGenerator<any, void, unknown> | Promise<any>;
14
32
 
15
33
  /**
16
34
  * Factory for creating function instances by name.
@@ -25,6 +43,208 @@ import Function from "./function";
25
43
  * ```
26
44
  */
27
45
  class FunctionFactory {
46
+ /**
47
+ * Registry for plugin functions (synchronous).
48
+ */
49
+ private static plugins: Map<string, FunctionCreator> = new Map();
50
+
51
+ /**
52
+ * Registry for async data provider functions used in LOAD operations.
53
+ */
54
+ private static asyncProviders: Map<string, AsyncDataProvider> = new Map();
55
+
56
+ /**
57
+ * Registry for function metadata (both sync and async).
58
+ */
59
+ private static metadata: Map<string, FunctionMetadata> = new Map();
60
+
61
+ /**
62
+ * Registers a synchronous plugin function.
63
+ *
64
+ * @param name - The function name (will be lowercased)
65
+ * @param factoryOrOptions - Factory function or options object with metadata
66
+ */
67
+ public static register(name: string, factoryOrOptions: FunctionCreator | RegisterFunctionOptions): void {
68
+ const lowerName = name.toLowerCase();
69
+ if (typeof factoryOrOptions === 'function') {
70
+ FunctionFactory.plugins.set(lowerName, factoryOrOptions);
71
+ } else {
72
+ FunctionFactory.plugins.set(lowerName, factoryOrOptions.factory);
73
+ FunctionFactory.metadata.set(lowerName, {
74
+ ...factoryOrOptions.metadata,
75
+ name: lowerName,
76
+ isAsyncProvider: false
77
+ });
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Unregisters a synchronous plugin function.
83
+ *
84
+ * @param name - The function name to unregister
85
+ */
86
+ public static unregister(name: string): void {
87
+ const lowerName = name.toLowerCase();
88
+ FunctionFactory.plugins.delete(lowerName);
89
+ FunctionFactory.metadata.delete(lowerName);
90
+ }
91
+
92
+ /**
93
+ * Registers an async data provider function for use in LOAD operations.
94
+ *
95
+ * @param name - The function name (will be lowercased)
96
+ * @param providerOrOptions - Async provider or options object with metadata
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * // Register with metadata for LLM consumption
101
+ * FunctionFactory.registerAsyncProvider("fetchUsers", {
102
+ * provider: async function* (endpoint: string) {
103
+ * const response = await fetch(endpoint);
104
+ * const data = await response.json();
105
+ * for (const item of data) {
106
+ * yield item;
107
+ * }
108
+ * },
109
+ * metadata: {
110
+ * name: "fetchUsers",
111
+ * description: "Fetches user data from an API endpoint",
112
+ * parameters: [
113
+ * { name: "endpoint", description: "API endpoint URL", type: "string" }
114
+ * ],
115
+ * output: {
116
+ * description: "User objects",
117
+ * type: "object",
118
+ * properties: {
119
+ * id: { description: "User ID", type: "number" },
120
+ * name: { description: "User name", type: "string" }
121
+ * }
122
+ * },
123
+ * examples: ["LOAD JSON FROM fetchUsers('https://api.example.com/users') AS user"]
124
+ * }
125
+ * });
126
+ * ```
127
+ */
128
+ public static registerAsyncProvider(name: string, providerOrOptions: AsyncDataProvider | RegisterAsyncProviderOptions): void {
129
+ const lowerName = name.toLowerCase();
130
+ if (typeof providerOrOptions === 'function') {
131
+ FunctionFactory.asyncProviders.set(lowerName, providerOrOptions);
132
+ } else {
133
+ FunctionFactory.asyncProviders.set(lowerName, providerOrOptions.provider);
134
+ FunctionFactory.metadata.set(lowerName, {
135
+ ...providerOrOptions.metadata,
136
+ name: lowerName,
137
+ isAsyncProvider: true
138
+ });
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Unregisters an async data provider function.
144
+ *
145
+ * @param name - The function name to unregister
146
+ */
147
+ public static unregisterAsyncProvider(name: string): void {
148
+ const lowerName = name.toLowerCase();
149
+ FunctionFactory.asyncProviders.delete(lowerName);
150
+ FunctionFactory.metadata.delete(lowerName);
151
+ }
152
+
153
+ /**
154
+ * Gets an async data provider by name.
155
+ *
156
+ * @param name - The function name (case-insensitive)
157
+ * @returns The async data provider, or undefined if not found
158
+ */
159
+ public static getAsyncProvider(name: string): AsyncDataProvider | undefined {
160
+ return FunctionFactory.asyncProviders.get(name.toLowerCase());
161
+ }
162
+
163
+ /**
164
+ * Checks if a function name is registered as an async data provider.
165
+ *
166
+ * @param name - The function name (case-insensitive)
167
+ * @returns True if the function is an async data provider
168
+ */
169
+ public static isAsyncProvider(name: string): boolean {
170
+ return FunctionFactory.asyncProviders.has(name.toLowerCase());
171
+ }
172
+
173
+ /**
174
+ * Gets metadata for a specific function.
175
+ *
176
+ * @param name - The function name (case-insensitive)
177
+ * @returns The function metadata, or undefined if not found
178
+ */
179
+ public static getMetadata(name: string): FunctionMetadata | undefined {
180
+ const lowerName = name.toLowerCase();
181
+ // Check plugin metadata first
182
+ if (FunctionFactory.metadata.has(lowerName)) {
183
+ return FunctionFactory.metadata.get(lowerName);
184
+ }
185
+ // Fall back to built-in metadata
186
+ return BUILTIN_FUNCTION_METADATA.find(m => m.name === lowerName);
187
+ }
188
+
189
+ /**
190
+ * Lists all registered functions with their metadata.
191
+ * Includes both built-in and plugin functions.
192
+ *
193
+ * @param options - Optional filter options
194
+ * @returns Array of function metadata
195
+ */
196
+ public static listFunctions(options?: {
197
+ category?: string;
198
+ includeBuiltins?: boolean;
199
+ asyncOnly?: boolean;
200
+ syncOnly?: boolean;
201
+ }): FunctionMetadata[] {
202
+ const result: FunctionMetadata[] = [];
203
+ const includeBuiltins = options?.includeBuiltins !== false;
204
+
205
+ // Add built-in functions
206
+ if (includeBuiltins) {
207
+ for (const meta of BUILTIN_FUNCTION_METADATA) {
208
+ if (options?.category && meta.category !== options.category) continue;
209
+ if (options?.asyncOnly) continue; // Built-ins are sync
210
+ result.push(meta);
211
+ }
212
+ }
213
+
214
+ // Add plugin functions
215
+ for (const [name, meta] of FunctionFactory.metadata) {
216
+ if (options?.category && meta.category !== options.category) continue;
217
+ if (options?.asyncOnly && !meta.isAsyncProvider) continue;
218
+ if (options?.syncOnly && meta.isAsyncProvider) continue;
219
+ result.push(meta);
220
+ }
221
+
222
+ return result;
223
+ }
224
+
225
+ /**
226
+ * Lists all registered function names.
227
+ *
228
+ * @returns Array of function names
229
+ */
230
+ public static listFunctionNames(): string[] {
231
+ const builtinNames = BUILTIN_FUNCTION_METADATA.map(m => m.name);
232
+ const pluginNames = Array.from(FunctionFactory.plugins.keys());
233
+ const asyncNames = Array.from(FunctionFactory.asyncProviders.keys());
234
+ return [...new Set([...builtinNames, ...pluginNames, ...asyncNames])];
235
+ }
236
+
237
+ /**
238
+ * Gets all function metadata as a JSON-serializable object for LLM consumption.
239
+ *
240
+ * @returns Object with functions grouped by category
241
+ */
242
+ public static toJSON(): { functions: FunctionMetadata[]; categories: string[] } {
243
+ const functions = FunctionFactory.listFunctions();
244
+ const categories = [...new Set(functions.map(f => f.category).filter(Boolean))] as string[];
245
+ return { functions, categories };
246
+ }
247
+
28
248
  /**
29
249
  * Creates a function instance by name.
30
250
  *
@@ -32,7 +252,14 @@ class FunctionFactory {
32
252
  * @returns A Function instance of the appropriate type
33
253
  */
34
254
  public static create(name: string): Function {
35
- switch (name.toLowerCase()) {
255
+ const lowerName = name.toLowerCase();
256
+
257
+ // Check plugin registry first (allows overriding built-ins)
258
+ if (FunctionFactory.plugins.has(lowerName)) {
259
+ return FunctionFactory.plugins.get(lowerName)!();
260
+ }
261
+
262
+ switch (lowerName) {
36
263
  case "sum":
37
264
  return new Sum();
38
265
  case "collect":
@@ -57,6 +284,8 @@ class FunctionFactory {
57
284
  return new Stringify();
58
285
  case "size":
59
286
  return new Size();
287
+ case "functions":
288
+ return new Functions();
60
289
  default:
61
290
  return new Function(name);
62
291
  }