flowquery 1.0.5 → 1.0.7

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 (138) hide show
  1. package/README.md +74 -0
  2. package/dist/compute/runner.d.ts +1 -22
  3. package/dist/compute/runner.d.ts.map +1 -1
  4. package/dist/compute/runner.js.map +1 -1
  5. package/dist/extensibility.d.ts +35 -0
  6. package/dist/extensibility.d.ts.map +1 -0
  7. package/dist/extensibility.js +49 -0
  8. package/dist/extensibility.js.map +1 -0
  9. package/dist/flowquery.min.js +1 -1
  10. package/dist/index.browser.d.ts.map +1 -1
  11. package/dist/index.browser.js +0 -80
  12. package/dist/index.browser.js.map +1 -1
  13. package/dist/index.node.d.ts +3 -3
  14. package/dist/index.node.d.ts.map +1 -1
  15. package/dist/index.node.js +0 -80
  16. package/dist/index.node.js.map +1 -1
  17. package/dist/parsing/functions/avg.d.ts.map +1 -1
  18. package/dist/parsing/functions/avg.js +20 -2
  19. package/dist/parsing/functions/avg.js.map +1 -1
  20. package/dist/parsing/functions/collect.d.ts.map +1 -1
  21. package/dist/parsing/functions/collect.js +20 -2
  22. package/dist/parsing/functions/collect.js.map +1 -1
  23. package/dist/parsing/functions/function_factory.d.ts +26 -80
  24. package/dist/parsing/functions/function_factory.d.ts.map +1 -1
  25. package/dist/parsing/functions/function_factory.js +46 -168
  26. package/dist/parsing/functions/function_factory.js.map +1 -1
  27. package/dist/parsing/functions/function_metadata.d.ts +81 -20
  28. package/dist/parsing/functions/function_metadata.d.ts.map +1 -1
  29. package/dist/parsing/functions/function_metadata.js +154 -152
  30. package/dist/parsing/functions/function_metadata.js.map +1 -1
  31. package/dist/parsing/functions/functions.d.ts.map +1 -1
  32. package/dist/parsing/functions/functions.js +37 -2
  33. package/dist/parsing/functions/functions.js.map +1 -1
  34. package/dist/parsing/functions/join.d.ts.map +1 -1
  35. package/dist/parsing/functions/join.js +21 -2
  36. package/dist/parsing/functions/join.js.map +1 -1
  37. package/dist/parsing/functions/predicate_function.d.ts +1 -0
  38. package/dist/parsing/functions/predicate_function.d.ts.map +1 -1
  39. package/dist/parsing/functions/predicate_function.js +3 -0
  40. package/dist/parsing/functions/predicate_function.js.map +1 -1
  41. package/dist/parsing/functions/predicate_sum.d.ts.map +1 -1
  42. package/dist/parsing/functions/predicate_sum.js +23 -2
  43. package/dist/parsing/functions/predicate_sum.js.map +1 -1
  44. package/dist/parsing/functions/rand.d.ts.map +1 -1
  45. package/dist/parsing/functions/rand.js +18 -2
  46. package/dist/parsing/functions/rand.js.map +1 -1
  47. package/dist/parsing/functions/range.d.ts.map +1 -1
  48. package/dist/parsing/functions/range.js +21 -2
  49. package/dist/parsing/functions/range.js.map +1 -1
  50. package/dist/parsing/functions/replace.d.ts.map +1 -1
  51. package/dist/parsing/functions/replace.js +22 -2
  52. package/dist/parsing/functions/replace.js.map +1 -1
  53. package/dist/parsing/functions/round.d.ts.map +1 -1
  54. package/dist/parsing/functions/round.js +20 -2
  55. package/dist/parsing/functions/round.js.map +1 -1
  56. package/dist/parsing/functions/size.d.ts.map +1 -1
  57. package/dist/parsing/functions/size.js +20 -2
  58. package/dist/parsing/functions/size.js.map +1 -1
  59. package/dist/parsing/functions/split.d.ts.map +1 -1
  60. package/dist/parsing/functions/split.js +21 -2
  61. package/dist/parsing/functions/split.js.map +1 -1
  62. package/dist/parsing/functions/stringify.d.ts.map +1 -1
  63. package/dist/parsing/functions/stringify.js +20 -2
  64. package/dist/parsing/functions/stringify.js.map +1 -1
  65. package/dist/parsing/functions/sum.d.ts.map +1 -1
  66. package/dist/parsing/functions/sum.js +20 -2
  67. package/dist/parsing/functions/sum.js.map +1 -1
  68. package/dist/parsing/functions/to_json.d.ts.map +1 -1
  69. package/dist/parsing/functions/to_json.js +20 -2
  70. package/dist/parsing/functions/to_json.js.map +1 -1
  71. package/dist/parsing/parser.d.ts.map +1 -1
  72. package/dist/parsing/parser.js +1 -2
  73. package/dist/parsing/parser.js.map +1 -1
  74. package/docs/flowquery.min.js +1 -1
  75. package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
  76. package/misc/apps/RAG/.env.example +14 -0
  77. package/misc/apps/RAG/README.md +0 -7
  78. package/misc/apps/RAG/package.json +16 -7
  79. package/misc/apps/RAG/public/index.html +18 -0
  80. package/misc/apps/RAG/src/App.css +42 -0
  81. package/misc/apps/RAG/src/App.tsx +50 -0
  82. package/misc/apps/RAG/src/components/ApiKeySettings.tsx +245 -0
  83. package/misc/apps/RAG/src/components/ChatContainer.css +67 -0
  84. package/misc/apps/RAG/src/components/ChatContainer.tsx +239 -0
  85. package/misc/apps/RAG/src/components/ChatInput.css +23 -0
  86. package/misc/apps/RAG/src/components/ChatInput.tsx +62 -0
  87. package/misc/apps/RAG/src/components/ChatMessage.css +136 -0
  88. package/misc/apps/RAG/src/components/ChatMessage.tsx +152 -0
  89. package/misc/apps/RAG/src/components/FlowQueryAgent.ts +390 -0
  90. package/misc/apps/RAG/src/components/FlowQueryRunner.css +104 -0
  91. package/misc/apps/RAG/src/components/FlowQueryRunner.tsx +332 -0
  92. package/misc/apps/RAG/src/components/index.ts +15 -0
  93. package/misc/apps/RAG/src/index.tsx +17 -0
  94. package/misc/apps/RAG/src/plugins/README.md +139 -0
  95. package/misc/apps/RAG/src/plugins/index.ts +68 -0
  96. package/misc/apps/RAG/src/plugins/loaders/CatFacts.ts +75 -0
  97. package/misc/apps/RAG/src/plugins/loaders/FetchJson.ts +67 -0
  98. package/misc/apps/RAG/src/plugins/loaders/Llm.ts +437 -0
  99. package/misc/apps/RAG/src/plugins/loaders/MockData.ts +151 -0
  100. package/misc/apps/RAG/src/prompts/FlowQuerySystemPrompt.ts +385 -0
  101. package/misc/apps/RAG/src/prompts/index.ts +10 -0
  102. package/misc/apps/RAG/src/utils/FlowQueryExecutor.ts +131 -0
  103. package/misc/apps/RAG/src/utils/FlowQueryExtractor.ts +203 -0
  104. package/misc/apps/RAG/src/utils/index.ts +9 -0
  105. package/misc/apps/RAG/tsconfig.json +4 -2
  106. package/misc/apps/RAG/webpack.config.js +23 -12
  107. package/package.json +7 -1
  108. package/src/compute/runner.ts +1 -26
  109. package/src/extensibility.ts +45 -0
  110. package/src/index.browser.ts +2 -88
  111. package/src/index.node.ts +3 -92
  112. package/src/parsing/functions/avg.ts +10 -0
  113. package/src/parsing/functions/collect.ts +10 -0
  114. package/src/parsing/functions/function_factory.ts +56 -194
  115. package/src/parsing/functions/function_metadata.ts +187 -168
  116. package/src/parsing/functions/functions.ts +27 -0
  117. package/src/parsing/functions/join.ts +11 -0
  118. package/src/parsing/functions/predicate_function.ts +4 -0
  119. package/src/parsing/functions/predicate_sum.ts +13 -0
  120. package/src/parsing/functions/rand.ts +8 -0
  121. package/src/parsing/functions/range.ts +11 -0
  122. package/src/parsing/functions/replace.ts +12 -0
  123. package/src/parsing/functions/round.ts +10 -0
  124. package/src/parsing/functions/size.ts +10 -0
  125. package/src/parsing/functions/split.ts +11 -0
  126. package/src/parsing/functions/stringify.ts +10 -0
  127. package/src/parsing/functions/sum.ts +10 -0
  128. package/src/parsing/functions/to_json.ts +10 -0
  129. package/src/parsing/parser.ts +1 -2
  130. package/tests/extensibility.test.ts +563 -0
  131. package/tsconfig.json +1 -0
  132. package/dist/parsing/functions/predicate_function_factory.d.ts +0 -6
  133. package/dist/parsing/functions/predicate_function_factory.d.ts.map +0 -1
  134. package/dist/parsing/functions/predicate_function_factory.js +0 -19
  135. package/dist/parsing/functions/predicate_function_factory.js.map +0 -1
  136. package/misc/apps/RAG/src/index.ts +0 -20
  137. package/src/parsing/functions/predicate_function_factory.ts +0 -15
  138. package/tests/parsing/function_plugins.test.ts +0 -369
