@d34dman/flowdrop 0.0.61 → 0.0.62

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 (204) hide show
  1. package/README.md +6 -0
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/agentspec/AgentSpecAdapter.js +3 -1
  4. package/dist/api/client.d.ts +4 -0
  5. package/dist/api/client.js +6 -1
  6. package/dist/api/enhanced-client.js +7 -6
  7. package/dist/components/App.svelte +143 -219
  8. package/dist/components/CanvasBanner.stories.svelte +25 -0
  9. package/dist/components/CanvasBanner.stories.svelte.d.ts +27 -0
  10. package/dist/components/CanvasBanner.svelte +2 -2
  11. package/dist/components/ConfigForm.svelte +37 -36
  12. package/dist/components/ConfigPanel.stories.svelte +38 -0
  13. package/dist/components/ConfigPanel.stories.svelte.d.ts +27 -0
  14. package/dist/components/ConfigPanel.svelte +2 -2
  15. package/dist/components/ConnectionLine.svelte +2 -2
  16. package/dist/components/FlowDropZone.svelte +18 -2
  17. package/dist/components/FlowDropZone.svelte.d.ts +2 -0
  18. package/dist/components/LoadingSpinner.stories.svelte +30 -0
  19. package/dist/components/LoadingSpinner.stories.svelte.d.ts +27 -0
  20. package/dist/components/Logo.stories.svelte +22 -0
  21. package/dist/components/Logo.stories.svelte.d.ts +27 -0
  22. package/dist/components/Logo.svelte +33 -13
  23. package/dist/components/Logo.svelte.d.ts +1 -1
  24. package/dist/components/MarkdownDisplay.stories.svelte +21 -0
  25. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +27 -0
  26. package/dist/components/MarkdownDisplay.svelte +4 -3
  27. package/dist/components/Navbar.stories.svelte +41 -0
  28. package/dist/components/Navbar.stories.svelte.d.ts +27 -0
  29. package/dist/components/Navbar.svelte +4 -4
  30. package/dist/components/NodeSidebar.svelte +12 -12
  31. package/dist/components/NodeStatusOverlay.stories.svelte +74 -0
  32. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +27 -0
  33. package/dist/components/PipelineStatus.svelte +11 -4
  34. package/dist/components/PortCoordinateTracker.svelte +1 -1
  35. package/dist/components/SchemaForm.stories.svelte +101 -0
  36. package/dist/components/SchemaForm.stories.svelte.d.ts +27 -0
  37. package/dist/components/SchemaForm.svelte +17 -12
  38. package/dist/components/SettingsModal.svelte +3 -3
  39. package/dist/components/SettingsPanel.svelte +23 -22
  40. package/dist/components/StatusIcon.stories.svelte +60 -0
  41. package/dist/components/StatusIcon.stories.svelte.d.ts +27 -0
  42. package/dist/components/StatusIcon.svelte +7 -0
  43. package/dist/components/StatusLabel.stories.svelte +17 -0
  44. package/dist/components/StatusLabel.stories.svelte.d.ts +27 -0
  45. package/dist/components/ThemeToggle.stories.svelte +25 -0
  46. package/dist/components/ThemeToggle.stories.svelte.d.ts +27 -0
  47. package/dist/components/ThemeToggle.svelte +8 -8
  48. package/dist/components/UniversalNode.svelte +1 -1
  49. package/dist/components/WorkflowEditor.svelte +298 -294
  50. package/dist/components/form/FormAutocomplete.svelte +20 -19
  51. package/dist/components/form/FormCheckboxGroup.stories.svelte +28 -0
  52. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +27 -0
  53. package/dist/components/form/FormField.svelte +3 -3
  54. package/dist/components/form/FormFieldLight.svelte +2 -2
  55. package/dist/components/form/FormFieldWrapper.stories.svelte +31 -0
  56. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +27 -0
  57. package/dist/components/form/FormFieldset.svelte +7 -7
  58. package/dist/components/form/FormNumberField.stories.svelte +33 -0
  59. package/dist/components/form/FormNumberField.stories.svelte.d.ts +27 -0
  60. package/dist/components/form/FormRangeField.stories.svelte +31 -0
  61. package/dist/components/form/FormRangeField.stories.svelte.d.ts +27 -0
  62. package/dist/components/form/FormSelect.stories.svelte +50 -0
  63. package/dist/components/form/FormSelect.stories.svelte.d.ts +27 -0
  64. package/dist/components/form/FormTemplateEditor.svelte +2 -1
  65. package/dist/components/form/FormTextField.stories.svelte +30 -0
  66. package/dist/components/form/FormTextField.stories.svelte.d.ts +27 -0
  67. package/dist/components/form/FormTextarea.stories.svelte +31 -0
  68. package/dist/components/form/FormTextarea.stories.svelte.d.ts +27 -0
  69. package/dist/components/form/FormToggle.stories.svelte +30 -0
  70. package/dist/components/form/FormToggle.stories.svelte.d.ts +27 -0
  71. package/dist/components/form/FormUISchemaRenderer.svelte +1 -1
  72. package/dist/components/form/types.d.ts +15 -47
  73. package/dist/components/interrupt/ChoicePrompt.stories.svelte +43 -0
  74. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +27 -0
  75. package/dist/components/interrupt/ChoicePrompt.svelte +24 -24
  76. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +49 -0
  77. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +27 -0
  78. package/dist/components/interrupt/ConfirmationPrompt.svelte +19 -19
  79. package/dist/components/interrupt/FormPrompt.svelte +15 -15
  80. package/dist/components/interrupt/InterruptBubble.svelte +202 -236
  81. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +1 -1
  82. package/dist/components/interrupt/ReviewPrompt.stories.svelte +46 -0
  83. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +27 -0
  84. package/dist/components/interrupt/ReviewPrompt.svelte +842 -0
  85. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +23 -0
  86. package/dist/components/interrupt/TextInputPrompt.stories.svelte +34 -0
  87. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +27 -0
  88. package/dist/components/interrupt/TextInputPrompt.svelte +21 -21
  89. package/dist/components/nodes/GatewayNode.stories.svelte +76 -0
  90. package/dist/components/nodes/GatewayNode.stories.svelte.d.ts +26 -0
  91. package/dist/components/nodes/GatewayNode.svelte +19 -17
  92. package/dist/components/nodes/IdeaNode.stories.svelte +48 -0
  93. package/dist/components/nodes/IdeaNode.stories.svelte.d.ts +26 -0
  94. package/dist/components/nodes/IdeaNode.svelte +10 -26
  95. package/dist/components/nodes/NotesNode.stories.svelte +69 -0
  96. package/dist/components/nodes/NotesNode.stories.svelte.d.ts +26 -0
  97. package/dist/components/nodes/NotesNode.svelte +8 -8
  98. package/dist/components/nodes/SimpleNode.stories.svelte +101 -0
  99. package/dist/components/nodes/SimpleNode.stories.svelte.d.ts +26 -0
  100. package/dist/components/nodes/SimpleNode.svelte +16 -24
  101. package/dist/components/nodes/SquareNode.stories.svelte +56 -0
  102. package/dist/components/nodes/SquareNode.stories.svelte.d.ts +26 -0
  103. package/dist/components/nodes/SquareNode.svelte +13 -21
  104. package/dist/components/nodes/TerminalNode.stories.svelte +25 -0
  105. package/dist/components/nodes/TerminalNode.stories.svelte.d.ts +26 -0
  106. package/dist/components/nodes/TerminalNode.svelte +6 -6
  107. package/dist/components/nodes/ToolNode.stories.svelte +71 -0
  108. package/dist/components/nodes/ToolNode.stories.svelte.d.ts +26 -0
  109. package/dist/components/nodes/ToolNode.svelte +7 -15
  110. package/dist/components/nodes/WorkflowNode.stories.svelte +50 -0
  111. package/dist/components/nodes/WorkflowNode.stories.svelte.d.ts +26 -0
  112. package/dist/components/nodes/WorkflowNode.svelte +13 -13
  113. package/dist/components/playground/ChatPanel.svelte +48 -48
  114. package/dist/components/playground/ExecutionLogs.svelte +23 -23
  115. package/dist/components/playground/InputCollector.svelte +24 -24
  116. package/dist/components/playground/MessageBubble.stories.svelte +49 -0
  117. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +27 -0
  118. package/dist/components/playground/MessageBubble.svelte +49 -46
  119. package/dist/components/playground/Playground.svelte +194 -129
  120. package/dist/components/playground/PlaygroundModal.svelte +5 -5
  121. package/dist/components/playground/SessionManager.svelte +26 -26
  122. package/dist/config/constants.d.ts +22 -0
  123. package/dist/config/constants.js +22 -0
  124. package/dist/config/endpoints.d.ts +19 -0
  125. package/dist/config/runtimeConfig.js +2 -1
  126. package/dist/core/index.d.ts +5 -2
  127. package/dist/core/index.js +9 -1
  128. package/dist/editor/index.d.ts +13 -9
  129. package/dist/editor/index.js +15 -11
  130. package/dist/form/code.d.ts +2 -1
  131. package/dist/form/code.js +1 -3
  132. package/dist/form/markdown.d.ts +2 -1
  133. package/dist/form/markdown.js +1 -3
  134. package/dist/helpers/workflowEditorHelper.js +13 -9
  135. package/dist/mocks/app-forms.js +1 -0
  136. package/dist/mocks/app-navigation.js +3 -1
  137. package/dist/mocks/app-stores.d.ts +4 -4
  138. package/dist/playground/index.d.ts +4 -3
  139. package/dist/playground/index.js +12 -10
  140. package/dist/playground/mount.js +6 -13
  141. package/dist/services/agentSpecExecutionService.js +2 -1
  142. package/dist/services/api.js +10 -18
  143. package/dist/services/apiVariableService.js +2 -1
  144. package/dist/services/autoSaveService.d.ts +3 -3
  145. package/dist/services/autoSaveService.js +21 -17
  146. package/dist/services/categoriesApi.js +13 -5
  147. package/dist/services/draftStorage.js +5 -4
  148. package/dist/services/dynamicSchemaService.js +4 -4
  149. package/dist/services/globalSave.d.ts +60 -11
  150. package/dist/services/globalSave.js +160 -83
  151. package/dist/services/historyService.d.ts +2 -1
  152. package/dist/services/historyService.js +7 -3
  153. package/dist/services/interruptService.js +9 -8
  154. package/dist/services/nodeExecutionService.js +14 -6
  155. package/dist/services/playgroundService.js +2 -1
  156. package/dist/services/portConfigApi.js +11 -7
  157. package/dist/services/toastService.d.ts +1 -1
  158. package/dist/services/toastService.js +6 -5
  159. package/dist/services/variableService.js +3 -2
  160. package/dist/settings/index.d.ts +1 -1
  161. package/dist/settings/index.js +1 -1
  162. package/dist/stores/{categoriesStore.d.ts → categoriesStore.svelte.d.ts} +3 -3
  163. package/dist/stores/{categoriesStore.js → categoriesStore.svelte.js} +15 -18
  164. package/dist/stores/editorStateMachine.svelte.d.ts +42 -0
  165. package/dist/stores/editorStateMachine.svelte.js +132 -0
  166. package/dist/stores/{historyStore.d.ts → historyStore.svelte.d.ts} +18 -15
  167. package/dist/stores/{historyStore.js → historyStore.svelte.js} +40 -21
  168. package/dist/stores/{interruptStore.d.ts → interruptStore.svelte.d.ts} +16 -15
  169. package/dist/stores/{interruptStore.js → interruptStore.svelte.js} +85 -94
  170. package/dist/stores/{playgroundStore.d.ts → playgroundStore.svelte.d.ts} +41 -33
  171. package/dist/stores/{playgroundStore.js → playgroundStore.svelte.js} +164 -84
  172. package/dist/stores/{portCoordinateStore.d.ts → portCoordinateStore.svelte.d.ts} +10 -4
  173. package/dist/stores/{portCoordinateStore.js → portCoordinateStore.svelte.js} +38 -35
  174. package/dist/stores/{settingsStore.d.ts → settingsStore.svelte.d.ts} +45 -28
  175. package/dist/stores/{settingsStore.js → settingsStore.svelte.js} +169 -128
  176. package/dist/stores/{workflowStore.d.ts → workflowStore.svelte.d.ts} +101 -65
  177. package/dist/stores/{workflowStore.js → workflowStore.svelte.js} +285 -239
  178. package/dist/stories/CanvasDecorator.svelte +50 -0
  179. package/dist/stories/CanvasDecorator.svelte.d.ts +8 -0
  180. package/dist/stories/NodeDecorator.svelte +74 -0
  181. package/dist/stories/NodeDecorator.svelte.d.ts +8 -0
  182. package/dist/stories/utils.d.ts +93 -0
  183. package/dist/stories/utils.js +122 -0
  184. package/dist/styles/base.css +114 -61
  185. package/dist/styles/toast.css +2 -2
  186. package/dist/styles/tokens.css +250 -185
  187. package/dist/svelte-app.d.ts +0 -6
  188. package/dist/svelte-app.js +13 -31
  189. package/dist/types/index.d.ts +2 -0
  190. package/dist/types/interrupt.d.ts +89 -5
  191. package/dist/types/interrupt.js +13 -1
  192. package/dist/types/playground.d.ts +5 -0
  193. package/dist/types/settings.js +1 -1
  194. package/dist/utils/colors.js +4 -4
  195. package/dist/utils/connections.js +33 -8
  196. package/dist/utils/icons.js +1 -1
  197. package/dist/utils/logger.d.ts +47 -0
  198. package/dist/utils/logger.js +72 -0
  199. package/dist/utils/nodeWrapper.js +1 -1
  200. package/dist/utils/sanitize.d.ts +19 -0
  201. package/dist/utils/sanitize.js +31 -0
  202. package/dist/utils/validation.d.ts +29 -0
  203. package/dist/utils/validation.js +39 -0
  204. package/package.json +243 -232
