@flowdrop/flowdrop 1.14.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 (218) 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 +110 -66
  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 +45 -40
  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 +29 -13
  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 +292 -0
  61. package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
  62. package/dist/components/nodes/GatewayNode.svelte +19 -10
  63. package/dist/components/nodes/IdeaNode.svelte +7 -0
  64. package/dist/components/nodes/SimpleNode.svelte +11 -6
  65. package/dist/components/nodes/SquareNode.svelte +15 -8
  66. package/dist/components/nodes/TerminalNode.svelte +9 -4
  67. package/dist/components/nodes/ToolNode.svelte +7 -1
  68. package/dist/components/nodes/WorkflowNode.svelte +16 -7
  69. package/dist/components/playground/ChatInput.svelte +11 -14
  70. package/dist/components/playground/ChatPanel.svelte +6 -49
  71. package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
  72. package/dist/components/playground/ControlPanel.svelte +134 -123
  73. package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
  74. package/dist/components/playground/ExecutionLogs.svelte +11 -9
  75. package/dist/components/playground/InputCollector.svelte +11 -9
  76. package/dist/components/playground/MessageStream.svelte +17 -23
  77. package/dist/components/playground/PipelineKanbanView.svelte +65 -6
  78. package/dist/components/playground/PipelinePanel.svelte +11 -5
  79. package/dist/components/playground/PipelineTableView.svelte +186 -44
  80. package/dist/components/playground/Playground.svelte +95 -92
  81. package/dist/components/playground/Playground.svelte.d.ts +2 -0
  82. package/dist/components/playground/PlaygroundApp.svelte +6 -1
  83. package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
  84. package/dist/components/playground/PlaygroundModal.svelte +13 -3
  85. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
  86. package/dist/components/playground/PlaygroundStudio.svelte +34 -32
  87. package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
  88. package/dist/components/playground/SessionManager.svelte +9 -12
  89. package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
  90. package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
  91. package/dist/config/endpoints.d.ts +0 -7
  92. package/dist/config/endpoints.js +2 -10
  93. package/dist/core/index.d.ts +4 -4
  94. package/dist/core/index.js +6 -6
  95. package/dist/display/index.d.ts +0 -2
  96. package/dist/display/index.js +0 -6
  97. package/dist/editor/index.d.ts +19 -20
  98. package/dist/editor/index.js +25 -35
  99. package/dist/form/code.d.ts +25 -15
  100. package/dist/form/code.js +44 -41
  101. package/dist/form/fieldRegistry.d.ts +17 -13
  102. package/dist/form/fieldRegistry.js +32 -12
  103. package/dist/form/full.d.ts +17 -13
  104. package/dist/form/full.js +22 -27
  105. package/dist/form/index.d.ts +3 -3
  106. package/dist/form/index.js +3 -3
  107. package/dist/form/markdown.d.ts +13 -8
  108. package/dist/form/markdown.js +22 -23
  109. package/dist/helpers/proximityConnect.d.ts +7 -3
  110. package/dist/helpers/proximityConnect.js +19 -6
  111. package/dist/helpers/workflowEditorHelper.d.ts +12 -5
  112. package/dist/helpers/workflowEditorHelper.js +27 -25
  113. package/dist/index.d.ts +28 -24
  114. package/dist/index.js +27 -50
  115. package/dist/messages/defaults.d.ts +2 -5
  116. package/dist/messages/defaults.js +3 -6
  117. package/dist/messages/index.d.ts +0 -1
  118. package/dist/messages/index.js +0 -1
  119. package/dist/mocks/app-forms.d.ts +6 -2
  120. package/dist/mocks/app-forms.js +11 -4
  121. package/dist/openapi/v1/openapi.yaml +227 -164
  122. package/dist/playground/index.d.ts +2 -3
  123. package/dist/playground/index.js +2 -30
  124. package/dist/playground/mount.d.ts +15 -0
  125. package/dist/playground/mount.js +46 -20
  126. package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
  127. package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
  128. package/dist/registry/builtinFormats.d.ts +9 -18
  129. package/dist/registry/builtinFormats.js +9 -39
  130. package/dist/registry/builtinNodes.d.ts +1 -26
  131. package/dist/registry/builtinNodes.js +14 -50
  132. package/dist/registry/index.d.ts +3 -4
  133. package/dist/registry/index.js +4 -6
  134. package/dist/registry/nodeComponentRegistry.d.ts +182 -15
  135. package/dist/registry/nodeComponentRegistry.js +235 -17
  136. package/dist/registry/workflowFormatRegistry.d.ts +14 -9
  137. package/dist/registry/workflowFormatRegistry.js +24 -8
  138. package/dist/{schema → schemas}/index.d.ts +2 -2
  139. package/dist/{schema → schemas}/index.js +2 -2
  140. package/dist/schemas/v1/workflow.schema.json +53 -6
  141. package/dist/services/agentSpecExecutionService.js +0 -1
  142. package/dist/services/apiVariableService.d.ts +2 -1
  143. package/dist/services/apiVariableService.js +5 -22
  144. package/dist/services/autoSaveService.d.ts +7 -0
  145. package/dist/services/autoSaveService.js +6 -4
  146. package/dist/services/chatService.d.ts +8 -4
  147. package/dist/services/chatService.js +15 -15
  148. package/dist/services/draftStorage.d.ts +129 -13
  149. package/dist/services/draftStorage.js +185 -37
  150. package/dist/services/dynamicSchemaService.d.ts +2 -1
  151. package/dist/services/dynamicSchemaService.js +5 -22
  152. package/dist/services/globalSave.d.ts +13 -12
  153. package/dist/services/globalSave.js +29 -51
  154. package/dist/services/historyService.d.ts +9 -3
  155. package/dist/services/historyService.js +9 -3
  156. package/dist/services/interruptService.d.ts +14 -9
  157. package/dist/services/interruptService.js +27 -27
  158. package/dist/services/nodeExecutionService.d.ts +18 -3
  159. package/dist/services/nodeExecutionService.js +71 -45
  160. package/dist/services/playgroundService.d.ts +14 -9
  161. package/dist/services/playgroundService.js +31 -30
  162. package/dist/services/variableService.d.ts +2 -1
  163. package/dist/services/variableService.js +2 -2
  164. package/dist/services/workflowStorage.js +6 -6
  165. package/dist/stores/apiContext.d.ts +45 -0
  166. package/dist/stores/apiContext.js +65 -0
  167. package/dist/stores/categoriesStore.svelte.d.ts +28 -23
  168. package/dist/stores/categoriesStore.svelte.js +70 -64
  169. package/dist/stores/getInstance.svelte.d.ts +39 -0
  170. package/dist/stores/getInstance.svelte.js +65 -0
  171. package/dist/stores/historyStore.svelte.d.ts +77 -93
  172. package/dist/stores/historyStore.svelte.js +134 -160
  173. package/dist/stores/instanceContainer.svelte.d.ts +111 -0
  174. package/dist/stores/instanceContainer.svelte.js +114 -0
  175. package/dist/stores/interruptStore.svelte.d.ts +112 -82
  176. package/dist/stores/interruptStore.svelte.js +253 -226
  177. package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
  178. package/dist/stores/pipelinePanelStore.svelte.js +61 -14
  179. package/dist/stores/playgroundStore.svelte.d.ts +169 -216
  180. package/dist/stores/playgroundStore.svelte.js +515 -572
  181. package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
  182. package/dist/stores/portCoordinateStore.svelte.js +109 -98
  183. package/dist/stores/settingsStore.svelte.d.ts +4 -1
  184. package/dist/stores/settingsStore.svelte.js +47 -12
  185. package/dist/stores/workflowStore.svelte.d.ts +178 -213
  186. package/dist/stores/workflowStore.svelte.js +449 -501
  187. package/dist/stories/EdgeDecorator.svelte +5 -2
  188. package/dist/stories/NodeDecorator.svelte +5 -3
  189. package/dist/svelte-app.d.ts +60 -10
  190. package/dist/svelte-app.js +157 -53
  191. package/dist/types/events.d.ts +6 -3
  192. package/dist/types/index.d.ts +71 -6
  193. package/dist/types/navbar.d.ts +7 -0
  194. package/dist/types/playground.d.ts +18 -3
  195. package/dist/types/settings.d.ts +13 -0
  196. package/dist/types/settings.js +1 -0
  197. package/dist/utils/colors.d.ts +47 -21
  198. package/dist/utils/colors.js +69 -68
  199. package/dist/utils/connections.d.ts +9 -15
  200. package/dist/utils/connections.js +13 -32
  201. package/dist/utils/duration.d.ts +13 -0
  202. package/dist/utils/duration.js +45 -0
  203. package/dist/utils/formMerge.d.ts +36 -0
  204. package/dist/utils/formMerge.js +70 -0
  205. package/dist/utils/icons.d.ts +5 -2
  206. package/dist/utils/icons.js +6 -5
  207. package/dist/utils/nodeSwap.d.ts +6 -2
  208. package/dist/utils/nodeSwap.js +62 -126
  209. package/dist/utils/nodeTypes.d.ts +17 -8
  210. package/dist/utils/nodeTypes.js +27 -19
  211. package/dist/utils/performanceUtils.js +7 -0
  212. package/package.json +6 -5
  213. package/dist/messages/deprecation.d.ts +0 -20
  214. package/dist/messages/deprecation.js +0 -33
  215. package/dist/registry/plugin.d.ts +0 -215
  216. package/dist/registry/plugin.js +0 -249
  217. package/dist/services/api.d.ts +0 -129
  218. package/dist/services/api.js +0 -217
