@d34dman/flowdrop 0.0.15 → 0.0.16

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.
@@ -17,24 +17,44 @@
17
17
  import { sampleNodes } from '../data/samples.js';
18
18
  import { createEndpointConfig } from '../config/endpoints.js';
19
19
  import type { EndpointConfig } from '../config/endpoints.js';
20
- import { workflowStore, workflowActions, workflowName } from '../stores/workflowStore.js';
20
+ import type { AuthProvider } from '../types/auth.js';
21
+ import type { FlowDropEventHandlers, FlowDropFeatures } from '../types/events.js';
22
+ import { mergeFeatures } from '../types/events.js';
23
+ import {
24
+ workflowStore,
25
+ workflowActions,
26
+ workflowName,
27
+ markAsSaved
28
+ } from '../stores/workflowStore.js';
21
29
  import { apiToasts, dismissToast } from '../services/toastService.js';
22
30
 
23
- // Configuration props for runtime customization
31
+ /**
32
+ * Configuration props for runtime customization
33
+ */
24
34
  interface Props {
35
+ /** Initial workflow to load */
25
36
  workflow?: Workflow;
37
+ /** Pre-loaded node types (if provided, skips API fetch) */
38
+ nodes?: NodeMetadata[];
39
+ /** Editor height */
26
40
  height?: string | number;
41
+ /** Editor width */
27
42
  width?: string | number;
43
+ /** Show the navbar */
28
44
  showNavbar?: boolean;
29
- // New configuration options for pipeline status mode
45
+ /** Disable the node sidebar */
30
46
  disableSidebar?: boolean;
47
+ /** Lock the workflow (prevent changes) */
31
48
  lockWorkflow?: boolean;
49
+ /** Read-only mode */
32
50
  readOnly?: boolean;
51
+ /** Node execution statuses */
33
52
  nodeStatuses?: Record<string, 'pending' | 'running' | 'completed' | 'error'>;
34
- // Pipeline ID for fetching node execution info from jobs
53
+ /** Pipeline ID for fetching node execution info */
35
54
  pipelineId?: string;
36
- // Navbar customization
55
+ /** Custom navbar title */
37
56
  navbarTitle?: string;
57
+ /** Custom navbar actions */
38
58
  navbarActions?: Array<{
39
59
  label: string;
40
60
  href: string;
@@ -42,13 +62,21 @@
42
62
  variant?: 'primary' | 'secondary' | 'outline';
43
63
  onclick?: (event: Event) => void;
44
64
  }>;
45
- // API configuration - optional, defaults to '/api/flowdrop'
65
+ /** API base URL */
46
66
  apiBaseUrl?: string;
67
+ /** Endpoint configuration */
47
68
  endpointConfig?: EndpointConfig;
69
+ /** Authentication provider */
70
+ authProvider?: AuthProvider;
71
+ /** Event handlers */
72
+ eventHandlers?: FlowDropEventHandlers;
73
+ /** Feature configuration */
74
+ features?: FlowDropFeatures;
48
75
  }
49
76
 
50
77
  let {
51
78
  workflow: initialWorkflow,
79
+ nodes: propNodes,
52
80
  height = '100vh',
53
81
  width = '100%',
54
82
  showNavbar = false,
@@ -60,9 +88,15 @@
60
88
  navbarTitle,
61
89
  navbarActions = [],
62
90
  apiBaseUrl,
63
- endpointConfig: propEndpointConfig
91
+ endpointConfig: propEndpointConfig,
92
+ authProvider,
93
+ eventHandlers,
94
+ features: propFeatures
64
95
  }: Props = $props();
65
96
 
97
+ // Merge features with defaults
98
+ const features = mergeFeatures(propFeatures);
99
+
66
100
  // Create breadcrumb-style title - at top level to avoid store subscription issues
