@d34dman/flowdrop 0.0.4 → 0.0.6
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/README.md +107 -104
- package/dist/components/App.svelte +67 -64
- package/dist/components/App.svelte.d.ts +1 -0
- package/dist/components/Navbar.svelte +1 -11
- package/dist/components/NodeSidebar.svelte +2 -1
- package/dist/components/NodeStatusOverlay.svelte +0 -1
- package/dist/components/NodeStatusOverlay.svelte.d.ts +0 -1
- package/dist/components/PipelineStatus.svelte +0 -37
- package/dist/components/SimpleNode.svelte +0 -7
- package/dist/components/SquareNode.svelte +0 -7
- package/dist/components/UniversalNode.svelte +0 -8
- package/dist/components/WorkflowEditor.svelte +63 -456
- package/dist/components/WorkflowNode.svelte +1 -8
- package/dist/config/apiConfig.d.ts +2 -1
- package/dist/config/apiConfig.js +3 -2
- package/dist/helpers/workflowEditorHelper.d.ts +87 -0
- package/dist/helpers/workflowEditorHelper.js +365 -0
- package/dist/index.d.ts +31 -1
- package/dist/index.js +30 -1
- package/dist/mocks/app-navigation.d.ts +4 -4
- package/dist/mocks/app-navigation.js +4 -4
- package/dist/services/api.js +13 -18
- package/dist/services/globalSave.js +12 -12
- package/dist/svelte-app.js +2 -3
- package/dist/utils/config.d.ts +5 -1
- package/dist/utils/config.js +9 -17
- package/package.json +2 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Editor Helper
|
|
3
|
+
* Contains business logic for workflow operations
|
|
4
|
+
*/
|
|
5
|
+
import type { WorkflowNode as WorkflowNodeType, NodeMetadata, Workflow, WorkflowEdge, NodeExecutionInfo } from '../types/index.js';
|
|
6
|
+
import type { EndpointConfig } from '../config/endpoints.js';
|
|
7
|
+
/**
|
|
8
|
+
* Edge styling configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare class EdgeStylingHelper {
|
|
11
|
+
/**
|
|
12
|
+
* Apply custom styling to connection edges based on rules:
|
|
13
|
+
* - Dashed lines for connections to tool nodes
|
|
14
|
+
* - Arrow markers pointing towards input ports
|
|
15
|
+
*/
|
|
16
|
+
static applyConnectionStyling(edge: WorkflowEdge, sourceNode: WorkflowNodeType, targetNode: WorkflowNodeType): void;
|
|
17
|
+
/**
|
|
18
|
+
* Update existing edges with custom styling rules
|
|
19
|
+
*/
|
|
20
|
+
static updateEdgeStyles(edges: WorkflowEdge[], nodes: WorkflowNodeType[]): WorkflowEdge[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Node operations helper
|
|
24
|
+
*/
|
|
25
|
+
export declare class NodeOperationsHelper {
|
|
26
|
+
/**
|
|
27
|
+
* Load nodes from API
|
|
28
|
+
*/
|
|
29
|
+
static loadNodesFromApi(providedNodes?: NodeMetadata[]): Promise<NodeMetadata[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Load node execution information for all nodes in the workflow
|
|
32
|
+
*/
|
|
33
|
+
static loadNodeExecutionInfo(workflow: Workflow | null, pipelineId?: string): Promise<Record<string, NodeExecutionInfo>>;
|
|
34
|
+
/**
|
|
35
|
+
* Create a new node from dropped data
|
|
36
|
+
*/
|
|
37
|
+
static createNodeFromDrop(nodeTypeData: string, position: {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
}): WorkflowNodeType | null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Workflow operations helper
|
|
44
|
+
*/
|
|
45
|
+
export declare class WorkflowOperationsHelper {
|
|
46
|
+
/**
|
|
47
|
+
* Generate workflow metadata for updates
|
|
48
|
+
*/
|
|
49
|
+
static generateMetadata(existingMetadata?: Workflow['metadata']): Workflow['metadata'];
|
|
50
|
+
/**
|
|
51
|
+
* Update workflow with new nodes/edges and generate new metadata
|
|
52
|
+
*/
|
|
53
|
+
static updateWorkflow(workflow: Workflow, nodes: WorkflowNodeType[], edges: WorkflowEdge[]): Workflow;
|
|
54
|
+
/**
|
|
55
|
+
* Clear workflow (remove all nodes and edges)
|
|
56
|
+
*/
|
|
57
|
+
static clearWorkflow(workflow: Workflow): Workflow;
|
|
58
|
+
/**
|
|
59
|
+
* Update node configuration
|
|
60
|
+
*/
|
|
61
|
+
static updateNodeConfig(workflow: Workflow, nodeId: string, newConfig: Record<string, unknown>): Workflow;
|
|
62
|
+
/**
|
|
63
|
+
* Add a new node to the workflow
|
|
64
|
+
*/
|
|
65
|
+
static addNode(workflow: Workflow, node: WorkflowNodeType): Workflow;
|
|
66
|
+
/**
|
|
67
|
+
* Save workflow to backend
|
|
68
|
+
*/
|
|
69
|
+
static saveWorkflow(workflow: Workflow | null): Promise<Workflow | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Export workflow as JSON file
|
|
72
|
+
*/
|
|
73
|
+
static exportWorkflow(workflow: Workflow | null): void;
|
|
74
|
+
/**
|
|
75
|
+
* Check if workflow has cycles
|
|
76
|
+
*/
|
|
77
|
+
static checkWorkflowCycles(nodes: WorkflowNodeType[], edges: WorkflowEdge[]): boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Configuration helper
|
|
81
|
+
*/
|
|
82
|
+
export declare class ConfigurationHelper {
|
|
83
|
+
/**
|
|
84
|
+
* Configure API endpoints
|
|
85
|
+
*/
|
|
86
|
+
static configureEndpoints(config: EndpointConfig): void;
|
|
87
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Editor Helper
|
|
3
|
+
* Contains business logic for workflow operations
|
|
4
|
+
*/
|
|
5
|
+
import { MarkerType } from '@xyflow/svelte';
|
|
6
|
+
import { hasCycles } from '../utils/connections.js';
|
|
7
|
+
import { workflowApi, nodeApi, setEndpointConfig } from '../services/api.js';
|
|
8
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
9
|
+
import { workflowActions } from '../stores/workflowStore.js';
|
|
10
|
+
import { nodeExecutionService } from '../services/nodeExecutionService.js';
|
|
11
|
+
/**
|
|
12
|
+
* Edge styling configuration
|
|
13
|
+
*/
|
|
14
|
+
export class EdgeStylingHelper {
|
|
15
|
+
/**
|
|
16
|
+
* Apply custom styling to connection edges based on rules:
|
|
17
|
+
* - Dashed lines for connections to tool nodes
|
|
18
|
+
* - Arrow markers pointing towards input ports
|
|
19
|
+
*/
|
|
20
|
+
static applyConnectionStyling(edge, sourceNode, targetNode) {
|
|
21
|
+
// Rule 1: Dashed lines for tool nodes
|
|
22
|
+
// A node is a tool node when it uses the ToolNode component,
|
|
23
|
+
// which happens when sourceNode.type === "tool"
|
|
24
|
+
const isToolNode = sourceNode.type === 'tool';
|
|
25
|
+
// Use inline styles for dashed lines (more reliable than CSS classes)
|
|
26
|
+
if (isToolNode) {
|
|
27
|
+
edge.style = 'stroke-dasharray: 0 4 0; stroke: amber !important;';
|
|
28
|
+
edge.class = 'flowdrop--edge--tool';
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
edge.style = 'stroke: grey;';
|
|
32
|
+
}
|
|
33
|
+
// Store metadata in edge data for debugging
|
|
34
|
+
edge.data = {
|
|
35
|
+
...edge.data,
|
|
36
|
+
isToolConnection: isToolNode,
|
|
37
|
+
targetNodeType: targetNode.type,
|
|
38
|
+
targetCategory: targetNode.data.metadata.category
|
|
39
|
+
};
|
|
40
|
+
// Rule 2: Always add arrow pointing towards input port
|
|
41
|
+
// This replaces the default arrows we removed
|
|
42
|
+
if (!isToolNode) {
|
|
43
|
+
edge.markerEnd = {
|
|
44
|
+
type: MarkerType.ArrowClosed,
|
|
45
|
+
width: 16,
|
|
46
|
+
height: 16,
|
|
47
|
+
color: 'grey'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Update existing edges with custom styling rules
|
|
53
|
+
*/
|
|
54
|
+
static updateEdgeStyles(edges, nodes) {
|
|
55
|
+
return edges.map((edge) => {
|
|
56
|
+
// Find source and target nodes
|
|
57
|
+
const sourceNode = nodes.find((node) => node.id === edge.source);
|
|
58
|
+
const targetNode = nodes.find((node) => node.id === edge.target);
|
|
59
|
+
if (!sourceNode || !targetNode) {
|
|
60
|
+
console.warn('Could not find nodes for edge:', edge.id);
|
|
61
|
+
return edge;
|
|
62
|
+
}
|
|
63
|
+
// Create a copy of the edge and apply styling
|
|
64
|
+
const updatedEdge = { ...edge };
|
|
65
|
+
this.applyConnectionStyling(updatedEdge, sourceNode, targetNode);
|
|
66
|
+
return updatedEdge;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Node operations helper
|
|
72
|
+
*/
|
|
73
|
+
export class NodeOperationsHelper {
|
|
74
|
+
/**
|
|
75
|
+
* Load nodes from API
|
|
76
|
+
*/
|
|
77
|
+
static async loadNodesFromApi(providedNodes) {
|
|
78
|
+
// If nodes are provided via props, use them
|
|
79
|
+
if (providedNodes && providedNodes.length > 0) {
|
|
80
|
+
return providedNodes;
|
|
81
|
+
}
|
|
82
|
+
// Otherwise, load from API
|
|
83
|
+
try {
|
|
84
|
+
const fetchedNodes = await nodeApi.getNodes();
|
|
85
|
+
return fetchedNodes;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error('❌ Failed to load nodes from API:', error);
|
|
89
|
+
// Use fallback sample nodes
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
id: 'text-input',
|
|
93
|
+
name: 'Text Input',
|
|
94
|
+
category: 'inputs',
|
|
95
|
+
description: 'Simple text input field',
|
|
96
|
+
version: '1.0.0',
|
|
97
|
+
icon: 'mdi:text-box',
|
|
98
|
+
inputs: [],
|
|
99
|
+
outputs: [{ id: 'text', name: 'text', type: 'output', dataType: 'string' }]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'text-output',
|
|
103
|
+
name: 'Text Output',
|
|
104
|
+
category: 'outputs',
|
|
105
|
+
description: 'Display text output',
|
|
106
|
+
version: '1.0.0',
|
|
107
|
+
icon: 'mdi:text-box-outline',
|
|
108
|
+
inputs: [{ id: 'text', name: 'text', type: 'input', dataType: 'string' }],
|
|
109
|
+
outputs: []
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Load node execution information for all nodes in the workflow
|
|
116
|
+
*/
|
|
117
|
+
static async loadNodeExecutionInfo(workflow, pipelineId) {
|
|
118
|
+
if (!workflow?.nodes)
|
|
119
|
+
return {};
|
|
120
|
+
// Only load execution info if we have a pipelineId (for pipeline status mode)
|
|
121
|
+
if (!pipelineId)
|
|
122
|
+
return {};
|
|
123
|
+
try {
|
|
124
|
+
const nodeIds = workflow.nodes.map((node) => node.id);
|
|
125
|
+
const executionInfo = await nodeExecutionService.getMultipleNodeExecutionInfo(nodeIds, pipelineId);
|
|
126
|
+
return executionInfo;
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error('Failed to load node execution info:', error);
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a new node from dropped data
|
|
135
|
+
*/
|
|
136
|
+
static createNodeFromDrop(nodeTypeData, position) {
|
|
137
|
+
try {
|
|
138
|
+
const parsedData = JSON.parse(nodeTypeData);
|
|
139
|
+
// Handle both old format (with type: "node") and new format (direct NodeMetadata)
|
|
140
|
+
let nodeType;
|
|
141
|
+
let nodeData;
|
|
142
|
+
if (parsedData.type === 'node') {
|
|
143
|
+
// Old format from sidebar
|
|
144
|
+
nodeType = parsedData.nodeData.metadata;
|
|
145
|
+
nodeData = parsedData.nodeData;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// New format (direct NodeMetadata)
|
|
149
|
+
nodeType = parsedData;
|
|
150
|
+
// Extract initial config from configSchema
|
|
151
|
+
let initialConfig = {};
|
|
152
|
+
if (nodeType.configSchema && typeof nodeType.configSchema === 'object') {
|
|
153
|
+
// If configSchema is a JSON Schema, extract default values
|
|
154
|
+
if (nodeType.configSchema.properties) {
|
|
155
|
+
// JSON Schema format - extract defaults
|
|
156
|
+
Object.entries(nodeType.configSchema.properties).forEach(([key, prop]) => {
|
|
157
|
+
if (prop && typeof prop === 'object' && 'default' in prop) {
|
|
158
|
+
initialConfig[key] = prop.default;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Simple object format - use as is
|
|
164
|
+
initialConfig = { ...nodeType.configSchema };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
nodeData = {
|
|
168
|
+
label: nodeType.name,
|
|
169
|
+
config: initialConfig,
|
|
170
|
+
metadata: nodeType
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const newNodeId = uuidv4();
|
|
174
|
+
// All nodes use "universalNode" type
|
|
175
|
+
// UniversalNode component handles internal switching based on metadata and config
|
|
176
|
+
const newNode = {
|
|
177
|
+
id: newNodeId,
|
|
178
|
+
type: 'universalNode',
|
|
179
|
+
position, // Use the position calculated from the drop event
|
|
180
|
+
deletable: true,
|
|
181
|
+
data: {
|
|
182
|
+
...nodeData,
|
|
183
|
+
nodeId: newNodeId // Use the same ID
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
return newNode;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.error('Error parsing node data:', error);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Workflow operations helper
|
|
196
|
+
*/
|
|
197
|
+
export class WorkflowOperationsHelper {
|
|
198
|
+
/**
|
|
199
|
+
* Generate workflow metadata for updates
|
|
200
|
+
*/
|
|
201
|
+
static generateMetadata(existingMetadata) {
|
|
202
|
+
return {
|
|
203
|
+
...existingMetadata,
|
|
204
|
+
updatedAt: new Date().toISOString(),
|
|
205
|
+
versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
206
|
+
updateNumber: (existingMetadata?.updateNumber || 0) + 1
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Update workflow with new nodes/edges and generate new metadata
|
|
211
|
+
*/
|
|
212
|
+
static updateWorkflow(workflow, nodes, edges) {
|
|
213
|
+
return {
|
|
214
|
+
...workflow,
|
|
215
|
+
nodes,
|
|
216
|
+
edges,
|
|
217
|
+
metadata: this.generateMetadata(workflow.metadata)
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Clear workflow (remove all nodes and edges)
|
|
222
|
+
*/
|
|
223
|
+
static clearWorkflow(workflow) {
|
|
224
|
+
return {
|
|
225
|
+
...workflow,
|
|
226
|
+
nodes: [],
|
|
227
|
+
edges: [],
|
|
228
|
+
metadata: this.generateMetadata(workflow.metadata)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Update node configuration
|
|
233
|
+
*/
|
|
234
|
+
static updateNodeConfig(workflow, nodeId, newConfig) {
|
|
235
|
+
return {
|
|
236
|
+
...workflow,
|
|
237
|
+
nodes: workflow.nodes.map((node) => node.id === nodeId
|
|
238
|
+
? {
|
|
239
|
+
...node,
|
|
240
|
+
data: { ...node.data, config: { ...newConfig } }
|
|
241
|
+
}
|
|
242
|
+
: node),
|
|
243
|
+
metadata: this.generateMetadata(workflow.metadata)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Add a new node to the workflow
|
|
248
|
+
*/
|
|
249
|
+
static addNode(workflow, node) {
|
|
250
|
+
return {
|
|
251
|
+
...workflow,
|
|
252
|
+
nodes: [...workflow.nodes, node],
|
|
253
|
+
metadata: this.generateMetadata(workflow.metadata)
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Save workflow to backend
|
|
258
|
+
*/
|
|
259
|
+
static async saveWorkflow(workflow) {
|
|
260
|
+
if (!workflow) {
|
|
261
|
+
console.warn('⚠️ No workflow data available to save');
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
// Determine the workflow ID based on whether we have an existing workflow
|
|
266
|
+
let workflowId;
|
|
267
|
+
if (workflow.id) {
|
|
268
|
+
// Use the existing workflow ID
|
|
269
|
+
workflowId = workflow.id;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
// Generate a new UUID for a new workflow
|
|
273
|
+
workflowId = uuidv4();
|
|
274
|
+
}
|
|
275
|
+
const workflowToSave = {
|
|
276
|
+
id: workflowId,
|
|
277
|
+
name: workflow.name || 'Untitled Workflow',
|
|
278
|
+
nodes: workflow.nodes || [],
|
|
279
|
+
edges: workflow.edges || [],
|
|
280
|
+
metadata: {
|
|
281
|
+
version: '1.0.0',
|
|
282
|
+
createdAt: workflow.metadata?.createdAt || new Date().toISOString(),
|
|
283
|
+
updatedAt: new Date().toISOString()
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
console.log('💾 WorkflowEditor: Saving workflow to backend:');
|
|
287
|
+
console.log(' - ID:', workflowToSave.id);
|
|
288
|
+
console.log(' - Name:', workflowToSave.name);
|
|
289
|
+
console.log(' - Nodes count:', workflowToSave.nodes.length);
|
|
290
|
+
console.log(' - Edges count:', workflowToSave.edges.length);
|
|
291
|
+
console.log(' - Full workflow object:', JSON.stringify(workflowToSave, null, 2));
|
|
292
|
+
const savedWorkflow = await workflowApi.saveWorkflow(workflowToSave);
|
|
293
|
+
console.log('✅ WorkflowEditor: Received workflow from backend:');
|
|
294
|
+
console.log(' - ID:', savedWorkflow.id);
|
|
295
|
+
console.log(' - Name:', savedWorkflow.name);
|
|
296
|
+
console.log(' - Nodes count:', savedWorkflow.nodes?.length || 0);
|
|
297
|
+
console.log(' - Edges count:', savedWorkflow.edges?.length || 0);
|
|
298
|
+
// Update the workflow ID if it changed (new workflow)
|
|
299
|
+
if (savedWorkflow.id && savedWorkflow.id !== workflowToSave.id) {
|
|
300
|
+
console.log('🔄 Updating workflow ID from', workflowToSave.id, 'to', savedWorkflow.id);
|
|
301
|
+
workflowActions.batchUpdate({
|
|
302
|
+
nodes: workflowToSave.nodes,
|
|
303
|
+
edges: workflowToSave.edges,
|
|
304
|
+
name: workflowToSave.name,
|
|
305
|
+
metadata: {
|
|
306
|
+
...workflowToSave.metadata,
|
|
307
|
+
...savedWorkflow.metadata
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
return savedWorkflow;
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
console.error('❌ Failed to save workflow:', error);
|
|
315
|
+
throw error;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Export workflow as JSON file
|
|
320
|
+
*/
|
|
321
|
+
static exportWorkflow(workflow) {
|
|
322
|
+
if (!workflow) {
|
|
323
|
+
console.warn('⚠️ No workflow data available to export');
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
// Use the same ID logic as saveWorkflow
|
|
327
|
+
const workflowId = workflow.id || uuidv4();
|
|
328
|
+
const workflowToExport = {
|
|
329
|
+
id: workflowId,
|
|
330
|
+
name: workflow.name || 'Untitled Workflow',
|
|
331
|
+
nodes: workflow.nodes || [],
|
|
332
|
+
edges: workflow.edges || [],
|
|
333
|
+
metadata: {
|
|
334
|
+
version: '1.0.0',
|
|
335
|
+
createdAt: workflow.metadata?.createdAt || new Date().toISOString(),
|
|
336
|
+
updatedAt: new Date().toISOString()
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
const dataStr = JSON.stringify(workflowToExport, null, 2);
|
|
340
|
+
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
|
341
|
+
const url = URL.createObjectURL(dataBlob);
|
|
342
|
+
const link = document.createElement('a');
|
|
343
|
+
link.href = url;
|
|
344
|
+
link.download = `${workflowToExport.name}.json`;
|
|
345
|
+
link.click();
|
|
346
|
+
URL.revokeObjectURL(url);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Check if workflow has cycles
|
|
350
|
+
*/
|
|
351
|
+
static checkWorkflowCycles(nodes, edges) {
|
|
352
|
+
return hasCycles(nodes, edges);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Configuration helper
|
|
357
|
+
*/
|
|
358
|
+
export class ConfigurationHelper {
|
|
359
|
+
/**
|
|
360
|
+
* Configure API endpoints
|
|
361
|
+
*/
|
|
362
|
+
static configureEndpoints(config) {
|
|
363
|
+
setEndpointConfig(config);
|
|
364
|
+
}
|
|
365
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* FlowDrop - Visual Workflow Editor Library
|
|
3
3
|
* A Svelte 5 component library built on @xyflow/svelte for creating node-based workflow editors
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import './styles/base.css';
|
|
6
6
|
export type { NodeCategory, NodeDataType, NodePort, NodeMetadata, NodeConfig, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents } from './types/index.js';
|
|
7
7
|
export type { WorkflowEditorConfig, EditorFeatures, UIConfig, APIConfig, ExecutionConfig, StorageConfig, NodeType, WorkflowData, ExecutionResult as EditorExecutionResult, EditorState } from './types/config.js';
|
|
8
8
|
export { FlowDropApiClient } from './api/client.js';
|
|
@@ -14,14 +14,44 @@ export { default as SimpleNodeComponent } from './components/SimpleNode.svelte';
|
|
|
14
14
|
export { default as ToolNodeComponent } from './components/ToolNode.svelte';
|
|
15
15
|
export { default as NotesNodeComponent } from './components/NotesNode.svelte';
|
|
16
16
|
export { default as CanvasBanner } from './components/CanvasBanner.svelte';
|
|
17
|
+
export { default as UniversalNode } from './components/UniversalNode.svelte';
|
|
18
|
+
export { default as GatewayNode } from './components/GatewayNode.svelte';
|
|
19
|
+
export { default as SquareNode } from './components/SquareNode.svelte';
|
|
20
|
+
export { default as LoadingSpinner } from './components/LoadingSpinner.svelte';
|
|
21
|
+
export { default as StatusIcon } from './components/StatusIcon.svelte';
|
|
22
|
+
export { default as StatusLabel } from './components/StatusLabel.svelte';
|
|
23
|
+
export { default as NodeStatusOverlay } from './components/NodeStatusOverlay.svelte';
|
|
24
|
+
export { default as MarkdownDisplay } from './components/MarkdownDisplay.svelte';
|
|
25
|
+
export { default as ConfigForm } from './components/ConfigForm.svelte';
|
|
26
|
+
export { default as ConfigModal } from './components/ConfigModal.svelte';
|
|
27
|
+
export { default as ConfigSidebar } from './components/ConfigSidebar.svelte';
|
|
28
|
+
export { default as ConnectionLine } from './components/ConnectionLine.svelte';
|
|
29
|
+
export { default as LogsSidebar } from './components/LogsSidebar.svelte';
|
|
30
|
+
export { default as PipelineStatus } from './components/PipelineStatus.svelte';
|
|
31
|
+
export { default as Navbar } from './components/Navbar.svelte';
|
|
32
|
+
export { default as Logo } from './components/Logo.svelte';
|
|
17
33
|
export { sampleNodes, sampleWorkflow } from './data/samples.js';
|
|
18
34
|
export * from './utils/icons.js';
|
|
19
35
|
export * from './utils/colors.js';
|
|
20
36
|
export * from './utils/connections.js';
|
|
21
37
|
export * from './utils/config.js';
|
|
22
38
|
export * from './utils/nodeTypes.js';
|
|
39
|
+
export { getStatusColor, getStatusIcon, getStatusLabel, getStatusBackgroundColor, getStatusTextColor, createDefaultExecutionInfo, updateExecutionStart, updateExecutionComplete, updateExecutionFailed, resetExecutionInfo, formatExecutionDuration, formatLastExecuted } from './utils/nodeStatus.js';
|
|
40
|
+
export { createNodeWrapperConfig, shouldShowNodeStatus, getOptimalStatusPosition, getOptimalStatusSize, DEFAULT_NODE_STATUS_CONFIG } from './utils/nodeWrapper.js';
|
|
41
|
+
export type { NodeStatusConfig } from './utils/nodeWrapper.js';
|
|
23
42
|
export * from './services/api.js';
|
|
43
|
+
export { showSuccess, showError, showWarning, showInfo, showLoading, dismissToast, dismissAllToasts, showPromise, showConfirmation, apiToasts, workflowToasts, pipelineToasts } from './services/toastService.js';
|
|
44
|
+
export type { ToastType, ToastOptions } from './services/toastService.js';
|
|
45
|
+
export { NodeExecutionService, nodeExecutionService } from './services/nodeExecutionService.js';
|
|
46
|
+
export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
|
|
47
|
+
export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
|
|
48
|
+
export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
|
|
49
|
+
export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from './helpers/workflowEditorHelper.js';
|
|
50
|
+
export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged } from './stores/workflowStore.js';
|
|
24
51
|
export * from './config/endpoints.js';
|
|
52
|
+
export { defaultApiConfig, getEndpointUrl } from './config/apiConfig.js';
|
|
53
|
+
export type { ApiConfig } from './config/apiConfig.js';
|
|
54
|
+
export { DEFAULT_PORT_CONFIG } from './config/defaultPortConfig.js';
|
|
25
55
|
export * from './adapters/WorkflowAdapter.js';
|
|
26
56
|
export * from './clients/ApiClient.js';
|
|
27
57
|
export { mountWorkflowEditor, unmountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from './svelte-app.js';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* A Svelte 5 component library built on @xyflow/svelte for creating node-based workflow editors
|
|
4
4
|
*/
|
|
5
5
|
// Import CSS to ensure styles are included in the library build
|
|
6
|
-
import
|
|
6
|
+
import './styles/base.css';
|
|
7
7
|
// Export API clients
|
|
8
8
|
export { FlowDropApiClient } from './api/client.js';
|
|
9
9
|
export { EnhancedFlowDropApiClient } from './api/enhanced-client.js';
|
|
@@ -15,6 +15,22 @@ export { default as SimpleNodeComponent } from './components/SimpleNode.svelte';
|
|
|
15
15
|
export { default as ToolNodeComponent } from './components/ToolNode.svelte';
|
|
16
16
|
export { default as NotesNodeComponent } from './components/NotesNode.svelte';
|
|
17
17
|
export { default as CanvasBanner } from './components/CanvasBanner.svelte';
|
|
18
|
+
export { default as UniversalNode } from './components/UniversalNode.svelte';
|
|
19
|
+
export { default as GatewayNode } from './components/GatewayNode.svelte';
|
|
20
|
+
export { default as SquareNode } from './components/SquareNode.svelte';
|
|
21
|
+
export { default as LoadingSpinner } from './components/LoadingSpinner.svelte';
|
|
22
|
+
export { default as StatusIcon } from './components/StatusIcon.svelte';
|
|
23
|
+
export { default as StatusLabel } from './components/StatusLabel.svelte';
|
|
24
|
+
export { default as NodeStatusOverlay } from './components/NodeStatusOverlay.svelte';
|
|
25
|
+
export { default as MarkdownDisplay } from './components/MarkdownDisplay.svelte';
|
|
26
|
+
export { default as ConfigForm } from './components/ConfigForm.svelte';
|
|
27
|
+
export { default as ConfigModal } from './components/ConfigModal.svelte';
|
|
28
|
+
export { default as ConfigSidebar } from './components/ConfigSidebar.svelte';
|
|
29
|
+
export { default as ConnectionLine } from './components/ConnectionLine.svelte';
|
|
30
|
+
export { default as LogsSidebar } from './components/LogsSidebar.svelte';
|
|
31
|
+
export { default as PipelineStatus } from './components/PipelineStatus.svelte';
|
|
32
|
+
export { default as Navbar } from './components/Navbar.svelte';
|
|
33
|
+
export { default as Logo } from './components/Logo.svelte';
|
|
18
34
|
// Export sample data for development
|
|
19
35
|
export { sampleNodes, sampleWorkflow } from './data/samples.js';
|
|
20
36
|
// Export utilities
|
|
@@ -23,10 +39,23 @@ export * from './utils/colors.js';
|
|
|
23
39
|
export * from './utils/connections.js';
|
|
24
40
|
export * from './utils/config.js';
|
|
25
41
|
export * from './utils/nodeTypes.js';
|
|
42
|
+
export { getStatusColor, getStatusIcon, getStatusLabel, getStatusBackgroundColor, getStatusTextColor, createDefaultExecutionInfo, updateExecutionStart, updateExecutionComplete, updateExecutionFailed, resetExecutionInfo, formatExecutionDuration, formatLastExecuted } from './utils/nodeStatus.js';
|
|
43
|
+
export { createNodeWrapperConfig, shouldShowNodeStatus, getOptimalStatusPosition, getOptimalStatusSize, DEFAULT_NODE_STATUS_CONFIG } from './utils/nodeWrapper.js';
|
|
26
44
|
// Export services
|
|
27
45
|
export * from './services/api.js';
|
|
46
|
+
export { showSuccess, showError, showWarning, showInfo, showLoading, dismissToast, dismissAllToasts, showPromise, showConfirmation, apiToasts, workflowToasts, pipelineToasts } from './services/toastService.js';
|
|
47
|
+
export { NodeExecutionService, nodeExecutionService } from './services/nodeExecutionService.js';
|
|
48
|
+
export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
|
|
49
|
+
export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
|
|
50
|
+
export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
|
|
51
|
+
// Export helpers
|
|
52
|
+
export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from './helpers/workflowEditorHelper.js';
|
|
53
|
+
// Export stores
|
|
54
|
+
export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged } from './stores/workflowStore.js';
|
|
28
55
|
// Export endpoint configuration
|
|
29
56
|
export * from './config/endpoints.js';
|
|
57
|
+
export { defaultApiConfig, getEndpointUrl } from './config/apiConfig.js';
|
|
58
|
+
export { DEFAULT_PORT_CONFIG } from './config/defaultPortConfig.js';
|
|
30
59
|
// Export adapters
|
|
31
60
|
export * from './adapters/WorkflowAdapter.js';
|
|
32
61
|
// Export API client
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export function goto(
|
|
2
|
-
export function invalidate(
|
|
1
|
+
export function goto(): Promise<void>;
|
|
2
|
+
export function invalidate(): Promise<void>;
|
|
3
3
|
export function invalidateAll(): Promise<void>;
|
|
4
|
-
export function preloadData(
|
|
5
|
-
export function preloadCode(
|
|
4
|
+
export function preloadData(): Promise<{}>;
|
|
5
|
+
export function preloadCode(): Promise<void>;
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Mock goto function
|
|
7
|
-
export const goto = async (
|
|
7
|
+
export const goto = async () => {
|
|
8
8
|
// No-op for library context
|
|
9
9
|
console.warn('Navigation not available in library context');
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// Mock invalidate function
|
|
13
|
-
export const invalidate = async (
|
|
13
|
+
export const invalidate = async () => {
|
|
14
14
|
// No-op for library context
|
|
15
15
|
return Promise.resolve();
|
|
16
16
|
};
|
|
@@ -22,13 +22,13 @@ export const invalidateAll = async () => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// Mock preloadData function
|
|
25
|
-
export const preloadData = async (
|
|
25
|
+
export const preloadData = async () => {
|
|
26
26
|
// No-op for library context
|
|
27
27
|
return Promise.resolve({});
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// Mock preloadCode function
|
|
31
|
-
export const preloadCode = async (
|
|
31
|
+
export const preloadCode = async () => {
|
|
32
32
|
// No-op for library context
|
|
33
33
|
return Promise.resolve();
|
|
34
34
|
};
|
package/dist/services/api.js
CHANGED
|
@@ -28,7 +28,7 @@ export function setApiBaseUrl(url) {
|
|
|
28
28
|
.then(({ createEndpointConfig }) => {
|
|
29
29
|
endpointConfig = createEndpointConfig(url);
|
|
30
30
|
})
|
|
31
|
-
.catch((
|
|
31
|
+
.catch(() => {
|
|
32
32
|
// Failed to load endpoint config
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -43,24 +43,19 @@ async function apiRequest(endpointKey, endpointPath, params, options = {}) {
|
|
|
43
43
|
if (!endpointConfig) {
|
|
44
44
|
throw new Error('Endpoint configuration not set. Call setEndpointConfig() first.');
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
58
|
-
}
|
|
59
|
-
return data;
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
throw error;
|
|
46
|
+
const url = buildEndpointUrl(endpointConfig, endpointPath, params);
|
|
47
|
+
const method = getEndpointMethod(endpointConfig, endpointKey);
|
|
48
|
+
const headers = getEndpointHeaders(endpointConfig, endpointKey);
|
|
49
|
+
const response = await fetch(url, {
|
|
50
|
+
method,
|
|
51
|
+
headers,
|
|
52
|
+
...options
|
|
53
|
+
});
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
63
57
|
}
|
|
58
|
+
return data;
|
|
64
59
|
}
|
|
65
60
|
/**
|
|
66
61
|
* Node API methods
|