@@ -136,30 +136,32 @@ export { InterruptService, interruptService } from '../services/interruptService
136
136
  // Playground Store
137
137
  // ============================================================================
138
138
  export {
139
- // Core stores
140
- currentSession, sessions, messages, isExecuting, isLoading, error as playgroundError, currentWorkflow, lastPollTimestamp,
141
- // Derived stores
142
- sessionStatus, messageCount, chatMessages, logMessages, latestMessage, inputFields, hasChatInput, sessionCount,
139
+ // Core state getters
140
+ getCurrentSession, getSessions, getMessages, getIsExecuting, getIsLoading, getError as getPlaygroundError, getCurrentWorkflow, getLastPollTimestamp,
141
+ // Derived getters
142
+ getSessionStatus, getMessageCount, getChatMessages, getLogMessages, getLatestMessage, getInputFields, getHasChatInput, getSessionCount,
143
143
  // Actions
144
144
  playgroundActions,
145
145
  // Polling callback factory
146
146
  createPollingCallback,
147
+ // Subscription utility
148
+ subscribeToSessionStatus,
147
149
  // Utilities
148
- getCurrentSessionId, isSessionSelected, getMessagesSnapshot, getLatestMessageTimestamp } from '../stores/playgroundStore.js';
150
+ getCurrentSessionId, isSessionSelected, getMessagesSnapshot, getLatestMessageTimestamp } from '../stores/playgroundStore.svelte.js';
149
151
  export { isChatInputNode, CHAT_INPUT_PATTERNS, defaultShouldStopPolling, defaultIsTerminalStatus, DEFAULT_STOP_POLLING_STATUSES, DEFAULT_TERMINAL_STATUSES } from '../types/playground.js';
