flowneer 0.7.1 → 0.8.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/dist/{FlowBuilder-DJkzGH5l.d.ts → FlowBuilder-B0SMJ4um.d.ts} +26 -3
- package/dist/errors-u-hq7p5N.d.ts +16 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +35 -5
- package/dist/{patterns-CCtG27Gv.d.ts → patterns-1gFxWo6a.d.ts} +1 -1
- package/dist/plugins/agent/index.d.ts +2 -2
- package/dist/plugins/agent/index.js +35 -5
- package/dist/plugins/dev/index.d.ts +87 -2
- package/dist/plugins/dev/index.js +131 -0
- package/dist/plugins/eval/index.d.ts +1 -1
- package/dist/plugins/graph/index.d.ts +1 -1
- package/dist/plugins/index.d.ts +298 -3
- package/dist/plugins/index.js +701 -35
- package/dist/plugins/llm/index.d.ts +18 -2
- package/dist/plugins/llm/index.js +16 -11
- package/dist/plugins/memory/index.d.ts +1 -1
- package/dist/plugins/messaging/index.d.ts +6 -1
- package/dist/plugins/observability/index.d.ts +1 -1
- package/dist/plugins/output/index.d.ts +1 -1
- package/dist/plugins/persistence/index.d.ts +1 -1
- package/dist/plugins/resilience/index.d.ts +1 -1
- package/dist/plugins/telemetry/index.d.ts +1 -1
- package/dist/plugins/tools/index.d.ts +1 -1
- package/dist/src/index.d.ts +4 -18
- package/dist/src/index.js +35 -5
- package/package.json +1 -1
|
@@ -65,6 +65,29 @@ interface StepMeta {
|
|
|
65
65
|
type: "fn" | "branch" | "loop" | "batch" | "parallel" | "anchor";
|
|
66
66
|
label?: string;
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Narrows which steps a hook fires for.
|
|
70
|
+
*
|
|
71
|
+
* - **String array** — only steps whose `label` matches are affected.
|
|
72
|
+
* Entries are exact matches unless they contain `*`, which acts as a
|
|
73
|
+
* wildcard matching any substring (glob-style).
|
|
74
|
+
* e.g. `["llm:*"]` matches `"llm:summarise"`, `"llm:embed"`, etc.
|
|
75
|
+
* - **Predicate** — full control; return `true` to match.
|
|
76
|
+
*
|
|
77
|
+
* Unmatched `wrapStep`/`wrapParallelFn` hooks still call `next()` automatically
|
|
78
|
+
* so the middleware chain is never broken.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // Exact labels
|
|
82
|
+
* flow.withRateLimit({ intervalMs: 1000 }, ["callLlm", "callEmbeddings"]);
|
|
83
|
+
*
|
|
84
|
+
* // Wildcard — any step whose label starts with "llm:"
|
|
85
|
+
* flow.withRateLimit({ intervalMs: 1000 }, ["llm:*"]);
|
|
86
|
+
*
|
|
87
|
+
* // Custom predicate
|
|
88
|
+
* flow.addHooks({ beforeStep: log }, (meta) => meta.type === "fn");
|
|
89
|
+
*/
|
|
90
|
+
type StepFilter = string[] | ((meta: StepMeta) => boolean);
|
|
68
91
|
/** Lifecycle hooks that plugins can register. */
|
|
69
92
|
interface FlowHooks<S = any, P extends Record<string, unknown> = Record<string, unknown>> {
|
|
70
93
|
/** Fires once before the first step runs. */
|
|
@@ -230,7 +253,7 @@ declare class CoreFlowBuilder<S = any, P extends Record<string, unknown> = Recor
|
|
|
230
253
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
231
254
|
* Returns a dispose function that removes these hooks when called.
|
|
232
255
|
*/
|
|
233
|
-
protected _setHooks(hooks: Partial<FlowHooks<S, P
|
|
256
|
+
protected _setHooks(hooks: Partial<FlowHooks<S, P>>, filter?: StepFilter): () => void;
|
|
234
257
|
/**
|
|
235
258
|
* Register a plugin — copies its methods onto `FlowBuilder.prototype`.
|
|
236
259
|
*/
|
|
@@ -250,7 +273,7 @@ declare class CoreFlowBuilder<S = any, P extends Record<string, unknown> = Recor
|
|
|
250
273
|
* Register lifecycle hooks directly on this instance.
|
|
251
274
|
* Returns a dispose function that removes these hooks when called.
|
|
252
275
|
*/
|
|
253
|
-
addHooks(hooks: Partial<FlowHooks<S, P
|
|
276
|
+
addHooks(hooks: Partial<FlowHooks<S, P>>, filter?: StepFilter): () => void;
|
|
254
277
|
/** Execute the flow. */
|
|
255
278
|
run(shared: S, params?: P, options?: RunOptions): Promise<void>;
|
|
256
279
|
/**
|
|
@@ -364,4 +387,4 @@ declare class FlowBuilder<S = any, P extends Record<string, unknown> = Record<st
|
|
|
364
387
|
private _addFn;
|
|
365
388
|
}
|
|
366
389
|
|
|
367
|
-
export { type AnchorStep as A, type BatchStep as B, CoreFlowBuilder as C, FlowBuilder as F, type InstancePlugin as I, type LoopStep as L, type NodeFn as N, type ParallelStep as P, type ResolvedHooks as R, type
|
|
390
|
+
export { type AnchorStep as A, type BatchStep as B, CoreFlowBuilder as C, FlowBuilder as F, type InstancePlugin as I, type LoopStep as L, type NodeFn as N, type ParallelStep as P, type ResolvedHooks as R, type StepFilter as S, type Validator as V, type FlowneerPlugin as a, type NodeOptions as b, type FlowHooks as c, type StepMeta as d, type BranchStep as e, type FnStep as f, type NumberOrFn as g, type RunOptions as h, type Step as i, type StepContext as j, type StepHandler as k, type StreamEvent as l };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Wraps step failures with context about which step failed. */
|
|
2
|
+
declare class FlowError extends Error {
|
|
3
|
+
readonly step: string;
|
|
4
|
+
readonly cause: unknown;
|
|
5
|
+
constructor(step: string, cause: unknown);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Thrown by `interruptIf` to pause a flow.
|
|
9
|
+
* Catch this in your runner to save `savedShared` and resume later.
|
|
10
|
+
*/
|
|
11
|
+
declare class InterruptError extends Error {
|
|
12
|
+
readonly savedShared: unknown;
|
|
13
|
+
constructor(shared: unknown);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { FlowError as F, InterruptError as I };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { F as FlowBuilder, c as FlowHooks, a as FlowneerPlugin, N as NodeFn, b as NodeOptions,
|
|
2
|
-
export {
|
|
1
|
+
export { F as FlowBuilder, c as FlowHooks, a as FlowneerPlugin, N as NodeFn, b as NodeOptions, g as NumberOrFn, h as RunOptions, d as StepMeta, l as StreamEvent, V as Validator } from './FlowBuilder-B0SMJ4um.js';
|
|
2
|
+
export { Fragment, fragment } from './src/index.js';
|
|
3
|
+
export { F as FlowError, I as InterruptError } from './errors-u-hq7p5N.js';
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,35 @@ function buildAnchorMap(steps) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// src/core/CoreFlowBuilder.ts
|
|
77
|
+
function matchesFilter(filter, meta) {
|
|
78
|
+
if (!Array.isArray(filter)) return filter(meta);
|
|
79
|
+
if (meta.label === void 0) return false;
|
|
80
|
+
const label = meta.label;
|
|
81
|
+
return filter.some((pattern) => {
|
|
82
|
+
if (!pattern.includes("*")) return pattern === label;
|
|
83
|
+
const re = new RegExp(
|
|
84
|
+
"^" + pattern.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
|
|
85
|
+
);
|
|
86
|
+
return re.test(label);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function applyStepFilter(hooks, filter) {
|
|
90
|
+
const match = (meta) => matchesFilter(filter, meta);
|
|
91
|
+
const wrap = (key, fallback = () => {
|
|
92
|
+
}) => {
|
|
93
|
+
const orig = hooks[key];
|
|
94
|
+
if (!orig) return;
|
|
95
|
+
hooks[key] = ((...args) => match(args[0]) ? orig(...args) : fallback(
|
|
96
|
+
...args
|
|
97
|
+
));
|
|
98
|
+
};
|
|
99
|
+
wrap("beforeStep");
|
|
100
|
+
wrap("afterStep");
|
|
101
|
+
wrap("onError");
|
|
102
|
+
wrap("wrapStep", (_m, next) => next());
|
|
103
|
+
wrap("wrapParallelFn", (_m, _fi, next) => next());
|
|
104
|
+
return hooks;
|
|
105
|
+
}
|
|
77
106
|
function buildHookCache(list) {
|
|
78
107
|
const pick = (key) => list.map((h) => h[key]).filter(Boolean);
|
|
79
108
|
return {
|
|
@@ -120,11 +149,12 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
120
149
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
121
150
|
* Returns a dispose function that removes these hooks when called.
|
|
122
151
|
*/
|
|
123
|
-
_setHooks(hooks) {
|
|
124
|
-
|
|
152
|
+
_setHooks(hooks, filter) {
|
|
153
|
+
const resolved = filter ? applyStepFilter(hooks, filter) : hooks;
|
|
154
|
+
this._hooksList.push(resolved);
|
|
125
155
|
this._hooksCache = null;
|
|
126
156
|
return () => {
|
|
127
|
-
const idx = this._hooksList.indexOf(
|
|
157
|
+
const idx = this._hooksList.indexOf(resolved);
|
|
128
158
|
if (idx !== -1) this._hooksList.splice(idx, 1);
|
|
129
159
|
this._hooksCache = null;
|
|
130
160
|
};
|
|
@@ -156,8 +186,8 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
156
186
|
* Register lifecycle hooks directly on this instance.
|
|
157
187
|
* Returns a dispose function that removes these hooks when called.
|
|
158
188
|
*/
|
|
159
|
-
addHooks(hooks) {
|
|
160
|
-
return this._setHooks(hooks);
|
|
189
|
+
addHooks(hooks, filter) {
|
|
190
|
+
return this._setHooks(hooks, filter);
|
|
161
191
|
}
|
|
162
192
|
// -------------------------------------------------------------------------
|
|
163
193
|
// Execution API
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { H as HumanNodeOptions, R as ReActLoopOptions, T as ThinkResult, h as hierarchicalCrew, r as resumeFlow, a as roundRobinDebate, s as sequentialCrew, b as supervisorCrew, w as withHumanNode, c as withReActLoop } from '../../patterns-
|
|
2
|
-
import { F as FlowBuilder } from '../../FlowBuilder-
|
|
1
|
+
export { H as HumanNodeOptions, R as ReActLoopOptions, T as ThinkResult, h as hierarchicalCrew, r as resumeFlow, a as roundRobinDebate, s as sequentialCrew, b as supervisorCrew, w as withHumanNode, c as withReActLoop } from '../../patterns-1gFxWo6a.js';
|
|
2
|
+
import { F as FlowBuilder } from '../../FlowBuilder-B0SMJ4um.js';
|
|
3
3
|
import { ToolRegistry, ToolResult, Tool, ToolCall, ToolParam } from '../tools/index.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -157,6 +157,35 @@ function buildAnchorMap(steps) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// src/core/CoreFlowBuilder.ts
|
|
160
|
+
function matchesFilter(filter, meta) {
|
|
161
|
+
if (!Array.isArray(filter)) return filter(meta);
|
|
162
|
+
if (meta.label === void 0) return false;
|
|
163
|
+
const label = meta.label;
|
|
164
|
+
return filter.some((pattern) => {
|
|
165
|
+
if (!pattern.includes("*")) return pattern === label;
|
|
166
|
+
const re = new RegExp(
|
|
167
|
+
"^" + pattern.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
|
|
168
|
+
);
|
|
169
|
+
return re.test(label);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function applyStepFilter(hooks, filter) {
|
|
173
|
+
const match = (meta) => matchesFilter(filter, meta);
|
|
174
|
+
const wrap = (key, fallback = () => {
|
|
175
|
+
}) => {
|
|
176
|
+
const orig = hooks[key];
|
|
177
|
+
if (!orig) return;
|
|
178
|
+
hooks[key] = ((...args) => match(args[0]) ? orig(...args) : fallback(
|
|
179
|
+
...args
|
|
180
|
+
));
|
|
181
|
+
};
|
|
182
|
+
wrap("beforeStep");
|
|
183
|
+
wrap("afterStep");
|
|
184
|
+
wrap("onError");
|
|
185
|
+
wrap("wrapStep", (_m, next) => next());
|
|
186
|
+
wrap("wrapParallelFn", (_m, _fi, next) => next());
|
|
187
|
+
return hooks;
|
|
188
|
+
}
|
|
160
189
|
function buildHookCache(list) {
|
|
161
190
|
const pick = (key) => list.map((h) => h[key]).filter(Boolean);
|
|
162
191
|
return {
|
|
@@ -203,11 +232,12 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
203
232
|
* Register lifecycle hooks (called by plugin methods, not by consumers).
|
|
204
233
|
* Returns a dispose function that removes these hooks when called.
|
|
205
234
|
*/
|
|
206
|
-
_setHooks(hooks) {
|
|
207
|
-
|
|
235
|
+
_setHooks(hooks, filter) {
|
|
236
|
+
const resolved = filter ? applyStepFilter(hooks, filter) : hooks;
|
|
237
|
+
this._hooksList.push(resolved);
|
|
208
238
|
this._hooksCache = null;
|
|
209
239
|
return () => {
|
|
210
|
-
const idx = this._hooksList.indexOf(
|
|
240
|
+
const idx = this._hooksList.indexOf(resolved);
|
|
211
241
|
if (idx !== -1) this._hooksList.splice(idx, 1);
|
|
212
242
|
this._hooksCache = null;
|
|
213
243
|
};
|
|
@@ -239,8 +269,8 @@ var CoreFlowBuilder = class _CoreFlowBuilder {
|
|
|
239
269
|
* Register lifecycle hooks directly on this instance.
|
|
240
270
|
* Returns a dispose function that removes these hooks when called.
|
|
241
271
|
*/
|
|
242
|
-
addHooks(hooks) {
|
|
243
|
-
return this._setHooks(hooks);
|
|
272
|
+
addHooks(hooks, filter) {
|
|
273
|
+
return this._setHooks(hooks, filter);
|
|
244
274
|
}
|
|
245
275
|
// -------------------------------------------------------------------------
|
|
246
276
|
// Execution API
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-
|
|
1
|
+
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-B0SMJ4um.js';
|
|
2
2
|
|
|
3
3
|
declare module "../../Flowneer" {
|
|
4
4
|
interface FlowBuilder<S, P> {
|
|
@@ -45,4 +45,89 @@ declare module "../../Flowneer" {
|
|
|
45
45
|
*/
|
|
46
46
|
declare const withAtomicUpdates: FlowneerPlugin;
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* A single node in the static path tree.
|
|
50
|
+
*/
|
|
51
|
+
interface PathNode {
|
|
52
|
+
/** Stable id: `"fn_0"`, `"branch_2"`, `"anchor:refine"`, etc. */
|
|
53
|
+
id: string;
|
|
54
|
+
type: "fn" | "branch" | "loop" | "batch" | "parallel" | "anchor";
|
|
55
|
+
label?: string;
|
|
56
|
+
/** Branch arms — keys are the branch names, values are the arm subtrees. */
|
|
57
|
+
branches?: Record<string, PathNode[]>;
|
|
58
|
+
/** Inline body steps (loop / batch). */
|
|
59
|
+
body?: PathNode[];
|
|
60
|
+
/** One lane per parallel fn. */
|
|
61
|
+
parallel?: PathNode[][];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The result of `.analyzeFlow()`.
|
|
65
|
+
*/
|
|
66
|
+
interface PathMap {
|
|
67
|
+
nodes: PathNode[];
|
|
68
|
+
/** All anchor names declared in this flow (and nested sub-flows). */
|
|
69
|
+
anchors: string[];
|
|
70
|
+
/**
|
|
71
|
+
* True if any `fn` step exists — those can dynamically return goto targets
|
|
72
|
+
* (`"#anchorName"`) that cannot be resolved without running the flow.
|
|
73
|
+
* Static analysis is therefore necessarily conservative for those edges.
|
|
74
|
+
*/
|
|
75
|
+
hasDynamicGotos: boolean;
|
|
76
|
+
}
|
|
77
|
+
interface TraceEvent {
|
|
78
|
+
stepIndex: number;
|
|
79
|
+
type: string;
|
|
80
|
+
label?: string;
|
|
81
|
+
durationMs: number;
|
|
82
|
+
}
|
|
83
|
+
interface TraceReport {
|
|
84
|
+
events: TraceEvent[];
|
|
85
|
+
totalDurationMs: number;
|
|
86
|
+
/** Human-readable ordered list of visited step labels (unlabelled steps are skipped). */
|
|
87
|
+
pathSummary: string[];
|
|
88
|
+
}
|
|
89
|
+
interface TraceHandle {
|
|
90
|
+
/** Returns the trace collected so far. Safe to call mid-run. */
|
|
91
|
+
getTrace(): TraceReport;
|
|
92
|
+
/** Removes the installed hooks. */
|
|
93
|
+
dispose(): void;
|
|
94
|
+
}
|
|
95
|
+
declare module "../../Flowneer" {
|
|
96
|
+
interface FlowBuilder<S, P> {
|
|
97
|
+
/**
|
|
98
|
+
* Statically analyze this flow and return a `PathMap` describing all
|
|
99
|
+
* possible nodes and anchors without executing anything.
|
|
100
|
+
*
|
|
101
|
+
* `hasDynamicGotos` is true whenever `fn` steps are present — those may
|
|
102
|
+
* return goto targets at runtime that cannot be resolved statically.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* const map = flow.analyzeFlow();
|
|
106
|
+
* console.log("anchors:", map.anchors);
|
|
107
|
+
* console.log("has dynamic gotos:", map.hasDynamicGotos);
|
|
108
|
+
*/
|
|
109
|
+
analyzeFlow(): PathMap;
|
|
110
|
+
/**
|
|
111
|
+
* Install execution-trace hooks on this flow.
|
|
112
|
+
*
|
|
113
|
+
* Records every step's index, type, label, and wall-clock duration.
|
|
114
|
+
* Call `getTrace()` after (or during) the run to inspect results.
|
|
115
|
+
* Call `dispose()` to remove the hooks when no longer needed.
|
|
116
|
+
*
|
|
117
|
+
* Composable with `withDryRun` — trace structure without side effects:
|
|
118
|
+
* ```ts
|
|
119
|
+
* flow.withDryRun().withTrace()
|
|
120
|
+
* ```
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const trace = flow.withTrace();
|
|
124
|
+
* await flow.run(shared);
|
|
125
|
+
* console.log(trace.getTrace().pathSummary);
|
|
126
|
+
* trace.dispose();
|
|
127
|
+
*/
|
|
128
|
+
withTrace(): TraceHandle;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
declare const withFlowAnalyzer: FlowneerPlugin;
|
|
132
|
+
|
|
133
|
+
export { type PathMap, type PathNode, type TraceEvent, type TraceHandle, type TraceReport, withAtomicUpdates, withDryRun, withFlowAnalyzer, withMocks, withStepLimit };
|
|
@@ -49,9 +49,140 @@ var withAtomicUpdates = {
|
|
|
49
49
|
return this.parallel(fns, options, reducer);
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
|
+
|
|
53
|
+
// plugins/dev/withFlowAnalyzer.ts
|
|
54
|
+
function walkSteps(steps, prefix = "") {
|
|
55
|
+
const nodes = [];
|
|
56
|
+
const anchors = [];
|
|
57
|
+
let hasDynamicGotos = false;
|
|
58
|
+
for (let i = 0; i < steps.length; i++) {
|
|
59
|
+
const step = steps[i];
|
|
60
|
+
const rawId = step.type === "anchor" ? `anchor:${step.name}` : `${step.type}_${i}`;
|
|
61
|
+
const id = prefix ? `${prefix}:${rawId}` : rawId;
|
|
62
|
+
switch (step.type) {
|
|
63
|
+
case "fn": {
|
|
64
|
+
hasDynamicGotos = true;
|
|
65
|
+
nodes.push({
|
|
66
|
+
id,
|
|
67
|
+
type: "fn",
|
|
68
|
+
label: step.label ?? step.fn?.name
|
|
69
|
+
});
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "anchor": {
|
|
73
|
+
anchors.push(step.name);
|
|
74
|
+
nodes.push({ id, type: "anchor", label: step.name });
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "branch": {
|
|
78
|
+
const branchMap = {};
|
|
79
|
+
for (const [key, fn] of Object.entries(step.branches ?? {})) {
|
|
80
|
+
branchMap[key] = [
|
|
81
|
+
{
|
|
82
|
+
id: `${id}:arm:${key}`,
|
|
83
|
+
type: "fn",
|
|
84
|
+
label: fn?.name ?? key
|
|
85
|
+
}
|
|
86
|
+
];
|
|
87
|
+
hasDynamicGotos = true;
|
|
88
|
+
}
|
|
89
|
+
nodes.push({
|
|
90
|
+
id,
|
|
91
|
+
type: "branch",
|
|
92
|
+
label: step.label ?? step.router?.name,
|
|
93
|
+
branches: branchMap
|
|
94
|
+
});
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case "loop": {
|
|
98
|
+
const bodySteps = step.body?.steps ?? [];
|
|
99
|
+
const inner = walkSteps(bodySteps, `${id}:body`);
|
|
100
|
+
anchors.push(...inner.anchors);
|
|
101
|
+
hasDynamicGotos = hasDynamicGotos || inner.hasDynamicGotos;
|
|
102
|
+
nodes.push({
|
|
103
|
+
id,
|
|
104
|
+
type: "loop",
|
|
105
|
+
label: step.label,
|
|
106
|
+
body: inner.nodes
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case "batch": {
|
|
111
|
+
const procSteps = step.processor?.steps ?? [];
|
|
112
|
+
const inner = walkSteps(procSteps, `${id}:each`);
|
|
113
|
+
anchors.push(...inner.anchors);
|
|
114
|
+
hasDynamicGotos = hasDynamicGotos || inner.hasDynamicGotos;
|
|
115
|
+
nodes.push({
|
|
116
|
+
id,
|
|
117
|
+
type: "batch",
|
|
118
|
+
label: step.label,
|
|
119
|
+
body: inner.nodes
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "parallel": {
|
|
124
|
+
const fns = step.fns ?? [];
|
|
125
|
+
const lanes = fns.map((fn, fi) => [
|
|
126
|
+
{
|
|
127
|
+
id: `${id}:fn_${fi}`,
|
|
128
|
+
type: "fn",
|
|
129
|
+
label: fn?.name ?? `fn_${fi}`
|
|
130
|
+
}
|
|
131
|
+
]);
|
|
132
|
+
hasDynamicGotos = hasDynamicGotos || fns.length > 0;
|
|
133
|
+
nodes.push({
|
|
134
|
+
id,
|
|
135
|
+
type: "parallel",
|
|
136
|
+
label: step.label,
|
|
137
|
+
parallel: lanes
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
default:
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { nodes, anchors, hasDynamicGotos };
|
|
146
|
+
}
|
|
147
|
+
var withFlowAnalyzer = {
|
|
148
|
+
analyzeFlow() {
|
|
149
|
+
const steps = this.steps ?? [];
|
|
150
|
+
const { nodes, anchors, hasDynamicGotos } = walkSteps(steps);
|
|
151
|
+
return { nodes, anchors, hasDynamicGotos };
|
|
152
|
+
},
|
|
153
|
+
withTrace() {
|
|
154
|
+
const events = [];
|
|
155
|
+
const starts = /* @__PURE__ */ new Map();
|
|
156
|
+
const dispose = this.addHooks({
|
|
157
|
+
beforeStep: (meta) => {
|
|
158
|
+
starts.set(meta.index, Date.now());
|
|
159
|
+
},
|
|
160
|
+
afterStep: (meta) => {
|
|
161
|
+
const start = starts.get(meta.index) ?? Date.now();
|
|
162
|
+
const durationMs = Date.now() - start;
|
|
163
|
+
starts.delete(meta.index);
|
|
164
|
+
events.push({
|
|
165
|
+
stepIndex: meta.index,
|
|
166
|
+
type: meta.type,
|
|
167
|
+
label: meta.label,
|
|
168
|
+
durationMs
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
getTrace() {
|
|
174
|
+
const totalDurationMs = events.reduce((s, e) => s + e.durationMs, 0);
|
|
175
|
+
const pathSummary = events.filter((e) => e.label !== void 0).map((e) => e.label);
|
|
176
|
+
return { events: [...events], totalDurationMs, pathSummary };
|
|
177
|
+
},
|
|
178
|
+
dispose
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
};
|
|
52
182
|
export {
|
|
53
183
|
withAtomicUpdates,
|
|
54
184
|
withDryRun,
|
|
185
|
+
withFlowAnalyzer,
|
|
55
186
|
withMocks,
|
|
56
187
|
withStepLimit
|
|
57
188
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-
|
|
1
|
+
import { a as FlowneerPlugin, N as NodeFn, b as NodeOptions } from '../../FlowBuilder-B0SMJ4um.js';
|
|
2
2
|
|
|
3
3
|
/** A single node entry in the exported graph. */
|
|
4
4
|
interface GraphNodeExport {
|