@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.
- package/CHANGELOG.md +475 -0
- package/MIGRATION-2.0.md +472 -0
- package/README.md +23 -23
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +14 -8
- package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
- package/dist/chat/batchFeedback.d.ts +39 -0
- package/dist/chat/batchFeedback.js +51 -0
- package/dist/commands/executor.js +15 -1
- package/dist/commands/storeIntegration.svelte.d.ts +4 -1
- package/dist/commands/storeIntegration.svelte.js +26 -21
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/App.svelte +162 -192
- package/dist/components/App.svelte.d.ts +47 -8
- package/dist/components/ConfigForm.svelte +110 -66
- package/dist/components/ConfigModal.svelte +7 -2
- package/dist/components/ConnectionLine.svelte +4 -2
- package/dist/components/Navbar.svelte +61 -1
- package/dist/components/NodeSidebar.svelte +27 -45
- package/dist/components/NodeStatusOverlay.svelte +94 -6
- package/dist/components/NodeSwapPicker.svelte +10 -8
- package/dist/components/PipelineStatus.svelte +16 -67
- package/dist/components/PortCoordinateTracker.svelte +5 -6
- package/dist/components/SchemaForm.stories.svelte +1 -3
- package/dist/components/SchemaForm.svelte +45 -40
- package/dist/components/SchemaForm.svelte.d.ts +0 -8
- package/dist/components/SettingsModal.svelte +8 -3
- package/dist/components/SettingsPanel.svelte +20 -4
- package/dist/components/SwapMappingEditor.svelte +67 -49
- package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
- package/dist/components/UniversalNode.svelte +9 -7
- package/dist/components/WorkflowEditor.svelte +118 -111
- package/dist/components/WorkflowEditor.svelte.d.ts +18 -10
- package/dist/components/chat/AIChatPanel.svelte +93 -89
- package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
- package/dist/components/chat/CommandPreview.svelte +2 -1
- package/dist/components/console/CommandConsole.svelte +7 -5
- package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
- package/dist/components/console/ConsoleInput.svelte +15 -6
- package/dist/components/console/ConsoleOutput.svelte +2 -1
- package/dist/components/form/FormArray.svelte +5 -9
- package/dist/components/form/FormArray.svelte.d.ts +2 -1
- package/dist/components/form/FormAutocomplete.svelte +29 -13
- package/dist/components/form/FormField.svelte +4 -2
- package/dist/components/form/FormFieldLight.svelte +4 -2
- package/dist/components/form/FormMarkdownEditor.svelte +9 -4
- package/dist/components/form/FormRangeField.svelte +1 -0
- package/dist/components/form/FormTemplateEditor.svelte +11 -3
- package/dist/components/form/FormToggle.svelte +5 -12
- package/dist/components/form/FormToggle.svelte.d.ts +4 -2
- package/dist/components/form/templateAutocomplete.js +1 -5
- package/dist/components/form/types.d.ts +1 -14
- package/dist/components/interrupt/FormPrompt.svelte +3 -2
- package/dist/components/interrupt/InterruptBubble.svelte +16 -17
- package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
- package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
- package/dist/components/layouts/MainLayout.svelte +20 -13
- package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
- package/dist/components/nodes/AtomNode.svelte +292 -0
- package/dist/components/nodes/AtomNode.svelte.d.ts +26 -0
- package/dist/components/nodes/GatewayNode.svelte +19 -10
- package/dist/components/nodes/IdeaNode.svelte +7 -0
- package/dist/components/nodes/SimpleNode.svelte +11 -6
- package/dist/components/nodes/SquareNode.svelte +15 -8
- package/dist/components/nodes/TerminalNode.svelte +9 -4
- package/dist/components/nodes/ToolNode.svelte +7 -1
- package/dist/components/nodes/WorkflowNode.svelte +16 -7
- package/dist/components/playground/ChatInput.svelte +11 -14
- package/dist/components/playground/ChatPanel.svelte +6 -49
- package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
- package/dist/components/playground/ControlPanel.svelte +134 -123
- package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
- package/dist/components/playground/ExecutionLogs.svelte +11 -9
- package/dist/components/playground/InputCollector.svelte +11 -9
- package/dist/components/playground/MessageStream.svelte +17 -23
- package/dist/components/playground/PipelineKanbanView.svelte +65 -6
- package/dist/components/playground/PipelinePanel.svelte +11 -5
- package/dist/components/playground/PipelineTableView.svelte +186 -44
- package/dist/components/playground/Playground.svelte +95 -92
- package/dist/components/playground/Playground.svelte.d.ts +2 -0
- package/dist/components/playground/PlaygroundApp.svelte +6 -1
- package/dist/components/playground/PlaygroundApp.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundModal.svelte +13 -3
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -0
- package/dist/components/playground/PlaygroundStudio.svelte +34 -32
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +3 -0
- package/dist/components/playground/SessionManager.svelte +9 -12
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +28 -0
- package/dist/components/playground/pipelineViewUtils.svelte.js +38 -1
- package/dist/config/endpoints.d.ts +0 -7
- package/dist/config/endpoints.js +2 -10
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +6 -6
- package/dist/display/index.d.ts +0 -2
- package/dist/display/index.js +0 -6
- package/dist/editor/index.d.ts +19 -20
- package/dist/editor/index.js +25 -35
- package/dist/form/code.d.ts +25 -15
- package/dist/form/code.js +44 -41
- package/dist/form/fieldRegistry.d.ts +17 -13
- package/dist/form/fieldRegistry.js +32 -12
- package/dist/form/full.d.ts +17 -13
- package/dist/form/full.js +22 -27
- package/dist/form/index.d.ts +3 -3
- package/dist/form/index.js +3 -3
- package/dist/form/markdown.d.ts +13 -8
- package/dist/form/markdown.js +22 -23
- package/dist/helpers/proximityConnect.d.ts +7 -3
- package/dist/helpers/proximityConnect.js +19 -6
- package/dist/helpers/workflowEditorHelper.d.ts +12 -5
- package/dist/helpers/workflowEditorHelper.js +27 -25
- package/dist/index.d.ts +28 -24
- package/dist/index.js +27 -50
- package/dist/messages/defaults.d.ts +2 -5
- package/dist/messages/defaults.js +3 -6
- package/dist/messages/index.d.ts +0 -1
- package/dist/messages/index.js +0 -1
- package/dist/mocks/app-forms.d.ts +6 -2
- package/dist/mocks/app-forms.js +11 -4
- package/dist/openapi/v1/openapi.yaml +227 -164
- package/dist/playground/index.d.ts +2 -3
- package/dist/playground/index.js +2 -30
- package/dist/playground/mount.d.ts +15 -0
- package/dist/playground/mount.js +46 -20
- package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
- package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
- package/dist/registry/builtinFormats.d.ts +9 -18
- package/dist/registry/builtinFormats.js +9 -39
- package/dist/registry/builtinNodes.d.ts +1 -26
- package/dist/registry/builtinNodes.js +14 -50
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.js +4 -6
- package/dist/registry/nodeComponentRegistry.d.ts +182 -15
- package/dist/registry/nodeComponentRegistry.js +235 -17
- package/dist/registry/workflowFormatRegistry.d.ts +14 -9
- package/dist/registry/workflowFormatRegistry.js +24 -8
- package/dist/{schema → schemas}/index.d.ts +2 -2
- package/dist/{schema → schemas}/index.js +2 -2
- package/dist/schemas/v1/workflow.schema.json +53 -6
- package/dist/services/agentSpecExecutionService.js +0 -1
- package/dist/services/apiVariableService.d.ts +2 -1
- package/dist/services/apiVariableService.js +5 -22
- package/dist/services/autoSaveService.d.ts +7 -0
- package/dist/services/autoSaveService.js +6 -4
- package/dist/services/chatService.d.ts +8 -4
- package/dist/services/chatService.js +15 -15
- package/dist/services/draftStorage.d.ts +129 -13
- package/dist/services/draftStorage.js +185 -37
- package/dist/services/dynamicSchemaService.d.ts +2 -1
- package/dist/services/dynamicSchemaService.js +5 -22
- package/dist/services/globalSave.d.ts +13 -12
- package/dist/services/globalSave.js +29 -51
- package/dist/services/historyService.d.ts +9 -3
- package/dist/services/historyService.js +9 -3
- package/dist/services/interruptService.d.ts +14 -9
- package/dist/services/interruptService.js +27 -27
- package/dist/services/nodeExecutionService.d.ts +18 -3
- package/dist/services/nodeExecutionService.js +71 -45
- package/dist/services/playgroundService.d.ts +14 -9
- package/dist/services/playgroundService.js +31 -30
- package/dist/services/variableService.d.ts +2 -1
- package/dist/services/variableService.js +2 -2
- package/dist/services/workflowStorage.js +6 -6
- package/dist/stores/apiContext.d.ts +45 -0
- package/dist/stores/apiContext.js +65 -0
- package/dist/stores/categoriesStore.svelte.d.ts +28 -23
- package/dist/stores/categoriesStore.svelte.js +70 -64
- package/dist/stores/getInstance.svelte.d.ts +39 -0
- package/dist/stores/getInstance.svelte.js +65 -0
- package/dist/stores/historyStore.svelte.d.ts +77 -93
- package/dist/stores/historyStore.svelte.js +134 -160
- package/dist/stores/instanceContainer.svelte.d.ts +111 -0
- package/dist/stores/instanceContainer.svelte.js +114 -0
- package/dist/stores/interruptStore.svelte.d.ts +112 -82
- package/dist/stores/interruptStore.svelte.js +253 -226
- package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
- package/dist/stores/pipelinePanelStore.svelte.js +61 -14
- package/dist/stores/playgroundStore.svelte.d.ts +169 -216
- package/dist/stores/playgroundStore.svelte.js +515 -572
- package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
- package/dist/stores/portCoordinateStore.svelte.js +109 -98
- package/dist/stores/settingsStore.svelte.d.ts +4 -1
- package/dist/stores/settingsStore.svelte.js +47 -12
- package/dist/stores/workflowStore.svelte.d.ts +178 -213
- package/dist/stores/workflowStore.svelte.js +449 -501
- package/dist/stories/EdgeDecorator.svelte +5 -2
- package/dist/stories/NodeDecorator.svelte +5 -3
- package/dist/svelte-app.d.ts +60 -10
- package/dist/svelte-app.js +157 -53
- package/dist/types/events.d.ts +6 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/navbar.d.ts +7 -0
- package/dist/types/playground.d.ts +18 -3
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.js +1 -0
- package/dist/utils/colors.d.ts +47 -21
- package/dist/utils/colors.js +69 -68
- package/dist/utils/connections.d.ts +9 -15
- package/dist/utils/connections.js +13 -32
- package/dist/utils/duration.d.ts +13 -0
- package/dist/utils/duration.js +45 -0
- package/dist/utils/formMerge.d.ts +36 -0
- package/dist/utils/formMerge.js +70 -0
- package/dist/utils/icons.d.ts +5 -2
- package/dist/utils/icons.js +6 -5
- package/dist/utils/nodeSwap.d.ts +6 -2
- package/dist/utils/nodeSwap.js +62 -126
- package/dist/utils/nodeTypes.d.ts +17 -8
- package/dist/utils/nodeTypes.js +27 -19
- package/dist/utils/performanceUtils.js +7 -0
- package/package.json +6 -5
- package/dist/messages/deprecation.d.ts +0 -20
- package/dist/messages/deprecation.js +0 -33
- package/dist/registry/plugin.d.ts +0 -215
- package/dist/registry/plugin.js +0 -249
- package/dist/services/api.d.ts +0 -129
- package/dist/services/api.js +0 -217
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module services/dynamicSchemaService
|
|
7
7
|
*/
|
|
8
|
-
import { getEndpointConfig } from './api.js';
|
|
9
8
|
import { DEFAULT_CACHE_TTL_MS } from '../config/constants.js';
|
|
10
9
|
/**
|
|
11
10
|
* Schema cache with TTL support
|
|
@@ -137,7 +136,7 @@ function isCacheValid(entry, ttl = DEFAULT_CACHE_TTL) {
|
|
|
137
136
|
* }
|
|
138
137
|
* ```
|
|
139
138
|
*/
|
|
140
|
-
export async function fetchDynamicSchema(endpoint, node, workflowId) {
|
|
139
|
+
export async function fetchDynamicSchema(endpointConfig, endpoint, node, workflowId) {
|
|
141
140
|
// Build the context from the node
|
|
142
141
|
const context = {
|
|
143
142
|
id: node.id,
|
|
@@ -163,13 +162,10 @@ export async function fetchDynamicSchema(endpoint, node, workflowId) {
|
|
|
163
162
|
// Resolve the URL with template variables
|
|
164
163
|
let url = resolveTemplate(endpoint.url, endpoint.parameterMapping, context);
|
|
165
164
|
// If URL is relative, try to prepend base URL from endpoint config
|
|
166
|
-
if (url.startsWith('/')) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const baseUrl = currentConfig.baseUrl.replace(/\/$/, '');
|
|
171
|
-
url = `${baseUrl}${url}`;
|
|
172
|
-
}
|
|
165
|
+
if (url.startsWith('/') && endpointConfig?.baseUrl) {
|
|
166
|
+
// Remove trailing slash from base URL and leading slash from relative URL
|
|
167
|
+
const baseUrl = endpointConfig.baseUrl.replace(/\/$/, '');
|
|
168
|
+
url = `${baseUrl}${url}`;
|
|
173
169
|
}
|
|
174
170
|
// Prepare request options
|
|
175
171
|
const method = endpoint.method ?? 'GET';
|
|
@@ -179,19 +175,6 @@ export async function fetchDynamicSchema(endpoint, node, workflowId) {
|
|
|
179
175
|
'Content-Type': 'application/json',
|
|
180
176
|
...endpoint.headers
|
|
181
177
|
};
|
|
182
|
-
// Add auth headers from endpoint config if available
|
|
183
|
-
const currentConfig = getEndpointConfig();
|
|
184
|
-
if (currentConfig?.auth) {
|
|
185
|
-
if (currentConfig.auth.type === 'bearer' && currentConfig.auth.token) {
|
|
186
|
-
headers['Authorization'] = `Bearer ${currentConfig.auth.token}`;
|
|
187
|
-
}
|
|
188
|
-
else if (currentConfig.auth.type === 'api_key' && currentConfig.auth.apiKey) {
|
|
189
|
-
headers['X-API-Key'] = currentConfig.auth.apiKey;
|
|
190
|
-
}
|
|
191
|
-
else if (currentConfig.auth.type === 'custom' && currentConfig.auth.headers) {
|
|
192
|
-
Object.assign(headers, currentConfig.auth.headers);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
178
|
// Prepare fetch options
|
|
196
179
|
const fetchOptions = {
|
|
197
180
|
method,
|
|
@@ -7,24 +7,15 @@
|
|
|
7
7
|
* reimplementing the logic, ensuring "blur active element" flushing and metadata
|
|
8
8
|
* construction happen in exactly one place.
|
|
9
9
|
*/
|
|
10
|
+
import { type FlowDropInstance } from '../stores/instanceContainer.svelte.js';
|
|
10
11
|
import type { Workflow } from '../types/index.js';
|
|
11
12
|
import type { FlowDropEventHandlers, FlowDropFeatures } from '../types/events.js';
|
|
12
|
-
/**
|
|
13
|
-
* Minimal interface for the enhanced API client used when an authProvider is present.
|
|
14
|
-
* Matches the surface of EnhancedFlowDropApiClient that save needs.
|
|
15
|
-
*/
|
|
16
|
-
export interface SaveApiClient {
|
|
17
|
-
saveWorkflow(workflow: Workflow): Promise<Workflow>;
|
|
18
|
-
updateWorkflow(id: string, workflow: Workflow): Promise<Workflow>;
|
|
19
|
-
}
|
|
20
13
|
/**
|
|
21
14
|
* Options accepted by globalSaveWorkflow().
|
|
22
15
|
* All fields are optional — omitting them falls back to the basic behaviour
|
|
23
|
-
* (no event handlers, always show toasts
|
|
16
|
+
* (no event handlers, always show toasts).
|
|
24
17
|
*/
|
|
25
18
|
export interface GlobalSaveOptions {
|
|
26
|
-
/** Enhanced API client with authProvider support. Falls back to legacy workflowApi when absent. */
|
|
27
|
-
apiClient?: SaveApiClient | null;
|
|
28
19
|
/** Event handler hooks (onBeforeSave, onAfterSave, onSaveError, onApiError). */
|
|
29
20
|
eventHandlers?: FlowDropEventHandlers;
|
|
30
21
|
/** Feature flags (showToasts). Defaults to DEFAULT_FEATURES. */
|
|
@@ -41,6 +32,11 @@ export interface GlobalSaveOptions {
|
|
|
41
32
|
* Use this to update draft storage keys or other ID-dependent state.
|
|
42
33
|
*/
|
|
43
34
|
onSaved?: (savedWorkflow: Workflow) => void;
|
|
35
|
+
/**
|
|
36
|
+
* The FlowDrop instance whose workflow should be saved.
|
|
37
|
+
* Defaults to the page-default instance when omitted.
|
|
38
|
+
*/
|
|
39
|
+
instance?: FlowDropInstance;
|
|
44
40
|
}
|
|
45
41
|
/**
|
|
46
42
|
* Options accepted by globalExportWorkflow().
|
|
@@ -48,6 +44,11 @@ export interface GlobalSaveOptions {
|
|
|
48
44
|
export interface GlobalExportOptions {
|
|
49
45
|
/** Feature flags (showToasts). Defaults to DEFAULT_FEATURES. */
|
|
50
46
|
features?: Partial<FlowDropFeatures>;
|
|
47
|
+
/**
|
|
48
|
+
* The FlowDrop instance whose workflow should be exported.
|
|
49
|
+
* Defaults to the page-default instance when omitted.
|
|
50
|
+
*/
|
|
51
|
+
instance?: FlowDropInstance;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
54
|
* Save the current workflow to the backend.
|
|
@@ -59,7 +60,7 @@ export interface GlobalExportOptions {
|
|
|
59
60
|
* 1. Flush pending form changes (blur active element + tick)
|
|
60
61
|
* 2. Optionally call onBeforeSave — return false cancels the save
|
|
61
62
|
* 3. Build the canonical Workflow object (preserving metadata, format, etc.)
|
|
62
|
-
* 4. Persist via
|
|
63
|
+
* 4. Persist via the instance's API client (fd.api.client)
|
|
63
64
|
* 5. Update the store if the server assigned a new ID
|
|
64
65
|
* 6. Call onMarkAsSaved / onAfterSave hooks
|
|
65
66
|
* 7. Show toast notifications (respecting features.showToasts)
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
* construction happen in exactly one place.
|
|
9
9
|
*/
|
|
10
10
|
import { tick } from 'svelte';
|
|
11
|
-
import {
|
|
12
|
-
import { workflowApi, setEndpointConfig } from './api.js';
|
|
11
|
+
import { getDefaultInstance } from '../stores/instanceContainer.svelte.js';
|
|
13
12
|
import { createEndpointConfig } from '../config/endpoints.js';
|
|
14
13
|
import { v4 as uuidv4 } from 'uuid';
|
|
15
14
|
import { DEFAULT_WORKFLOW_FORMAT } from '../types/index.js';
|
|
@@ -19,29 +18,19 @@ import { DEFAULT_FEATURES } from '../types/events.js';
|
|
|
19
18
|
// Internal helpers
|
|
20
19
|
// ---------------------------------------------------------------------------
|
|
21
20
|
/**
|
|
22
|
-
* Ensure API
|
|
23
|
-
* This is needed when the global save function is called from the layout
|
|
24
|
-
* which doesn't initialize the API configuration like
|
|
21
|
+
* Ensure the instance's API context is configured.
|
|
22
|
+
* This is needed when the global save function is called from the layout
|
|
23
|
+
* component which doesn't initialize the API configuration like <App> does.
|
|
25
24
|
*/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const currentConfig = getEndpointConfig();
|
|
30
|
-
if (currentConfig && currentConfig.baseUrl) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
// Could not check existing API configuration, initializing
|
|
25
|
+
function ensureApiConfiguration(fd) {
|
|
26
|
+
if (fd.api.isConfigured() && fd.api.config?.baseUrl) {
|
|
27
|
+
return;
|
|
36
28
|
}
|
|
37
29
|
// API configuration is not initialized — derive URL from window.location when available
|
|
38
30
|
const apiBaseUrl = typeof window !== 'undefined'
|
|
39
31
|
? `${window.location.protocol}//${window.location.host}/api/flowdrop`
|
|
40
32
|
: '/api/flowdrop';
|
|
41
33
|
const config = createEndpointConfig(apiBaseUrl, {
|
|
42
|
-
auth: {
|
|
43
|
-
type: 'none'
|
|
44
|
-
},
|
|
45
34
|
timeout: 10000,
|
|
46
35
|
retry: {
|
|
47
36
|
enabled: true,
|
|
@@ -50,7 +39,7 @@ async function ensureApiConfiguration() {
|
|
|
50
39
|
backoff: 'exponential'
|
|
51
40
|
}
|
|
52
41
|
});
|
|
53
|
-
|
|
42
|
+
fd.api.configure(config);
|
|
54
43
|
}
|
|
55
44
|
/**
|
|
56
45
|
* Flush any pending form changes by blurring the active element.
|
|
@@ -79,18 +68,21 @@ async function flushPendingFormChanges() {
|
|
|
79
68
|
* 1. Flush pending form changes (blur active element + tick)
|
|
80
69
|
* 2. Optionally call onBeforeSave — return false cancels the save
|
|
81
70
|
* 3. Build the canonical Workflow object (preserving metadata, format, etc.)
|
|
82
|
-
* 4. Persist via
|
|
71
|
+
* 4. Persist via the instance's API client (fd.api.client)
|
|
83
72
|
* 5. Update the store if the server assigned a new ID
|
|
84
73
|
* 6. Call onMarkAsSaved / onAfterSave hooks
|
|
85
74
|
* 7. Show toast notifications (respecting features.showToasts)
|
|
86
75
|
*/
|
|
87
76
|
export async function globalSaveWorkflow(options = {}) {
|
|
88
|
-
const {
|
|
77
|
+
const { eventHandlers, onMarkAsSaved, onSaved } = options;
|
|
89
78
|
const features = { ...DEFAULT_FEATURES, ...options.features };
|
|
79
|
+
// Resolve the target instance; defaults to the page-default instance.
|
|
80
|
+
const fd = options.instance ?? getDefaultInstance();
|
|
81
|
+
const readWorkflow = () => fd.workflow.current;
|
|
90
82
|
// Step 1 — Flush pending form changes (single location for this logic)
|
|
91
83
|
await flushPendingFormChanges();
|
|
92
|
-
// Get current workflow from
|
|
93
|
-
const currentWorkflow =
|
|
84
|
+
// Get current workflow from the instance's store after flush
|
|
85
|
+
const currentWorkflow = readWorkflow();
|
|
94
86
|
if (!currentWorkflow) {
|
|
95
87
|
if (features.showToasts) {
|
|
96
88
|
apiToasts.error('Save workflow', 'No workflow to save');
|
|
@@ -107,7 +99,7 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
107
99
|
const loadingToast = features.showToasts ? apiToasts.loading('Saving workflow') : null;
|
|
108
100
|
try {
|
|
109
101
|
// Ensure API configuration is initialised (needed when called outside App.svelte)
|
|
110
|
-
|
|
102
|
+
ensureApiConfiguration(fd);
|
|
111
103
|
// Step 3 — Build the canonical workflow object.
|
|
112
104
|
// Preserve all existing metadata fields (format, tags, etc.) so nothing is dropped.
|
|
113
105
|
//
|
|
@@ -124,35 +116,21 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
124
116
|
edges: currentWorkflow.edges || [],
|
|
125
117
|
metadata: {
|
|
126
118
|
...currentWorkflow.metadata,
|
|
127
|
-
|
|
119
|
+
schemaVersion: currentWorkflow.metadata?.schemaVersion || '1.0.0',
|
|
128
120
|
format: currentWorkflow.metadata?.format || DEFAULT_WORKFLOW_FORMAT,
|
|
129
121
|
createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
|
|
130
122
|
updatedAt: new Date().toISOString()
|
|
131
123
|
}
|
|
132
124
|
};
|
|
133
|
-
// Step 4 — Persist
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
else {
|
|
140
|
-
savedWorkflow = await apiClient.saveWorkflow(finalWorkflow);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
// Legacy path
|
|
145
|
-
if (isExistingWorkflow) {
|
|
146
|
-
savedWorkflow = await workflowApi.updateWorkflow(finalWorkflow.id, finalWorkflow);
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
const { id: _id, ...workflowData } = finalWorkflow;
|
|
150
|
-
savedWorkflow = await workflowApi.createWorkflow(workflowData);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
125
|
+
// Step 4 — Persist via this instance's API client.
|
|
126
|
+
// id-presence (computed above) decides update vs create — never UUID-regex.
|
|
127
|
+
const apiClient = fd.api.client;
|
|
128
|
+
const savedWorkflow = isExistingWorkflow
|
|
129
|
+
? await apiClient.updateWorkflow(finalWorkflow.id, finalWorkflow)
|
|
130
|
+
: await apiClient.saveWorkflow(finalWorkflow);
|
|
153
131
|
// Step 5 — If the server assigned a new ID, sync the store
|
|
154
132
|
if (savedWorkflow.id && savedWorkflow.id !== finalWorkflow.id) {
|
|
155
|
-
|
|
133
|
+
fd.workflow.batchUpdate({
|
|
156
134
|
nodes: finalWorkflow.nodes,
|
|
157
135
|
edges: finalWorkflow.edges,
|
|
158
136
|
name: finalWorkflow.name,
|
|
@@ -167,8 +145,7 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
167
145
|
onMarkAsSaved();
|
|
168
146
|
}
|
|
169
147
|
else {
|
|
170
|
-
|
|
171
|
-
storeMarkAsSaved();
|
|
148
|
+
fd.workflow.markAsSaved();
|
|
172
149
|
}
|
|
173
150
|
// Notify caller with the definitive saved workflow (server-assigned ID)
|
|
174
151
|
if (onSaved) {
|
|
@@ -190,7 +167,7 @@ export async function globalSaveWorkflow(options = {}) {
|
|
|
190
167
|
dismissToast(loadingToast);
|
|
191
168
|
const errorObj = error instanceof Error ? error : new Error('Unknown error occurred');
|
|
192
169
|
// onSaveError hook
|
|
193
|
-
const currentWorkflowForError =
|
|
170
|
+
const currentWorkflowForError = readWorkflow();
|
|
194
171
|
if (eventHandlers?.onSaveError && currentWorkflowForError) {
|
|
195
172
|
await eventHandlers.onSaveError(errorObj, currentWorkflowForError);
|
|
196
173
|
}
|
|
@@ -218,7 +195,8 @@ export async function globalExportWorkflow(options = {}) {
|
|
|
218
195
|
try {
|
|
219
196
|
// Flush pending changes before exporting (same discipline as save)
|
|
220
197
|
await flushPendingFormChanges();
|
|
221
|
-
const
|
|
198
|
+
const fd = options.instance ?? getDefaultInstance();
|
|
199
|
+
const currentWorkflow = fd.workflow.current;
|
|
222
200
|
if (!currentWorkflow) {
|
|
223
201
|
if (features.showToasts) {
|
|
224
202
|
apiToasts.error('Export workflow', 'No workflow to export');
|
|
@@ -234,7 +212,7 @@ export async function globalExportWorkflow(options = {}) {
|
|
|
234
212
|
edges: currentWorkflow.edges || [],
|
|
235
213
|
metadata: {
|
|
236
214
|
...currentWorkflow.metadata,
|
|
237
|
-
|
|
215
|
+
schemaVersion: currentWorkflow.metadata?.schemaVersion || '1.0.0',
|
|
238
216
|
format: currentWorkflow.metadata?.format || DEFAULT_WORKFLOW_FORMAT,
|
|
239
217
|
createdAt: currentWorkflow.metadata?.createdAt || new Date().toISOString(),
|
|
240
218
|
updatedAt: new Date().toISOString()
|
|
@@ -202,9 +202,15 @@ export declare class HistoryService {
|
|
|
202
202
|
private notifyChange;
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
205
|
-
*
|
|
205
|
+
* History service of the page-default FlowDrop instance.
|
|
206
206
|
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
207
|
+
* Kept as a module-level export for backward compatibility (re-exported via
|
|
208
|
+
* `@flowdrop/flowdrop/editor`) — `createFlowDropInstance({ isDefault: true })`
|
|
209
|
+
* adopts this exact object, so legacy `historyService.undo()` calls keep
|
|
210
|
+
* operating on the default editor's history stack. Holds no reactive state,
|
|
211
|
+
* so module-level construction is SSR-safe.
|
|
212
|
+
*
|
|
213
|
+
* For additional editor instances, the instance container creates a fresh
|
|
214
|
+
* `new HistoryService()` — use `getInstance().history` in components.
|
|
209
215
|
*/
|
|
210
216
|
export declare const historyService: HistoryService;
|
|
@@ -328,9 +328,15 @@ export class HistoryService {
|
|
|
328
328
|
// Singleton Instance
|
|
329
329
|
// =========================================================================
|
|
330
330
|
/**
|
|
331
|
-
*
|
|
331
|
+
* History service of the page-default FlowDrop instance.
|
|
332
332
|
*
|
|
333
|
-
*
|
|
334
|
-
*
|
|
333
|
+
* Kept as a module-level export for backward compatibility (re-exported via
|
|
334
|
+
* `@flowdrop/flowdrop/editor`) — `createFlowDropInstance({ isDefault: true })`
|
|
335
|
+
* adopts this exact object, so legacy `historyService.undo()` calls keep
|
|
336
|
+
* operating on the default editor's history stack. Holds no reactive state,
|
|
337
|
+
* so module-level construction is SSR-safe.
|
|
338
|
+
*
|
|
339
|
+
* For additional editor instances, the instance container creates a fresh
|
|
340
|
+
* `new HistoryService()` — use `getInstance().history` in components.
|
|
335
341
|
*/
|
|
336
342
|
export const historyService = new HistoryService();
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module services/interruptService
|
|
9
9
|
*/
|
|
10
10
|
import type { Interrupt, InterruptPollingConfig } from '../types/interrupt.js';
|
|
11
|
+
import type { EndpointConfig } from '../config/endpoints.js';
|
|
11
12
|
/**
|
|
12
13
|
* Interrupt Service class
|
|
13
14
|
*
|
|
@@ -46,19 +47,23 @@ export declare class InterruptService {
|
|
|
46
47
|
/**
|
|
47
48
|
* Check if interrupt endpoints are configured
|
|
48
49
|
*
|
|
50
|
+
* @param config - The instance's endpoint configuration (from fd.api.config)
|
|
49
51
|
* @returns True if interrupt endpoints are available
|
|
50
52
|
*/
|
|
51
|
-
isConfigured(): boolean;
|
|
53
|
+
isConfigured(config: EndpointConfig | null): boolean;
|
|
52
54
|
/**
|
|
53
|
-
*
|
|
55
|
+
* Validate and return the caller-supplied endpoint configuration.
|
|
54
56
|
*
|
|
55
|
-
*
|
|
57
|
+
* Callers thread the config from `getInstance().api.config`.
|
|
58
|
+
*
|
|
59
|
+
* @throws Error if endpoint configuration is not set or lacks interrupts
|
|
56
60
|
* @returns The endpoint configuration
|
|
57
61
|
*/
|
|
58
62
|
private getConfig;
|
|
59
63
|
/**
|
|
60
64
|
* Generic API request helper
|
|
61
65
|
*
|
|
66
|
+
* @param config - The endpoint configuration
|
|
62
67
|
* @param url - The URL to fetch
|
|
63
68
|
* @param options - Fetch options
|
|
64
69
|
* @returns The parsed JSON response
|
|
@@ -70,7 +75,7 @@ export declare class InterruptService {
|
|
|
70
75
|
* @param interruptId - The interrupt UUID
|
|
71
76
|
* @returns The interrupt details
|
|
72
77
|
*/
|
|
73
|
-
getInterrupt(interruptId: string): Promise<Interrupt>;
|
|
78
|
+
getInterrupt(endpointConfig: EndpointConfig | null, interruptId: string): Promise<Interrupt>;
|
|
74
79
|
/**
|
|
75
80
|
* Resolve an interrupt with user response
|
|
76
81
|
*
|
|
@@ -78,28 +83,28 @@ export declare class InterruptService {
|
|
|
78
83
|
* @param value - The user's response value
|
|
79
84
|
* @returns The updated interrupt
|
|
80
85
|
*/
|
|
81
|
-
resolveInterrupt(interruptId: string, value: unknown): Promise<Interrupt>;
|
|
86
|
+
resolveInterrupt(endpointConfig: EndpointConfig | null, interruptId: string, value: unknown): Promise<Interrupt>;
|
|
82
87
|
/**
|
|
83
88
|
* Cancel a pending interrupt
|
|
84
89
|
*
|
|
85
90
|
* @param interruptId - The interrupt UUID
|
|
86
91
|
* @returns The updated interrupt
|
|
87
92
|
*/
|
|
88
|
-
cancelInterrupt(interruptId: string): Promise<Interrupt>;
|
|
93
|
+
cancelInterrupt(endpointConfig: EndpointConfig | null, interruptId: string): Promise<Interrupt>;
|
|
89
94
|
/**
|
|
90
95
|
* List interrupts for a playground session
|
|
91
96
|
*
|
|
92
97
|
* @param sessionId - The session UUID
|
|
93
98
|
* @returns Array of interrupts for the session
|
|
94
99
|
*/
|
|
95
|
-
listSessionInterrupts(sessionId: string): Promise<Interrupt[]>;
|
|
100
|
+
listSessionInterrupts(endpointConfig: EndpointConfig | null, sessionId: string): Promise<Interrupt[]>;
|
|
96
101
|
/**
|
|
97
102
|
* List interrupts for a pipeline
|
|
98
103
|
*
|
|
99
104
|
* @param pipelineId - The pipeline UUID
|
|
100
105
|
* @returns Array of interrupts for the pipeline
|
|
101
106
|
*/
|
|
102
|
-
listPipelineInterrupts(pipelineId: string): Promise<Interrupt[]>;
|
|
107
|
+
listPipelineInterrupts(endpointConfig: EndpointConfig | null, pipelineId: string): Promise<Interrupt[]>;
|
|
103
108
|
/**
|
|
104
109
|
* Start polling for interrupts in a session
|
|
105
110
|
*
|
|
@@ -109,7 +114,7 @@ export declare class InterruptService {
|
|
|
109
114
|
* @param sessionId - The session UUID to poll
|
|
110
115
|
* @param callback - Callback function to handle new interrupts
|
|
111
116
|
*/
|
|
112
|
-
startPolling(sessionId: string, callback: (interrupts: Interrupt[]) => void): void;
|
|
117
|
+
startPolling(endpointConfig: EndpointConfig | null, sessionId: string, callback: (interrupts: Interrupt[]) => void): void;
|
|
113
118
|
/**
|
|
114
119
|
* Stop polling for interrupts
|
|
115
120
|
*/
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { defaultInterruptPollingConfig } from '../types/interrupt.js';
|
|
11
11
|
import { buildEndpointUrl, getEndpointHeaders } from '../config/endpoints.js';
|
|
12
|
-
import { getEndpointConfig } from './api.js';
|
|
13
12
|
import { logger } from '../utils/logger.js';
|
|
14
13
|
/**
|
|
15
14
|
* Interrupt Service class
|
|
@@ -62,22 +61,23 @@ export class InterruptService {
|
|
|
62
61
|
/**
|
|
63
62
|
* Check if interrupt endpoints are configured
|
|
64
63
|
*
|
|
64
|
+
* @param config - The instance's endpoint configuration (from fd.api.config)
|
|
65
65
|
* @returns True if interrupt endpoints are available
|
|
66
66
|
*/
|
|
67
|
-
isConfigured() {
|
|
68
|
-
const config = getEndpointConfig();
|
|
67
|
+
isConfigured(config) {
|
|
69
68
|
return Boolean(config?.endpoints?.interrupts);
|
|
70
69
|
}
|
|
71
70
|
/**
|
|
72
|
-
*
|
|
71
|
+
* Validate and return the caller-supplied endpoint configuration.
|
|
73
72
|
*
|
|
74
|
-
*
|
|
73
|
+
* Callers thread the config from `getInstance().api.config`.
|
|
74
|
+
*
|
|
75
|
+
* @throws Error if endpoint configuration is not set or lacks interrupts
|
|
75
76
|
* @returns The endpoint configuration
|
|
76
77
|
*/
|
|
77
|
-
getConfig() {
|
|
78
|
-
const config = getEndpointConfig();
|
|
78
|
+
getConfig(config) {
|
|
79
79
|
if (!config) {
|
|
80
|
-
throw new Error('Endpoint configuration not set.
|
|
80
|
+
throw new Error('Endpoint configuration not set. Configure the instance via fd.api.configure().');
|
|
81
81
|
}
|
|
82
82
|
if (!config.endpoints.interrupts) {
|
|
83
83
|
throw new Error('Interrupt endpoints not configured.');
|
|
@@ -87,12 +87,12 @@ export class InterruptService {
|
|
|
87
87
|
/**
|
|
88
88
|
* Generic API request helper
|
|
89
89
|
*
|
|
90
|
+
* @param config - The endpoint configuration
|
|
90
91
|
* @param url - The URL to fetch
|
|
91
92
|
* @param options - Fetch options
|
|
92
93
|
* @returns The parsed JSON response
|
|
93
94
|
*/
|
|
94
|
-
async request(url, options = {}) {
|
|
95
|
-
const config = this.getConfig();
|
|
95
|
+
async request(config, url, options = {}) {
|
|
96
96
|
const headers = getEndpointHeaders(config, 'interrupts');
|
|
97
97
|
const response = await fetch(url, {
|
|
98
98
|
...options,
|
|
@@ -119,12 +119,12 @@ export class InterruptService {
|
|
|
119
119
|
* @param interruptId - The interrupt UUID
|
|
120
120
|
* @returns The interrupt details
|
|
121
121
|
*/
|
|
122
|
-
async getInterrupt(interruptId) {
|
|
123
|
-
const config = this.getConfig();
|
|
122
|
+
async getInterrupt(endpointConfig, interruptId) {
|
|
123
|
+
const config = this.getConfig(endpointConfig);
|
|
124
124
|
const url = buildEndpointUrl(config, config.endpoints.interrupts.get, {
|
|
125
125
|
interruptId
|
|
126
126
|
});
|
|
127
|
-
const response = await this.request(url);
|
|
127
|
+
const response = await this.request(config, url);
|
|
128
128
|
if (!response.data) {
|
|
129
129
|
throw new Error('Interrupt not found');
|
|
130
130
|
}
|
|
@@ -137,13 +137,13 @@ export class InterruptService {
|
|
|
137
137
|
* @param value - The user's response value
|
|
138
138
|
* @returns The updated interrupt
|
|
139
139
|
*/
|
|
140
|
-
async resolveInterrupt(interruptId, value) {
|
|
141
|
-
const config = this.getConfig();
|
|
140
|
+
async resolveInterrupt(endpointConfig, interruptId, value) {
|
|
141
|
+
const config = this.getConfig(endpointConfig);
|
|
142
142
|
const url = buildEndpointUrl(config, config.endpoints.interrupts.resolve, {
|
|
143
143
|
interruptId
|
|
144
144
|
});
|
|
145
145
|
const resolution = { value };
|
|
146
|
-
const response = await this.request(url, {
|
|
146
|
+
const response = await this.request(config, url, {
|
|
147
147
|
method: 'POST',
|
|
148
148
|
body: JSON.stringify(resolution)
|
|
149
149
|
});
|
|
@@ -158,12 +158,12 @@ export class InterruptService {
|
|
|
158
158
|
* @param interruptId - The interrupt UUID
|
|
159
159
|
* @returns The updated interrupt
|
|
160
160
|
*/
|
|
161
|
-
async cancelInterrupt(interruptId) {
|
|
162
|
-
const config = this.getConfig();
|
|
161
|
+
async cancelInterrupt(endpointConfig, interruptId) {
|
|
162
|
+
const config = this.getConfig(endpointConfig);
|
|
163
163
|
const url = buildEndpointUrl(config, config.endpoints.interrupts.cancel, {
|
|
164
164
|
interruptId
|
|
165
165
|
});
|
|
166
|
-
const response = await this.request(url, {
|
|
166
|
+
const response = await this.request(config, url, {
|
|
167
167
|
method: 'POST'
|
|
168
168
|
});
|
|
169
169
|
if (!response.data) {
|
|
@@ -177,12 +177,12 @@ export class InterruptService {
|
|
|
177
177
|
* @param sessionId - The session UUID
|
|
178
178
|
* @returns Array of interrupts for the session
|
|
179
179
|
*/
|
|
180
|
-
async listSessionInterrupts(sessionId) {
|
|
181
|
-
const config = this.getConfig();
|
|
180
|
+
async listSessionInterrupts(endpointConfig, sessionId) {
|
|
181
|
+
const config = this.getConfig(endpointConfig);
|
|
182
182
|
const url = buildEndpointUrl(config, config.endpoints.interrupts.listBySession, {
|
|
183
183
|
sessionId
|
|
184
184
|
});
|
|
185
|
-
const response = await this.request(url);
|
|
185
|
+
const response = await this.request(config, url);
|
|
186
186
|
return response.data ?? [];
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
@@ -191,12 +191,12 @@ export class InterruptService {
|
|
|
191
191
|
* @param pipelineId - The pipeline UUID
|
|
192
192
|
* @returns Array of interrupts for the pipeline
|
|
193
193
|
*/
|
|
194
|
-
async listPipelineInterrupts(pipelineId) {
|
|
195
|
-
const config = this.getConfig();
|
|
194
|
+
async listPipelineInterrupts(endpointConfig, pipelineId) {
|
|
195
|
+
const config = this.getConfig(endpointConfig);
|
|
196
196
|
const url = buildEndpointUrl(config, config.endpoints.interrupts.listByPipeline, {
|
|
197
197
|
pipelineId
|
|
198
198
|
});
|
|
199
|
-
const response = await this.request(url);
|
|
199
|
+
const response = await this.request(config, url);
|
|
200
200
|
return response.data ?? [];
|
|
201
201
|
}
|
|
202
202
|
// =========================================================================
|
|
@@ -211,7 +211,7 @@ export class InterruptService {
|
|
|
211
211
|
* @param sessionId - The session UUID to poll
|
|
212
212
|
* @param callback - Callback function to handle new interrupts
|
|
213
213
|
*/
|
|
214
|
-
startPolling(sessionId, callback) {
|
|
214
|
+
startPolling(endpointConfig, sessionId, callback) {
|
|
215
215
|
if (!this.pollingConfig.enabled) {
|
|
216
216
|
logger.warn('[InterruptService] Polling is disabled. Enable via setPollingConfig().');
|
|
217
217
|
return;
|
|
@@ -225,7 +225,7 @@ export class InterruptService {
|
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
try {
|
|
228
|
-
const interrupts = await this.listSessionInterrupts(sessionId);
|
|
228
|
+
const interrupts = await this.listSessionInterrupts(endpointConfig, sessionId);
|
|
229
229
|
const pendingInterrupts = interrupts.filter((i) => i.status === 'pending');
|
|
230
230
|
// Reset backoff on successful request
|
|
231
231
|
this.currentBackoff = this.pollingConfig.interval ?? defaultInterruptPollingConfig.interval;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Handles fetching and managing node execution information from the backend
|
|
4
4
|
*/
|
|
5
5
|
import type { NodeExecutionInfo } from '../types/index.js';
|
|
6
|
+
import type { EndpointConfig } from '../config/endpoints.js';
|
|
6
7
|
/**
|
|
7
8
|
* Service for managing node execution information
|
|
8
9
|
*/
|
|
@@ -18,15 +19,15 @@ export declare class NodeExecutionService {
|
|
|
18
19
|
/**
|
|
19
20
|
* Get execution information for a specific node from pipeline data
|
|
20
21
|
*/
|
|
21
|
-
getNodeExecutionInfo(nodeId: string, pipelineId?: string): Promise<NodeExecutionInfo | null>;
|
|
22
|
+
getNodeExecutionInfo(endpointConfig: EndpointConfig | null, nodeId: string, pipelineId?: string): Promise<NodeExecutionInfo | null>;
|
|
22
23
|
/**
|
|
23
24
|
* Get execution information for multiple nodes from pipeline data
|
|
24
25
|
*/
|
|
25
|
-
getMultipleNodeExecutionInfo(nodeIds: string[], pipelineId?: string): Promise<Record<string, NodeExecutionInfo>>;
|
|
26
|
+
getMultipleNodeExecutionInfo(endpointConfig: EndpointConfig | null, nodeIds: string[], pipelineId?: string): Promise<Record<string, NodeExecutionInfo>>;
|
|
26
27
|
/**
|
|
27
28
|
* Get all node execution counts
|
|
28
29
|
*/
|
|
29
|
-
getAllNodeExecutionCounts(): Promise<Record<string, number>>;
|
|
30
|
+
getAllNodeExecutionCounts(endpointConfig: EndpointConfig | null): Promise<Record<string, number>>;
|
|
30
31
|
/**
|
|
31
32
|
* Get cached execution info for a node
|
|
32
33
|
*/
|
|
@@ -47,6 +48,20 @@ export declare class NodeExecutionService {
|
|
|
47
48
|
* Update execution info for a node (for real-time updates)
|
|
48
49
|
*/
|
|
49
50
|
updateNodeExecutionInfo(nodeId: string, executionInfo: Partial<NodeExecutionInfo>): void;
|
|
51
|
+
/**
|
|
52
|
+
* Build execution info for one node from the pipeline payload.
|
|
53
|
+
*
|
|
54
|
+
* The `node_statuses` entry is the backend-resolved summary (latest job's
|
|
55
|
+
* status, timing from the most recent run, `executions` count); the per-job
|
|
56
|
+
* history is attached from the `jobs` array so loop iterations stay
|
|
57
|
+
* inspectable. Falls back to the node's jobs when no entry exists (older
|
|
58
|
+
* backends).
|
|
59
|
+
*/
|
|
60
|
+
private buildNodeExecutionInfo;
|
|
61
|
+
/**
|
|
62
|
+
* Map a pipeline job payload entry to a NodeJobExecution history item.
|
|
63
|
+
*/
|
|
64
|
+
private mapJobToNodeJobExecution;
|
|
50
65
|
/**
|
|
51
66
|
* Map job status to execution status
|
|
52
67
|
*/
|