@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,26 @@
1
+ /**
2
+ * Mock for $app/stores
3
+ * Provides minimal implementations for SvelteKit stores in library context
4
+ */
5
+
6
+ import { writable } from 'svelte/store';
7
+
8
+ // Mock page store
9
+ export const page = writable({
10
+ url: new URL('http://localhost:3000'),
11
+ params: {},
12
+ route: { id: null },
13
+ status: 200,
14
+ error: null,
15
+ data: {},
16
+ form: null
17
+ });
18
+
19
+ // Mock navigating store
20
+ export const navigating = writable(null);
21
+
22
+ // Mock updated store
23
+ export const updated = writable(false);
24
+
25
+ // Mock preloadData function
26
+ export const preloadData = async () => ({});
@@ -1,10 +1,20 @@
1
1
  /**
2
2
  * Client-side API service for FlowDrop
3
- * Provides methods to interact with the backend APIs
3
+ * Provides methods to interact with the backend APIs using configurable endpoints
4
4
  */
5
- import type { NodeMetadata, Workflow } from "../types/index.js";
5
+ import type { NodeMetadata, Workflow } from '../types/index.js';
6
+ import type { EndpointConfig } from '../config/endpoints.js';
6
7
  /**
7
- * Set the API base URL at runtime
8
+ * Set the endpoint configuration at runtime
9
+ */
10
+ export declare function setEndpointConfig(config: EndpointConfig): void;
11
+ /**
12
+ * Get the current endpoint configuration
13
+ */
14
+ export declare function getEndpointConfig(): EndpointConfig | null;
15
+ /**
16
+ * Set the API base URL (deprecated - use setEndpointConfig instead)
17
+ * @deprecated Use setEndpointConfig() with a full EndpointConfig object instead
8
18
  */
9
19
  export declare function setApiBaseUrl(url: string): void;
10
20
  /**
@@ -1,24 +1,55 @@
1
1
  /**
2
2
  * Client-side API service for FlowDrop
3
- * Provides methods to interact with the backend APIs
3
+ * Provides methods to interact with the backend APIs using configurable endpoints
4
4
  */
5
- let API_BASE = "/api";
5
+ import { buildEndpointUrl, getEndpointMethod, getEndpointHeaders } from '../config/endpoints.js';
6
+ let endpointConfig = null;
6
7
  /**
7
- * Set the API base URL at runtime
8
+ * Set the endpoint configuration at runtime
9
+ */
10
+ export function setEndpointConfig(config) {
11
+ endpointConfig = config;
12
+ }
13
+ /**
14
+ * Get the current endpoint configuration
15
+ */
16
+ export function getEndpointConfig() {
17
+ return endpointConfig;
18
+ }
19
+ /**
20
+ * Set the API base URL (deprecated - use setEndpointConfig instead)
21
+ * @deprecated Use setEndpointConfig() with a full EndpointConfig object instead
8
22
  */
9
23
  export function setApiBaseUrl(url) {
10
- API_BASE = url;
24
+ console.warn('⚠️ setApiBaseUrl() is deprecated. Use setEndpointConfig() with a full EndpointConfig object instead.');
25
+ if (!endpointConfig) {
26
+ // Dynamic import for backward compatibility
27
+ import('../config/endpoints.js')
28
+ .then(({ createEndpointConfig }) => {
29
+ endpointConfig = createEndpointConfig(url);
30
+ })
31
+ .catch((error) => {
32
+ // Failed to load endpoint config
33
+ });
34
+ }
35
+ else {
36
+ endpointConfig.baseUrl = url.replace(/\/$/, '');
37
+ }
11
38
  }
12
39
  /**
13
- * Generic API request helper
40
+ * Generic API request helper with endpoint configuration
14
41
  */
