@blokjs/runner 0.4.0 → 0.6.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/Blok.js +32 -3
- package/dist/Blok.js.map +1 -1
- package/dist/Configuration.d.ts +41 -5
- package/dist/Configuration.js +215 -92
- package/dist/Configuration.js.map +1 -1
- package/dist/ForEachNode.d.ts +59 -0
- package/dist/ForEachNode.js +522 -0
- package/dist/ForEachNode.js.map +1 -0
- package/dist/LoopMaxIterationsError.d.ts +11 -0
- package/dist/LoopMaxIterationsError.js +18 -0
- package/dist/LoopMaxIterationsError.js.map +1 -0
- package/dist/LoopNode.d.ts +36 -0
- package/dist/LoopNode.js +182 -0
- package/dist/LoopNode.js.map +1 -0
- package/dist/Runner.d.ts +11 -1
- package/dist/Runner.js +9 -2
- package/dist/Runner.js.map +1 -1
- package/dist/RunnerSteps.js +419 -112
- package/dist/RunnerSteps.js.map +1 -1
- package/dist/RuntimeAdapterNode.d.ts +2 -1
- package/dist/RuntimeAdapterNode.js +2 -2
- package/dist/RuntimeAdapterNode.js.map +1 -1
- package/dist/RuntimeRegistry.d.ts +23 -2
- package/dist/RuntimeRegistry.js +31 -2
- package/dist/RuntimeRegistry.js.map +1 -1
- package/dist/SubworkflowNode.d.ts +106 -0
- package/dist/SubworkflowNode.js +261 -3
- package/dist/SubworkflowNode.js.map +1 -1
- package/dist/SwitchNode.d.ts +37 -0
- package/dist/SwitchNode.js +153 -0
- package/dist/SwitchNode.js.map +1 -0
- package/dist/TriggerBase.d.ts +50 -0
- package/dist/TriggerBase.js +262 -4
- package/dist/TriggerBase.js.map +1 -1
- package/dist/TryCatchNode.d.ts +32 -0
- package/dist/TryCatchNode.js +207 -0
- package/dist/TryCatchNode.js.map +1 -0
- package/dist/adapters/grpc/GrpcCodec.js +2 -2
- package/dist/adapters/grpc/GrpcRuntimeAdapter.d.ts +6 -4
- package/dist/adapters/grpc/GrpcRuntimeAdapter.js +6 -4
- package/dist/adapters/grpc/GrpcRuntimeAdapter.js.map +1 -1
- package/dist/adapters/grpc/types.d.ts +7 -5
- package/dist/adapters/grpc/types.js.map +1 -1
- package/dist/adapters/transport.d.ts +12 -41
- package/dist/adapters/transport.js +21 -70
- package/dist/adapters/transport.js.map +1 -1
- package/dist/cache/NodeResultCache.js +7 -0
- package/dist/cache/NodeResultCache.js.map +1 -1
- package/dist/concurrency/NatsKvConcurrencyBackend.js +18 -5
- package/dist/concurrency/NatsKvConcurrencyBackend.js.map +1 -1
- package/dist/concurrency/RedisConcurrencyBackend.d.ts +64 -0
- package/dist/concurrency/RedisConcurrencyBackend.js +374 -0
- package/dist/concurrency/RedisConcurrencyBackend.js.map +1 -0
- package/dist/concurrency/createConcurrencyBackend.d.ts +1 -0
- package/dist/concurrency/createConcurrencyBackend.js +5 -1
- package/dist/concurrency/createConcurrencyBackend.js.map +1 -1
- package/dist/defineNode.d.ts +8 -0
- package/dist/defineNode.js +25 -5
- package/dist/defineNode.js.map +1 -1
- package/dist/graphql/GraphQLSchemaGenerator.js +1 -1
- package/dist/graphql/GraphQLSchemaGenerator.js.map +1 -1
- package/dist/index.d.ts +10 -6
- package/dist/index.js +13 -9
- package/dist/index.js.map +1 -1
- package/dist/marketplace/RuntimeCatalog.d.ts +6 -0
- package/dist/marketplace/RuntimeCatalog.js.map +1 -1
- package/dist/marketplace/RuntimeDiscovery.d.ts +2 -2
- package/dist/marketplace/RuntimeDiscovery.js +18 -6
- package/dist/marketplace/RuntimeDiscovery.js.map +1 -1
- package/dist/monitoring/ConcurrencyMetrics.d.ts +26 -0
- package/dist/monitoring/ConcurrencyMetrics.js +36 -4
- package/dist/monitoring/ConcurrencyMetrics.js.map +1 -1
- package/dist/monitoring/ForEachWaitMetrics.d.ts +22 -0
- package/dist/monitoring/ForEachWaitMetrics.js +36 -0
- package/dist/monitoring/ForEachWaitMetrics.js.map +1 -0
- package/dist/openapi/OpenAPIGenerator.js +7 -2
- package/dist/openapi/OpenAPIGenerator.js.map +1 -1
- package/dist/runtime/PrimitiveStack.d.ts +64 -0
- package/dist/runtime/PrimitiveStack.js +92 -0
- package/dist/runtime/PrimitiveStack.js.map +1 -0
- package/dist/scheduling/DebounceBackend.d.ts +108 -0
- package/dist/scheduling/DebounceBackend.js +23 -0
- package/dist/scheduling/DebounceBackend.js.map +1 -0
- package/dist/scheduling/DebounceCoordinator.d.ts +65 -12
- package/dist/scheduling/DebounceCoordinator.js +234 -13
- package/dist/scheduling/DebounceCoordinator.js.map +1 -1
- package/dist/scheduling/DeferredRunScheduler.d.ts +28 -0
- package/dist/scheduling/DeferredRunScheduler.js +105 -3
- package/dist/scheduling/DeferredRunScheduler.js.map +1 -1
- package/dist/scheduling/NatsKvDebounceBackend.d.ts +53 -0
- package/dist/scheduling/NatsKvDebounceBackend.js +334 -0
- package/dist/scheduling/NatsKvDebounceBackend.js.map +1 -0
- package/dist/scheduling/RedisDebounceBackend.d.ts +49 -0
- package/dist/scheduling/RedisDebounceBackend.js +356 -0
- package/dist/scheduling/RedisDebounceBackend.js.map +1 -0
- package/dist/scheduling/createDebounceBackend.d.ts +25 -0
- package/dist/scheduling/createDebounceBackend.js +39 -0
- package/dist/scheduling/createDebounceBackend.js.map +1 -0
- package/dist/security/AuditLogger.js +1 -1
- package/dist/security/AuditLogger.js.map +1 -1
- package/dist/security/AuthMiddleware.d.ts +19 -20
- package/dist/security/AuthMiddleware.js +35 -20
- package/dist/security/AuthMiddleware.js.map +1 -1
- package/dist/security/OAuthProvider.js +2 -2
- package/dist/security/OAuthProvider.js.map +1 -1
- package/dist/security/SecretManager.js +14 -13
- package/dist/security/SecretManager.js.map +1 -1
- package/dist/security/index.d.ts +3 -1
- package/dist/security/index.js +3 -1
- package/dist/security/index.js.map +1 -1
- package/dist/testing/TestHarness.d.ts +27 -12
- package/dist/testing/TestHarness.js +19 -3
- package/dist/testing/TestHarness.js.map +1 -1
- package/dist/testing/WorkflowTestRunner.js +0 -7
- package/dist/testing/WorkflowTestRunner.js.map +1 -1
- package/dist/tracing/InMemoryRunStore.d.ts +14 -1
- package/dist/tracing/InMemoryRunStore.js +95 -6
- package/dist/tracing/InMemoryRunStore.js.map +1 -1
- package/dist/tracing/PostgresRunStore.d.ts +28 -2
- package/dist/tracing/PostgresRunStore.js +276 -3
- package/dist/tracing/PostgresRunStore.js.map +1 -1
- package/dist/tracing/RoutingDiagnostics.d.ts +55 -0
- package/dist/tracing/RoutingDiagnostics.js +50 -0
- package/dist/tracing/RoutingDiagnostics.js.map +1 -0
- package/dist/tracing/RunStore.d.ts +82 -1
- package/dist/tracing/RunTracker.d.ts +7 -1
- package/dist/tracing/RunTracker.js +23 -0
- package/dist/tracing/RunTracker.js.map +1 -1
- package/dist/tracing/SqliteRunStore.d.ts +57 -2
- package/dist/tracing/SqliteRunStore.js +408 -48
- package/dist/tracing/SqliteRunStore.js.map +1 -1
- package/dist/tracing/TraceRouter.js +380 -18
- package/dist/tracing/TraceRouter.js.map +1 -1
- package/dist/tracing/createStore.js +14 -3
- package/dist/tracing/createStore.js.map +1 -1
- package/dist/tracing/metadataFilter.d.ts +63 -0
- package/dist/tracing/metadataFilter.js +224 -0
- package/dist/tracing/metadataFilter.js.map +1 -0
- package/dist/tracing/types.d.ts +331 -7
- package/dist/utils/envAllowlist.d.ts +35 -0
- package/dist/utils/envAllowlist.js +113 -0
- package/dist/utils/envAllowlist.js.map +1 -0
- package/dist/version/RuntimeVersionValidator.d.ts +38 -0
- package/dist/version/RuntimeVersionValidator.js +121 -0
- package/dist/version/RuntimeVersionValidator.js.map +1 -0
- package/dist/visualization/WorkflowVisualizer.js +4 -4
- package/dist/visualization/WorkflowVisualizer.js.map +1 -1
- package/dist/workflow/PersistenceHelper.d.ts +18 -10
- package/dist/workflow/PersistenceHelper.js +35 -9
- package/dist/workflow/PersistenceHelper.js.map +1 -1
- package/dist/workflow/WorkflowNormalizer.d.ts +19 -1
- package/dist/workflow/WorkflowNormalizer.js +469 -19
- package/dist/workflow/WorkflowNormalizer.js.map +1 -1
- package/dist/workflow/WorkflowRegistry.d.ts +122 -0
- package/dist/workflow/WorkflowRegistry.js +121 -0
- package/dist/workflow/WorkflowRegistry.js.map +1 -1
- package/dist/workflow/sampleBody.d.ts +54 -0
- package/dist/workflow/sampleBody.js +320 -0
- package/dist/workflow/sampleBody.js.map +1 -0
- package/package.json +3 -8
- package/dist/adapters/HttpRuntimeAdapter.d.ts +0 -79
- package/dist/adapters/HttpRuntimeAdapter.js +0 -233
- package/dist/adapters/HttpRuntimeAdapter.js.map +0 -1
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ForEachNode — v0.5 primitive. Iterates over a collection, running a
|
|
3
|
+
* sub-pipeline of steps for each item. Supports sequential and parallel
|
|
4
|
+
* (with bounded concurrency) modes.
|
|
5
|
+
*
|
|
6
|
+
* The runtime config is read from `ctx.config[this.name]`:
|
|
7
|
+
* {
|
|
8
|
+
* in: unknown[], // resolved by the blueprint mapper before run()
|
|
9
|
+
* as: string, // per-iteration variable name; lands at ctx.state[as]
|
|
10
|
+
* mode: "sequential" | "parallel",
|
|
11
|
+
* concurrency: number, // parallel mode only
|
|
12
|
+
* steps: NodeBase[], // pre-resolved by Configuration's isFlowWithProperties path
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* Per-iteration scoping: a child ctx is built per item with
|
|
16
|
+
* `ctx.state[as] = item` and `ctx.state[as + "Index"] = i`. Mutations
|
|
17
|
+
* to state inside an iteration do NOT bleed to other iterations
|
|
18
|
+
* (parallel safety, plus matches the documented forEach contract).
|
|
19
|
+
*
|
|
20
|
+
* Errors in any iteration propagate to the caller. The runner's outer
|
|
21
|
+
* step-level catch wraps them into the standard error envelope.
|
|
22
|
+
*
|
|
23
|
+
* v0.6 wait-inside-primitives:
|
|
24
|
+
* - Phase 2 — sequential + wait shipped via the cursor stamps below.
|
|
25
|
+
* - Phase 3 — parallel + wait shipped via the pool AbortController +
|
|
26
|
+
* Promise.allSettled classification + cursor write. See
|
|
27
|
+
* `docs/c/devtools/parallel-foreach-wait-spec.mdx` for the full
|
|
28
|
+
* design.
|
|
29
|
+
*/
|
|
30
|
+
import _ from "lodash";
|
|
31
|
+
import { RunCancelledError } from "./RunCancelledError";
|
|
32
|
+
import RunnerNode from "./RunnerNode";
|
|
33
|
+
import { WaitDispatchRequest } from "./WaitDispatchRequest";
|
|
34
|
+
import { ForEachWaitMetrics } from "./monitoring/ForEachWaitMetrics";
|
|
35
|
+
import { consumeRehydratedCursor, popPrimitiveFrame, pushPrimitiveFrame, readRehydratedCursor, } from "./runtime/PrimitiveStack";
|
|
36
|
+
import { RunTracker } from "./tracing/RunTracker";
|
|
37
|
+
import { applyStepOutput } from "./workflow/PersistenceHelper";
|
|
38
|
+
export class ForEachNode extends RunnerNode {
|
|
39
|
+
/**
|
|
40
|
+
* v0.6 marker — RunnerSteps stamps `_blokActivePrimitiveNodeRunId`
|
|
41
|
+
* on ctx around `step.process()` calls when this flag is true so a
|
|
42
|
+
* nested wait fired inside an iteration body knows which NodeRun to
|
|
43
|
+
* write its `iteration_context` cursor to. Static `true` (vs an
|
|
44
|
+
* instance method) avoids importing ForEachNode inside RunnerSteps,
|
|
45
|
+
* which would create a circular dependency.
|
|
46
|
+
*/
|
|
47
|
+
isPrimitiveIterator = true;
|
|
48
|
+
async run(ctx) {
|
|
49
|
+
this.contentType = "application/json";
|
|
50
|
+
const response = { success: true, data: [], error: null };
|
|
51
|
+
const opts = (ctx.config?.[this.name] ?? {});
|
|
52
|
+
const items = Array.isArray(opts.in) ? opts.in : [];
|
|
53
|
+
const as = typeof opts.as === "string" && opts.as.length > 0 ? opts.as : "item";
|
|
54
|
+
const mode = opts.mode === "parallel" ? "parallel" : "sequential";
|
|
55
|
+
const concurrency = typeof opts.concurrency === "number" && opts.concurrency > 0 ? opts.concurrency : 10;
|
|
56
|
+
const steps = (opts.steps ?? []);
|
|
57
|
+
if (items.length === 0 || steps.length === 0) {
|
|
58
|
+
response.data = [];
|
|
59
|
+
applyStepOutput(ctx, this, { data: [] });
|
|
60
|
+
return response;
|
|
61
|
+
}
|
|
62
|
+
// v0.6 — read the resume hint that TriggerBase rehydrated.
|
|
63
|
+
//
|
|
64
|
+
// Phase 4 — cursors are now keyed by NodeRun id in a map on ctx
|
|
65
|
+
// (`_blokIterationCursors`). Each primitive looks itself up by
|
|
66
|
+
// `ctx._traceNodeId` so nested primitives (forEach > forEach,
|
|
67
|
+
// switch > forEach, etc.) each find their OWN cursor — not the
|
|
68
|
+
// previous Phase 2/3 single-slot model that lost outer cursors
|
|
69
|
+
// to inner overwrites. Falls back to the legacy single-slot
|
|
70
|
+
// `_blokIterationResume` field for first-pass compatibility with
|
|
71
|
+
// callers that haven't migrated yet.
|
|
72
|
+
const ctxAny = ctx;
|
|
73
|
+
const myNodeRunId = ctxAny._traceNodeId;
|
|
74
|
+
// Phase 4 — lookup by step NAME (not NodeRun id). Names are
|
|
75
|
+
// stable across dispatchDeferred re-entries; NodeRun ids change.
|
|
76
|
+
let resume = readRehydratedCursor(ctx, this.name);
|
|
77
|
+
if (resume) {
|
|
78
|
+
consumeRehydratedCursor(ctx, this.name);
|
|
79
|
+
}
|
|
80
|
+
if (resume === undefined) {
|
|
81
|
+
const legacyResume = ctxAny._blokIterationResume;
|
|
82
|
+
if (legacyResume !== undefined) {
|
|
83
|
+
resume = legacyResume;
|
|
84
|
+
ctxAny._blokIterationResume = undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Lazy import to avoid circular dep (Runner pulls in Configuration).
|
|
88
|
+
const { default: Runner } = await import("./Runner");
|
|
89
|
+
// Discriminate on cursor mode at the read side. Sequential cursors
|
|
90
|
+
// written before this PR omit the `mode` field — treated as
|
|
91
|
+
// "sequential" by default. Parallel cursors are written ONLY by
|
|
92
|
+
// the parallel branch below.
|
|
93
|
+
const sequentialResume = resume && (resume.mode ?? "sequential") === "sequential" ? resume : undefined;
|
|
94
|
+
const parallelResume = resume?.mode === "parallel" ? resume : undefined;
|
|
95
|
+
// Pre-populate results[] from cursor — both sequential and parallel
|
|
96
|
+
// resume paths use this. Sequential: contiguous slice [0..N-1].
|
|
97
|
+
// Parallel: sparse — only slots present in `completedResults` get
|
|
98
|
+
// filled; the rest stay `undefined` and get re-launched.
|
|
99
|
+
const results = new Array(items.length);
|
|
100
|
+
if (sequentialResume?.completedResults) {
|
|
101
|
+
const cap = Math.min(sequentialResume.completedResults.length, items.length);
|
|
102
|
+
for (let k = 0; k < cap; k++) {
|
|
103
|
+
results[k] = sequentialResume.completedResults[k];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (parallelResume?.completedResults) {
|
|
107
|
+
const cap = Math.min(parallelResume.completedResults.length, items.length);
|
|
108
|
+
for (let k = 0; k < cap; k++) {
|
|
109
|
+
const slot = parallelResume.completedResults[k];
|
|
110
|
+
// `null` distinguishes "ran but returned undefined" (re-use
|
|
111
|
+
// this value, don't re-launch) from a JSON-undefined hole
|
|
112
|
+
// (not present — re-launch). Both deserialise to `null` in
|
|
113
|
+
// some JSON encoders; we normalise sparse holes via
|
|
114
|
+
// `Object.prototype.hasOwnProperty` against the parsed
|
|
115
|
+
// array.
|
|
116
|
+
if (Object.prototype.hasOwnProperty.call(parallelResume.completedResults, k) && slot !== undefined) {
|
|
117
|
+
// null sentinel survives — readers see `results[k] === null`
|
|
118
|
+
// meaning "ran, returned undefined" and skip re-launch.
|
|
119
|
+
results[k] = slot;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const runIteration = async (item, index, innerResumeIndex) => {
|
|
124
|
+
const childCtx = this.cloneCtxForIteration(ctx, as, item, index);
|
|
125
|
+
// v0.6 Phase 2 — pass the inner-step resume cursor on the
|
|
126
|
+
// child ctx. Phase 4 — propagate even the index-0 case so
|
|
127
|
+
// the wait-re-entry detection can distinguish a resumed
|
|
128
|
+
// iteration (innerResumeIndex defined) from a fresh one.
|
|
129
|
+
if (innerResumeIndex !== undefined) {
|
|
130
|
+
childCtx._blokInnerResumeIndex = innerResumeIndex;
|
|
131
|
+
}
|
|
132
|
+
const runner = new Runner(steps);
|
|
133
|
+
// `deep: true` — inner pipelines must not inherit the outer
|
|
134
|
+
// run's `lastCompletedStepIndex` cursor (PR 4 wait/resume logic)
|
|
135
|
+
// or every iteration's nested steps get marked "skipped (resumed
|
|
136
|
+
// past wait...)" and the iteration produces no output.
|
|
137
|
+
await runner.run(childCtx, { deep: true, stepName: this.name });
|
|
138
|
+
// After Runner.runSteps, `childCtx.response` is set to the last
|
|
139
|
+
// step's resolved data (RunnerSteps line ~349: `ctx.response =
|
|
140
|
+
// model.data`). So `childCtx.response` IS the iteration's
|
|
141
|
+
// output value, not a wrapped envelope.
|
|
142
|
+
return childCtx.response;
|
|
143
|
+
};
|
|
144
|
+
// v0.6 Phase 4 — push a primitive frame onto the ctx stack so the
|
|
145
|
+
// wait-throw site (and parallel-branch cursor writer) persists
|
|
146
|
+
// THIS forEach's iteration cursor to THIS forEach's NodeRun.
|
|
147
|
+
// Nested primitives (forEach > forEach, switch > forEach, etc.)
|
|
148
|
+
// each push their own frame, which is what unblocks the
|
|
149
|
+
// `forEach > forEach > wait` shape that the Phase 2/3 single-slot
|
|
150
|
+
// machinery couldn't represent.
|
|
151
|
+
const initialCursor = mode === "parallel"
|
|
152
|
+
? {
|
|
153
|
+
mode: "parallel",
|
|
154
|
+
waitFiringIteration: 0,
|
|
155
|
+
innerStepIndex: 0,
|
|
156
|
+
completedResults: [],
|
|
157
|
+
cancelledIterations: [],
|
|
158
|
+
}
|
|
159
|
+
: { mode: "sequential", iteration: 0, innerStepIndex: 0, completedResults: [] };
|
|
160
|
+
const frame = myNodeRunId
|
|
161
|
+
? { nodeRunId: myNodeRunId, cursor: initialCursor }
|
|
162
|
+
: undefined;
|
|
163
|
+
if (frame)
|
|
164
|
+
pushPrimitiveFrame(ctx, frame);
|
|
165
|
+
try {
|
|
166
|
+
if (mode === "sequential") {
|
|
167
|
+
// v0.6 Phase 2 — start at the resume iteration if present,
|
|
168
|
+
// else 0. Iterations before the resume index are not re-run;
|
|
169
|
+
// their results were rehydrated above.
|
|
170
|
+
const startIndex = sequentialResume?.iteration ?? 0;
|
|
171
|
+
for (let i = startIndex; i < items.length; i++) {
|
|
172
|
+
// v0.6 Phase 4 — update the frame cursor so a wait fired
|
|
173
|
+
// from inside this iteration body lands the right snapshot
|
|
174
|
+
// in `node_runs.iteration_context`. `completedResults`
|
|
175
|
+
// reflects results-so-far so on resume we don't re-run
|
|
176
|
+
// iterations that already finished before the wait.
|
|
177
|
+
if (frame) {
|
|
178
|
+
const seq = frame.cursor;
|
|
179
|
+
seq.iteration = i;
|
|
180
|
+
seq.completedResults = results.slice(0, i);
|
|
181
|
+
}
|
|
182
|
+
const innerResumeIndex = i === startIndex && sequentialResume !== undefined ? sequentialResume.innerStepIndex : undefined;
|
|
183
|
+
results[i] = await runIteration(items[i], i, innerResumeIndex);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// === v0.6 Phase 3 — parallel forEach + wait =====================
|
|
188
|
+
// See `docs/c/devtools/parallel-foreach-wait-spec.mdx`. Headline
|
|
189
|
+
// contract: when one iteration's inner step throws
|
|
190
|
+
// `WaitDispatchRequest`, peer in-flight iterations are
|
|
191
|
+
// cancelled via a pool AbortController. Completed iterations'
|
|
192
|
+
// results are persisted in the cursor's `completedResults`
|
|
193
|
+
// sparse array; cancelled + queued iterations go in
|
|
194
|
+
// `cancelledIterations` for re-launch on resume.
|
|
195
|
+
//
|
|
196
|
+
// The pool AbortController is distinct from `ctx.signal` —
|
|
197
|
+
// tripping it MUST NOT cascade to the parent run (the parent
|
|
198
|
+
// is waiting, not cancelled). Each iteration's child ctx gets
|
|
199
|
+
// a per-iteration signal chained off BOTH the parent's signal
|
|
200
|
+
// (so user-cancel still cascades) AND the pool signal (so
|
|
201
|
+
// peer-wait cascades).
|
|
202
|
+
const poolController = new AbortController();
|
|
203
|
+
const poolSignal = poolController.signal;
|
|
204
|
+
// On resume in parallel mode, the work queue is the set of
|
|
205
|
+
// iterations that need to run: the wait-firing iter (with
|
|
206
|
+
// inner resume hint) + all cancelled/queued iters (from
|
|
207
|
+
// scratch). On a fresh pass, every iteration runs.
|
|
208
|
+
const queue = [];
|
|
209
|
+
if (parallelResume) {
|
|
210
|
+
const toRun = new Set();
|
|
211
|
+
toRun.add(parallelResume.waitFiringIteration);
|
|
212
|
+
for (const idx of parallelResume.cancelledIterations) {
|
|
213
|
+
if (idx < items.length)
|
|
214
|
+
toRun.add(idx);
|
|
215
|
+
}
|
|
216
|
+
// Any iterations beyond `completedResults.length` are
|
|
217
|
+
// trailing-not-started — also need to run.
|
|
218
|
+
for (let i = parallelResume.completedResults.length; i < items.length; i++) {
|
|
219
|
+
if (results[i] === undefined)
|
|
220
|
+
toRun.add(i);
|
|
221
|
+
}
|
|
222
|
+
queue.push(...Array.from(toRun).sort((a, b) => a - b));
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
for (let i = 0; i < items.length; i++)
|
|
226
|
+
queue.push(i);
|
|
227
|
+
}
|
|
228
|
+
let queuePos = 0;
|
|
229
|
+
const workerCount = Math.min(concurrency, queue.length);
|
|
230
|
+
const launchWorker = async () => {
|
|
231
|
+
const outcomes = [];
|
|
232
|
+
while (true) {
|
|
233
|
+
const myQueuePos = queuePos++;
|
|
234
|
+
if (myQueuePos >= queue.length)
|
|
235
|
+
return outcomes;
|
|
236
|
+
const index = queue[myQueuePos];
|
|
237
|
+
// Skip if pool tripped before this iteration started —
|
|
238
|
+
// classify as cancelled without doing any work.
|
|
239
|
+
if (poolSignal.aborted) {
|
|
240
|
+
outcomes.push({ kind: "cancelled", index });
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const innerResumeIndex = parallelResume !== undefined && index === parallelResume.waitFiringIteration
|
|
244
|
+
? parallelResume.innerStepIndex
|
|
245
|
+
: undefined;
|
|
246
|
+
try {
|
|
247
|
+
const result = await runIterationWithPool(item(items, index), index, innerResumeIndex, poolSignal);
|
|
248
|
+
outcomes.push({ kind: "completed", index, result });
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
if (err instanceof WaitDispatchRequest) {
|
|
252
|
+
// This iteration fired a wait. Trip the pool so
|
|
253
|
+
// peers stop accepting new iterations and exit
|
|
254
|
+
// their current ones at the next step boundary.
|
|
255
|
+
if (!poolSignal.aborted)
|
|
256
|
+
poolController.abort();
|
|
257
|
+
outcomes.push({ kind: "wait", index, throwObj: err });
|
|
258
|
+
}
|
|
259
|
+
else if (err instanceof RunCancelledError) {
|
|
260
|
+
// Distinguish pool-cancel from user-cancel. If
|
|
261
|
+
// poolSignal is aborted AND ctx.signal is NOT,
|
|
262
|
+
// this is a pool-induced cancellation due to a
|
|
263
|
+
// peer's wait — re-runnable. Otherwise it's a
|
|
264
|
+
// user-cancel — re-throw upstream.
|
|
265
|
+
const userCancel = ctx.signal?.aborted === true && !poolSignal.aborted;
|
|
266
|
+
if (userCancel) {
|
|
267
|
+
outcomes.push({ kind: "error", index, err });
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
outcomes.push({ kind: "cancelled", index });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
// Real error. Trip the pool so peers stop
|
|
275
|
+
// (don't waste CPU on a doomed forEach), then
|
|
276
|
+
// record. Classification later: real errors
|
|
277
|
+
// beat waits.
|
|
278
|
+
if (!poolSignal.aborted)
|
|
279
|
+
poolController.abort();
|
|
280
|
+
outcomes.push({ kind: "error", index, err });
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
// Helper — same as `runIteration` but threads the pool signal
|
|
286
|
+
// through to the per-iteration child ctx (so RunnerSteps'
|
|
287
|
+
// between-step abort check sees pool aborts).
|
|
288
|
+
const runIterationWithPool = async (item_, index, innerResumeIndex, poolSig) => {
|
|
289
|
+
const childCtx = this.cloneCtxForIteration(ctx, as, item_, index);
|
|
290
|
+
const iterCtl = new AbortController();
|
|
291
|
+
const listenerCleanup = new AbortController();
|
|
292
|
+
// Chain from parent ctx.signal (user cancel cascade)
|
|
293
|
+
if (ctx.signal) {
|
|
294
|
+
if (ctx.signal.aborted)
|
|
295
|
+
iterCtl.abort();
|
|
296
|
+
else
|
|
297
|
+
ctx.signal.addEventListener("abort", () => {
|
|
298
|
+
if (!iterCtl.signal.aborted)
|
|
299
|
+
iterCtl.abort();
|
|
300
|
+
}, { once: true, signal: listenerCleanup.signal });
|
|
301
|
+
}
|
|
302
|
+
// Chain from pool signal (peer wait cascade)
|
|
303
|
+
if (poolSig.aborted)
|
|
304
|
+
iterCtl.abort();
|
|
305
|
+
else
|
|
306
|
+
poolSig.addEventListener("abort", () => {
|
|
307
|
+
if (!iterCtl.signal.aborted)
|
|
308
|
+
iterCtl.abort();
|
|
309
|
+
}, { once: true, signal: listenerCleanup.signal });
|
|
310
|
+
// Replace the inherited signal (which would be parent's)
|
|
311
|
+
// with the per-iteration signal that cascades both ways.
|
|
312
|
+
childCtx.signal = iterCtl.signal;
|
|
313
|
+
// Phase 4 — propagate the index-0 case too.
|
|
314
|
+
if (innerResumeIndex !== undefined) {
|
|
315
|
+
childCtx._blokInnerResumeIndex = innerResumeIndex;
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const runner = new Runner(steps);
|
|
319
|
+
await runner.run(childCtx, { deep: true, stepName: this.name });
|
|
320
|
+
return childCtx.response;
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
// Detach the per-iteration listeners from parent +
|
|
324
|
+
// pool signals (PR 1 A3 pattern — prevents listener
|
|
325
|
+
// accumulation on long-lived parents over many
|
|
326
|
+
// parallel forEach invocations).
|
|
327
|
+
listenerCleanup.abort();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
// Spawn workers. Each returns its set of outcomes (one
|
|
331
|
+
// outcome per iteration the worker handled). `allSettled`
|
|
332
|
+
// gives us each worker's final state; we then flatten.
|
|
333
|
+
const workers = [];
|
|
334
|
+
for (let w = 0; w < workerCount; w++) {
|
|
335
|
+
workers.push(launchWorker());
|
|
336
|
+
}
|
|
337
|
+
const settled = await Promise.allSettled(workers);
|
|
338
|
+
// Flatten. A worker can't reject (its try/catch handles all
|
|
339
|
+
// errors and classifies into the outcome list). But guard
|
|
340
|
+
// defensively — rejection means a bug in launchWorker.
|
|
341
|
+
const allOutcomes = [];
|
|
342
|
+
for (const s of settled) {
|
|
343
|
+
if (s.status === "fulfilled") {
|
|
344
|
+
allOutcomes.push(...s.value);
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
// Defensive — re-throw so the failure isn't lost.
|
|
348
|
+
throw s.reason;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Classification step. Walk outcomes:
|
|
352
|
+
// 1. If ANY non-wait error, re-throw the first one (errors
|
|
353
|
+
// beat waits per the spec).
|
|
354
|
+
// 2. If ANY wait, identify the lowest-index wait as the
|
|
355
|
+
// wait-firing iteration; reclassify others as cancelled.
|
|
356
|
+
// 3. Else, all iterations completed — fall through to
|
|
357
|
+
// normal completion below.
|
|
358
|
+
const errors = allOutcomes.filter((o) => o.kind === "error");
|
|
359
|
+
if (errors.length > 0) {
|
|
360
|
+
// First error wins (sorted by iteration index for
|
|
361
|
+
// determinism, even though only one re-throws).
|
|
362
|
+
errors.sort((a, b) => a.index - b.index);
|
|
363
|
+
throw errors[0].err;
|
|
364
|
+
}
|
|
365
|
+
const waits = allOutcomes.filter((o) => o.kind === "wait");
|
|
366
|
+
if (waits.length > 0) {
|
|
367
|
+
// First-wait-wins by iteration index.
|
|
368
|
+
waits.sort((a, b) => a.index - b.index);
|
|
369
|
+
const waitFiring = waits[0];
|
|
370
|
+
// Demote other waits to cancelled (they were racing — only
|
|
371
|
+
// one resumes; the others re-run from scratch).
|
|
372
|
+
const cancelledFromOtherWaits = waits.slice(1).map((w) => w.index);
|
|
373
|
+
const completed = allOutcomes.filter((o) => o.kind === "completed");
|
|
374
|
+
const cancelled = allOutcomes
|
|
375
|
+
.filter((o) => o.kind === "cancelled")
|
|
376
|
+
.map((o) => o.index);
|
|
377
|
+
// Build the sparse completedResults array. Slot k =
|
|
378
|
+
// completed iteration k's return value; null = "ran but
|
|
379
|
+
// returned undefined"; JSON-undefined hole = "not
|
|
380
|
+
// present" (cancelled / queued / wait-firing). On resume,
|
|
381
|
+
// only present-non-undefined slots short-circuit
|
|
382
|
+
// re-launch.
|
|
383
|
+
const completedResults = new Array(items.length);
|
|
384
|
+
for (const c of completed) {
|
|
385
|
+
completedResults[c.index] = c.result === undefined ? null : c.result;
|
|
386
|
+
}
|
|
387
|
+
// Also stash the rehydrated completed iterations from the
|
|
388
|
+
// resume cursor — if we're already on a re-entry pass,
|
|
389
|
+
// those iterations don't appear in `completed` (they
|
|
390
|
+
// were never launched this pass) but their results are
|
|
391
|
+
// in `results[]` from the pre-populate above.
|
|
392
|
+
if (parallelResume) {
|
|
393
|
+
for (let k = 0; k < parallelResume.completedResults.length; k++) {
|
|
394
|
+
if (Object.prototype.hasOwnProperty.call(parallelResume.completedResults, k) &&
|
|
395
|
+
parallelResume.completedResults[k] !== undefined &&
|
|
396
|
+
completedResults[k] === undefined) {
|
|
397
|
+
completedResults[k] = parallelResume.completedResults[k];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const allCancelled = [...new Set([...cancelled, ...cancelledFromOtherWaits])].sort((a, b) => a - b);
|
|
402
|
+
const cursor = {
|
|
403
|
+
mode: "parallel",
|
|
404
|
+
waitFiringIteration: waitFiring.index,
|
|
405
|
+
// `throwObj.info.stepIndex` carries the inner step index
|
|
406
|
+
// the wait fired at — exactly what we need.
|
|
407
|
+
innerStepIndex: waitFiring.throwObj.info.stepIndex,
|
|
408
|
+
completedResults,
|
|
409
|
+
cancelledIterations: allCancelled,
|
|
410
|
+
};
|
|
411
|
+
// Write the cursor BEFORE re-throwing so TriggerBase's
|
|
412
|
+
// rehydrate (on the next resume) sees the parallel-mode
|
|
413
|
+
// shape. RunnerSteps may have written a sequential-shape
|
|
414
|
+
// cursor at the wait-throw moment (Phase 2 path); this
|
|
415
|
+
// write overwrites it on the same NodeRun id.
|
|
416
|
+
this.writeCursor(ctx, frame, cursor);
|
|
417
|
+
// OTel — record the cancelled count so dashboards can
|
|
418
|
+
// spot workflows that frequently waste work on cancel +
|
|
419
|
+
// re-launch.
|
|
420
|
+
try {
|
|
421
|
+
ForEachWaitMetrics.getInstance().recordCancellation({
|
|
422
|
+
workflowName: ctx.workflow_name ?? "unknown",
|
|
423
|
+
cancelledCount: allCancelled.length,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// Metrics never block.
|
|
428
|
+
}
|
|
429
|
+
// Re-throw the original wait — TriggerBase catches it,
|
|
430
|
+
// schedules the deferred dispatch, returns 202 to HTTP.
|
|
431
|
+
throw waitFiring.throwObj;
|
|
432
|
+
}
|
|
433
|
+
// No waits, no errors — every iteration completed. Populate
|
|
434
|
+
// `results[]` from the completed outcomes.
|
|
435
|
+
for (const o of allOutcomes) {
|
|
436
|
+
if (o.kind === "completed") {
|
|
437
|
+
results[o.index] = o.result;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
response.data = results;
|
|
442
|
+
// Persist to ctx.state[this.name] so downstream steps can read via
|
|
443
|
+
// $.state[id]. Class-based RunnerNode subclasses must call
|
|
444
|
+
// applyStepOutput explicitly (BlokService does it implicitly via
|
|
445
|
+
// its `run()` method, but we own our own run() here).
|
|
446
|
+
applyStepOutput(ctx, this, { data: results });
|
|
447
|
+
return response;
|
|
448
|
+
}
|
|
449
|
+
finally {
|
|
450
|
+
// v0.6 Phase 4 — always pop the frame so sibling primitives
|
|
451
|
+
// later in the workflow don't see a stale cursor pointer, AND
|
|
452
|
+
// so a thrown WaitDispatchRequest unwind doesn't leave the
|
|
453
|
+
// stack in an inconsistent state for the outer runSteps.
|
|
454
|
+
if (frame)
|
|
455
|
+
popPrimitiveFrame(ctx);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Write the parallel cursor to the active forEach NodeRun's frame.
|
|
460
|
+
* Stamped from inside the parallel branch (vs. via RunnerSteps' wait-
|
|
461
|
+
* throw site) because the parallel cursor is built post-
|
|
462
|
+
* `Promise.allSettled` — the wait-throw site doesn't know which peer
|
|
463
|
+
* iterations got cancelled OR which completed.
|
|
464
|
+
*
|
|
465
|
+
* v0.6 Phase 4 — additionally mutates the frame's cursor in place so
|
|
466
|
+
* the wait-throw site (which runs AFTER this, when the throw unwinds
|
|
467
|
+
* back through RunnerSteps) re-persists the same parallel-shape
|
|
468
|
+
* cursor on outer-frame writes. Without the in-place mutation, the
|
|
469
|
+
* outer write would clobber this back to the initial parallel
|
|
470
|
+
* placeholder.
|
|
471
|
+
*/
|
|
472
|
+
writeCursor(ctx, frame, cursor) {
|
|
473
|
+
if (!frame)
|
|
474
|
+
return;
|
|
475
|
+
// Replace the in-stack cursor so any subsequent stack walk uses
|
|
476
|
+
// the parallel-shape one we built (not the placeholder pushed at
|
|
477
|
+
// run() entry).
|
|
478
|
+
frame.cursor = cursor;
|
|
479
|
+
const primitiveNodeRunId = frame.nodeRunId;
|
|
480
|
+
try {
|
|
481
|
+
RunTracker.getInstance().getStore().updateNodeRun(primitiveNodeRunId, {
|
|
482
|
+
iterationContext: cursor,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
catch (err) {
|
|
486
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
487
|
+
ctx.logger.logLevel("warn", `[blok][wait] forEach parallel cursor write failed: ${msg}. Resume will re-run every iteration from scratch.`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
cloneCtxForIteration(ctx, as, item, index) {
|
|
491
|
+
const baseState = (ctx.state ?? {});
|
|
492
|
+
const state = { ...baseState };
|
|
493
|
+
state[as] = item;
|
|
494
|
+
state[`${as}Index`] = index;
|
|
495
|
+
// Deep-clone config so per-iteration blueprint mapper resolutions
|
|
496
|
+
// don't bleed across iterations (same hazard the v0.4 Configuration
|
|
497
|
+
// deep-clone fix addressed at the workflow level).
|
|
498
|
+
const config = _.cloneDeep(ctx.config);
|
|
499
|
+
const childCtx = {
|
|
500
|
+
...ctx,
|
|
501
|
+
state,
|
|
502
|
+
vars: state,
|
|
503
|
+
config,
|
|
504
|
+
response: { data: null, success: true, error: null, contentType: "application/json" },
|
|
505
|
+
};
|
|
506
|
+
// v0.5.3 — stash the iteration index on the child ctx so RunnerSteps
|
|
507
|
+
// can propagate it to NodeRun.iterationIndex when starting each
|
|
508
|
+
// inner step. Studio reads this to group sibling rows by iteration
|
|
509
|
+
// (5 iterations × 3 inner steps render as 5 collapsible sections,
|
|
510
|
+
// not 15 flat rows with duplicate names). Overrides any sentinel
|
|
511
|
+
// inherited from a parent iteration scope — the inner-most forEach
|
|
512
|
+
// owns the index its inner steps see.
|
|
513
|
+
childCtx._blokIterationIndex = index;
|
|
514
|
+
return childCtx;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// Local helper to keep the parallel branch readable when reading
|
|
518
|
+
// `items[index]` from inside an async lambda.
|
|
519
|
+
function item(items, index) {
|
|
520
|
+
return items[index];
|
|
521
|
+
}
|
|
522
|
+
//# sourceMappingURL=ForEachNode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ForEachNode.js","sourceRoot":"","sources":["../src/ForEachNode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a `loop` step exceeds its `maxIterations` cap. Distinct
|
|
3
|
+
* from generic Error so callers can `instanceof` discriminate (e.g. to
|
|
4
|
+
* route to a tryCatch.catch arm or surface a 500 vs a 408).
|
|
5
|
+
*/
|
|
6
|
+
export declare class LoopMaxIterationsError extends Error {
|
|
7
|
+
readonly stepId: string;
|
|
8
|
+
readonly maxIterations: number;
|
|
9
|
+
readonly actualIterations: number;
|
|
10
|
+
constructor(stepId: string, maxIterations: number, actualIterations: number);
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a `loop` step exceeds its `maxIterations` cap. Distinct
|
|
3
|
+
* from generic Error so callers can `instanceof` discriminate (e.g. to
|
|
4
|
+
* route to a tryCatch.catch arm or surface a 500 vs a 408).
|
|
5
|
+
*/
|
|
6
|
+
export class LoopMaxIterationsError extends Error {
|
|
7
|
+
stepId;
|
|
8
|
+
maxIterations;
|
|
9
|
+
actualIterations;
|
|
10
|
+
constructor(stepId, maxIterations, actualIterations) {
|
|
11
|
+
super(`Loop "${stepId}" exceeded maxIterations=${maxIterations} (ran ${actualIterations} times). This is a hard safety cap to prevent infinite loops. Increase the cap if your loop legitimately runs longer, or check the \`while\` condition.`);
|
|
12
|
+
this.name = "LoopMaxIterationsError";
|
|
13
|
+
this.stepId = stepId;
|
|
14
|
+
this.maxIterations = maxIterations;
|
|
15
|
+
this.actualIterations = actualIterations;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=LoopMaxIterationsError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoopMaxIterationsError.js","sourceRoot":"","sources":["../src/LoopMaxIterationsError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAChC,MAAM,CAAS;IACf,aAAa,CAAS;IACtB,gBAAgB,CAAS;IAEzC,YAAY,MAAc,EAAE,aAAqB,EAAE,gBAAwB;QAC1E,KAAK,CACJ,SAAS,MAAM,4BAA4B,aAAa,SAAS,gBAAgB,yJAAyJ,CAC1O,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC1C,CAAC;CACD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoopNode — v0.5 primitive. While-loop with hard maxIterations safety
|
|
3
|
+
* cap. Runs a sub-pipeline as long as the `while` condition evaluates
|
|
4
|
+
* truthy against the live ctx. Each iteration carries forward state
|
|
5
|
+
* mutations (sequential by definition) so condition variables can
|
|
6
|
+
* advance between iterations.
|
|
7
|
+
*
|
|
8
|
+
* The runtime config is read from `ctx.config[this.name]`:
|
|
9
|
+
* {
|
|
10
|
+
* while: string, // JS expression evaluated against ctx
|
|
11
|
+
* maxIterations: number, // hard safety cap; throws LoopMaxIterationsError on hit
|
|
12
|
+
* steps: NodeBase[], // pre-resolved by Configuration's isFlowWithProperties path
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* The loop counter is exposed as `ctx.state[<stepId>Index]` (0-based)
|
|
16
|
+
* so the `while` expression can reference it. Each iteration's final
|
|
17
|
+
* step output is the loop step's overall data (the last iteration's
|
|
18
|
+
* `ctx.response.data`).
|
|
19
|
+
*/
|
|
20
|
+
import type { Context, ResponseContext } from "@blokjs/shared";
|
|
21
|
+
import RunnerNode from "./RunnerNode";
|
|
22
|
+
export declare class LoopNode extends RunnerNode {
|
|
23
|
+
/**
|
|
24
|
+
* v0.6 marker — RunnerSteps stamps `_blokActivePrimitiveNodeRunId`
|
|
25
|
+
* on ctx around `step.process()` calls when this flag is true so a
|
|
26
|
+
* nested wait fired inside an iteration body knows which NodeRun to
|
|
27
|
+
* write its `iteration_context` cursor to. Mirrors ForEachNode's
|
|
28
|
+
* marker (Phase 2). Static `true` (vs an instance method) avoids
|
|
29
|
+
* importing LoopNode inside RunnerSteps, which would create a
|
|
30
|
+
* circular dependency.
|
|
31
|
+
*/
|
|
32
|
+
readonly isPrimitiveIterator = true;
|
|
33
|
+
run(ctx: Context): Promise<ResponseContext>;
|
|
34
|
+
private evaluateCondition;
|
|
35
|
+
private cloneCtxForIteration;
|
|
36
|
+
}
|