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.
Files changed (91) hide show
  1. package/CLAUDE.md +51 -8
  2. package/README.md +1 -0
  3. package/dist/advanced.js +2 -3
  4. package/dist/esm/advanced.js +2 -2
  5. package/dist/esm/index.js +2 -2
  6. package/dist/esm/lib/builder/FlowChartBuilder.js +552 -103
  7. package/dist/esm/lib/builder/index.js +2 -2
  8. package/dist/esm/lib/builder/structure/StructureRecorder.js +158 -0
  9. package/dist/esm/lib/builder/structure/StructureRecorderDispatcher.js +171 -0
  10. package/dist/esm/lib/builder/types.js +1 -1
  11. package/dist/esm/lib/engine/errors/errorInfo.js +4 -3
  12. package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +14 -3
  13. package/dist/esm/lib/engine/handlers/DeciderHandler.js +5 -7
  14. package/dist/esm/lib/engine/handlers/RuntimeStructureManager.js +2 -2
  15. package/dist/esm/lib/engine/handlers/SelectorHandler.js +13 -7
  16. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +7 -1
  17. package/dist/esm/lib/engine/handlers/index.js +1 -3
  18. package/dist/esm/lib/engine/handlers/types.js +2 -2
  19. package/dist/esm/lib/engine/index.js +1 -1
  20. package/dist/esm/lib/engine/narrative/CombinedNarrativeRecorder.js +12 -2
  21. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +17 -3
  22. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +8 -1
  23. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
  24. package/dist/esm/lib/engine/narrative/index.js +1 -1
  25. package/dist/esm/lib/engine/narrative/types.js +1 -1
  26. package/dist/esm/lib/engine/runtimeStageId.js +64 -2
  27. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +35 -57
  28. package/dist/esm/lib/engine/types.js +1 -1
  29. package/dist/esm/lib/engine/walkSubflowSpec.js +168 -0
  30. package/dist/esm/lib/recorder/CombinedRecorder.js +8 -1
  31. package/dist/esm/lib/runner/FlowChartExecutor.js +23 -17
  32. package/dist/esm/lib/runner/getSubtreeSnapshot.js +3 -2
  33. package/dist/esm/trace.js +3 -2
  34. package/dist/index.js +3 -2
  35. package/dist/lib/builder/FlowChartBuilder.js +554 -104
  36. package/dist/lib/builder/index.js +3 -2
  37. package/dist/lib/builder/structure/StructureRecorder.js +159 -0
  38. package/dist/lib/builder/structure/StructureRecorderDispatcher.js +175 -0
  39. package/dist/lib/builder/types.js +1 -1
  40. package/dist/lib/engine/errors/errorInfo.js +4 -3
  41. package/dist/lib/engine/handlers/ChildrenExecutor.js +14 -3
  42. package/dist/lib/engine/handlers/DeciderHandler.js +5 -7
  43. package/dist/lib/engine/handlers/RuntimeStructureManager.js +2 -2
  44. package/dist/lib/engine/handlers/SelectorHandler.js +13 -7
  45. package/dist/lib/engine/handlers/SubflowExecutor.js +7 -1
  46. package/dist/lib/engine/handlers/index.js +2 -5
  47. package/dist/lib/engine/handlers/types.js +2 -2
  48. package/dist/lib/engine/index.js +1 -1
  49. package/dist/lib/engine/narrative/CombinedNarrativeRecorder.js +12 -2
  50. package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +17 -3
  51. package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +8 -1
  52. package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +2 -1
  53. package/dist/lib/engine/narrative/index.js +1 -1
  54. package/dist/lib/engine/narrative/types.js +1 -1
  55. package/dist/lib/engine/runtimeStageId.js +66 -3
  56. package/dist/lib/engine/traversal/FlowchartTraverser.js +35 -57
  57. package/dist/lib/engine/types.js +1 -1
  58. package/dist/lib/engine/walkSubflowSpec.js +172 -0
  59. package/dist/lib/recorder/CombinedRecorder.js +8 -1
  60. package/dist/lib/runner/FlowChartExecutor.js +23 -17
  61. package/dist/lib/runner/getSubtreeSnapshot.js +3 -2
  62. package/dist/trace.js +5 -2
  63. package/dist/types/advanced.d.ts +4 -4
  64. package/dist/types/index.d.ts +4 -2
  65. package/dist/types/lib/builder/FlowChartBuilder.d.ts +202 -17
  66. package/dist/types/lib/builder/index.d.ts +3 -2
  67. package/dist/types/lib/builder/structure/StructureRecorder.d.ts +349 -0
  68. package/dist/types/lib/builder/structure/StructureRecorderDispatcher.d.ts +77 -0
  69. package/dist/types/lib/builder/types.d.ts +39 -6
  70. package/dist/types/lib/engine/errors/errorInfo.d.ts +3 -2
  71. package/dist/types/lib/engine/handlers/ChildrenExecutor.d.ts +1 -1
  72. package/dist/types/lib/engine/handlers/DeciderHandler.d.ts +3 -3
  73. package/dist/types/lib/engine/handlers/RuntimeStructureManager.d.ts +1 -1
  74. package/dist/types/lib/engine/handlers/SelectorHandler.d.ts +2 -2
  75. package/dist/types/lib/engine/handlers/index.d.ts +1 -2
  76. package/dist/types/lib/engine/handlers/types.d.ts +2 -9
  77. package/dist/types/lib/engine/index.d.ts +1 -1
  78. package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +4 -2
  79. package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +1 -0
  80. package/dist/types/lib/engine/narrative/index.d.ts +1 -1
  81. package/dist/types/lib/engine/narrative/types.d.ts +59 -2
  82. package/dist/types/lib/engine/runtimeStageId.d.ts +57 -1
  83. package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +1 -6
  84. package/dist/types/lib/engine/types.d.ts +19 -26
  85. package/dist/types/lib/engine/walkSubflowSpec.d.ts +95 -0
  86. package/dist/types/lib/runner/FlowChartExecutor.d.ts +4 -16
  87. package/dist/types/trace.d.ts +3 -1
  88. package/package.json +6 -4
  89. package/dist/esm/lib/engine/handlers/ExtractorRunner.js +0 -122
  90. package/dist/lib/engine/handlers/ExtractorRunner.js +0 -126
  91. 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
- /** Called when a stage executes. First stage uses distinct opening pattern. */
18
- onStageExecuted(stageName: string, description?: string, traversalContext?: TraversalContext): void;
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
- /** Parse a runtimeStageId into its components. */
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 { ExtractorError, IExecutionRuntime, ILogger, ScopeFactory, SerializedPipelineStructure, StageFunction, StageNode, StreamHandlers, SubflowResult, TraversalExtractor, TraversalResult } from '../types.js';
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, enrichSnapshots: true });
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 ExtractorError, type RunOptions, type ScopeFactory, type SerializedPipelineStructure, type StageNode, type StreamHandlers, type SubflowResult } from '../engine/types.js';
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
- * enrichSnapshots: true,
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, enrichSnapshots: true })
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.
@@ -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": "5.0.0",
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=