footprintjs 5.0.0 → 6.1.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/CLAUDE.md +51 -8
- package/README.md +1 -0
- package/dist/advanced.js +2 -3
- package/dist/esm/advanced.js +2 -2
- package/dist/esm/index.js +2 -2
- package/dist/esm/lib/builder/FlowChartBuilder.js +552 -103
- package/dist/esm/lib/builder/index.js +2 -2
- package/dist/esm/lib/builder/structure/StructureRecorder.js +158 -0
- package/dist/esm/lib/builder/structure/StructureRecorderDispatcher.js +171 -0
- package/dist/esm/lib/builder/types.js +1 -1
- package/dist/esm/lib/engine/errors/errorInfo.js +4 -3
- package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +14 -3
- package/dist/esm/lib/engine/handlers/DeciderHandler.js +5 -7
- package/dist/esm/lib/engine/handlers/RuntimeStructureManager.js +2 -2
- package/dist/esm/lib/engine/handlers/SelectorHandler.js +13 -7
- package/dist/esm/lib/engine/handlers/SubflowExecutor.js +7 -1
- package/dist/esm/lib/engine/handlers/index.js +1 -3
- package/dist/esm/lib/engine/handlers/types.js +2 -2
- package/dist/esm/lib/engine/index.js +1 -1
- package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +12 -2
- package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +17 -3
- package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +8 -1
- package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
- package/dist/esm/lib/engine/narrative/index.js +1 -1
- package/dist/esm/lib/engine/narrative/types.js +1 -1
- package/dist/esm/lib/engine/runtimeStageId.js +64 -2
- package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +35 -57
- package/dist/esm/lib/engine/types.js +1 -1
- package/dist/esm/lib/engine/walkSubflowSpec.js +168 -0
- package/dist/esm/lib/recorder/CombinedRecorder.js +8 -1
- package/dist/esm/lib/runner/FlowChartExecutor.js +23 -17
- package/dist/esm/lib/runner/getSubtreeSnapshot.js +3 -2
- package/dist/esm/trace.js +3 -2
- package/dist/index.js +3 -2
- package/dist/lib/builder/FlowChartBuilder.js +554 -104
- package/dist/lib/builder/index.js +3 -2
- package/dist/lib/builder/structure/StructureRecorder.js +159 -0
- package/dist/lib/builder/structure/StructureRecorderDispatcher.js +175 -0
- package/dist/lib/builder/types.js +1 -1
- package/dist/lib/engine/errors/errorInfo.js +4 -3
- package/dist/lib/engine/handlers/ChildrenExecutor.js +14 -3
- package/dist/lib/engine/handlers/DeciderHandler.js +5 -7
- package/dist/lib/engine/handlers/RuntimeStructureManager.js +2 -2
- package/dist/lib/engine/handlers/SelectorHandler.js +13 -7
- package/dist/lib/engine/handlers/SubflowExecutor.js +7 -1
- package/dist/lib/engine/handlers/index.js +2 -5
- package/dist/lib/engine/handlers/types.js +2 -2
- package/dist/lib/engine/index.js +1 -1
- package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +12 -2
- package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +17 -3
- package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +8 -1
- package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
- package/dist/lib/engine/narrative/index.js +1 -1
- package/dist/lib/engine/narrative/types.js +1 -1
- package/dist/lib/engine/runtimeStageId.js +66 -3
- package/dist/lib/engine/traversal/FlowchartTraverser.js +35 -57
- package/dist/lib/engine/types.js +1 -1
- package/dist/lib/engine/walkSubflowSpec.js +172 -0
- package/dist/lib/recorder/CombinedRecorder.js +8 -1
- package/dist/lib/runner/FlowChartExecutor.js +23 -17
- package/dist/lib/runner/getSubtreeSnapshot.js +3 -2
- package/dist/trace.js +5 -2
- package/dist/types/advanced.d.ts +4 -4
- package/dist/types/index.d.ts +4 -2
- package/dist/types/lib/builder/FlowChartBuilder.d.ts +202 -17
- package/dist/types/lib/builder/index.d.ts +3 -2
- package/dist/types/lib/builder/structure/StructureRecorder.d.ts +349 -0
- package/dist/types/lib/builder/structure/StructureRecorderDispatcher.d.ts +77 -0
- package/dist/types/lib/builder/types.d.ts +39 -6
- package/dist/types/lib/engine/errors/errorInfo.d.ts +3 -2
- package/dist/types/lib/engine/handlers/ChildrenExecutor.d.ts +1 -1
- package/dist/types/lib/engine/handlers/DeciderHandler.d.ts +3 -3
- package/dist/types/lib/engine/handlers/RuntimeStructureManager.d.ts +1 -1
- package/dist/types/lib/engine/handlers/SelectorHandler.d.ts +2 -2
- package/dist/types/lib/engine/handlers/index.d.ts +1 -2
- package/dist/types/lib/engine/handlers/types.d.ts +2 -9
- package/dist/types/lib/engine/index.d.ts +1 -1
- package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +4 -2
- package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +1 -0
- package/dist/types/lib/engine/narrative/index.d.ts +1 -1
- package/dist/types/lib/engine/narrative/types.d.ts +59 -2
- package/dist/types/lib/engine/runtimeStageId.d.ts +57 -1
- package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +1 -6
- package/dist/types/lib/engine/types.d.ts +19 -26
- package/dist/types/lib/engine/walkSubflowSpec.d.ts +95 -0
- package/dist/types/lib/runner/FlowChartExecutor.d.ts +4 -16
- package/dist/types/trace.d.ts +3 -1
- package/package.json +6 -4
- package/dist/esm/lib/engine/handlers/ExtractorRunner.js +0 -122
- package/dist/lib/engine/handlers/ExtractorRunner.js +0 -126
- package/dist/types/lib/engine/handlers/ExtractorRunner.d.ts +0 -41
|
@@ -5,7 +5,7 @@ export { NullControlFlowNarrativeGenerator } from './NullControlFlowNarrativeGen
|
|
|
5
5
|
export type { IControlFlowNarrative } from './types.js';
|
|
6
6
|
export { FlowRecorderDispatcher } from './FlowRecorderDispatcher.js';
|
|
7
7
|
export { NarrativeFlowRecorder } from './NarrativeFlowRecorder.js';
|
|
8
|
-
export type { FlowBreakEvent, FlowDecisionEvent, FlowErrorEvent, FlowForkEvent, FlowLoopEvent, FlowNextEvent, FlowRecorder, FlowSelectedEvent, FlowStageEvent, FlowSubflowEvent, } from './types.js';
|
|
8
|
+
export type { FlowBreakEvent, FlowDecisionEvent, FlowErrorEvent, FlowForkEvent, FlowLoopEvent, FlowNextEvent, FlowRecorder, FlowRunEvent, FlowRunFailedEvent, FlowSelectedEvent, FlowStageEvent, FlowSubflowEvent, } from './types.js';
|
|
9
9
|
export { AdaptiveNarrativeFlowRecorder } from './recorders/AdaptiveNarrativeFlowRecorder.js';
|
|
10
10
|
export { MilestoneNarrativeFlowRecorder } from './recorders/MilestoneNarrativeFlowRecorder.js';
|
|
11
11
|
export { ProgressiveNarrativeFlowRecorder } from './recorders/ProgressiveNarrativeFlowRecorder.js';
|
|
@@ -13,9 +13,25 @@ import type { StructuredErrorInfo } from '../errors/errorInfo.js';
|
|
|
13
13
|
* Uses Null Object pattern: NullControlFlowNarrativeGenerator satisfies this
|
|
14
14
|
* interface with empty methods for zero-cost disabled path.
|
|
15
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* The kind of stage that completed. Lets consumers route uniform
|
|
18
|
+
* "did this stage execute" handling without a side-table lookup into
|
|
19
|
+
* the chart spec. Required on every `onStageExecuted` event since
|
|
20
|
+
* proposal #003 unified the event to fire for ALL stage kinds.
|
|
21
|
+
*/
|
|
22
|
+
export type StageType = 'linear' | 'decider' | 'fork' | 'selector' | 'subflow-mount';
|
|
16
23
|
export interface IControlFlowNarrative {
|
|
17
|
-
/**
|
|
18
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Called when a stage completes its main work. Fires for ALL stage
|
|
26
|
+
* kinds (`'linear'`, `'decider'`, `'fork'`, `'selector'`, `'subflow-mount'`)
|
|
27
|
+
* AFTER the corresponding specialized event (`onDecision`, `onFork`,
|
|
28
|
+
* `onSelected`, `onSubflowEntry`). For linear stages, fires after
|
|
29
|
+
* the stage function returns.
|
|
30
|
+
*
|
|
31
|
+
* Consumers tracking "did this stage run?" use this event uniformly
|
|
32
|
+
* and switch on `stageType` for kind-specific work.
|
|
33
|
+
*/
|
|
34
|
+
onStageExecuted(stageName: string, description: string | undefined, traversalContext: TraversalContext | undefined, stageType: StageType): void;
|
|
19
35
|
/** Called on linear continuation from one stage to the next. */
|
|
20
36
|
onNext(fromStage: string, toStage: string, description?: string, traversalContext?: TraversalContext): void;
|
|
21
37
|
/** Called when a decider selects a branch. Most valuable for LLM context. */
|
|
@@ -61,6 +77,22 @@ export interface IControlFlowNarrative {
|
|
|
61
77
|
* NOT fire this event — they fire `onSubflowExit`.
|
|
62
78
|
*/
|
|
63
79
|
onRunEnd(output: unknown, traversalContext?: TraversalContext): void;
|
|
80
|
+
/**
|
|
81
|
+
* Called once per top-level `executor.run()` when traversal throws a
|
|
82
|
+
* NON-pause error, BEFORE the exception propagates to `run()`'s caller.
|
|
83
|
+
*
|
|
84
|
+
* This is the TERMINAL counterpart to `onRunEnd` — it closes the run
|
|
85
|
+
* boundary symmetrically so every `onRunStart` is followed by exactly
|
|
86
|
+
* one of `onRunEnd` (clean) or `onRunFailed` (error). Without it, a
|
|
87
|
+
* live monitor sees `onRunStart` then silence on a failed run and can't
|
|
88
|
+
* tell "still running" from "crashed."
|
|
89
|
+
*
|
|
90
|
+
* Errors STILL throw — this is the observable terminal signal, not a
|
|
91
|
+
* recovery mechanism (no routing, no error-subflow). Pause is NOT an
|
|
92
|
+
* error, so it does not fire this. Subflow-traversers do NOT fire it;
|
|
93
|
+
* their errors propagate to the parent and surface at the top level.
|
|
94
|
+
*/
|
|
95
|
+
onRunFailed(error: StructuredErrorInfo, traversalContext?: TraversalContext): void;
|
|
64
96
|
/** Returns accumulated narrative sentences in execution order. */
|
|
65
97
|
getSentences(): string[];
|
|
66
98
|
}
|
|
@@ -115,6 +147,12 @@ export interface FlowStageEvent {
|
|
|
115
147
|
description?: string;
|
|
116
148
|
/** Traversal context from the engine — read-only, set by traverser. */
|
|
117
149
|
traversalContext?: TraversalContext;
|
|
150
|
+
/**
|
|
151
|
+
* Which kind of stage completed. The engine fires `onStageExecuted`
|
|
152
|
+
* uniformly for every stage kind (proposal #003); consumers route by
|
|
153
|
+
* `stageType` without a chart-spec lookup.
|
|
154
|
+
*/
|
|
155
|
+
stageType: StageType;
|
|
118
156
|
}
|
|
119
157
|
/** Event passed to FlowRecorder.onNext. */
|
|
120
158
|
export interface FlowNextEvent {
|
|
@@ -242,6 +280,18 @@ export interface FlowRunEvent {
|
|
|
242
280
|
payload?: unknown;
|
|
243
281
|
traversalContext?: TraversalContext;
|
|
244
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Event passed to FlowRecorder.onRunFailed — the TERMINAL failure boundary
|
|
285
|
+
* for a top-level run. Mirror of `onRunEnd` for the error path: it fires
|
|
286
|
+
* once, during traversal, when the run throws a non-pause error, before
|
|
287
|
+
* the exception propagates. Carries the structured error (field-level
|
|
288
|
+
* issues, codes) rather than a flattened string.
|
|
289
|
+
*/
|
|
290
|
+
export interface FlowRunFailedEvent {
|
|
291
|
+
/** Structured details of the error that terminated the run. */
|
|
292
|
+
structuredError: StructuredErrorInfo;
|
|
293
|
+
traversalContext?: TraversalContext;
|
|
294
|
+
}
|
|
245
295
|
/**
|
|
246
296
|
* FlowRecorder — Pluggable observer for control flow events.
|
|
247
297
|
*
|
|
@@ -287,6 +337,13 @@ export interface FlowRecorder {
|
|
|
287
337
|
* pause (the run didn't end) or uncaught error.
|
|
288
338
|
*/
|
|
289
339
|
onRunEnd?(event: FlowRunEvent): void;
|
|
340
|
+
/**
|
|
341
|
+
* Called once per top-level `executor.run()` when the run throws a
|
|
342
|
+
* non-pause error, BEFORE the exception propagates. The TERMINAL
|
|
343
|
+
* counterpart to `onRunEnd` — lets a monitor close the run boundary on
|
|
344
|
+
* failure instead of waiting forever. NOT fired on pause.
|
|
345
|
+
*/
|
|
346
|
+
onRunFailed?(event: FlowRunFailedEvent): void;
|
|
290
347
|
/** Called before each run to reset per-run state. Implement for stateful recorders. */
|
|
291
348
|
clear?(): void;
|
|
292
349
|
/** Optional: expose collected data for inclusion in snapshots. */
|
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
* - Human-readable ('sf-tools/execute-tool-calls#8')
|
|
15
15
|
* - Parseable (split on '#' for stageId and index, split stageId on '/' for subflow path)
|
|
16
16
|
*
|
|
17
|
+
* Naming-collision warning
|
|
18
|
+
* ────────────────────────
|
|
19
|
+
* The parsed-output `.stageId` field below is the LOCAL form (segment
|
|
20
|
+
* after the last '/'). This is NOT the same as `spec.id` / `node.id`
|
|
21
|
+
* for subflow-nested stages, which carry the FULL prefixed form
|
|
22
|
+
* (`'sf-tools/execute-tool-calls'`). To compare safely, use
|
|
23
|
+
* `splitStageId(spec.id)` to decompose the prefixed form the same
|
|
24
|
+
* way `parseRuntimeStageId` decomposes a runtimeStageId.
|
|
25
|
+
*
|
|
17
26
|
* @example
|
|
18
27
|
* ```
|
|
19
28
|
* buildRuntimeStageId('call-llm', 5) // 'call-llm#5'
|
|
@@ -29,12 +38,59 @@
|
|
|
29
38
|
* consumers constructing IDs from parsed components (round-trip via parseRuntimeStageId).
|
|
30
39
|
*/
|
|
31
40
|
export declare function buildRuntimeStageId(stageId: string, executionIndex: number, subflowPath?: string): string;
|
|
32
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Parse a runtimeStageId into its components.
|
|
43
|
+
*
|
|
44
|
+
* IMPORTANT — naming collision: the returned `stageId` is the LOCAL
|
|
45
|
+
* form (the segment between the last '/' and the '#'). This is NOT
|
|
46
|
+
* the same as `spec.id` or `node.id` for subflow-nested stages,
|
|
47
|
+
* which contain the FULL prefixed form.
|
|
48
|
+
*
|
|
49
|
+
* parseRuntimeStageId('sf-tools/execute-tool-calls#8').stageId
|
|
50
|
+
* // → 'execute-tool-calls' (LOCAL)
|
|
51
|
+
*
|
|
52
|
+
* node.id // (post-mount, in a spec that contains subflows)
|
|
53
|
+
* // → 'sf-tools/execute-tool-calls' (FULL prefixed)
|
|
54
|
+
*
|
|
55
|
+
* To compare these two safely, use `splitStageId(node.id)` to get
|
|
56
|
+
* the local form, OR reconstruct the full form via
|
|
57
|
+
* `(subflowPath ? subflowPath + '/' : '') + stageId`.
|
|
58
|
+
*/
|
|
33
59
|
export declare function parseRuntimeStageId(runtimeStageId: string): {
|
|
34
60
|
stageId: string;
|
|
35
61
|
executionIndex: number;
|
|
36
62
|
subflowPath: string | undefined;
|
|
37
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Decompose a (possibly prefixed) stage id into its components.
|
|
66
|
+
*
|
|
67
|
+
* Use this when you have an id WITHOUT the `#N` execution suffix and
|
|
68
|
+
* need the local stage name and/or the subflow path. Common sources
|
|
69
|
+
* of such ids:
|
|
70
|
+
* - `spec.id` (post-mount the id includes any subflow prefix)
|
|
71
|
+
* - `CommitBundle.stageId` (post-mount id)
|
|
72
|
+
* - `node.id` from xyflow nodes built off the spec
|
|
73
|
+
* - the segment of `runtimeStageId` BEFORE the `#` (use
|
|
74
|
+
* `parseRuntimeStageId` directly for full runtimeStageId strings)
|
|
75
|
+
*
|
|
76
|
+
* Mirrors the decomposition `parseRuntimeStageId` performs on the
|
|
77
|
+
* stageId portion of a runtimeStageId, so the two helpers stay in
|
|
78
|
+
* lockstep on naming and behavior.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* splitStageId('sf-tools/execute-tool-calls')
|
|
82
|
+
* // → { localStageId: 'execute-tool-calls', subflowPath: 'sf-tools' }
|
|
83
|
+
*
|
|
84
|
+
* splitStageId('execute-tool-calls')
|
|
85
|
+
* // → { localStageId: 'execute-tool-calls', subflowPath: undefined }
|
|
86
|
+
*
|
|
87
|
+
* splitStageId('sf-outer/sf-inner/validate')
|
|
88
|
+
* // → { localStageId: 'validate', subflowPath: 'sf-outer/sf-inner' }
|
|
89
|
+
*/
|
|
90
|
+
export declare function splitStageId(prefixedStageId: string): {
|
|
91
|
+
localStageId: string;
|
|
92
|
+
subflowPath: string | undefined;
|
|
93
|
+
};
|
|
38
94
|
/**
|
|
39
95
|
* Shared mutable counter for execution index.
|
|
40
96
|
* Passed by reference to child traversers (subflows) so they
|
|
@@ -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 {
|
|
24
|
+
import type { IExecutionRuntime, ILogger, ScopeFactory, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, 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>>;
|
|
@@ -32,12 +32,10 @@ export interface TraverserOptions<TOut = any, TScope = any> {
|
|
|
32
32
|
executionEnv?: import('../../engine/types').ExecutionEnv;
|
|
33
33
|
throttlingErrorChecker?: (error: unknown) => boolean;
|
|
34
34
|
streamHandlers?: StreamHandlers;
|
|
35
|
-
extractor?: TraversalExtractor;
|
|
36
35
|
scopeProtectionMode?: ScopeProtectionMode;
|
|
37
36
|
subflows?: Record<string, {
|
|
38
37
|
root: StageNode<TOut, TScope>;
|
|
39
38
|
}>;
|
|
40
|
-
enrichSnapshots?: boolean;
|
|
41
39
|
narrativeEnabled?: boolean;
|
|
42
40
|
buildTimeStructure?: SerializedPipelineStructure;
|
|
43
41
|
logger: ILogger;
|
|
@@ -103,7 +101,6 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
|
103
101
|
private readonly deciderHandler;
|
|
104
102
|
private readonly selectorHandler;
|
|
105
103
|
private readonly structureManager;
|
|
106
|
-
private readonly extractorRunner;
|
|
107
104
|
private readonly narrativeGenerator;
|
|
108
105
|
private readonly flowRecorderDispatcher;
|
|
109
106
|
private subflowResults;
|
|
@@ -194,8 +191,6 @@ export declare class FlowchartTraverser<TOut = any, TScope = any> {
|
|
|
194
191
|
getBranchIds(): string[];
|
|
195
192
|
getRuntimeRoot(): StageNode;
|
|
196
193
|
getSubflowResults(): Map<string, SubflowResult>;
|
|
197
|
-
getExtractedResults<TResult = unknown>(): Map<string, TResult>;
|
|
198
|
-
getExtractorErrors(): ExtractorError[];
|
|
199
194
|
getNarrative(): string[];
|
|
200
195
|
/** Returns the FlowRecorderDispatcher, or undefined if narrative is disabled. */
|
|
201
196
|
getFlowRecorderDispatcher(): FlowRecorderDispatcher | undefined;
|
|
@@ -88,6 +88,25 @@ export interface SubflowMountOptions<TParentScope = any, TSubflowInput = any, TS
|
|
|
88
88
|
* @default ArrayMergeMode.Concat
|
|
89
89
|
*/
|
|
90
90
|
arrayMerge?: ArrayMergeMode;
|
|
91
|
+
/**
|
|
92
|
+
* STRUCTURE-ONLY: the downstream stage id this branch's convergence edge
|
|
93
|
+
* points to, overriding the default "converge at the next linear stage."
|
|
94
|
+
*
|
|
95
|
+
* When a fork/selector/decider branch carries `convergeAt`, its visualized
|
|
96
|
+
* `next` edge is fired to the named stage instead of to the shared
|
|
97
|
+
* convergence node its siblings use. This expresses an unequal-depth merge
|
|
98
|
+
* — e.g. a `tools` slot that bypasses the `messageAPI` assembly node and
|
|
99
|
+
* pairs with its output at `call-llm` (so `call-llm` reads as a 2-parent
|
|
100
|
+
* merge), faithful to the Anthropic wire protocol (system+messages = the
|
|
101
|
+
* message payload; tools = a separate top-level field).
|
|
102
|
+
*
|
|
103
|
+
* **This affects the STRUCTURE/topology graph only** — the runtime is
|
|
104
|
+
* unchanged (the branch's outputs ride shared scope; the named target reads
|
|
105
|
+
* them when it runs). It does NOT introduce a join barrier. The target is a
|
|
106
|
+
* FORWARD stage (added later in the chain), so it is not validated at
|
|
107
|
+
* branch-declaration time.
|
|
108
|
+
*/
|
|
109
|
+
convergeAt?: string;
|
|
91
110
|
/**
|
|
92
111
|
* When `true`, an inner `scope.$break(reason)` call inside this subflow
|
|
93
112
|
* propagates up to the parent — i.e., the parent's `breakFlag` is set
|
|
@@ -323,32 +342,6 @@ export interface RuntimeStructureMetadata {
|
|
|
323
342
|
isLoopReference?: boolean;
|
|
324
343
|
streamId?: string;
|
|
325
344
|
}
|
|
326
|
-
export interface StageSnapshot<TOut = any, TScope = any> {
|
|
327
|
-
node: StageNode<TOut, TScope>;
|
|
328
|
-
context: StageContext;
|
|
329
|
-
stepNumber: number;
|
|
330
|
-
structureMetadata: RuntimeStructureMetadata;
|
|
331
|
-
scopeState?: Record<string, unknown>;
|
|
332
|
-
debugInfo?: {
|
|
333
|
-
logs: Record<string, unknown>;
|
|
334
|
-
errors: Record<string, unknown>;
|
|
335
|
-
metrics: Record<string, unknown>;
|
|
336
|
-
evals: Record<string, unknown>;
|
|
337
|
-
flowMessages?: FlowMessage[];
|
|
338
|
-
};
|
|
339
|
-
stageOutput?: unknown;
|
|
340
|
-
errorInfo?: {
|
|
341
|
-
type: string;
|
|
342
|
-
message: string;
|
|
343
|
-
};
|
|
344
|
-
historyIndex?: number;
|
|
345
|
-
}
|
|
346
|
-
export type TraversalExtractor<TResult = unknown> = (snapshot: StageSnapshot) => TResult | undefined | null;
|
|
347
|
-
export interface ExtractorError {
|
|
348
|
-
stagePath: string;
|
|
349
|
-
message: string;
|
|
350
|
-
error: unknown;
|
|
351
|
-
}
|
|
352
345
|
export type NodeResultType = {
|
|
353
346
|
id: string;
|
|
354
347
|
result: unknown;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* walkSubflowSpec — yield the structural shape of a subflow spec as a
|
|
3
|
+
* flat ordered stream of items, with `subflowPath` already composed
|
|
4
|
+
* for nested subflows.
|
|
5
|
+
*
|
|
6
|
+
* This is the public contract for traversing the structure delivered
|
|
7
|
+
* via `StructureSubflowMountedEvent.subflowSpec`. Item shapes mirror
|
|
8
|
+
* the corresponding Structure event payloads so consumers can route
|
|
9
|
+
* walker items through the same handlers they use for live events.
|
|
10
|
+
*
|
|
11
|
+
* Walker contract (LOCKED):
|
|
12
|
+
* 1. AUTO-RECURSE by default into nested subflows, with composed
|
|
13
|
+
* paths (`parent/child/...`). Pass `{ recurse: false }` to walk
|
|
14
|
+
* only one level.
|
|
15
|
+
* 2. ENTRY-STAGE MARKER FIRST: for each subflow (top-level and
|
|
16
|
+
* nested), yields a `{ kind: 'subflow-start', ... }` item BEFORE
|
|
17
|
+
* any stage/edge items from that subflow. Lets consumers draw the
|
|
18
|
+
* boundary edge from the mount node to the entry stage.
|
|
19
|
+
* 3. COMPOSED PATHS: nested subflows get `parentPath + '/' + localId`.
|
|
20
|
+
* Top-level mount paths are local-only (`'auth'`, NOT `'__root__/auth'`).
|
|
21
|
+
* 4. SHAPE MIRRORING: stage/edge/loop items have the same payload
|
|
22
|
+
* shape as Structure events, with `subflowPath` added.
|
|
23
|
+
* 5. SOURCE DISCRIMINATOR: every walker item carries `source: 'walker'`
|
|
24
|
+
* (Structure events do NOT). Lets consumers distinguish event vs
|
|
25
|
+
* walker in logs/debuggers while still sharing handler code paths.
|
|
26
|
+
* 6. STAGE-ID PREFIXING: stage IDs in nested subflows are already
|
|
27
|
+
* prefixed by the spec (e.g. `'auth/verify/check'`). Walker
|
|
28
|
+
* preserves this; `subflowPath` field is redundant-but-explicit.
|
|
29
|
+
*/
|
|
30
|
+
import type { SerializedPipelineStructure } from '../builder/types.js';
|
|
31
|
+
export interface WalkerOptions {
|
|
32
|
+
/** Auto-recurse into nested subflows (default: true). When false,
|
|
33
|
+
* nested subflow items are yielded but their internals are not
|
|
34
|
+
* traversed. */
|
|
35
|
+
recurse?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export type WalkerItem = {
|
|
38
|
+
kind: 'subflow-start';
|
|
39
|
+
stageId: string;
|
|
40
|
+
subflowPath: string;
|
|
41
|
+
source: 'walker';
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'stage';
|
|
44
|
+
stageId: string;
|
|
45
|
+
name: string;
|
|
46
|
+
type: NonNullable<SerializedPipelineStructure['type']>;
|
|
47
|
+
isPausable?: boolean;
|
|
48
|
+
spec: SerializedPipelineStructure;
|
|
49
|
+
subflowPath: string;
|
|
50
|
+
source: 'walker';
|
|
51
|
+
} | {
|
|
52
|
+
kind: 'edge';
|
|
53
|
+
from: string;
|
|
54
|
+
to: string;
|
|
55
|
+
edgeKind: 'next' | 'fork-branch' | 'decision-branch';
|
|
56
|
+
label?: string;
|
|
57
|
+
subflowPath: string;
|
|
58
|
+
source: 'walker';
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'loop';
|
|
61
|
+
from: string;
|
|
62
|
+
to: string;
|
|
63
|
+
subflowPath: string;
|
|
64
|
+
source: 'walker';
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'subflow';
|
|
67
|
+
mountStageId: string;
|
|
68
|
+
subflowId: string;
|
|
69
|
+
subflowName: string;
|
|
70
|
+
subflowSpec: SerializedPipelineStructure;
|
|
71
|
+
subflowPath: string;
|
|
72
|
+
source: 'walker';
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Walk a subflow spec, yielding its structure as flat ordered items.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { walkSubflowSpec } from 'footprintjs/trace';
|
|
80
|
+
*
|
|
81
|
+
* onSubflowMounted(event) {
|
|
82
|
+
* if (!event.subflowSpec) return; // lazy mount — no spec yet
|
|
83
|
+
* for (const item of walkSubflowSpec(event.subflowSpec, event.subflowPath)) {
|
|
84
|
+
* switch (item.kind) {
|
|
85
|
+
* case 'subflow-start': break; // entry boundary
|
|
86
|
+
* case 'stage': break; // inner stage
|
|
87
|
+
* case 'edge': break; // inner edge
|
|
88
|
+
* case 'loop': break; // inner loop back-edge
|
|
89
|
+
* case 'subflow': break; // nested mount marker
|
|
90
|
+
* }
|
|
91
|
+
* }
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function walkSubflowSpec(spec: SerializedPipelineStructure, subflowPath: string, options?: WalkerOptions): Generator<WalkerItem, void, void>;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* const executor = new FlowChartExecutor(chart);
|
|
10
10
|
*
|
|
11
11
|
* // Options-object form (preferred when you need to customize behavior):
|
|
12
|
-
* const executor = new FlowChartExecutor(chart, { scopeFactory: myFactory
|
|
12
|
+
* const executor = new FlowChartExecutor(chart, { scopeFactory: myFactory });
|
|
13
13
|
*
|
|
14
14
|
* // 2-param form (accepts a ScopeFactory directly, for backward compatibility):
|
|
15
15
|
* const executor = new FlowChartExecutor(chart, myFactory);
|
|
@@ -21,7 +21,7 @@ import type { CombinedNarrativeRecorderOptions } from '../engine/narrative/Combi
|
|
|
21
21
|
import type { CombinedNarrativeEntry } from '../engine/narrative/narrativeTypes.js';
|
|
22
22
|
import type { ManifestEntry } from '../engine/narrative/recorders/ManifestFlowRecorder.js';
|
|
23
23
|
import type { FlowRecorder } from '../engine/narrative/types.js';
|
|
24
|
-
import { type ExecutorResult, type
|
|
24
|
+
import { type ExecutorResult, type RunOptions, type ScopeFactory, type SerializedPipelineStructure, type StageNode, type StreamHandlers, type SubflowResult } from '../engine/types.js';
|
|
25
25
|
import type { FlowchartCheckpoint } from '../pause/types.js';
|
|
26
26
|
import type { CombinedRecorder } from '../recorder/CombinedRecorder.js';
|
|
27
27
|
import type { EmitRecorder } from '../recorder/EmitRecorder.js';
|
|
@@ -34,7 +34,7 @@ import { type RuntimeSnapshot } from './ExecutionRuntime.js';
|
|
|
34
34
|
* ```typescript
|
|
35
35
|
* const ex = new FlowChartExecutor(chart, {
|
|
36
36
|
* scopeFactory: myFactory,
|
|
37
|
-
*
|
|
37
|
+
* defaultValuesForContext: { ... },
|
|
38
38
|
* });
|
|
39
39
|
* ```
|
|
40
40
|
*
|
|
@@ -50,14 +50,6 @@ import { type RuntimeSnapshot } from './ExecutionRuntime.js';
|
|
|
50
50
|
export interface FlowChartExecutorOptions<TScope = any> {
|
|
51
51
|
/** Custom scope factory. Defaults to TypedScope or ScopeFacade auto-detection. */
|
|
52
52
|
scopeFactory?: ScopeFactory<TScope>;
|
|
53
|
-
/**
|
|
54
|
-
* Attach a per-stage scope snapshot to each extractor result. When `true`, the
|
|
55
|
-
* extraction callback receives the full shared state at the point that stage
|
|
56
|
-
* committed — useful for debugging multi-stage state transitions. Defaults to
|
|
57
|
-
* `false` (no scope snapshot attached). Can also be set on the chart via
|
|
58
|
-
* `flowChart(...).enrichSnapshots(true)`.
|
|
59
|
-
*/
|
|
60
|
-
enrichSnapshots?: boolean;
|
|
61
53
|
/**
|
|
62
54
|
* Default values pre-populated into the shared context before **each** stage
|
|
63
55
|
* (re-applied every stage, acting as baseline defaults).
|
|
@@ -121,7 +113,7 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
121
113
|
*
|
|
122
114
|
* **Options object form** (preferred):
|
|
123
115
|
* ```typescript
|
|
124
|
-
* new FlowChartExecutor(chart, { scopeFactory,
|
|
116
|
+
* new FlowChartExecutor(chart, { scopeFactory, defaultValuesForContext })
|
|
125
117
|
* ```
|
|
126
118
|
*
|
|
127
119
|
* **2-param form** (also supported):
|
|
@@ -419,10 +411,6 @@ export declare class FlowChartExecutor<TOut = any, TScope = any> {
|
|
|
419
411
|
getRuntimeStructure(): SerializedPipelineStructure | undefined;
|
|
420
412
|
/** @internal */
|
|
421
413
|
getSubflowResults(): Map<string, SubflowResult>;
|
|
422
|
-
/** @internal */
|
|
423
|
-
getExtractedResults<TResult = unknown>(): Map<string, TResult>;
|
|
424
|
-
/** @internal */
|
|
425
|
-
getExtractorErrors(): ExtractorError[];
|
|
426
414
|
/**
|
|
427
415
|
* Returns the subflow manifest from an attached ManifestFlowRecorder.
|
|
428
416
|
* Returns empty array if no ManifestFlowRecorder is attached.
|
package/dist/types/trace.d.ts
CHANGED
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
export type { ExecutionCounter } from './lib/engine/runtimeStageId.js';
|
|
24
|
-
export { buildRuntimeStageId, createExecutionCounter, parseRuntimeStageId } from './lib/engine/runtimeStageId.js';
|
|
24
|
+
export { buildRuntimeStageId, createExecutionCounter, parseRuntimeStageId, splitStageId, } from './lib/engine/runtimeStageId.js';
|
|
25
|
+
export type { WalkerItem, WalkerOptions } from './lib/engine/walkSubflowSpec.js';
|
|
26
|
+
export { walkSubflowSpec } from './lib/engine/walkSubflowSpec.js';
|
|
25
27
|
export { findCommit, findCommits, findLastWriter } from './lib/memory/commitLogUtils.js';
|
|
26
28
|
export type { CausalChainOptions, CausalNode, KeysReadLookup } from './lib/memory/backtrack.js';
|
|
27
29
|
export { causalChain, flattenCausalDAG, formatCausalChain } from './lib/memory/backtrack.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "footprintjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Explainable backend flows — automatic causal traces, decision evidence, and MCP tool generation for AI agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Sanjay Krishna Anbalagan",
|
|
@@ -55,7 +55,9 @@
|
|
|
55
55
|
"format": "prettier --config .prettierrc.js --list-different '{src,test}/**/*.ts'",
|
|
56
56
|
"bench": "npx tsx bench/run.ts",
|
|
57
57
|
"docs": "typedoc",
|
|
58
|
-
"docs:serve": "typedoc && npx serve docs"
|
|
58
|
+
"docs:serve": "typedoc && npx serve docs",
|
|
59
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=json-summary --coverage.reporter=text-summary --coverage.reportsDirectory=coverage",
|
|
60
|
+
"coverage:badge": "npm run test:coverage; node scripts/coverage-badge.mjs"
|
|
59
61
|
},
|
|
60
62
|
"files": [
|
|
61
63
|
"dist/**/*",
|
|
@@ -113,6 +115,7 @@
|
|
|
113
115
|
"devDependencies": {
|
|
114
116
|
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
115
117
|
"@typescript-eslint/parser": "^5.61.0",
|
|
118
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
116
119
|
"concurrently": "^8.0.1",
|
|
117
120
|
"eslint": "^8.44.0",
|
|
118
121
|
"eslint-config-prettier": "^6.15.0",
|
|
@@ -129,6 +132,5 @@
|
|
|
129
132
|
"typedoc": "^0.28.17",
|
|
130
133
|
"typescript": "~5.4.5",
|
|
131
134
|
"vitest": "^4.0.18"
|
|
132
|
-
}
|
|
133
|
-
"dependencies": {}
|
|
135
|
+
}
|
|
134
136
|
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ExtractorRunner — Per-stage snapshot extraction.
|
|
3
|
-
*
|
|
4
|
-
* Coordinates traversal extractor invocations: step counting,
|
|
5
|
-
* snapshot enrichment, error collection, and result storage.
|
|
6
|
-
*/
|
|
7
|
-
import { computeNodeType } from './RuntimeStructureManager.js';
|
|
8
|
-
export class ExtractorRunner {
|
|
9
|
-
extractor;
|
|
10
|
-
enrichSnapshots;
|
|
11
|
-
executionRuntime;
|
|
12
|
-
logger;
|
|
13
|
-
extractedResults = new Map();
|
|
14
|
-
extractorErrors = [];
|
|
15
|
-
stepCounter = 0;
|
|
16
|
-
/** Current subflow context for metadata propagation. Set/cleared during subflow execution. */
|
|
17
|
-
currentSubflowId;
|
|
18
|
-
/** Current fork context for metadata propagation. Set/cleared during parallel children execution. */
|
|
19
|
-
currentForkId;
|
|
20
|
-
constructor(extractor, enrichSnapshots, executionRuntime, logger) {
|
|
21
|
-
this.extractor = extractor;
|
|
22
|
-
this.enrichSnapshots = enrichSnapshots;
|
|
23
|
-
this.executionRuntime = executionRuntime;
|
|
24
|
-
this.logger = logger;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Call the extractor for a stage and store the result.
|
|
28
|
-
* Increments stepCounter (1-based) before creating snapshot.
|
|
29
|
-
*/
|
|
30
|
-
callExtractor(node, context, stagePath, stageOutput, errorInfo) {
|
|
31
|
-
if (!this.extractor)
|
|
32
|
-
return;
|
|
33
|
-
this.stepCounter++;
|
|
34
|
-
try {
|
|
35
|
-
const snapshot = {
|
|
36
|
-
node,
|
|
37
|
-
context,
|
|
38
|
-
stepNumber: this.stepCounter,
|
|
39
|
-
structureMetadata: this.buildStructureMetadata(node),
|
|
40
|
-
};
|
|
41
|
-
if (this.enrichSnapshots) {
|
|
42
|
-
try {
|
|
43
|
-
snapshot.scopeState = { ...this.executionRuntime.globalStore.getState() };
|
|
44
|
-
snapshot.debugInfo = {
|
|
45
|
-
logs: { ...context.debug.logContext },
|
|
46
|
-
errors: { ...context.debug.errorContext },
|
|
47
|
-
metrics: { ...context.debug.metricContext },
|
|
48
|
-
evals: { ...context.debug.evalContext },
|
|
49
|
-
};
|
|
50
|
-
if (context.debug.flowMessages.length > 0) {
|
|
51
|
-
snapshot.debugInfo.flowMessages = [...context.debug.flowMessages];
|
|
52
|
-
}
|
|
53
|
-
snapshot.stageOutput = stageOutput;
|
|
54
|
-
if (errorInfo) {
|
|
55
|
-
snapshot.errorInfo = errorInfo;
|
|
56
|
-
}
|
|
57
|
-
snapshot.historyIndex = this.executionRuntime.executionHistory.list().length;
|
|
58
|
-
}
|
|
59
|
-
catch (enrichError) {
|
|
60
|
-
this.logger.warn(`Enrichment error at stage '${stagePath}':`, { error: enrichError });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
const result = this.extractor(snapshot);
|
|
64
|
-
if (result !== undefined && result !== null) {
|
|
65
|
-
this.extractedResults.set(stagePath, result);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
this.logger.error(`Extractor error at stage '${stagePath}':`, { error });
|
|
70
|
-
this.extractorErrors.push({
|
|
71
|
-
stagePath,
|
|
72
|
-
message: error?.message ?? String(error),
|
|
73
|
-
error,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Generate the stage path for extractor results.
|
|
79
|
-
* Uses node.id combined with branchPath.
|
|
80
|
-
*/
|
|
81
|
-
getStagePath(node, branchPath, contextStageName) {
|
|
82
|
-
const baseName = node.id;
|
|
83
|
-
const nodeId = contextStageName && contextStageName !== node.name ? contextStageName : baseName;
|
|
84
|
-
if (!branchPath)
|
|
85
|
-
return nodeId;
|
|
86
|
-
return `${branchPath}.${nodeId}`;
|
|
87
|
-
}
|
|
88
|
-
buildStructureMetadata(node) {
|
|
89
|
-
const metadata = {
|
|
90
|
-
type: computeNodeType(node),
|
|
91
|
-
};
|
|
92
|
-
if (node.isSubflowRoot) {
|
|
93
|
-
metadata.isSubflowRoot = true;
|
|
94
|
-
metadata.subflowId = node.subflowId;
|
|
95
|
-
metadata.subflowName = node.subflowName;
|
|
96
|
-
}
|
|
97
|
-
else if (this.currentSubflowId) {
|
|
98
|
-
metadata.subflowId = this.currentSubflowId;
|
|
99
|
-
}
|
|
100
|
-
if (this.currentForkId) {
|
|
101
|
-
metadata.isParallelChild = true;
|
|
102
|
-
metadata.parallelGroupId = this.currentForkId;
|
|
103
|
-
}
|
|
104
|
-
if (node.isStreaming) {
|
|
105
|
-
metadata.streamId = node.streamId;
|
|
106
|
-
}
|
|
107
|
-
const hasDynamicChildren = Boolean(node.children?.length && !node.nextNodeSelector && node.fn);
|
|
108
|
-
if (hasDynamicChildren) {
|
|
109
|
-
metadata.isDynamic = true;
|
|
110
|
-
}
|
|
111
|
-
return metadata;
|
|
112
|
-
}
|
|
113
|
-
/** Returns extracted results collected during execution. */
|
|
114
|
-
getExtractedResults() {
|
|
115
|
-
return this.extractedResults;
|
|
116
|
-
}
|
|
117
|
-
/** Returns errors encountered during extraction. */
|
|
118
|
-
getExtractorErrors() {
|
|
119
|
-
return this.extractorErrors;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXh0cmFjdG9yUnVubmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9lbmdpbmUvaGFuZGxlcnMvRXh0cmFjdG9yUnVubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztHQUtHO0FBWUgsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRS9ELE1BQU0sT0FBTyxlQUFlO0lBQ1QsU0FBUyxDQUFzQjtJQUMvQixlQUFlLENBQVU7SUFDekIsZ0JBQWdCLENBQW9CO0lBQ3BDLE1BQU0sQ0FBVTtJQUV6QixnQkFBZ0IsR0FBeUIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNuRCxlQUFlLEdBQXFCLEVBQUUsQ0FBQztJQUN2QyxXQUFXLEdBQUcsQ0FBQyxDQUFDO0lBRXhCLDhGQUE4RjtJQUM5RixnQkFBZ0IsQ0FBVTtJQUUxQixxR0FBcUc7SUFDckcsYUFBYSxDQUFVO0lBRXZCLFlBQ0UsU0FBeUMsRUFDekMsZUFBd0IsRUFDeEIsZ0JBQW1DLEVBQ25DLE1BQWU7UUFFZixJQUFJLENBQUMsU0FBUyxHQUFHLFNBQVMsQ0FBQztRQUMzQixJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztRQUN2QyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUM7UUFDekMsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVEOzs7T0FHRztJQUNILGFBQWEsQ0FDWCxJQUFlLEVBQ2YsT0FBcUIsRUFDckIsU0FBaUIsRUFDakIsV0FBcUIsRUFDckIsU0FBNkM7UUFFN0MsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTO1lBQUUsT0FBTztRQUU1QixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFFbkIsSUFBSSxDQUFDO1lBQ0gsTUFBTSxRQUFRLEdBQWtCO2dCQUM5QixJQUFJO2dCQUNKLE9BQU87Z0JBQ1AsVUFBVSxFQUFFLElBQUksQ0FBQyxXQUFX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KiBFeHRyYWN0b3JSdW5uZXIg4oCUIFBlci1zdGFnZSBzbmFwc2hvdCBleHRyYWN0aW9uLlxuICpcbiAqIENvb3JkaW5hdGVzIHRyYXZlcnNhbCBleHRyYWN0b3IgaW52b2NhdGlvbnM6IHN0ZXAgY291bnRpbmcsXG4gKiBzbmFwc2hvdCBlbnJpY2htZW50LCBlcnJvciBjb2xsZWN0aW9uLCBhbmQgcmVzdWx0IHN0b3JhZ2UuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBTdGFnZUNvbnRleHQgfSBmcm9tICcuLi8uLi9tZW1vcnkvU3RhZ2VDb250ZXh0LmpzJztcbmltcG9ydCB0eXBlIHsgU3RhZ2VOb2RlIH0gZnJvbSAnLi4vZ3JhcGgvU3RhZ2VOb2RlLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgRXh0cmFjdG9yRXJyb3IsXG4gIElFeGVjdXRpb25SdW50aW1lLFxuICBJTG9nZ2VyLFxuICBSdW50aW1lU3RydWN0dXJlTWV0YWRhdGEsXG4gIFN0YWdlU25hcHNob3QsXG4gIFRyYXZlcnNhbEV4dHJhY3Rvcixcbn0gZnJvbSAnLi4vdHlwZXMuanMnO1xuaW1wb3J0IHsgY29tcHV0ZU5vZGVUeXBlIH0gZnJvbSAnLi9SdW50aW1lU3RydWN0dXJlTWFuYWdlci5qcyc7XG5cbmV4cG9ydCBjbGFzcyBFeHRyYWN0b3JSdW5uZXI8VE91dCA9IGFueSwgVFNjb3BlID0gYW55PiB7XG4gIHByaXZhdGUgcmVhZG9ubHkgZXh0cmFjdG9yPzogVHJhdmVyc2FsRXh0cmFjdG9yO1xuICBwcml2YXRlIHJlYWRvbmx5IGVucmljaFNuYXBzaG90czogYm9vbGVhbjtcbiAgcHJpdmF0ZSByZWFkb25seSBleGVjdXRpb25SdW50aW1lOiBJRXhlY3V0aW9uUnVudGltZTtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2dnZXI6IElMb2dnZXI7XG5cbiAgcHJpdmF0ZSBleHRyYWN0ZWRSZXN1bHRzOiBNYXA8c3RyaW5nLCB1bmtub3duPiA9IG5ldyBNYXAoKTtcbiAgcHJpdmF0ZSBleHRyYWN0b3JFcnJvcnM6IEV4dHJhY3RvckVycm9yW10gPSBbXTtcbiAgcHJpdmF0ZSBzdGVwQ291bnRlciA9IDA7XG5cbiAgLyoqIEN1cnJlbnQgc3ViZmxvdyBjb250ZXh0IGZvciBtZXRhZGF0YSBwcm9wYWdhdGlvbi4gU2V0L2NsZWFyZWQgZHVyaW5nIHN1YmZsb3cgZXhlY3V0aW9uLiAqL1xuICBjdXJyZW50U3ViZmxvd0lkPzogc3RyaW5nO1xuXG4gIC8qKiBDdXJyZW50IGZvcmsgY29udGV4dCBmb3IgbWV0YWRhdGEgcHJvcGFnYXRpb24uIFNldC9jbGVhcmVkIGR1cmluZyBwYXJhbGxlbCBjaGlsZHJlbiBleGVjdXRpb24uICovXG4gIGN1cnJlbnRGb3JrSWQ/OiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgZXh0cmFjdG9yOiBUcmF2ZXJzYWxFeHRyYWN0b3IgfCB1bmRlZmluZWQsXG4gICAgZW5yaWNoU25hcHNob3RzOiBib29sZWFuLFxuICAgIGV4ZWN1dGlvblJ1bnRpbWU6IElFeGVjdXRpb25SdW50aW1lLFxuICAgIGxvZ2dlcjogSUxvZ2dlcixcbiAgKSB7XG4gICAgdGhpcy5leHRyYWN0b3IgPSBleHRyYWN0b3I7XG4gICAgdGhpcy5lbnJpY2hTbmFwc2hvdHMgPSBlbnJpY2hTbmFwc2hvdHM7XG4gICAgdGhpcy5leGVjdXRpb25SdW50aW1lID0gZXhlY3V0aW9uUnVudGltZTtcbiAgICB0aGlzLmxvZ2dlciA9IGxvZ2dlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsIHRoZSBleHRyYWN0b3IgZm9yIGEgc3RhZ2UgYW5kIHN0b3JlIHRoZSByZXN1bHQuXG4gICAqIEluY3JlbWVudHMgc3RlcENvdW50ZXIgKDEtYmFzZWQpIGJlZm9yZSBjcmVhdGluZyBzbmFwc2hvdC5cbiAgICovXG4gIGNhbGxFeHRyYWN0b3IoXG4gICAgbm9kZTogU3RhZ2VOb2RlLFxuICAgIGNvbnRleHQ6IFN0YWdlQ29udGV4dCxcbiAgICBzdGFnZVBhdGg6IHN0cmluZyxcbiAgICBzdGFnZU91dHB1dD86IHVua25vd24sXG4gICAgZXJyb3JJbmZvPzogeyB0eXBlOiBzdHJpbmc7IG1lc3NhZ2U6IHN0cmluZyB9LFxuICApOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuZXh0cmFjdG9yKSByZXR1cm47XG5cbiAgICB0aGlzLnN0ZXBDb3VudGVyKys7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3Qgc25hcHNob3Q6IFN0YWdlU25hcHNob3QgPSB7XG4gICAgICAgIG5vZGUsXG4gICAgICAgIGNvbnRleHQsXG4gICAgICAgIHN0ZXBOdW1iZXI6IHRoaXMuc3RlcENvdW50ZXIsXG4gICAgICAgIHN0cnVjdHVyZU1ldGFkYXRhOiB0aGlzLmJ1aWxkU3RydWN0dXJlTWV0YWRhdGEobm9kZSksXG4gICAgICB9O1xuXG4gICAgICBpZiAodGhpcy5lbnJpY2hTbmFwc2hvdHMpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBzbmFwc2hvdC5zY29wZVN0YXRlID0geyAuLi50aGlzLmV4ZWN1dGlvblJ1bnRpbWUuZ2xvYmFsU3RvcmUuZ2V0U3RhdGUoKSB9O1xuXG4gICAgICAgICAgc25hcHNob3QuZGVidWdJbmZvID0ge1xuICAgICAgICAgICAgbG9nczogeyAuLi5jb250ZXh0LmRlYnVnLmxvZ0NvbnRleHQgfSxcbiAgICAgICAgICAgIGVycm9yczogeyAuLi5jb250ZXh0LmRlYnVnLmVycm9yQ29udGV4dCB9LFxuICAgICAgICAgICAgbWV0cmljczogeyAuLi5jb250ZXh0LmRlYnVnLm1ldHJpY0NvbnRleHQgfSxcbiAgICAgICAgICAgIGV2YWxzOiB7IC4uLmNvbnRleHQuZGVidWcuZXZhbENvbnRleHQgfSxcbiAgICAgICAgICB9O1xuICAgICAgICAgIGlmIChjb250ZXh0LmRlYnVnLmZsb3dNZXNzYWdlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBzbmFwc2hvdC5kZWJ1Z0luZm8uZmxvd01lc3NhZ2VzID0gWy4uLmNvbnRleHQuZGVidWcuZmxvd01lc3NhZ2VzXTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBzbmFwc2hvdC5zdGFnZU91dHB1dCA9IHN0YWdlT3V0cHV0O1xuXG4gICAgICAgICAgaWYgKGVycm9ySW5mbykge1xuICAgICAgICAgICAgc25hcHNob3QuZXJyb3JJbmZvID0gZXJyb3JJbmZvO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHNuYXBzaG90Lmhpc3RvcnlJbmRleCA9IHRoaXMuZXhlY3V0aW9uUnVudGltZS5leGVjdXRpb25IaXN0b3J5Lmxpc3QoKS5sZW5ndGg7XG4gICAgICAgIH0gY2F0Y2ggKGVucmljaEVycm9yOiBhbnkpIHtcbiAgICAgICAgICB0aGlzLmxvZ2dlci53YXJuKGBFbnJpY2htZW50IGVycm9yIGF0IHN0YWdlICcke3N0YWdlUGF0aH0nOmAsIHsgZXJyb3I6IGVucmljaEVycm9yIH0pO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuZXh0cmFjdG9yKHNuYXBzaG90KTtcblxuICAgICAgaWYgKHJlc3VsdCAhPT0gdW5kZWZpbmVkICYmIHJlc3VsdCAhPT0gbnVsbCkge1xuICAgICAgICB0aGlzLmV4dHJhY3RlZFJlc3VsdHMuc2V0KHN0YWdlUGF0aCwgcmVzdWx0KTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcihgRXh0cmFjdG9yIGVycm9yIGF0IHN0YWdlICcke3N0YWdlUGF0aH0nOmAsIHsgZXJyb3IgfSk7XG4gICAgICB0aGlzLmV4dHJhY3RvckVycm9ycy5wdXNoKHtcbiAgICAgICAgc3RhZ2VQYXRoLFxuICAgICAgICBtZXNzYWdlOiBlcnJvcj8ubWVzc2FnZSA/PyBTdHJpbmcoZXJyb3IpLFxuICAgICAgICBlcnJvcixcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSB0aGUgc3RhZ2UgcGF0aCBmb3IgZXh0cmFjdG9yIHJlc3VsdHMuXG4gICAqIFVzZXMgbm9kZS5pZCBjb21iaW5lZCB3aXRoIGJyYW5jaFBhdGguXG4gICAqL1xuICBnZXRTdGFnZVBhdGgobm9kZTogU3RhZ2VOb2RlLCBicmFuY2hQYXRoPzogc3RyaW5nLCBjb250ZXh0U3RhZ2VOYW1lPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBiYXNlTmFtZSA9IG5vZGUuaWQ7XG4gICAgY29uc3Qgbm9kZUlkID0gY29udGV4dFN0YWdlTmFtZSAmJiBjb250ZXh0U3RhZ2VOYW1lICE9PSBub2RlLm5hbWUgPyBjb250ZXh0U3RhZ2VOYW1lIDogYmFzZU5hbWU7XG4gICAgaWYgKCFicmFuY2hQYXRoKSByZXR1cm4gbm9kZUlkO1xuICAgIHJldHVybiBgJHticmFuY2hQYXRofS4ke25vZGVJZH1gO1xuICB9XG5cbiAgcHJpdmF0ZSBidWlsZFN0cnVjdHVyZU1ldGFkYXRhKG5vZGU6IFN0YWdlTm9kZSk6IFJ1bnRpbWVTdHJ1Y3R1cmVNZXRhZGF0YSB7XG4gICAgY29uc3QgbWV0YWRhdGE6IFJ1bnRpbWVTdHJ1Y3R1cmVNZXRhZGF0YSA9IHtcbiAgICAgIHR5cGU6IGNvbXB1dGVOb2RlVHlwZShub2RlKSxcbiAgICB9O1xuXG4gICAgaWYgKG5vZGUuaXNTdWJmbG93Um9vdCkge1xuICAgICAgbWV0YWRhdGEuaXNTdWJmbG93Um9vdCA9IHRydWU7XG4gICAgICBtZXRhZGF0YS5zdWJmbG93SWQgPSBub2RlLnN1YmZsb3dJZDtcbiAgICAgIG1ldGFkYXRhLnN1YmZsb3dOYW1lID0gbm9kZS5zdWJmbG93TmFtZTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuY3VycmVudFN1YmZsb3dJZCkge1xuICAgICAgbWV0YWRhdGEuc3ViZmxvd0lkID0gdGhpcy5jdXJyZW50U3ViZmxvd0lkO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmN1cnJlbnRGb3JrSWQpIHtcbiAgICAgIG1ldGFkYXRhLmlzUGFyYWxsZWxDaGlsZCA9IHRydWU7XG4gICAgICBtZXRhZGF0YS5wYXJhbGxlbEdyb3VwSWQgPSB0aGlzLmN1cnJlbnRGb3JrSWQ7XG4gICAgfVxuXG4gICAgaWYgKG5vZGUuaXNTdHJlYW1pbmcpIHtcbiAgICAgIG1ldGFkYXRhLnN0cmVhbUlkID0gbm9kZS5zdHJlYW1JZDtcbiAgICB9XG5cbiAgICBjb25zdCBoYXNEeW5hbWljQ2hpbGRyZW4gPSBCb29sZWFuKG5vZGUuY2hpbGRyZW4/Lmxlbmd0aCAmJiAhbm9kZS5uZXh0Tm9kZVNlbGVjdG9yICYmIG5vZGUuZm4pO1xuICAgIGlmIChoYXNEeW5hbWljQ2hpbGRyZW4pIHtcbiAgICAgIG1ldGFkYXRhLmlzRHluYW1pYyA9IHRydWU7XG4gICAgfVxuXG4gICAgcmV0dXJuIG1ldGFkYXRhO1xuICB9XG5cbiAgLyoqIFJldHVybnMgZXh0cmFjdGVkIHJlc3VsdHMgY29sbGVjdGVkIGR1cmluZyBleGVjdXRpb24uICovXG4gIGdldEV4dHJhY3RlZFJlc3VsdHM8VFJlc3VsdCA9IHVua25vd24+KCk6IE1hcDxzdHJpbmcsIFRSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5leHRyYWN0ZWRSZXN1bHRzIGFzIE1hcDxzdHJpbmcsIFRSZXN1bHQ+O1xuICB9XG5cbiAgLyoqIFJldHVybnMgZXJyb3JzIGVuY291bnRlcmVkIGR1cmluZyBleHRyYWN0aW9uLiAqL1xuICBnZXRFeHRyYWN0b3JFcnJvcnMoKTogRXh0cmFjdG9yRXJyb3JbXSB7XG4gICAgcmV0dXJuIHRoaXMuZXh0cmFjdG9yRXJyb3JzO1xuICB9XG59XG4iXX0=
|