@@ -11,15 +11,17 @@
11
11
  *
12
12
  * Extends BaseRegistry for shared mechanics (subscribe, onClear, etc.).
13
13
  */
14
- import { BaseRegistry } from './BaseRegistry.js';
14
+ import { BaseRegistry } from './BaseRegistry.svelte.js';
15
15
  /**
16
- * Central registry for workflow format adapters.
17
- * Singleton — extends BaseRegistry for shared mechanics.
16
+ * Per-instance registry for workflow format adapters.
17
+ * Extends BaseRegistry for shared mechanics; seeded with the built-in
18
+ * adapters at construction (see `createFlowDropInstance`). Reach it via
19
+ * `fd.formats`, or supply adapters through the `formatAdapters` mount option.
18
20
  *
19
21
  * @example
20
22
  * ```typescript
21
23
  * // Register a custom format
22
- * workflowFormatRegistry.register({
24
+ * fd.formats.register({
23
25
  * id: 'n8n',
24
26
  * name: 'n8n Workflow',
25
27
  * export: (workflow) => JSON.stringify(convertToN8n(workflow)),
@@ -27,10 +29,23 @@ import { BaseRegistry } from './BaseRegistry.js';
27
29
  * });
28
30
  *
29
31
  * // Get an adapter
30
- * const adapter = workflowFormatRegistry.get('n8n');
32
+ * const adapter = fd.formats.get('n8n');
31
33
  * ```
32
34
  */
