@d34dman/flowdrop 0.0.1 → 0.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 (119) hide show
  1. package/README.md +307 -215
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/WorkflowAdapter.js +30 -30
  4. package/dist/api/client.d.ts +24 -1
  5. package/dist/api/client.js +55 -38
  6. package/dist/api/enhanced-client.d.ts +46 -0
  7. package/dist/api/enhanced-client.js +211 -0
  8. package/dist/clients/ApiClient.d.ts +19 -23
  9. package/dist/clients/ApiClient.js +36 -34
  10. package/dist/components/App.svelte +1299 -230
  11. package/dist/components/App.svelte.d.ts +21 -1
  12. package/dist/components/CanvasBanner.svelte +50 -44
  13. package/dist/components/CanvasBanner.svelte.d.ts +5 -19
  14. package/dist/components/ConfigForm.svelte +555 -0
  15. package/dist/components/ConfigForm.svelte.d.ts +32 -0
  16. package/dist/components/ConfigModal.svelte +261 -0
  17. package/dist/components/ConfigModal.svelte.d.ts +31 -0
  18. package/dist/components/ConfigSidebar.svelte +934 -0
  19. package/dist/components/ConfigSidebar.svelte.d.ts +51 -0
  20. package/dist/components/ConnectionLine.svelte +32 -0
  21. package/dist/components/ConnectionLine.svelte.d.ts +3 -0
  22. package/dist/components/GatewayNode.svelte +471 -0
  23. package/dist/components/GatewayNode.svelte.d.ts +15 -0
  24. package/dist/components/LoadingSpinner.svelte +23 -23
  25. package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
  26. package/dist/components/Logo.svelte +82 -0
  27. package/dist/components/Logo.svelte.d.ts +26 -0
  28. package/dist/components/LogsSidebar.svelte +565 -0
  29. package/dist/components/LogsSidebar.svelte.d.ts +34 -0
  30. package/dist/components/MarkdownDisplay.svelte +28 -0
  31. package/dist/components/MarkdownDisplay.svelte.d.ts +7 -0
  32. package/dist/components/Navbar.svelte +663 -0
  33. package/dist/components/Navbar.svelte.d.ts +21 -0
  34. package/dist/components/NodeSidebar.svelte +629 -488
  35. package/dist/components/NodeSidebar.svelte.d.ts +1 -2
  36. package/dist/components/NodeStatusOverlay.svelte +327 -0
  37. package/dist/components/NodeStatusOverlay.svelte.d.ts +11 -0
  38. package/dist/components/NotesNode.svelte +566 -0
  39. package/dist/components/NotesNode.svelte.d.ts +43 -0
  40. package/dist/components/PipelineStatus.svelte +331 -0
  41. package/dist/components/PipelineStatus.svelte.d.ts +18 -0
  42. package/dist/components/SimpleNode.svelte +447 -0
  43. package/dist/components/SimpleNode.svelte.d.ts +24 -0
  44. package/dist/components/SquareNode.svelte +346 -0
  45. package/dist/components/SquareNode.svelte.d.ts +24 -0
  46. package/dist/components/StatusIcon.svelte +112 -0
  47. package/dist/components/StatusIcon.svelte.d.ts +10 -0
  48. package/dist/components/StatusLabel.svelte +33 -0
  49. package/dist/components/StatusLabel.svelte.d.ts +7 -0
  50. package/dist/components/ToolNode.svelte +385 -0
  51. package/dist/components/ToolNode.svelte.d.ts +36 -0
  52. package/dist/components/UniversalNode.svelte +126 -0
  53. package/dist/components/UniversalNode.svelte.d.ts +15 -0
  54. package/dist/components/WorkflowEditor.svelte +871 -528
  55. package/dist/components/WorkflowEditor.svelte.d.ts +15 -5
  56. package/dist/components/WorkflowNode.svelte +428 -542
  57. package/dist/components/WorkflowNode.svelte.d.ts +7 -3
  58. package/dist/config/apiConfig.d.ts +33 -0
  59. package/dist/config/apiConfig.js +39 -0
  60. package/dist/config/defaultPortConfig.d.ts +6 -0
  61. package/dist/config/defaultPortConfig.js +192 -0
  62. package/dist/config/demo.d.ts +58 -0
  63. package/dist/config/demo.js +142 -0
  64. package/dist/config/endpoints.d.ts +106 -0
  65. package/dist/config/endpoints.js +128 -0
  66. package/dist/data/samples.d.ts +38 -4
  67. package/dist/data/samples.js +2789 -737
  68. package/dist/examples/adapter-usage.d.ts +4 -4
  69. package/dist/examples/adapter-usage.js +21 -26
  70. package/dist/examples/api-client-usage.d.ts +6 -6
  71. package/dist/examples/api-client-usage.js +55 -54
  72. package/dist/index.d.ts +23 -15
  73. package/dist/index.js +23 -15
  74. package/dist/mocks/app-environment.d.ts +8 -0
  75. package/dist/mocks/app-environment.js +16 -0
  76. package/dist/mocks/app-forms.d.ts +2 -0
  77. package/dist/mocks/app-forms.js +21 -0
  78. package/dist/mocks/app-navigation.d.ts +5 -0
  79. package/dist/mocks/app-navigation.js +34 -0
  80. package/dist/mocks/app-stores.d.ts +14 -0
  81. package/dist/mocks/app-stores.js +26 -0
  82. package/dist/services/api.d.ts +13 -3
  83. package/dist/services/api.js +91 -36
  84. package/dist/services/globalSave.d.ts +20 -0
  85. package/dist/services/globalSave.js +165 -0
  86. package/dist/services/nodeExecutionService.d.ts +63 -0
  87. package/dist/services/nodeExecutionService.js +261 -0
  88. package/dist/services/portConfigApi.d.ts +14 -0
  89. package/dist/services/portConfigApi.js +69 -0
  90. package/dist/services/toastService.d.ts +147 -0
  91. package/dist/services/toastService.js +235 -0
  92. package/dist/services/workflowStorage.d.ts +2 -2
  93. package/dist/services/workflowStorage.js +10 -10
  94. package/dist/stores/workflowStore.d.ts +53 -0
  95. package/dist/stores/workflowStore.js +264 -0
  96. package/dist/styles/base.css +896 -363
  97. package/dist/svelte-app.d.ts +52 -5
  98. package/dist/svelte-app.js +128 -6
  99. package/dist/types/config.d.ts +291 -0
  100. package/dist/types/config.js +4 -0
  101. package/dist/types/index.d.ts +231 -19
  102. package/dist/types/index.js +1 -1
  103. package/dist/utils/colors.d.ts +67 -33
  104. package/dist/utils/colors.js +183 -118
  105. package/dist/utils/config.d.ts +41 -0
  106. package/dist/utils/config.js +248 -0
  107. package/dist/utils/connections.d.ts +40 -3
  108. package/dist/utils/connections.js +115 -44
  109. package/dist/utils/icons.d.ts +1 -1
  110. package/dist/utils/icons.js +71 -70
  111. package/dist/utils/nodeStatus.d.ts +53 -0
  112. package/dist/utils/nodeStatus.js +183 -0
  113. package/dist/utils/nodeTypes.d.ts +57 -0
  114. package/dist/utils/nodeTypes.js +109 -0
  115. package/dist/utils/nodeWrapper.d.ts +39 -0
  116. package/dist/utils/nodeWrapper.js +62 -0
  117. package/package.json +129 -97
  118. package/dist/components/Node.svelte +0 -38
  119. package/dist/components/Node.svelte.d.ts +0 -4
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Toast Service
3
+ * Centralized toast notification service using svelte-french-toast
4
+ * Provides consistent toast notifications across the FlowDrop application
5
+ */
6
+ import { toast } from 'svelte-5-french-toast';
7
+ /**
8
+ * Show a success toast notification
9
+ */
10
+ export function showSuccess(message, options) {
11
+ return toast.success(message, {
12
+ duration: options?.duration || 4000,
13
+ position: options?.position || 'bottom-center'
14
+ });
15
+ }
16
+ /**
17
+ * Show an error toast notification
18
+ */
19
+ export function showError(message, options) {
20
+ return toast.error(message, {
21
+ duration: options?.duration || 6000,
22
+ position: options?.position || 'bottom-center'
23
+ });
24
+ }
25
+ /**
26
+ * Show a warning toast notification
27
+ */
28
+ export function showWarning(message, options) {
29
+ return toast.error(message, {
30
+ duration: options?.duration || 5000,
31
+ position: options?.position || 'bottom-center'
32
+ });
33
+ }
34
+ /**
35
+ * Show an info toast notification
36
+ */
37
+ export function showInfo(message, options) {
38
+ return toast.success(message, {
39
+ duration: options?.duration || 4000,
40
+ position: options?.position || 'bottom-center'
41
+ });
42
+ }
43
+ /**
44
+ * Show a loading toast notification
45
+ */
46
+ export function showLoading(message, options) {
47
+ return toast.loading(message, {
48
+ duration: options?.duration || Infinity,
49
+ position: options?.position || 'bottom-center'
50
+ });
51
+ }
52
+ /**
53
+ * Dismiss a specific toast by ID
54
+ */
55
+ export function dismissToast(toastId) {
56
+ toast.dismiss(toastId);
57
+ }
58
+ /**
59
+ * Dismiss all toasts
60
+ */
61
+ export function dismissAllToasts() {
62
+ toast.dismiss();
63
+ }
64
+ /**
65
+ * Show a promise-based toast (loading -> success/error)
66
+ */
67
+ export function showPromise(promise, { loading, success, error, options }) {
68
+ return toast.promise(promise, {
69
+ loading,
70
+ success,
71
+ error,
72
+ ...options
73
+ });
74
+ }
75
+ /**
76
+ * Show a confirmation toast (simplified version without action buttons)
77
+ */
78
+ export function showConfirmation(message, options) {
79
+ return toast(message, {
80
+ duration: options?.duration || 5000,
81
+ position: options?.position || 'bottom-center'
82
+ });
83
+ }
84
+ /**
85
+ * API-specific toast helpers
86
+ */
87
+ export const apiToasts = {
88
+ /**
89
+ * Show API success message
90
+ */
91
+ success: (operation, details) => {
92
+ const message = details ? `${operation}: ${details}` : operation;
93
+ return showSuccess(message);
94
+ },
95
+ /**
96
+ * Show API error message
97
+ */
98
+ error: (operation, error) => {
99
+ const errorMessage = error instanceof Error ? error.message : error;
100
+ return showError(`${operation} failed: ${errorMessage}`);
101
+ },
102
+ /**
103
+ * Show API loading message
104
+ */
105
+ loading: (operation) => {
106
+ return showLoading(`${operation}...`);
107
+ },
108
+ /**
109
+ * Show API promise with automatic success/error handling
110
+ */
111
+ promise: (promise, operation, options) => {
112
+ return showPromise(promise, {
113
+ loading: `${operation}...`,
114
+ success: options?.successMessage || `${operation} completed successfully`,
115
+ error: options?.errorMessage || `${operation} failed`
116
+ });
117
+ }
118
+ };
119
+ /**
120
+ * Workflow-specific toast helpers
121
+ */
122
+ export const workflowToasts = {
123
+ /**
124
+ * Show workflow save success
125
+ */
126
+ saved: (workflowName) => {
127
+ const message = workflowName
128
+ ? `Workflow "${workflowName}" saved successfully`
129
+ : 'Workflow saved successfully';
130
+ return showSuccess(message);
131
+ },
132
+ /**
133
+ * Show workflow save error
134
+ */
135
+ saveError: (error) => {
136
+ const errorMessage = error instanceof Error ? error.message : error;
137
+ return showError(`Failed to save workflow: ${errorMessage}`);
138
+ },
139
+ /**
140
+ * Show workflow delete success
141
+ */
142
+ deleted: (workflowName) => {
143
+ const message = workflowName
144
+ ? `Workflow "${workflowName}" deleted successfully`
145
+ : 'Workflow deleted successfully';
146
+ return showSuccess(message);
147
+ },
148
+ /**
149
+ * Show workflow delete error
150
+ */
151
+ deleteError: (error) => {
152
+ const errorMessage = error instanceof Error ? error.message : error;
153
+ return showError(`Failed to delete workflow: ${errorMessage}`);
154
+ },
155
+ /**
156
+ * Show workflow execution started
157
+ */
158
+ executionStarted: (workflowName) => {
159
+ const message = workflowName
160
+ ? `Workflow "${workflowName}" execution started`
161
+ : 'Workflow execution started';
162
+ return showInfo(message);
163
+ },
164
+ /**
165
+ * Show workflow execution completed
166
+ */
167
+ executionCompleted: (workflowName) => {
168
+ const message = workflowName
169
+ ? `Workflow "${workflowName}" execution completed`
170
+ : 'Workflow execution completed';
171
+ return showSuccess(message);
172
+ },
173
+ /**
174
+ * Show workflow export success
175
+ */
176
+ exported: (workflowName) => {
177
+ const message = workflowName
178
+ ? `Workflow "${workflowName}" exported successfully`
179
+ : 'Workflow exported successfully';
180
+ return showSuccess(message);
181
+ },
182
+ /**
183
+ * Show workflow execution error
184
+ */
185
+ executionError: (error) => {
186
+ const errorMessage = error instanceof Error ? error.message : error;
187
+ return showError(`Workflow execution failed: ${errorMessage}`);
188
+ }
189
+ };
190
+ /**
191
+ * Pipeline-specific toast helpers
192
+ */
193
+ export const pipelineToasts = {
194
+ /**
195
+ * Show pipeline creation success
196
+ */
197
+ created: (pipelineName) => {
198
+ const message = pipelineName
199
+ ? `Pipeline "${pipelineName}" created successfully`
200
+ : 'Pipeline created successfully';
201
+ return showSuccess(message);
202
+ },
203
+ /**
204
+ * Show pipeline creation error
205
+ */
206
+ creationError: (error) => {
207
+ const errorMessage = error instanceof Error ? error.message : error;
208
+ return showError(`Failed to create pipeline: ${errorMessage}`);
209
+ },
210
+ /**
211
+ * Show pipeline execution started
212
+ */
213
+ executionStarted: (pipelineId) => {
214
+ return showInfo(`Pipeline ${pipelineId} execution started`);
215
+ },
216
+ /**
217
+ * Show pipeline execution completed
218
+ */
219
+ executionCompleted: (pipelineId) => {
220
+ return showSuccess(`Pipeline ${pipelineId} execution completed`);
221
+ },
222
+ /**
223
+ * Show pipeline execution error
224
+ */
225
+ executionError: (pipelineId, error) => {
226
+ const errorMessage = error instanceof Error ? error.message : error;
227
+ return showError(`Pipeline ${pipelineId} execution failed: ${errorMessage}`);
228
+ },
229
+ /**
230
+ * Show pipeline status update
231
+ */
232
+ statusUpdate: (pipelineId, status) => {
233
+ return showInfo(`Pipeline ${pipelineId} status: ${status}`);
234
+ }
235
+ };
@@ -2,11 +2,11 @@
2
2
  * In-memory workflow storage service