150
152
  export { isInterruptMetadata, extractInterruptMetadata, metadataToInterrupt, defaultInterruptPollingConfig } from '../types/interrupt.js';
151
153
  // ============================================================================
152
154
  // Interrupt Store (Human-in-the-Loop)
153
155
  // ============================================================================
154
156
  export {
155
- // Core stores
156
- interrupts,
157
- // Derived stores
158
- pendingInterruptIds, pendingInterrupts, pendingInterruptCount, resolvedInterrupts, isAnySubmitting,
157
+ // Core state accessor
158
+ getInterruptsMap,
159
+ // Getter functions (replace derived stores)
160
+ getPendingInterruptIds, getPendingInterrupts, getPendingInterruptCount, getResolvedInterrupts, getIsAnySubmitting,
159
161
  // Actions
160
162
  interruptActions,
161
163
  // Utilities
162
- getInterrupt, isInterruptPending, isInterruptSubmitting, getInterruptError, getInterruptByMessageId } from '../stores/interruptStore.js';
164
+ getInterrupt, isInterruptPending, isInterruptSubmitting, getInterruptError, getInterruptByMessageId, interruptHasError } from '../stores/interruptStore.svelte.js';
163
165
  // ============================================================================
164
166
  // Playground Mount Functions (for vanilla JS / Drupal / IIFE integration)