@@ -0,0 +1,104 @@
1
+ .flowquery-runner-surface {
2
+ max-width: 800px;
3
+ width: 90vw;
4
+ }
5
+
6
+ .flowquery-runner-content {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 16px;
10
+ padding-top: 16px;
11
+ }
12
+
13
+ .flowquery-input-container {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 8px;
17
+ }
18
+
19
+ .flowquery-textarea {
20
+ width: 100%;
21
+ min-height: 120px;
22
+ font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
23
+ font-size: 13px;
24
+ resize: vertical;
25
+ }
26
+
27
+ .flowquery-actions {
28
+ display: flex;
29
+ gap: 8px;
30
+ align-items: center;
31
+ }
32
+
33
+ .flowquery-execution-time {
34
+ margin-left: auto;
35
+ font-size: 12px;
36
+ color: #666;
37
+ }
38
+
39
+ .flowquery-results-container {
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 8px;
43
+ }
44
+
45
+ .flowquery-results-header {
46
+ display: flex;
47
+ justify-content: space-between;
48
+ align-items: center;
49
+ }
50
+
51
+ .flowquery-results-count {
52
+ font-size: 12px;
53
+ color: #666;
54
+ }
55
+
56
+ .flowquery-results {
57
+ max-height: 300px;
58
+ overflow: auto;
59
+ border: 1px solid #e0e0e0;
60
+ border-radius: 4px;
61
+ background-color: #f8f8f8;
62
+ }
63
+
64
+ .flowquery-results-content {
65
+ padding: 12px;
66
+ font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
67
+ font-size: 12px;
68
+ white-space: pre-wrap;
69
+ word-break: break-word;
70
+ margin: 0;
71
+ }
72
+
73
+ .flowquery-error {
74
+ color: #c50f1f;
75
+ padding: 12px;
76
+ margin: 0;
77
+ }
78
+
79
+ .flowquery-no-results {
80
+ color: #666;
81
+ font-style: italic;
82
+ padding: 12px;
83
+ }
84
+
85
+ .flowquery-loading {
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 8px;
89
+ padding: 12px;
90
+ color: #666;
91
+ }
92
+
93
+ .flowquery-examples-list {
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: 4px;
97
+ }
98
+
99
+ .flowquery-example-button {
100
+ text-align: left;
101
+ justify-content: flex-start;
102
+ font-family: 'Cascadia Code', 'Fira Code', 'Consolas', monospace;
103
+ font-size: 12px;
104
+ }
@@ -0,0 +1,332 @@
1
+ /**
2
+ * FlowQuery Runner Component
3
+ *
4
+ * A popup dialog that allows users to run FlowQuery statements directly
5
+ * and see the results in the dialog.
6
+ */
7
+
8
+ import React, { Component } from 'react';
9
+ import {
10
+ Dialog,
11
+ DialogTrigger,
12
+ DialogSurface,
13
+ DialogTitle,
14
+ DialogBody,
15
+ DialogActions,
16
+ DialogContent,
17
+ Button,
18
+ Textarea,
19
+ Label,
20
+ Field,
21
+ Spinner,
22
+ Tooltip,
23
+ } from '@fluentui/react-components';
24
+ import {
25
+ Play24Regular,
26
+ Code24Regular,
27
+ Dismiss24Regular,
28
+ Copy24Regular,
29
+ ArrowClockwise24Regular,
30
+ Checkmark24Regular,
31
+ } from '@fluentui/react-icons';
32
+ import { FlowQueryExecutor, FlowQueryExecutionResult } from '../utils/FlowQueryExecutor';
33
+ import './FlowQueryRunner.css';
34
+
35
+ const flowQueryExecutor = new FlowQueryExecutor();
36
+
37
+ interface FlowQueryRunnerProps {
38
+ /** Initial query to pre-populate the input */
39
+ initialQuery?: string;
40
+ /** Controlled open state - if provided, the component becomes controlled */
41
+ open?: boolean;
42
+ /** Callback when open state changes (for controlled mode) */
43
+ onOpenChange?: (open: boolean) => void;
44
+ /** Callback when the dialog is closed (deprecated, use onOpenChange) */
45
+ onClose?: () => void;
46
+ /** Custom trigger element. If not provided, a default button is shown. Not used in controlled mode. */
47
+ trigger?: React.ReactElement;
48
+ }
49
+
50
+ interface FlowQueryRunnerState {
51
+ open: boolean;
52
+ query: string;
53
+ isExecuting: boolean;
54
+ result: FlowQueryExecutionResult | null;
55
+ copied: boolean;
56
+ }
57
+
58
+ export class FlowQueryRunner extends Component<FlowQueryRunnerProps, FlowQueryRunnerState> {
59
+ constructor(props: FlowQueryRunnerProps) {
60
+ super(props);
61
+ this.state = {
62
+ open: props.open ?? false,
63
+ query: props.initialQuery || '',
64
+ isExecuting: false,
65
+ result: null,
66
+ copied: false,
67
+ };
68
+ }
69
+
70
+ componentDidUpdate(prevProps: FlowQueryRunnerProps): void {
71
+ // Handle controlled mode: sync open state from props
72
+ if (this.props.open !== undefined && this.props.open !== prevProps.open) {
73
+ this.setState({ open: this.props.open });
74
+ // If opening with a new initial query, update the query
75
+ if (this.props.open && this.props.initialQuery !== prevProps.initialQuery) {
76
+ this.setState({ query: this.props.initialQuery || '', result: null });
77
+ }
78
+ }
79
+ // Handle initialQuery changes when opening
80
+ if (this.props.initialQuery !== prevProps.initialQuery && this.props.open) {
81
+ this.setState({ query: this.props.initialQuery || '', result: null });
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Check if the component is in controlled mode
87
+ */
88
+ isControlled = (): boolean => {
89
+ return this.props.open !== undefined;
90
+ };
91
+
92
+ handleOpenChange = (_: unknown, data: { open: boolean }): void => {
93
+ if (this.isControlled()) {
94
+ // In controlled mode, notify parent
95
+ this.props.onOpenChange?.(data.open);
96
+ } else {
97
+ // In uncontrolled mode, manage state internally
98
+ this.setState({ open: data.open });
99
+ }
100
+
101
+ if (!data.open && this.props.onClose) {
102
+ this.props.onClose();
103
+ }
104
+ };
105
+
106
+ handleClose = (): void => {
107
+ if (this.isControlled()) {
108
+ this.props.onOpenChange?.(false);
109
+ } else {
110
+ this.setState({ open: false });
111
+ }
112
+ this.props.onClose?.();
113
+ };
114
+
115
+ handleQueryChange = (_: unknown, data: { value: string }): void => {
116
+ this.setState({ query: data.value });
117
+ };
118
+
119
+ handleExecute = async (): Promise<void> => {
120
+ const { query } = this.state;
121
+
122
+ if (!query.trim()) {
123
+ return;
124
+ }
125
+
126
+ this.setState({ isExecuting: true, result: null });
127
+
128
+ try {
129
+ const result = await flowQueryExecutor.execute(query);
130
+ this.setState({ result, isExecuting: false });
131
+ } catch (error) {
132
+ this.setState({
133
+ result: {
134
+ success: false,
135
+ query,
136
+ error: error instanceof Error ? error.message : String(error),
137
+ executionTime: 0,
138
+ },
139
+ isExecuting: false,
140
+ });
141
+ }
142
+ };
143
+
144
+ handleClear = (): void => {
145
+ this.setState({ query: '', result: null });
146
+ };
147
+
148
+ handleCopyResults = (): void => {
149
+ const { result } = this.state;
150
+ if (result?.success && result.results) {
151
+ navigator.clipboard.writeText(JSON.stringify(result.results, null, 2));
152
+ this.setState({ copied: true });
153
+ setTimeout(() => this.setState({ copied: false }), 2000);
154
+ }
155
+ };
156
+
157
+ handleKeyDown = (event: React.KeyboardEvent): void => {
158
+ // Execute on Ctrl+Enter or Cmd+Enter
159
+ if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
160
+ event.preventDefault();
161
+ this.handleExecute();
162
+ }
163
+ };
164
+
165
+ renderResults(): React.ReactNode {
166
+ const { result, isExecuting, copied } = this.state;
167
+
168
+ if (isExecuting) {
169
+ return (
170
+ <div className="flowquery-loading">
171
+ <Spinner size="tiny" />
172
+ <span>Executing query...</span>
173
+ </div>
174
+ );
175
+ }
176
+
177
+ if (!result) {
178
+ return (
179
+ <div className="flowquery-no-results">
180
+ Enter a FlowQuery statement and click Run to see results.
181
+ </div>
182
+ );
183
+ }
184
+
185
+ if (!result.success) {
186
+ return (
187
+ <pre className="flowquery-error">
188
+ Error: {result.error}
189
+ </pre>
190
+ );
191
+ }
192
+
193
+ const resultCount = result.results?.length || 0;
194
+ const resultsJson = JSON.stringify(result.results, null, 2);
195
+
196
+ return (
197
+ <div className="flowquery-results-container">
198
+ <div className="flowquery-results-header">
199
+ <span className="flowquery-results-count">
200
+ {resultCount} result{resultCount !== 1 ? 's' : ''}
201
+ {' • '}
202
+ {result.executionTime.toFixed(2)}ms
203
+ </span>
204
+ <Tooltip
205
+ content={copied ? "Copied!" : "Copy results"}
206
+ relationship="label"
207
+ >
208
+ <Button
209
+ appearance="subtle"
210
+ icon={copied ? <Checkmark24Regular /> : <Copy24Regular />}
211
+ size="small"
212
+ onClick={this.handleCopyResults}
213
+ disabled={!result.results?.length}
214
+ />
215
+ </Tooltip>
216
+ </div>
217
+ <div className="flowquery-results">
218
+ <pre className="flowquery-results-content">
219
+ {resultsJson}
220
+ </pre>
221
+ </div>
222
+ </div>
223
+ );
224
+ }
225
+
226
+ renderDialogSurface(): React.JSX.Element {
227
+ const { query, isExecuting } = this.state;
228
+
229
+ return (
230
+ <DialogSurface className="flowquery-runner-surface">
231
+ <DialogBody>
232
+ <DialogTitle
233
+ action={
234
+ <Button
235
+ appearance="subtle"
236
+ icon={<Dismiss24Regular />}
237
+ onClick={this.handleClose}
238
+ />
239
+ }
240
+ >
241
+ FlowQuery Runner
242
+ </DialogTitle>
243
+ <DialogContent className="flowquery-runner-content">
244
+ <div className="flowquery-input-container">
245
+ <Field label="FlowQuery Statement">
246
+ <Textarea
247
+ value={query}
248
+ onChange={this.handleQueryChange}
249
+ onKeyDown={this.handleKeyDown}
250
+ placeholder="Enter your FlowQuery statement here..."
251
+ className="flowquery-textarea"
252
+ resize="vertical"
253
+ />
254
+ </Field>
255
+ <div className="flowquery-actions">
256
+ <Tooltip content="Run query (Ctrl+Enter)" relationship="label">
257
+ <Button
258
+ appearance="primary"
259
+ icon={<Play24Regular />}
260
+ onClick={this.handleExecute}
261
+ disabled={isExecuting || !query.trim()}
262
+ >
263
+ Run
264
+ </Button>
265
+ </Tooltip>
266
+ <Tooltip content="Clear query and results" relationship="label">
267
+ <Button
268
+ appearance="subtle"
269
+ icon={<ArrowClockwise24Regular />}
270
+ onClick={this.handleClear}
271
+ disabled={isExecuting}
272
+ >
273
+ Clear
274
+ </Button>
275
+ </Tooltip>
276
+ </div>
277
+ </div>
278
+
279
+ <Field label="Results">
280
+ <div className="flowquery-results">
281
+ {this.renderResults()}
282
+ </div>
283
+ </Field>
284
+ </DialogContent>
285
+ <DialogActions>
286
+ <Button
287
+ appearance="secondary"
288
+ onClick={this.handleClose}
289
+ >
290
+ Close
291
+ </Button>
292
+ </DialogActions>
293
+ </DialogBody>
294
+ </DialogSurface>
295
+ );
296
+ }
297
+
298
+ render(): React.ReactNode {
299
+ const { open } = this.state;
300
+ const { trigger } = this.props;
301
+ const isControlled = this.isControlled();
302
+
303
+ // In controlled mode, render dialog without trigger
304
+ if (isControlled) {
305
+ return (
306
+ <Dialog open={open} onOpenChange={this.handleOpenChange}>
307
+ {this.renderDialogSurface()}
308
+ </Dialog>
309
+ );
310
+ }
311
+
312
+ // In uncontrolled mode, render with trigger
313
+ return (
314
+ <Dialog open={open} onOpenChange={this.handleOpenChange}>
315
+ <DialogTrigger disableButtonEnhancement>
316
+ {trigger || (
317
+ <Button
318
+ appearance="subtle"
319
+ icon={<Code24Regular />}
320
+ title="FlowQuery Runner"
321
+ >
322
+ FlowQuery
323
+ </Button>
324
+ )}
325
+ </DialogTrigger>
326
+ {this.renderDialogSurface()}
327
+ </Dialog>
328
+ );
329
+ }
330
+ }
331
+
332
+ export default FlowQueryRunner;
@@ -0,0 +1,15 @@
1
+ // Chat components exports
2
+ export { ChatContainer } from './ChatContainer';
3
+ export { ChatMessage } from './ChatMessage';
4
+ export { ChatInput } from './ChatInput';
5
+ export { ApiKeySettings } from './ApiKeySettings';
6
+
7
+ // FlowQuery Runner
8
+ export { FlowQueryRunner } from './FlowQueryRunner';
9
+
10
+ // FlowQuery Agent
11
+ export { processQuery, processQueryStream } from './FlowQueryAgent';
12
+ export type { AgentStep, AgentResult, FlowQueryAgentOptions, AgentStreamCallback } from './FlowQueryAgent';
13
+
14
+ // Types
15
+ export type { Message } from './ChatMessage';
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+ import { initializePlugins } from './plugins';
5
+
6
+ // Initialize FlowQuery plugins before rendering
7
+ initializePlugins();
8
+
9
+ const container = document.getElementById('root');
10
+ if (container) {
11
+ const root = createRoot(container);
12
+ root.render(
13
+ <React.StrictMode>
14
+ <App />
15
+ </React.StrictMode>
16
+ );
17
+ }
@@ -0,0 +1,139 @@
1
+ # FlowQuery Plugin System
2
+
3
+ This folder contains the plugin system for adding async data loader functions to FlowQuery.
4
+
5
+ ## Quick Start
6
+
7
+ 1. Create a new file in `loaders/` (e.g., `my-api.ts`)
8
+ 2. Define your plugin following the `AsyncLoaderPlugin` interface
9
+ 3. Import and add it to the `allPlugins` array in `index.ts`
10
+
11
+ ## Creating a Plugin
12
+
13
+ ### Basic Plugin Structure
14
+
15
+ ```typescript
16
+ import { AsyncLoaderPlugin } from '../types';
17
+
18
+ // Your async data provider function
19
+ async function* myDataProvider(arg1: string, arg2?: number): AsyncGenerator<any, void, unknown> {
20
+ const response = await fetch(`https://api.example.com/${arg1}`);
21
+ const data = await response.json();
22
+
23
+ // Yield items one at a time (for arrays)
24
+ if (Array.isArray(data)) {
25
+ for (const item of data) {
26
+ yield item;
27
+ }
28
+ } else {
29
+ yield data;
30
+ }
31
+ }
32
+
33
+ // Export the plugin definition
34
+ export const myPlugin: AsyncLoaderPlugin = {
35
+ name: 'myData', // Function name in FlowQuery
36
+ provider: myDataProvider,
37
+ metadata: {
38
+ description: 'Fetches data from My API',
39
+ category: 'data',
40
+ parameters: [
41
+ { name: 'arg1', description: 'First argument', type: 'string', required: true },
42
+ { name: 'arg2', description: 'Optional second arg', type: 'number', required: false }
43
+ ],
44
+ output: {
45
+ description: 'Data item',
46
+ type: 'object',
47
+ properties: {
48
+ id: { description: 'Item ID', type: 'number' },
49
+ value: { description: 'Item value', type: 'string' }
50
+ }
51
+ },
52
+ examples: [
53
+ "LOAD JSON FROM myData('users') AS item RETURN item.id, item.value"
54
+ ]
55
+ }
56
+ };
57
+
58
+ export default myPlugin;
59
+ ```
60
+
61
+ ### Register the Plugin
62
+
63
+ Add your plugin to `index.ts`:
64
+
65
+ ```typescript
66
+ import myPlugin from './loaders/my-api';
67
+
68
+ const allPlugins: AsyncLoaderPlugin[] = [
69
+ // ... existing plugins
70
+ myPlugin,
71
+ ];
72
+ ```
73
+
74
+ ## Using Plugins in FlowQuery
75
+
76
+ Once registered, use your plugin in FlowQuery queries:
77
+
78
+ ```sql
79
+ -- Fetch data from your custom source
80
+ LOAD JSON FROM myData('users') AS user
81
+ WHERE user.active = true
82
+ RETURN user.name, user.email
83
+
84
+ -- Combine with filtering and ordering
85
+ LOAD JSON FROM myData('products', 50) AS product
86
+ WHERE product.price > 10
87
+ ORDER BY product.name ASC
88
+ RETURN product.name, product.price
89
+ ```
90
+
91
+ ## Available Built-in Plugins
92
+
93
+ | Plugin | Description | Example |
94
+ |--------|-------------|---------|
95
+ | `fetchJson` | Fetch JSON from any URL | `fetchJson('https://api.example.com/data')` |
96
+
97
+ > **Note**: Additional plugins may be registered. Check `index.ts` for the current list of available plugins.
98
+
99
+ ## Plugin Types
100
+
101
+ ### AsyncLoaderPlugin
102
+
103
+ ```typescript
104
+ interface AsyncLoaderPlugin {
105
+ name: string; // Function name (lowercased when registered)
106
+ provider: AsyncDataProvider; // The async generator/function
107
+ metadata?: PluginMetadata; // Optional metadata for LLM consumption
108
+ }
109
+ ```
110
+
111
+ ### AsyncDataProvider
112
+
113
+ ```typescript
114
+ type AsyncDataProvider = (...args: any[]) => AsyncGenerator<any, void, unknown> | Promise<any>;
115
+ ```
116
+
117
+ Plugins can return either:
118
+ - An `AsyncGenerator` that yields items one at a time
119
+ - A `Promise` that resolves to an array or single value
120
+
121
+ ### PluginMetadata
122
+
123
+ ```typescript
124
+ interface PluginMetadata {
125
+ name: string;
126
+ description: string;
127
+ category?: string;
128
+ parameters?: ParameterSchema[];
129
+ output?: OutputSchema;
130
+ examples?: string[];
131
+ }
132
+ ```
133
+
134
+ ## Tips
135
+
136
+ 1. **Use generators for large datasets** - Yield items one at a time to avoid memory issues
137
+ 2. **Add comprehensive metadata** - This helps LLMs understand and use your functions
138
+ 3. **Handle errors gracefully** - Throw descriptive errors for API failures
139
+ 4. **Include examples** - Show users how to use your plugin in FlowQuery
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Plugin loader - automatically discovers and loads all plugins.
3
+ *
4
+ * To add a new plugin:
5
+ * 1. Create a new file in the `loaders/` directory
6
+ * 2. Add the @AsyncProviderDef decorator to your loader class
7
+ * 3. Import the class in this file (the decorator auto-registers with FlowQuery)
8
+ */
9
+
10
+ import FlowQuery from 'flowquery';
11
+ import { FunctionMetadata } from 'flowquery/extensibility';
12
+
13
+ // Import loader classes - the @AsyncProviderDef decorator auto-registers them with FlowQuery
14
+ import './loaders/FetchJson';
15
+ import './loaders/CatFacts';
16
+ import './loaders/MockData';
17
+ import './loaders/Llm';
18
+
19
+ /**
20
+ * Initialize plugins.
21
+ * Plugins are auto-registered via @AsyncProviderDef decorators when imported.
22
+ * This function just logs the registered plugins for debugging.
23
+ */
24
+ export function initializePlugins(): void {
25
+ const plugins = getLoadedPluginNames();
26
+ console.log(`FlowQuery plugins loaded: ${plugins.join(', ')}`);
27
+ }
28
+
29
+ /**
30
+ * Get the list of loaded plugin names.
31
+ * Uses FlowQuery's introspection to discover registered async providers.
32
+ */
33
+ export function getLoadedPluginNames(): string[] {
34
+ return FlowQuery.listFunctions({ asyncOnly: true }).map(f => f.name);
35
+ }
36
+
37
+ /**
38
+ * Get metadata for all loaded plugins.
39
+ * Uses FlowQuery's functions() introspection as the single source of truth.
40
+ */
41
+ export function getAllPluginMetadata(): FunctionMetadata[] {
42
+ return FlowQuery.listFunctions({ asyncOnly: true });
43
+ }
44
+
45
+ /**
46
+ * Get all available async loader plugins by querying FlowQuery directly.
47
+ * This is the preferred async method that uses functions() introspection.
48
+ *
49
+ * @returns Promise resolving to array of plugin metadata
50
+ */
51
+ export async function getAvailableLoaders(): Promise<FunctionMetadata[]> {
52
+ const runner = new FlowQuery(`
53
+ WITH functions() AS funcs
54
+ UNWIND funcs AS f
55
+ WHERE f.isAsyncProvider = true
56
+ RETURN f
57
+ `);
58
+ await runner.run();
59
+ return runner.results.map((r: any) => r.expr0 as FunctionMetadata);
60
+ }
61
+
62
+ // Re-export types for external use
63
+ export type { FunctionMetadata, FunctionDefOptions, ParameterSchema, OutputSchema, AsyncDataProvider } from 'flowquery/extensibility';
64
+ export { FunctionDef } from 'flowquery/extensibility';
65
+
66
+ // Re-export standalone loader functions for use outside of FlowQuery
67
+ export { llm, llmStream, extractContent } from './loaders/Llm';
68
+ export type { LlmOptions, LlmResponse } from './loaders/Llm';