@flowdrop/flowdrop 1.15.0 → 2.0.0-beta.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 (215) hide show
  1. package/CHANGELOG.md +475 -0
  2. package/MIGRATION-2.0.md +472 -0
  3. package/README.md +23 -23
  4. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  5. package/dist/adapters/WorkflowAdapter.js +14 -8
  6. package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
  7. package/dist/chat/batchFeedback.d.ts +39 -0
  8. package/dist/chat/batchFeedback.js +51 -0
  9. package/dist/commands/executor.js +15 -1
  10. package/dist/commands/storeIntegration.svelte.d.ts +4 -1
  11. package/dist/commands/storeIntegration.svelte.js +26 -21
  12. package/dist/commands/types.d.ts +2 -0
  13. package/dist/components/App.svelte +162 -192
  14. package/dist/components/App.svelte.d.ts +47 -8
  15. package/dist/components/ConfigForm.svelte +71 -47
  16. package/dist/components/ConfigModal.svelte +7 -2
  17. package/dist/components/ConnectionLine.svelte +4 -2
  18. package/dist/components/Navbar.svelte +61 -1
  19. package/dist/components/NodeSidebar.svelte +27 -45
  20. package/dist/components/NodeStatusOverlay.svelte +94 -6
  21. package/dist/components/NodeSwapPicker.svelte +10 -8
  22. package/dist/components/PipelineStatus.svelte +16 -67
  23. package/dist/components/PortCoordinateTracker.svelte +5 -6
  24. package/dist/components/SchemaForm.stories.svelte +1 -3
  25. package/dist/components/SchemaForm.svelte +18 -25
  26. package/dist/components/SchemaForm.svelte.d.ts +0 -8
  27. package/dist/components/SettingsModal.svelte +8 -3
  28. package/dist/components/SettingsPanel.svelte +20 -4
  29. package/dist/components/SwapMappingEditor.svelte +67 -49
  30. package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
  31. package/dist/components/UniversalNode.svelte +9 -7
  32. package/dist/components/WorkflowEditor.svelte +118 -111
  33. package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
  34. package/dist/components/chat/AIChatPanel.svelte +93 -89
  35. package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
  36. package/dist/components/chat/CommandPreview.svelte +2 -1
  37. package/dist/components/console/CommandConsole.svelte +7 -5
  38. package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
  39. package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
  40. package/dist/components/console/ConsoleInput.svelte +15 -6
  41. package/dist/components/console/ConsoleOutput.svelte +2 -1
  42. package/dist/components/form/FormArray.svelte +5 -9
  43. package/dist/components/form/FormArray.svelte.d.ts +2 -1
  44. package/dist/components/form/FormAutocomplete.svelte +8 -6
  45. package/dist/components/form/FormField.svelte +4 -2
  46. package/dist/components/form/FormFieldLight.svelte +4 -2
  47. package/dist/components/form/FormMarkdownEditor.svelte +9 -4
  48. package/dist/components/form/FormRangeField.svelte +1 -0
  49. package/dist/components/form/FormTemplateEditor.svelte +11 -3
  50. package/dist/components/form/FormToggle.svelte +5 -12
  51. package/dist/components/form/FormToggle.svelte.d.ts +4 -2
  52. package/dist/components/form/templateAutocomplete.js +1 -5
  53. package/dist/components/form/types.d.ts +1 -14
  54. package/dist/components/interrupt/FormPrompt.svelte +3 -2
  55. package/dist/components/interrupt/InterruptBubble.svelte +16 -17
  56. package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
  57. package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
  58. package/dist/components/layouts/MainLayout.svelte +20 -13
  59. package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
  60. package/dist/components/nodes/AtomNode.svelte +17 -5
  61. package/dist/components/nodes/GatewayNode.svelte +19 -10
  62. package/dist/components/nodes/IdeaNode.svelte +7 -0
  63. package/dist/components/nodes/SimpleNode.svelte +11 -6
  64. package/dist/components/nodes/SquareNode.svelte +15 -8
  65. package/dist/components/nodes/TerminalNode.svelte +9 -4
  66. package/dist/components/nodes/ToolNode.svelte +7 -1
  67. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  68. package/dist/components/playground/ChatInput.svelte +11 -14
  69. package/dist/components/playground/ChatPanel.svelte +6 -49
  70. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  71. package/dist/components/playground/ControlPanel.svelte +134 -123
  72. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  73. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  74. package/dist/components/playground/InputCollector.svelte +11 -9
  75. package/dist/components/playground/MessageStream.svelte +17 -23
  76. package/dist/components/playground/PipelineKanbanView.svelte +65 -6
  77. package/dist/components/playground/PipelinePanel.svelte +11 -5
  78. package/dist/components/playground/PipelineTableView.svelte +186 -44
  79. package/dist/components/playground/Playground.svelte +90 -92
  80. package/dist/components/playground/Playground.svelte.d.ts +2 -0
  81. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  82. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  83. package/dist/components/playground/PlaygroundModal.svelte +13 -3
  84. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  85. package/dist/components/playground/PlaygroundStudio.svelte +34 -32
  86. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  87. package/dist/components/playground/SessionManager.svelte +9 -12
  88. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
  89. package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
  90. package/dist/config/endpoints.d.ts +0 -7
  91. package/dist/config/endpoints.js +2 -10
  92. package/dist/core/index.d.ts +4 -4
  93. package/dist/core/index.js +6 -6
  94. package/dist/display/index.d.ts +0 -2
  95. package/dist/display/index.js +0 -6
  96. package/dist/editor/index.d.ts +19 -20
  97. package/dist/editor/index.js +25 -35
  98. package/dist/form/code.d.ts +25 -15
  99. package/dist/form/code.js +44 -41
  100. package/dist/form/fieldRegistry.d.ts +17 -13
  101. package/dist/form/fieldRegistry.js +32 -12
  102. package/dist/form/full.d.ts +17 -13
  103. package/dist/form/full.js +22 -27
  104. package/dist/form/index.d.ts +3 -3
  105. package/dist/form/index.js +3 -3
  106. package/dist/form/markdown.d.ts +13 -8
  107. package/dist/form/markdown.js +22 -23
  108. package/dist/helpers/proximityConnect.d.ts +3 -2
  109. package/dist/helpers/proximityConnect.js +2 -5
  110. package/dist/helpers/workflowEditorHelper.d.ts +12 -5
  111. package/dist/helpers/workflowEditorHelper.js +27 -25
  112. package/dist/index.d.ts +28 -24
  113. package/dist/index.js +27 -50
  114. package/dist/messages/defaults.d.ts +2 -5
  115. package/dist/messages/defaults.js +3 -6
  116. package/dist/messages/index.d.ts +0 -1
  117. package/dist/messages/index.js +0 -1
  118. package/dist/mocks/app-forms.d.ts +6 -2
  119. package/dist/mocks/app-forms.js +11 -4
  120. package/dist/openapi/v1/openapi.yaml +3 -3
  121. package/dist/playground/index.d.ts +2 -3
  122. package/dist/playground/index.js +2 -30
  123. package/dist/playground/mount.d.ts +15 -0
  124. package/dist/playground/mount.js +46 -20
  125. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  126. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  127. package/dist/registry/builtinFormats.d.ts +9 -18
  128. package/dist/registry/builtinFormats.js +9 -39
  129. package/dist/registry/builtinNodes.d.ts +0 -25
  130. package/dist/registry/builtinNodes.js +1 -50
  131. package/dist/registry/index.d.ts +3 -4
  132. package/dist/registry/index.js +4 -6
  133. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  134. package/dist/registry/nodeComponentRegistry.js +235 -17
  135. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  136. package/dist/registry/workflowFormatRegistry.js +24 -8
  137. package/dist/{schema → schemas}/index.d.ts +2 -2
  138. package/dist/{schema → schemas}/index.js +2 -2
  139. package/dist/schemas/v1/workflow.schema.json +3 -3
  140. package/dist/services/agentSpecExecutionService.js +0 -1
  141. package/dist/services/apiVariableService.d.ts +2 -1
  142. package/dist/services/apiVariableService.js +5 -22
  143. package/dist/services/autoSaveService.d.ts +7 -0
  144. package/dist/services/autoSaveService.js +6 -4
  145. package/dist/services/chatService.d.ts +8 -4
  146. package/dist/services/chatService.js +15 -15
  147. package/dist/services/draftStorage.d.ts +129 -13
  148. package/dist/services/draftStorage.js +185 -37
  149. package/dist/services/dynamicSchemaService.d.ts +2 -1
  150. package/dist/services/dynamicSchemaService.js +5 -22
  151. package/dist/services/globalSave.d.ts +13 -12
  152. package/dist/services/globalSave.js +29 -51
  153. package/dist/services/historyService.d.ts +9 -3
  154. package/dist/services/historyService.js +9 -3
  155. package/dist/services/interruptService.d.ts +14 -9
  156. package/dist/services/interruptService.js +27 -27
  157. package/dist/services/nodeExecutionService.d.ts +18 -3
  158. package/dist/services/nodeExecutionService.js +71 -45
  159. package/dist/services/playgroundService.d.ts +14 -9
  160. package/dist/services/playgroundService.js +31 -30
  161. package/dist/services/variableService.d.ts +2 -1
  162. package/dist/services/variableService.js +2 -2
  163. package/dist/services/workflowStorage.js +6 -6
  164. package/dist/stores/apiContext.d.ts +45 -0
  165. package/dist/stores/apiContext.js +65 -0
  166. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  167. package/dist/stores/categoriesStore.svelte.js +70 -64
  168. package/dist/stores/getInstance.svelte.d.ts +39 -0
  169. package/dist/stores/getInstance.svelte.js +65 -0
  170. package/dist/stores/historyStore.svelte.d.ts +77 -93
  171. package/dist/stores/historyStore.svelte.js +134 -160
  172. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  173. package/dist/stores/instanceContainer.svelte.js +114 -0
  174. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  175. package/dist/stores/interruptStore.svelte.js +253 -226
  176. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  177. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  178. package/dist/stores/playgroundStore.svelte.d.ts +169 -222
  179. package/dist/stores/playgroundStore.svelte.js +515 -580
  180. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  181. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  182. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  183. package/dist/stores/settingsStore.svelte.js +47 -12
  184. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  185. package/dist/stores/workflowStore.svelte.js +449 -501
  186. package/dist/stories/EdgeDecorator.svelte +5 -2
  187. package/dist/stories/NodeDecorator.svelte +5 -3
  188. package/dist/svelte-app.d.ts +60 -10
  189. package/dist/svelte-app.js +157 -53
  190. package/dist/types/events.d.ts +6 -3
  191. package/dist/types/index.d.ts +33 -3
  192. package/dist/types/navbar.d.ts +7 -0
  193. package/dist/types/playground.d.ts +18 -3
  194. package/dist/types/settings.d.ts +13 -0
  195. package/dist/types/settings.js +1 -0
  196. package/dist/utils/colors.d.ts +47 -21
  197. package/dist/utils/colors.js +69 -68
  198. package/dist/utils/connections.d.ts +9 -15
  199. package/dist/utils/connections.js +13 -32
  200. package/dist/utils/duration.d.ts +13 -0
  201. package/dist/utils/duration.js +45 -0
  202. package/dist/utils/icons.d.ts +5 -2
  203. package/dist/utils/icons.js +6 -5
  204. package/dist/utils/nodeSwap.d.ts +6 -2
  205. package/dist/utils/nodeSwap.js +62 -126
  206. package/dist/utils/nodeTypes.d.ts +17 -8
  207. package/dist/utils/nodeTypes.js +26 -19
  208. package/dist/utils/performanceUtils.js +7 -0
  209. package/package.json +6 -5
  210. package/dist/messages/deprecation.d.ts +0 -20
  211. package/dist/messages/deprecation.js +0 -33
  212. package/dist/registry/plugin.d.ts +0 -215
  213. package/dist/registry/plugin.js +0 -249
  214. package/dist/services/api.d.ts +0 -129
  215. package/dist/services/api.js +0 -217