165
167
  // ============================================================================
@@ -48,8 +48,7 @@ import Playground from '../components/playground/Playground.svelte';
48
48
  import PlaygroundModal from '../components/playground/PlaygroundModal.svelte';
49
49
  import { setEndpointConfig } from '../services/api.js';
50
50
  import { playgroundService } from '../services/playgroundService.js';
51
- import { currentSession, sessions, messages, sessionStatus, playgroundActions, createPollingCallback } from '../stores/playgroundStore.js';
52
- import { get } from 'svelte/store';
51
+ import { getCurrentSession, getSessions, getMessages, playgroundActions, createPollingCallback, subscribeToSessionStatus } from '../stores/playgroundStore.svelte.js';
53
52
  /**
54
53
  * Mount the Playground component in a container
55
54
  *
@@ -169,13 +168,7 @@ export async function mountPlayground(container, options) {
169
168
  // Subscribe to session status changes if callback provided
170
169
  let unsubscribeStatus;
171
170
  if (onSessionStatusChange) {
172
- let previousStatus = get(sessionStatus);
173
- unsubscribeStatus = sessionStatus.subscribe((status) => {
174
- if (status !== previousStatus) {
175
- onSessionStatusChange(status, previousStatus);
176
- previousStatus = status;
177
- }
178
- });
171
+ unsubscribeStatus = subscribeToSessionStatus(onSessionStatusChange);
179
172
  }
180
173
  // Create the mounted playground interface
181
174
  const mountedPlayground = {
@@ -190,13 +183,13 @@ export async function mountPlayground(container, options) {
190
183
  unmount(state.svelteApp);
191
184
  },
192
185
  getCurrentSession: () => {
193
- return get(currentSession);
186
+ return getCurrentSession();
194
187
  },
195
188
  getSessions: () => {
196
- return get(sessions);
189
+ return getSessions();
197
190
  },
198
191
  getMessageCount: () => {
199
- return get(messages).length;
192
+ return getMessages().length;
200
193
  },
201
194
  isExecuting: () => {
202
195
  return playgroundService.isPolling();
@@ -205,7 +198,7 @@ export async function mountPlayground(container, options) {
205
198
  playgroundService.stopPolling();
206
199
  },
207
200
  startPolling: () => {
208
- const session = get(currentSession);
201
+ const session = getCurrentSession();
209
202
  if (session) {
210
203
  playgroundService.startPolling(session.id, pollingCallback, pollingInterval, config.shouldStopPolling);
211
204
  }
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { buildAgentSpecUrl, getAgentSpecAuthHeaders } from '../config/agentSpecEndpoints.js';
10
10
  import { AgentSpecAdapter } from '../adapters/agentspec/AgentSpecAdapter.js';
11
+ import { logger } from '../utils/logger.js';
11
12
  /**
12
13
  * Service for executing FlowDrop workflows on Agent Spec runtimes.
13
14
  *
@@ -271,7 +272,7 @@ export class AgentSpecExecutionService {
271
272
  }
272
273
  catch (error) {
273
274
  // Don't stop polling on transient errors — let it retry
274
- console.error('[AgentSpecExecution] Polling error:', error);
275
+ logger.error('[AgentSpecExecution] Polling error:', error);
275
276
  }
276
277
  };
277
278
  // Initial poll immediately, then at interval
@@ -142,16 +142,13 @@ export const workflowApi = {
142
142
  if (!endpointConfig) {
143
143
  throw new Error('Endpoint configuration not set');
144
144
  }
145
- // Transform workflow data for Drupal backend compatibility
146
- // Drupal expects "label" instead of "name"
147
- const drupalWorkflow = {
148
- ...workflow,
149
- label: workflow.name, // Map name to label for Drupal
150
- name: workflow.name // Keep name as well for compatibility
151
- };
145
+ // Apply the consumer-provided payload transform (e.g. Drupal's label mapping).
146
+ // The default is the identity function — no CMS-specific logic in the core library.
147
+ const transform = endpointConfig.transformWorkflowPayload ?? ((w) => w);
148
+ const body = transform(workflow);
152
149
  const response = await apiRequest('workflows.create', endpointConfig.endpoints.workflows.create, undefined, {
153
150
  method: 'POST',
154
- body: JSON.stringify(drupalWorkflow)
151
+ body: JSON.stringify(body)
155
152
  });
156
153
  if (!response.data) {
157
154
  throw new Error('Failed to create workflow');
@@ -165,18 +162,13 @@ export const workflowApi = {
165
162
  if (!endpointConfig) {
166
163
  throw new Error('Endpoint configuration not set');
167
164
  }
168
- // Transform workflow data for Drupal backend compatibility
169
- // Drupal expects "label" instead of "name"
170
- const drupalWorkflow = workflow.name
171
- ? {
172
- ...workflow,
173
- label: workflow.name, // Map name to label for Drupal
174
- name: workflow.name // Keep name as well for compatibility
175
- }
176
- : workflow;
165
+ // Apply the consumer-provided payload transform (e.g. Drupal's label mapping).
166
+ // The default is the identity function — no CMS-specific logic in the core library.
167
+ const transform = endpointConfig.transformWorkflowPayload ?? ((w) => w);
168
+ const body = transform(workflow);
177
169
  const response = await apiRequest('workflows.update', endpointConfig.endpoints.workflows.update, { id }, {
178
170
  method: 'PUT',
179
- body: JSON.stringify(drupalWorkflow)
171
+ body: JSON.stringify(body)
180
172
  });
181
173
  if (!response.data) {
182
174
  throw new Error('Failed to update workflow');
@@ -6,6 +6,7 @@
6
6
  * @module services/apiVariableService
7
7
  */
