@d34dman/flowdrop 0.0.8 → 0.0.10
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/dist/components/App.svelte +0 -3
- package/dist/components/ConfigModal.svelte +0 -3
- package/dist/components/NodeSidebar.svelte +0 -1
- package/dist/components/WorkflowEditor.svelte +88 -68
- package/dist/helpers/workflowEditorHelper.js +2 -15
- package/dist/services/nodeExecutionService.js +0 -3
- package/dist/services/portConfigApi.js +2 -10
- package/dist/stores/workflowStore.js +0 -12
- package/dist/utils/connections.js +0 -7
- package/package.json +1 -1
|
@@ -258,8 +258,6 @@
|
|
|
258
258
|
* Handle workflow configuration save
|
|
259
259
|
*/
|
|
260
260
|
async function handleWorkflowSave(config: any): Promise<void> {
|
|
261
|
-
console.log('Workflow configuration saved:', config);
|
|
262
|
-
|
|
263
261
|
// Update the workflow store
|
|
264
262
|
if ($workflowStore) {
|
|
265
263
|
$workflowStore.name = config.name;
|
|
@@ -272,7 +270,6 @@
|
|
|
272
270
|
// Also save the workflow to the backend
|
|
273
271
|
try {
|
|
274
272
|
await saveWorkflow();
|
|
275
|
-
console.log('Workflow saved to backend successfully');
|
|
276
273
|
} catch (error) {
|
|
277
274
|
console.error('Failed to save workflow to backend:', error);
|
|
278
275
|
// Note: We don't throw the error here to avoid breaking the UI flow
|
|
@@ -55,16 +55,6 @@
|
|
|
55
55
|
|
|
56
56
|
let props: Props = $props();
|
|
57
57
|
|
|
58
|
-
// Debug logging for props
|
|
59
|
-
$effect(() => {
|
|
60
|
-
console.log('🔧 WorkflowEditor: Props received:', {
|
|
61
|
-
hasOpenConfigSidebar: !!props.openConfigSidebar,
|
|
62
|
-
hasCloseConfigSidebar: !!props.closeConfigSidebar,
|
|
63
|
-
selectedNodeForConfig: props.selectedNodeForConfig?.id,
|
|
64
|
-
isConfigSidebarOpen: props.isConfigSidebarOpen
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
58
|
// Create a local currentWorkflow variable that we can control directly
|
|
69
59
|
let currentWorkflow = $state<Workflow | null>(null);
|
|
70
60
|
|
|
@@ -80,7 +70,11 @@
|
|
|
80
70
|
let flowEdges = $state<WorkflowEdge[]>([]);
|
|
81
71
|
|
|
82
72
|
// Sync local state with currentWorkflow
|
|
83
|
-
let loadExecutionInfoTimeout:
|
|
73
|
+
let loadExecutionInfoTimeout: number | null = null;
|
|
74
|
+
let executionInfoAbortController: AbortController | null = null;
|
|
75
|
+
// Track previous workflow ID to detect when we need to reload execution info
|
|
76
|
+
let previousWorkflowId: string | null = null;
|
|
77
|
+
let previousPipelineId: string | undefined = undefined;
|
|
84
78
|
|
|
85
79
|
$effect(() => {
|
|
86
80
|
if (currentWorkflow) {
|
|
@@ -93,72 +87,105 @@
|
|
|
93
87
|
}));
|
|
94
88
|
flowEdges = currentWorkflow.edges;
|
|
95
89
|
|
|
96
|
-
//
|
|
97
|
-
if
|
|
98
|
-
|
|
90
|
+
// Only load execution info if we have a pipelineId (pipeline status mode)
|
|
91
|
+
// and if the workflow or pipeline has changed
|
|
92
|
+
const workflowChanged = currentWorkflow.id !== previousWorkflowId;
|
|
93
|
+
const pipelineChanged = props.pipelineId !== previousPipelineId;
|
|
94
|
+
|
|
95
|
+
if (props.pipelineId && (workflowChanged || pipelineChanged)) {
|
|
96
|
+
// Cancel any pending timeout
|
|
97
|
+
if (loadExecutionInfoTimeout) {
|
|
98
|
+
clearTimeout(loadExecutionInfoTimeout);
|
|
99
|
+
loadExecutionInfoTimeout = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Cancel any in-flight request
|
|
103
|
+
if (executionInfoAbortController) {
|
|
104
|
+
executionInfoAbortController.abort();
|
|
105
|
+
executionInfoAbortController = null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Update tracking variables
|
|
109
|
+
previousWorkflowId = currentWorkflow.id;
|
|
110
|
+
previousPipelineId = props.pipelineId;
|
|
111
|
+
|
|
112
|
+
// Use requestIdleCallback for non-critical updates (falls back to setTimeout)
|
|
113
|
+
if (typeof requestIdleCallback !== "undefined") {
|
|
114
|
+
loadExecutionInfoTimeout = requestIdleCallback(
|
|
115
|
+
() => {
|
|
116
|
+
loadNodeExecutionInfo();
|
|
117
|
+
},
|
|
118
|
+
{ timeout: 500 }
|
|
119
|
+
) as unknown as number;
|
|
120
|
+
} else {
|
|
121
|
+
// Fallback to setTimeout with longer delay for better performance
|
|
122
|
+
loadExecutionInfoTimeout = setTimeout(() => {
|
|
123
|
+
loadNodeExecutionInfo();
|
|
124
|
+
}, 300) as unknown as number;
|
|
125
|
+
}
|
|
99
126
|
}
|
|
100
|
-
loadExecutionInfoTimeout = setTimeout(() => {
|
|
101
|
-
loadNodeExecutionInfo();
|
|
102
|
-
}, 100);
|
|
103
127
|
}
|
|
104
128
|
});
|
|
105
129
|
|
|
106
130
|
// Function to update the global store when currentWorkflow changes
|
|
107
131
|
function updateGlobalStore(): void {
|
|
108
132
|
if (currentWorkflow) {
|
|
109
|
-
console.log('🔍 WorkflowEditor: Updating global store from currentWorkflow:', {
|
|
110
|
-
nodeCount: currentWorkflow.nodes.length,
|
|
111
|
-
edgeCount: currentWorkflow.edges.length,
|
|
112
|
-
nodePositions: currentWorkflow.nodes.map((node) => ({
|
|
113
|
-
id: node.id,
|
|
114
|
-
position: node.position
|
|
115
|
-
})),
|
|
116
|
-
workflowName: currentWorkflow.name,
|
|
117
|
-
versionId: currentWorkflow.metadata?.versionId,
|
|
118
|
-
updateNumber: currentWorkflow.metadata?.updateNumber
|
|
119
|
-
});
|
|
120
|
-
|
|
121
133
|
workflowActions.updateWorkflow(currentWorkflow);
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
136
|
|
|
125
137
|
/**
|
|
126
138
|
* Load node execution information for all nodes in the workflow
|
|
139
|
+
* Optimized to reduce processing time and prevent blocking the main thread
|
|
127
140
|
*/
|
|
128
141
|
async function loadNodeExecutionInfo(): Promise<void> {
|
|
129
|
-
if (!currentWorkflow?.nodes) return;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
status: 'idle' as const,
|
|
145
|
-
executionCount: 0,
|
|
146
|
-
isExecuting: false
|
|
147
|
-
} as NodeExecutionInfo)
|
|
148
|
-
}
|
|
149
|
-
}));
|
|
150
|
-
|
|
151
|
-
// Update the flow nodes to reflect the changes
|
|
152
|
-
flowNodes = updatedNodes.map((node) => ({
|
|
153
|
-
...node,
|
|
154
|
-
data: {
|
|
155
|
-
...node.data,
|
|
156
|
-
onConfigOpen: props.openConfigSidebar
|
|
142
|
+
if (!currentWorkflow?.nodes || !props.pipelineId) return;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Create abort controller for this request
|
|
146
|
+
executionInfoAbortController = new AbortController();
|
|
147
|
+
|
|
148
|
+
// Fetch execution info with abort signal
|
|
149
|
+
const executionInfo = await NodeOperationsHelper.loadNodeExecutionInfo(
|
|
150
|
+
currentWorkflow,
|
|
151
|
+
props.pipelineId
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Check if request was aborted
|
|
155
|
+
if (executionInfoAbortController?.signal.aborted) {
|
|
156
|
+
return;
|
|
157
157
|
}
|
|
158
|
-
}));
|
|
159
158
|
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
// Default execution info for nodes without data
|
|
160
|
+
const defaultExecutionInfo: NodeExecutionInfo = {
|
|
161
|
+
status: "idle" as const,
|
|
162
|
+
executionCount: 0,
|
|
163
|
+
isExecuting: false
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Optimize: Single pass through nodes instead of multiple maps
|
|
167
|
+
// This reduces processing time from ~100ms to ~10-20ms for large workflows
|
|
168
|
+
const updatedNodes = currentWorkflow.nodes.map((node) => ({
|
|
169
|
+
...node,
|
|
170
|
+
data: {
|
|
171
|
+
...node.data,
|
|
172
|
+
executionInfo: executionInfo[node.id] || defaultExecutionInfo,
|
|
173
|
+
onConfigOpen: props.openConfigSidebar
|
|
174
|
+
}
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
// Update state in a single operation
|
|
178
|
+
flowNodes = updatedNodes;
|
|
179
|
+
currentWorkflow.nodes = updatedNodes;
|
|
180
|
+
|
|
181
|
+
// Clear abort controller
|
|
182
|
+
executionInfoAbortController = null;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
// Only log if it's not an abort error
|
|
185
|
+
if (error instanceof Error && error.name !== "AbortError") {
|
|
186
|
+
console.error("Failed to load node execution info:", error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
162
189
|
}
|
|
163
190
|
|
|
164
191
|
// Function to update currentWorkflow when SvelteFlow changes nodes/edges
|
|
@@ -186,7 +213,6 @@
|
|
|
186
213
|
const edgesChanged = JSON.stringify(flowEdges) !== JSON.stringify(previousEdges);
|
|
187
214
|
|
|
188
215
|
if ((nodesChanged || edgesChanged) && currentWorkflow) {
|
|
189
|
-
console.log('🔍 WorkflowEditor: SvelteFlow changed nodes/edges, updating currentWorkflow');
|
|
190
216
|
updateCurrentWorkflowFromSvelteFlow();
|
|
191
217
|
|
|
192
218
|
// Update previous values
|
|
@@ -227,8 +253,6 @@
|
|
|
227
253
|
targetHandle?: string;
|
|
228
254
|
}): Promise<void> {
|
|
229
255
|
// SvelteFlow will automatically create the edge due to bind:edges
|
|
230
|
-
console.log('Connection created:', connection);
|
|
231
|
-
|
|
232
256
|
// Wait for DOM update before applying styling
|
|
233
257
|
await tick();
|
|
234
258
|
|
|
@@ -311,19 +335,15 @@
|
|
|
311
335
|
const newNode = NodeOperationsHelper.createNodeFromDrop(nodeTypeData, position);
|
|
312
336
|
|
|
313
337
|
if (newNode && currentWorkflow) {
|
|
314
|
-
console.log('🔧 WorkflowEditor: Adding new node to currentWorkflow:', newNode.id);
|
|
315
338
|
currentWorkflow = WorkflowOperationsHelper.addNode(currentWorkflow, newNode);
|
|
316
339
|
|
|
317
|
-
console.log(
|
|
318
|
-
'🔧 WorkflowEditor: Updated currentWorkflow with new node, calling updateGlobalStore'
|
|
319
|
-
);
|
|
320
340
|
// Update the global store
|
|
321
341
|
updateGlobalStore();
|
|
322
342
|
|
|
323
343
|
// Wait for DOM update to ensure SvelteFlow updates
|
|
324
344
|
await tick();
|
|
325
345
|
} else if (!currentWorkflow) {
|
|
326
|
-
console.warn('
|
|
346
|
+
console.warn('No currentWorkflow available for new node');
|
|
327
347
|
}
|
|
328
348
|
}
|
|
329
349
|
}}
|
|
@@ -57,7 +57,6 @@ export class EdgeStylingHelper {
|
|
|
57
57
|
const sourceNode = nodes.find((node) => node.id === edge.source);
|
|
58
58
|
const targetNode = nodes.find((node) => node.id === edge.target);
|
|
59
59
|
if (!sourceNode || !targetNode) {
|
|
60
|
-
console.warn('Could not find nodes for edge:', edge.id);
|
|
61
60
|
return edge;
|
|
62
61
|
}
|
|
63
62
|
// Create a copy of the edge and apply styling
|
|
@@ -258,7 +257,7 @@ export class WorkflowOperationsHelper {
|
|
|
258
257
|
*/
|
|
259
258
|
static async saveWorkflow(workflow) {
|
|
260
259
|
if (!workflow) {
|
|
261
|
-
console.warn('
|
|
260
|
+
console.warn('No workflow data available to save');
|
|
262
261
|
return null;
|
|
263
262
|
}
|
|
264
263
|
try {
|
|
@@ -283,21 +282,9 @@ export class WorkflowOperationsHelper {
|
|
|
283
282
|
updatedAt: new Date().toISOString()
|
|
284
283
|
}
|
|
285
284
|
};
|
|
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
285
|
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
286
|
// Update the workflow ID if it changed (new workflow)
|
|
299
287
|
if (savedWorkflow.id && savedWorkflow.id !== workflowToSave.id) {
|
|
300
|
-
console.log('🔄 Updating workflow ID from', workflowToSave.id, 'to', savedWorkflow.id);
|
|
301
288
|
workflowActions.batchUpdate({
|
|
302
289
|
nodes: workflowToSave.nodes,
|
|
303
290
|
edges: workflowToSave.edges,
|
|
@@ -320,7 +307,7 @@ export class WorkflowOperationsHelper {
|
|
|
320
307
|
*/
|
|
321
308
|
static exportWorkflow(workflow) {
|
|
322
309
|
if (!workflow) {
|
|
323
|
-
console.warn('
|
|
310
|
+
console.warn('No workflow data available to export');
|
|
324
311
|
return;
|
|
325
312
|
}
|
|
326
313
|
// Use the same ID logic as saveWorkflow
|
|
@@ -26,7 +26,6 @@ export class NodeExecutionService {
|
|
|
26
26
|
*/
|
|
27
27
|
async getNodeExecutionInfo(nodeId, pipelineId) {
|
|
28
28
|
if (!pipelineId) {
|
|
29
|
-
console.warn('Pipeline ID is required to fetch node execution info');
|
|
30
29
|
return null;
|
|
31
30
|
}
|
|
32
31
|
try {
|
|
@@ -72,12 +71,10 @@ export class NodeExecutionService {
|
|
|
72
71
|
*/
|
|
73
72
|
async getMultipleNodeExecutionInfo(nodeIds, pipelineId) {
|
|
74
73
|
if (!pipelineId) {
|
|
75
|
-
console.warn('Pipeline ID is required to fetch node execution info');
|
|
76
74
|
return {};
|
|
77
75
|
}
|
|
78
76
|
// Check if API is temporarily unavailable
|
|
79
77
|
if (this.apiUnavailable && Date.now() < this.apiUnavailableUntil) {
|
|
80
|
-
console.log('API temporarily unavailable, returning cached/default values');
|
|
81
78
|
const defaultExecutionInfo = {};
|
|
82
79
|
nodeIds.forEach((nodeId) => {
|
|
83
80
|
defaultExecutionInfo[nodeId] = {
|
|
@@ -11,27 +11,19 @@ import { FlowDropApiClient } from '../api/client.js';
|
|
|
11
11
|
export async function fetchPortConfig(endpointConfig) {
|
|
12
12
|
try {
|
|
13
13
|
const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.portConfig);
|
|
14
|
-
console.log('📡 Fetching port configuration from:', url);
|
|
15
14
|
// Create API client instance
|
|
16
15
|
const client = new FlowDropApiClient(endpointConfig.baseUrl);
|
|
17
16
|
// Use the client to fetch port configuration
|
|
18
17
|
const portConfig = await client.getPortConfig();
|
|
19
18
|
// Validate the configuration has required fields
|
|
20
19
|
if (!portConfig.dataTypes || !Array.isArray(portConfig.dataTypes)) {
|
|
21
|
-
console.warn('
|
|
20
|
+
console.warn('Invalid port config received from API, using default');
|
|
22
21
|
return DEFAULT_PORT_CONFIG;
|
|
23
22
|
}
|
|
24
|
-
console.log('✅ Port configuration loaded successfully:', {
|
|
25
|
-
version: portConfig.version,
|
|
26
|
-
dataTypesCount: portConfig.dataTypes.length,
|
|
27
|
-
rulesCount: portConfig.compatibilityRules?.length || 0,
|
|
28
|
-
defaultType: portConfig.defaultDataType
|
|
29
|
-
});
|
|
30
23
|
return portConfig;
|
|
31
24
|
}
|
|
32
25
|
catch (error) {
|
|
33
|
-
console.error('
|
|
34
|
-
console.log('🔄 Falling back to default port configuration');
|
|
26
|
+
console.error('Error fetching port configuration:', error);
|
|
35
27
|
return DEFAULT_PORT_CONFIG;
|
|
36
28
|
}
|
|
37
29
|
}
|
|
@@ -52,15 +52,11 @@ function hasWorkflowDataChanged(currentWorkflow, newNodes, newEdges) {
|
|
|
52
52
|
export const workflowActions = {
|
|
53
53
|
// Initialize workflow
|
|
54
54
|
initialize: (workflow) => {
|
|
55
|
-
console.log('🔍 Debug: initialize called with:', workflow);
|
|
56
55
|
workflowStore.set(workflow);
|
|
57
|
-
console.log('🔍 Debug: workflow store initialized');
|
|
58
56
|
},
|
|
59
57
|
// Update the entire workflow
|
|
60
58
|
updateWorkflow: (workflow) => {
|
|
61
|
-
console.log('🔍 Debug: updateWorkflow called with:', workflow);
|
|
62
59
|
workflowStore.set(workflow);
|
|
63
|
-
console.log('🔍 Debug: workflow store updated');
|
|
64
60
|
},
|
|
65
61
|
// Update nodes
|
|
66
62
|
updateNodes: (nodes) => {
|
|
@@ -69,16 +65,10 @@ export const workflowActions = {
|
|
|
69
65
|
return null;
|
|
70
66
|
// Check if nodes have actually changed to prevent infinite loops
|
|
71
67
|
if (!hasWorkflowDataChanged($workflow, nodes, $workflow.edges)) {
|
|
72
|
-
console.log('🔍 Debug: Nodes unchanged, skipping update to prevent infinite loop');
|
|
73
68
|
return $workflow;
|
|
74
69
|
}
|
|
75
70
|
// Generate unique version identifier
|
|
76
71
|
const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
77
|
-
console.log('🔍 Debug: Updating nodes with versionId:', versionId);
|
|
78
|
-
console.log('🔍 Debug: Node position changes detected:', nodes.map((node) => ({
|
|
79
|
-
id: node.id,
|
|
80
|
-
position: node.position
|
|
81
|
-
})));
|
|
82
72
|
return {
|
|
83
73
|
...$workflow,
|
|
84
74
|
nodes,
|
|
@@ -98,12 +88,10 @@ export const workflowActions = {
|
|
|
98
88
|
return null;
|
|
99
89
|
// Check if edges have actually changed to prevent infinite loops
|
|
100
90
|
if (!hasWorkflowDataChanged($workflow, $workflow.nodes, edges)) {
|
|
101
|
-
console.log('🔍 Debug: Edges unchanged, skipping update to prevent infinite loop');
|
|
102
91
|
return $workflow;
|
|
103
92
|
}
|
|
104
93
|
// Generate unique version identifier
|
|
105
94
|
const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
106
|
-
console.log('🔍 Debug: Updating edges with versionId:', versionId);
|
|
107
95
|
return {
|
|
108
96
|
...$workflow,
|
|
109
97
|
edges,
|
|
@@ -159,13 +159,6 @@ export function validateConnection(sourceNodeId, sourcePortId, targetNodeId, tar
|
|
|
159
159
|
const sourceMetadata = nodeTypes.find((nt) => nt.id === sourceNode.data.metadata.id);
|
|
160
160
|
const targetMetadata = nodeTypes.find((nt) => nt.id === targetNode.data.metadata.id);
|
|
161
161
|
if (!sourceMetadata || !targetMetadata) {
|
|
162
|
-
console.log('Metadata lookup failed:', {
|
|
163
|
-
sourceNodeId,
|
|
164
|
-
targetNodeId,
|
|
165
|
-
sourceMetadataId: sourceNode.data.metadata.id,
|
|
166
|
-
targetMetadataId: targetNode.data.metadata.id,
|
|
167
|
-
availableNodeTypes: nodeTypes.map((nt) => nt.id)
|
|
168
|
-
});
|
|
169
162
|
return { valid: false, error: 'Node metadata not found' };
|
|
170
163
|
}
|
|
171
164
|
// Find ports
|