@d34dman/flowdrop 0.0.1

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 (46) hide show
  1. package/README.md +293 -0
  2. package/dist/adapters/WorkflowAdapter.d.ts +166 -0
  3. package/dist/adapters/WorkflowAdapter.js +337 -0
  4. package/dist/api/client.d.ts +79 -0
  5. package/dist/api/client.js +208 -0
  6. package/dist/app.css +0 -0
  7. package/dist/clients/ApiClient.d.ts +203 -0
  8. package/dist/clients/ApiClient.js +212 -0
  9. package/dist/components/App.svelte +237 -0
  10. package/dist/components/App.svelte.d.ts +3 -0
  11. package/dist/components/CanvasBanner.svelte +51 -0
  12. package/dist/components/CanvasBanner.svelte.d.ts +22 -0
  13. package/dist/components/LoadingSpinner.svelte +36 -0
  14. package/dist/components/LoadingSpinner.svelte.d.ts +8 -0
  15. package/dist/components/Node.svelte +38 -0
  16. package/dist/components/Node.svelte.d.ts +4 -0
  17. package/dist/components/NodeSidebar.svelte +500 -0
  18. package/dist/components/NodeSidebar.svelte.d.ts +9 -0
  19. package/dist/components/WorkflowEditor.svelte +542 -0
  20. package/dist/components/WorkflowEditor.svelte.d.ts +10 -0
  21. package/dist/components/WorkflowNode.svelte +558 -0
  22. package/dist/components/WorkflowNode.svelte.d.ts +11 -0
  23. package/dist/data/samples.d.ts +17 -0
  24. package/dist/data/samples.js +1193 -0
  25. package/dist/examples/adapter-usage.d.ts +66 -0
  26. package/dist/examples/adapter-usage.js +138 -0
  27. package/dist/examples/api-client-usage.d.ts +31 -0
  28. package/dist/examples/api-client-usage.js +241 -0
  29. package/dist/index.d.ts +19 -0
  30. package/dist/index.js +27 -0
  31. package/dist/services/api.d.ts +110 -0
  32. package/dist/services/api.js +149 -0
  33. package/dist/services/workflowStorage.d.ts +37 -0
  34. package/dist/services/workflowStorage.js +116 -0
  35. package/dist/styles/base.css +858 -0
  36. package/dist/svelte-app.d.ts +17 -0
  37. package/dist/svelte-app.js +30 -0
  38. package/dist/types/index.d.ts +179 -0
  39. package/dist/types/index.js +4 -0
  40. package/dist/utils/colors.d.ts +121 -0
  41. package/dist/utils/colors.js +240 -0
  42. package/dist/utils/connections.d.ts +47 -0
  43. package/dist/utils/connections.js +240 -0
  44. package/dist/utils/icons.d.ts +102 -0
  45. package/dist/utils/icons.js +149 -0
  46. package/package.json +99 -0