8
8
  import { getEndpointConfig } from './api.js';
9
+ import { logger } from '../utils/logger.js';
9
10
  /**
10
11
  * Variable schema cache with TTL support
11
12
  * Key format: `variables:{workflowId}:{nodeId}`
@@ -158,7 +159,7 @@ export async function fetchVariableSchema(workflowId, nodeId, config, authProvid
158
159
  Object.assign(headers, authHeaders);
159
160
  }
160
161
  catch (error) {
161
- console.warn('Failed to get auth headers:', error);
162
+ logger.warn('Failed to get auth headers:', error);
162
163
  }
163
164
  }
164
165
  // Add auth headers from endpoint config as fallback
@@ -28,8 +28,8 @@ interface AutoSaveOptions {
28
28
  * Initialize auto-save functionality based on user settings
29
29
  *
30
30
  * Creates an interval-based auto-save mechanism that:
31
- * - Subscribes to behaviorSettings for auto-save configuration
32
- * - Monitors the isDirtyStore to check for unsaved changes
31
+ * - Listens to behaviorSettings changes for auto-save configuration
32
+ * - Monitors the isDirty state to check for unsaved changes
33
33
  * - Calls the provided save callback when dirty and auto-save is enabled
34
34
  *
35
35
  * @param options - Auto-save configuration options
@@ -77,7 +77,7 @@ export declare class AutoSaveManager {
77
77
  /**
78
78
  * Start the auto-save manager
79
79
  *
80
- * Subscribes to settings changes and starts the auto-save interval.
80
+ * Listens for settings changes and starts the auto-save interval.
81
81
  */
