flowcraft 2.0.0 → 2.1.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/README.md +51 -5
- package/dist/analysis.d.ts +1 -1
- package/dist/analysis.js +1 -1
- package/dist/{chunk-RYTIQZIB.js → chunk-3XVVR2SR.js} +167 -53
- package/dist/chunk-3XVVR2SR.js.map +1 -0
- package/dist/{chunk-6DNEDIIT.js → chunk-4A627Q6L.js} +47 -23
- package/dist/chunk-4A627Q6L.js.map +1 -0
- package/dist/{chunk-VFC342WL.js → chunk-4PELJWF7.js} +6 -6
- package/dist/chunk-4PELJWF7.js.map +1 -0
- package/dist/{chunk-WXT3YEWU.js → chunk-5ZXV3R5D.js} +2 -2
- package/dist/chunk-5ZXV3R5D.js.map +1 -0
- package/dist/{chunk-UYPIWXZG.js → chunk-CSZ6EOWG.js} +9 -10
- package/dist/chunk-CSZ6EOWG.js.map +1 -0
- package/dist/chunk-CYHZ2YVH.js +24 -0
- package/dist/chunk-CYHZ2YVH.js.map +1 -0
- package/dist/{chunk-J3RNCPED.js → chunk-DSYAC4WB.js} +2 -2
- package/dist/chunk-DSYAC4WB.js.map +1 -0
- package/dist/{chunk-M23P46ZL.js → chunk-HN72TZY5.js} +10 -5
- package/dist/chunk-HN72TZY5.js.map +1 -0
- package/dist/{chunk-MICPMOTW.js → chunk-KWQHFT7E.js} +2 -2
- package/dist/chunk-KWQHFT7E.js.map +1 -0
- package/dist/chunk-M2FRTT2K.js +144 -0
- package/dist/chunk-M2FRTT2K.js.map +1 -0
- package/dist/{chunk-RW4FH7IL.js → chunk-NBIRTKZ7.js} +89 -32
- package/dist/chunk-NBIRTKZ7.js.map +1 -0
- package/dist/chunk-O3XD45IL.js +236 -0
- package/dist/chunk-O3XD45IL.js.map +1 -0
- package/dist/chunk-PH2IYZHV.js +48 -0
- package/dist/chunk-PH2IYZHV.js.map +1 -0
- package/dist/{chunk-DSZSR7UE.js → chunk-U5V5O5MN.js} +11 -2
- package/dist/chunk-U5V5O5MN.js.map +1 -0
- package/dist/{chunk-RAZXOMZC.js → chunk-UETC63DP.js} +7 -6
- package/dist/chunk-UETC63DP.js.map +1 -0
- package/dist/context.d.ts +5 -5
- package/dist/context.js +1 -1
- package/dist/errors.js +1 -1
- package/dist/evaluator.d.ts +21 -13
- package/dist/evaluator.js +1 -1
- package/dist/flow.d.ts +6 -5
- package/dist/flow.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +15 -15
- package/dist/linter.d.ts +1 -1
- package/dist/linter.js +2 -2
- package/dist/logger.d.ts +5 -5
- package/dist/logger.js +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/runtime/adapter.d.ts +24 -4
- package/dist/runtime/adapter.js +13 -13
- package/dist/runtime/executors.d.ts +7 -7
- package/dist/runtime/executors.js +2 -2
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +13 -13
- package/dist/runtime/runtime.d.ts +9 -7
- package/dist/runtime/runtime.js +12 -12
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +2 -2
- package/dist/runtime/traverser.d.ts +5 -3
- package/dist/runtime/traverser.js +3 -3
- package/dist/runtime/types.d.ts +2 -1
- package/dist/sanitizer.d.ts +1 -1
- package/dist/sanitizer.js +1 -1
- package/dist/serializer.d.ts +2 -1
- package/dist/serializer.js +1 -1
- package/dist/{types-CZN_FcB6.d.ts → types-CQCe_nBM.d.ts} +35 -22
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
- package/dist/chunk-6DNEDIIT.js.map +0 -1
- package/dist/chunk-734J4PTM.js +0 -100
- package/dist/chunk-734J4PTM.js.map +0 -1
- package/dist/chunk-DSZSR7UE.js.map +0 -1
- package/dist/chunk-GTZC6PQI.js +0 -22
- package/dist/chunk-GTZC6PQI.js.map +0 -1
- package/dist/chunk-J3RNCPED.js.map +0 -1
- package/dist/chunk-M23P46ZL.js.map +0 -1
- package/dist/chunk-MICPMOTW.js.map +0 -1
- package/dist/chunk-NPAJNLXQ.js +0 -106
- package/dist/chunk-NPAJNLXQ.js.map +0 -1
- package/dist/chunk-RAZXOMZC.js.map +0 -1
- package/dist/chunk-REH55ZXV.js +0 -13
- package/dist/chunk-REH55ZXV.js.map +0 -1
- package/dist/chunk-RW4FH7IL.js.map +0 -1
- package/dist/chunk-RYTIQZIB.js.map +0 -1
- package/dist/chunk-UYPIWXZG.js.map +0 -1
- package/dist/chunk-VFC342WL.js.map +0 -1
- package/dist/chunk-WXT3YEWU.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,32 +24,78 @@ Build complex, multi-step processes with a lightweight, composable, and type-saf
|
|
|
24
24
|
npm install flowcraft
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
|
|
27
28
|
## Usage
|
|
28
29
|
|
|
30
|
+
Define and run a simple workflow in a few lines of code.
|
|
31
|
+
|
|
29
32
|
```typescript
|
|
30
|
-
import { createFlow, FlowRuntime } from '
|
|
33
|
+
import { createFlow, FlowRuntime } from 'flowcraft'
|
|
31
34
|
|
|
35
|
+
// 1. Define the workflow structure using the fluent API
|
|
32
36
|
const flow = createFlow('simple-workflow')
|
|
33
37
|
.node('start', async () => ({ output: 42 }))
|
|
34
|
-
.node('double', async ({ input }) => ({ output: input * 2 })
|
|
38
|
+
.node('double', async ({ input }) => ({ output: input * 2 }))
|
|
35
39
|
.edge('start', 'double')
|
|
36
40
|
.toBlueprint()
|
|
37
41
|
|
|
42
|
+
// 2. Create a runtime with the node implementations
|
|
38
43
|
const runtime = new FlowRuntime({
|
|
39
44
|
registry: flow.getFunctionRegistry(),
|
|
40
45
|
})
|
|
41
46
|
|
|
47
|
+
// 3. Execute the workflow
|
|
42
48
|
async function run() {
|
|
43
|
-
const result = await runtime.run(flow
|
|
44
|
-
console.log(result) // {
|
|
49
|
+
const result = await runtime.run(flow)
|
|
50
|
+
console.log(result.context) // { start: 42, double: 84 }
|
|
51
|
+
console.log(result.status) // 'completed'
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
run()
|
|
48
55
|
```
|
|
49
56
|
|
|
57
|
+
## Core Concepts
|
|
58
|
+
|
|
59
|
+
- **Blueprint**: A serializable object that represents the structure of your workflow. It contains all the nodes and edges and can be stored as JSON or YAML. This is the single source of truth for a workflow's logic.
|
|
60
|
+
- **Node**: A single unit of work. Node logic can be implemented as a simple async function or a structured class that extends `BaseNode` for more complex lifecycle management.
|
|
61
|
+
- **Edge**: A connection between two nodes that defines the direction of the flow. Edges can be conditional, allowing you to create branching logic based on the output or `action` of a source node.
|
|
62
|
+
- **Runtime**: The `FlowRuntime` is the engine that interprets a blueprint and executes its nodes in the correct order. It manages state, handles resiliency, and coordinates the entire process.
|
|
63
|
+
- **Context**: An object that holds the state of a single workflow execution. The outputs of completed nodes are stored in the context and can be accessed by subsequent nodes.
|
|
64
|
+
|
|
65
|
+
## Resiliency and Error Handling
|
|
66
|
+
|
|
67
|
+
Design robust workflows with built-in resiliency features.
|
|
68
|
+
|
|
69
|
+
- **Retries**: Configure the `maxRetries` property on a node to automatically retry it on failure.
|
|
70
|
+
- **Fallbacks**: Specify a `fallback` node ID in a node's configuration. If the node fails all its retry attempts, the fallback node will be executed instead, preventing the entire workflow from failing.
|
|
71
|
+
|
|
72
|
+
For more granular control, you can implement a node using the `BaseNode` class, which provides `prep`, `exec`, `post`, `fallback`, and `recover` lifecycle methods.
|
|
73
|
+
|
|
74
|
+
## Tooling and Utilities
|
|
75
|
+
|
|
76
|
+
Flowcraft includes tools to help you validate and visualize your workflows.
|
|
77
|
+
|
|
78
|
+
- **Linter (`lintBlueprint`)**: Statically analyze a blueprint to find common errors, such as orphan nodes, invalid edges, or nodes with missing implementations.
|
|
79
|
+
- **Analysis (`analyzeBlueprint`)**: Programmatically inspect a blueprint to detect cycles, find start/terminal nodes, and get other graph metrics.
|
|
80
|
+
- **Diagram Generation (`generateMermaid`)**: Automatically generate a [Mermaid](https://mermaid-js.github.io/mermaid/#/) syntax string from a blueprint to easily visualize your workflow's structure.
|
|
81
|
+
|
|
82
|
+
## Extensibility and Customization
|
|
83
|
+
|
|
84
|
+
The `FlowRuntime` can be configured with pluggable components to tailor its behavior to your specific needs:
|
|
85
|
+
|
|
86
|
+
- **Logger**: Provide a custom `ILogger` implementation (e.g., Pino, Winston) to integrate with your existing logging infrastructure.
|
|
87
|
+
- **Serializer**: Replace the default `JsonSerializer` with a more robust one (e.g., `superjson`) to handle complex data types like `Date`, `Map`, and `Set` in the workflow context.
|
|
88
|
+
- **Evaluator**: Swap the default `PropertyEvaluator` for a more powerful expression engine (like `jsep` or `govaluate`) to enable complex logic in edge conditions. For trusted environments, an `UnsafeEvaluator` is also available.
|
|
89
|
+
- **Middleware**: Wrap node execution with custom logic for cross-cutting concerns like distributed tracing, performance monitoring, or advanced authorization.
|
|
90
|
+
- **Event Bus**: An event emitter for monitoring workflow and node lifecycle events (`workflow:start`, `node:finish`, etc.).
|
|
91
|
+
|
|
92
|
+
## Distributed Execution
|
|
93
|
+
|
|
94
|
+
Flowcraft's architecture is designed for progressive scalability. The `BaseDistributedAdapter` provides a foundation for running workflows across multiple machines. Flowcraft provides official adapters for [BullMQ](https://www.npmjs.com/package/@flowcraft/bullmq-adapter), [AWS](https://www.npmjs.com/package/@flowcraft/sqs-adapter), [GCP](https://www.npmjs.com/package/@flowcraft/gcp-adapter), [Azure](https://www.npmjs.com/package/@flowcraft/azure-adapter), [RabbitMQ](https://www.npmjs.com/package/@flowcraft/rabbitmq-adapter), and [Kafka](https://www.npmjs.com/package/@flowcraft/kafka-adapter).
|
|
95
|
+
|
|
50
96
|
## Documentation
|
|
51
97
|
|
|
52
|
-
For a complete overview
|
|
98
|
+
For a complete overview of features, patterns, examples, and APIs, see the full [documentation](https://flowcraft.js.org/).
|
|
53
99
|
|
|
54
100
|
## License
|
|
55
101
|
|
package/dist/analysis.d.ts
CHANGED
package/dist/analysis.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { WorkflowState } from './chunk-
|
|
2
|
-
import { GraphTraverser } from './chunk-
|
|
3
|
-
import { sanitizeBlueprint } from './chunk-
|
|
4
|
-
import { JsonSerializer } from './chunk-
|
|
5
|
-
import { BuiltInNodeExecutor, ClassNodeExecutor, FunctionNodeExecutor } from './chunk-
|
|
6
|
-
import { AsyncContextView } from './chunk-
|
|
7
|
-
import { CancelledWorkflowError, NodeExecutionError, FatalNodeExecutionError } from './chunk-
|
|
8
|
-
import {
|
|
9
|
-
import { isNodeClass } from './chunk-
|
|
10
|
-
import { analyzeBlueprint } from './chunk-
|
|
11
|
-
import { NullLogger } from './chunk-
|
|
1
|
+
import { WorkflowState } from './chunk-CSZ6EOWG.js';
|
|
2
|
+
import { GraphTraverser } from './chunk-NBIRTKZ7.js';
|
|
3
|
+
import { sanitizeBlueprint } from './chunk-DSYAC4WB.js';
|
|
4
|
+
import { JsonSerializer } from './chunk-CYHZ2YVH.js';
|
|
5
|
+
import { BuiltInNodeExecutor, ClassNodeExecutor, FunctionNodeExecutor } from './chunk-M2FRTT2K.js';
|
|
6
|
+
import { AsyncContextView } from './chunk-KWQHFT7E.js';
|
|
7
|
+
import { CancelledWorkflowError, NodeExecutionError, FatalNodeExecutionError } from './chunk-5ZXV3R5D.js';
|
|
8
|
+
import { PropertyEvaluator } from './chunk-PH2IYZHV.js';
|
|
9
|
+
import { isNodeClass } from './chunk-U5V5O5MN.js';
|
|
10
|
+
import { analyzeBlueprint } from './chunk-HN72TZY5.js';
|
|
11
|
+
import { NullLogger } from './chunk-4PELJWF7.js';
|
|
12
12
|
|
|
13
13
|
// src/runtime/runtime.ts
|
|
14
14
|
var FlowRuntime = class {
|
|
@@ -20,6 +20,7 @@ var FlowRuntime = class {
|
|
|
20
20
|
serializer;
|
|
21
21
|
middleware;
|
|
22
22
|
evaluator;
|
|
23
|
+
analysisCache;
|
|
23
24
|
options;
|
|
24
25
|
constructor(options) {
|
|
25
26
|
this.registry = options.registry || {};
|
|
@@ -30,7 +31,8 @@ var FlowRuntime = class {
|
|
|
30
31
|
} };
|
|
31
32
|
this.serializer = options.serializer || new JsonSerializer();
|
|
32
33
|
this.middleware = options.middleware || [];
|
|
33
|
-
this.evaluator = options.evaluator || new
|
|
34
|
+
this.evaluator = options.evaluator || new PropertyEvaluator();
|
|
35
|
+
this.analysisCache = /* @__PURE__ */ new WeakMap();
|
|
34
36
|
this.options = options;
|
|
35
37
|
}
|
|
36
38
|
async run(blueprint, initialState = {}, options) {
|
|
@@ -39,15 +41,27 @@ var FlowRuntime = class {
|
|
|
39
41
|
const contextData = typeof initialState === "string" ? this.serializer.deserialize(initialState) : initialState;
|
|
40
42
|
blueprint = sanitizeBlueprint(blueprint);
|
|
41
43
|
const state = new WorkflowState(contextData);
|
|
42
|
-
this.logger.info(`Starting workflow execution`, {
|
|
44
|
+
this.logger.info(`Starting workflow execution`, {
|
|
45
|
+
blueprintId: blueprint.id,
|
|
46
|
+
executionId
|
|
47
|
+
});
|
|
43
48
|
try {
|
|
44
|
-
await this.eventBus.emit("workflow:start", {
|
|
45
|
-
|
|
49
|
+
await this.eventBus.emit("workflow:start", {
|
|
50
|
+
blueprintId: blueprint.id,
|
|
51
|
+
executionId
|
|
52
|
+
});
|
|
53
|
+
const analysis = this.analysisCache.get(blueprint) ?? (() => {
|
|
54
|
+
const computed = analyzeBlueprint(blueprint);
|
|
55
|
+
this.analysisCache.set(blueprint, computed);
|
|
56
|
+
return computed;
|
|
57
|
+
})();
|
|
46
58
|
if (options?.strict && !analysis.isDag) {
|
|
47
59
|
throw new Error(`Workflow '${blueprint.id}' failed strictness check: Cycles are not allowed.`);
|
|
48
60
|
}
|
|
49
61
|
if (!analysis.isDag) {
|
|
50
|
-
this.logger.warn(`Workflow contains cycles`, {
|
|
62
|
+
this.logger.warn(`Workflow contains cycles`, {
|
|
63
|
+
blueprintId: blueprint.id
|
|
64
|
+
});
|
|
51
65
|
}
|
|
52
66
|
const traverser = new GraphTraverser(
|
|
53
67
|
blueprint,
|
|
@@ -55,7 +69,8 @@ var FlowRuntime = class {
|
|
|
55
69
|
state,
|
|
56
70
|
options?.functionRegistry,
|
|
57
71
|
executionId,
|
|
58
|
-
options?.signal
|
|
72
|
+
options?.signal,
|
|
73
|
+
options?.concurrency
|
|
59
74
|
);
|
|
60
75
|
await traverser.traverse();
|
|
61
76
|
const status = state.getStatus(traverser.getAllNodeIds(), traverser.getFallbackNodeIds());
|
|
@@ -63,26 +78,65 @@ var FlowRuntime = class {
|
|
|
63
78
|
result.status = status;
|
|
64
79
|
const duration = Date.now() - startTime;
|
|
65
80
|
if (status === "stalled") {
|
|
66
|
-
await this.eventBus.emit("workflow:stall", {
|
|
81
|
+
await this.eventBus.emit("workflow:stall", {
|
|
82
|
+
blueprintId: blueprint.id,
|
|
83
|
+
executionId,
|
|
84
|
+
remainingNodes: traverser.getAllNodeIds().size - state.getCompletedNodes().size
|
|
85
|
+
});
|
|
67
86
|
}
|
|
68
|
-
this.logger.info(`Workflow execution completed`, {
|
|
69
|
-
|
|
87
|
+
this.logger.info(`Workflow execution completed`, {
|
|
88
|
+
blueprintId: blueprint.id,
|
|
89
|
+
executionId,
|
|
90
|
+
status,
|
|
91
|
+
duration,
|
|
92
|
+
errors: result.errors?.length || 0
|
|
93
|
+
});
|
|
94
|
+
await this.eventBus.emit("workflow:finish", {
|
|
95
|
+
blueprintId: blueprint.id,
|
|
96
|
+
executionId,
|
|
97
|
+
status,
|
|
98
|
+
errors: result.errors
|
|
99
|
+
});
|
|
70
100
|
return result;
|
|
71
101
|
} catch (error) {
|
|
72
102
|
const duration = Date.now() - startTime;
|
|
73
103
|
if (error instanceof DOMException ? error.name === "AbortError" : error instanceof CancelledWorkflowError) {
|
|
74
|
-
this.logger.info(`Workflow execution cancelled`, {
|
|
75
|
-
|
|
76
|
-
|
|
104
|
+
this.logger.info(`Workflow execution cancelled`, {
|
|
105
|
+
blueprintId: blueprint.id,
|
|
106
|
+
executionId,
|
|
107
|
+
duration
|
|
108
|
+
});
|
|
109
|
+
await this.eventBus.emit("workflow:finish", {
|
|
110
|
+
blueprintId: blueprint.id,
|
|
111
|
+
executionId,
|
|
112
|
+
status: "cancelled",
|
|
113
|
+
error
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
context: {},
|
|
117
|
+
serializedContext: "{}",
|
|
118
|
+
status: "cancelled"
|
|
119
|
+
};
|
|
77
120
|
}
|
|
78
|
-
this.logger.error(`Workflow execution failed`, {
|
|
121
|
+
this.logger.error(`Workflow execution failed`, {
|
|
122
|
+
blueprintId: blueprint.id,
|
|
123
|
+
executionId,
|
|
124
|
+
duration,
|
|
125
|
+
error: error instanceof Error ? error.message : String(error)
|
|
126
|
+
});
|
|
79
127
|
throw error;
|
|
80
128
|
}
|
|
81
129
|
}
|
|
82
130
|
async executeNode(blueprint, nodeId, state, allPredecessors, functionRegistry, executionId, signal) {
|
|
83
131
|
const nodeDef = blueprint.nodes.find((n) => n.id === nodeId);
|
|
84
132
|
if (!nodeDef) {
|
|
85
|
-
throw new NodeExecutionError(
|
|
133
|
+
throw new NodeExecutionError(
|
|
134
|
+
`Node '${nodeId}' not found in blueprint.`,
|
|
135
|
+
nodeId,
|
|
136
|
+
blueprint.id,
|
|
137
|
+
void 0,
|
|
138
|
+
executionId
|
|
139
|
+
);
|
|
86
140
|
}
|
|
87
141
|
const contextImpl = state.getContext();
|
|
88
142
|
const asyncContext = contextImpl.type === "sync" ? new AsyncContextView(contextImpl) : contextImpl;
|
|
@@ -102,7 +156,16 @@ var FlowRuntime = class {
|
|
|
102
156
|
let error;
|
|
103
157
|
try {
|
|
104
158
|
for (const hook of beforeHooks) await hook(nodeContext.context, nodeId);
|
|
105
|
-
result = await this.executeWithFallback(
|
|
159
|
+
result = await this.executeWithFallback(
|
|
160
|
+
blueprint,
|
|
161
|
+
nodeDef,
|
|
162
|
+
nodeContext,
|
|
163
|
+
executor,
|
|
164
|
+
executionId,
|
|
165
|
+
signal,
|
|
166
|
+
state,
|
|
167
|
+
functionRegistry
|
|
168
|
+
);
|
|
106
169
|
return result;
|
|
107
170
|
} catch (e) {
|
|
108
171
|
error = e;
|
|
@@ -118,12 +181,26 @@ var FlowRuntime = class {
|
|
|
118
181
|
executionChain = () => hook(nodeContext.context, nodeId, next);
|
|
119
182
|
}
|
|
120
183
|
try {
|
|
121
|
-
await this.eventBus.emit("node:start", {
|
|
184
|
+
await this.eventBus.emit("node:start", {
|
|
185
|
+
blueprintId: blueprint.id,
|
|
186
|
+
nodeId,
|
|
187
|
+
executionId
|
|
188
|
+
});
|
|
122
189
|
const result = await executionChain();
|
|
123
|
-
await this.eventBus.emit("node:finish", {
|
|
190
|
+
await this.eventBus.emit("node:finish", {
|
|
191
|
+
blueprintId: blueprint.id,
|
|
192
|
+
nodeId,
|
|
193
|
+
result,
|
|
194
|
+
executionId
|
|
195
|
+
});
|
|
124
196
|
return result;
|
|
125
197
|
} catch (error) {
|
|
126
|
-
await this.eventBus.emit("node:error", {
|
|
198
|
+
await this.eventBus.emit("node:error", {
|
|
199
|
+
blueprintId: blueprint.id,
|
|
200
|
+
nodeId,
|
|
201
|
+
error,
|
|
202
|
+
executionId
|
|
203
|
+
});
|
|
127
204
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
128
205
|
throw new CancelledWorkflowError("Workflow cancelled");
|
|
129
206
|
}
|
|
@@ -136,7 +213,11 @@ var FlowRuntime = class {
|
|
|
136
213
|
}
|
|
137
214
|
const implementation = functionRegistry?.get(nodeDef.uses) || this.registry[nodeDef.uses];
|
|
138
215
|
if (!implementation) {
|
|
139
|
-
throw new FatalNodeExecutionError(
|
|
216
|
+
throw new FatalNodeExecutionError(
|
|
217
|
+
`Implementation for '${nodeDef.uses}' not found for node '${nodeDef.id}'.`,
|
|
218
|
+
nodeDef.id,
|
|
219
|
+
""
|
|
220
|
+
);
|
|
140
221
|
}
|
|
141
222
|
const maxRetries = nodeDef.config?.maxRetries ?? 1;
|
|
142
223
|
return isNodeClass(implementation) ? new ClassNodeExecutor(implementation, maxRetries, this.eventBus) : new FunctionNodeExecutor(implementation, maxRetries, this.eventBus);
|
|
@@ -146,20 +227,40 @@ var FlowRuntime = class {
|
|
|
146
227
|
return await executor.execute(nodeDef, context, executionId, signal);
|
|
147
228
|
} catch (error) {
|
|
148
229
|
const isFatal = error instanceof FatalNodeExecutionError || error instanceof NodeExecutionError && error.originalError instanceof FatalNodeExecutionError;
|
|
149
|
-
if (isFatal)
|
|
150
|
-
throw error;
|
|
230
|
+
if (isFatal) throw error;
|
|
151
231
|
const fallbackNodeId = nodeDef.config?.fallback;
|
|
152
232
|
if (fallbackNodeId && state) {
|
|
153
|
-
context.dependencies.logger.warn(`Executing fallback for node`, {
|
|
154
|
-
|
|
233
|
+
context.dependencies.logger.warn(`Executing fallback for node`, {
|
|
234
|
+
nodeId: nodeDef.id,
|
|
235
|
+
fallbackNodeId,
|
|
236
|
+
error: error instanceof Error ? error.message : String(error),
|
|
237
|
+
executionId
|
|
238
|
+
});
|
|
239
|
+
await this.eventBus.emit("node:fallback", {
|
|
240
|
+
blueprintId: blueprint.id,
|
|
241
|
+
nodeId: nodeDef.id,
|
|
242
|
+
executionId,
|
|
243
|
+
fallback: fallbackNodeId
|
|
244
|
+
});
|
|
155
245
|
const fallbackNode = blueprint.nodes.find((n) => n.id === fallbackNodeId);
|
|
156
246
|
if (!fallbackNode) {
|
|
157
|
-
throw new NodeExecutionError(
|
|
247
|
+
throw new NodeExecutionError(
|
|
248
|
+
`Fallback node '${fallbackNodeId}' not found in blueprint.`,
|
|
249
|
+
nodeDef.id,
|
|
250
|
+
blueprint.id,
|
|
251
|
+
void 0,
|
|
252
|
+
executionId
|
|
253
|
+
);
|
|
158
254
|
}
|
|
159
255
|
const fallbackExecutor = this.getExecutor(fallbackNode, functionRegistry);
|
|
160
256
|
const fallbackResult = await fallbackExecutor.execute(fallbackNode, context, executionId, signal);
|
|
161
257
|
state.markFallbackExecuted();
|
|
162
|
-
|
|
258
|
+
state.addCompletedNode(fallbackNodeId, fallbackResult.output);
|
|
259
|
+
context.dependencies.logger.info(`Fallback execution completed`, {
|
|
260
|
+
nodeId: nodeDef.id,
|
|
261
|
+
fallbackNodeId,
|
|
262
|
+
executionId
|
|
263
|
+
});
|
|
163
264
|
return { ...fallbackResult, _fallbackExecuted: true };
|
|
164
265
|
}
|
|
165
266
|
throw error;
|
|
@@ -169,18 +270,19 @@ var FlowRuntime = class {
|
|
|
169
270
|
const outgoingEdges = blueprint.edges.filter((edge) => edge.source === nodeId);
|
|
170
271
|
const matched = [];
|
|
171
272
|
const evaluateEdge = async (edge) => {
|
|
172
|
-
if (!edge.condition)
|
|
173
|
-
return true;
|
|
273
|
+
if (!edge.condition) return true;
|
|
174
274
|
const contextData = context.type === "sync" ? context.toJSON() : await context.toJSON();
|
|
175
|
-
return !!this.evaluator.evaluate(edge.condition, {
|
|
275
|
+
return !!this.evaluator.evaluate(edge.condition, {
|
|
276
|
+
...contextData,
|
|
277
|
+
result
|
|
278
|
+
});
|
|
176
279
|
};
|
|
177
280
|
if (result.action) {
|
|
178
281
|
const actionEdges = outgoingEdges.filter((edge) => edge.action === result.action);
|
|
179
282
|
for (const edge of actionEdges) {
|
|
180
283
|
if (await evaluateEdge(edge)) {
|
|
181
284
|
const targetNode = blueprint.nodes.find((n) => n.id === edge.target);
|
|
182
|
-
if (targetNode)
|
|
183
|
-
matched.push({ node: targetNode, edge });
|
|
285
|
+
if (targetNode) matched.push({ node: targetNode, edge });
|
|
184
286
|
}
|
|
185
287
|
}
|
|
186
288
|
}
|
|
@@ -189,17 +291,22 @@ var FlowRuntime = class {
|
|
|
189
291
|
for (const edge of defaultEdges) {
|
|
190
292
|
if (await evaluateEdge(edge)) {
|
|
191
293
|
const targetNode = blueprint.nodes.find((n) => n.id === edge.target);
|
|
192
|
-
if (targetNode)
|
|
193
|
-
matched.push({ node: targetNode, edge });
|
|
294
|
+
if (targetNode) matched.push({ node: targetNode, edge });
|
|
194
295
|
}
|
|
195
296
|
}
|
|
196
297
|
}
|
|
197
|
-
this.logger.debug(`Determined next nodes for ${nodeId}`, {
|
|
298
|
+
this.logger.debug(`Determined next nodes for ${nodeId}`, {
|
|
299
|
+
matchedNodes: matched.map((m) => m.node.id),
|
|
300
|
+
action: result.action
|
|
301
|
+
});
|
|
198
302
|
return matched;
|
|
199
303
|
}
|
|
200
304
|
async applyEdgeTransform(edge, sourceResult, targetNode, context, allPredecessors) {
|
|
201
305
|
const asyncContext = context.type === "sync" ? new AsyncContextView(context) : context;
|
|
202
|
-
const finalInput = edge.transform ? this.evaluator.evaluate(edge.transform, {
|
|
306
|
+
const finalInput = edge.transform ? this.evaluator.evaluate(edge.transform, {
|
|
307
|
+
input: sourceResult.output,
|
|
308
|
+
context: await asyncContext.toJSON()
|
|
309
|
+
}) : sourceResult.output;
|
|
203
310
|
const inputKey = `${targetNode.id}_input`;
|
|
204
311
|
await asyncContext.set(inputKey, finalInput);
|
|
205
312
|
if (targetNode.config?.joinStrategy === "any") {
|
|
@@ -213,8 +320,7 @@ var FlowRuntime = class {
|
|
|
213
320
|
}
|
|
214
321
|
async _resolveNodeInput(nodeDef, context, allPredecessors) {
|
|
215
322
|
if (nodeDef.inputs) {
|
|
216
|
-
if (typeof nodeDef.inputs === "string")
|
|
217
|
-
return await context.get(nodeDef.inputs);
|
|
323
|
+
if (typeof nodeDef.inputs === "string") return await context.get(nodeDef.inputs);
|
|
218
324
|
if (typeof nodeDef.inputs === "object") {
|
|
219
325
|
const input = {};
|
|
220
326
|
for (const key in nodeDef.inputs) {
|
|
@@ -240,7 +346,7 @@ var FlowRuntime = class {
|
|
|
240
346
|
case "batch-scatter": {
|
|
241
347
|
const inputArray = await context.get(inputs) || [];
|
|
242
348
|
if (!Array.isArray(inputArray))
|
|
243
|
-
throw new
|
|
349
|
+
throw new FatalNodeExecutionError(`Input for batch-scatter node '${id}' must be an array.`, id, "");
|
|
244
350
|
const batchId = globalThis.crypto.randomUUID();
|
|
245
351
|
const dynamicNodes = [];
|
|
246
352
|
for (let i = 0; i < inputArray.length; i++) {
|
|
@@ -270,7 +376,11 @@ var FlowRuntime = class {
|
|
|
270
376
|
throw new FatalNodeExecutionError(`Subflow node '${id}' is missing the 'blueprintId' parameter.`, id, "");
|
|
271
377
|
const subBlueprint = this.blueprints[blueprintId];
|
|
272
378
|
if (!subBlueprint)
|
|
273
|
-
throw new FatalNodeExecutionError(
|
|
379
|
+
throw new FatalNodeExecutionError(
|
|
380
|
+
`Sub-blueprint with ID '${blueprintId}' not found in runtime registry.`,
|
|
381
|
+
id,
|
|
382
|
+
""
|
|
383
|
+
);
|
|
274
384
|
const subflowInitialContext = {};
|
|
275
385
|
if (inputMapping) {
|
|
276
386
|
for (const [targetKey, sourceKey] of Object.entries(inputMapping)) {
|
|
@@ -281,11 +391,15 @@ var FlowRuntime = class {
|
|
|
281
391
|
}
|
|
282
392
|
const subflowResult = await this.run(subBlueprint, subflowInitialContext);
|
|
283
393
|
if (subflowResult.status !== "completed")
|
|
284
|
-
throw new NodeExecutionError(
|
|
394
|
+
throw new NodeExecutionError(
|
|
395
|
+
`Sub-workflow '${blueprintId}' did not complete successfully. Status: ${subflowResult.status}`,
|
|
396
|
+
id,
|
|
397
|
+
subBlueprint.id
|
|
398
|
+
);
|
|
285
399
|
if (outputMapping) {
|
|
286
400
|
for (const [parentKey, subKey] of Object.entries(outputMapping)) {
|
|
287
401
|
const subflowFinalContext = subflowResult.context;
|
|
288
|
-
if (Object.
|
|
402
|
+
if (Object.hasOwn(subflowFinalContext, subKey)) {
|
|
289
403
|
await context.set(parentKey, subflowFinalContext[subKey]);
|
|
290
404
|
}
|
|
291
405
|
}
|
|
@@ -299,5 +413,5 @@ var FlowRuntime = class {
|
|
|
299
413
|
};
|
|
300
414
|
|
|
301
415
|
export { FlowRuntime };
|
|
302
|
-
//# sourceMappingURL=chunk-
|
|
303
|
-
//# sourceMappingURL=chunk-
|
|
416
|
+
//# sourceMappingURL=chunk-3XVVR2SR.js.map
|
|
417
|
+
//# sourceMappingURL=chunk-3XVVR2SR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/runtime.ts"],"names":["nodeDef"],"mappings":"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","file":"chunk-3XVVR2SR.js","sourcesContent":["import type { BlueprintAnalysis } from '../analysis'\nimport { analyzeBlueprint } from '../analysis'\nimport { AsyncContextView } from '../context'\nimport { CancelledWorkflowError, FatalNodeExecutionError, NodeExecutionError } from '../errors'\nimport { PropertyEvaluator } from '../evaluator'\nimport { NullLogger } from '../logger'\nimport type { BaseNode } from '../node'\nimport { isNodeClass } from '../node'\nimport { sanitizeBlueprint } from '../sanitizer'\nimport { JsonSerializer } from '../serializer'\nimport type {\n\tContextImplementation,\n\tEdgeDefinition,\n\tIAsyncContext,\n\tIEvaluator,\n\tIEventBus,\n\tILogger,\n\tISerializer,\n\tISyncContext,\n\tMiddleware,\n\tNodeClass,\n\tNodeContext,\n\tNodeDefinition,\n\tNodeFunction,\n\tNodeResult,\n\tRuntimeOptions,\n\tWorkflowBlueprint,\n\tWorkflowResult,\n} from '../types'\nimport type { ExecutionStrategy } from './executors'\nimport { BuiltInNodeExecutor, ClassNodeExecutor, FunctionNodeExecutor } from './executors'\nimport { WorkflowState } from './state'\nimport { GraphTraverser } from './traverser'\nimport type { IRuntime } from './types'\n\nexport class FlowRuntime<TContext extends Record<string, any>, TDependencies extends Record<string, any>>\n\timplements IRuntime<TContext, TDependencies>\n{\n\tpublic registry: Record<string, NodeFunction | NodeClass | typeof BaseNode>\n\tprivate blueprints: Record<string, WorkflowBlueprint>\n\tprivate dependencies: TDependencies\n\tprivate logger: ILogger\n\tprivate eventBus: IEventBus\n\tprivate serializer: ISerializer\n\tprivate middleware: Middleware[]\n\tprivate evaluator: IEvaluator\n\tprivate analysisCache: WeakMap<WorkflowBlueprint, BlueprintAnalysis>\n\tpublic options: RuntimeOptions<TDependencies>\n\n\tconstructor(options: RuntimeOptions<TDependencies>) {\n\t\tthis.registry = options.registry || {}\n\t\tthis.blueprints = options.blueprints || {}\n\t\tthis.dependencies = options.dependencies || ({} as TDependencies)\n\t\tthis.logger = options.logger || new NullLogger()\n\t\tthis.eventBus = options.eventBus || { emit: () => {} }\n\t\tthis.serializer = options.serializer || new JsonSerializer()\n\t\tthis.middleware = options.middleware || []\n\t\tthis.evaluator = options.evaluator || new PropertyEvaluator()\n\t\tthis.analysisCache = new WeakMap()\n\t\tthis.options = options\n\t}\n\n\tasync run(\n\t\tblueprint: WorkflowBlueprint,\n\t\tinitialState: Partial<TContext> | string = {},\n\t\toptions?: {\n\t\t\tfunctionRegistry?: Map<string, any>\n\t\t\tstrict?: boolean\n\t\t\tsignal?: AbortSignal\n\t\t\tconcurrency?: number\n\t\t},\n\t): Promise<WorkflowResult<TContext>> {\n\t\tconst executionId = globalThis.crypto?.randomUUID()\n\t\tconst startTime = Date.now()\n\t\tconst contextData =\n\t\t\ttypeof initialState === 'string' ? (this.serializer.deserialize(initialState) as Partial<TContext>) : initialState\n\t\tblueprint = sanitizeBlueprint(blueprint)\n\t\tconst state = new WorkflowState<TContext>(contextData)\n\n\t\tthis.logger.info(`Starting workflow execution`, {\n\t\t\tblueprintId: blueprint.id,\n\t\t\texecutionId,\n\t\t})\n\n\t\ttry {\n\t\t\tawait this.eventBus.emit('workflow:start', {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\texecutionId,\n\t\t\t})\n\t\t\tconst analysis =\n\t\t\t\tthis.analysisCache.get(blueprint) ??\n\t\t\t\t(() => {\n\t\t\t\t\tconst computed = analyzeBlueprint(blueprint)\n\t\t\t\t\tthis.analysisCache.set(blueprint, computed)\n\t\t\t\t\treturn computed\n\t\t\t\t})()\n\t\t\tif (options?.strict && !analysis.isDag) {\n\t\t\t\tthrow new Error(`Workflow '${blueprint.id}' failed strictness check: Cycles are not allowed.`)\n\t\t\t}\n\t\t\tif (!analysis.isDag) {\n\t\t\t\tthis.logger.warn(`Workflow contains cycles`, {\n\t\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst traverser = new GraphTraverser<TContext, TDependencies>(\n\t\t\t\tblueprint,\n\t\t\t\tthis,\n\t\t\t\tstate,\n\t\t\t\toptions?.functionRegistry,\n\t\t\t\texecutionId,\n\t\t\t\toptions?.signal,\n\t\t\t\toptions?.concurrency,\n\t\t\t)\n\t\t\tawait traverser.traverse()\n\t\t\tconst status = state.getStatus(traverser.getAllNodeIds(), traverser.getFallbackNodeIds())\n\t\t\tconst result = state.toResult(this.serializer)\n\t\t\tresult.status = status\n\t\t\tconst duration = Date.now() - startTime\n\t\t\tif (status === 'stalled') {\n\t\t\t\tawait this.eventBus.emit('workflow:stall', {\n\t\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\t\texecutionId,\n\t\t\t\t\tremainingNodes: traverser.getAllNodeIds().size - state.getCompletedNodes().size,\n\t\t\t\t})\n\t\t\t}\n\t\t\tthis.logger.info(`Workflow execution completed`, {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\texecutionId,\n\t\t\t\tstatus,\n\t\t\t\tduration,\n\t\t\t\terrors: result.errors?.length || 0,\n\t\t\t})\n\t\t\tawait this.eventBus.emit('workflow:finish', {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\texecutionId,\n\t\t\t\tstatus,\n\t\t\t\terrors: result.errors,\n\t\t\t})\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\tconst duration = Date.now() - startTime\n\t\t\tif (error instanceof DOMException ? error.name === 'AbortError' : error instanceof CancelledWorkflowError) {\n\t\t\t\tthis.logger.info(`Workflow execution cancelled`, {\n\t\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\t\texecutionId,\n\t\t\t\t\tduration,\n\t\t\t\t})\n\t\t\t\tawait this.eventBus.emit('workflow:finish', {\n\t\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\t\texecutionId,\n\t\t\t\t\tstatus: 'cancelled',\n\t\t\t\t\terror,\n\t\t\t\t})\n\t\t\t\treturn {\n\t\t\t\t\tcontext: {} as TContext,\n\t\t\t\t\tserializedContext: '{}',\n\t\t\t\t\tstatus: 'cancelled',\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.logger.error(`Workflow execution failed`, {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\texecutionId,\n\t\t\t\tduration,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync executeNode(\n\t\tblueprint: WorkflowBlueprint,\n\t\tnodeId: string,\n\t\tstate: WorkflowState<TContext>,\n\t\tallPredecessors?: Map<string, Set<string>>,\n\t\tfunctionRegistry?: Map<string, any>,\n\t\texecutionId?: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<NodeResult<any, any>> {\n\t\tconst nodeDef = blueprint.nodes.find((n) => n.id === nodeId)\n\t\tif (!nodeDef) {\n\t\t\tthrow new NodeExecutionError(\n\t\t\t\t`Node '${nodeId}' not found in blueprint.`,\n\t\t\t\tnodeId,\n\t\t\t\tblueprint.id,\n\t\t\t\tundefined,\n\t\t\t\texecutionId,\n\t\t\t)\n\t\t}\n\n\t\tconst contextImpl = state.getContext()\n\t\tconst asyncContext: IAsyncContext<TContext> =\n\t\t\tcontextImpl.type === 'sync'\n\t\t\t\t? new AsyncContextView(contextImpl as ISyncContext<TContext>)\n\t\t\t\t: (contextImpl as IAsyncContext<TContext>)\n\t\tconst nodeContext: NodeContext<TContext, TDependencies, any> = {\n\t\t\tcontext: asyncContext,\n\t\t\tinput: await this._resolveNodeInput(nodeDef, asyncContext, allPredecessors),\n\t\t\tparams: nodeDef.params || {},\n\t\t\tdependencies: { ...this.dependencies, logger: this.logger },\n\t\t\tsignal,\n\t\t}\n\n\t\tconst beforeHooks = this.middleware\n\t\t\t.map((m) => m.beforeNode)\n\t\t\t.filter((hook): hook is NonNullable<Middleware['beforeNode']> => !!hook)\n\t\tconst afterHooks = this.middleware\n\t\t\t.map((m) => m.afterNode)\n\t\t\t.filter((hook): hook is NonNullable<Middleware['afterNode']> => !!hook)\n\t\tconst aroundHooks = this.middleware\n\t\t\t.map((m) => m.aroundNode)\n\t\t\t.filter((hook): hook is NonNullable<Middleware['aroundNode']> => !!hook)\n\n\t\tconst executor = this.getExecutor(nodeDef, functionRegistry)\n\t\tconst coreExecution = async (): Promise<NodeResult> => {\n\t\t\tlet result: NodeResult | undefined\n\t\t\tlet error: Error | undefined\n\t\t\ttry {\n\t\t\t\tfor (const hook of beforeHooks) await hook(nodeContext.context, nodeId)\n\t\t\t\tresult = await this.executeWithFallback(\n\t\t\t\t\tblueprint,\n\t\t\t\t\tnodeDef,\n\t\t\t\t\tnodeContext,\n\t\t\t\t\texecutor,\n\t\t\t\t\texecutionId,\n\t\t\t\t\tsignal,\n\t\t\t\t\tstate,\n\t\t\t\t\tfunctionRegistry,\n\t\t\t\t)\n\t\t\t\treturn result\n\t\t\t} catch (e: any) {\n\t\t\t\terror = e\n\t\t\t\tthrow e\n\t\t\t} finally {\n\t\t\t\tfor (const hook of afterHooks) await hook(nodeContext.context, nodeId, result, error)\n\t\t\t}\n\t\t}\n\n\t\tlet executionChain: () => Promise<NodeResult> = coreExecution\n\t\tfor (let i = aroundHooks.length - 1; i >= 0; i--) {\n\t\t\tconst hook = aroundHooks[i]\n\t\t\tconst next = executionChain\n\t\t\texecutionChain = () => hook(nodeContext.context, nodeId, next)\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.eventBus.emit('node:start', {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\tnodeId,\n\t\t\t\texecutionId,\n\t\t\t})\n\t\t\tconst result = await executionChain()\n\t\t\tawait this.eventBus.emit('node:finish', {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\tnodeId,\n\t\t\t\tresult,\n\t\t\t\texecutionId,\n\t\t\t})\n\t\t\treturn result\n\t\t} catch (error: any) {\n\t\t\tawait this.eventBus.emit('node:error', {\n\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\tnodeId,\n\t\t\t\terror,\n\t\t\t\texecutionId,\n\t\t\t})\n\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\tthrow new CancelledWorkflowError('Workflow cancelled')\n\t\t\t}\n\t\t\tthrow error instanceof NodeExecutionError\n\t\t\t\t? error\n\t\t\t\t: new NodeExecutionError(`Node '${nodeId}' failed execution.`, nodeId, blueprint.id, error, executionId)\n\t\t}\n\t}\n\n\tprivate getExecutor(nodeDef: NodeDefinition, functionRegistry?: Map<string, any>): ExecutionStrategy {\n\t\tif (nodeDef.uses.startsWith('batch-') || nodeDef.uses.startsWith('loop-') || nodeDef.uses === 'subflow') {\n\t\t\treturn new BuiltInNodeExecutor((nodeDef, context) => this._executeBuiltInNode(nodeDef, context))\n\t\t}\n\t\tconst implementation = functionRegistry?.get(nodeDef.uses) || this.registry[nodeDef.uses]\n\t\tif (!implementation) {\n\t\t\tthrow new FatalNodeExecutionError(\n\t\t\t\t`Implementation for '${nodeDef.uses}' not found for node '${nodeDef.id}'.`,\n\t\t\t\tnodeDef.id,\n\t\t\t\t'',\n\t\t\t)\n\t\t}\n\t\tconst maxRetries = nodeDef.config?.maxRetries ?? 1\n\t\treturn isNodeClass(implementation)\n\t\t\t? new ClassNodeExecutor(implementation, maxRetries, this.eventBus)\n\t\t\t: new FunctionNodeExecutor(implementation, maxRetries, this.eventBus)\n\t}\n\n\tprivate async executeWithFallback(\n\t\tblueprint: WorkflowBlueprint,\n\t\tnodeDef: NodeDefinition,\n\t\tcontext: NodeContext<TContext, TDependencies, any>,\n\t\texecutor: ExecutionStrategy,\n\t\texecutionId?: string,\n\t\tsignal?: AbortSignal,\n\t\tstate?: WorkflowState<TContext>,\n\t\tfunctionRegistry?: Map<string, any>,\n\t): Promise<NodeResult<any, any>> {\n\t\ttry {\n\t\t\treturn await executor.execute(nodeDef, context, executionId, signal)\n\t\t} catch (error) {\n\t\t\tconst isFatal =\n\t\t\t\terror instanceof FatalNodeExecutionError ||\n\t\t\t\t(error instanceof NodeExecutionError && error.originalError instanceof FatalNodeExecutionError)\n\t\t\tif (isFatal) throw error\n\t\t\tconst fallbackNodeId = nodeDef.config?.fallback\n\t\t\tif (fallbackNodeId && state) {\n\t\t\t\tcontext.dependencies.logger.warn(`Executing fallback for node`, {\n\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\tfallbackNodeId,\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\texecutionId,\n\t\t\t\t})\n\t\t\t\tawait this.eventBus.emit('node:fallback', {\n\t\t\t\t\tblueprintId: blueprint.id,\n\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\texecutionId,\n\t\t\t\t\tfallback: fallbackNodeId,\n\t\t\t\t})\n\t\t\t\tconst fallbackNode = blueprint.nodes.find((n: NodeDefinition) => n.id === fallbackNodeId)\n\t\t\t\tif (!fallbackNode) {\n\t\t\t\t\tthrow new NodeExecutionError(\n\t\t\t\t\t\t`Fallback node '${fallbackNodeId}' not found in blueprint.`,\n\t\t\t\t\t\tnodeDef.id,\n\t\t\t\t\t\tblueprint.id,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\texecutionId,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst fallbackExecutor = this.getExecutor(fallbackNode, functionRegistry)\n\t\t\t\tconst fallbackResult = await fallbackExecutor.execute(fallbackNode, context, executionId, signal)\n\t\t\t\tstate.markFallbackExecuted()\n\t\t\t\tstate.addCompletedNode(fallbackNodeId, fallbackResult.output)\n\t\t\t\tcontext.dependencies.logger.info(`Fallback execution completed`, {\n\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\tfallbackNodeId,\n\t\t\t\t\texecutionId,\n\t\t\t\t})\n\t\t\t\treturn { ...fallbackResult, _fallbackExecuted: true }\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync determineNextNodes(\n\t\tblueprint: WorkflowBlueprint,\n\t\tnodeId: string,\n\t\tresult: NodeResult<any, any>,\n\t\tcontext: ContextImplementation<TContext>,\n\t): Promise<{ node: NodeDefinition; edge: EdgeDefinition }[]> {\n\t\tconst outgoingEdges = blueprint.edges.filter((edge) => edge.source === nodeId)\n\t\tconst matched: { node: NodeDefinition; edge: EdgeDefinition }[] = []\n\t\tconst evaluateEdge = async (edge: EdgeDefinition): Promise<boolean> => {\n\t\t\tif (!edge.condition) return true\n\t\t\tconst contextData = context.type === 'sync' ? context.toJSON() : await context.toJSON()\n\t\t\treturn !!this.evaluator.evaluate(edge.condition, {\n\t\t\t\t...contextData,\n\t\t\t\tresult,\n\t\t\t})\n\t\t}\n\t\tif (result.action) {\n\t\t\tconst actionEdges = outgoingEdges.filter((edge) => edge.action === result.action)\n\t\t\tfor (const edge of actionEdges) {\n\t\t\t\tif (await evaluateEdge(edge)) {\n\t\t\t\t\tconst targetNode = blueprint.nodes.find((n) => n.id === edge.target)\n\t\t\t\t\tif (targetNode) matched.push({ node: targetNode, edge })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (matched.length === 0) {\n\t\t\tconst defaultEdges = outgoingEdges.filter((edge) => !edge.action)\n\t\t\tfor (const edge of defaultEdges) {\n\t\t\t\tif (await evaluateEdge(edge)) {\n\t\t\t\t\tconst targetNode = blueprint.nodes.find((n) => n.id === edge.target)\n\t\t\t\t\tif (targetNode) matched.push({ node: targetNode, edge })\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.logger.debug(`Determined next nodes for ${nodeId}`, {\n\t\t\tmatchedNodes: matched.map((m) => m.node.id),\n\t\t\taction: result.action,\n\t\t})\n\t\treturn matched\n\t}\n\n\tpublic async applyEdgeTransform(\n\t\tedge: EdgeDefinition,\n\t\tsourceResult: NodeResult<any, any>,\n\t\ttargetNode: NodeDefinition,\n\t\tcontext: ContextImplementation<TContext>,\n\t\tallPredecessors?: Map<string, Set<string>>,\n\t): Promise<void> {\n\t\tconst asyncContext = context.type === 'sync' ? new AsyncContextView(context) : context\n\t\tconst finalInput = edge.transform\n\t\t\t? this.evaluator.evaluate(edge.transform, {\n\t\t\t\t\tinput: sourceResult.output,\n\t\t\t\t\tcontext: await asyncContext.toJSON(),\n\t\t\t\t})\n\t\t\t: sourceResult.output\n\t\tconst inputKey = `${targetNode.id}_input`\n\t\tawait asyncContext.set(inputKey as any, finalInput)\n\t\tif (targetNode.config?.joinStrategy === 'any') {\n\t\t\ttargetNode.inputs = inputKey\n\t\t} else if (!targetNode.inputs) {\n\t\t\tconst predecessors = allPredecessors?.get(targetNode.id)\n\t\t\tif (!predecessors || predecessors.size === 1) {\n\t\t\t\ttargetNode.inputs = inputKey\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async _resolveNodeInput(\n\t\tnodeDef: NodeDefinition,\n\t\tcontext: IAsyncContext<TContext>,\n\t\tallPredecessors?: Map<string, Set<string>>,\n\t): Promise<any> {\n\t\tif (nodeDef.inputs) {\n\t\t\tif (typeof nodeDef.inputs === 'string') return await context.get(nodeDef.inputs as any)\n\t\t\tif (typeof nodeDef.inputs === 'object') {\n\t\t\t\tconst input: Record<string, any> = {}\n\t\t\t\tfor (const key in nodeDef.inputs) {\n\t\t\t\t\tconst contextKey = nodeDef.inputs[key]\n\t\t\t\t\tinput[key] = await context.get(contextKey as any)\n\t\t\t\t}\n\t\t\t\treturn input\n\t\t\t}\n\t\t}\n\t\tif (allPredecessors) {\n\t\t\tconst predecessors = allPredecessors.get(nodeDef.id)\n\t\t\tif (predecessors && predecessors.size === 1) {\n\t\t\t\tconst singlePredecessorId = predecessors.values().next().value\n\t\t\t\treturn await context.get(singlePredecessorId as any)\n\t\t\t}\n\t\t}\n\t\treturn undefined\n\t}\n\n\tprotected async _executeBuiltInNode(\n\t\tnodeDef: NodeDefinition,\n\t\tcontextImpl: ContextImplementation<TContext>,\n\t): Promise<NodeResult<any, any>> {\n\t\tconst context = contextImpl.type === 'sync' ? new AsyncContextView(contextImpl) : contextImpl\n\t\tconst { params = {}, id, inputs } = nodeDef\n\t\tswitch (nodeDef.uses) {\n\t\t\tcase 'batch-scatter': {\n\t\t\t\tconst inputArray = (await context.get(inputs as any)) || []\n\t\t\t\tif (!Array.isArray(inputArray))\n\t\t\t\t\tthrow new FatalNodeExecutionError(`Input for batch-scatter node '${id}' must be an array.`, id, '')\n\t\t\t\tconst batchId = globalThis.crypto.randomUUID()\n\t\t\t\tconst dynamicNodes: NodeDefinition[] = []\n\t\t\t\tfor (let i = 0; i < inputArray.length; i++) {\n\t\t\t\t\tconst item = inputArray[i]\n\t\t\t\t\tconst itemInputKey = `${id}_${batchId}_item_${i}`\n\t\t\t\t\tawait context.set(itemInputKey as any, item)\n\t\t\t\t\tdynamicNodes.push({\n\t\t\t\t\t\tid: `${params.workerUsesKey}_${batchId}_${i}`,\n\t\t\t\t\t\tuses: params.workerUsesKey,\n\t\t\t\t\t\tinputs: itemInputKey,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst gatherNodeId = params.gatherNodeId\n\t\t\t\treturn { dynamicNodes, output: { gatherNodeId } }\n\t\t\t}\n\t\t\tcase 'batch-gather': {\n\t\t\t\treturn { output: {} }\n\t\t\t}\n\t\t\tcase 'loop-controller': {\n\t\t\t\tconst contextData = await context.toJSON()\n\t\t\t\tconst shouldContinue = !!this.evaluator.evaluate(params.condition, contextData)\n\t\t\t\treturn { action: shouldContinue ? 'continue' : 'break' }\n\t\t\t}\n\t\t\tcase 'subflow': {\n\t\t\t\tconst { blueprintId, inputs: inputMapping, outputs: outputMapping } = params\n\t\t\t\tif (!blueprintId)\n\t\t\t\t\tthrow new FatalNodeExecutionError(`Subflow node '${id}' is missing the 'blueprintId' parameter.`, id, '')\n\n\t\t\t\tconst subBlueprint = this.blueprints[blueprintId]\n\t\t\t\tif (!subBlueprint)\n\t\t\t\t\tthrow new FatalNodeExecutionError(\n\t\t\t\t\t\t`Sub-blueprint with ID '${blueprintId}' not found in runtime registry.`,\n\t\t\t\t\t\tid,\n\t\t\t\t\t\t'',\n\t\t\t\t\t)\n\n\t\t\t\tconst subflowInitialContext: Record<string, any> = {}\n\n\t\t\t\tif (inputMapping) {\n\t\t\t\t\tfor (const [targetKey, sourceKey] of Object.entries(inputMapping)) {\n\t\t\t\t\t\tif (await context.has(sourceKey as any)) {\n\t\t\t\t\t\t\tsubflowInitialContext[targetKey] = await context.get(sourceKey as any)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst subflowResult = await this.run(subBlueprint, subflowInitialContext as Partial<TContext>)\n\n\t\t\t\tif (subflowResult.status !== 'completed')\n\t\t\t\t\tthrow new NodeExecutionError(\n\t\t\t\t\t\t`Sub-workflow '${blueprintId}' did not complete successfully. Status: ${subflowResult.status}`,\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tsubBlueprint.id,\n\t\t\t\t\t)\n\n\t\t\t\tif (outputMapping) {\n\t\t\t\t\tfor (const [parentKey, subKey] of Object.entries(outputMapping)) {\n\t\t\t\t\t\tconst subflowFinalContext = subflowResult.context as Record<string, any>\n\t\t\t\t\t\tif (Object.hasOwn(subflowFinalContext, subKey as string)) {\n\t\t\t\t\t\t\tawait context.set(parentKey as any, subflowFinalContext[subKey as string])\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn { output: subflowResult.context }\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new FatalNodeExecutionError(`Unknown built-in node type: '${nodeDef.uses}'`, id, '')\n\t\t}\n\t}\n}\n"]}
|