flowcraft 2.1.0 → 2.2.0
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/LICENSE +21 -0
- package/README.md +52 -6
- package/dist/analysis.d.ts +3 -2
- package/dist/analysis.js +1 -1
- package/dist/{chunk-HN72TZY5.js → chunk-233SESC2.js} +34 -23
- package/dist/chunk-233SESC2.js.map +1 -0
- package/dist/{chunk-UETC63DP.js → chunk-EUJWJWFA.js} +24 -4
- package/dist/chunk-EUJWJWFA.js.map +1 -0
- package/dist/{chunk-UMXW3TCY.js → chunk-GEKDR2SS.js} +44 -8
- package/dist/chunk-GEKDR2SS.js.map +1 -0
- package/dist/chunk-M2FRTT2K.js +144 -0
- package/dist/chunk-M2FRTT2K.js.map +1 -0
- package/dist/{chunk-CO5BTPKI.js → chunk-OTS5YJ3S.js} +108 -24
- package/dist/chunk-OTS5YJ3S.js.map +1 -0
- package/dist/{chunk-5EHIPX23.js → chunk-QLGJUDQF.js} +84 -23
- package/dist/chunk-QLGJUDQF.js.map +1 -0
- package/dist/{chunk-KWQHFT7E.js → chunk-R3HQXIEL.js} +6 -6
- package/dist/chunk-R3HQXIEL.js.map +1 -0
- package/dist/{chunk-5QMPFUKA.js → chunk-U5V5O5MN.js} +11 -2
- package/dist/chunk-U5V5O5MN.js.map +1 -0
- package/dist/{chunk-CSZ6EOWG.js → chunk-VSGQDLBF.js} +3 -3
- package/dist/chunk-VSGQDLBF.js.map +1 -0
- package/dist/{chunk-5ZWYSKMH.js → chunk-ZCHFZBGL.js} +25 -8
- package/dist/chunk-ZCHFZBGL.js.map +1 -0
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/evaluator.d.ts +1 -1
- package/dist/flow.d.ts +19 -8
- package/dist/flow.js +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +10 -10
- package/dist/linter.d.ts +3 -3
- package/dist/linter.js +2 -2
- package/dist/logger.d.ts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +1 -1
- package/dist/runtime/adapter.d.ts +8 -1
- package/dist/runtime/adapter.js +8 -8
- package/dist/runtime/builtin-keys.d.ts +38 -0
- package/dist/runtime/builtin-keys.js +10 -0
- package/dist/runtime/builtin-keys.js.map +1 -0
- package/dist/runtime/executors.d.ts +1 -1
- package/dist/runtime/executors.js +1 -1
- package/dist/runtime/index.d.ts +2 -1
- package/dist/runtime/index.js +8 -8
- package/dist/runtime/runtime.d.ts +11 -4
- package/dist/runtime/runtime.js +7 -7
- package/dist/runtime/state.d.ts +1 -1
- package/dist/runtime/state.js +2 -2
- package/dist/runtime/traverser.d.ts +2 -1
- package/dist/runtime/traverser.js +2 -2
- package/dist/runtime/types.d.ts +3 -2
- package/dist/sanitizer.d.ts +1 -1
- package/dist/serializer.d.ts +1 -1
- package/dist/{types-lG3xCzp_.d.ts → types-CsTeXTiA.d.ts} +24 -8
- package/dist/types.d.ts +1 -1
- package/package.json +51 -51
- package/dist/chunk-5EHIPX23.js.map +0 -1
- package/dist/chunk-5QMPFUKA.js.map +0 -1
- package/dist/chunk-5ZWYSKMH.js.map +0 -1
- package/dist/chunk-CO5BTPKI.js.map +0 -1
- package/dist/chunk-CSZ6EOWG.js.map +0 -1
- package/dist/chunk-HN72TZY5.js.map +0 -1
- package/dist/chunk-KWQHFT7E.js.map +0 -1
- package/dist/chunk-QRMUKDSP.js +0 -141
- package/dist/chunk-QRMUKDSP.js.map +0 -1
- package/dist/chunk-UETC63DP.js.map +0 -1
- package/dist/chunk-UMXW3TCY.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Feathers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `flowcraft`
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/flowcraft)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -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 of
|
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { W as WorkflowBlueprint } from './types-
|
|
1
|
+
import { W as WorkflowBlueprint } from './types-CsTeXTiA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A list of cycles found in the graph. Each cycle is an array of node IDs.
|
|
@@ -22,7 +22,8 @@ interface BlueprintAnalysis {
|
|
|
22
22
|
isDag: boolean;
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Analyzes a workflow blueprint to detect cycles.
|
|
25
|
+
* Analyzes a workflow blueprint to detect cycles using an iterative DFS algorithm.
|
|
26
|
+
* This avoids stack overflow issues for deep graphs compared to the recursive version.
|
|
26
27
|
* @param blueprint The WorkflowBlueprint object containing nodes and edges.
|
|
27
28
|
* @returns An array of cycles found. Each cycle is represented as an array of node IDs.
|
|
28
29
|
*/
|
package/dist/analysis.js
CHANGED
|
@@ -12,28 +12,39 @@ function checkForCycles(blueprint) {
|
|
|
12
12
|
for (const edge of blueprint.edges) {
|
|
13
13
|
adj.get(edge.source)?.push(edge.target);
|
|
14
14
|
}
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
visited.add(nodeId);
|
|
19
|
-
recursionStack.add(nodeId);
|
|
20
|
-
path.push(nodeId);
|
|
21
|
-
const neighbors = adj.get(nodeId) || [];
|
|
22
|
-
for (const neighbor of neighbors) {
|
|
23
|
-
if (recursionStack.has(neighbor)) {
|
|
24
|
-
const cycleStartIndex = path.indexOf(neighbor);
|
|
25
|
-
const cycle = path.slice(cycleStartIndex);
|
|
26
|
-
cycles.push([...cycle, neighbor]);
|
|
27
|
-
} else if (!visited.has(neighbor)) {
|
|
28
|
-
detectCycleUtil(neighbor, path);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
recursionStack.delete(nodeId);
|
|
32
|
-
path.pop();
|
|
15
|
+
const state = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const id of allNodeIds) {
|
|
17
|
+
state.set(id, 0);
|
|
33
18
|
}
|
|
34
|
-
for (const
|
|
35
|
-
if (
|
|
36
|
-
|
|
19
|
+
for (const startNode of allNodeIds) {
|
|
20
|
+
if (state.get(startNode) !== 0) continue;
|
|
21
|
+
const stack = [{ node: startNode, path: [] }];
|
|
22
|
+
const pathSet = /* @__PURE__ */ new Set();
|
|
23
|
+
while (stack.length > 0) {
|
|
24
|
+
const { node, path } = stack[stack.length - 1];
|
|
25
|
+
if (state.get(node) === 0) {
|
|
26
|
+
state.set(node, 1);
|
|
27
|
+
pathSet.add(node);
|
|
28
|
+
path.push(node);
|
|
29
|
+
}
|
|
30
|
+
const neighbors = adj.get(node) || [];
|
|
31
|
+
let foundUnvisited = false;
|
|
32
|
+
for (const neighbor of neighbors) {
|
|
33
|
+
if (state.get(neighbor) === 1) {
|
|
34
|
+
const cycleStartIndex = path.indexOf(neighbor);
|
|
35
|
+
const cycle = path.slice(cycleStartIndex);
|
|
36
|
+
cycles.push([...cycle, neighbor]);
|
|
37
|
+
} else if (state.get(neighbor) === 0) {
|
|
38
|
+
stack.push({ node: neighbor, path: [...path] });
|
|
39
|
+
foundUnvisited = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!foundUnvisited) {
|
|
44
|
+
state.set(node, 2);
|
|
45
|
+
stack.pop();
|
|
46
|
+
pathSet.delete(node);
|
|
47
|
+
}
|
|
37
48
|
}
|
|
38
49
|
}
|
|
39
50
|
return cycles;
|
|
@@ -106,5 +117,5 @@ function analyzeBlueprint(blueprint) {
|
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
export { analyzeBlueprint, checkForCycles, generateMermaid };
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
110
|
-
//# sourceMappingURL=chunk-
|
|
120
|
+
//# sourceMappingURL=chunk-233SESC2.js.map
|
|
121
|
+
//# sourceMappingURL=chunk-233SESC2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/analysis.ts"],"names":[],"mappings":"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","file":"chunk-233SESC2.js","sourcesContent":["import type { WorkflowBlueprint } from './types'\n\n/**\n * A list of cycles found in the graph. Each cycle is an array of node IDs.\n */\nexport type Cycles = string[][]\n\n/**\n * Analysis result for a workflow blueprint\n */\nexport interface BlueprintAnalysis {\n\t/** Cycles found in the graph */\n\tcycles: Cycles\n\t/** Node IDs that have no incoming edges (start nodes) */\n\tstartNodeIds: string[]\n\t/** Node IDs that have no outgoing edges (terminal nodes) */\n\tterminalNodeIds: string[]\n\t/** Total number of nodes */\n\tnodeCount: number\n\t/** Total number of edges */\n\tedgeCount: number\n\t/** Whether the graph is a valid DAG (no cycles) */\n\tisDag: boolean\n}\n\n/**\n * Analyzes a workflow blueprint to detect cycles using an iterative DFS algorithm.\n * This avoids stack overflow issues for deep graphs compared to the recursive version.\n * @param blueprint The WorkflowBlueprint object containing nodes and edges.\n * @returns An array of cycles found. Each cycle is represented as an array of node IDs.\n */\nexport function checkForCycles(blueprint: WorkflowBlueprint): Cycles {\n\tconst cycles: Cycles = []\n\tif (!blueprint || !blueprint.nodes || blueprint.nodes.length === 0) {\n\t\treturn cycles\n\t}\n\n\tconst allNodeIds = blueprint.nodes.map((node) => node.id)\n\tconst adj = new Map<string, string[]>()\n\tfor (const id of allNodeIds) {\n\t\tadj.set(id, [])\n\t}\n\tfor (const edge of blueprint.edges) {\n\t\tadj.get(edge.source)?.push(edge.target)\n\t}\n\n\t// 0 = not visited, 1 = visiting, 2 = visited\n\tconst state = new Map<string, number>()\n\tfor (const id of allNodeIds) {\n\t\tstate.set(id, 0)\n\t}\n\n\tfor (const startNode of allNodeIds) {\n\t\tif (state.get(startNode) !== 0) continue\n\n\t\tconst stack: { node: string; path: string[] }[] = [{ node: startNode, path: [] }]\n\t\tconst pathSet = new Set<string>()\n\n\t\twhile (stack.length > 0) {\n\t\t\tconst { node, path } = stack[stack.length - 1]\n\n\t\t\tif (state.get(node) === 0) {\n\t\t\t\t// first visit\n\t\t\t\tstate.set(node, 1) // visiting\n\t\t\t\tpathSet.add(node)\n\t\t\t\tpath.push(node)\n\t\t\t}\n\n\t\t\tconst neighbors = adj.get(node) || []\n\t\t\tlet foundUnvisited = false\n\n\t\t\tfor (const neighbor of neighbors) {\n\t\t\t\tif (state.get(neighbor) === 1) {\n\t\t\t\t\t// back edge, cycle found\n\t\t\t\t\tconst cycleStartIndex = path.indexOf(neighbor)\n\t\t\t\t\tconst cycle = path.slice(cycleStartIndex)\n\t\t\t\t\tcycles.push([...cycle, neighbor])\n\t\t\t\t} else if (state.get(neighbor) === 0) {\n\t\t\t\t\t// unvisited neighbor\n\t\t\t\t\tstack.push({ node: neighbor, path: [...path] })\n\t\t\t\t\tfoundUnvisited = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!foundUnvisited) {\n\t\t\t\t// all neighbors visited\n\t\t\t\tstate.set(node, 2) // visited\n\t\t\t\tstack.pop()\n\t\t\t\tpathSet.delete(node)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn cycles\n}\n\n/**\n * Generates Mermaid diagram syntax from a WorkflowBlueprint\n * @param blueprint The WorkflowBlueprint object containing nodes and edges\n * @returns Mermaid syntax string for the flowchart\n */\nexport function generateMermaid(blueprint: WorkflowBlueprint): string {\n\tif (!blueprint || !blueprint.nodes || blueprint.nodes.length === 0) {\n\t\treturn 'flowchart TD\\n empty[Empty Blueprint]'\n\t}\n\n\tlet mermaid = 'flowchart TD\\n'\n\n\tfor (const node of blueprint.nodes) {\n\t\t// Using backticks and characters like <br/> for newlines in Mermaid labels\n\t\tconst paramsString = node.params ? `<br/>params: ${JSON.stringify(node.params)}` : ''\n\t\tconst nodeLabel = `${node.id}${paramsString}`\n\t\tmermaid += ` ${node.id}[\"${nodeLabel}\"]\\n`\n\t}\n\n\tfor (const edge of blueprint.edges || []) {\n\t\tconst labelParts: string[] = []\n\n\t\tif (edge.action) {\n\t\t\tlabelParts.push(edge.action)\n\t\t}\n\t\tif (edge.condition) {\n\t\t\tlabelParts.push(edge.condition)\n\t\t}\n\t\tif (edge.transform) {\n\t\t\tlabelParts.push(edge.transform)\n\t\t}\n\n\t\tif (labelParts.length > 0) {\n\t\t\tconst edgeLabel = labelParts.join(' | ')\n\t\t\tmermaid += ` ${edge.source} -- \"${edgeLabel}\" --> ${edge.target}\\n`\n\t\t} else {\n\t\t\tmermaid += ` ${edge.source} --> ${edge.target}\\n`\n\t\t}\n\t}\n\n\treturn mermaid\n}\n\n/**\n * Analyzes a workflow blueprint and returns comprehensive analysis\n * @param blueprint The WorkflowBlueprint object containing nodes and edges\n * @returns Analysis result with cycles, start nodes, terminal nodes, and other metrics\n */\nexport function analyzeBlueprint(blueprint: WorkflowBlueprint): BlueprintAnalysis {\n\tif (!blueprint || !blueprint.nodes || blueprint.nodes.length === 0) {\n\t\treturn {\n\t\t\tcycles: [],\n\t\t\tstartNodeIds: [],\n\t\t\tterminalNodeIds: [],\n\t\t\tnodeCount: 0,\n\t\t\tedgeCount: 0,\n\t\t\tisDag: true,\n\t\t}\n\t}\n\n\tconst cycles = checkForCycles(blueprint)\n\tconst nodeCount = blueprint.nodes.length\n\tconst edgeCount = blueprint.edges?.length || 0\n\n\tconst nodesWithIncoming = new Set<string>()\n\tfor (const edge of blueprint.edges || []) {\n\t\tnodesWithIncoming.add(edge.target)\n\t}\n\n\tconst startNodeIds = blueprint.nodes.map((node) => node.id).filter((nodeId) => !nodesWithIncoming.has(nodeId))\n\n\tconst nodesWithOutgoing = new Set<string>()\n\tfor (const edge of blueprint.edges || []) {\n\t\tnodesWithOutgoing.add(edge.source)\n\t}\n\n\tconst terminalNodeIds = blueprint.nodes.map((node) => node.id).filter((nodeId) => !nodesWithOutgoing.has(nodeId))\n\n\treturn {\n\t\tcycles,\n\t\tstartNodeIds,\n\t\tterminalNodeIds,\n\t\tnodeCount,\n\t\tedgeCount,\n\t\tisDag: cycles.length === 0,\n\t}\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { analyzeBlueprint } from './chunk-
|
|
1
|
+
import { analyzeBlueprint } from './chunk-233SESC2.js';
|
|
2
2
|
|
|
3
3
|
// src/linter.ts
|
|
4
|
-
function lintBlueprint(blueprint, registry) {
|
|
4
|
+
function lintBlueprint(blueprint, registry, blueprints) {
|
|
5
5
|
const issues = [];
|
|
6
6
|
const nodeIds = new Set(blueprint.nodes.map((n) => n.id));
|
|
7
7
|
const registryKeys = registry instanceof Map ? new Set(registry.keys()) : new Set(Object.keys(registry));
|
|
@@ -14,6 +14,26 @@ function lintBlueprint(blueprint, registry) {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
for (const node of blueprint.nodes) {
|
|
18
|
+
if (node.uses.startsWith("batch-") && node.params?.workerUsesKey) {
|
|
19
|
+
if (!registryKeys.has(node.params.workerUsesKey)) {
|
|
20
|
+
issues.push({
|
|
21
|
+
code: "INVALID_BATCH_WORKER_KEY",
|
|
22
|
+
message: `Batch node '${node.id}' references workerUsesKey '${node.params.workerUsesKey}' which is not found in the registry.`,
|
|
23
|
+
nodeId: node.id
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (node.uses === "subflow" && node.params?.blueprintId) {
|
|
28
|
+
if (!blueprints || !blueprints[node.params.blueprintId]) {
|
|
29
|
+
issues.push({
|
|
30
|
+
code: "INVALID_SUBFLOW_BLUEPRINT_ID",
|
|
31
|
+
message: `Subflow node '${node.id}' references blueprintId '${node.params.blueprintId}' which is not found in the blueprints registry.`,
|
|
32
|
+
nodeId: node.id
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
17
37
|
for (const edge of blueprint.edges || []) {
|
|
18
38
|
if (!nodeIds.has(edge.source)) {
|
|
19
39
|
issues.push({
|
|
@@ -61,5 +81,5 @@ function lintBlueprint(blueprint, registry) {
|
|
|
61
81
|
}
|
|
62
82
|
|
|
63
83
|
export { lintBlueprint };
|
|
64
|
-
//# sourceMappingURL=chunk-
|
|
65
|
-
//# sourceMappingURL=chunk-
|
|
84
|
+
//# sourceMappingURL=chunk-EUJWJWFA.js.map
|
|
85
|
+
//# sourceMappingURL=chunk-EUJWJWFA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/linter.ts"],"names":["e"],"mappings":"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","file":"chunk-EUJWJWFA.js","sourcesContent":["import { analyzeBlueprint } from './analysis'\nimport type { NodeClass, NodeFunction, WorkflowBlueprint } from './types'\n\nexport type LinterIssueCode =\n\t| 'INVALID_EDGE_SOURCE'\n\t| 'INVALID_EDGE_TARGET'\n\t| 'MISSING_NODE_IMPLEMENTATION'\n\t| 'ORPHAN_NODE'\n\t| 'INVALID_BATCH_WORKER_KEY'\n\t| 'INVALID_SUBFLOW_BLUEPRINT_ID'\n\nexport interface LinterIssue {\n\tcode: LinterIssueCode\n\tmessage: string\n\tnodeId?: string\n\trelatedId?: string\n}\n\nexport interface LinterResult {\n\tisValid: boolean\n\tissues: LinterIssue[]\n}\n\n/**\n * Statically analyzes a workflow blueprint against a registry of implementations\n * to find common errors before runtime.\n *\n * @param blueprint The WorkflowBlueprint to analyze.\n * @param registry A map of node implementations (functions or classes) to check against.\n * @returns A LinterResult object containing any issues found.\n */\nexport function lintBlueprint(\n\tblueprint: WorkflowBlueprint,\n\tregistry: Map<string, NodeFunction | NodeClass> | Record<string, NodeFunction | NodeClass>,\n\tblueprints?: Record<string, WorkflowBlueprint>,\n): LinterResult {\n\tconst issues: LinterIssue[] = []\n\tconst nodeIds = new Set(blueprint.nodes.map((n) => n.id))\n\tconst registryKeys = registry instanceof Map ? new Set(registry.keys()) : new Set(Object.keys(registry))\n\n\t// check for missing node implementations\n\tfor (const node of blueprint.nodes) {\n\t\tif (!node.uses.startsWith('batch-') && !node.uses.startsWith('loop-') && !registryKeys.has(node.uses)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'MISSING_NODE_IMPLEMENTATION',\n\t\t\t\tmessage: `Node implementation key '${node.uses}' is not found in the provided registry.`,\n\t\t\t\tnodeId: node.id,\n\t\t\t})\n\t\t}\n\t}\n\n\t// check for dynamic node validation\n\tfor (const node of blueprint.nodes) {\n\t\tif (node.uses.startsWith('batch-') && node.params?.workerUsesKey) {\n\t\t\tif (!registryKeys.has(node.params.workerUsesKey)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'INVALID_BATCH_WORKER_KEY',\n\t\t\t\t\tmessage: `Batch node '${node.id}' references workerUsesKey '${node.params.workerUsesKey}' which is not found in the registry.`,\n\t\t\t\t\tnodeId: node.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (node.uses === 'subflow' && node.params?.blueprintId) {\n\t\t\tif (!blueprints || !blueprints[node.params.blueprintId]) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'INVALID_SUBFLOW_BLUEPRINT_ID',\n\t\t\t\t\tmessage: `Subflow node '${node.id}' references blueprintId '${node.params.blueprintId}' which is not found in the blueprints registry.`,\n\t\t\t\t\tnodeId: node.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t// check for graph integrity (edges must point to valid nodes)\n\tfor (const edge of blueprint.edges || []) {\n\t\tif (!nodeIds.has(edge.source)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'INVALID_EDGE_SOURCE',\n\t\t\t\tmessage: `Edge source '${edge.source}' does not correspond to a valid node ID.`,\n\t\t\t\trelatedId: edge.target,\n\t\t\t})\n\t\t}\n\t\tif (!nodeIds.has(edge.target)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'INVALID_EDGE_TARGET',\n\t\t\t\tmessage: `Edge target '${edge.target}' does not correspond to a valid node ID.`,\n\t\t\t\trelatedId: edge.source,\n\t\t\t})\n\t\t}\n\t}\n\n\t// check for orphan nodes (not connected to the main graph)\n\tif (blueprint.nodes.length > 1) {\n\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\tconst connectedNodes = new Set<string>()\n\t\tconst nodesToVisit = [...analysis.startNodeIds]\n\t\tconst visited = new Set<string>()\n\n\t\twhile (nodesToVisit.length > 0) {\n\t\t\tconst currentId = nodesToVisit.pop()\n\t\t\tif (!currentId || visited.has(currentId)) continue\n\n\t\t\tvisited.add(currentId)\n\t\t\tconnectedNodes.add(currentId)\n\n\t\t\tfor (const e of blueprint.edges.filter((e) => e.source === currentId)) {\n\t\t\t\tnodesToVisit.push(e.target)\n\t\t\t}\n\t\t}\n\n\t\tfor (const nodeId of nodeIds) {\n\t\t\tif (!connectedNodes.has(nodeId)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'ORPHAN_NODE',\n\t\t\t\t\tmessage: `Node '${nodeId}' is not reachable from any start node.`,\n\t\t\t\t\tnodeId,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tisValid: issues.length === 0,\n\t\tissues,\n\t}\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { CancelledWorkflowError } from './chunk-5ZXV3R5D.js';
|
|
2
|
-
import { analyzeBlueprint } from './chunk-
|
|
1
|
+
import { CancelledWorkflowError, NodeExecutionError } from './chunk-5ZXV3R5D.js';
|
|
2
|
+
import { analyzeBlueprint } from './chunk-233SESC2.js';
|
|
3
3
|
|
|
4
4
|
// src/runtime/traverser.ts
|
|
5
5
|
var GraphTraverser = class {
|
|
@@ -10,6 +10,10 @@ var GraphTraverser = class {
|
|
|
10
10
|
this.executionId = executionId;
|
|
11
11
|
this.signal = signal;
|
|
12
12
|
this.concurrency = concurrency;
|
|
13
|
+
if (this.concurrency === void 0) {
|
|
14
|
+
const hardwareConcurrency = globalThis.navigator?.hardwareConcurrency || 4;
|
|
15
|
+
this.concurrency = Math.min(hardwareConcurrency, 10);
|
|
16
|
+
}
|
|
13
17
|
this.dynamicBlueprint = structuredClone(blueprint);
|
|
14
18
|
this.allPredecessors = /* @__PURE__ */ new Map();
|
|
15
19
|
for (const node of this.dynamicBlueprint.nodes) {
|
|
@@ -22,8 +26,12 @@ var GraphTraverser = class {
|
|
|
22
26
|
this.frontier = new Set(analysis.startNodeIds.filter((id) => !this.isFallbackNode(id)));
|
|
23
27
|
if (this.frontier.size === 0 && analysis.cycles.length > 0 && this.runtime.options.strict !== true) {
|
|
24
28
|
const uniqueStartNodes = /* @__PURE__ */ new Set();
|
|
29
|
+
const cycleEntryPoints = new Set(blueprint.metadata?.cycleEntryPoints || []);
|
|
25
30
|
for (const cycle of analysis.cycles) {
|
|
26
|
-
if (cycle.length > 0)
|
|
31
|
+
if (cycle.length > 0) {
|
|
32
|
+
const entryPoint = cycle.find((node) => cycleEntryPoints.has(node));
|
|
33
|
+
uniqueStartNodes.add(entryPoint || cycle[0]);
|
|
34
|
+
}
|
|
27
35
|
}
|
|
28
36
|
this.frontier = new Set(uniqueStartNodes);
|
|
29
37
|
}
|
|
@@ -34,6 +42,23 @@ var GraphTraverser = class {
|
|
|
34
42
|
isFallbackNode(nodeId) {
|
|
35
43
|
return this.dynamicBlueprint.nodes.some((n) => n.config?.fallback === nodeId);
|
|
36
44
|
}
|
|
45
|
+
getEffectiveJoinStrategy(nodeId) {
|
|
46
|
+
const node = this.dynamicBlueprint.nodes.find((n) => n.id === nodeId);
|
|
47
|
+
const baseJoinStrategy = node?.config?.joinStrategy || "all";
|
|
48
|
+
if (node?.uses === "loop-controller") {
|
|
49
|
+
return "any";
|
|
50
|
+
}
|
|
51
|
+
const predecessors = this.allPredecessors.get(nodeId);
|
|
52
|
+
if (predecessors) {
|
|
53
|
+
for (const predecessorId of predecessors) {
|
|
54
|
+
const predecessorNode = this.dynamicBlueprint.nodes.find((n) => n.id === predecessorId);
|
|
55
|
+
if (predecessorNode?.uses === "loop-controller") {
|
|
56
|
+
return "any";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return baseJoinStrategy;
|
|
61
|
+
}
|
|
37
62
|
async traverse() {
|
|
38
63
|
try {
|
|
39
64
|
this.signal?.throwIfAborted();
|
|
@@ -69,12 +94,13 @@ var GraphTraverser = class {
|
|
|
69
94
|
this.dynamicBlueprint,
|
|
70
95
|
nodeId,
|
|
71
96
|
result,
|
|
72
|
-
this.state.getContext()
|
|
97
|
+
this.state.getContext(),
|
|
98
|
+
this.executionId
|
|
73
99
|
);
|
|
74
100
|
const loopControllerMatch = matched.find((m) => m.node.uses === "loop-controller");
|
|
75
101
|
const finalMatched = loopControllerMatch ? [loopControllerMatch] : matched;
|
|
76
102
|
for (const { node, edge } of finalMatched) {
|
|
77
|
-
const joinStrategy = node.
|
|
103
|
+
const joinStrategy = this.getEffectiveJoinStrategy(node.id);
|
|
78
104
|
if (joinStrategy !== "any" && this.state.getCompletedNodes().has(node.id)) continue;
|
|
79
105
|
await this.runtime.applyEdgeTransform(edge, result, node, this.state.getContext(), this.allPredecessors);
|
|
80
106
|
const requiredPredecessors = this.allPredecessors.get(node.id);
|
|
@@ -85,7 +111,7 @@ var GraphTraverser = class {
|
|
|
85
111
|
if (matched.length === 0) {
|
|
86
112
|
for (const [potentialNextId, predecessors] of this.allPredecessors) {
|
|
87
113
|
if (predecessors.has(nodeId) && !this.state.getCompletedNodes().has(potentialNextId)) {
|
|
88
|
-
const joinStrategy = this.
|
|
114
|
+
const joinStrategy = this.getEffectiveJoinStrategy(potentialNextId);
|
|
89
115
|
const isReady = joinStrategy === "any" ? [...predecessors].some((p) => completedThisTurn.has(p)) : [...predecessors].every((p) => this.state.getCompletedNodes().has(p));
|
|
90
116
|
if (isReady) this.frontier.add(potentialNextId);
|
|
91
117
|
}
|
|
@@ -136,6 +162,16 @@ var GraphTraverser = class {
|
|
|
136
162
|
if (result.dynamicNodes && result.dynamicNodes.length > 0) {
|
|
137
163
|
const gatherNodeId = result.output?.gatherNodeId;
|
|
138
164
|
for (const dynamicNode of result.dynamicNodes) {
|
|
165
|
+
const implementation = this.functionRegistry?.get(dynamicNode.uses) || this.runtime.registry[dynamicNode.uses];
|
|
166
|
+
if (!implementation) {
|
|
167
|
+
throw new NodeExecutionError(
|
|
168
|
+
`Implementation for '${dynamicNode.uses}' not found for dynamic node '${dynamicNode.id}' generated by node '${nodeId}'.`,
|
|
169
|
+
dynamicNode.id,
|
|
170
|
+
this.dynamicBlueprint.id,
|
|
171
|
+
void 0,
|
|
172
|
+
this.executionId
|
|
173
|
+
);
|
|
174
|
+
}
|
|
139
175
|
this.dynamicBlueprint.nodes.push(dynamicNode);
|
|
140
176
|
this.allPredecessors.set(dynamicNode.id, /* @__PURE__ */ new Set([nodeId]));
|
|
141
177
|
if (gatherNodeId) {
|
|
@@ -161,5 +197,5 @@ var GraphTraverser = class {
|
|
|
161
197
|
};
|
|
162
198
|
|
|
163
199
|
export { GraphTraverser };
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
200
|
+
//# sourceMappingURL=chunk-GEKDR2SS.js.map
|
|
201
|
+
//# sourceMappingURL=chunk-GEKDR2SS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/traverser.ts"],"names":["nodeId"],"mappings":"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","file":"chunk-GEKDR2SS.js","sourcesContent":["import { analyzeBlueprint } from '../analysis'\nimport { CancelledWorkflowError, NodeExecutionError } from '../errors'\nimport type { NodeResult, WorkflowBlueprint } from '../types'\nimport type { WorkflowState } from './state'\nimport type { IRuntime } from './types'\n\nexport class GraphTraverser<TContext extends Record<string, any>, TDependencies extends Record<string, any>> {\n\tprivate frontier = new Set<string>()\n\tprivate allPredecessors: Map<string, Set<string>>\n\tprivate dynamicBlueprint: WorkflowBlueprint\n\n\tconstructor(\n\t\tblueprint: WorkflowBlueprint,\n\t\tprivate runtime: IRuntime<TContext, TDependencies>,\n\t\tprivate state: WorkflowState<TContext>,\n\t\tprivate functionRegistry: Map<string, any> | undefined,\n\t\tprivate executionId: string,\n\t\tprivate signal?: AbortSignal,\n\t\tprivate concurrency?: number,\n\t) {\n\t\tif (this.concurrency === undefined) {\n\t\t\tconst hardwareConcurrency = globalThis.navigator?.hardwareConcurrency || 4\n\t\t\tthis.concurrency = Math.min(hardwareConcurrency, 10)\n\t\t}\n\t\tthis.dynamicBlueprint = structuredClone(blueprint) as WorkflowBlueprint\n\t\tthis.allPredecessors = new Map<string, Set<string>>()\n\t\tfor (const node of this.dynamicBlueprint.nodes) {\n\t\t\tthis.allPredecessors.set(node.id, new Set())\n\t\t}\n\t\tfor (const edge of this.dynamicBlueprint.edges) {\n\t\t\tthis.allPredecessors.get(edge.target)?.add(edge.source)\n\t\t}\n\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\tthis.frontier = new Set(analysis.startNodeIds.filter((id) => !this.isFallbackNode(id)))\n\t\tif (this.frontier.size === 0 && analysis.cycles.length > 0 && this.runtime.options.strict !== true) {\n\t\t\tconst uniqueStartNodes = new Set<string>()\n\t\t\tconst cycleEntryPoints = new Set(blueprint.metadata?.cycleEntryPoints || [])\n\t\t\tfor (const cycle of analysis.cycles) {\n\t\t\t\tif (cycle.length > 0) {\n\t\t\t\t\t// prefer entry points if specified, otherwise use the first node\n\t\t\t\t\tconst entryPoint = cycle.find((node) => cycleEntryPoints.has(node))\n\t\t\t\t\tuniqueStartNodes.add(entryPoint || cycle[0])\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.frontier = new Set(uniqueStartNodes)\n\t\t}\n\t}\n\n\tprivate isFallbackNode(nodeId: string): boolean {\n\t\treturn this.dynamicBlueprint.nodes.some((n) => n.config?.fallback === nodeId)\n\t}\n\n\tprivate getEffectiveJoinStrategy(nodeId: string): 'any' | 'all' {\n\t\tconst node = this.dynamicBlueprint.nodes.find((n) => n.id === nodeId)\n\t\tconst baseJoinStrategy = node?.config?.joinStrategy || 'all'\n\n\t\tif (node?.uses === 'loop-controller') {\n\t\t\treturn 'any'\n\t\t}\n\n\t\tconst predecessors = this.allPredecessors.get(nodeId)\n\t\tif (predecessors) {\n\t\t\tfor (const predecessorId of predecessors) {\n\t\t\t\tconst predecessorNode = this.dynamicBlueprint.nodes.find((n) => n.id === predecessorId)\n\t\t\t\tif (predecessorNode?.uses === 'loop-controller') {\n\t\t\t\t\treturn 'any'\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn baseJoinStrategy\n\t}\n\n\tasync traverse(): Promise<void> {\n\t\ttry {\n\t\t\tthis.signal?.throwIfAborted()\n\t\t} catch (error) {\n\t\t\tif (error instanceof DOMException && error.name === 'AbortError')\n\t\t\t\tthrow new CancelledWorkflowError('Workflow cancelled')\n\t\t\tthrow error\n\t\t}\n\t\tlet iterations = 0\n\t\tconst maxIterations = 10000\n\t\twhile (this.frontier.size > 0) {\n\t\t\tif (++iterations > maxIterations) throw new Error('Traversal exceeded maximum iterations, possible infinite loop')\n\n\t\t\ttry {\n\t\t\t\tthis.signal?.throwIfAborted()\n\t\t\t\tconst currentJobs = Array.from(this.frontier)\n\t\t\t\tthis.frontier.clear()\n\t\t\t\tconst settledResults = await this.executeWithConcurrency(currentJobs)\n\t\t\t\tconst completedThisTurn = new Set<string>()\n\t\t\t\tfor (const promiseResult of settledResults) {\n\t\t\t\t\tif (promiseResult.status === 'rejected') {\n\t\t\t\t\t\tconst { nodeId, error } = promiseResult.reason\n\t\t\t\t\t\tif (error instanceof CancelledWorkflowError) throw error\n\t\t\t\t\t\tthis.state.addError(nodeId, error as Error)\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst { nodeId, result } = promiseResult.value\n\t\t\t\t\tthis.state.addCompletedNode(nodeId, result.output)\n\t\t\t\t\tcompletedThisTurn.add(nodeId)\n\t\t\t\t\tif (result._fallbackExecuted) this.state.markFallbackExecuted()\n\t\t\t\t\tawait this.handleDynamicNodes(nodeId, result)\n\t\t\t\t\tif (!result._fallbackExecuted) {\n\t\t\t\t\t\tconst matched = await this.runtime.determineNextNodes(\n\t\t\t\t\t\t\tthis.dynamicBlueprint,\n\t\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\t\tresult,\n\t\t\t\t\t\t\tthis.state.getContext(),\n\t\t\t\t\t\t\tthis.executionId,\n\t\t\t\t\t\t)\n\n\t\t\t\t\t\t// if one of the next nodes is a loop controller, prioritize it to avoid ambiguity from manual cycle edges.\n\t\t\t\t\t\tconst loopControllerMatch = matched.find((m) => m.node.uses === 'loop-controller')\n\t\t\t\t\t\tconst finalMatched = loopControllerMatch ? [loopControllerMatch] : matched\n\n\t\t\t\t\t\tfor (const { node, edge } of finalMatched) {\n\t\t\t\t\t\t\tconst joinStrategy = this.getEffectiveJoinStrategy(node.id)\n\t\t\t\t\t\t\tif (joinStrategy !== 'any' && this.state.getCompletedNodes().has(node.id)) continue\n\t\t\t\t\t\t\tawait this.runtime.applyEdgeTransform(edge, result, node, this.state.getContext(), this.allPredecessors)\n\t\t\t\t\t\t\tconst requiredPredecessors = this.allPredecessors.get(node.id)\n\t\t\t\t\t\t\tif (!requiredPredecessors) continue\n\t\t\t\t\t\t\tconst isReady =\n\t\t\t\t\t\t\t\tjoinStrategy === 'any'\n\t\t\t\t\t\t\t\t\t? [...requiredPredecessors].some((p) => completedThisTurn.has(p))\n\t\t\t\t\t\t\t\t\t: [...requiredPredecessors].every((p) => this.state.getCompletedNodes().has(p))\n\t\t\t\t\t\t\tif (isReady) this.frontier.add(node.id)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (matched.length === 0) {\n\t\t\t\t\t\t\tfor (const [potentialNextId, predecessors] of this.allPredecessors) {\n\t\t\t\t\t\t\t\tif (predecessors.has(nodeId) && !this.state.getCompletedNodes().has(potentialNextId)) {\n\t\t\t\t\t\t\t\t\tconst joinStrategy = this.getEffectiveJoinStrategy(potentialNextId)\n\t\t\t\t\t\t\t\t\tconst isReady =\n\t\t\t\t\t\t\t\t\t\tjoinStrategy === 'any'\n\t\t\t\t\t\t\t\t\t\t\t? [...predecessors].some((p) => completedThisTurn.has(p))\n\t\t\t\t\t\t\t\t\t\t\t: [...predecessors].every((p) => this.state.getCompletedNodes().has(p))\n\t\t\t\t\t\t\t\t\tif (isReady) this.frontier.add(potentialNextId)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\t\tthrow new CancelledWorkflowError('Workflow cancelled')\n\t\t\t\t}\n\t\t\t\tthrow error\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async executeWithConcurrency(\n\t\tnodeIds: string[],\n\t): Promise<\n\t\tArray<\n\t\t\t| { status: 'fulfilled'; value: { nodeId: string; result: NodeResult<any, any> } }\n\t\t\t| { status: 'rejected'; reason: { nodeId: string; error: unknown } }\n\t\t>\n\t> {\n\t\tconst maxConcurrency = this.concurrency || nodeIds.length\n\t\tconst results: Array<\n\t\t\t| { status: 'fulfilled'; value: { nodeId: string; result: NodeResult<any, any> } }\n\t\t\t| { status: 'rejected'; reason: { nodeId: string; error: unknown } }\n\t\t> = []\n\n\t\tfor (let i = 0; i < nodeIds.length; i += maxConcurrency) {\n\t\t\tconst batch = nodeIds.slice(i, i + maxConcurrency)\n\t\t\tconst batchPromises = batch.map(async (nodeId) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await this.runtime.executeNode(\n\t\t\t\t\t\tthis.dynamicBlueprint,\n\t\t\t\t\t\tnodeId,\n\t\t\t\t\t\tthis.state,\n\t\t\t\t\t\tthis.allPredecessors,\n\t\t\t\t\t\tthis.functionRegistry,\n\t\t\t\t\t\tthis.executionId,\n\t\t\t\t\t\tthis.signal,\n\t\t\t\t\t)\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tstatus: 'fulfilled' as const,\n\t\t\t\t\t\tvalue: { nodeId, result },\n\t\t\t\t\t})\n\t\t\t\t} catch (error) {\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tstatus: 'rejected' as const,\n\t\t\t\t\t\treason: { nodeId, error },\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tawait Promise.all(batchPromises)\n\t\t}\n\n\t\treturn results\n\t}\n\n\tprivate async handleDynamicNodes(nodeId: string, result: NodeResult<any, any>) {\n\t\tif (result.dynamicNodes && result.dynamicNodes.length > 0) {\n\t\t\tconst gatherNodeId = result.output?.gatherNodeId\n\t\t\tfor (const dynamicNode of result.dynamicNodes) {\n\t\t\t\tconst implementation = this.functionRegistry?.get(dynamicNode.uses) || this.runtime.registry[dynamicNode.uses]\n\t\t\t\tif (!implementation) {\n\t\t\t\t\tthrow new NodeExecutionError(\n\t\t\t\t\t\t`Implementation for '${dynamicNode.uses}' not found for dynamic node '${dynamicNode.id}' generated by node '${nodeId}'.`,\n\t\t\t\t\t\tdynamicNode.id,\n\t\t\t\t\t\tthis.dynamicBlueprint.id,\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\tthis.executionId,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tthis.dynamicBlueprint.nodes.push(dynamicNode)\n\t\t\t\tthis.allPredecessors.set(dynamicNode.id, new Set([nodeId]))\n\t\t\t\tif (gatherNodeId) {\n\t\t\t\t\tthis.allPredecessors.get(gatherNodeId)?.add(dynamicNode.id)\n\t\t\t\t}\n\t\t\t\tthis.frontier.add(dynamicNode.id)\n\t\t\t}\n\t\t}\n\t}\n\n\tgetAllNodeIds(): Set<string> {\n\t\treturn new Set(this.dynamicBlueprint.nodes.map((n) => n.id))\n\t}\n\n\tgetFallbackNodeIds(): Set<string> {\n\t\tconst fallbackNodeIds = new Set<string>()\n\t\tfor (const node of this.dynamicBlueprint.nodes) {\n\t\t\tif (node.config?.fallback) fallbackNodeIds.add(node.config.fallback)\n\t\t}\n\t\treturn fallbackNodeIds\n\t}\n\n\tgetDynamicBlueprint(): WorkflowBlueprint {\n\t\treturn this.dynamicBlueprint\n\t}\n}\n"]}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { CancelledWorkflowError, FatalNodeExecutionError } from './chunk-5ZXV3R5D.js';
|
|
2
|
+
|
|
3
|
+
// src/runtime/executors.ts
|
|
4
|
+
async function withRetries(executor, maxRetries, nodeDef, context, executionId, signal, eventBus) {
|
|
5
|
+
let lastError;
|
|
6
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
7
|
+
try {
|
|
8
|
+
signal?.throwIfAborted();
|
|
9
|
+
const result = await executor();
|
|
10
|
+
if (attempt > 1) {
|
|
11
|
+
context.dependencies.logger.info(`Node execution succeeded after retry`, {
|
|
12
|
+
nodeId: nodeDef.id,
|
|
13
|
+
attempt,
|
|
14
|
+
executionId
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
lastError = error;
|
|
20
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
21
|
+
throw new CancelledWorkflowError("Workflow cancelled");
|
|
22
|
+
}
|
|
23
|
+
if (error instanceof FatalNodeExecutionError) break;
|
|
24
|
+
if (attempt < maxRetries) {
|
|
25
|
+
context.dependencies.logger.warn(`Node execution failed, retrying`, {
|
|
26
|
+
nodeId: nodeDef.id,
|
|
27
|
+
attempt,
|
|
28
|
+
maxRetries,
|
|
29
|
+
error: error instanceof Error ? error.message : String(error),
|
|
30
|
+
executionId
|
|
31
|
+
});
|
|
32
|
+
if (eventBus) {
|
|
33
|
+
await eventBus.emit("node:retry", {
|
|
34
|
+
blueprintId: context.dependencies.blueprint?.id || "",
|
|
35
|
+
nodeId: nodeDef.id,
|
|
36
|
+
attempt,
|
|
37
|
+
executionId
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
context.dependencies.logger.error(`Node execution failed after all retries`, {
|
|
42
|
+
nodeId: nodeDef.id,
|
|
43
|
+
attempts: maxRetries,
|
|
44
|
+
error: error instanceof Error ? error.message : String(error),
|
|
45
|
+
executionId
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
throw lastError;
|
|
51
|
+
}
|
|
52
|
+
var FunctionNodeExecutor = class {
|
|
53
|
+
constructor(implementation, maxRetries, eventBus) {
|
|
54
|
+
this.implementation = implementation;
|
|
55
|
+
this.maxRetries = maxRetries;
|
|
56
|
+
this.eventBus = eventBus;
|
|
57
|
+
}
|
|
58
|
+
async execute(nodeDef, context, executionId, signal) {
|
|
59
|
+
return withRetries(
|
|
60
|
+
() => this.implementation(context),
|
|
61
|
+
this.maxRetries,
|
|
62
|
+
nodeDef,
|
|
63
|
+
context,
|
|
64
|
+
executionId,
|
|
65
|
+
signal,
|
|
66
|
+
this.eventBus
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var ClassNodeExecutor = class {
|
|
71
|
+
constructor(implementation, maxRetries, eventBus) {
|
|
72
|
+
this.implementation = implementation;
|
|
73
|
+
this.maxRetries = maxRetries;
|
|
74
|
+
this.eventBus = eventBus;
|
|
75
|
+
}
|
|
76
|
+
async execute(nodeDef, context, executionId, signal) {
|
|
77
|
+
const instance = new this.implementation(nodeDef.params || {});
|
|
78
|
+
let lastError;
|
|
79
|
+
try {
|
|
80
|
+
signal?.throwIfAborted();
|
|
81
|
+
const prepResult = await instance.prep(context);
|
|
82
|
+
let execResult;
|
|
83
|
+
try {
|
|
84
|
+
execResult = await withRetries(
|
|
85
|
+
() => instance.exec(prepResult, context),
|
|
86
|
+
this.maxRetries,
|
|
87
|
+
nodeDef,
|
|
88
|
+
context,
|
|
89
|
+
executionId,
|
|
90
|
+
signal,
|
|
91
|
+
this.eventBus
|
|
92
|
+
);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
95
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
96
|
+
throw new CancelledWorkflowError("Workflow cancelled");
|
|
97
|
+
}
|
|
98
|
+
if (error instanceof FatalNodeExecutionError) {
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (lastError) {
|
|
103
|
+
signal?.throwIfAborted();
|
|
104
|
+
execResult = await instance.fallback(lastError, context);
|
|
105
|
+
}
|
|
106
|
+
signal?.throwIfAborted();
|
|
107
|
+
if (!execResult) {
|
|
108
|
+
throw new Error("Execution failed after all retries");
|
|
109
|
+
}
|
|
110
|
+
return await instance.post(execResult, context);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
113
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
114
|
+
throw new CancelledWorkflowError("Workflow cancelled");
|
|
115
|
+
}
|
|
116
|
+
throw error;
|
|
117
|
+
} finally {
|
|
118
|
+
if (lastError) {
|
|
119
|
+
try {
|
|
120
|
+
await instance.recover(lastError, context);
|
|
121
|
+
} catch (recoverError) {
|
|
122
|
+
context.dependencies.logger.warn(`Recover phase failed`, {
|
|
123
|
+
nodeId: nodeDef.id,
|
|
124
|
+
originalError: lastError.message,
|
|
125
|
+
recoverError: recoverError instanceof Error ? recoverError.message : String(recoverError),
|
|
126
|
+
executionId
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var BuiltInNodeExecutor = class {
|
|
134
|
+
constructor(executeBuiltIn) {
|
|
135
|
+
this.executeBuiltIn = executeBuiltIn;
|
|
136
|
+
}
|
|
137
|
+
async execute(nodeDef, context) {
|
|
138
|
+
return this.executeBuiltIn(nodeDef, context.context);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export { BuiltInNodeExecutor, ClassNodeExecutor, FunctionNodeExecutor };
|
|
143
|
+
//# sourceMappingURL=chunk-M2FRTT2K.js.map
|
|
144
|
+
//# sourceMappingURL=chunk-M2FRTT2K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/executors.ts"],"names":[],"mappings":"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","file":"chunk-M2FRTT2K.js","sourcesContent":["import { CancelledWorkflowError, FatalNodeExecutionError } from '../errors'\nimport type {\n\tContextImplementation,\n\tIEventBus,\n\tNodeClass,\n\tNodeContext,\n\tNodeDefinition,\n\tNodeFunction,\n\tNodeResult,\n} from '../types'\n\nasync function withRetries<T>(\n\texecutor: () => Promise<T>,\n\tmaxRetries: number,\n\tnodeDef: NodeDefinition,\n\tcontext: NodeContext<any, any, any>,\n\texecutionId?: string,\n\tsignal?: AbortSignal,\n\teventBus?: IEventBus,\n): Promise<T> {\n\tlet lastError: any\n\tfor (let attempt = 1; attempt <= maxRetries; attempt++) {\n\t\ttry {\n\t\t\tsignal?.throwIfAborted()\n\t\t\tconst result = await executor()\n\t\t\tif (attempt > 1) {\n\t\t\t\tcontext.dependencies.logger.info(`Node execution succeeded after retry`, {\n\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\tattempt,\n\t\t\t\t\texecutionId,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\tlastError = error\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\tif (error instanceof FatalNodeExecutionError) break\n\t\t\tif (attempt < maxRetries) {\n\t\t\t\tcontext.dependencies.logger.warn(`Node execution failed, retrying`, {\n\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\tattempt,\n\t\t\t\t\tmaxRetries,\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\tif (eventBus) {\n\t\t\t\t\tawait eventBus.emit('node:retry', {\n\t\t\t\t\t\tblueprintId: context.dependencies.blueprint?.id || '',\n\t\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\t\tattempt,\n\t\t\t\t\t\texecutionId,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcontext.dependencies.logger.error(`Node execution failed after all retries`, {\n\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\tattempts: maxRetries,\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}\n\t\t}\n\t}\n\tthrow lastError\n}\n\nexport interface ExecutionStrategy {\n\texecute: (\n\t\tnodeDef: NodeDefinition,\n\t\tcontext: NodeContext<any, any, any>,\n\t\texecutionId?: string,\n\t\tsignal?: AbortSignal,\n\t) => Promise<NodeResult<any, any>>\n}\n\nexport class FunctionNodeExecutor implements ExecutionStrategy {\n\tconstructor(\n\t\tprivate implementation: NodeFunction,\n\t\tprivate maxRetries: number,\n\t\tprivate eventBus: IEventBus,\n\t) {}\n\n\tasync execute(\n\t\tnodeDef: NodeDefinition,\n\t\tcontext: NodeContext<any, any, any>,\n\t\texecutionId?: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<NodeResult<any, any>> {\n\t\treturn withRetries(\n\t\t\t() => this.implementation(context),\n\t\t\tthis.maxRetries,\n\t\t\tnodeDef,\n\t\t\tcontext,\n\t\t\texecutionId,\n\t\t\tsignal,\n\t\t\tthis.eventBus,\n\t\t)\n\t}\n}\n\nexport class ClassNodeExecutor implements ExecutionStrategy {\n\tconstructor(\n\t\tprivate implementation: NodeClass,\n\t\tprivate maxRetries: number,\n\t\tprivate eventBus: IEventBus,\n\t) {}\n\n\tasync execute(\n\t\tnodeDef: NodeDefinition,\n\t\tcontext: NodeContext<any, any, any>,\n\t\texecutionId?: string,\n\t\tsignal?: AbortSignal,\n\t): Promise<NodeResult<any, any>> {\n\t\tconst instance = new this.implementation(nodeDef.params || {})\n\t\tlet lastError: Error | undefined\n\t\ttry {\n\t\t\tsignal?.throwIfAborted()\n\t\t\tconst prepResult = await instance.prep(context)\n\t\t\tlet execResult: Omit<NodeResult, 'error'> | undefined\n\t\t\ttry {\n\t\t\t\texecResult = await withRetries(\n\t\t\t\t\t() => instance.exec(prepResult, context),\n\t\t\t\t\tthis.maxRetries,\n\t\t\t\t\tnodeDef,\n\t\t\t\t\tcontext,\n\t\t\t\t\texecutionId,\n\t\t\t\t\tsignal,\n\t\t\t\t\tthis.eventBus,\n\t\t\t\t)\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error instanceof Error ? error : new Error(String(error))\n\t\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\t\tthrow new CancelledWorkflowError('Workflow cancelled')\n\t\t\t\t}\n\t\t\t\tif (error instanceof FatalNodeExecutionError) {\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (lastError) {\n\t\t\t\tsignal?.throwIfAborted()\n\t\t\t\texecResult = await instance.fallback(lastError, context)\n\t\t\t}\n\t\t\tsignal?.throwIfAborted()\n\t\t\tif (!execResult) {\n\t\t\t\tthrow new Error('Execution failed after all retries')\n\t\t\t}\n\t\t\treturn await instance.post(execResult, context)\n\t\t} catch (error) {\n\t\t\tlastError = error instanceof Error ? error : new Error(String(error))\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\n\t\t} finally {\n\t\t\tif (lastError) {\n\t\t\t\ttry {\n\t\t\t\t\tawait instance.recover(lastError, context)\n\t\t\t\t} catch (recoverError) {\n\t\t\t\t\tcontext.dependencies.logger.warn(`Recover phase failed`, {\n\t\t\t\t\t\tnodeId: nodeDef.id,\n\t\t\t\t\t\toriginalError: lastError.message,\n\t\t\t\t\t\trecoverError: recoverError instanceof Error ? recoverError.message : String(recoverError),\n\t\t\t\t\t\texecutionId,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class BuiltInNodeExecutor implements ExecutionStrategy {\n\tconstructor(\n\t\tprivate executeBuiltIn: (\n\t\t\tnodeDef: NodeDefinition,\n\t\t\tcontext: ContextImplementation<any>,\n\t\t) => Promise<NodeResult<any, any>>,\n\t) {}\n\n\tasync execute(\n\t\tnodeDef: NodeDefinition,\n\t\tcontext: NodeContext<Record<string, unknown>, Record<string, unknown>, any>,\n\t): Promise<NodeResult<any, any>> {\n\t\treturn this.executeBuiltIn(nodeDef, context.context as ContextImplementation<any>)\n\t}\n}\n"]}
|