@@ -2,7 +2,6 @@
2
2
  * Node Execution Service
3
3
  * Handles fetching and managing node execution information from the backend
4
4
  */
5
- import { getEndpointConfig } from './api.js';
6
5
  import { buildEndpointUrl } from '../config/endpoints.js';
7
6
  import { NODE_EXECUTION_CACHE_TIMEOUT_MS, PIPELINE_API_UNAVAILABLE_DURATION_MS } from '../config/constants.js';
8
7
  import { logger } from '../utils/logger.js';
@@ -26,12 +25,11 @@ export class NodeExecutionService {
26
25
  /**
27
26
  * Get execution information for a specific node from pipeline data
28
27
  */
29
- async getNodeExecutionInfo(nodeId, pipelineId) {
28
+ async getNodeExecutionInfo(endpointConfig, nodeId, pipelineId) {
30
29
  if (!pipelineId) {
31
30
  return null;
32
31
  }
33
32
  try {
34
- const endpointConfig = getEndpointConfig();
35
33
  if (!endpointConfig)
36
34
  throw new Error('Endpoint config not available');
37
35
  const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.pipelines.get, {
@@ -45,24 +43,7 @@ export class NodeExecutionService {
45
43
  const pipelineData = raw.data ?? raw;
46
44
  const jobs = pipelineData.jobs || [];
47
45
  const nodeStatuses = pipelineData.node_statuses || {};
48
- // Find the job for this node
49
- const nodeJob = jobs.find((job) => job.node_id === nodeId);
50
- const nodeStatus = nodeStatuses[nodeId];
51
- if (!nodeJob && !nodeStatus) {
52
- return {
53
- status: 'idle',
54
- executionCount: 0,
55
- isExecuting: false
56
- };
57
- }
58
- const executionInfo = {
59
- status: this.mapJobStatusToExecutionStatus(nodeStatus?.status || nodeJob?.status || 'idle'),
60
- executionCount: nodeJob?.execution_count || 0,
61
- isExecuting: nodeStatus?.status === 'running' || nodeJob?.status === 'running',
62
- lastExecuted: nodeJob?.last_executed || nodeStatus?.last_executed,
63
- lastExecutionDuration: nodeJob?.execution_time || nodeStatus?.execution_time,
64
- lastError: nodeJob?.error || nodeStatus?.error
65
- };
46
+ const executionInfo = this.buildNodeExecutionInfo(nodeId, nodeStatuses[nodeId], jobs);
66
47
  this.cache.set(nodeId, executionInfo);
67
48
  return executionInfo;
68
49
  }
@@ -74,7 +55,7 @@ export class NodeExecutionService {
74
55
  /**
75
56
  * Get execution information for multiple nodes from pipeline data
76
57
  */
77
- async getMultipleNodeExecutionInfo(nodeIds, pipelineId) {
58
+ async getMultipleNodeExecutionInfo(endpointConfig, nodeIds, pipelineId) {
78
59
  if (!pipelineId) {
79
60
  return {};
80
61
  }
@@ -91,7 +72,6 @@ export class NodeExecutionService {
91
72
  return defaultExecutionInfo;
92
73
  }
93
74
  try {
94
- const endpointConfig = getEndpointConfig();
95
75
  if (!endpointConfig)
96
76
  throw new Error('Endpoint config not available');
97
77
  const url = buildEndpointUrl(endpointConfig, endpointConfig.endpoints.pipelines.get, {
@@ -120,28 +100,11 @@ export class NodeExecutionService {
120
100
  const raw = await response.json();
121
101
  const result = raw.data ?? raw;
122
102
  const jobs = result.jobs || [];
103
+ const nodeStatuses = result.node_statuses || {};
123
104
  const executionInfoMap = {};
124
- // Initialize all nodes with default values
125
105
  nodeIds.forEach((nodeId) => {
126
- executionInfoMap[nodeId] = {
127
- status: 'idle',
128
- executionCount: 0,
129
- isExecuting: false
130
- };
131
- });
132
- // Update with actual job data
133
- jobs.forEach((job) => {
134
- const nodeId = job.node_id;
135
- if (nodeIds.includes(nodeId)) {
136
- executionInfoMap[nodeId] = {
137
- status: this.mapJobStatusToExecutionStatus(job.status),
138
- executionCount: job.execution_count || 0,
139
- isExecuting: job.status === 'running',
140
- lastExecuted: job.completed || job.started,
141
- lastExecutionDuration: job.execution_time,
142
- lastError: job.error_message
143
- };
144
- // Update cache
106
+ executionInfoMap[nodeId] = this.buildNodeExecutionInfo(nodeId, nodeStatuses[nodeId], jobs);
107
+ if (executionInfoMap[nodeId].status !== 'idle' || executionInfoMap[nodeId].jobs) {
145
108
  this.cache.set(nodeId, executionInfoMap[nodeId]);
146
109
  }
147
110
  });
@@ -164,9 +127,8 @@ export class NodeExecutionService {
164
127
  /**
165
128
  * Get all node execution counts
166
129
  */
167
- async getAllNodeExecutionCounts() {
130
+ async getAllNodeExecutionCounts(endpointConfig) {
168
131
  try {
169
- const endpointConfig = getEndpointConfig();
170
132
  if (!endpointConfig)
171
133
  throw new Error('Endpoint config not available');
172
134
  const url = buildEndpointUrl(endpointConfig, '/node-execution-counts');
@@ -227,6 +189,66 @@ export class NodeExecutionService {
227
189
  });
228
190
  }
229
191
  }
192
+ /**
193
+ * Build execution info for one node from the pipeline payload.
194
+ *
195
+ * The `node_statuses` entry is the backend-resolved summary (latest job's
196
+ * status, timing from the most recent run, `executions` count); the per-job
197
+ * history is attached from the `jobs` array so loop iterations stay
198
+ * inspectable. Falls back to the node's jobs when no entry exists (older
199
+ * backends).
200
+ */
201
+ buildNodeExecutionInfo(nodeId, entry, jobs) {
202
+ const nodeJobs = jobs.filter((job) => job.node_id === nodeId);
203
+ if (!entry && nodeJobs.length === 0) {
204
+ return {
205
+ status: 'idle',
206
+ executionCount: 0,
207
+ isExecuting: false
208
+ };
209
+ }
210
+ // Fallback for payloads without a node_statuses entry: the last job in
211
+ // pipeline order mirrors the backend's latest-wins resolution.
212
+ const lastJob = nodeJobs[nodeJobs.length - 1];
213
+ const status = entry?.status ?? lastJob?.status ?? 'idle';
214
+ const startedCount = nodeJobs.filter((job) => job.started).length;
215
+ const executionInfo = {
216
+ status: this.mapJobStatusToExecutionStatus(status),
217
+ executionCount: entry?.executions ?? startedCount,
218
+ isExecuting: status === 'running' || nodeJobs.some((job) => job.status === 'running'),
219
+ lastExecuted: entry?.last_executed ?? lastJob?.completed ?? lastJob?.started ?? undefined,
220
+ lastExecutionDuration: entry?.execution_time ?? lastJob?.execution_time ?? undefined,
221
+ lastExecutionDurationUs: entry?.execution_time_us ?? lastJob?.execution_time_us ?? undefined,
222
+ lastError: entry?.error ?? lastJob?.error_message ?? undefined
223
+ };
224
+ if (nodeJobs.length > 0) {
225
+ executionInfo.jobs = nodeJobs.map((job) => this.mapJobToNodeJobExecution(job));
226
+ }
227
+ return executionInfo;
228
+ }
229
+ /**
230
+ * Map a pipeline job payload entry to a NodeJobExecution history item.
231
+ */
232
+ mapJobToNodeJobExecution(job) {
233
+ let executionTime = job.execution_time;
234
+ if (executionTime == null && job.started && job.completed) {
235
+ const started = Date.parse(job.started);
236
+ const completed = Date.parse(job.completed);
237
+ if (!Number.isNaN(started) && !Number.isNaN(completed)) {
238
+ executionTime = completed - started;
239
+ }
240
+ }
241
+ return {
242
+ id: job.id,
243
+ label: job.label,
244
+ status: this.mapJobStatusToExecutionStatus(job.status),
245
+ started: job.started,
246
+ completed: job.completed,
247
+ executionTime,
248
+ executionTimeUs: job.execution_time_us ?? (executionTime != null ? executionTime * 1000 : undefined),
249
+ error: job.error_message ?? job.error
250
+ };
251
+ }
230
252
  /**
231
253
  * Map job status to execution status
232
254
  */
@@ -244,6 +266,10 @@ export class NodeExecutionService {
244
266
  return 'cancelled';
245
267
  case 'skipped':
246
268
  return 'skipped';
269
+ case 'paused':
270
+ return 'paused';
271
+ case 'interrupted':
272
+ return 'interrupted';
247
273
  default:
248
274
  return 'idle';
249
275
  }
@@ -7,6 +7,7 @@
7
7
  * @module services/playgroundService
8
8
  */
9
9
  import type { PlaygroundSession, PlaygroundMessage, PlaygroundMessagesApiResponse, PlaygroundSessionStatus } from '../types/playground.js';
10
+ import type { EndpointConfig } from '../config/endpoints.js';
10
11
  /**
11
12
  * Pagination options for {@link PlaygroundService.getMessages}.
12
13
  * `since`, `before`, and `latest` are mutually exclusive.
@@ -41,7 +42,10 @@ export declare class PlaygroundService {
41
42
  */
42
43
  static getInstance(): PlaygroundService;
43
44
  /**
44
- * Get the endpoint configuration
45
+ * Validate and return the endpoint configuration passed by the caller.
46
+ *
47
+ * Callers thread the config from `getInstance().api.config`; this enforces
48
+ * the legacy "throws if not configured" contract.
45
49
  *
46
50
  * @throws Error if endpoint configuration is not set
47
51
  * @returns The endpoint configuration
@@ -50,6 +54,7 @@ export declare class PlaygroundService {
50
54
  /**
51
55
  * Generic API request helper
52
56
  *
57
+ * @param config - The endpoint configuration
53
58
  * @param url - The URL to fetch
54
59
  * @param options - Fetch options
55
60
  * @returns The parsed JSON response
@@ -62,7 +67,7 @@ export declare class PlaygroundService {
62
67
  * @param options - Optional pagination parameters
63
68
  * @returns Array of playground sessions
64
69
  */
65
- listSessions(workflowId: string, options?: {
70
+ listSessions(endpointConfig: EndpointConfig | null, workflowId: string, options?: {
66
71
  limit?: number;
67
72
  offset?: number;
68
73
  }): Promise<PlaygroundSession[]>;
@@ -74,20 +79,20 @@ export declare class PlaygroundService {
74
79
  * @param metadata - Optional session metadata
75
80
  * @returns The created session
76
81
  */
77
- createSession(workflowId: string, name?: string, metadata?: Record<string, unknown>): Promise<PlaygroundSession>;
82
+ createSession(endpointConfig: EndpointConfig | null, workflowId: string, name?: string, metadata?: Record<string, unknown>): Promise<PlaygroundSession>;
78
83
  /**
79
84
  * Get a playground session by ID
80
85
  *
81
86
  * @param sessionId - The session UUID
82
87
  * @returns The session details
83
88
  */
84
- getSession(sessionId: string): Promise<PlaygroundSession>;
89
+ getSession(endpointConfig: EndpointConfig | null, sessionId: string): Promise<PlaygroundSession>;
85
90
  /**
86
91
  * Delete a playground session
87
92
  *
88
93
  * @param sessionId - The session UUID
89
94
  */
90
- deleteSession(sessionId: string): Promise<void>;
95
+ deleteSession(endpointConfig: EndpointConfig | null, sessionId: string): Promise<void>;
91
96
  /**
92
97
  * Get messages from a playground session.
93
98
  *
@@ -101,7 +106,7 @@ export declare class PlaygroundService {
101
106
  * @param options - Pagination options
102
107
  * @returns Messages and session status
103
108
  */
104
- getMessages(sessionId: string, options?: GetMessagesOptions): Promise<PlaygroundMessagesApiResponse>;
109
+ getMessages(endpointConfig: EndpointConfig | null, sessionId: string, options?: GetMessagesOptions): Promise<PlaygroundMessagesApiResponse>;
105
110
  /**
106
111
  * Send a message to a playground session
107
112
  *
@@ -110,13 +115,13 @@ export declare class PlaygroundService {
110
115
  * @param inputs - Optional additional inputs for workflow nodes
111
116
  * @returns The created message
112
117
  */
113
- sendMessage(sessionId: string, content: string, inputs?: Record<string, unknown>): Promise<PlaygroundMessage>;
118
+ sendMessage(endpointConfig: EndpointConfig | null, sessionId: string, content: string, inputs?: Record<string, unknown>): Promise<PlaygroundMessage>;
114
119
  /**
115
120
  * Stop execution in a playground session
116
121
  *
117
122
  * @param sessionId - The session UUID
118
123
  */
119
- stopExecution(sessionId: string): Promise<void>;
124
+ stopExecution(endpointConfig: EndpointConfig | null, sessionId: string): Promise<void>;
120
125
  /**
121
126
  * Start polling for new messages
122
127
  *
@@ -126,7 +131,7 @@ export declare class PlaygroundService {
126
131
  * @param shouldStopPolling - Optional override for stop conditions (default: defaultShouldStopPolling)
127
132
  * @param initialSequenceNumber - Optional sequence number to seed polling from (avoids re-fetching already loaded messages)
128
133
  */
129
- startPolling(sessionId: string, callback: (response: PlaygroundMessagesApiResponse) => void, interval?: number, shouldStopPolling?: (status: PlaygroundSessionStatus) => boolean, initialSequenceNumber?: number | null): void;
134
+ startPolling(endpointConfig: EndpointConfig | null, sessionId: string, callback: (response: PlaygroundMessagesApiResponse) => void, interval?: number, shouldStopPolling?: (status: PlaygroundSessionStatus) => boolean, initialSequenceNumber?: number | null): void;
130
135
  /**
131
136
  * Stop polling for messages
132
137
  */
@@ -8,7 +8,6 @@
8
8
  */
9
9
  import { defaultShouldStopPolling } from '../types/playground.js';
10
10
  import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
11
- import { getEndpointConfig } from './api.js';
12
11
  import { logger } from '../utils/logger.js';
13
12
  /**
14
13
  * Default polling interval in milliseconds
@@ -43,27 +42,29 @@ export class PlaygroundService {
43
42
  return PlaygroundService.instance;
44
43
  }
45
44
  /**
46
- * Get the endpoint configuration
45
+ * Validate and return the endpoint configuration passed by the caller.
46
+ *
47
+ * Callers thread the config from `getInstance().api.config`; this enforces
48
+ * the legacy "throws if not configured" contract.
47
49
  *
48
50
  * @throws Error if endpoint configuration is not set
49
51
  * @returns The endpoint configuration
50
52
  */
51
- getConfig() {
52
- const config = getEndpointConfig();
53
+ getConfig(config) {
53
54
  if (!config) {
54
- throw new Error('Endpoint configuration not set. Call setEndpointConfig() first.');
55
+ throw new Error('Endpoint configuration not set. Configure the instance via fd.api.configure().');
55
56
  }
56
57
  return config;
57
58
  }
58
59
  /**
59
60
  * Generic API request helper
60
61
  *
62
+ * @param config - The endpoint configuration
61
63
  * @param url - The URL to fetch
62
64
  * @param options - Fetch options
63
65
  * @returns The parsed JSON response
64
66
  */
65
- async request(url, options = {}) {
66
- const config = this.getConfig();
67
+ async request(config, url, options = {}) {
67
68
  const headers = getEndpointHeaders(config, 'playground');
68
69
  const response = await fetch(url, {
69
70
  ...options,
@@ -91,8 +92,8 @@ export class PlaygroundService {
91
92
  * @param options - Optional pagination parameters
92
93
  * @returns Array of playground sessions
93
94
  */
94
- async listSessions(workflowId, options) {
95
- const config = this.getConfig();
95
+ async listSessions(endpointConfig, workflowId, options) {
96
+ const config = this.getConfig(endpointConfig);
96
97
  let url = buildEndpointUrl(config, config.endpoints.playground.listSessions, {
97
98
  id: workflowId
98
99
  });
@@ -108,7 +109,7 @@ export class PlaygroundService {
108
109
  if (queryString) {
109
110
  url = `${url}?${queryString}`;
110
111
  }
111
- const response = await this.request(url);
112
+ const response = await this.request(config, url);
112
113
  return response.data ?? [];
113
114
  }
114
115
  /**
@@ -119,12 +120,12 @@ export class PlaygroundService {
119
120
  * @param metadata - Optional session metadata
120
121
  * @returns The created session
121
122
  */
122
- async createSession(workflowId, name, metadata) {
123
- const config = this.getConfig();
123
+ async createSession(endpointConfig, workflowId, name, metadata) {
124
+ const config = this.getConfig(endpointConfig);
124
125
  const url = buildEndpointUrl(config, config.endpoints.playground.createSession, {
125
126
  id: workflowId
126
127
  });
127
- const response = await this.request(url, {
128
+ const response = await this.request(config, url, {
128
129
  method: 'POST',
129
130
  body: JSON.stringify({ name, metadata })
130
131
  });
@@ -139,12 +140,12 @@ export class PlaygroundService {
139
140
  * @param sessionId - The session UUID
140
141
  * @returns The session details
141
142
  */
142
- async getSession(sessionId) {
143
- const config = this.getConfig();
143
+ async getSession(endpointConfig, sessionId) {
144
+ const config = this.getConfig(endpointConfig);
144
145
  const url = buildEndpointUrl(config, config.endpoints.playground.getSession, {
145
146
  sessionId
146
147
  });
147
- const response = await this.request(url);
148
+ const response = await this.request(config, url);
148
149
  if (!response.data) {
149
150
  throw new Error('Session not found');
150
151
  }
@@ -155,12 +156,12 @@ export class PlaygroundService {
155
156
  *
156
157
  * @param sessionId - The session UUID
157
158
  */
158
- async deleteSession(sessionId) {
159
- const config = this.getConfig();
159
+ async deleteSession(endpointConfig, sessionId) {
160
+ const config = this.getConfig(endpointConfig);
160
161
  const url = buildEndpointUrl(config, config.endpoints.playground.deleteSession, {
161
162
  sessionId
162
163
  });
163
- await this.request(url, {
164
+ await this.request(config, url, {
164
165
  method: 'DELETE'
165
166
  });
166
167
  }
@@ -180,8 +181,8 @@ export class PlaygroundService {
180
181
  * @param options - Pagination options
181
182
  * @returns Messages and session status
182
183
  */
183
- async getMessages(sessionId, options = {}) {
184
- const config = this.getConfig();
184
+ async getMessages(endpointConfig, sessionId, options = {}) {
185
+ const config = this.getConfig(endpointConfig);
185
186
  let url = buildEndpointUrl(config, config.endpoints.playground.getMessages, {
186
187
  sessionId
187
188
  });
@@ -202,7 +203,7 @@ export class PlaygroundService {
202
203
  if (queryString) {
203
204
  url = `${url}?${queryString}`;
204
205
  }
205
- return this.request(url);
206
+ return this.request(config, url);
206
207
  }
207
208
  /**
208
209
  * Send a message to a playground session
@@ -212,8 +213,8 @@ export class PlaygroundService {
212
213
  * @param inputs - Optional additional inputs for workflow nodes
213
214
  * @returns The created message
214
215
  */
215
- async sendMessage(sessionId, content, inputs) {
216
- const config = this.getConfig();
216
+ async sendMessage(endpointConfig, sessionId, content, inputs) {
217
+ const config = this.getConfig(endpointConfig);
217
218
  const url = buildEndpointUrl(config, config.endpoints.playground.sendMessage, {
218
219
  sessionId
219
220
  });
@@ -221,7 +222,7 @@ export class PlaygroundService {
221
222
  if (inputs) {
222
223
  requestBody.inputs = inputs;
223
224
  }
224
- const response = await this.request(url, {
225
+ const response = await this.request(config, url, {
225
226
  method: 'POST',
226
227
  body: JSON.stringify(requestBody)
227
228
  });
@@ -235,12 +236,12 @@ export class PlaygroundService {
235
236
  *
236
237
  * @param sessionId - The session UUID
237
238
  */
238
- async stopExecution(sessionId) {
239
- const config = this.getConfig();
239
+ async stopExecution(endpointConfig, sessionId) {
240
+ const config = this.getConfig(endpointConfig);
240
241
  const url = buildEndpointUrl(config, config.endpoints.playground.stopExecution, {
241
242
  sessionId
242
243
  });
243
- await this.request(url, {
244
+ await this.request(config, url, {
244
245
  method: 'POST'
245
246
  });
246
247
  }
@@ -256,7 +257,7 @@ export class PlaygroundService {
256
257
  * @param shouldStopPolling - Optional override for stop conditions (default: defaultShouldStopPolling)
257
258
  * @param initialSequenceNumber - Optional sequence number to seed polling from (avoids re-fetching already loaded messages)
258
259
  */
259
- startPolling(sessionId, callback, interval = DEFAULT_POLLING_INTERVAL, shouldStopPolling, initialSequenceNumber) {
260
+ startPolling(endpointConfig, sessionId, callback, interval = DEFAULT_POLLING_INTERVAL, shouldStopPolling, initialSequenceNumber) {
260
261
  // Stop any existing polling
261
262
  this.stopPolling();
262
263
  this.pollingSessionId = sessionId;
@@ -268,7 +269,7 @@ export class PlaygroundService {
268
269
  return;
269
270
  }
270
271
  try {
271
- const response = await this.getMessages(sessionId, {
272
+ const response = await this.getMessages(endpointConfig, sessionId, {
272
273
  since: this.lastSequenceNumber ?? undefined
273
274
  });
274
275
  // Update last sequence number cursor
@@ -6,6 +6,7 @@
6
6
  * @module services/variableService
7
7
  */
8
8
  import type { WorkflowNode, WorkflowEdge, VariableSchema, TemplateVariable, TemplateVariablesConfig, AuthProvider } from '../types/index.js';
9
+ import type { EndpointConfig } from '../config/endpoints.js';
9
10
  /**
10
11
  * Options for deriving available variables.
11
12
  */
@@ -138,4 +139,4 @@ export declare function mergeVariableSchemas(primary: VariableSchema, secondary:
138
139
  * const schema = await getVariableSchema(node, nodes, edges, config, workflowId, authProvider);
139
140
  * ```
140
141
  */
141
- export declare function getVariableSchema(node: WorkflowNode, nodes: WorkflowNode[], edges: WorkflowEdge[], config: TemplateVariablesConfig, workflowId?: string, authProvider?: AuthProvider): Promise<VariableSchema>;
142
+ export declare function getVariableSchema(endpointConfig: EndpointConfig | null, node: WorkflowNode, nodes: WorkflowNode[], edges: WorkflowEdge[], config: TemplateVariablesConfig, workflowId?: string, authProvider?: AuthProvider): Promise<VariableSchema>;
@@ -405,14 +405,14 @@ export function mergeVariableSchemas(primary, secondary) {
405
405
  * const schema = await getVariableSchema(node, nodes, edges, config, workflowId, authProvider);
406
406
  * ```
407
407
  */
408
- export async function getVariableSchema(node, nodes, edges, config, workflowId, authProvider) {
408
+ export async function getVariableSchema(endpointConfig, node, nodes, edges, config, workflowId, authProvider) {
409
409
  let resultSchema = { variables: {} };
410
410
  // Try API mode first (if configured)
411
411
  if (config.api) {
412
412
  try {
413
413
  // Import API variable service dynamically to avoid circular dependencies
414
414
  const { fetchVariableSchema } = await import('./apiVariableService.js');
415
- const apiResult = await fetchVariableSchema(workflowId, node.id, config.api, authProvider);
415
+ const apiResult = await fetchVariableSchema(endpointConfig, workflowId, node.id, config.api, authProvider);
416
416
  if (apiResult.success && apiResult.schema) {
417
417
  resultSchema = apiResult.schema;
418
418
  // Merge with static schema if configured
@@ -20,10 +20,10 @@ export async function saveWorkflow(workflow) {
20
20
  ...workflow,
21
21
  id,
22
22
  metadata: {
23
- version: '1.0.0',
24
- createdAt: new Date().toISOString(),
25
- updatedAt: new Date().toISOString(),
26
- ...workflow.metadata
23
+ ...workflow.metadata,
24
+ schemaVersion: workflow.metadata?.schemaVersion ?? '1.0.0',
25
+ createdAt: workflow.metadata?.createdAt ?? new Date().toISOString(),
26
+ updatedAt: workflow.metadata?.updatedAt ?? new Date().toISOString()
27
27
  }
28
28
  };
29
29
  workflows.set(id, newWorkflow);
@@ -42,7 +42,7 @@ export async function updateWorkflow(id, workflow) {
42
42
  ...workflow,
43
43
  id, // Ensure ID doesn't change
44
44
  metadata: {
45
- version: existing.metadata?.version || '1.0.0',
45
+ schemaVersion: existing.metadata?.schemaVersion || '1.0.0',
46
46
  createdAt: existing.metadata?.createdAt || new Date().toISOString(),
47
47
  updatedAt: new Date().toISOString(),
48
48
  author: workflow.metadata?.author || existing.metadata?.author,
@@ -105,7 +105,7 @@ export async function initializeSampleWorkflows() {
105
105
  nodes: [],
106
106
  edges: [],
107
107
  metadata: {
108
- version: '1.0.0',
108
+ schemaVersion: '1.0.0',
109
109
  createdAt: new Date().toISOString(),
110
110
  updatedAt: new Date().toISOString(),
111
111
  author: 'System',
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Per-instance API context for FlowDrop.
3
+ *
4
+ * Holds the endpoint configuration and auth provider for one FlowDrop
5
+ * instance and lazily exposes an {@link EnhancedFlowDropApiClient} built from
6
+ * them. Replaces the former module-level singleton in `services/api.ts`:
7
+ * each instance owns its own configuration so multiple editors can talk to
8
+ * different backends on a single page.
9
+ *
10
+ * Resolved in components via `getInstance().api`; configured at mount time by
11
+ * `mountFlowDropApp` / `mountPlayground` (or `<App>` when used directly).
12
+ *
13
+ * @module stores/apiContext
14
+ */
15
+ import type { EndpointConfig } from '../config/endpoints.js';
16
+ import type { AuthProvider } from '../types/auth.js';
17
+ import { EnhancedFlowDropApiClient } from '../api/enhanced-client.js';
18
+ /**
19
+ * Per-instance endpoint configuration, auth provider, and API client.
20
+ *
21
+ * The config is `null` until {@link configure} is called — mirroring the
22
+ * legacy "throws if not configured" contract. The client is created lazily on
23
+ * first access and rebuilt whenever the configuration changes.
24
+ */
25
+ export declare class ApiContext {
26
+ #private;
27
+ /** The current endpoint configuration, or null until {@link configure}d. */
28
+ get config(): EndpointConfig | null;
29
+ /** The current auth provider (defaults to {@link NoAuthProvider}). */
30
+ get authProvider(): AuthProvider;
31
+ /**
32
+ * The API client for this instance, created lazily from the configured
33
+ * endpoints and auth provider.
34
+ *
35
+ * @throws Error if the context has not been configured yet.
36
+ */
37
+ get client(): EnhancedFlowDropApiClient;
38
+ /**
39
+ * Configure this instance's endpoints and (optionally) auth provider.
40
+ * Discards any previously built client so the next access rebuilds it.
41
+ */
42
+ configure(config: EndpointConfig, authProvider?: AuthProvider): void;
43
+ /** Whether {@link configure} has been called with a usable config. */
44
+ isConfigured(): boolean;
45
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Per-instance API context for FlowDrop.
3
+ *
4
+ * Holds the endpoint configuration and auth provider for one FlowDrop
5
+ * instance and lazily exposes an {@link EnhancedFlowDropApiClient} built from
6
+ * them. Replaces the former module-level singleton in `services/api.ts`:
7
+ * each instance owns its own configuration so multiple editors can talk to
8
+ * different backends on a single page.
9
+ *
10
+ * Resolved in components via `getInstance().api`; configured at mount time by
11
+ * `mountFlowDropApp` / `mountPlayground` (or `<App>` when used directly).
12
+ *
13
+ * @module stores/apiContext
14
+ */
15
+ import { NoAuthProvider } from '../types/auth.js';
16
+ import { EnhancedFlowDropApiClient } from '../api/enhanced-client.js';
17
+ /**
18
+ * Per-instance endpoint configuration, auth provider, and API client.
19
+ *
20
+ * The config is `null` until {@link configure} is called — mirroring the
21
+ * legacy "throws if not configured" contract. The client is created lazily on
22
+ * first access and rebuilt whenever the configuration changes.
23
+ */
24
+ export class ApiContext {
25
+ #config = null;
26
+ #authProvider = new NoAuthProvider();
27
+ #client = null;
28
+ /** The current endpoint configuration, or null until {@link configure}d. */
29
+ get config() {
30
+ return this.#config;
31
+ }
32
+ /** The current auth provider (defaults to {@link NoAuthProvider}). */
33
+ get authProvider() {
34
+ return this.#authProvider;
35
+ }
36
+ /**
37
+ * The API client for this instance, created lazily from the configured
38
+ * endpoints and auth provider.
39
+ *
40
+ * @throws Error if the context has not been configured yet.
41
+ */
42
+ get client() {
43
+ if (!this.#config) {
44
+ throw new Error('Endpoint configuration not set. Configure the instance via fd.api.configure() ' +
45
+ '(done automatically by mountFlowDropApp / <App>) before using fd.api.client.');
46
+ }
47
+ return (this.#client ??= new EnhancedFlowDropApiClient(this.#config, this.#authProvider));
48
+ }
49
+ /**
50
+ * Configure this instance's endpoints and (optionally) auth provider.
51
+ * Discards any previously built client so the next access rebuilds it.
52
+ */
53
+ configure(config, authProvider) {
54
+ this.#config = config;
55
+ if (authProvider) {
56
+ this.#authProvider = authProvider;
57
+ }
58
+ // Drop the cached client so it picks up the new config/auth on next access.
59
+ this.#client = null;
60
+ }
61
+ /** Whether {@link configure} has been called with a usable config. */
62
+ isConfigured() {
63
+ return Boolean(this.#config);
64
+ }
65
+ }