footprintjs 8.2.0 → 8.3.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/esm/lib/engine/handlers/NodeResolver.js +14 -4
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +137 -39
- package/dist/lib/engine/handlers/NodeResolver.js +14 -4
- package/dist/lib/engine/traversal/FlowchartTraverser.js +137 -39
- package/dist/types/lib/engine/handlers/NodeResolver.d.ts +9 -1
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +65 -1
- package/package.json +1 -1
|
@@ -11,7 +11,15 @@ import type { HandlerDeps } from '../types.js';
|
|
|
11
11
|
export declare class NodeResolver<TOut = any, TScope = any> {
|
|
12
12
|
private deps;
|
|
13
13
|
private readonly nodeIdMap;
|
|
14
|
-
|
|
14
|
+
private readonly getEffectiveChildren?;
|
|
15
|
+
constructor(deps: HandlerDeps<TOut, TScope>, nodeIdMap?: Map<string, StageNode<TOut, TScope>>,
|
|
16
|
+
/**
|
|
17
|
+
* Overlay-aware children accessor injected by the traverser. The DFS
|
|
18
|
+
* fallback resolves loop targets against the LIVE runtime shape — a node
|
|
19
|
+
* patched by a dynamic StageNode return carries its children in the
|
|
20
|
+
* traverser-local overlay, not on the shared built-chart node.
|
|
21
|
+
*/
|
|
22
|
+
getEffectiveChildren?: (node: StageNode<TOut, TScope>) => StageNode<TOut, TScope>[] | undefined);
|
|
15
23
|
/**
|
|
16
24
|
* O(1) node lookup via pre-built ID map.
|
|
17
25
|
* Falls back to DFS from startNode (for dynamic nodes added at runtime
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import type { ScopeProtectionMode } from '../../scope/protection/types.js';
|
|
22
22
|
import { FlowRecorderDispatcher } from '../narrative/FlowRecorderDispatcher.js';
|
|
23
23
|
import type { FlowRecorder, IControlFlowNarrative } from '../narrative/types.js';
|
|
24
|
-
import type { IExecutionRuntime, ILogger, ScopeFactory, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, SubflowResult, TraversalResult } from '../types.js';
|
|
24
|
+
import type { IExecutionRuntime, ILogger, ScopeFactory, Selector, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, SubflowMountOptions, SubflowResult, TraversalResult } from '../types.js';
|
|
25
25
|
export interface TraverserOptions<TOut = any, TScope = any> {
|
|
26
26
|
root: StageNode<TOut, TScope>;
|
|
27
27
|
stageMap: Map<string, StageFunction<TOut, TScope>>;
|
|
@@ -78,6 +78,37 @@ export interface TraverserOptions<TOut = any, TScope = any> {
|
|
|
78
78
|
*/
|
|
79
79
|
runId: string;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Traverser-local overlay entry for a node whose stage function returned a
|
|
83
|
+
* StageNode (dynamic continuation). Holds the dynamic values that earlier
|
|
84
|
+
* versions wrote DIRECTLY onto the shared built-chart node — which leaked the
|
|
85
|
+
* dynamic shape into every later run of the same built chart and raced
|
|
86
|
+
* concurrent executors. The overlay keeps the built graph immutable: patches
|
|
87
|
+
* live in a per-traverser Map keyed by `node.id` and die with the run.
|
|
88
|
+
*
|
|
89
|
+
* `next` is intentionally ABSENT: a dynamic `next` only ever applies to the
|
|
90
|
+
* visit that produced it (the old code wrote `node.next` and restored it
|
|
91
|
+
* before anything could observe the write), so it stays a local variable in
|
|
92
|
+
* `executeNode` and is routed through `ContinuationResolver` directly.
|
|
93
|
+
*/
|
|
94
|
+
export interface DynamicNodePatch<TOut = any, TScope = any> {
|
|
95
|
+
/**
|
|
96
|
+
* Subflow mount metadata from a dynamic-subflow return. Grouped so the
|
|
97
|
+
* merged view reproduces the old field-wise overwrite exactly — including
|
|
98
|
+
* `subflowName`/`subflowMountOptions` becoming undefined when the dynamic
|
|
99
|
+
* return omitted them.
|
|
100
|
+
*/
|
|
101
|
+
subflowMeta?: {
|
|
102
|
+
isSubflowRoot: true;
|
|
103
|
+
subflowId: string;
|
|
104
|
+
subflowName: string | undefined;
|
|
105
|
+
subflowMountOptions: SubflowMountOptions | undefined;
|
|
106
|
+
};
|
|
107
|
+
/** Dynamic fork children (replaces the built node's children for this run). */
|
|
108
|
+
children?: StageNode<TOut, TScope>[];
|
|
109
|
+
/** Dynamic output-based selector accompanying dynamic children. */
|
|
110
|
+
nextNodeSelector?: Selector;
|
|
111
|
+
}
|
|
81
112
|
export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
82
113
|
private readonly root;
|
|
83
114
|
private stageMap;
|
|
@@ -111,6 +142,21 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
|
111
142
|
* resolver before another traverser has finished using it.
|
|
112
143
|
*/
|
|
113
144
|
private readonly resolvedLazySubflows;
|
|
145
|
+
/**
|
|
146
|
+
* Per-traverser overlay of dynamic StageNode returns, keyed by `node.id`.
|
|
147
|
+
* Phase 4 writes patches HERE instead of mutating the shared built-chart
|
|
148
|
+
* node objects (same isolation convention as `resolvedLazySubflows`).
|
|
149
|
+
* All engine reads of the patched fields go through the `eff*` accessors
|
|
150
|
+
* below. The map dies with the traverser — one run, one overlay — so a
|
|
151
|
+
* fresh executor over the same built chart always sees the original graph.
|
|
152
|
+
*
|
|
153
|
+
* Keyed by the node OBJECT (WeakMap), not `node.id`: a dynamic child that
|
|
154
|
+
* reuses a built node's id must NOT make the built node inherit the patch
|
|
155
|
+
* (id-keyed lookup caused phantom double-execution). `patchCount` is the
|
|
156
|
+
* fast-path check — WeakMap has no `size`.
|
|
157
|
+
*/
|
|
158
|
+
private readonly dynamicPatches;
|
|
159
|
+
private patchCount;
|
|
114
160
|
/**
|
|
115
161
|
* Recursion depth counter for executeNode.
|
|
116
162
|
* Each recursive executeNode call increments this; decrements on exit (try/finally).
|
|
@@ -203,6 +249,24 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
|
203
249
|
*/
|
|
204
250
|
private buildNodeIdMap;
|
|
205
251
|
private getStageFn;
|
|
252
|
+
private getPatch;
|
|
253
|
+
private getOrCreatePatch;
|
|
254
|
+
/** Effective children: dynamic patch first, then the built node's children. */
|
|
255
|
+
private effChildren;
|
|
256
|
+
/** Effective output-based selector: dynamic patch first, then the built node's. */
|
|
257
|
+
private effSelector;
|
|
258
|
+
/** Effective subflow-root marker (true when a dynamic subflow was patched on). */
|
|
259
|
+
private effIsSubflowRoot;
|
|
260
|
+
/** Effective subflow id (patched verbatim by a dynamic subflow return). */
|
|
261
|
+
private effSubflowId;
|
|
262
|
+
/**
|
|
263
|
+
* Materialize the effective view of a node — field-identical to what the
|
|
264
|
+
* pre-overlay code produced by mutating the shared node. Used where a node
|
|
265
|
+
* is handed to a helper executor (NodeResolver / SubflowExecutor /
|
|
266
|
+
* ChildrenExecutor) so helpers never read stale built fields. Returns the
|
|
267
|
+
* node itself (no allocation) when it carries no patch.
|
|
268
|
+
*/
|
|
269
|
+
private effNode;
|
|
206
270
|
private executeStage;
|
|
207
271
|
/**
|
|
208
272
|
* Pre-order DFS traversal — the core algorithm.
|
package/package.json
CHANGED