82
82
  start(): void;
83
83
  /**
@@ -6,15 +6,15 @@
6
6
  *
7
7
  * @module services/autoSaveService
8
8
  */
9
- import { get } from 'svelte/store';
10
- import { behaviorSettings } from '../stores/settingsStore.js';
11
- import { isDirtyStore, isDirty } from '../stores/workflowStore.js';
9
+ import { getBehaviorSettings, onSettingsChange } from '../stores/settingsStore.svelte.js';
10
+ import { isDirty } from '../stores/workflowStore.svelte.js';
11
+ import { logger } from '../utils/logger.js';
12
12
  /**
13
13
  * Initialize auto-save functionality based on user settings
14
14
  *
15
15
  * Creates an interval-based auto-save mechanism that:
16
- * - Subscribes to behaviorSettings for auto-save configuration
17
- * - Monitors the isDirtyStore to check for unsaved changes
16
+ * - Listens to behaviorSettings changes for auto-save configuration
17
+ * - Monitors the isDirty state to check for unsaved changes
18
18
  * - Calls the provided save callback when dirty and auto-save is enabled
19
19
  *
20
20
  * @param options - Auto-save configuration options
@@ -62,7 +62,7 @@ export function initAutoSave(options) {
62
62
  }
63
63
  catch (error) {
64
64
  const err = error instanceof Error ? error : new Error(String(error));
65
- console.error('Auto-save failed:', err);
65
+ logger.error('Auto-save failed:', err);
66
66
  onError?.(err);
67
67
  }
68
68
  finally {
@@ -78,7 +78,7 @@ export function initAutoSave(options) {
78
78
  clearInterval(state.intervalId);
79
79
  state.intervalId = null;
80
80
  }
81
- const settings = get(behaviorSettings);
81
+ const settings = getBehaviorSettings();
82
82
  // Start new interval if auto-save is enabled
83
83
  if (settings.autoSave) {
84
84
  state.intervalId = setInterval(() => {
@@ -86,9 +86,11 @@ export function initAutoSave(options) {
86
86
  }, settings.autoSaveInterval);
87
87
  }
88
88
  }
89
- // Subscribe to settings changes to react to auto-save toggle/interval changes
90
- state.settingsUnsubscriber = behaviorSettings.subscribe(() => {
91
- updateAutoSaveInterval();
89
+ // Listen for settings changes to react to auto-save toggle/interval changes
90
+ state.settingsUnsubscriber = onSettingsChange((event) => {
91
+ if (event.category === 'behavior') {
92
+ updateAutoSaveInterval();
93
+ }
92
94
  });
93
95
  // Initial setup
94
96
  updateAutoSaveInterval();
@@ -101,7 +103,7 @@ export function initAutoSave(options) {
101
103
  clearInterval(state.intervalId);
102
104
  state.intervalId = null;
103
105
  }
104
- // Unsubscribe from stores
106
+ // Unsubscribe from settings changes
105
107
  if (state.settingsUnsubscriber) {
106
108
  state.settingsUnsubscriber();
107
109
  state.settingsUnsubscriber = null;
@@ -138,14 +140,16 @@ export class AutoSaveManager {
138
140
  /**
139
141
  * Start the auto-save manager
140
142
  *
141
- * Subscribes to settings changes and starts the auto-save interval.
143
+ * Listens for settings changes and starts the auto-save interval.
142
144
  */
