72flow-nodejs 1.0.2 → 1.0.3
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 +13 -13
- package/dist/index.cjs +90 -10
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +90 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# 72flow-nodejs
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/72flow-nodejs)
|
|
3
4
|
[](./LICENSE)
|
|
4
5
|
[](https://www.typescriptlang.org/)
|
|
5
6
|
[](https://nodejs.org/)
|
|
6
7
|
|
|
7
|
-
72flow
|
|
8
|
+
**72flow-nodejs** 是 72flow 工作流编排引擎的轻量级 JavaScript 实现。它旨在提供一个与 Java 后端行为完全一致的、可在浏览器和 Node.js 环境中无缝运行的流程执行核心。
|
|
8
9
|
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
##
|
|
12
|
+
## 核心特性
|
|
12
13
|
|
|
13
|
-
-
|
|
14
|
-
- ✅
|
|
15
|
-
- ✅
|
|
16
|
-
- ✅
|
|
17
|
-
- ✅
|
|
18
|
-
- ✅ **
|
|
19
|
-
- ✅
|
|
20
|
-
- ✅
|
|
21
|
-
- ✅ **ESM + CJS 双格式产物**:兼容各种打包场景
|
|
14
|
+
- 🚀 **极轻量**:无重度依赖,压缩后产物极小。
|
|
15
|
+
- ✅ **全能力节点**:支持 START、END、SCRIPT、DECISION、CONDITION、PARALLEL、LOOP、API、LLM 等 9 种核心节点。
|
|
16
|
+
- ✅ **双运行时**:天然支持现代浏览器与 Node.js 18+ 环境。
|
|
17
|
+
- ✅ **精准输出**:END 节点支持根据 `sourceCode` 路由特定节点的产物作为流程结果。
|
|
18
|
+
- ✅ **脚本捕获**:SCRIPT 节点支持捕获显式 `return` 的值。
|
|
19
|
+
- ✅ **X6 友好**:内置对 `@antv/x6` 导出的图形 JSON 的原生解析支持。
|
|
20
|
+
- ✅ **事件驱动**:完整的生命周期钩子(starting, completed, failed)。
|
|
21
|
+
- ✅ **零配置 LLM**:内置对 OpenAI 协议大模型的支持。
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -147,8 +147,8 @@ const result = await new FlowEngine().execute(flowDefinition, {});
|
|
|
147
147
|
| 节点类型 | 说明 | 关键配置字段 |
|
|
148
148
|
|-------------|--------------------------------------------------|-------------------------------------------------|
|
|
149
149
|
| `START` | 流程起始节点 | — |
|
|
150
|
-
| `END` |
|
|
151
|
-
| `SCRIPT` | 执行 JavaScript
|
|
150
|
+
| `END` | 流程结束周期。支持精准提取指定节点产物 | `config.outputResult.sourceCode` |
|
|
151
|
+
| `SCRIPT` | 执行 JavaScript 脚本。支持 `scriptResult` 返回值 | `config.script.scriptCode` |
|
|
152
152
|
| `DECISION` | 运行脚本计算返回值,与出边 `condition` 字段匹配 | `config.decision.scriptCode` |
|
|
153
153
|
| `CONDITION` | 对每条出边独立求值布尔表达式 | `config.condition.scriptCode` |
|
|
154
154
|
| `PARALLEL` | 并发触发所有出边分支,需配合 `convergeMap` 使用 | `convergeMap` (流程定义级) |
|
package/dist/index.cjs
CHANGED
|
@@ -75,6 +75,8 @@ var FlowContext = class {
|
|
|
75
75
|
nodeOutputs = {};
|
|
76
76
|
convergeStates = /* @__PURE__ */ new Map();
|
|
77
77
|
traces = [];
|
|
78
|
+
onStreamHandler;
|
|
79
|
+
streamingMode = false;
|
|
78
80
|
constructor(executionId, definition, variables) {
|
|
79
81
|
this.executionId = executionId;
|
|
80
82
|
this.definition = definition;
|
|
@@ -82,6 +84,12 @@ var FlowContext = class {
|
|
|
82
84
|
this.startTime = Date.now();
|
|
83
85
|
this.buildIndex();
|
|
84
86
|
}
|
|
87
|
+
setStreamingMode(enabled) {
|
|
88
|
+
this.streamingMode = enabled;
|
|
89
|
+
}
|
|
90
|
+
isStreamingEnabled() {
|
|
91
|
+
return this.streamingMode;
|
|
92
|
+
}
|
|
85
93
|
/** 构造时预建索引,所有后续查询均为 O(1) */
|
|
86
94
|
buildIndex() {
|
|
87
95
|
for (const node of this.definition.nodes) {
|
|
@@ -202,6 +210,7 @@ var FlowContext = class {
|
|
|
202
210
|
}
|
|
203
211
|
}
|
|
204
212
|
const durationMs = endTime - startTime;
|
|
213
|
+
const durationNs = durationMs * 1e6;
|
|
205
214
|
this.traces.push({
|
|
206
215
|
nodeId,
|
|
207
216
|
code: node?.code ?? nodeId,
|
|
@@ -209,10 +218,19 @@ var FlowContext = class {
|
|
|
209
218
|
startTime,
|
|
210
219
|
endTime,
|
|
211
220
|
duration: durationMs,
|
|
212
|
-
durationNs
|
|
221
|
+
durationNs,
|
|
213
222
|
// 毫秒 → 纳秒,与 Java 对齐
|
|
214
223
|
data: output
|
|
215
224
|
});
|
|
225
|
+
this.emitStream("__node_event__", {
|
|
226
|
+
type: "completed",
|
|
227
|
+
nodeId,
|
|
228
|
+
output,
|
|
229
|
+
startTime,
|
|
230
|
+
endTime,
|
|
231
|
+
duration: durationMs,
|
|
232
|
+
durationNs
|
|
233
|
+
});
|
|
216
234
|
}
|
|
217
235
|
fail(nodeId, error, startTime) {
|
|
218
236
|
const endTime = Date.now();
|
|
@@ -253,6 +271,16 @@ var FlowContext = class {
|
|
|
253
271
|
getLoopBodyPath(loopStartNodeId) {
|
|
254
272
|
return this.definition.loopBodyPaths?.[loopStartNodeId] ?? [];
|
|
255
273
|
}
|
|
274
|
+
/** 设置流式消息处理器(内部由 FlowEngine 调用以向外广播) */
|
|
275
|
+
setStreamHandler(handler) {
|
|
276
|
+
this.onStreamHandler = handler;
|
|
277
|
+
}
|
|
278
|
+
/** 发射流式消息块(由执行器调用) */
|
|
279
|
+
emitStream(nodeId, chunk) {
|
|
280
|
+
if (this.onStreamHandler) {
|
|
281
|
+
this.onStreamHandler(nodeId, chunk);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
256
284
|
};
|
|
257
285
|
|
|
258
286
|
// src/support/logger.ts
|
|
@@ -716,22 +744,59 @@ var LlmExecutor = class _LlmExecutor {
|
|
|
716
744
|
"Content-Type": "application/json",
|
|
717
745
|
"Authorization": `Bearer ${apiKey}`
|
|
718
746
|
},
|
|
719
|
-
body: JSON.stringify({
|
|
747
|
+
body: JSON.stringify({
|
|
748
|
+
model,
|
|
749
|
+
messages,
|
|
750
|
+
temperature,
|
|
751
|
+
max_tokens: maxTokens,
|
|
752
|
+
stream: true
|
|
753
|
+
// 强制开启流式以支持吐字效果
|
|
754
|
+
})
|
|
720
755
|
});
|
|
721
756
|
if (!response.ok) {
|
|
722
757
|
const err = await response.json().catch(() => ({ error: { message: response.statusText } }));
|
|
723
758
|
return { success: false, message: `LLM \u8C03\u7528\u5931\u8D25: ${err?.error?.message ?? response.status}` };
|
|
724
759
|
}
|
|
725
|
-
const
|
|
726
|
-
const
|
|
727
|
-
|
|
760
|
+
const reader = response.body?.getReader();
|
|
761
|
+
const decoder = new TextDecoder();
|
|
762
|
+
let fullContent = "";
|
|
763
|
+
let modelId = model;
|
|
764
|
+
if (reader) {
|
|
765
|
+
while (true) {
|
|
766
|
+
const { done, value } = await reader.read();
|
|
767
|
+
if (done) break;
|
|
768
|
+
const chunk = decoder.decode(value);
|
|
769
|
+
const lines = chunk.split("\n");
|
|
770
|
+
for (const line of lines) {
|
|
771
|
+
const trimmed = line.trim();
|
|
772
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
773
|
+
const dataStr = trimmed.slice(6);
|
|
774
|
+
if (dataStr === "[DONE]") break;
|
|
775
|
+
try {
|
|
776
|
+
const json = JSON.parse(dataStr);
|
|
777
|
+
const delta = json.choices?.[0]?.delta?.content ?? "";
|
|
778
|
+
if (delta) {
|
|
779
|
+
fullContent += delta;
|
|
780
|
+
context.emitStream(node.id, { delta, fullContent });
|
|
781
|
+
}
|
|
782
|
+
if (json.model) modelId = json.model;
|
|
783
|
+
} catch (e) {
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
const data = await response.json();
|
|
789
|
+
fullContent = data.choices?.[0]?.message?.content ?? "";
|
|
790
|
+
modelId = data.model ?? model;
|
|
791
|
+
}
|
|
728
792
|
return {
|
|
729
793
|
success: true,
|
|
730
794
|
data: {
|
|
731
|
-
llmResponse:
|
|
732
|
-
model:
|
|
733
|
-
inputTokens:
|
|
734
|
-
|
|
795
|
+
llmResponse: fullContent,
|
|
796
|
+
model: modelId,
|
|
797
|
+
inputTokens: 0,
|
|
798
|
+
// 流式协议通常不包含实时 usage,需后期计算或忽略
|
|
799
|
+
outputTokens: 0
|
|
735
800
|
}
|
|
736
801
|
};
|
|
737
802
|
} catch (e) {
|
|
@@ -799,10 +864,25 @@ var FlowEngine = class extends SimpleEmitter {
|
|
|
799
864
|
super();
|
|
800
865
|
}
|
|
801
866
|
// ─── 公开入口 ────────────────────────────────────────────
|
|
802
|
-
async execute(definition, variables = {}) {
|
|
867
|
+
async execute(definition, variables = {}, options = {}) {
|
|
803
868
|
const executionId = `exec-${Math.random().toString(36).slice(2, 10)}`;
|
|
804
869
|
const context = new FlowContext(executionId, definition, variables);
|
|
870
|
+
if (options.stream !== void 0) {
|
|
871
|
+
context.setStreamingMode(options.stream);
|
|
872
|
+
}
|
|
805
873
|
this.activeContexts.set(executionId, context);
|
|
874
|
+
context.setStreamHandler((nodeId, chunk) => {
|
|
875
|
+
if (nodeId === "__node_event__") {
|
|
876
|
+
const { type, ...payload } = chunk;
|
|
877
|
+
if (type === "completed") {
|
|
878
|
+
this.emit("node.completed", payload);
|
|
879
|
+
} else if (type === "failed") {
|
|
880
|
+
this.emit("node.failed", payload);
|
|
881
|
+
}
|
|
882
|
+
} else {
|
|
883
|
+
this.emit("node.stream", { executionId, nodeId, chunk });
|
|
884
|
+
}
|
|
885
|
+
});
|
|
806
886
|
return new Promise((resolve) => {
|
|
807
887
|
this.once(`flow.finished.${executionId}`, () => {
|
|
808
888
|
this.activeContexts.delete(executionId);
|
package/dist/index.d.cts
CHANGED
|
@@ -137,7 +137,11 @@ declare class FlowContext {
|
|
|
137
137
|
private nodeOutputs;
|
|
138
138
|
private convergeStates;
|
|
139
139
|
private traces;
|
|
140
|
+
private onStreamHandler?;
|
|
141
|
+
private streamingMode;
|
|
140
142
|
constructor(executionId: string, definition: FlowDefinition, variables: Record<string, any>);
|
|
143
|
+
setStreamingMode(enabled: boolean): void;
|
|
144
|
+
isStreamingEnabled(): boolean;
|
|
141
145
|
/** 构造时预建索引,所有后续查询均为 O(1) */
|
|
142
146
|
private buildIndex;
|
|
143
147
|
getExecutionId(): string;
|
|
@@ -175,6 +179,10 @@ declare class FlowContext {
|
|
|
175
179
|
tryConverge(nodeId: string, fromNodeId: string): boolean;
|
|
176
180
|
/** 获取 LOOP_START 节点的循环体路径(来自解析器预计算的 loopBodyPaths) */
|
|
177
181
|
getLoopBodyPath(loopStartNodeId: string): string[];
|
|
182
|
+
/** 设置流式消息处理器(内部由 FlowEngine 调用以向外广播) */
|
|
183
|
+
setStreamHandler(handler: (nodeId: string, chunk: any) => void): void;
|
|
184
|
+
/** 发射流式消息块(由执行器调用) */
|
|
185
|
+
emitStream(nodeId: string, chunk: any): void;
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
type EventHandler = (...args: any[]) => void;
|
|
@@ -188,7 +196,9 @@ declare class SimpleEmitter {
|
|
|
188
196
|
declare class FlowEngine extends SimpleEmitter {
|
|
189
197
|
private activeContexts;
|
|
190
198
|
constructor();
|
|
191
|
-
execute(definition: FlowDefinition, variables?: Record<string, any
|
|
199
|
+
execute(definition: FlowDefinition, variables?: Record<string, any>, options?: {
|
|
200
|
+
stream?: boolean;
|
|
201
|
+
}): Promise<FlowResult>;
|
|
192
202
|
private scheduleNode;
|
|
193
203
|
private runNode;
|
|
194
204
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -137,7 +137,11 @@ declare class FlowContext {
|
|
|
137
137
|
private nodeOutputs;
|
|
138
138
|
private convergeStates;
|
|
139
139
|
private traces;
|
|
140
|
+
private onStreamHandler?;
|
|
141
|
+
private streamingMode;
|
|
140
142
|
constructor(executionId: string, definition: FlowDefinition, variables: Record<string, any>);
|
|
143
|
+
setStreamingMode(enabled: boolean): void;
|
|
144
|
+
isStreamingEnabled(): boolean;
|
|
141
145
|
/** 构造时预建索引,所有后续查询均为 O(1) */
|
|
142
146
|
private buildIndex;
|
|
143
147
|
getExecutionId(): string;
|
|
@@ -175,6 +179,10 @@ declare class FlowContext {
|
|
|
175
179
|
tryConverge(nodeId: string, fromNodeId: string): boolean;
|
|
176
180
|
/** 获取 LOOP_START 节点的循环体路径(来自解析器预计算的 loopBodyPaths) */
|
|
177
181
|
getLoopBodyPath(loopStartNodeId: string): string[];
|
|
182
|
+
/** 设置流式消息处理器(内部由 FlowEngine 调用以向外广播) */
|
|
183
|
+
setStreamHandler(handler: (nodeId: string, chunk: any) => void): void;
|
|
184
|
+
/** 发射流式消息块(由执行器调用) */
|
|
185
|
+
emitStream(nodeId: string, chunk: any): void;
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
type EventHandler = (...args: any[]) => void;
|
|
@@ -188,7 +196,9 @@ declare class SimpleEmitter {
|
|
|
188
196
|
declare class FlowEngine extends SimpleEmitter {
|
|
189
197
|
private activeContexts;
|
|
190
198
|
constructor();
|
|
191
|
-
execute(definition: FlowDefinition, variables?: Record<string, any
|
|
199
|
+
execute(definition: FlowDefinition, variables?: Record<string, any>, options?: {
|
|
200
|
+
stream?: boolean;
|
|
201
|
+
}): Promise<FlowResult>;
|
|
192
202
|
private scheduleNode;
|
|
193
203
|
private runNode;
|
|
194
204
|
/**
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,8 @@ var FlowContext = class {
|
|
|
42
42
|
nodeOutputs = {};
|
|
43
43
|
convergeStates = /* @__PURE__ */ new Map();
|
|
44
44
|
traces = [];
|
|
45
|
+
onStreamHandler;
|
|
46
|
+
streamingMode = false;
|
|
45
47
|
constructor(executionId, definition, variables) {
|
|
46
48
|
this.executionId = executionId;
|
|
47
49
|
this.definition = definition;
|
|
@@ -49,6 +51,12 @@ var FlowContext = class {
|
|
|
49
51
|
this.startTime = Date.now();
|
|
50
52
|
this.buildIndex();
|
|
51
53
|
}
|
|
54
|
+
setStreamingMode(enabled) {
|
|
55
|
+
this.streamingMode = enabled;
|
|
56
|
+
}
|
|
57
|
+
isStreamingEnabled() {
|
|
58
|
+
return this.streamingMode;
|
|
59
|
+
}
|
|
52
60
|
/** 构造时预建索引,所有后续查询均为 O(1) */
|
|
53
61
|
buildIndex() {
|
|
54
62
|
for (const node of this.definition.nodes) {
|
|
@@ -169,6 +177,7 @@ var FlowContext = class {
|
|
|
169
177
|
}
|
|
170
178
|
}
|
|
171
179
|
const durationMs = endTime - startTime;
|
|
180
|
+
const durationNs = durationMs * 1e6;
|
|
172
181
|
this.traces.push({
|
|
173
182
|
nodeId,
|
|
174
183
|
code: node?.code ?? nodeId,
|
|
@@ -176,10 +185,19 @@ var FlowContext = class {
|
|
|
176
185
|
startTime,
|
|
177
186
|
endTime,
|
|
178
187
|
duration: durationMs,
|
|
179
|
-
durationNs
|
|
188
|
+
durationNs,
|
|
180
189
|
// 毫秒 → 纳秒,与 Java 对齐
|
|
181
190
|
data: output
|
|
182
191
|
});
|
|
192
|
+
this.emitStream("__node_event__", {
|
|
193
|
+
type: "completed",
|
|
194
|
+
nodeId,
|
|
195
|
+
output,
|
|
196
|
+
startTime,
|
|
197
|
+
endTime,
|
|
198
|
+
duration: durationMs,
|
|
199
|
+
durationNs
|
|
200
|
+
});
|
|
183
201
|
}
|
|
184
202
|
fail(nodeId, error, startTime) {
|
|
185
203
|
const endTime = Date.now();
|
|
@@ -220,6 +238,16 @@ var FlowContext = class {
|
|
|
220
238
|
getLoopBodyPath(loopStartNodeId) {
|
|
221
239
|
return this.definition.loopBodyPaths?.[loopStartNodeId] ?? [];
|
|
222
240
|
}
|
|
241
|
+
/** 设置流式消息处理器(内部由 FlowEngine 调用以向外广播) */
|
|
242
|
+
setStreamHandler(handler) {
|
|
243
|
+
this.onStreamHandler = handler;
|
|
244
|
+
}
|
|
245
|
+
/** 发射流式消息块(由执行器调用) */
|
|
246
|
+
emitStream(nodeId, chunk) {
|
|
247
|
+
if (this.onStreamHandler) {
|
|
248
|
+
this.onStreamHandler(nodeId, chunk);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
223
251
|
};
|
|
224
252
|
|
|
225
253
|
// src/support/logger.ts
|
|
@@ -683,22 +711,59 @@ var LlmExecutor = class _LlmExecutor {
|
|
|
683
711
|
"Content-Type": "application/json",
|
|
684
712
|
"Authorization": `Bearer ${apiKey}`
|
|
685
713
|
},
|
|
686
|
-
body: JSON.stringify({
|
|
714
|
+
body: JSON.stringify({
|
|
715
|
+
model,
|
|
716
|
+
messages,
|
|
717
|
+
temperature,
|
|
718
|
+
max_tokens: maxTokens,
|
|
719
|
+
stream: true
|
|
720
|
+
// 强制开启流式以支持吐字效果
|
|
721
|
+
})
|
|
687
722
|
});
|
|
688
723
|
if (!response.ok) {
|
|
689
724
|
const err = await response.json().catch(() => ({ error: { message: response.statusText } }));
|
|
690
725
|
return { success: false, message: `LLM \u8C03\u7528\u5931\u8D25: ${err?.error?.message ?? response.status}` };
|
|
691
726
|
}
|
|
692
|
-
const
|
|
693
|
-
const
|
|
694
|
-
|
|
727
|
+
const reader = response.body?.getReader();
|
|
728
|
+
const decoder = new TextDecoder();
|
|
729
|
+
let fullContent = "";
|
|
730
|
+
let modelId = model;
|
|
731
|
+
if (reader) {
|
|
732
|
+
while (true) {
|
|
733
|
+
const { done, value } = await reader.read();
|
|
734
|
+
if (done) break;
|
|
735
|
+
const chunk = decoder.decode(value);
|
|
736
|
+
const lines = chunk.split("\n");
|
|
737
|
+
for (const line of lines) {
|
|
738
|
+
const trimmed = line.trim();
|
|
739
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
740
|
+
const dataStr = trimmed.slice(6);
|
|
741
|
+
if (dataStr === "[DONE]") break;
|
|
742
|
+
try {
|
|
743
|
+
const json = JSON.parse(dataStr);
|
|
744
|
+
const delta = json.choices?.[0]?.delta?.content ?? "";
|
|
745
|
+
if (delta) {
|
|
746
|
+
fullContent += delta;
|
|
747
|
+
context.emitStream(node.id, { delta, fullContent });
|
|
748
|
+
}
|
|
749
|
+
if (json.model) modelId = json.model;
|
|
750
|
+
} catch (e) {
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
} else {
|
|
755
|
+
const data = await response.json();
|
|
756
|
+
fullContent = data.choices?.[0]?.message?.content ?? "";
|
|
757
|
+
modelId = data.model ?? model;
|
|
758
|
+
}
|
|
695
759
|
return {
|
|
696
760
|
success: true,
|
|
697
761
|
data: {
|
|
698
|
-
llmResponse:
|
|
699
|
-
model:
|
|
700
|
-
inputTokens:
|
|
701
|
-
|
|
762
|
+
llmResponse: fullContent,
|
|
763
|
+
model: modelId,
|
|
764
|
+
inputTokens: 0,
|
|
765
|
+
// 流式协议通常不包含实时 usage,需后期计算或忽略
|
|
766
|
+
outputTokens: 0
|
|
702
767
|
}
|
|
703
768
|
};
|
|
704
769
|
} catch (e) {
|
|
@@ -766,10 +831,25 @@ var FlowEngine = class extends SimpleEmitter {
|
|
|
766
831
|
super();
|
|
767
832
|
}
|
|
768
833
|
// ─── 公开入口 ────────────────────────────────────────────
|
|
769
|
-
async execute(definition, variables = {}) {
|
|
834
|
+
async execute(definition, variables = {}, options = {}) {
|
|
770
835
|
const executionId = `exec-${Math.random().toString(36).slice(2, 10)}`;
|
|
771
836
|
const context = new FlowContext(executionId, definition, variables);
|
|
837
|
+
if (options.stream !== void 0) {
|
|
838
|
+
context.setStreamingMode(options.stream);
|
|
839
|
+
}
|
|
772
840
|
this.activeContexts.set(executionId, context);
|
|
841
|
+
context.setStreamHandler((nodeId, chunk) => {
|
|
842
|
+
if (nodeId === "__node_event__") {
|
|
843
|
+
const { type, ...payload } = chunk;
|
|
844
|
+
if (type === "completed") {
|
|
845
|
+
this.emit("node.completed", payload);
|
|
846
|
+
} else if (type === "failed") {
|
|
847
|
+
this.emit("node.failed", payload);
|
|
848
|
+
}
|
|
849
|
+
} else {
|
|
850
|
+
this.emit("node.stream", { executionId, nodeId, chunk });
|
|
851
|
+
}
|
|
852
|
+
});
|
|
773
853
|
return new Promise((resolve) => {
|
|
774
854
|
this.once(`flow.finished.${executionId}`, () => {
|
|
775
855
|
this.activeContexts.delete(executionId);
|