3
3
  * This can be replaced with a database implementation later
4
4
  */
5
- import type { Workflow } from "../types/index.js";
5
+ import type { Workflow } from '../types/index.js';
6
6
  /**
7
7
  * Save a workflow
8
8
  */
9
- export declare function saveWorkflow(workflow: Omit<Workflow, "id">): Promise<Workflow>;
9
+ export declare function saveWorkflow(workflow: Omit<Workflow, 'id'>): Promise<Workflow>;
10
10
  /**
11
11
  * Update an existing workflow
12
12
  */
@@ -2,7 +2,7 @@
2
2
  * In-memory workflow storage service
3
3
  * This can be replaced with a database implementation later
4
4
  */
5
- import { v4 as uuidv4 } from "uuid";
5
+ import { v4 as uuidv4 } from 'uuid';
6
6
  // In-memory storage
7
7
  const workflows = new Map();
8
8
  /**
@@ -20,7 +20,7 @@ export async function saveWorkflow(workflow) {
20
20
  ...workflow,
21
21
  id,
22
22
  metadata: {
23
- version: "1.0.0",
23
+ version: '1.0.0',
24
24
  createdAt: new Date().toISOString(),
25
25
  updatedAt: new Date().toISOString(),
26
26
  ...workflow.metadata
@@ -42,7 +42,7 @@ export async function updateWorkflow(id, workflow) {
42
42
  ...workflow,
43
43
  id, // Ensure ID doesn't change
44
44
  metadata: {
45
- version: existing.metadata?.version || "1.0.0",
45
+ version: existing.metadata?.version || '1.0.0',
46
46
  createdAt: existing.metadata?.createdAt || new Date().toISOString(),
47
47
  updatedAt: new Date().toISOString(),
48
48
  author: workflow.metadata?.author || existing.metadata?.author,
@@ -66,9 +66,9 @@ export async function getWorkflows(options) {
66
66
  // Apply search filter
67
67
  if (options?.search) {
68
68
  const searchLower = options.search.toLowerCase();
69
- filteredWorkflows = filteredWorkflows.filter(workflow => workflow.name.toLowerCase().includes(searchLower) ||
69
+ filteredWorkflows = filteredWorkflows.filter((workflow) => workflow.name.toLowerCase().includes(searchLower) ||
70
70
  workflow.description?.toLowerCase().includes(searchLower) ||
71
- workflow.metadata?.tags?.some(tag => tag.toLowerCase().includes(searchLower)));
71
+ workflow.metadata?.tags?.some((tag) => tag.toLowerCase().includes(searchLower)));
72
72
  }
73
73
  // Sort by updated date (newest first)
74
74
  filteredWorkflows.sort((a, b) => new Date(b.metadata?.updatedAt || 0).getTime() -
@@ -100,16 +100,16 @@ export async function initializeSampleWorkflows() {
100
100
  }
101
101
  // Add a sample workflow
102
102
  const sampleWorkflow = {
103
- name: "Sample Chat Workflow",
104
- description: "A simple workflow demonstrating chat completion",
103
+ name: 'Sample Chat Workflow',
104
+ description: 'A simple workflow demonstrating chat completion',
105
105
  nodes: [],
106
106
  edges: [],
107
107
  metadata: {
108
- version: "1.0.0",
108
+ version: '1.0.0',
109
109
  createdAt: new Date().toISOString(),
110
110
  updatedAt: new Date().toISOString(),
111
- author: "System",
112
- tags: ["sample", "chat"]
111
+ author: 'System',
112
+ tags: ['sample', 'chat']
113
113
  }
114
114
  };
115
115
  await saveWorkflow(sampleWorkflow);
@@ -0,0 +1,53 @@
1
+ import type { Workflow, WorkflowNode, WorkflowEdge } from '../types';
2
+ export declare const workflowStore: import("svelte/store").Writable<Workflow>;
3
+ export declare const workflowId: import("svelte/store").Readable<string>;
4
+ export declare const workflowName: import("svelte/store").Readable<string>;
5
+ export declare const workflowNodes: import("svelte/store").Readable<WorkflowNode[]>;
6
+ export declare const workflowEdges: import("svelte/store").Readable<WorkflowEdge[]>;
7
+ export declare const workflowMetadata: import("svelte/store").Readable<{
8
+ version: string;
9
+ createdAt: string;
10
+ updatedAt: string;
11
+ author?: string;
12
+ tags?: string[];
13
+ versionId?: string;
14
+ updateNumber?: number;
15
+ }>;
16
+ export declare const workflowActions: {
17
+ initialize: (workflow: Workflow) => void;
18
+ updateWorkflow: (workflow: Workflow) => void;
19
+ updateNodes: (nodes: WorkflowNode[]) => void;
20
+ updateEdges: (edges: WorkflowEdge[]) => void;
21
+ updateName: (name: string) => void;
22
+ addNode: (node: WorkflowNode) => void;
23
+ removeNode: (nodeId: string) => void;
24
+ addEdge: (edge: WorkflowEdge) => void;
25
+ removeEdge: (edgeId: string) => void;
26
+ updateNode: (nodeId: string, updates: Partial<WorkflowNode>) => void;
27
+ clear: () => void;
28
+ updateMetadata: (metadata: Partial<Workflow["metadata"]>) => void;
29
+ batchUpdate: (updates: {
30
+ nodes?: WorkflowNode[];
31
+ edges?: WorkflowEdge[];
32
+ name?: string;
33
+ description?: string;
34
+ metadata?: Partial<Workflow["metadata"]>;
35
+ }) => void;
36
+ };
37
+ export declare const workflowChanged: import("svelte/store").Readable<{
38
+ nodes: WorkflowNode[];
39
+ edges: WorkflowEdge[];
40
+ name: string;
41
+ }>;
42
+ export declare const workflowValidation: import("svelte/store").Readable<{
43
+ hasNodes: boolean;
44
+ hasEdges: boolean;
45
+ nodeCount: number;
46
+ edgeCount: number;
47
+ isValid: boolean;
48
+ }>;
49
+ export declare const workflowMetadataChanged: import("svelte/store").Readable<{
50
+ createdAt: string;
51
+ updatedAt: string;
52
+ version: string;
53
+ }>;
@@ -0,0 +1,264 @@
1
+ import { writable, derived } from 'svelte/store';
2
+ // Global workflow state
3
+ export const workflowStore = writable(null);
4
+ // Derived stores for individual workflow properties
5
+ export const workflowId = derived(workflowStore, ($workflow) => $workflow?.id || null);
6
+ export const workflowName = derived(workflowStore, ($workflow) => $workflow?.name || 'Untitled Workflow');
7
+ export const workflowNodes = derived(workflowStore, ($workflow) => $workflow?.nodes || []);
8
+ export const workflowEdges = derived(workflowStore, ($workflow) => $workflow?.edges || []);
9
+ export const workflowMetadata = derived(workflowStore, ($workflow) => $workflow?.metadata || {
10
+ version: '1.0.0',
11
+ createdAt: new Date().toISOString(),
12
+ updatedAt: new Date().toISOString(),
13
+ versionId: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
14
+ updateNumber: 0
15
+ });
16
+ // Helper function to check if workflow data has actually changed
17
+ function hasWorkflowDataChanged(currentWorkflow, newNodes, newEdges) {
18
+ if (!currentWorkflow)
19
+ return true;
20
+ // Check if nodes have changed
21
+ if (currentWorkflow.nodes.length !== newNodes.length)
22
+ return true;
23
+ for (let i = 0; i < newNodes.length; i++) {
24
+ const currentNode = currentWorkflow.nodes[i];
25
+ const newNode = newNodes[i];
26
+ if (!currentNode || !newNode)
27
+ return true;
28
+ if (currentNode.id !== newNode.id)
29
+ return true;
30
+ if (currentNode.position.x !== newNode.position.x ||
31
+ currentNode.position.y !== newNode.position.y)
32
+ return true;
33
+ if (JSON.stringify(currentNode.data) !== JSON.stringify(newNode.data))
34
+ return true;
35
+ }
36
+ // Check if edges have changed
37
+ if (currentWorkflow.edges.length !== newEdges.length)
38
+ return true;
39
+ for (let i = 0; i < newEdges.length; i++) {
40
+ const currentEdge = currentWorkflow.edges[i];
41
+ const newEdge = newEdges[i];
42
+ if (!currentEdge || !newEdge)
43
+ return true;
44
+ if (currentEdge.id !== newEdge.id)
45
+ return true;
46
+ if (currentEdge.source !== newEdge.source || currentEdge.target !== newEdge.target)
47
+ return true;
48
+ }
49
+ return false;
50
+ }
51
+ // Actions for updating the workflow
52
+ export const workflowActions = {
53
+ // Initialize workflow
54
+ initialize: (workflow) => {
55
+ console.log('🔍 Debug: initialize called with:', workflow);
56
+ workflowStore.set(workflow);
57
+ console.log('🔍 Debug: workflow store initialized');
58
+ },
59
+ // Update the entire workflow
60
+ updateWorkflow: (workflow) => {
61
+ console.log('🔍 Debug: updateWorkflow called with:', workflow);
62
+ workflowStore.set(workflow);
63
+ console.log('🔍 Debug: workflow store updated');
64
+ },
65
+ // Update nodes
66
+ updateNodes: (nodes) => {
67
+ workflowStore.update(($workflow) => {
68
+ if (!$workflow)
69
+ return null;
70
+ // Check if nodes have actually changed to prevent infinite loops
71
+ if (!hasWorkflowDataChanged($workflow, nodes, $workflow.edges)) {
72
+ console.log('🔍 Debug: Nodes unchanged, skipping update to prevent infinite loop');
73
+ return $workflow;
74
+ }
75
+ // Generate unique version identifier
76
+ 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
+ return {
83
+ ...$workflow,
84
+ nodes,
85
+ metadata: {
86
+ ...$workflow.metadata,
87
+ updatedAt: new Date().toISOString(),
88
+ versionId,
89
+ updateNumber: ($workflow.metadata?.updateNumber || 0) + 1
90
+ }
91
+ };
92
+ });
93
+ },
94
+ // Update edges
95
+ updateEdges: (edges) => {
96
+ workflowStore.update(($workflow) => {
97
+ if (!$workflow)
98
+ return null;
99
+ // Check if edges have actually changed to prevent infinite loops
100
+ if (!hasWorkflowDataChanged($workflow, $workflow.nodes, edges)) {
101
+ console.log('🔍 Debug: Edges unchanged, skipping update to prevent infinite loop');
102
+ return $workflow;
103
+ }
104
+ // Generate unique version identifier
105
+ const versionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
106
+ console.log('🔍 Debug: Updating edges with versionId:', versionId);
107
+ return {
108
+ ...$workflow,
109
+ edges,
110
+ metadata: {
111
+ ...$workflow.metadata,
112
+ updatedAt: new Date().toISOString(),
113
+ versionId,
114
+ updateNumber: ($workflow.metadata?.updateNumber || 0) + 1
115
+ }
116
+ };
117
+ });
118
+ },
119
+ // Update workflow name
120
+ updateName: (name) => {
121
+ workflowStore.update(($workflow) => {
122
+ if (!$workflow)
123
+ return null;
124
+ return {
125
+ ...$workflow,
126
+ name,
127
+ metadata: {
128
+ ...$workflow.metadata,
129
+ updatedAt: new Date().toISOString()
130
+ }
131
+ };
132
+ });
133
+ },
134
+ // Add a node
135
+ addNode: (node) => {
136
+ workflowStore.update(($workflow) => {
137
+ if (!$workflow)
138
+ return null;
139
+ return {
140
+ ...$workflow,
141
+ nodes: [...$workflow.nodes, node],
142
+ metadata: {
143
+ ...$workflow.metadata,
144
+ updatedAt: new Date().toISOString()
145
+ }
146
+ };
147
+ });
148
+ },
149
+ // Remove a node
150
+ removeNode: (nodeId) => {
151
+ workflowStore.update(($workflow) => {
152
+ if (!$workflow)
153
+ return null;
154
+ return {
155
+ ...$workflow,
156
+ nodes: $workflow.nodes.filter((node) => node.id !== nodeId),
157
+ edges: $workflow.edges.filter((edge) => edge.source !== nodeId && edge.target !== nodeId),
158
+ metadata: {
159
+ ...$workflow.metadata,
160
+ updatedAt: new Date().toISOString()
161
+ }
162
+ };
163
+ });
164
+ },
165
+ // Add an edge
166
+ addEdge: (edge) => {
167
+ workflowStore.update(($workflow) => {
168
+ if (!$workflow)
169
+ return null;
170
+ return {
171
+ ...$workflow,
172
+ edges: [...$workflow.edges, edge],
173
+ metadata: {
174
+ ...$workflow.metadata,
175
+ updatedAt: new Date().toISOString()
176
+ }
177
+ };
178
+ });
179
+ },
180
+ // Remove an edge
181
+ removeEdge: (edgeId) => {
182
+ workflowStore.update(($workflow) => {
183
+ if (!$workflow)
184
+ return null;
185
+ return {
186
+ ...$workflow,
187
+ edges: $workflow.edges.filter((edge) => edge.id !== edgeId),
188
+ metadata: {
189
+ ...$workflow.metadata,
190
+ updatedAt: new Date().toISOString()
191
+ }
192
+ };
193
+ });
194
+ },
195
+ // Update a specific node
196
+ updateNode: (nodeId, updates) => {
197
+ workflowStore.update(($workflow) => {
198
+ if (!$workflow)
199
+ return null;
200
+ return {
201
+ ...$workflow,
202
+ nodes: $workflow.nodes.map((node) => (node.id === nodeId ? { ...node, ...updates } : node)),
203
+ metadata: {
204
+ ...$workflow.metadata,
205
+ updatedAt: new Date().toISOString()
206
+ }
207
+ };
208
+ });
209
+ },
210
+ // Clear the workflow
211
+ clear: () => {
212
+ workflowStore.set(null);
213
+ },
214
+ // Update workflow metadata
215
+ updateMetadata: (metadata) => {
216
+ workflowStore.update(($workflow) => {
217
+ if (!$workflow)
218
+ return null;
219
+ return {
220
+ ...$workflow,
221
+ metadata: {
222
+ ...$workflow.metadata,
223
+ ...metadata,
224
+ updatedAt: new Date().toISOString()
225
+ }
226
+ };
227
+ });
228
+ },
229
+ // Batch update nodes and edges (useful for complex operations)
230
+ batchUpdate: (updates) => {
231
+ workflowStore.update(($workflow) => {
232
+ if (!$workflow)
233
+ return null;
234
+ return {
235
+ ...$workflow,
236
+ ...(updates.nodes && { nodes: updates.nodes }),
237
+ ...(updates.edges && { edges: updates.edges }),
238
+ ...(updates.name && { name: updates.name }),
239
+ ...(updates.description !== undefined && { description: updates.description }),
240
+ metadata: {
241
+ ...$workflow.metadata,
242
+ ...(updates.metadata && { ...updates.metadata }),
243
+ updatedAt: new Date().toISOString()
244
+ }
245
+ };
246
+ });
247
+ }
248
+ };
249
+ // Derived store for workflow changes (useful for triggering saves)
250
+ export const workflowChanged = derived([workflowNodes, workflowEdges, workflowName], ([nodes, edges, name]) => ({ nodes, edges, name }));
251
+ // Derived store for workflow validation
252
+ export const workflowValidation = derived([workflowNodes, workflowEdges], ([nodes, edges]) => ({
253
+ hasNodes: nodes.length > 0,
254
+ hasEdges: edges.length > 0,
255
+ nodeCount: nodes.length,
256
+ edgeCount: edges.length,
257
+ isValid: nodes.length > 0 && edges.length >= 0
258
+ }));
259
+ // Derived store for workflow metadata changes
260
+ export const workflowMetadataChanged = derived(workflowMetadata, (metadata) => ({
261
+ createdAt: metadata.createdAt,
262
+ updatedAt: metadata.updatedAt,
263
+ version: metadata.version || '1.0.0'
264
+ }));