143
145
  start() {
144
146
  if (this.settingsUnsubscriber) {
145
147
  return; // Already started
146
148
  }
147
- this.settingsUnsubscriber = behaviorSettings.subscribe(() => {
148
- this.updateInterval();
149
+ this.settingsUnsubscriber = onSettingsChange((event) => {
150
+ if (event.category === 'behavior') {
151
+ this.updateInterval();
152
+ }
149
153
  });
150
154
  this.updateInterval();
151
155
  }
@@ -176,7 +180,7 @@ export class AutoSaveManager {
176
180
  * Check if auto-save is currently enabled
177
181
  */
178
182
  isEnabled() {
179
- return get(behaviorSettings).autoSave;
183
+ return getBehaviorSettings().autoSave;
180
184
  }
181
185
  /**
182
186
  * Check if auto-save is currently running
@@ -192,7 +196,7 @@ export class AutoSaveManager {
192
196
  clearInterval(this.intervalId);
193
197
  this.intervalId = null;
194
198
  }
195
- const settings = get(behaviorSettings);
199
+ const settings = getBehaviorSettings();
196
200
  if (settings.autoSave) {
197
201
  this.intervalId = setInterval(() => {
198
202
  this.performSave();
@@ -213,7 +217,7 @@ export class AutoSaveManager {
213
217
  }
214
218
  catch (error) {
215
219
  const err = error instanceof Error ? error : new Error(String(error));
216
- console.error('Auto-save failed:', err);
220
+ logger.error('Auto-save failed:', err);
217
221
  this.onError?.(err);
218
222
  }
219
223
  finally {
@@ -2,23 +2,31 @@
2
2
  * Categories API Service
3
3
  * Handles fetching category definitions from the backend
4
4
  */
5
+ import { buildEndpointUrl } from '../config/endpoints.js';
5
6
  import { DEFAULT_CATEGORIES } from '../config/defaultCategories.js';
6
- import { FlowDropApiClient } from '../api/client.js';
7
+ import { logger } from '../utils/logger.js';
7
8
  /**
8
9
  * Fetch category definitions from API
9
10
  */
10
11
  export async function fetchCategories(endpointConfig) {
11
12
  try {
12
- const client = new FlowDropApiClient(endpointConfig.baseUrl);
13
- const categories = await client.getCategories();
13
+ const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.categories);
14
+ const response = await fetch(url, {
15
+ headers: { 'Content-Type': 'application/json' }
16
+ });
17
+ if (!response.ok) {
18
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
19
+ }
20
+ const data = await response.json();
21
+ const categories = data.data ?? data;
14
22
  if (!categories || !Array.isArray(categories)) {
15
- console.warn('Invalid categories received from API, using default');
23
+ logger.warn('Invalid categories received from API, using default');
16
24
  return DEFAULT_CATEGORIES;
17
25
  }
18
26
  return categories;
19
27
  }
20
28
  catch (error) {
21
- console.error('Error fetching categories:', error);
29
+ logger.error('Error fetching categories:', error);
22
30
  return DEFAULT_CATEGORIES;
23
31
  }
24
32
  }
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * @module services/draftStorage
8
8
  */
9
+ import { logger } from '../utils/logger.js';
9
10
  /**
10
11
  * Default storage key prefix
11
12
  */
@@ -51,7 +52,7 @@ export function saveDraft(workflow, storageKey) {
51
52
  }
52
53
  catch (error) {
53
54
  // localStorage might be full or disabled
54
- console.warn('Failed to save draft to localStorage:', error);
55
+ logger.warn('Failed to save draft to localStorage:', error);
55
56
  return false;
56
57
  }
57
58
  }
@@ -70,13 +71,13 @@ export function loadDraft(storageKey) {
70
71
  const draft = JSON.parse(stored);
71
72
  // Validate the draft structure
72
73
  if (!draft.workflow || !draft.metadata) {
73
- console.warn('Invalid draft structure in localStorage');
74
+ logger.warn('Invalid draft structure in localStorage');
74
75
  return null;
75
76
  }
76
77
  return draft;
77
78
  }
78
79
  catch (error) {
79
- console.warn('Failed to load draft from localStorage:', error);
80
+ logger.warn('Failed to load draft from localStorage:', error);
80
81
  return null;
81
82
  }
82
83
  }
@@ -90,7 +91,7 @@ export function deleteDraft(storageKey) {
90
91
  localStorage.removeItem(storageKey);
91
92
  }
92
93
  catch (error) {
93
- console.warn('Failed to delete draft from localStorage:', error);
94
+ logger.warn('Failed to delete draft from localStorage:', error);
94
95
  }
95
96
  }