33
- class WorkflowFormatRegistry extends BaseRegistry {
35
+ export class WorkflowFormatRegistry extends BaseRegistry {
36
+ /**
37
+ * @param seed - Optional initial format adapters. When omitted the registry
38
+ * starts empty; instances created via `createFlowDropInstance` pass the
39
+ * built-in flowdrop + agentspec adapters (see `builtinFormats.ts`).
40
+ */
41
+ constructor(seed) {
42
+ super();
43
+ if (seed) {
44
+ for (const adapter of seed) {
45
+ this.register(adapter, true);
46
+ }
47
+ }
48
+ }
34
49
  /**
35
50
  * Register a workflow format adapter.
36
51
  *
@@ -44,6 +59,7 @@ class WorkflowFormatRegistry extends BaseRegistry {
44
59
  `Use overwrite: true to replace it.`);
45
60
  }
46
61
  this.items.set(adapter.id, adapter);
62
+ this.touch();
47
63
  this.notifyListeners();
48
64
  }
49
65
  /**
@@ -61,6 +77,7 @@ class WorkflowFormatRegistry extends BaseRegistry {
61
77
  * @returns Array of NodeMetadata from all format adapters
62
78
  */
63
79
  getAllFormatNodes() {
80
+ this.trackVersion(); // reactive dependency (reads items directly)
64
81
  const allNodes = [];
65
82
  for (const adapter of this.items.values()) {
66
83
  if (adapter.nodes && adapter.nodes.length > 0) {
@@ -76,6 +93,7 @@ class WorkflowFormatRegistry extends BaseRegistry {
76
93
  * @returns Array of NodeMetadata for the format, or empty array
77
94
  */
78
95
  getFormatNodes(formatId) {
96
+ this.trackVersion(); // reactive dependency (reads items directly)
79
97
  const adapter = this.items.get(formatId);
80
98
  return adapter?.nodes ?? [];
81
99
  }
@@ -92,5 +110,3 @@ class WorkflowFormatRegistry extends BaseRegistry {
92
110
  }));
93
111
  }
94
112
  }
95
- /** Singleton instance of the workflow format registry */
96
- export const workflowFormatRegistry = new WorkflowFormatRegistry();
@@ -15,9 +15,9 @@
15
15
  * const valid = validate(myWorkflow);
16
16
  * ```
17
17
  *
18
- * @module schema
18
+ * @module schemas
19
19
  */
20
- import workflowSchema from '../schemas/v1/workflow.schema.json';
20
+ import workflowSchema from './v1/workflow.schema.json';
21
21
  /** Current workflow schema format version */
22
22
  export declare const WORKFLOW_SCHEMA_VERSION = "1.0.0";
23
23
  export { workflowSchema };
@@ -15,9 +15,9 @@
15
15
  * const valid = validate(myWorkflow);
16
16
  * ```
17
17
  *
18
- * @module schema
18
+ * @module schemas
19
19
  */
20
- import workflowSchema from '../schemas/v1/workflow.schema.json';
20
+ import workflowSchema from './v1/workflow.schema.json';
21
21
  /** Current workflow schema format version */
22
22
  export const WORKFLOW_SCHEMA_VERSION = '1.0.0';
23
23
  export { workflowSchema };
@@ -51,6 +51,46 @@
51
51
  "metadata"
52
52
  ],
53
53
  "$defs": {
54
+ "AtomUIConfig": {
55
+ "type": "object",
56
+ "description": "Display/behaviour settings for minimalist `atom` nodes (e.g. Constant, Cast).\nLives under `extensions.ui.atom`. The atom renderer reads these to decide what\nto show, and `valueTypeKey` drives the bound output port's `dataType` from\nconfig so connection validation matches the type the user picked.\n\nAll fields are optional — an empty object renders a label-only pill using the\nnode's `label`.\n",
57
+ "properties": {
58
+ "valueKey": {
59
+ "type": "string",
60
+ "description": "Config key whose value becomes the node body text.\nFalls back to the node `label` when unset or empty.\n"
61
+ },
62
+ "valueTypeKey": {
63
+ "type": "string",
64
+ "description": "Config key holding the selected value's type (a port dataType id).\nThe bound output port adopts this dataType.\n"
65
+ },
66
+ "outputPortId": {
67
+ "type": "string",
68
+ "description": "Output port id driven by `valueTypeKey`.\nDefaults to the first output port when unset.\n"
69
+ },
70
+ "shape": {
71
+ "type": "string",
72
+ "enum": [
73
+ "pill",
74
+ "rectangle"
75
+ ],
76
+ "default": "pill",
77
+ "description": "Body shape. `pill` (default) is fully rounded; `rectangle` is lightly rounded.\n"
78
+ },
79
+ "prefix": {
80
+ "type": "string",
81
+ "description": "Dimmed affordance rendered before the body (e.g. `\"→ \"` to mark a transform).\nStays visible while the body value ellipsizes. Hidden in the empty state.\n"
82
+ },
83
+ "placeholder": {
84
+ "type": "string",
85
+ "description": "Text shown (dimmed) when the resolved body value is empty/unset."
86
+ },
87
+ "maxWidth": {
88
+ "type": "integer",
89
+ "description": "Max body width in px before the label ellipsizes."
90
+ }
91
+ },
92
+ "additionalProperties": false
93
+ },
54
94
  "AutocompleteConfig": {
55
95
  "type": "object",
56
96
  "description": "Configuration for autocomplete fields that fetch suggestions from a callback URL.\nUsed when format is \"autocomplete\".\n",
@@ -414,15 +454,17 @@
414
454
  },
415
455
  "nodeType": {
416
456
  "type": "string",
417
- "description": "Changes how the node is visually rendered. This allows a single node definition\nto support multiple visual representations.\n\nAvailable built-in types:\n- `default`: Standard workflow node with full details\n- `simple`: Compact layout with minimal chrome\n- `square`: Geometric square layout (icon-only)\n- `tool`: Specialized style for agent tools\n- `gateway`: Branching control flow visualization\n- `terminal`: Start/end/exit node styling\n- `note`: Sticky note style for annotations\n\nThe node's `metadata.supportedTypes` defines which types are allowed.\nIf invalid or missing, falls back to `metadata.type` or \"default\".\n",
457
+ "description": "Changes how the node is visually rendered. This allows a single node definition\nto support multiple visual representations.\n\nAvailable built-in types:\n- `default`: Standard workflow node with full details\n- `simple`: Compact layout with minimal chrome\n- `square`: Geometric square layout (icon-only)\n- `atom`: Minimal label-only pill/rectangle (uses extensions.ui.atom)\n- `tool`: Specialized style for agent tools\n- `gateway`: Branching control flow visualization\n- `terminal`: Start/end/exit node styling\n- `note`: Sticky note style for annotations\n- `idea`: Conceptual idea node for BPMN-like flow diagrams\n\nThe node's `metadata.supportedTypes` defines which types are allowed.\nIf invalid or missing, falls back to `metadata.type` or \"default\".\n",
418
458
  "enum": [
419
459
  "default",
420
460
  "simple",
421
461
  "square",
462
+ "atom",
422
463
  "tool",
423
464
  "gateway",
424
465
  "terminal",
425
- "note"
466
+ "note",
467
+ "idea"
426
468
  ]
427
469
  },
428
470
  "dynamicInputs": {
@@ -677,12 +719,14 @@
677
719
  "note",
678
720
  "simple",
679
721
  "square",
722
+ "atom",
680
723
  "tool",
681
724
  "gateway",
682
725
  "terminal",
726
+ "idea",
683
727
  "default"
684
728
  ],
685
- "description": "Visual rendering type for the node.\n\nBuilt-in types:\n- `note` - Sticky note with markdown support\n- `simple` - Compact layout with header and description\n- `square` - Minimal square node with centered icon\n- `tool` - Specialized node for agent tools\n- `gateway` - Branching control flow with dynamic branches (uses config.branches)\n- `terminal` - Circular node for workflow start/end/exit points\n- `default` - Full-featured workflow node with dynamic port support\n\n## Dynamic Port Support\n\nThe `default` and `gateway` node types support dynamic ports:\n\n- **default**: Supports `config.dynamicInputs` and `config.dynamicOutputs`\n for user-defined input/output handles\n- **gateway**: Supports `config.branches` for conditional branching paths\n\n## UI Extensions\n\nAll node types support `extensions.ui.hideUnconnectedHandles` to control\nvisibility of unconnected ports.\n"
729
+ "description": "Visual rendering type for the node.\n\nBuilt-in types:\n- `note` - Sticky note with markdown support\n- `simple` - Compact layout with header and description\n- `square` - Minimal square node with centered icon\n- `atom` - Minimal label-only pill/rectangle for value/transform nodes (uses extensions.ui.atom)\n- `tool` - Specialized node for agent tools\n- `gateway` - Branching control flow with dynamic branches (uses config.branches)\n- `terminal` - Circular node for workflow start/end/exit points\n- `idea` - Conceptual idea node for BPMN-like flow diagrams\n- `default` - Full-featured workflow node with dynamic port support\n\n## Dynamic Port Support\n\nThe `default` and `gateway` node types support dynamic ports:\n\n- **default**: Supports `config.dynamicInputs` and `config.dynamicOutputs`\n for user-defined input/output handles\n- **gateway**: Supports `config.branches` for conditional branching paths\n\n## UI Extensions\n\nAll node types support `extensions.ui.hideUnconnectedHandles` to control\nvisibility of unconnected ports.\n"
686
730
  },
687
731
  "NodeUIExtensions": {
688
732
  "type": "object",
@@ -692,6 +736,9 @@
692
736
  "type": "boolean",
693
737
  "description": "Show/hide unconnected handles (ports) to reduce visual noise.\nWhen true, only ports with active connections are displayed.\nUseful for nodes with many optional ports.\n"
694
738
  },
739
+ "atom": {
740
+ "$ref": "#/$defs/AtomUIConfig"
741
+ },
695
742
  "style": {
696
743
  "type": "object",
697
744
  "additionalProperties": true,
@@ -1006,9 +1053,9 @@
1006
1053
  "WorkflowMetadata": {
1007
1054
  "type": "object",
1008
1055
  "properties": {
1009
- "version": {
1056
+ "schemaVersion": {
1010
1057
  "type": "string",
1011
- "description": "Workflow version"
1058
+ "description": "Workflow schema format version — identifies the document format, not the workflow's own revision."
1012
1059
  },
1013
1060
  "createdAt": {
1014
1061
  "type": "string",
@@ -1046,7 +1093,7 @@
1046
1093
  }
1047
1094
  },
1048
1095
  "required": [
1049
- "version",
1096
+ "schemaVersion",
1050
1097
  "createdAt",
1051
1098
  "updatedAt"
1052
1099
  ]
@@ -227,7 +227,6 @@ export class AgentSpecExecutionService {
227
227
  /** Get the config, throwing if not configured */
228
228
  getConfig() {
229
229
  this.ensureConfigured();
230
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
231
230
  return this.config;
232
231
  }
233
232
  startPolling(executionId, nameToNodeId, onNodeUpdate, onComplete, onError, intervalMs = 2000) {
@@ -6,6 +6,7 @@
6
6
  * @module services/apiVariableService
7
7
  */
8
8
  import type { VariableSchema, ApiVariablesConfig, AuthProvider } from '../types/index.js';
9
+ import type { EndpointConfig } from '../config/endpoints.js';
9
10
  /**
10
11
  * Context for variable API requests
11
12
  */
@@ -71,7 +72,7 @@ export declare function resolveEndpointUrl(template: string, context: VariableCo
71
72
  * }
72
73
  * ```
73
74
  */
74
- export declare function fetchVariableSchema(workflowId: string | undefined, nodeId: string, config: ApiVariablesConfig, authProvider?: AuthProvider): Promise<ApiVariableResult>;
75
+ export declare function fetchVariableSchema(endpointConfig: EndpointConfig | null, workflowId: string | undefined, nodeId: string, config: ApiVariablesConfig, authProvider?: AuthProvider): Promise<ApiVariableResult>;
75
76
  /**
76
77
  * Clears the variable schema cache.
77
78
  * Can optionally clear only entries matching a specific pattern.
@@ -5,7 +5,6 @@
5
5
  *
6
6
  * @module services/apiVariableService
7
7
  */
8
- import { getEndpointConfig } from './api.js';
9
8
  import { logger } from '../utils/logger.js';
10
9
  import { DEFAULT_CACHE_TTL_MS } from '../config/constants.js';
11
10
  /**
@@ -116,7 +115,7 @@ function resolveBodyTemplates(body, context) {
116
115
  * }
117
116
  * ```
118
117
  */
119
- export async function fetchVariableSchema(workflowId, nodeId, config, authProvider) {
118
+ export async function fetchVariableSchema(endpointConfig, workflowId, nodeId, config, authProvider) {
120
119
  const endpoint = config.endpoint;
121
120
  const context = { workflowId, nodeId };
122
121
  // Generate cache key
@@ -136,12 +135,9 @@ export async function fetchVariableSchema(workflowId, nodeId, config, authProvid
136
135
  // Resolve the URL with template variables
137
136
  let url = resolveEndpointUrl(endpoint.url, context);
138
137
  // If URL is relative, prepend base URL from endpoint config
139
- if (url.startsWith('/')) {
140
- const currentConfig = getEndpointConfig();
141
- if (currentConfig?.baseUrl) {
142
- const baseUrl = currentConfig.baseUrl.replace(/\/$/, '');
143
- url = `${baseUrl}${url}`;
144
- }
138
+ if (url.startsWith('/') && endpointConfig?.baseUrl) {
139
+ const baseUrl = endpointConfig.baseUrl.replace(/\/$/, '');
140
+ url = `${baseUrl}${url}`;
145
141
  }
146
142
  // Prepare request options
147
143
  const method = endpoint.method ?? 'GET';
@@ -161,19 +157,6 @@ export async function fetchVariableSchema(workflowId, nodeId, config, authProvid
161
157
  logger.warn('Failed to get auth headers:', error);
162
158
  }
163
159
  }
164
- // Add auth headers from endpoint config as fallback
165
- const currentConfig = getEndpointConfig();
166
- if (currentConfig?.auth) {
167
- if (currentConfig.auth.type === 'bearer' && currentConfig.auth.token) {
168
- headers['Authorization'] = headers['Authorization'] ?? `Bearer ${currentConfig.auth.token}`;
169
- }
170
- else if (currentConfig.auth.type === 'api_key' && currentConfig.auth.apiKey) {
171
- headers['X-API-Key'] = headers['X-API-Key'] ?? currentConfig.auth.apiKey;
172
- }
173
- else if (currentConfig.auth.type === 'custom' && currentConfig.auth.headers) {
174
- Object.assign(headers, currentConfig.auth.headers);
175
- }
176
- }
177
160
  // Prepare fetch options
178
161
  const fetchOptions = {
179
162
  method,
@@ -202,7 +185,7 @@ export async function fetchVariableSchema(workflowId, nodeId, config, authProvid
202
185
  const refreshed = await authProvider.onUnauthorized();
203
186
  if (refreshed) {
204
187
  // Retry with refreshed auth
205
- return fetchVariableSchema(workflowId, nodeId, config, authProvider);
188
+ return fetchVariableSchema(endpointConfig, workflowId, nodeId, config, authProvider);
206
189
  }
207
190
  }
208
191
  return {
@@ -23,6 +23,12 @@ interface AutoSaveOptions {
23
23
  * Optional callback for successful saves
24
24
  */
25
25
  onSuccess?: () => void;
26
+ /**
27
+ * Dirty-state probe for the owning FlowDrop instance, e.g.
28
+ * `() => fd.workflow.isDirty`. Defaults to the page-default
29
+ * instance's dirty state when omitted.
30
+ */
31
+ isDirty?: () => boolean;
26
32
  }
27
33
  /**
28
34
  * Initialize auto-save functionality based on user settings
@@ -68,6 +74,7 @@ export declare class AutoSaveManager {
68
74
  private onSave;
69
75
  private onError?;
70
76
  private onSuccess?;
77
+ private isDirtyProbe;
71
78
  /**
72
79
  * Create a new AutoSaveManager
73
80
  *
@@ -7,7 +7,7 @@
7
7
  * @module services/autoSaveService
8
8
  */
9
9
  import { getBehaviorSettings, onSettingsChange } from '../stores/settingsStore.svelte.js';
10
- import { isDirty } from '../stores/workflowStore.svelte.js';
10
+ import { getDefaultInstance } from '../stores/instanceContainer.svelte.js';
11
11
  import { logger } from '../utils/logger.js';
12
12
  /**
13
13
  * Initialize auto-save functionality based on user settings
@@ -40,7 +40,7 @@ import { logger } from '../utils/logger.js';
40
40
  * ```
41
41
  */
42
42
  export function initAutoSave(options) {
43
- const { onSave, onError, onSuccess } = options;
43
+ const { onSave, onError, onSuccess, isDirty: isDirtyProbe = () => getDefaultInstance().workflow.isDirty } = options;
44
44
  const state = {
45
45
  intervalId: null,
46
46
  isSaving: false,
@@ -52,7 +52,7 @@ export function initAutoSave(options) {
52
52
  */
53
53
  async function performAutoSave() {
54
54
  // Skip if already saving or not dirty
55
- if (state.isSaving || !isDirty()) {
55
+ if (state.isSaving || !isDirtyProbe()) {
56
56
  return;
57
57
  }
58
58
  state.isSaving = true;
@@ -127,6 +127,7 @@ export class AutoSaveManager {
127
127
  onSave;
128
128
  onError;
129
129
  onSuccess;
130
+ isDirtyProbe;
130
131
  /**
131
132
  * Create a new AutoSaveManager
132
133
  *
@@ -136,6 +137,7 @@ export class AutoSaveManager {
136
137
  this.onSave = options.onSave;
137
138
  this.onError = options.onError;
138
139
  this.onSuccess = options.onSuccess;
140
+ this.isDirtyProbe = options.isDirty ?? (() => getDefaultInstance().workflow.isDirty);
139
141
  }
140
142
  /**
141
143
  * Start the auto-save manager
@@ -207,7 +209,7 @@ export class AutoSaveManager {
207
209
  * Perform the save operation
208
210
  */
209
211
  async performSave() {
210
- if (this.isSaving || !isDirty()) {
212
+ if (this.isSaving || !this.isDirtyProbe()) {
211
213
  return;
212
214
  }
213
215
  this.isSaving = true;
@@ -7,6 +7,7 @@
7
7
  * @module services/chatService
8
8
  */
9
9
  import type { ChatRequest, ChatResponse, ChatHistoryMessage } from '../types/chat.js';
10
+ import type { EndpointConfig } from '../config/endpoints.js';
10
11
  /**
11
12
  * Chat Service class
12
13
  *
@@ -23,7 +24,9 @@ export declare class ChatService {
23
24
  */
24
25
  static getInstance(): ChatService;
25
26
  /**
26
- * Get the endpoint configuration
27
+ * Validate and return the caller-supplied endpoint configuration.
28
+ *
29
+ * Callers thread the config from `getInstance().api.config`.
27
30
  *
28
31
  * @throws Error if endpoint configuration is not set
29
32
  * @returns The endpoint configuration
@@ -32,6 +35,7 @@ export declare class ChatService {
32
35
  /**
33
36
  * Generic API request helper
34
37
  *
38
+ * @param config - The endpoint configuration
35
39
  * @param url - The URL to fetch
36
40
  * @param options - Fetch options
37
41
  * @returns The parsed JSON response
@@ -44,20 +48,20 @@ export declare class ChatService {
44
48
  * @param request - The chat request payload
45
49
  * @returns The chat response from the LLM
46
50
  */
47
- sendMessage(workflowId: string, request: ChatRequest): Promise<ChatResponse>;
51
+ sendMessage(endpointConfig: EndpointConfig | null, workflowId: string, request: ChatRequest): Promise<ChatResponse>;
48
52
  /**
49
53
  * Get conversation history for a workflow
50
54
  *
51
55
  * @param workflowId - The workflow ID
52
56
  * @returns Array of chat history messages
53
57
  */
54
- getHistory(workflowId: string): Promise<ChatHistoryMessage[]>;
58
+ getHistory(endpointConfig: EndpointConfig | null, workflowId: string): Promise<ChatHistoryMessage[]>;
55
59
  /**
56
60
  * Clear conversation history for a workflow
57
61
  *
58
62
  * @param workflowId - The workflow ID
59
63
  */
60
- clearHistory(workflowId: string): Promise<void>;
64
+ clearHistory(endpointConfig: EndpointConfig | null, workflowId: string): Promise<void>;
61
65
  }
62
66
  /**
63
67
  * Pre-instantiated ChatService singleton
@@ -7,7 +7,6 @@
7
7
  * @module services/chatService
8
8
  */
9
9
  import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
10
- import { getEndpointConfig } from './api.js';
11
10
  import { logger } from '../utils/logger.js';
12
11
  /**
13
12
  * Chat Service class
@@ -30,27 +29,28 @@ export class ChatService {
30
29
  return ChatService.instance;
31
30
  }
32
31
  /**
33
- * Get the endpoint configuration
32
+ * Validate and return the caller-supplied endpoint configuration.
33
+ *
34
+ * Callers thread the config from `getInstance().api.config`.
34
35
  *
35
36
  * @throws Error if endpoint configuration is not set
36
37
  * @returns The endpoint configuration
37
38
  */
38
- getConfig() {
39
- const config = getEndpointConfig();
39
+ getConfig(config) {
40
40
  if (!config) {
41
- throw new Error('Endpoint configuration not set. Call setEndpointConfig() first.');
41
+ throw new Error('Endpoint configuration not set. Configure the instance via fd.api.configure().');
42
42
  }
43
43
  return config;
44
44
  }
45
45
  /**
46
46
  * Generic API request helper
47
47
  *
48
+ * @param config - The endpoint configuration
48
49
  * @param url - The URL to fetch
49
50
  * @param options - Fetch options
50
51
  * @returns The parsed JSON response
51
52
  */
52
- async request(url, options = {}) {
53
- const config = this.getConfig();
53
+ async request(config, url, options = {}) {
54
54
  const headers = getEndpointHeaders(config, 'chat');
55
55
  const response = await fetch(url, {
56
56
  ...options,
@@ -83,13 +83,13 @@ export class ChatService {
83
83
  * @param request - The chat request payload
84
84
  * @returns The chat response from the LLM
85
85
  */
86
- async sendMessage(workflowId, request) {
87
- const config = this.getConfig();
86
+ async sendMessage(endpointConfig, workflowId, request) {
87
+ const config = this.getConfig(endpointConfig);
88
88
  const url = buildEndpointUrl(config, config.endpoints.chat.sendMessage, {
89
89
  id: workflowId
90
90
  });
91
91
  logger.debug('[ChatService] Sending message to', url);
92
- return this.request(url, {
92
+ return this.request(config, url, {
93
93
  method: 'POST',
94
94
  body: JSON.stringify(request)
95
95
  });
@@ -100,21 +100,21 @@ export class ChatService {
100
100
  * @param workflowId - The workflow ID
101
101
  * @returns Array of chat history messages
102
102
  */
103
- async getHistory(workflowId) {
104
- const config = this.getConfig();
103
+ async getHistory(endpointConfig, workflowId) {
104
+ const config = this.getConfig(endpointConfig);
105
105
  const url = buildEndpointUrl(config, config.endpoints.chat.getHistory, {
106
106
  id: workflowId
107
107
  });
108
108
  logger.debug('[ChatService] Getting history from', url);
109
- return this.request(url);
109
+ return this.request(config, url);
110
110
  }
111
111
  /**
112
112
  * Clear conversation history for a workflow
113
113
  *
114
114
  * @param workflowId - The workflow ID
115
115
  */
116
- async clearHistory(workflowId) {
117
- const config = this.getConfig();
116
+ async clearHistory(endpointConfig, workflowId) {
117
+ const config = this.getConfig(endpointConfig);
118
118
  const url = buildEndpointUrl(config, config.endpoints.chat.clearHistory, {
119
119
  id: workflowId
120
120
  });