67
101
  let breadcrumbTitle = $derived(() => {
68
102
  // Use custom navbar title if provided
@@ -129,10 +163,19 @@
129
163
 
130
164
  /**
131
165
  * Fetch node types from the server
166
+ *
167
+ * If propNodes is provided, uses those instead of fetching from API.
168
+ * This fixes the bug where propNodes was ignored.
132
169
  */
133
170
  async function fetchNodeTypes(): Promise<void> {
134
- // Show loading toast
135
- const loadingToast = apiToasts.loading('Loading node types');
171
+ // If nodes were provided as props, use them directly (skip API fetch)
172
+ if (propNodes && propNodes.length > 0) {
173
+ nodes = propNodes;
174
+ return;
175
+ }
176
+
177
+ // Show loading toast (if toasts are enabled)
178
+ const loadingToast = features.showToasts ? apiToasts.loading('Loading node types') : null;
136
179
  try {
137
180
  error = null;
138
181
 
@@ -142,13 +185,35 @@
142
185
  error = null;
143
186
 
144
187
  // Dismiss loading toast
145
- dismissToast(loadingToast);
188
+ if (loadingToast) {
189
+ dismissToast(loadingToast);
190
+ }
146
191
  } catch (err) {
147
192
  // Dismiss loading toast and show error toast
148
- dismissToast(loadingToast);
193
+ if (loadingToast) {
194
+ dismissToast(loadingToast);
195
+ }
196
+
197
+ const errorMessage = err instanceof Error ? err.message : 'Unknown error';
198
+
199
+ // Notify parent via event handler
200
+ if (eventHandlers?.onApiError) {
201
+ const suppressToast = eventHandlers.onApiError(
202
+ err instanceof Error ? err : new Error(errorMessage),
203
+ 'fetchNodes'
204
+ );
205
+ if (suppressToast) {
206
+ // Parent handled the error, don't show default toast
207
+ nodes = sampleNodes;
208
+ return;
209
+ }
210
+ }
211
+
149
212
  // Show error but don't block the UI
150
- error = `API Error: ${err instanceof Error ? err.message : 'Unknown error'}. Using sample data.`;
151
- apiToasts.error('Load node types', err instanceof Error ? err.message : 'Unknown error');
213
+ error = `API Error: ${errorMessage}. Using sample data.`;
214
+ if (features.showToasts) {
215
+ apiToasts.error('Load node types', errorMessage);
216
+ }
152
217
 
153
218
  // Fallback to sample data
154
219
  nodes = sampleNodes;
@@ -283,27 +348,37 @@
283
348
 
284
349
  /**
285
350
  * Save workflow - exposed API function
351
+ *
352
+ * Integrates with event handlers for enterprise customization.
286
353
  */
287
354
  async function saveWorkflow(): Promise<void> {
288
355
  // Wait for any pending DOM updates before saving
289
356
  await tick();
290
357
 
291
- // Show loading toast
292
- const loadingToast = apiToasts.loading('Saving workflow');
358
+ // Use current workflow from global store
359
+ const workflowToSave = $workflowStore;
360
+
361
+ if (!workflowToSave) {
362
+ return;
363
+ }
364
+
365
+ // Call onBeforeSave if provided - allows cancellation
366
+ if (eventHandlers?.onBeforeSave) {
367
+ const shouldContinue = await eventHandlers.onBeforeSave(workflowToSave);
368
+ if (shouldContinue === false) {
369
+ // Save cancelled by event handler
370
+ return;
371
+ }
372
+ }
373
+
374
+ // Show loading toast (if enabled)
375
+ const loadingToast = features.showToasts ? apiToasts.loading('Saving workflow') : null;
293
376
 
294
377
  try {
295
378
  // Import necessary modules
296
379
  const { workflowApi } = await import('../services/api.js');
297
380
  const { v4: uuidv4 } = await import('uuid');
298
381
 
299
- // Use current workflow from global store
300
- const workflowToSave = $workflowStore;
301
-
302
- if (!workflowToSave) {
303
- dismissToast(loadingToast);
304
- return;
305
- }
306
-
307
382
  // Determine the workflow ID
308
383
  let workflowId: string;
309
384
  if (workflowToSave.id) {
@@ -313,7 +388,7 @@
313
388
  }
314
389
 
315
390
  // Create workflow object for saving
316
- const finalWorkflow = {
391
+ const finalWorkflow: Workflow = {
317
392
  id: workflowId,
318
393
  name: workflowToSave.name || 'Untitled Workflow',
319
394
  description: workflowToSave.description || '',
@@ -342,14 +417,45 @@
342
417
  });
343
418
  }
344
419
 
420
+ // Mark as saved (clears dirty state)
421
+ markAsSaved();
422
+
345
423
  // Dismiss loading toast and show success
346
- dismissToast(loadingToast);
347
- apiToasts.success('Save workflow', 'Workflow saved successfully');
424
+ if (loadingToast) {
425
+ dismissToast(loadingToast);
426
+ }
427
+ if (features.showToasts) {
428
+ apiToasts.success('Save workflow', 'Workflow saved successfully');
429
+ }
430
+
431
+ // Call onAfterSave if provided
432
+ if (eventHandlers?.onAfterSave) {
433
+ await eventHandlers.onAfterSave(savedWorkflow);
434
+ }
348
435
  } catch (error) {
349
- // Dismiss loading toast and show error
350
- dismissToast(loadingToast);
351
- const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
352
- apiToasts.error('Save workflow', errorMessage);
436
+ // Dismiss loading toast
437
+ if (loadingToast) {
438
+ dismissToast(loadingToast);
439
+ }
440
+
441
+ const errorObj = error instanceof Error ? error : new Error('Unknown error occurred');
442
+
443
+ // Call onSaveError if provided
444
+ if (eventHandlers?.onSaveError && workflowToSave) {
445
+ await eventHandlers.onSaveError(errorObj, workflowToSave);
446
+ }
447
+
448
+ // Check if parent wants to handle API errors
449
+ let suppressToast = false;
450
+ if (eventHandlers?.onApiError) {
451
+ suppressToast = eventHandlers.onApiError(errorObj, 'save') === true;
452
+ }
453
+
454
+ // Show error toast if not suppressed
455
+ if (features.showToasts && !suppressToast) {
456
+ apiToasts.error('Save workflow', errorObj.message);
457
+ }
458
+
353
459
  throw error; // Re-throw to allow calling code to handle if needed
354
460
  }
355
461
  }
@@ -398,9 +504,7 @@
398
504
 
399
505
  // Expose save and export functions globally for external access
400
506
  if (typeof window !== 'undefined') {
401
- // @ts-expect-error - Adding to window for external access
402
507
  window.flowdropSave = saveWorkflow;
403
- // @ts-expect-error - Adding to window for external access
404
508
  window.flowdropExport = exportWorkflow;
405
509
  }
406
510
 
@@ -425,6 +529,11 @@
425
529
  // Initialize the workflow store if we have an initial workflow
426
530
  if (initialWorkflow) {
427
531
  workflowActions.initialize(initialWorkflow);
532
+
533
+ // Emit onWorkflowLoad event
534
+ if (eventHandlers?.onWorkflowLoad) {
535
+ eventHandlers.onWorkflowLoad(initialWorkflow);
536
+ }
428
537
  }
429
538
  })();
430
539
 
@@ -635,7 +744,10 @@
635
744
  <div class="flowdrop-config-sidebar__detail">
636
745
  <span class="flowdrop-config-sidebar__detail-label">Node ID:</span>
637
746
  <div class="flowdrop-config-sidebar__detail-value-with-copy">
638
- <span class="flowdrop-config-sidebar__detail-value" style="font-family: monospace;">
747
+ <span
748
+ class="flowdrop-config-sidebar__detail-value"
749
+ style="font-family: monospace;"
750
+ >
639
751
  {selectedNodeForConfig().id}
640
752
  </span>
641
753
  <button
@@ -1,16 +1,34 @@
1
- import type { Workflow } from '../types/index.js';
1
+ import type { NodeMetadata, Workflow } from '../types/index.js';
2
2
  import type { EndpointConfig } from '../config/endpoints.js';
3
+ import type { AuthProvider } from '../types/auth.js';
4
+ import type { FlowDropEventHandlers, FlowDropFeatures } from '../types/events.js';
5
+ /**
6
+ * Configuration props for runtime customization
7
+ */
3
8
  interface Props {
9
+ /** Initial workflow to load */
4
10
  workflow?: Workflow;
11
+ /** Pre-loaded node types (if provided, skips API fetch) */
12
+ nodes?: NodeMetadata[];
13
+ /** Editor height */
5
14
  height?: string | number;
15
+ /** Editor width */
6
16
  width?: string | number;
17
+ /** Show the navbar */
7
18
  showNavbar?: boolean;
19
+ /** Disable the node sidebar */
8
20
  disableSidebar?: boolean;
21
+ /** Lock the workflow (prevent changes) */
9
22
  lockWorkflow?: boolean;
23
+ /** Read-only mode */
10
24
  readOnly?: boolean;
25
+ /** Node execution statuses */
11
26
  nodeStatuses?: Record<string, 'pending' | 'running' | 'completed' | 'error'>;
27
+ /** Pipeline ID for fetching node execution info */
12
28
  pipelineId?: string;
29
+ /** Custom navbar title */
13
30
  navbarTitle?: string;
31
+ /** Custom navbar actions */
14
32
  navbarActions?: Array<{
15
33
  label: string;
16
34
  href: string;
@@ -18,8 +36,16 @@ interface Props {
18
36
  variant?: 'primary' | 'secondary' | 'outline';
19
37
  onclick?: (event: Event) => void;
20
38
  }>;
39
+ /** API base URL */
21
40
  apiBaseUrl?: string;
41
+ /** Endpoint configuration */
22
42
  endpointConfig?: EndpointConfig;
43
+ /** Authentication provider */
44
+ authProvider?: AuthProvider;
45
+ /** Event handlers */
46
+ eventHandlers?: FlowDropEventHandlers;
47
+ /** Feature configuration */
48
+ features?: FlowDropFeatures;
23
49
  }
24
50
  declare const App: import("svelte").Component<Props, {}, "">;
25
51
  type App = ReturnType<typeof App>;
@@ -444,8 +444,8 @@ export const sampleNodes = [
444
444
  type: 'string',
445
445
  title: 'Model',
446
446
  description: 'OpenAI model to use',
447
- default: 'gpt-3.5-turbo',
448
- enum: ['gpt-3.5-turbo', 'gpt-4', 'gpt-4-turbo']
447
+ default: 'gpt-4o-mini',
448
+ enum: ['gpt-4o-mini', 'gpt-5-chat-latest', 'gpt-4.1']
449
449
  },
450
450
  temperature: {
451
451
  type: 'number',
@@ -2107,8 +2107,8 @@ export const sampleNodes = [
2107
2107
  type: 'string',
2108
2108
  title: 'Model',
2109
2109
  description: 'Chat model to use',
2110
- default: 'gpt-3.5-turbo',
2111
- enum: ['gpt-3.5-turbo', 'gpt-4', 'claude-3-sonnet']
2110
+ default: 'gpt-4o-mini',
2111
+ enum: ['gpt-4o-mini', 'gpt-5-chat-latest', 'claude-3-sonnet']
2112
2112
  },
2113
2113
  temperature: {
2114
2114
  type: 'number',
@@ -2601,8 +2601,8 @@ export const sampleNodes = [
2601
2601
  type: 'string',
2602
2602
  title: 'AI Model',
2603
2603
  description: 'AI model to use for content analysis',
2604
- default: 'gpt-4',
2605
- enum: ['gpt-3.5-turbo', 'gpt-4', 'claude-3-sonnet', 'claude-3-haiku']
2604
+ default: 'gpt-5-chat-latest',
2605
+ enum: ['gpt-4o-mini', 'gpt-5-chat-latest', 'claude-3-sonnet', 'claude-3-haiku']
2606
2606
  },
2607
2607
  analysisType: {
2608
2608
  type: 'string',
@@ -2725,8 +2725,8 @@ export const sampleNodes = [
2725
2725
  type: 'string',
2726
2726
  title: 'AI Model',
2727
2727
  description: 'AI model to use for content editing',
2728
- default: 'gpt-4',
2729
- enum: ['gpt-3.5-turbo', 'gpt-4', 'claude-3-sonnet', 'claude-3-haiku']
2728
+ default: 'gpt-5-chat-latest',
2729
+ enum: ['gpt-4o-mini', 'gpt-5-chat-latest', 'claude-3-sonnet', 'claude-3-haiku']
2730
2730
  },
2731
2731
  editingStyle: {
2732
2732
  type: 'string',
@@ -3184,7 +3184,7 @@ export const sampleWorkflow = {
3184
3184
  data: {
3185
3185
  label: 'OpenAI',
3186
3186
  config: {
3187
- model: 'gpt-3.5-turbo',
3187
+ model: 'gpt-4o-mini',
3188
3188
  temperature: 0.7,
3189
3189
  maxTokens: 1000
3190
3190
  },
@@ -14,7 +14,7 @@ function createSimpleWorkflow() {
14
14
  placeholder: 'Enter your message'
15
15
  });
16
16
  const modelNode = adapter.addNode(workflow, 'openai', { x: 300, y: 100 }, {
17
- model: 'gpt-4',
17
+ model: 'gpt-4o-mini',
18
18
  temperature: 0.7
19
19
  });
20
20
  const outputNode = adapter.addNode(workflow, 'text-output', { x: 500, y: 100 });
package/dist/index.d.ts CHANGED
@@ -2,9 +2,13 @@
2
2
  * FlowDrop - Visual Workflow Editor Library
3
3
  * A Svelte 5 component library built on @xyflow/svelte for creating node-based workflow editors
4
4
  */
5
- import './styles/base.css';
6
- export type { NodeCategory, NodeDataType, NodePort, NodeMetadata, NodeConfig, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents } from './types/index.js';
7
- export type { WorkflowEditorConfig, EditorFeatures, UIConfig, APIConfig, ExecutionConfig, StorageConfig, NodeType, WorkflowData, ExecutionResult as EditorExecutionResult, EditorState } from './types/config.js';
5
+ import "./styles/base.css";
6
+ export type { NodeCategory, NodeDataType, NodePort, NodeMetadata, NodeConfig, WorkflowNode, WorkflowEdge, Workflow, ApiResponse, NodesResponse, WorkflowResponse, WorkflowsResponse, ExecutionStatus, ExecutionResult, FlowDropConfig, WorkflowEvents } from "./types/index.js";
7
+ export type { WorkflowEditorConfig, EditorFeatures, UIConfig, APIConfig, ExecutionConfig, StorageConfig, NodeType, WorkflowData, ExecutionResult as EditorExecutionResult, EditorState } from "./types/config.js";
8
+ export type { AuthProvider, StaticAuthConfig, CallbackAuthConfig } from "./types/auth.js";
9
+ export { StaticAuthProvider, CallbackAuthProvider, NoAuthProvider, createAuthProviderFromLegacyConfig } from "./types/auth.js";
10
+ export type { WorkflowChangeType, FlowDropEventHandlers, FlowDropFeatures } from "./types/events.js";
11
+ export { DEFAULT_FEATURES, mergeFeatures } from "./types/events.js";
8
12
  export { FlowDropApiClient } from './api/client.js';
9
13
  export { EnhancedFlowDropApiClient } from './api/enhanced-client.js';
10
14
  export { default as WorkflowEditor } from './components/WorkflowEditor.svelte';
@@ -45,9 +49,10 @@ export type { ToastType, ToastOptions } from './services/toastService.js';
45
49
  export { NodeExecutionService, nodeExecutionService } from './services/nodeExecutionService.js';
46
50
  export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
47
51
  export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
48
- export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
52
+ export { fetchPortConfig, validatePortConfig } from "./services/portConfigApi.js";
53
+ export { getDraftStorageKey, saveDraft, loadDraft, deleteDraft, hasDraft, getDraftMetadata, DraftAutoSaveManager } from "./services/draftStorage.js";
49
54
  export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from './helpers/workflowEditorHelper.js';
50
- export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged } from './stores/workflowStore.js';
55
+ export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged, isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange } from "./stores/workflowStore.js";
51
56
  export * from './config/endpoints.js';
52
57
  export { defaultApiConfig, getEndpointUrl } from './config/apiConfig.js';
53
58
  export type { ApiConfig } from './config/apiConfig.js';
@@ -55,4 +60,6 @@ export { DEFAULT_PORT_CONFIG } from './config/defaultPortConfig.js';
55
60
  export * from './config/runtimeConfig.js';
56
61
  export * from './adapters/WorkflowAdapter.js';
57
62
  export * from './clients/ApiClient.js';
58
- export { mountWorkflowEditor, unmountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from './svelte-app.js';
63
+ export { mountWorkflowEditor, unmountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from "./svelte-app.js";
64
+ export type { FlowDropMountOptions, MountedFlowDropApp, NavbarAction } from "./svelte-app.js";
65
+ export { ApiError } from "./api/enhanced-client.js";
package/dist/index.js CHANGED
@@ -3,7 +3,9 @@
3
3
  * A Svelte 5 component library built on @xyflow/svelte for creating node-based workflow editors
4
4
  */
5
5
  // Import CSS to ensure styles are included in the library build
6
- import './styles/base.css';
6
+ import "./styles/base.css";
7
+ export { StaticAuthProvider, CallbackAuthProvider, NoAuthProvider, createAuthProviderFromLegacyConfig } from "./types/auth.js";
8
+ export { DEFAULT_FEATURES, mergeFeatures } from "./types/events.js";
7
9
  // Export API clients
8
10
  export { FlowDropApiClient } from './api/client.js';
9
11
  export { EnhancedFlowDropApiClient } from './api/enhanced-client.js';
@@ -47,11 +49,15 @@ export { showSuccess, showError, showWarning, showInfo, showLoading, dismissToas
47
49
  export { NodeExecutionService, nodeExecutionService } from './services/nodeExecutionService.js';
48
50
  export { saveWorkflow, updateWorkflow, getWorkflow, getWorkflows, deleteWorkflow, getWorkflowCount, initializeSampleWorkflows } from './services/workflowStorage.js';
49
51
  export { globalSaveWorkflow, globalExportWorkflow, initializeGlobalSave } from './services/globalSave.js';
50
- export { fetchPortConfig, validatePortConfig } from './services/portConfigApi.js';
52
+ export { fetchPortConfig, validatePortConfig } from "./services/portConfigApi.js";
53
+ // Export draft storage service
54
+ export { getDraftStorageKey, saveDraft, loadDraft, deleteDraft, hasDraft, getDraftMetadata, DraftAutoSaveManager } from "./services/draftStorage.js";
51
55
  // Export helpers
52
56
  export { EdgeStylingHelper, NodeOperationsHelper, WorkflowOperationsHelper, ConfigurationHelper } from './helpers/workflowEditorHelper.js';
53
57
  // Export stores
54
- export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged } from './stores/workflowStore.js';
58
+ export { workflowStore, workflowActions, workflowId, workflowName, workflowNodes, workflowEdges, workflowMetadata, workflowChanged, workflowValidation, workflowMetadataChanged,
59
+ // Dirty state tracking
60
+ isDirtyStore, isDirty, markAsSaved, getWorkflow as getWorkflowFromStore, setOnDirtyStateChange, setOnWorkflowChange } from "./stores/workflowStore.js";
55
61
  // Export endpoint configuration
56
62
  export * from './config/endpoints.js';
57
63
  export { defaultApiConfig, getEndpointUrl } from './config/apiConfig.js';
@@ -62,4 +68,6 @@ export * from './adapters/WorkflowAdapter.js';
62
68
  // Export API client
63
69
  export * from './clients/ApiClient.js';
64
70
  // Export Svelte app wrapper for framework integration
65
- export { mountWorkflowEditor, unmountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from './svelte-app.js';
71
+ export { mountWorkflowEditor, unmountWorkflowEditor, mountFlowDropApp, unmountFlowDropApp } from "./svelte-app.js";
72
+ // Export API error class
73
+ export { ApiError } from "./api/enhanced-client.js";
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Draft Storage Service for FlowDrop
3
+ *
4
+ * Handles saving and loading workflow drafts to/from localStorage.
5
+ * Provides interval-based auto-save functionality.
6
+ *
7
+ * @module services/draftStorage
8
+ */
9
+ import type { Workflow } from "../types/index.js";
10
+ /**
11
+ * Draft metadata stored alongside the workflow
12
+ */
13
+ interface DraftMetadata {
14
+ /** Timestamp when the draft was saved */
15
+ savedAt: string;
16
+ /** Workflow ID (if available) */
17
+ workflowId?: string;
18
+ /** Workflow name */
19
+ workflowName?: string;
20
+ }
21
+ /**
22
+ * Complete draft data stored in localStorage
23
+ */
24
+ interface StoredDraft {
25
+ /** The workflow data */
26
+ workflow: Workflow;
27
+ /** Draft metadata */
28
+ metadata: DraftMetadata;
29
+ }
30
+ /**
31
+ * Generate a storage key for a workflow
32
+ *
33
+ * If a custom key is provided, use it directly.
34
+ * Otherwise, generate based on workflow ID or use "new" for unsaved workflows.
35
+ *
36
+ * @param workflowId - The workflow ID (optional)
37
+ * @param customKey - Custom storage key provided by enterprise (optional)
38
+ * @returns The storage key to use
39
+ */
40
+ export declare function getDraftStorageKey(workflowId?: string, customKey?: string): string;
41
+ /**
42
+ * Save a workflow draft to localStorage
43
+ *
44
+ * @param workflow - The workflow to save
45
+ * @param storageKey - The storage key to use
46
+ * @returns true if saved successfully, false otherwise
47
+ */
48
+ export declare function saveDraft(workflow: Workflow, storageKey: string): boolean;
49
+ /**
50
+ * Load a workflow draft from localStorage
51
+ *
52
+ * @param storageKey - The storage key to load from
53
+ * @returns The stored draft, or null if not found
54
+ */
55
+ export declare function loadDraft(storageKey: string): StoredDraft | null;
56
+ /**
57
+ * Delete a workflow draft from localStorage
58
+ *
59
+ * @param storageKey - The storage key to delete
60
+ */
61
+ export declare function deleteDraft(storageKey: string): void;
62
+ /**
63
+ * Check if a draft exists for a given storage key
64
+ *
65
+ * @param storageKey - The storage key to check
66
+ * @returns true if a draft exists
67
+ */
68
+ export declare function hasDraft(storageKey: string): boolean;
69
+ /**
70
+ * Get draft metadata without loading the full workflow
71
+ *
72
+ * Useful for displaying draft information without parsing the entire workflow.
73
+ *
74
+ * @param storageKey - The storage key to check
75
+ * @returns Draft metadata, or null if not found
76
+ */
77
+ export declare function getDraftMetadata(storageKey: string): DraftMetadata | null;
78
+ /**
79
+ * Draft auto-save manager
80
+ *
81
+ * Handles interval-based auto-saving of workflow drafts.
82
+ * Should be instantiated per FlowDrop instance.
83
+ */
84
+ export declare class DraftAutoSaveManager {
85
+ /** Interval timer ID */
86
+ private intervalId;
87
+ /** Storage key for drafts */
88
+ private storageKey;
89
+ /** Auto-save interval in milliseconds */
90
+ private interval;
91
+ /** Whether auto-save is enabled */
92
+ private enabled;
93
+ /** Function to get current workflow */
94
+ private getWorkflow;
95
+ /** Function to check if workflow is dirty */
96
+ private isDirty;
97
+ /** Last saved workflow hash (for change detection) */
98
+ private lastSavedHash;
99
+ /**
100
+ * Create a new DraftAutoSaveManager
101
+ *
102
+ * @param options - Configuration options
103
+ */
104
+ constructor(options: {
105
+ storageKey: string;
106
+ interval: number;
107
+ enabled: boolean;
108
+ getWorkflow: () => Workflow | null;
109
+ isDirty: () => boolean;
110
+ });
111
+ /**
112
+ * Start auto-save interval
113
+ *
114
+ * Will save drafts at the configured interval if there are unsaved changes.
115
+ */
116
+ start(): void;
117
+ /**
118
+ * Stop auto-save interval
119
+ */
120
+ stop(): void;
121
+ /**
122
+ * Save draft if there are unsaved changes
123
+ *
124
+ * @returns true if a draft was saved
125
+ */
126
+ saveIfDirty(): boolean;
127
+ /**
128
+ * Force save the current workflow as a draft
129
+ *
130
+ * Saves regardless of dirty state.
131
+ *
132
+ * @returns true if saved successfully
133
+ */
134
+ forceSave(): boolean;
135
+ /**
136
+ * Clear the draft from storage
137
+ */
138
+ clearDraft(): void;
139
+ /**
140
+ * Mark the current state as saved
141
+ *
142
+ * Updates the hash so the next saveIfDirty won't save unless there are new changes.
143
+ */
144
+ markAsSaved(): void;
145
+ /**
146
+ * Update the storage key
147
+ *
148
+ * Useful when the workflow ID changes (e.g., after first save).
149
+ *
150
+ * @param newKey - The new storage key
151
+ */
152
+ updateStorageKey(newKey: string): void;
153
+ /**
154
+ * Simple hash function for change detection
155
+ *
156
+ * Not cryptographically secure, just for detecting changes.
157
+ *
158
+ * @param workflow - The workflow to hash
159
+ * @returns A simple hash string
160
+ */
161
+ private hashWorkflow;
162
+ /**
163
+ * Check if auto-save is currently running
164
+ */
165
+ isRunning(): boolean;
166
+ /**
167
+ * Get the current storage key
168
+ */
169
+ getStorageKey(): string;
170
+ }
171
+ export {};