96
97
  /**
@@ -351,15 +351,15 @@ export function getEffectiveConfigEditOptions(node) {
351
351
  // Deep merge external edit link
352
352
  externalEditLink: (instanceConfig.externalEditLink ?? typeConfig.externalEditLink)
353
353
  ? {
354
- ...typeConfig.externalEditLink,
355
- ...instanceConfig.externalEditLink
354
+ ...(typeConfig.externalEditLink ?? {}),
355
+ ...(instanceConfig.externalEditLink ?? {})
356
356
  }
357
357
  : undefined,
358
358
  // Deep merge dynamic schema
359
359
  dynamicSchema: (instanceConfig.dynamicSchema ?? typeConfig.dynamicSchema)
360
360
  ? {
361
- ...typeConfig.dynamicSchema,
362
- ...instanceConfig.dynamicSchema
361
+ ...(typeConfig.dynamicSchema ?? {}),
362
+ ...(instanceConfig.dynamicSchema ?? {})
363
363
  }
364
364
  : undefined
365
365
  };
@@ -1,20 +1,69 @@
1
1
  /**
2
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
3
+ * Provides save and export functionality that can be accessed from anywhere in the app.
4
+ * This is the single source of truth for save/export logic.
5
+ *
6
+ * App.svelte delegates to globalSaveWorkflow() / globalExportWorkflow() rather than
7
+ * reimplementing the logic, ensuring "blur active element" flushing and metadata
8
+ * construction happen in exactly one place.
5
9
  */
10
+ import type { Workflow } from '../types/index.js';
11
+ import type { FlowDropEventHandlers, FlowDropFeatures } from '../types/events.js';
6
12
  /**
7
- * Global save function that can be called from anywhere
8
- * Uses the current workflow from the global store
13
+ * Minimal interface for the enhanced API client used when an authProvider is present.
14
+ * Matches the surface of EnhancedFlowDropApiClient that save needs.
9
15
  */
10
- export declare function globalSaveWorkflow(): Promise<void>;
16
+ export interface SaveApiClient {
17
+ saveWorkflow(workflow: Workflow): Promise<Workflow>;
18
+ updateWorkflow(id: string, workflow: Workflow): Promise<Workflow>;
19
+ }
11
20
  /**
12
- * Global export function that can be called from anywhere
13
- * Uses the current workflow from the global store
21
+ * Options accepted by globalSaveWorkflow().
22
+ * All fields are optional omitting them falls back to the basic behaviour
23
+ * (no event handlers, always show toasts, legacy workflowApi).
14
24
  */
15
- export declare function globalExportWorkflow(): Promise<void>;
25
+ export interface GlobalSaveOptions {
26
+ /** Enhanced API client with authProvider support. Falls back to legacy workflowApi when absent. */
27
+ apiClient?: SaveApiClient | null;
28
+ /** Event handler hooks (onBeforeSave, onAfterSave, onSaveError, onApiError). */
29
+ eventHandlers?: FlowDropEventHandlers;
30
+ /** Feature flags (showToasts). Defaults to DEFAULT_FEATURES. */
31
+ features?: Partial<FlowDropFeatures>;
32
+ /**
33
+ * Callback invoked after a successful save to clear the dirty state.
34
+ * Pass workflowStore's markAsSaved here when calling from App.svelte.
35
+ */
36
+ onMarkAsSaved?: () => void;
37
+ }
16
38
  /**
17
- * Initialize global save functions on window object for external access
18
- * This allows the functions to be called from anywhere in the application
39
+ * Options accepted by globalExportWorkflow().
19
40
  */
20
- export declare function initializeGlobalSave(): void;
41
+ export interface GlobalExportOptions {
42
+ /** Feature flags (showToasts). Defaults to DEFAULT_FEATURES. */
43
+ features?: Partial<FlowDropFeatures>;
44
+ }
45
+ /**
46
+ * Save the current workflow to the backend.
47
+ *
48
+ * This is the single source of truth for save logic. App.svelte delegates
49
+ * to this function rather than reimplementing the steps.
50
+ *
51
+ * Steps performed:
52
+ * 1. Flush pending form changes (blur active element + tick)
53
+ * 2. Optionally call onBeforeSave — return false cancels the save
54
+ * 3. Build the canonical Workflow object (preserving metadata, format, etc.)
55
+ * 4. Persist via enhanced apiClient or legacy workflowApi
56
+ * 5. Update the store if the server assigned a new ID
57
+ * 6. Call onMarkAsSaved / onAfterSave hooks
58
+ * 7. Show toast notifications (respecting features.showToasts)
59
+ */
60
+ export declare function globalSaveWorkflow(options?: GlobalSaveOptions): Promise<void>;
61
+ /**
62
+ * Export the current workflow as a downloadable JSON file.
63
+ *
64
+ * This is the single source of truth for export logic. App.svelte delegates
65
+ * to this function rather than reimplementing the steps.
66
+ *
67
+ * Preserves all metadata fields (format, tags, etc.) consistently with save.
68
+ */
69
+ export declare function globalExportWorkflow(options?: GlobalExportOptions): Promise<void>;