|
@@ -70,6 +70,11 @@ export interface NodeDefinition {
|
|
|
inputs: { name: string; displayName: string; type: string; required: boolean }[]
|
|
inputs: { name: string; displayName: string; type: string; required: boolean }[]
|
|
|
outputs: NodeOutput[]
|
|
outputs: NodeOutput[]
|
|
|
dynamicOutputs?: DynamicOutputs
|
|
dynamicOutputs?: DynamicOutputs
|
|
|
|
|
+ // Declares that running this node does something the engine can't undo -
|
|
|
|
|
+ // an external call, a write, a publish. Not sent by the backend yet (the
|
|
|
|
|
+ // module-level field is being built separately); read defensively so the
|
|
|
|
|
+ // re-run confirmation can go from generic to specific the day it is.
|
|
|
|
|
+ sideEffects?: boolean
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Transform backend workflow data to frontend format
|
|
// Transform backend workflow data to frontend format
|
|
@@ -249,17 +254,27 @@ export const workflowsApi = {
|
|
|
|
|
|
|
|
// Execute workflow up to a specific node (for testing single nodes)
|
|
// Execute workflow up to a specific node (for testing single nodes)
|
|
|
// cachedOutputs: Map of nodeId -> { output, configHash } for upstream nodes with valid cached data
|
|
// cachedOutputs: Map of nodeId -> { output, configHash } for upstream nodes with valid cached data
|
|
|
|
|
+ // targetLoopIteration: the 0-based loop iteration to run the target node
|
|
|
|
|
+ // as, when it lives inside a loop body. Omitted entirely (not sent as
|
|
|
|
|
+ // undefined) when not given, so an engine that predates this field sees
|
|
|
|
|
+ // nothing different from before - it degrades to iteration 0, today's
|
|
|
|
|
+ // behaviour, rather than erroring on an unknown field.
|
|
|
executeToNode: async (
|
|
executeToNode: async (
|
|
|
id: string,
|
|
id: string,
|
|
|
targetNodeId: string,
|
|
targetNodeId: string,
|
|
|
triggerData?: any,
|
|
triggerData?: any,
|
|
|
- cachedOutputs?: Record<string, { output: any; configHash: string }>
|
|
|
|
|
|
|
+ cachedOutputs?: Record<string, { output: any; configHash: string }>,
|
|
|
|
|
+ targetLoopIteration?: number
|
|
|
) => {
|
|
) => {
|
|
|
- const response = await api.post(`/workflows/${id}/execute`, {
|
|
|
|
|
|
|
+ const payload: Record<string, any> = {
|
|
|
...triggerData,
|
|
...triggerData,
|
|
|
_targetNodeId: targetNodeId,
|
|
_targetNodeId: targetNodeId,
|
|
|
_cachedOutputs: cachedOutputs,
|
|
_cachedOutputs: cachedOutputs,
|
|
|
- })
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ if (targetLoopIteration !== undefined) {
|
|
|
|
|
+ payload._targetLoopIteration = targetLoopIteration
|
|
|
|
|
+ }
|
|
|
|
|
+ const response = await api.post(`/workflows/${id}/execute`, payload)
|
|
|
return response.data
|
|
return response.data
|
|
|
},
|
|
},
|
|
|
|
|
|
|
@@ -503,6 +518,11 @@ export interface ExecutionDetail {
|
|
|
// absent entirely on executions recorded before the engine started sending
|
|
// absent entirely on executions recorded before the engine started sending
|
|
|
// it - treat its absence as "not a single-node test", never as an error.
|
|
// it - treat its absence as "not a single-node test", never as an error.
|
|
|
singleNodeTarget?: string
|
|
singleNodeTarget?: string
|
|
|
|
|
+ // The loop iteration a single-node test targeted, when singleNodeTarget
|
|
|
|
|
+ // ran inside a loop. Absent means iteration 0 (today's behaviour) or an
|
|
|
|
|
+ // engine that doesn't send the field yet - both read the same way, so
|
|
|
|
|
+ // this is never treated as an error condition.
|
|
|
|
|
+ singleNodeIteration?: number
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
export interface ExecutionListItem {
|
|
export interface ExecutionListItem {
|