15
- async function apiRequest(endpoint, options = {}) {
42
+ async function apiRequest(endpointKey, endpointPath, params, options = {}) {
43
+ if (!endpointConfig) {
44
+ throw new Error('Endpoint configuration not set. Call setEndpointConfig() first.');
45
+ }
16
46
  try {
17
- const response = await fetch(`${API_BASE}${endpoint}`, {
18
- headers: {
19
- "Content-Type": "application/json",
20
- ...options.headers
21
- },
47
+ const url = buildEndpointUrl(endpointConfig, endpointPath, params);
48
+ const method = getEndpointMethod(endpointConfig, endpointKey);
49
+ const headers = getEndpointHeaders(endpointConfig, endpointKey);
50
+ const response = await fetch(url, {
51
+ method,
52
+ headers,
22
53
  ...options
23
54
  });
24
55
  const data = await response.json();
@@ -28,7 +59,6 @@ async function apiRequest(endpoint, options = {}) {
28
59
  return data;
29
60
  }
30
61
  catch (error) {
31
- console.error(`API request failed for ${endpoint}:`, error);
32
62
  throw error;
33
63
  }
34
64
  }
@@ -40,25 +70,31 @@ export const nodeApi = {
40
70
  * Get all node types with optional filtering
41
71
  */
42
72
  async getNodes(options) {
73
+ if (!endpointConfig) {
74
+ throw new Error('Endpoint configuration not set');
75
+ }
43
76
  const params = new URLSearchParams();
44
77
  if (options?.category)
45
- params.append("category", options.category);
78
+ params.append('category', options.category);
46
79
  if (options?.search)
47
- params.append("search", options.search);
80
+ params.append('search', options.search);
48
81
  if (options?.limit)
49
- params.append("limit", options.limit.toString());
82
+ params.append('limit', options.limit.toString());
50
83
  if (options?.offset)
51
- params.append("offset", options.offset.toString());
52
- const response = await apiRequest("/nodes?" + params.toString());
84
+ params.append('offset', options.offset.toString());
85
+ const response = await apiRequest('nodes.list', endpointConfig.endpoints.nodes.list + '?' + params.toString());
53
86
  return response.data || [];
54
87
  },
55
88
  /**
56
89
  * Get a specific node type by ID
57
90
  */
58
91
  async getNode(id) {
59
- const response = await apiRequest(`/nodes/${id}`);
92
+ if (!endpointConfig) {
93
+ throw new Error('Endpoint configuration not set');
94
+ }
95
+ const response = await apiRequest('nodes.get', endpointConfig.endpoints.nodes.get, { id });
60
96
  if (!response.data) {
61
- throw new Error("Node not found");
97
+ throw new Error('Node not found');
62
98
  }
63
99
  return response.data;
64
100
  }
@@ -71,23 +107,29 @@ export const workflowApi = {
71
107
  * Get all workflows with optional filtering
72
108
  */
73
109
  async getWorkflows(options) {
110
+ if (!endpointConfig) {
111
+ throw new Error('Endpoint configuration not set');
112
+ }
74
113
  const params = new URLSearchParams();
75
114
  if (options?.search)
76
- params.append("search", options.search);
115
+ params.append('search', options.search);
77
116
  if (options?.limit)
78
- params.append("limit", options.limit.toString());
117
+ params.append('limit', options.limit.toString());
79
118
  if (options?.offset)
80
- params.append("offset", options.offset.toString());
81
- const response = await apiRequest("/workflows?" + params.toString());
119
+ params.append('offset', options.offset.toString());
120
+ const response = await apiRequest('workflows.list', endpointConfig.endpoints.workflows.list + '?' + params.toString());
82
121
  return response.data || [];
83
122
  },
84
123
  /**
85
124
  * Get a specific workflow by ID
86
125
  */
87
126
  async getWorkflow(id) {
88
- const response = await apiRequest(`/workflows/${id}`);
127
+ if (!endpointConfig) {
128
+ throw new Error('Endpoint configuration not set');
129
+ }
130
+ const response = await apiRequest('workflows.get', endpointConfig.endpoints.workflows.get, { id });
89
131
  if (!response.data) {
90
- throw new Error("Workflow not found");
132
+ throw new Error('Workflow not found');
91
133
  }
92
134
  return response.data;
93
135
  },
@@ -95,12 +137,15 @@ export const workflowApi = {
95
137
  * Create a new workflow
96
138
  */
97
139
  async createWorkflow(workflow) {
98
- const response = await apiRequest("/workflows", {
99
- method: "POST",
140
+ if (!endpointConfig) {
141
+ throw new Error('Endpoint configuration not set');
142
+ }
143
+ const response = await apiRequest('workflows.create', endpointConfig.endpoints.workflows.create, undefined, {
144
+ method: 'POST',
100
145
  body: JSON.stringify(workflow)
101
146
  });
102
147
  if (!response.data) {
103
- throw new Error("Failed to create workflow");
148
+ throw new Error('Failed to create workflow');
104
149
  }
105
150
  return response.data;
106
151
  },
@@ -108,12 +153,15 @@ export const workflowApi = {
108
153
  * Update an existing workflow
109
154
  */
110
155
  async updateWorkflow(id, workflow) {
111
- const response = await apiRequest(`/workflows/${id}`, {
112
- method: "PUT",
156
+ if (!endpointConfig) {
157
+ throw new Error('Endpoint configuration not set');
158
+ }
159
+ const response = await apiRequest('workflows.update', endpointConfig.endpoints.workflows.update, { id }, {
160
+ method: 'PUT',
113
161
  body: JSON.stringify(workflow)
114
162
  });
115
163
  if (!response.data) {
116
- throw new Error("Failed to update workflow");
164
+ throw new Error('Failed to update workflow');
117
165
  }
118
166
  return response.data;
119
167
  },
@@ -121,20 +169,27 @@ export const workflowApi = {
121
169
  * Delete a workflow
122
170
  */
123
171
  async deleteWorkflow(id) {
124
- await apiRequest(`/workflows/${id}`, {
125
- method: "DELETE"
126
- });
172
+ if (!endpointConfig) {
173
+ throw new Error('Endpoint configuration not set');
174
+ }
175
+ await apiRequest('workflows.delete', endpointConfig.endpoints.workflows.delete, { id }, { method: 'DELETE' });
127
176
  },
128
177
  /**
129
178
  * Save workflow (create or update)
130
179
  */
131
180
  async saveWorkflow(workflow) {
132
- if (workflow.id && workflow.id.length === 36) {
133
- // Update existing workflow (UUID is 36 characters)
181
+ // Check if this is an existing workflow by looking for a valid ID
182
+ // Valid IDs should not be a UUID (which indicates a new workflow)
183
+ const isExistingWorkflow = workflow.id &&
184
+ workflow.id.length > 0 &&
185
+ !workflow.id.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
186
+ if (isExistingWorkflow) {
187
+ // Update existing workflow
134
188
  return this.updateWorkflow(workflow.id, workflow);
135
189
  }
136
190
  else {
137
191
  // Create new workflow
192
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
138
193
  const { id, ...workflowData } = workflow;
139
194
  return this.createWorkflow(workflowData);
140
195
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Global Save Service
3
+ * Provides save and export functionality that can be accessed from anywhere in the app
4
+ * This allows the main navbar to save workflows without being tied to a specific component
5
+ */
6
+ /**
7
+ * Global save function that can be called from anywhere
8
+ * Uses the current workflow from the global store
9
+ */
10
+ export declare function globalSaveWorkflow(): Promise<void>;
11
+ /**
12
+ * Global export function that can be called from anywhere
13
+ * Uses the current workflow from the global store
14
+ */
15
+ export declare function globalExportWorkflow(): Promise<void>;
16
+ /**
17
+ * Initialize global save functions on window object for external access
18
+ * This allows the functions to be called from anywhere in the application
19
+ */
20
+ export declare function initializeGlobalSave(): void;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Global Save Service
3
+ * Provides save and export functionality that can be accessed from anywhere in the app
4
+ * This allows the main navbar to save workflows without being tied to a specific component
5
+ */
6
+ import { get } from 'svelte/store';
7
+ import { workflowStore } from '../stores/workflowStore.js';
8
+ import { workflowApi, setEndpointConfig } from './api.js';
9
+ import { createEndpointConfig } from '../config/endpoints.js';
10
+ import { v4 as uuidv4 } from 'uuid';
11
+ import { apiToasts, workflowToasts, dismissToast } from './toastService.js';
12
+ /**
13
+ * Ensure API configuration is initialized
14
+ * This is needed when the global save function is called from the layout component
15
+ * which doesn't initialize the API configuration like the App component does
16
+ */
17
+ async function ensureApiConfiguration() {
18
+ // Check if we need to initialize the API configuration
19
+ // We'll check if the endpointConfig is already set by importing the api module
20
+ try {
21
+ // Import the api module to check if endpointConfig is already set
22
+ const { getEndpointConfig } = await import('./api.js');
23
+ // Try to get the current configuration
24
+ const currentConfig = getEndpointConfig();
25
+ if (currentConfig && currentConfig.baseUrl) {
26
+ return;
27
+ }
28
+ }
29
+ catch (error) {
30
+ // Could not check existing API configuration, initializing
31
+ }
32
+ // API configuration is not initialized, so let's initialize it
33
+ // Use the same environment variable priority as the App component
34
+ // Prioritize VITE_API_BASE_URL since it's configured correctly
35
+ const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ||
36
+ import.meta.env.VITE_DRUPAL_API_URL ||
37
+ import.meta.env.VITE_FLOWDROP_API_URL ||
38
+ (() => {
39
+ // If we're in development (localhost:5173), use relative path
40
+ if (window.location.hostname === 'localhost' && window.location.port === '5173') {
41
+ return '/api/flowdrop';
42
+ }
43
+ // Otherwise, use the current domain
44
+ return `${window.location.protocol}//${window.location.host}/api/flowdrop`;
45
+ })();
46
+ const config = createEndpointConfig(apiBaseUrl, {
47
+ auth: {
48
+ type: 'none' // No authentication for now
49
+ },
50
+ timeout: 10000, // 10 second timeout
51
+ retry: {
52
+ enabled: true,
53
+ maxAttempts: 2,
54
+ delay: 1000,
55
+ backoff: 'exponential'
56
+ }
57
+ });
58
+ setEndpointConfig(config);
59
+ }
60
+ /**
61
+ * Global save function that can be called from anywhere
62
+ * Uses the current workflow from the global store
63
+ */
64
+ export async function globalSaveWorkflow() {
65
+ let loadingToast;
66
+ try {
67
+ // Show loading toast
68
+ loadingToast = apiToasts.loading('Saving workflow');
69
+ // Ensure API configuration is initialized
70
+ await ensureApiConfiguration();
71
+ // Get current workflow from global store
72
+ const currentWorkflow = get(workflowStore);
73
+ if (!currentWorkflow) {
74
+ if (loadingToast)
75
+ dismissToast(loadingToast);
76
+ apiToasts.error('Save workflow', 'No workflow to save');
77
+ return;
78
+ }
79
+ // Determine the workflow ID
80
+ let workflowId;
81
+ if (currentWorkflow.id) {
82
+ workflowId = currentWorkflow.id;
83
+ }
84
+ else {
85
+ workflowId = uuidv4();
86
+ }
87
+ // Create workflow object for saving
88
+ const finalWorkflow = {
89
+ id: workflowId,
90
+ name: currentWorkflow.name || 'Untitled Workflow',
91
+ nodes: currentWorkflow.nodes || [],
92
+ edges: currentWorkflow.edges || [],
93
+ metadata: {
94
+ version: '1.0.0',
95
+ createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
96
+ updatedAt: new Date().toISOString()
97
+ }
98
+ };
99
+ const savedWorkflow = await workflowApi.saveWorkflow(finalWorkflow);
100
+ // Dismiss loading toast and show success toast
101
+ if (loadingToast)
102
+ dismissToast(loadingToast);
103
+ workflowToasts.saved(finalWorkflow.name);
104
+ }
105
+ catch (error) {
106
+ // Dismiss loading toast and show error toast
107
+ if (loadingToast)
108
+ dismissToast(loadingToast);
109
+ apiToasts.error('Save workflow', error instanceof Error ? error.message : 'Unknown error');
110
+ throw error;
111
+ }
112
+ }
113
+ /**
114
+ * Global export function that can be called from anywhere
115
+ * Uses the current workflow from the global store
116
+ */
117
+ export async function globalExportWorkflow() {
118
+ try {
119
+ // Get current workflow from global store
120
+ const currentWorkflow = get(workflowStore);
121
+ if (!currentWorkflow) {
122
+ apiToasts.error('Export workflow', 'No workflow to export');
123
+ return;
124
+ }
125
+ // Create workflow object for export
126
+ const finalWorkflow = {
127
+ id: currentWorkflow.id || 'untitled-workflow',
128
+ name: currentWorkflow.name || 'Untitled Workflow',
129
+ nodes: currentWorkflow.nodes || [],
130
+ edges: currentWorkflow.edges || [],
131
+ metadata: {
132
+ version: '1.0.0',
133
+ createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
134
+ updatedAt: new Date().toISOString()
135
+ }
136
+ };
137
+ // Create and download the file
138
+ const dataStr = JSON.stringify(finalWorkflow, null, 2);
139
+ const dataBlob = new Blob([dataStr], { type: 'application/json' });
140
+ const url = URL.createObjectURL(dataBlob);
141
+ const link = document.createElement('a');
142
+ link.href = url;
143
+ link.download = `${finalWorkflow.name}.json`;
144
+ link.click();
145
+ URL.revokeObjectURL(url);
146
+ // Show success toast
147
+ workflowToasts.exported(finalWorkflow.name);
148
+ }
149
+ catch (error) {
150
+ // Export failed
151
+ apiToasts.error('Export workflow', error instanceof Error ? error.message : 'Unknown error');
152
+ }
153
+ }
154
+ /**
155
+ * Initialize global save functions on window object for external access
156
+ * This allows the functions to be called from anywhere in the application
157
+ */
158
+ export function initializeGlobalSave() {
159
+ if (typeof window !== 'undefined') {
160
+ // @ts-ignore - Adding to window for external access
161
+ window.flowdropGlobalSave = globalSaveWorkflow;
162
+ // @ts-ignore - Adding to window for external access
163
+ window.flowdropGlobalExport = globalExportWorkflow;
164
+ }
165
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Node Execution Service
3
+ * Handles fetching and managing node execution information from the backend
4
+ */
5
+ import type { NodeExecutionInfo } from '../types/index.js';
6
+ /**
7
+ * Service for managing node execution information
8
+ */
9
+ export declare class NodeExecutionService {
10
+ private static instance;
11
+ private cache;
12
+ private cacheTimeout;
13
+ private lastFetch;
14
+ private apiUnavailable;
15
+ private apiUnavailableUntil;
16
+ private constructor();
17
+ static getInstance(): NodeExecutionService;
18
+ /**
19
+ * Get execution information for a specific node from pipeline data
20
+ */
21
+ getNodeExecutionInfo(nodeId: string, pipelineId?: string): Promise<NodeExecutionInfo | null>;
22
+ /**
23
+ * Get execution information for multiple nodes from pipeline data
24
+ */
25
+ getMultipleNodeExecutionInfo(nodeIds: string[], pipelineId?: string): Promise<Record<string, NodeExecutionInfo>>;
26
+ /**
27
+ * Get all node execution counts
28
+ */
29
+ getAllNodeExecutionCounts(): Promise<Record<string, number>>;
30
+ /**
31
+ * Get cached execution info for a node
32
+ */
33
+ getCachedNodeExecutionInfo(nodeId: string): NodeExecutionInfo | null;
34
+ /**
35
+ * Clear cache for a specific node
36
+ */
37
+ clearNodeCache(nodeId: string): void;
38
+ /**
39
+ * Clear all cache
40
+ */
41
+ clearAllCache(): void;
42
+ /**
43
+ * Check if cache is stale
44
+ */
45
+ isCacheStale(): boolean;
46
+ /**
47
+ * Update execution info for a node (for real-time updates)
48
+ */
49
+ updateNodeExecutionInfo(nodeId: string, executionInfo: Partial<NodeExecutionInfo>): void;
50
+ /**
51
+ * Map job status to execution status
52
+ */
53
+ private mapJobStatusToExecutionStatus;
54
+ /**
55
+ * Batch update execution info for multiple nodes
56
+ */
57
+ updateMultipleNodeExecutionInfo(updates: Record<string, Partial<NodeExecutionInfo>>): void;
58
+ /**
59
+ * Reset API availability status (useful for testing or when API becomes available)
60
+ */
61
+ resetApiAvailability(): void;
62
+ }
63
+ export declare const nodeExecutionService: NodeExecutionService;