@@ -0,0 +1,337 @@
1
+ /**
2
+ * WorkflowAdapter - Abstracts SvelteFlow internals for external systems
3
+ *
4
+ * This adapter provides a clean interface for working with workflows without
5
+ * needing to understand SvelteFlow's internal structure. It handles:
6
+ * - Converting between standard workflow format and SvelteFlow format
7
+ * - CRUD operations on workflows, nodes, and edges
8
+ * - Validation and error checking
9
+ * - Import/export functionality
10
+ *
11
+ * The adapter is designed to be used by:
12
+ * - Backend systems that need to process workflows
13
+ * - External applications that want to integrate with FlowDrop
14
+ * - Systems that need to generate or modify workflows programmatically
15
+ */
16
+ import { v4 as uuidv4 } from "uuid";
17
+ /**
18
+ * Workflow Adapter Class
19
+ * Provides a clean API for workflow operations without exposing SvelteFlow internals
20
+ */
21
+ export class WorkflowAdapter {
22
+ nodeTypes = [];
23
+ constructor(nodeTypes = []) {
24
+ this.nodeTypes = nodeTypes;
25
+ }
26
+ /**
27
+ * Create a new workflow
28
+ */
29
+ createWorkflow(name, description) {
30
+ return {
31
+ id: uuidv4(),
32
+ name,
33
+ description,
34
+ nodes: [],
35
+ edges: [],
36
+ metadata: {
37
+ version: "1.0.0",
38
+ createdAt: new Date().toISOString(),
39
+ updatedAt: new Date().toISOString()
40
+ }
41
+ };
42
+ }
43
+ /**
44
+ * Add a node to a workflow
45
+ */
46
+ addNode(workflow, nodeType, position, config) {
47
+ const metadata = this.nodeTypes.find(nt => nt.id === nodeType);
48
+ if (!metadata) {
49
+ throw new Error(`Node type '${nodeType}' not found`);
50
+ }
51
+ const node = {
52
+ id: uuidv4(),
53
+ type: nodeType,
54
+ position,
55
+ data: {
56
+ label: metadata.name,
57
+ config: config || {},
58
+ metadata
59
+ }
60
+ };
61
+ workflow.nodes.push(node);
62
+ workflow.metadata.updatedAt = new Date().toISOString();
63
+ return node;
64
+ }
65
+ /**
66
+ * Remove a node from a workflow
67
+ */
68
+ removeNode(workflow, nodeId) {
69
+ const nodeIndex = workflow.nodes.findIndex(n => n.id === nodeId);
70
+ if (nodeIndex === -1)
71
+ return false;
72
+ workflow.nodes.splice(nodeIndex, 1);
73
+ // Remove associated edges
74
+ workflow.edges = workflow.edges.filter(edge => edge.source !== nodeId && edge.target !== nodeId);
75
+ workflow.metadata.updatedAt = new Date().toISOString();
76
+ return true;
77
+ }
78
+ /**
79
+ * Update node position
80
+ */
81
+ updateNodePosition(workflow, nodeId, position) {
82
+ const node = workflow.nodes.find(n => n.id === nodeId);
83
+ if (!node)
84
+ return false;
85
+ node.position = position;
86
+ workflow.metadata.updatedAt = new Date().toISOString();
87
+ return true;
88
+ }
89
+ /**
90
+ * Update node configuration
91
+ */
92
+ updateNodeConfig(workflow, nodeId, config) {
93
+ const node = workflow.nodes.find(n => n.id === nodeId);
94
+ if (!node)
95
+ return false;
96
+ node.data.config = { ...node.data.config, ...config };
97
+ workflow.metadata.updatedAt = new Date().toISOString();
98
+ return true;
99
+ }
100
+ /**
101
+ * Add an edge between nodes
102
+ */
103
+ addEdge(workflow, sourceNodeId, targetNodeId, sourceHandle, targetHandle) {
104
+ const edge = {
105
+ id: uuidv4(),
106
+ source: sourceNodeId,
107
+ target: targetNodeId,
108
+ sourceHandle,
109
+ targetHandle
110
+ };
111
+ workflow.edges.push(edge);
112
+ workflow.metadata.updatedAt = new Date().toISOString();
113
+ return edge;
114
+ }
115
+ /**
116
+ * Remove an edge from a workflow
117
+ */
118
+ removeEdge(workflow, edgeId) {
119
+ const edgeIndex = workflow.edges.findIndex(e => e.id === edgeId);
120
+ if (edgeIndex === -1)
121
+ return false;
122
+ workflow.edges.splice(edgeIndex, 1);
123
+ workflow.metadata.updatedAt = new Date().toISOString();
124
+ return true;
125
+ }
126
+ /**
127
+ * Get all nodes of a specific type
128
+ */
129
+ getNodesByType(workflow, nodeType) {
130
+ return workflow.nodes.filter(node => node.type === nodeType);
131
+ }
132
+ /**
133
+ * Get all edges connected to a node
134
+ */
135
+ getNodeEdges(workflow, nodeId) {
136
+ return workflow.edges.filter(edge => edge.source === nodeId || edge.target === nodeId);
137
+ }
138
+ /**
139
+ * Get connected nodes (both incoming and outgoing)
140
+ */
141
+ getConnectedNodes(workflow, nodeId) {
142
+ const connectedNodeIds = new Set();
143
+ workflow.edges.forEach(edge => {
144
+ if (edge.source === nodeId) {
145
+ connectedNodeIds.add(edge.target);
146
+ }
147
+ else if (edge.target === nodeId) {
148
+ connectedNodeIds.add(edge.source);
149
+ }
150
+ });
151
+ return workflow.nodes.filter(node => connectedNodeIds.has(node.id));
152
+ }
153
+ /**
154
+ * Validate workflow structure
155
+ */
156
+ validateWorkflow(workflow) {
157
+ const errors = [];
158
+ const warnings = [];
159
+ // Check for empty workflow
160
+ if (workflow.nodes.length === 0) {
161
+ warnings.push("Workflow has no nodes");
162
+ }
163
+ // Check for orphaned edges
164
+ const nodeIds = new Set(workflow.nodes.map(n => n.id));
165
+ workflow.edges.forEach(edge => {
166
+ if (!nodeIds.has(edge.source)) {
167
+ errors.push(`Edge ${edge.id} references non-existent source node ${edge.source}`);
168
+ }
169
+ if (!nodeIds.has(edge.target)) {
170
+ errors.push(`Edge ${edge.id} references non-existent target node ${edge.target}`);
171
+ }
172
+ });
173
+ // Check for self-connections
174
+ workflow.edges.forEach(edge => {
175
+ if (edge.source === edge.target) {
176
+ errors.push(`Node ${edge.source} cannot connect to itself`);
177
+ }
178
+ });
179
+ // Check for duplicate node IDs
180
+ const nodeIdCounts = new Map();
181
+ workflow.nodes.forEach(node => {
182
+ nodeIdCounts.set(node.id, (nodeIdCounts.get(node.id) || 0) + 1);
183
+ });
184
+ nodeIdCounts.forEach((count, id) => {
185
+ if (count > 1) {
186
+ errors.push(`Duplicate node ID: ${id}`);
187
+ }
188
+ });
189
+ // Check for duplicate edge IDs
190
+ const edgeIdCounts = new Map();
191
+ workflow.edges.forEach(edge => {
192
+ edgeIdCounts.set(edge.id, (edgeIdCounts.get(edge.id) || 0) + 1);
193
+ });
194
+ edgeIdCounts.forEach((count, id) => {
195
+ if (count > 1) {
196
+ errors.push(`Duplicate edge ID: ${id}`);
197
+ }
198
+ });
199
+ return {
200
+ valid: errors.length === 0,
201
+ errors,
202
+ warnings
203
+ };
204
+ }
205
+ /**
206
+ * Export workflow to JSON
207
+ */
208
+ exportWorkflow(workflow) {
209
+ return JSON.stringify(workflow, null, 2);
210
+ }
211
+ /**
212
+ * Import workflow from JSON
213
+ */
214
+ importWorkflow(json) {
215
+ try {
216
+ const workflow = JSON.parse(json);
217
+ // Validate the imported workflow
218
+ const validation = this.validateWorkflow(workflow);
219
+ if (!validation.valid) {
220
+ throw new Error(`Invalid workflow: ${validation.errors.join(", ")}`);
221
+ }
222
+ // Update metadata
223
+ workflow.metadata = {
224
+ version: workflow.metadata?.version || "1.0.0",
225
+ createdAt: workflow.metadata?.createdAt || new Date().toISOString(),
226
+ updatedAt: new Date().toISOString(),
227
+ author: workflow.metadata?.author,
228
+ tags: workflow.metadata?.tags
229
+ };
230
+ return workflow;
231
+ }
232
+ catch (error) {
233
+ throw new Error(`Failed to import workflow: ${error instanceof Error ? error.message : "Unknown error"}`);
234
+ }
235
+ }
236
+ /**
237
+ * Convert SvelteFlow workflow to standard format
238
+ */
239
+ fromSvelteFlow(svelteFlowWorkflow) {
240
+ return {
241
+ id: svelteFlowWorkflow.id,
242
+ name: svelteFlowWorkflow.name,
243
+ description: svelteFlowWorkflow.description,
244
+ nodes: svelteFlowWorkflow.nodes.map(node => ({
245
+ id: node.id,
246
+ type: node.data.metadata.id,
247
+ position: node.position,
248
+ data: {
249
+ label: node.data.label,
250
+ config: node.data.config,
251
+ metadata: node.data.metadata
252
+ }
253
+ })),
254
+ edges: svelteFlowWorkflow.edges.map(edge => ({
255
+ id: edge.id,
256
+ source: edge.source,
257
+ target: edge.target,
258
+ sourceHandle: edge.sourceHandle,
259
+ targetHandle: edge.targetHandle
260
+ })),
261
+ metadata: svelteFlowWorkflow.metadata
262
+ };
263
+ }
264
+ /**
265
+ * Convert standard workflow to SvelteFlow format
266
+ */
267
+ toSvelteFlow(workflow) {
268
+ return {
269
+ id: workflow.id,
270
+ name: workflow.name,
271
+ description: workflow.description,
272
+ nodes: workflow.nodes.map(node => ({
273
+ id: node.id,
274
+ type: "workflowNode",
275
+ position: node.position,
276
+ deletable: true,
277
+ data: {
278
+ label: node.data.label,
279
+ config: node.data.config,
280
+ metadata: node.data.metadata,
281
+ nodeId: node.id
282
+ }
283
+ })),
284
+ edges: workflow.edges.map(edge => ({
285
+ id: edge.id,
286
+ source: edge.source,
287
+ target: edge.target,
288
+ sourceHandle: edge.sourceHandle,
289
+ targetHandle: edge.targetHandle
290
+ })),
291
+ metadata: workflow.metadata
292
+ };
293
+ }
294
+ /**
295
+ * Get workflow statistics
296
+ */
297
+ getWorkflowStats(workflow) {
298
+ const nodeTypeCounts = new Map();
299
+ workflow.nodes.forEach(node => {
300
+ nodeTypeCounts.set(node.type, (nodeTypeCounts.get(node.type) || 0) + 1);
301
+ });
302
+ return {
303
+ totalNodes: workflow.nodes.length,
304
+ totalEdges: workflow.edges.length,
305
+ nodeTypeCounts: Object.fromEntries(nodeTypeCounts),
306
+ lastModified: workflow.metadata?.updatedAt
307
+ };
308
+ }
309
+ /**
310
+ * Clone a workflow
311
+ */
312
+ cloneWorkflow(workflow, newName) {
313
+ const cloned = JSON.parse(JSON.stringify(workflow));
314
+ // Generate new IDs for all nodes and edges
315
+ const idMapping = new Map();
316
+ cloned.nodes.forEach(node => {
317
+ const oldId = node.id;
318
+ node.id = uuidv4();
319
+ idMapping.set(oldId, node.id);
320
+ });
321
+ cloned.edges.forEach(edge => {
322
+ edge.id = uuidv4();
323
+ edge.source = idMapping.get(edge.source) || edge.source;
324
+ edge.target = idMapping.get(edge.target) || edge.target;
325
+ });
326
+ cloned.id = uuidv4();
327
+ cloned.name = newName || `${workflow.name} (Copy)`;
328
+ cloned.metadata = {
329
+ version: cloned.metadata?.version || "1.0.0",
330
+ createdAt: new Date().toISOString(),
331
+ updatedAt: new Date().toISOString(),
332
+ author: cloned.metadata?.author,
333
+ tags: cloned.metadata?.tags
334
+ };
335
+ return cloned;
336
+ }
337
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * API Client for FlowDrop Workflow Library
3
+ */
4
+ import type { NodeMetadata, Workflow, ExecutionResult } from "../types/index.js";
5
+ /**
6
+ * HTTP API client for FlowDrop
7
+ */
8
+ export declare class FlowDropApiClient {
9
+ private baseUrl;
10
+ private headers;
11
+ constructor(baseUrl: string, apiKey?: string);
12
+ /**
13
+ * Make HTTP request with error handling
14
+ */
15
+ private request;
16
+ /**
17
+ * Fetch available node types and their metadata
18
+ */
19
+ getAvailableNodes(): Promise<NodeMetadata[]>;
20
+ /**
21
+ * Fetch nodes by category
22
+ */
23
+ getNodesByCategory(category: string): Promise<NodeMetadata[]>;
24
+ /**
25
+ * Fetch a specific node's metadata
26
+ */
27
+ getNodeMetadata(nodeId: string): Promise<NodeMetadata>;
28
+ /**
29
+ * Save a workflow
30
+ */
31
+ saveWorkflow(workflow: Workflow): Promise<Workflow>;
32
+ /**
33
+ * Update an existing workflow
34
+ */
35
+ updateWorkflow(workflowId: string, workflow: Partial<Workflow>): Promise<Workflow>;
36
+ /**
37
+ * Load a workflow by ID
38
+ */
39
+ loadWorkflow(workflowId: string): Promise<Workflow>;
40
+ /**
41
+ * List all workflows
42
+ */
43
+ listWorkflows(): Promise<Workflow[]>;
44
+ /**
45
+ * Delete a workflow
46
+ */
47
+ deleteWorkflow(workflowId: string): Promise<void>;
48
+ /**
49
+ * Execute a workflow
50
+ */
51
+ executeWorkflow(workflowId: string, inputs?: Record<string, unknown>): Promise<ExecutionResult>;
52
+ /**
53
+ * Get execution status
54
+ */
55
+ getExecutionStatus(executionId: string): Promise<ExecutionResult>;
56
+ /**
57
+ * Cancel workflow execution
58
+ */
59
+ cancelExecution(executionId: string): Promise<void>;
60
+ /**
61
+ * Get execution logs
62
+ */
63
+ getExecutionLogs(executionId: string): Promise<string[]>;
64
+ /**
65
+ * Validate workflow configuration
66
+ */
67
+ validateWorkflow(workflow: Workflow): Promise<{
68
+ valid: boolean;
69
+ errors: string[];
70
+ }>;
71
+ /**
72
+ * Export workflow as JSON
73
+ */
74
+ exportWorkflow(workflowId: string): Promise<string>;
75
+ /**
76
+ * Import workflow from JSON
77
+ */
78
+ importWorkflow(workflowJson: string): Promise<Workflow>;
79
+ }
@@ -0,0 +1,208 @@
1
+ /**
2
+ * API Client for FlowDrop Workflow Library
3
+ */
4
+ /**
5
+ * HTTP API client for FlowDrop
6
+ */
7
+ export class FlowDropApiClient {
8
+ baseUrl;
9
+ headers;
10
+ constructor(baseUrl, apiKey) {
11
+ this.baseUrl = baseUrl.replace(/\/$/, "");
12
+ this.headers = {
13
+ "Content-Type": "application/json",
14
+ };
15
+ if (apiKey) {
16
+ this.headers["Authorization"] = `Bearer ${apiKey}`;
17
+ }
18
+ }
19
+ /**
20
+ * Make HTTP request with error handling
21
+ */
22
+ async request(endpoint, options = {}) {
23
+ const url = `${this.baseUrl}${endpoint}`;
24
+ const config = {
25
+ headers: this.headers,
26
+ ...options,
27
+ };
28
+ try {
29
+ const response = await fetch(url, config);
30
+ if (!response.ok) {
31
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
32
+ }
33
+ const data = await response.json();
34
+ return data;
35
+ }
36
+ catch (error) {
37
+ console.error("API request failed:", error);
38
+ throw new Error(`API request failed: ${error instanceof Error ? error.message : "Unknown error"}`);
39
+ }
40
+ }
41
+ /**
42
+ * Fetch available node types and their metadata
43
+ */
44
+ async getAvailableNodes() {
45
+ const response = await this.request("/api/nodes");
46
+ if (!response.success || !response.data) {
47
+ throw new Error(response.error || "Failed to fetch available nodes");
48
+ }
49
+ return response.data;
50
+ }
51
+ /**
52
+ * Fetch nodes by category
53
+ */
54
+ async getNodesByCategory(category) {
55
+ const response = await this.request(`/api/nodes?category=${encodeURIComponent(category)}`);
56
+ if (!response.success || !response.data) {
57
+ throw new Error(response.error || "Failed to fetch nodes by category");
58
+ }
59
+ return response.data;
60
+ }
61
+ /**
62
+ * Fetch a specific node's metadata
63
+ */
64
+ async getNodeMetadata(nodeId) {
65
+ const response = await this.request(`/api/nodes/${encodeURIComponent(nodeId)}`);
66
+ if (!response.success || !response.data) {
67
+ throw new Error(response.error || "Failed to fetch node metadata");
68
+ }
69
+ return response.data;
70
+ }
71
+ /**
72
+ * Save a workflow
73
+ */
74
+ async saveWorkflow(workflow) {
75
+ const response = await this.request("/api/workflows", {
76
+ method: "POST",
77
+ body: JSON.stringify(workflow),
78
+ });
79
+ if (!response.success || !response.data) {
80
+ throw new Error(response.error || "Failed to save workflow");
81
+ }
82
+ return response.data;
83
+ }
84
+ /**
85
+ * Update an existing workflow
86
+ */
87
+ async updateWorkflow(workflowId, workflow) {
88
+ const response = await this.request(`/api/workflows/${encodeURIComponent(workflowId)}`, {
89
+ method: "PUT",
90
+ body: JSON.stringify(workflow),
91
+ });
92
+ if (!response.success || !response.data) {
93
+ throw new Error(response.error || "Failed to update workflow");
94
+ }
95
+ return response.data;
96
+ }
97
+ /**
98
+ * Load a workflow by ID
99
+ */
100
+ async loadWorkflow(workflowId) {
101
+ const response = await this.request(`/api/workflows/${encodeURIComponent(workflowId)}`);
102
+ if (!response.success || !response.data) {
103
+ throw new Error(response.error || "Failed to load workflow");
104
+ }
105
+ return response.data;
106
+ }
107
+ /**
108
+ * List all workflows
109
+ */
110
+ async listWorkflows() {
111
+ const response = await this.request("/api/workflows");
112
+ if (!response.success || !response.data) {
113
+ throw new Error(response.error || "Failed to list workflows");
114
+ }
115
+ return response.data;
116
+ }
117
+ /**
118
+ * Delete a workflow
119
+ */
120
+ async deleteWorkflow(workflowId) {
121
+ const response = await this.request(`/api/workflows/${encodeURIComponent(workflowId)}`, {
122
+ method: "DELETE",
123
+ });
124
+ if (!response.success) {
125
+ throw new Error(response.error || "Failed to delete workflow");
126
+ }
127
+ }
128
+ /**
129
+ * Execute a workflow
130
+ */
131
+ async executeWorkflow(workflowId, inputs) {
132
+ const response = await this.request(`/api/workflows/${encodeURIComponent(workflowId)}/execute`, {
133
+ method: "POST",
134
+ body: JSON.stringify({ inputs }),
135
+ });
136
+ if (!response.success || !response.data) {
137
+ throw new Error(response.error || "Failed to execute workflow");
138
+ }
139
+ return response.data;
140
+ }
141
+ /**
142
+ * Get execution status
143
+ */
144
+ async getExecutionStatus(executionId) {
145
+ const response = await this.request(`/api/executions/${encodeURIComponent(executionId)}`);
146
+ if (!response.success || !response.data) {
147
+ throw new Error(response.error || "Failed to get execution status");
148
+ }
149
+ return response.data;
150
+ }
151
+ /**
152
+ * Cancel workflow execution
153
+ */
154
+ async cancelExecution(executionId) {
155
+ const response = await this.request(`/api/executions/${encodeURIComponent(executionId)}/cancel`, {
156
+ method: "POST",
157
+ });
158
+ if (!response.success) {
159
+ throw new Error(response.error || "Failed to cancel execution");
160
+ }
161
+ }
162
+ /**
163
+ * Get execution logs
164
+ */
165
+ async getExecutionLogs(executionId) {
166
+ const response = await this.request(`/api/executions/${encodeURIComponent(executionId)}/logs`);
167
+ if (!response.success || !response.data) {
168
+ throw new Error(response.error || "Failed to get execution logs");
169
+ }
170
+ return response.data;
171
+ }
172
+ /**
173
+ * Validate workflow configuration
174
+ */
175
+ async validateWorkflow(workflow) {
176
+ const response = await this.request("/api/workflows/validate", {
177
+ method: "POST",
178
+ body: JSON.stringify(workflow),
179
+ });
180
+ if (!response.success || !response.data) {
181
+ throw new Error(response.error || "Failed to validate workflow");
182
+ }
183
+ return response.data;
184
+ }
185
+ /**
186
+ * Export workflow as JSON
187
+ */
188
+ async exportWorkflow(workflowId) {
189
+ const response = await this.request(`/api/workflows/${encodeURIComponent(workflowId)}/export`);
190
+ if (!response.success || !response.data) {
191
+ throw new Error(response.error || "Failed to export workflow");
192
+ }
193
+ return response.data;
194
+ }
195
+ /**
196
+ * Import workflow from JSON
197
+ */
198
+ async importWorkflow(workflowJson) {
199
+ const response = await this.request("/api/workflows/import", {
200
+ method: "POST",
201
+ body: JSON.stringify({ workflow: workflowJson }),
202
+ });
203
+ if (!response.success || !response.data) {
204
+ throw new Error(response.error || "Failed to import workflow");
205
+ }
206
+ return response.data;
207
+ }
208
+ }
package/dist/app.css ADDED
File without changes