footprint-explainable-ui 0.18.0 → 0.19.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/adapters/fromRuntimeSnapshot.d.ts +33 -4
- package/dist/adapters/fromRuntimeSnapshot.d.ts.map +1 -1
- package/dist/adapters/fromRuntimeSnapshot.js +162 -33
- package/dist/adapters/fromRuntimeSnapshot.js.map +1 -1
- package/dist/components/ExplainableShell/ExplainableShell.d.ts +157 -14
- package/dist/components/ExplainableShell/ExplainableShell.d.ts.map +1 -1
- package/dist/components/ExplainableShell/ExplainableShell.js +676 -68
- package/dist/components/ExplainableShell/ExplainableShell.js.map +1 -1
- package/dist/components/ExplainableShell/index.d.ts +1 -1
- package/dist/components/ExplainableShell/index.d.ts.map +1 -1
- package/dist/components/FlowchartView/SubflowTree.d.ts +7 -14
- package/dist/components/FlowchartView/SubflowTree.d.ts.map +1 -1
- package/dist/components/FlowchartView/SubflowTree.js +56 -46
- package/dist/components/FlowchartView/SubflowTree.js.map +1 -1
- package/dist/components/FlowchartView/index.d.ts +32 -4
- package/dist/components/FlowchartView/index.d.ts.map +1 -1
- package/dist/components/FlowchartView/index.js +22 -2
- package/dist/components/FlowchartView/index.js.map +1 -1
- package/dist/components/FlowchartView/useSubflowNavigation.d.ts +41 -16
- package/dist/components/FlowchartView/useSubflowNavigation.d.ts.map +1 -1
- package/dist/components/FlowchartView/useSubflowNavigation.js +69 -50
- package/dist/components/FlowchartView/useSubflowNavigation.js.map +1 -1
- package/dist/components/GanttTimeline/GanttTimeline.d.ts.map +1 -1
- package/dist/components/GanttTimeline/GanttTimeline.js +5 -5
- package/dist/components/GanttTimeline/GanttTimeline.js.map +1 -1
- package/dist/components/MemoryInspector/MemoryInspector.d.ts.map +1 -1
- package/dist/components/MemoryInspector/MemoryInspector.js +36 -13
- package/dist/components/MemoryInspector/MemoryInspector.js.map +1 -1
- package/dist/components/NarrativeTrace/NarrativeTrace.d.ts.map +1 -1
- package/dist/components/NarrativeTrace/NarrativeTrace.js +24 -17
- package/dist/components/NarrativeTrace/NarrativeTrace.js.map +1 -1
- package/dist/components/ScopeDiff/ScopeDiff.js +3 -3
- package/dist/components/ScopeDiff/ScopeDiff.js.map +1 -1
- package/dist/components/StageNode/StageNode.d.ts +21 -0
- package/dist/components/StageNode/StageNode.d.ts.map +1 -1
- package/dist/components/StageNode/StageNode.js +189 -9
- package/dist/components/StageNode/StageNode.js.map +1 -1
- package/dist/components/TimeTravelControls/TimeTravelControls.d.ts.map +1 -1
- package/dist/components/TimeTravelControls/TimeTravelControls.js +19 -3
- package/dist/components/TimeTravelControls/TimeTravelControls.js.map +1 -1
- package/dist/components/TimeTravelDebugger/TimeTravelDebugger.d.ts +19 -8
- package/dist/components/TimeTravelDebugger/TimeTravelDebugger.d.ts.map +1 -1
- package/dist/components/TimeTravelDebugger/TimeTravelDebugger.js +23 -8
- package/dist/components/TimeTravelDebugger/TimeTravelDebugger.js.map +1 -1
- package/dist/flowchart.cjs +3485 -1340
- package/dist/flowchart.cjs.map +1 -1
- package/dist/flowchart.d.cts +1616 -177
- package/dist/flowchart.d.ts +1616 -177
- package/dist/flowchart.d.ts.map +1 -1
- package/dist/flowchart.js +3507 -1384
- package/dist/flowchart.js.map +1 -1
- package/dist/index.cjs +749 -557
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +276 -29
- package/dist/index.d.ts +276 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +742 -547
- package/dist/index.js.map +1 -1
- package/dist/theme/ThemeProvider.d.ts +14 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -1
- package/dist/theme/ThemeProvider.js +15 -1
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/index.d.ts +4 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +3 -2
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/presets.d.ts +3 -0
- package/dist/theme/presets.d.ts.map +1 -1
- package/dist/theme/presets.js +22 -0
- package/dist/theme/presets.js.map +1 -1
- package/dist/theme/tokens.d.ts +22 -1
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +23 -2
- package/dist/theme/tokens.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/components/FlowchartView/FlowchartView.d.ts +0 -20
- package/dist/components/FlowchartView/FlowchartView.d.ts.map +0 -1
- package/dist/components/FlowchartView/FlowchartView.js +0 -80
- package/dist/components/FlowchartView/FlowchartView.js.map +0 -1
- package/dist/components/FlowchartView/TracedFlowchartView.d.ts +0 -20
- package/dist/components/FlowchartView/TracedFlowchartView.d.ts.map +0 -1
- package/dist/components/FlowchartView/TracedFlowchartView.js +0 -101
- package/dist/components/FlowchartView/TracedFlowchartView.js.map +0 -1
- package/dist/components/FlowchartView/specToReactFlow.d.ts +0 -56
- package/dist/components/FlowchartView/specToReactFlow.d.ts.map +0 -1
- package/dist/components/FlowchartView/specToReactFlow.js +0 -202
- package/dist/components/FlowchartView/specToReactFlow.js.map +0 -1
package/dist/flowchart.d.cts
CHANGED
|
@@ -1,7 +1,59 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import * as _xyflow_react from '@xyflow/react';
|
|
3
|
-
import { Node, Edge, NodeTypes } from '@xyflow/react';
|
|
4
1
|
import * as react from 'react';
|
|
2
|
+
import * as _xyflow_react from '@xyflow/react';
|
|
3
|
+
import { Node, Edge } from '@xyflow/react';
|
|
4
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
interface StageNodeData {
|
|
7
|
+
label: string;
|
|
8
|
+
active?: boolean;
|
|
9
|
+
done?: boolean;
|
|
10
|
+
error?: boolean;
|
|
11
|
+
linked?: boolean;
|
|
12
|
+
/** Semantic icon hint (e.g., "llm", "tool", "rag", "start", "parse", "agent", "guard") */
|
|
13
|
+
icon?: string;
|
|
14
|
+
/** Step numbers in execution order (shown as badges — multiple when revisited via loops) */
|
|
15
|
+
stepNumbers?: number[];
|
|
16
|
+
/** Node was not executed (dim it) */
|
|
17
|
+
dimmed?: boolean;
|
|
18
|
+
/** Node is a subflow root (show nested indicator) */
|
|
19
|
+
isSubflow?: boolean;
|
|
20
|
+
/** Node uses lazy resolution (dashed border + cloud icon when unresolved) */
|
|
21
|
+
isLazy?: boolean;
|
|
22
|
+
/** Node is a decider (renders as diamond shape per flowchart convention) */
|
|
23
|
+
isDecider?: boolean;
|
|
24
|
+
/** Node is a fork (parallel fan-out) */
|
|
25
|
+
isFork?: boolean;
|
|
26
|
+
/** Human-readable description of what this stage does */
|
|
27
|
+
description?: string;
|
|
28
|
+
/** Subflow identifier — set when this node belongs to a subflow */
|
|
29
|
+
subflowId?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Stable stage identifier from the spec (`SpecNode.id`). Renderable as a
|
|
32
|
+
* small monospace caption under the label when `showStageId` is true —
|
|
33
|
+
* useful for teaching the runtimeStageId convention and for debugging
|
|
34
|
+
* which node a recorder event belongs to.
|
|
35
|
+
*/
|
|
36
|
+
stageId?: string;
|
|
37
|
+
/**
|
|
38
|
+
* When true, render the `stageId` as a small monospace caption beneath
|
|
39
|
+
* the label. Default false. Drives the "Show IDs" toggle in
|
|
40
|
+
* ExplainableShell.
|
|
41
|
+
*/
|
|
42
|
+
showStageId?: boolean;
|
|
43
|
+
[key: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Custom ReactFlow node for pipeline stages.
|
|
47
|
+
* All colors and fonts come from `--fp-*` CSS variables (via theme).
|
|
48
|
+
* Shows execution state via color, icon, step badge, and pulse animation.
|
|
49
|
+
*/
|
|
50
|
+
declare const StageNode: react.NamedExoticComponent<Pick<_xyflow_react.Node<Record<string, unknown>, string | undefined>, "id" | "data" | "width" | "height" | "sourcePosition" | "targetPosition" | "dragHandle" | "parentId"> & Required<Pick<_xyflow_react.Node<Record<string, unknown>, string | undefined>, "type" | "dragging" | "zIndex" | "selectable" | "deletable" | "selected" | "draggable">> & {
|
|
51
|
+
isConnectable: boolean;
|
|
52
|
+
positionAbsoluteX: number;
|
|
53
|
+
positionAbsoluteY: number;
|
|
54
|
+
} & {
|
|
55
|
+
data: StageNodeData;
|
|
56
|
+
}>;
|
|
5
57
|
|
|
6
58
|
/** Snapshot of a single pipeline stage — the core data shape for all components. */
|
|
7
59
|
interface StageSnapshot {
|
|
@@ -42,164 +94,558 @@ interface BaseComponentProps {
|
|
|
42
94
|
style?: React.CSSProperties;
|
|
43
95
|
}
|
|
44
96
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
|
|
61
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Branded types for translator key discipline.
|
|
99
|
+
*
|
|
100
|
+
* The footprintjs/trace contract uses two distinct identity strings:
|
|
101
|
+
*
|
|
102
|
+
* - **StageId**: the stable identifier the user picks at build time
|
|
103
|
+
* (e.g. `'load-order'`, `'check-inventory'`). Identity per chart spec.
|
|
104
|
+
*
|
|
105
|
+
* - **RuntimeStageId**: `[subflowPath/]stageId#executionIndex` — the
|
|
106
|
+
* per-execution identity. A loop visiting the same stage 3 times
|
|
107
|
+
* produces 3 distinct RuntimeStageIds with the same StageId base.
|
|
108
|
+
*
|
|
109
|
+
* Translator outputs index per-stage data by StageId (`Map<StageId, ...>`)
|
|
110
|
+
* AND per-execution data by RuntimeStageId (`Map<RuntimeStageId, ...>`).
|
|
111
|
+
* Both are `string` at runtime — TypeScript can't distinguish them
|
|
112
|
+
* without help. Branded types make `byStageId.get(runtimeStageId)` a
|
|
113
|
+
* **compile-time error** instead of a silent `undefined` at runtime.
|
|
114
|
+
*
|
|
115
|
+
* Why branded types over wrapper classes
|
|
116
|
+
* ──────────────────────────────────────
|
|
117
|
+
* - Zero runtime cost (the brand exists only at the type level)
|
|
118
|
+
* - JSON-serializable as-is (no `toJSON` glue needed)
|
|
119
|
+
* - Interop with consumer code that uses raw `string` is one cast
|
|
120
|
+
* (`stageId as StageId`) when the consumer is sure of the source.
|
|
121
|
+
*
|
|
122
|
+
* Usage pattern
|
|
123
|
+
* ─────────────
|
|
124
|
+
* ```ts
|
|
125
|
+
* import type { StageId, RuntimeStageId } from './_internal/keys';
|
|
126
|
+
*
|
|
127
|
+
* // In a translator, when accepting input from a footprintjs event:
|
|
128
|
+
* const sid = event.stageId as StageId;
|
|
129
|
+
* const rsid = event.traversalContext.runtimeStageId as RuntimeStageId;
|
|
130
|
+
*
|
|
131
|
+
* // In a consumer reading from the index:
|
|
132
|
+
* const node = index.byStageId.get(stageId); // typechecks
|
|
133
|
+
* const node = index.byStageId.get(runtimeStageId); // TS ERROR ✓
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* Helper to derive a StageId from a RuntimeStageId — strips `#N` suffix
|
|
137
|
+
* and optional subflow path. **Use only for DISPLAY**, NEVER for matching
|
|
138
|
+
* commitLog stageIds (see invariant I3 in `traceStructureRecorder.ts`).
|
|
139
|
+
*/
|
|
140
|
+
/** Stable per-spec identifier (e.g. `'load-order'`). */
|
|
141
|
+
type StageId = string & {
|
|
142
|
+
readonly __brand: "StageId";
|
|
143
|
+
};
|
|
144
|
+
/** Per-execution identifier (e.g. `'load-order#0'` or `'sf-foo/inner#3'`). */
|
|
145
|
+
type RuntimeStageId = string & {
|
|
146
|
+
readonly __brand: "RuntimeStageId";
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Tag a raw string as a `StageId`. Use at translator boundaries when
|
|
150
|
+
* ingesting from a footprintjs event payload (the source guarantees
|
|
151
|
+
* the string IS a stage id). Zero runtime cost.
|
|
152
|
+
*/
|
|
153
|
+
declare function asStageId(s: string): StageId;
|
|
154
|
+
/**
|
|
155
|
+
* Tag a raw string as a `RuntimeStageId`. Use at translator boundaries
|
|
156
|
+
* when ingesting from a `traversalContext.runtimeStageId`. Zero
|
|
157
|
+
* runtime cost.
|
|
158
|
+
*/
|
|
159
|
+
declare function asRuntimeStageId(s: string): RuntimeStageId;
|
|
62
160
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
161
|
+
/**
|
|
162
|
+
* traceStructureRecorder — event-driven xyflow Node[] + Edge[] collector.
|
|
163
|
+
*
|
|
164
|
+
* Implements footprintjs v6.0+ `StructureRecorder` interface. Accumulates
|
|
165
|
+
* an unpositioned graph (xyflow node + edge shape) as the chart is being
|
|
166
|
+
* built — no spec tree walk required.
|
|
167
|
+
*
|
|
168
|
+
* Why event-driven
|
|
169
|
+
* ────────────────
|
|
170
|
+
* The recorder fires SYNCHRONOUSLY at every spec-mutation moment during
|
|
171
|
+
* construction. By the time `.build()` returns, the recorder's
|
|
172
|
+
* `getGraph()` returns the complete graph — zero extra walking
|
|
173
|
+
* (the "collect during traversal, never post-process" rule footprintjs
|
|
174
|
+
* documents in its core principle).
|
|
175
|
+
*
|
|
176
|
+
* Bonus: the same recorder shape can drive incremental UI updates if
|
|
177
|
+
* the builder is constructed asynchronously (e.g., a UI builder where
|
|
178
|
+
* each "add stage" click re-renders the live graph).
|
|
179
|
+
*
|
|
180
|
+
* Layout is a separate concern
|
|
181
|
+
* ────────────────────────────
|
|
182
|
+
* This module produces UNPOSITIONED nodes (no `position` field set;
|
|
183
|
+
* xyflow defaults to `{x: 0, y: 0}`). Apply a layout algorithm
|
|
184
|
+
* downstream — either:
|
|
185
|
+
*
|
|
186
|
+
* - a graph algorithm (dagre / elk / d3-force)
|
|
187
|
+
* - manual positioning via your own walk over `recorder.getGraph()`
|
|
188
|
+
*
|
|
189
|
+
* The `<TraceFlow>` component wires a default BFS layout.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```ts
|
|
193
|
+
* import { flowChart } from 'footprintjs';
|
|
194
|
+
* import { createTraceStructureRecorder } from 'footprint-explainable-ui/flowchart';
|
|
195
|
+
*
|
|
196
|
+
* const trace = createTraceStructureRecorder();
|
|
197
|
+
* const chart = flowChart('seed', fn, 'seed', {
|
|
198
|
+
* structureRecorders: [trace.recorder],
|
|
199
|
+
* })
|
|
200
|
+
* .addFunction('a', fnA, 'a')
|
|
201
|
+
* .build();
|
|
202
|
+
*
|
|
203
|
+
* const { nodes, edges } = trace.getGraph();
|
|
204
|
+
* // → nodes: [{ id: 'seed', data: { label: 'seed', ... } }, { id: 'a', ... }]
|
|
205
|
+
* // → edges: [{ id: 'seed->a', source: 'seed', target: 'a', data: { kind: 'next' } }]
|
|
206
|
+
*
|
|
207
|
+
* <ReactFlow nodes={layout(nodes)} edges={edges} />
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
|
|
211
|
+
type StructureNodeType = "stage" | "decider" | "selector" | "fork" | "streaming" | "subflow" | "loop";
|
|
212
|
+
interface StructureSpecRef {
|
|
213
|
+
readonly id: string;
|
|
214
|
+
readonly name: string;
|
|
215
|
+
readonly type?: StructureNodeType;
|
|
216
|
+
readonly description?: string;
|
|
217
|
+
readonly icon?: string;
|
|
218
|
+
readonly isSubflowRoot?: boolean;
|
|
219
|
+
readonly subflowId?: string;
|
|
220
|
+
readonly isLazy?: boolean;
|
|
221
|
+
readonly [key: string]: unknown;
|
|
222
|
+
}
|
|
223
|
+
interface StageAddedEvent {
|
|
224
|
+
readonly stageId: string;
|
|
225
|
+
readonly name: string;
|
|
226
|
+
readonly type: StructureNodeType;
|
|
227
|
+
readonly isPausable?: boolean;
|
|
228
|
+
readonly spec: StructureSpecRef;
|
|
229
|
+
}
|
|
230
|
+
type EdgeKind = "next" | "fork-branch" | "decision-branch";
|
|
231
|
+
interface EdgeAddedEvent {
|
|
232
|
+
readonly from: string;
|
|
233
|
+
readonly to: string;
|
|
234
|
+
readonly kind: EdgeKind;
|
|
235
|
+
readonly label?: string;
|
|
236
|
+
}
|
|
237
|
+
interface LoopEdgeAddedEvent {
|
|
238
|
+
readonly from: string;
|
|
239
|
+
readonly to: string;
|
|
240
|
+
}
|
|
241
|
+
interface DeciderCompleteEvent {
|
|
242
|
+
readonly decider: string;
|
|
243
|
+
readonly type: "decider" | "selector";
|
|
244
|
+
readonly branchIds: readonly string[];
|
|
245
|
+
readonly defaultBranch?: string;
|
|
246
|
+
}
|
|
247
|
+
interface SubflowMountedEvent {
|
|
248
|
+
readonly subflowId: string;
|
|
249
|
+
readonly subflowName: string;
|
|
250
|
+
readonly rootStageId: string;
|
|
251
|
+
readonly isLazy?: boolean;
|
|
252
|
+
/**
|
|
253
|
+
* The mounted subflow's complete spec — added in footprintjs v6.0
|
|
254
|
+
* (proposal #001). Present for eager mounts, UNDEFINED for lazy
|
|
255
|
+
* mounts (the subflow isn't resolved yet at mount-event-fire-time).
|
|
256
|
+
* When present, the consumer walks it to materialize inner nodes +
|
|
257
|
+
* edges directly with `subflowOf` set — no retroactive tagging
|
|
258
|
+
* needed.
|
|
259
|
+
*
|
|
260
|
+
* Typed as `unknown` because the upstream `SerializedPipelineStructure`
|
|
261
|
+
* shape has no index signature and we want to stay loosely-coupled
|
|
262
|
+
* (see top-of-file rationale for the no-`footprintjs`-dep boundary).
|
|
263
|
+
* `walkSubflowSpecInto` accepts the duck-typed shape it actually
|
|
264
|
+
* walks.
|
|
265
|
+
*/
|
|
266
|
+
readonly subflowSpec?: unknown;
|
|
267
|
+
/**
|
|
268
|
+
* Local mount id within the parent (matches `subflowId` for top-level
|
|
269
|
+
* mounts; composed `'parent/child'` for nested). Tagged onto every
|
|
270
|
+
* inner stage's `data.subflowOf` during the walk.
|
|
271
|
+
*/
|
|
272
|
+
readonly subflowPath?: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Minimal StructureRecorder interface — mirrors footprintjs v6.0+.
|
|
276
|
+
*
|
|
277
|
+
* Named with the `Minimal` prefix to make the local-mirror nature
|
|
278
|
+
* explicit at consumer call sites — a consumer importing both this
|
|
279
|
+
* type and the upstream `StructureRecorder` from `footprintjs` won't
|
|
280
|
+
* collide, and code reviewers see at a glance which boundary the type
|
|
281
|
+
* crosses.
|
|
282
|
+
*
|
|
283
|
+
* The upstream `footprintjs.StructureRecorder` is structurally
|
|
284
|
+
* compatible (this type's shape is a subset of upstream's), so passing
|
|
285
|
+
* an instance of this to `flowChart(..., { structureRecorders: [...] })`
|
|
286
|
+
* typechecks cleanly.
|
|
287
|
+
*/
|
|
288
|
+
interface MinimalStructureRecorder {
|
|
289
|
+
readonly id: string;
|
|
290
|
+
onStageAdded?(event: StageAddedEvent): void;
|
|
291
|
+
onEdgeAdded?(event: EdgeAddedEvent): void;
|
|
292
|
+
onLoopEdgeAdded?(event: LoopEdgeAddedEvent): void;
|
|
293
|
+
onDeciderComplete?(event: DeciderCompleteEvent): void;
|
|
294
|
+
onSubflowMounted?(event: SubflowMountedEvent): void;
|
|
84
295
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/** Names of all stages that were executed (done + active) */
|
|
91
|
-
executedStages: Set<string>;
|
|
92
|
-
/** Ordered list of executed stage names (for step numbering) */
|
|
93
|
-
executionOrder?: string[];
|
|
94
|
-
}
|
|
95
|
-
/** Colors for the flowchart — consumer provides these to match their theme */
|
|
96
|
-
interface FlowchartColors {
|
|
97
|
-
edgeDefault: string;
|
|
98
|
-
edgeExecuted: string;
|
|
99
|
-
edgeActive: string;
|
|
100
|
-
edgeLoop: string;
|
|
101
|
-
labelDefault: string;
|
|
102
|
-
labelExecuted: string;
|
|
103
|
-
labelLoop: string;
|
|
104
|
-
pathGlow: string;
|
|
105
|
-
}
|
|
106
|
-
/** A positioned node with all static info, before overlay is applied. */
|
|
107
|
-
interface LayoutNode {
|
|
108
|
-
id: string;
|
|
109
|
-
x: number;
|
|
110
|
-
y: number;
|
|
296
|
+
/**
|
|
297
|
+
* Per-node data attached to the xyflow `Node`. Consumed by the
|
|
298
|
+
* `StageNode` renderer (shape mirrors what the renderer expects).
|
|
299
|
+
*/
|
|
300
|
+
interface TraceNodeData extends Record<string, unknown> {
|
|
111
301
|
label: string;
|
|
112
302
|
isDecider: boolean;
|
|
113
303
|
isFork: boolean;
|
|
304
|
+
isSubflow: boolean;
|
|
305
|
+
/** True when the event carried `type: 'streaming'` (the spec was added
|
|
306
|
+
* via `addStreamingFunction`). Renderers that style streaming stages
|
|
307
|
+
* distinctly key on this flag. */
|
|
308
|
+
isStreaming: boolean;
|
|
114
309
|
description?: string;
|
|
115
310
|
icon?: string;
|
|
116
311
|
subflowId?: string;
|
|
117
|
-
isSubflow: boolean;
|
|
118
312
|
isLazy?: boolean;
|
|
313
|
+
isPausable?: boolean;
|
|
314
|
+
/** Set later by `onDeciderComplete` when the decider's branch list is
|
|
315
|
+
* sealed. Useful for renderers that want to render decider with a
|
|
316
|
+
* branch-count badge. */
|
|
317
|
+
branchIds?: readonly string[];
|
|
318
|
+
defaultBranch?: string;
|
|
319
|
+
/**
|
|
320
|
+
* The subflow this node belongs to, OR `undefined` for top-level
|
|
321
|
+
* parent-chart stages. Tracked via a stack-based heuristic during
|
|
322
|
+
* event ingestion:
|
|
323
|
+
*
|
|
324
|
+
* - Push on `onSubflowMounted({subflowId, rootStageId})` →
|
|
325
|
+
* top-of-stack = `{subflowId, mountStageId}`
|
|
326
|
+
* - `onStageAdded` tags new nodes with the top-of-stack `subflowId`
|
|
327
|
+
* (the mount node itself stays UNtagged — it's part of the parent
|
|
328
|
+
* chain)
|
|
329
|
+
* - Pop on `onEdgeAdded` where `from === mount.id` (a mount's
|
|
330
|
+
* outgoing edge to a sibling means the parent chain has resumed);
|
|
331
|
+
* the wrongly-tagged target node is re-tagged to the parent scope
|
|
332
|
+
*
|
|
333
|
+
* Mount nodes (where `isSubflow=true`) have `subflowOf` reflecting
|
|
334
|
+
* their OWN parent context, NOT the subflow they mount. So a parent's
|
|
335
|
+
* mount node has `subflowOf=undefined` (top-level parent), and the
|
|
336
|
+
* subflow's INTERNAL stages have `subflowOf=<mount.subflowId>`.
|
|
337
|
+
*
|
|
338
|
+
* Renderers use this to filter the chart by drill-down level: show
|
|
339
|
+
* only nodes where `subflowOf === currentDrillSubflowId` (undefined
|
|
340
|
+
* for top-level view, mount.subflowId after drilling in).
|
|
341
|
+
*/
|
|
342
|
+
subflowOf?: string;
|
|
343
|
+
/**
|
|
344
|
+
* **L8.0 — STRUCTURAL prev/next**: stage ids that lead into / out of
|
|
345
|
+
* this node, derived live from incoming/outgoing edges. Excludes
|
|
346
|
+
* `loop` back-edges (visual only — per invariant I1).
|
|
347
|
+
*
|
|
348
|
+
* Convergence-correct: a fork-join node carries an ENTRY per branch
|
|
349
|
+
* child (e.g., `FinalizeOrder.prevIds = ['CheckInventory', 'RunFraudCheck']`).
|
|
350
|
+
*
|
|
351
|
+
* "Structural" qualifier: this is the chart-SHAPE prev/next, not
|
|
352
|
+
* runtime execution order. For runtime ancestry use `CommitView`
|
|
353
|
+
* fields on `CommitFlowIndex` (L8.2).
|
|
354
|
+
*/
|
|
355
|
+
prevIds: StageId[];
|
|
356
|
+
nextIds: StageId[];
|
|
119
357
|
}
|
|
120
|
-
/**
|
|
121
|
-
interface
|
|
122
|
-
|
|
123
|
-
source: string;
|
|
124
|
-
target: string;
|
|
358
|
+
/** Per-edge data attached to the xyflow `Edge`. */
|
|
359
|
+
interface TraceEdgeData extends Record<string, unknown> {
|
|
360
|
+
kind: EdgeKind | "loop";
|
|
125
361
|
label?: string;
|
|
126
|
-
isLoop: boolean;
|
|
127
362
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
idToName: Map<string, string>;
|
|
363
|
+
type TraceNode = Node<TraceNodeData>;
|
|
364
|
+
type TraceEdge = Edge<TraceEdgeData>;
|
|
365
|
+
interface TraceGraph {
|
|
366
|
+
nodes: TraceNode[];
|
|
367
|
+
edges: TraceEdge[];
|
|
134
368
|
}
|
|
369
|
+
interface TraceStructureRecorderHandle {
|
|
370
|
+
/** The recorder to register via `flowChart(..., { structureRecorders: [handle.recorder] })`
|
|
371
|
+
* or `.attachStructureRecorder(handle.recorder)`. */
|
|
372
|
+
recorder: MinimalStructureRecorder;
|
|
373
|
+
/** Returns a defensive copy of the current graph state. Safe to call
|
|
374
|
+
* multiple times; safe to mutate the returned arrays without affecting
|
|
375
|
+
* the recorder's internal state. */
|
|
376
|
+
getGraph(): TraceGraph;
|
|
377
|
+
/** Direct read-only access for hot-path consumers (Lens slider scrubbing).
|
|
378
|
+
* Returns the LIVE internal arrays — do NOT mutate. Prefer `getGraph()`
|
|
379
|
+
* unless you measure a performance cost. */
|
|
380
|
+
getGraphRef(): Readonly<TraceGraph>;
|
|
381
|
+
/**
|
|
382
|
+
* Subscribe to graph-change notifications. Pub-sub model — multiple
|
|
383
|
+
* listeners can subscribe; each fires after every event that mutates
|
|
384
|
+
* the graph (per-event, NOT batched). Returns an unsubscribe function.
|
|
385
|
+
* Designed for React's `useSyncExternalStore` consumption.
|
|
386
|
+
*
|
|
387
|
+
* Listeners are NOT invoked during `reset()`. **Consumer guidance**:
|
|
388
|
+
* if you need to clear a live `<TraceFlow recorder={handle} />`
|
|
389
|
+
* mid-mount, either (a) unmount the component before calling
|
|
390
|
+
* `reset()` and re-mount after, OR (b) drive a fresh recorder
|
|
391
|
+
* instance via state. See `reset()` JSDoc for the rationale.
|
|
392
|
+
*/
|
|
393
|
+
subscribe(listener: () => void): () => void;
|
|
394
|
+
/**
|
|
395
|
+
* Monotonically-increasing version counter — bumps once per
|
|
396
|
+
* graph-mutating event. Pair with `getGraph()` in
|
|
397
|
+
* `useSyncExternalStore` snapshot equality:
|
|
398
|
+
* ```ts
|
|
399
|
+
* const version = useSyncExternalStore(handle.subscribe, () => handle.version());
|
|
400
|
+
* const graph = useMemo(() => handle.getGraph(), [version]);
|
|
401
|
+
* ```
|
|
402
|
+
* Avoids defensive-copy allocation on every render while keeping
|
|
403
|
+
* React's identity-check happy.
|
|
404
|
+
*/
|
|
405
|
+
version(): number;
|
|
406
|
+
/** Reset the recorder for reuse across multiple builds. After this,
|
|
407
|
+
* the recorder behaves like a freshly-constructed instance. */
|
|
408
|
+
reset(): void;
|
|
409
|
+
}
|
|
410
|
+
interface CreateTraceStructureRecorderOptions {
|
|
411
|
+
/** Recorder id — surfaces in `StructureBuildError.recorderId` when
|
|
412
|
+
* the recorder throws. Defaults to `'trace-structure'`. Use distinct
|
|
413
|
+
* ids if you attach multiple instances. */
|
|
414
|
+
id?: string;
|
|
415
|
+
/** Optional callback fired after each event mutates the graph. Useful
|
|
416
|
+
* for incremental-render UIs (Lens) that want to re-render between
|
|
417
|
+
* builder operations. NOT invoked during the initial `attachStructureRecorder`
|
|
418
|
+
* seed replay; consumers can call `getGraph()` directly after attach. */
|
|
419
|
+
onChange?: (graph: Readonly<TraceGraph>) => void;
|
|
420
|
+
}
|
|
421
|
+
declare function createTraceStructureRecorder(options?: CreateTraceStructureRecorderOptions): TraceStructureRecorderHandle;
|
|
422
|
+
|
|
135
423
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
424
|
+
* createTraceRuntimeOverlay — event-driven runtime overlay for `<TracedFlow>`.
|
|
425
|
+
*
|
|
426
|
+
* The runtime twin of `createTraceStructureRecorder` (L7.7). Where the
|
|
427
|
+
* structure recorder accumulates the build-time graph SHAPE from
|
|
428
|
+
* `StructureRecorder` events, this recorder accumulates the runtime
|
|
429
|
+
* EXECUTION STATE (which nodes ran, current active, errors) from
|
|
430
|
+
* `FlowRecorder` events. The two compose into the full time-travel
|
|
431
|
+
* trace UI:
|
|
432
|
+
*
|
|
433
|
+
* StructureRecorder events → TraceGraph (nodes + edges, id-keyed)
|
|
434
|
+
* FlowRecorder events → RuntimeOverlay (per-node state, id-keyed)
|
|
435
|
+
* │
|
|
436
|
+
* ▼
|
|
437
|
+
* <TracedFlow graph={...} overlay={...} scrubIndex={i} />
|
|
438
|
+
*
|
|
439
|
+
* **Universal key**: `runtimeStageId = [subflowPath/]stageId#executionIndex`.
|
|
440
|
+
* Loops re-execute the same stageId with bumping executionIndex — the
|
|
441
|
+
* overlay records each execution as a distinct step in `executionOrder`
|
|
442
|
+
* but updates the SAME node-by-id in `doneStageIds` (because the build-
|
|
443
|
+
* time graph has one node per spec — loops re-visit it).
|
|
444
|
+
*
|
|
445
|
+
* Per L7.7 panel guidance: the consumer pattern mirrors recorder error
|
|
446
|
+
* isolation, exposes a pub-sub `subscribe(listener)` + monotonic
|
|
447
|
+
* `version()` for `useSyncExternalStore` integration, and accumulates
|
|
448
|
+
* pure data with zero React coupling.
|
|
138
449
|
*/
|
|
139
|
-
|
|
450
|
+
interface TraversalContext$2 {
|
|
451
|
+
readonly runtimeStageId: string;
|
|
452
|
+
readonly iteration?: number;
|
|
453
|
+
readonly runId?: string;
|
|
454
|
+
}
|
|
455
|
+
interface RuntimeStageExecutedEvent {
|
|
456
|
+
readonly stageName: string;
|
|
457
|
+
readonly stageId?: string;
|
|
458
|
+
/** Discriminator for which kind of stage completed. footprintjs v6+
|
|
459
|
+
* fires this event uniformly for every stage kind (proposal #003);
|
|
460
|
+
* consumers route by `stageType` without a chart-spec lookup. */
|
|
461
|
+
readonly stageType: 'linear' | 'decider' | 'fork' | 'selector' | 'subflow-mount';
|
|
462
|
+
readonly traversalContext: TraversalContext$2;
|
|
463
|
+
}
|
|
464
|
+
interface RuntimeErrorEvent {
|
|
465
|
+
readonly stageName: string;
|
|
466
|
+
readonly stageId?: string;
|
|
467
|
+
readonly message?: string;
|
|
468
|
+
readonly traversalContext?: TraversalContext$2;
|
|
469
|
+
}
|
|
470
|
+
interface RuntimeRunStartEvent {
|
|
471
|
+
readonly traversalContext?: TraversalContext$2;
|
|
472
|
+
}
|
|
473
|
+
interface RuntimeRunEndEvent {
|
|
474
|
+
readonly traversalContext?: TraversalContext$2;
|
|
475
|
+
}
|
|
476
|
+
/** Minimal FlowRecorder interface mirror — see top-of-file rationale.
|
|
477
|
+
*
|
|
478
|
+
* As of footprintjs v6 (proposal #003), `onStageExecuted` fires
|
|
479
|
+
* uniformly for ALL stage kinds — linear / decider / fork / selector
|
|
480
|
+
* / subflow-mount. The event payload carries a `stageType` field for
|
|
481
|
+
* consumers that need to route by kind. We no longer need separate
|
|
482
|
+
* `onDecision` / `onFork` / `onSelected` handlers to track "did this
|
|
483
|
+
* stage run" — a single `onStageExecuted` handler suffices. */
|
|
484
|
+
interface MinimalFlowRecorder {
|
|
485
|
+
readonly id: string;
|
|
486
|
+
onStageExecuted?(event: RuntimeStageExecutedEvent): void;
|
|
487
|
+
onError?(event: RuntimeErrorEvent): void;
|
|
488
|
+
onRunStart?(event: RuntimeRunStartEvent): void;
|
|
489
|
+
onRunEnd?(event: RuntimeRunEndEvent): void;
|
|
490
|
+
}
|
|
140
491
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
492
|
+
* One entry in the execution timeline. `<TracedFlow>` keys time-travel
|
|
493
|
+
* scrubbing on the index into this array — at index `i`, all entries
|
|
494
|
+
* `0..i-1` are "done", entry `i` is "active".
|
|
143
495
|
*/
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
496
|
+
interface RuntimeExecutionStep {
|
|
497
|
+
/** `[subflowPath/]stageId#executionIndex` — universal key. */
|
|
498
|
+
readonly runtimeStageId: string;
|
|
499
|
+
/** Base stage id (without `#N`) — matches the `TraceGraph` node id. */
|
|
500
|
+
readonly stageId: string;
|
|
501
|
+
/** Human-readable label (from event.stageName). */
|
|
502
|
+
readonly stageName: string;
|
|
503
|
+
/** When this step recorded, in ms since recorder start. */
|
|
504
|
+
readonly timestampMs: number;
|
|
505
|
+
}
|
|
506
|
+
interface RuntimeOverlay {
|
|
507
|
+
/** Ordered execution history — drives time-travel scrubbing. */
|
|
508
|
+
readonly executionOrder: readonly RuntimeExecutionStep[];
|
|
509
|
+
/** Per-base-stageId error message (most-recent wins). */
|
|
510
|
+
readonly errors: ReadonlyMap<string, string>;
|
|
511
|
+
/** True after `onRunStart` until `onRunEnd` — useful for "still running" indicators. */
|
|
512
|
+
readonly running: boolean;
|
|
513
|
+
}
|
|
514
|
+
interface TraceRuntimeOverlayHandle {
|
|
515
|
+
/** The recorder to attach via `executor.attachFlowRecorder(handle.recorder)`. */
|
|
516
|
+
recorder: MinimalFlowRecorder;
|
|
517
|
+
/** Returns a defensive copy of the current overlay state. */
|
|
518
|
+
getOverlay(): RuntimeOverlay;
|
|
519
|
+
/** Pub-sub: returns unsubscribe. Designed for `useSyncExternalStore`. */
|
|
520
|
+
subscribe(listener: () => void): () => void;
|
|
521
|
+
/** Monotonic version counter — bumps once per overlay-mutating event. */
|
|
522
|
+
version(): number;
|
|
523
|
+
/** Reset for reuse across runs. Does NOT bump version or notify (matches
|
|
524
|
+
* traceStructureRecorder's reset contract). */
|
|
525
|
+
reset(): void;
|
|
526
|
+
}
|
|
527
|
+
interface CreateTraceRuntimeOverlayOptions {
|
|
528
|
+
id?: string;
|
|
529
|
+
}
|
|
530
|
+
declare function createTraceRuntimeOverlay(options?: CreateTraceRuntimeOverlayOptions): TraceRuntimeOverlayHandle;
|
|
148
531
|
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
532
|
+
* Snapshot of overlay state AT a specific scrub index. Drives node
|
|
533
|
+
* coloring in `<TracedFlow>`.
|
|
151
534
|
*/
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
535
|
+
interface RuntimeOverlaySlice {
|
|
536
|
+
/** Base stage ids that completed before the active scrub position. */
|
|
537
|
+
readonly doneStageIds: ReadonlySet<string>;
|
|
538
|
+
/** The currently-active stage id (or `null` when scrub is at 0 with no exec yet). */
|
|
539
|
+
readonly activeStageId: string | null;
|
|
540
|
+
/** Union of done + active — convenient for "executed at all" checks. */
|
|
541
|
+
readonly executedStageIds: ReadonlySet<string>;
|
|
542
|
+
/** The full ordered list of base stage ids up through the active position —
|
|
543
|
+
* used by renderers that want to number stages by occurrence (e.g.,
|
|
544
|
+
* show "3rd loop iteration" badges). */
|
|
545
|
+
readonly executedOrderIds: readonly string[];
|
|
546
|
+
/** Pass-through errors map for the renderer. */
|
|
547
|
+
readonly errors: ReadonlyMap<string, string>;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Slice `overlay.executionOrder` at the given index:
|
|
551
|
+
*
|
|
552
|
+
* - `executionOrder[0..index-1]` → "done"
|
|
553
|
+
* - `executionOrder[index]` → "active"
|
|
554
|
+
* - `index >= executionOrder.length` → all done, no active
|
|
555
|
+
*
|
|
556
|
+
* Returns an empty slice when overlay has no execution history.
|
|
557
|
+
*/
|
|
558
|
+
declare function sliceOverlay(overlay: RuntimeOverlay, index: number): RuntimeOverlaySlice;
|
|
156
559
|
|
|
157
|
-
interface
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
560
|
+
interface TimeTravelDebuggerProps extends BaseComponentProps {
|
|
561
|
+
/** Stage snapshots */
|
|
562
|
+
snapshots: StageSnapshot[];
|
|
563
|
+
/** Recorder-captured build-time graph (from
|
|
564
|
+
* `createTraceStructureRecorder().getGraph()`). Required for the
|
|
565
|
+
* chart rendering — replaces the legacy `nodes` / `edges` props. */
|
|
566
|
+
graph: TraceGraph;
|
|
567
|
+
/** Optional runtime overlay (from
|
|
568
|
+
* `createTraceRuntimeOverlay().getOverlay()`). When provided, the
|
|
569
|
+
* chart renders via `<TracedFlow>` with per-step coloring synced to
|
|
570
|
+
* the scrubber; otherwise renders via `<TraceFlow>` (build-time only). */
|
|
571
|
+
runtimeOverlay?: RuntimeOverlay;
|
|
572
|
+
/** Show Gantt timeline */
|
|
573
|
+
showGantt?: boolean;
|
|
574
|
+
/** Layout direction */
|
|
575
|
+
layout?: "horizontal" | "vertical";
|
|
576
|
+
/** Title */
|
|
577
|
+
title?: string;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Full time-travel debugger: scrubber + recorder-driven flowchart +
|
|
581
|
+
* memory + narrative + gantt. This is the "batteries included"
|
|
582
|
+
* component for pipeline debugging.
|
|
583
|
+
*
|
|
584
|
+
* v6+: chart rendering is recorder-driven. Pass `graph` (always) and
|
|
585
|
+
* optionally `runtimeOverlay` for per-step coloring tied to the
|
|
586
|
+
* scrubber.
|
|
587
|
+
*/
|
|
588
|
+
declare function TimeTravelDebugger({ snapshots, graph, runtimeOverlay, showGantt, layout, title, size, unstyled, className, style, }: TimeTravelDebuggerProps): react_jsx_runtime.JSX.Element;
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* useSubflowNavigation — drill-down breadcrumb tracker for recorder-driven charts.
|
|
592
|
+
*
|
|
593
|
+
* Recorder-driven (v6+): replaces the legacy SpecNode-walk path. Accepts
|
|
594
|
+
* a `TraceGraph` (from `createTraceStructureRecorder`) and tracks WHICH
|
|
595
|
+
* subflow the user has drilled into.
|
|
596
|
+
*
|
|
597
|
+
* Limitation (intentional — recorder graph is flat / mount-only):
|
|
598
|
+
* The StructureRecorder records the MOUNT of each subflow in the
|
|
599
|
+
* parent chart, not the inner structure of each child chart. So
|
|
600
|
+
* "drill into a subflow" today returns the SAME graph with the
|
|
601
|
+
* `currentSubflowId` marker advanced — there is no separate
|
|
602
|
+
* child-graph to swap in. Filtering nodes by
|
|
603
|
+
* `data.subflowId === <selected>` would only surface mount nodes,
|
|
604
|
+
* not the child chart's stages.
|
|
605
|
+
*
|
|
606
|
+
* TODO(recorder-driven-nesting): when child charts attach their own
|
|
607
|
+
* `traceStructureRecorder` and surface those graphs via a registry,
|
|
608
|
+
* accept `Map<subflowId, TraceGraph>` and swap `currentGraph` to the
|
|
609
|
+
* child's graph on drill-down. Consumers can then render
|
|
610
|
+
* `<TraceFlow graph={currentGraph} />` per level.
|
|
611
|
+
*/
|
|
170
612
|
|
|
171
613
|
interface BreadcrumbEntry {
|
|
172
614
|
/** Display name for this level */
|
|
173
615
|
label: string;
|
|
174
|
-
/** The
|
|
175
|
-
|
|
616
|
+
/** The subflow id that was drilled into to reach this level
|
|
617
|
+
* (undefined for root). */
|
|
618
|
+
subflowId?: string;
|
|
176
619
|
/** Human-readable description of this subflow */
|
|
177
620
|
description?: string;
|
|
178
621
|
}
|
|
179
622
|
interface SubflowNavigation {
|
|
180
623
|
/** Current breadcrumb path (root → ... → current) */
|
|
181
624
|
breadcrumbs: BreadcrumbEntry[];
|
|
182
|
-
/** Current
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
/**
|
|
625
|
+
/** Current graph — today identical to the root graph (see file-level
|
|
626
|
+
* TODO). Consumers should still treat this as the source of truth so
|
|
627
|
+
* they remain forward-compatible once per-subflow graphs are wired in. */
|
|
628
|
+
currentGraph: TraceGraph;
|
|
629
|
+
/** Subflow id of the level the user is currently inside (null at root). */
|
|
630
|
+
currentSubflowId: string | null;
|
|
631
|
+
/** Display name of the subflow node we drilled into (null at root). */
|
|
632
|
+
currentSubflowNodeName: string | null;
|
|
633
|
+
/** Call when a node is clicked — drills in if it's a subflow.
|
|
634
|
+
* Returns true when the click pushed a new level. */
|
|
187
635
|
handleNodeClick: (nodeId: string) => boolean;
|
|
188
636
|
/** Navigate to a specific breadcrumb level (0 = root) */
|
|
189
637
|
navigateTo: (level: number) => void;
|
|
190
638
|
/** Whether we're currently inside a subflow (not at root) */
|
|
191
639
|
isInSubflow: boolean;
|
|
192
|
-
/** Name of the subflow node we drilled into (for finding execution data) */
|
|
193
|
-
currentSubflowNodeName: string | null;
|
|
194
640
|
}
|
|
195
641
|
/**
|
|
196
|
-
* Hook that
|
|
642
|
+
* Hook that tracks subflow drill-down state for a recorder-driven chart.
|
|
197
643
|
*
|
|
198
|
-
* Maintains a breadcrumb stack. When a subflow node is clicked
|
|
199
|
-
*
|
|
200
|
-
*
|
|
644
|
+
* Maintains a breadcrumb stack. When a subflow node is clicked the
|
|
645
|
+
* stack pushes a new entry; breadcrumb clicks pop back to that level.
|
|
646
|
+
* See file-level docs for the deferred per-subflow graph swap.
|
|
201
647
|
*/
|
|
202
|
-
declare function useSubflowNavigation(
|
|
648
|
+
declare function useSubflowNavigation(rootGraph: TraceGraph | null): SubflowNavigation;
|
|
203
649
|
|
|
204
650
|
interface SubflowBreadcrumbProps {
|
|
205
651
|
breadcrumbs: BreadcrumbEntry[];
|
|
@@ -220,12 +666,13 @@ interface SubflowTreeEntry {
|
|
|
220
666
|
subflowId?: string;
|
|
221
667
|
/** Whether this node is a subflow root (has nested structure) */
|
|
222
668
|
isSubflow?: boolean;
|
|
223
|
-
/** Nested children (subflow stages)
|
|
669
|
+
/** Nested children (subflow stages) — always undefined in the
|
|
670
|
+
* current recorder-driven implementation; see file-level TODO. */
|
|
224
671
|
children?: SubflowTreeEntry[];
|
|
225
672
|
}
|
|
226
673
|
interface SubflowTreeProps extends BaseComponentProps {
|
|
227
|
-
/**
|
|
228
|
-
|
|
674
|
+
/** Recorder-captured graph from `createTraceStructureRecorder().getGraph()`. */
|
|
675
|
+
graph: TraceGraph;
|
|
229
676
|
/** Currently active stage name (highlights in tree) */
|
|
230
677
|
activeStage?: string | null;
|
|
231
678
|
/** Set of completed stage names */
|
|
@@ -235,63 +682,1055 @@ interface SubflowTreeProps extends BaseComponentProps {
|
|
|
235
682
|
}
|
|
236
683
|
declare const SubflowTree: react.NamedExoticComponent<SubflowTreeProps>;
|
|
237
684
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
685
|
+
/** Layout function shape — takes a graph, returns a graph with positions set. */
|
|
686
|
+
type TraceFlowLayout = (graph: TraceGraph) => TraceGraph;
|
|
687
|
+
/**
|
|
688
|
+
* Default tree-walk layout.
|
|
689
|
+
*
|
|
690
|
+
* Edge-kind semantics (critical for parity)
|
|
691
|
+
* ─────────────────────────────────────────
|
|
692
|
+
* `fork-branch` + `decision-branch` edges are SIBLINGS at depth+1
|
|
693
|
+
* (centered under the parent). A `next` edge from a node that ALSO
|
|
694
|
+
* has branch edges is the "after convergence" target — its rank is
|
|
695
|
+
* `max(deepest branch subtree) + 1`, so it sits BELOW the fan-out,
|
|
696
|
+
* matching the legacy diamond layout.
|
|
697
|
+
*
|
|
698
|
+
* LoadOrder (fork)
|
|
699
|
+
* ├── fork-branch → CheckInventory (sibling, y+1)
|
|
700
|
+
* ├── fork-branch → RunFraudCheck (sibling, y+1)
|
|
701
|
+
* └── next → FinalizeOrder (after convergence,
|
|
702
|
+
* below deepest sibling)
|
|
703
|
+
*
|
|
704
|
+
* A `next` edge from a node with NO branch edges is a plain linear
|
|
705
|
+
* chain — target at depth+1.
|
|
706
|
+
*
|
|
707
|
+
* `loop` back-edges DO NOT shape layout (they're scrub-time visual
|
|
708
|
+
* markers only).
|
|
709
|
+
*
|
|
710
|
+
* Limitations (documented; not bugs)
|
|
711
|
+
* ──────────────────────────────────
|
|
712
|
+
* - Multi-root graphs render with the first-seen node as the visual
|
|
713
|
+
* root; orphans stack below.
|
|
714
|
+
* - Convergence where multiple branches join into a single node:
|
|
715
|
+
* the node is placed under its FIRST traversed branch (first-wins).
|
|
716
|
+
* For deep DAGs swap in a graph-layout library (dagre, elk).
|
|
717
|
+
* - Sibling x-positions are local to each parent's subtree; no
|
|
718
|
+
* global x-overlap detection across deep nested structures.
|
|
719
|
+
*/
|
|
720
|
+
declare const defaultTraceFlowLayout: TraceFlowLayout;
|
|
721
|
+
interface TraceFlowEdgeColors {
|
|
722
|
+
next: string;
|
|
723
|
+
forkBranch: string;
|
|
724
|
+
decisionBranch: string;
|
|
725
|
+
loop: string;
|
|
263
726
|
}
|
|
727
|
+
type TraceFlowSource = {
|
|
728
|
+
recorder: TraceStructureRecorderHandle;
|
|
729
|
+
graph?: never;
|
|
730
|
+
} | {
|
|
731
|
+
graph: TraceGraph;
|
|
732
|
+
recorder?: never;
|
|
733
|
+
};
|
|
734
|
+
type TraceFlowProps = BaseComponentProps & TraceFlowSource & {
|
|
735
|
+
/** Layout function. Default: BFS tree walk. Pass the literal
|
|
736
|
+
* `"passthrough"` to skip layout when nodes already carry positions. */
|
|
737
|
+
layout?: TraceFlowLayout | "passthrough";
|
|
738
|
+
/** Per-kind edge color overrides. */
|
|
739
|
+
edgeColors?: Partial<TraceFlowEdgeColors>;
|
|
740
|
+
/** Node click handler — receives the node id. */
|
|
741
|
+
onNodeClick?: (id: string) => void;
|
|
742
|
+
};
|
|
743
|
+
declare function TraceFlow(props: TraceFlowProps): react_jsx_runtime.JSX.Element;
|
|
744
|
+
|
|
745
|
+
interface TracedFlowColors {
|
|
746
|
+
/** Default (un-executed) node text + edge stroke. */
|
|
747
|
+
default: string;
|
|
748
|
+
/** Done — visually de-emphasised (lighter). */
|
|
749
|
+
done: string;
|
|
750
|
+
/** Active — current scrub position. */
|
|
751
|
+
active: string;
|
|
752
|
+
/** Error — node with recorded onError. */
|
|
753
|
+
error: string;
|
|
754
|
+
/** Loop back-edge color. */
|
|
755
|
+
loop: string;
|
|
756
|
+
}
|
|
757
|
+
interface TracedFlowProps extends BaseComponentProps {
|
|
758
|
+
/** Build-time graph from `createTraceStructureRecorder().getGraph()`. */
|
|
759
|
+
graph: TraceGraph;
|
|
760
|
+
/** Runtime overlay from `createTraceRuntimeOverlay().getOverlay()`. */
|
|
761
|
+
overlay?: RuntimeOverlay;
|
|
762
|
+
/** Time-travel scrub index. Defaults to the last step (latest state). */
|
|
763
|
+
scrubIndex?: number;
|
|
764
|
+
/** Layout function. Default: BFS tree walk over the recorder graph. */
|
|
765
|
+
layout?: TraceFlowLayout | "passthrough";
|
|
766
|
+
/** Color overrides. */
|
|
767
|
+
colors?: Partial<TracedFlowColors>;
|
|
768
|
+
/** Node click handler — receives stage id. */
|
|
769
|
+
onNodeClick?: (stageId: string) => void;
|
|
770
|
+
/**
|
|
771
|
+
* Fires when the chart drills into or out of a subflow (explicit
|
|
772
|
+
* user click on a mount node). Receives the mount stage id (drill
|
|
773
|
+
* in) or `null` (pop back). Container shells use this to keep
|
|
774
|
+
* their data panels in lock-step with the chart's drill state.
|
|
775
|
+
*/
|
|
776
|
+
onSubflowChange?: (mountStageId: string | null) => void;
|
|
777
|
+
}
|
|
778
|
+
declare function TracedFlow({ graph, overlay, scrubIndex, layout: layoutProp, colors: colorOverrides, onNodeClick, onSubflowChange, className, style, }: TracedFlowProps): react_jsx_runtime.JSX.Element;
|
|
779
|
+
|
|
264
780
|
/**
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
781
|
+
* createNodeViewRecorder — per-stage summary translator.
|
|
782
|
+
*
|
|
783
|
+
* Implements footprintjs v6.0+ `CombinedRecorder` (Flow + Scope) and
|
|
784
|
+
* produces a `NodeViewIndex` — per-stage data keyed by both `StageId`
|
|
785
|
+
* (chart-shape lookup) and `RuntimeStageId` (per-execution lookup).
|
|
786
|
+
*
|
|
787
|
+
* Composition (panel guidance, L8.0 review)
|
|
788
|
+
* ─────────────────────────────────────────
|
|
789
|
+
* NodeView does NOT duplicate the structural prev/next index — it
|
|
790
|
+
* READS those fields from the structure translator's
|
|
791
|
+
* `node.data.prevIds`/`nextIds`. The structure translator already
|
|
792
|
+
* maintains convergence-correct, loop-excluded neighbors. Duplicating
|
|
793
|
+
* the index here would re-introduce edge-ordering bugs we just
|
|
794
|
+
* solved.
|
|
795
|
+
*
|
|
796
|
+
* The NodeView translator owns ONLY runtime-derived per-stage state:
|
|
797
|
+
* - `executions[]` — every FlowRecorder.onStageExecuted event for
|
|
798
|
+
* this stage (loop-friendly: N executions of one stage produce
|
|
799
|
+
* N entries, each with its own runtimeStageId).
|
|
800
|
+
* - `commitRuntimeStageIds[]` — refs to commits made by this stage
|
|
801
|
+
* (the canonical CommitView lives in CommitFlowIndex from L8.2;
|
|
802
|
+
* NodeView stores only the join key).
|
|
803
|
+
*
|
|
804
|
+
* Plus derived convenience fields: `visitedInRun`, `executionCount`,
|
|
805
|
+
* `firstExecutedAt`, `lastExecutedAt`, `totalDurationMs`, `errorCount`.
|
|
806
|
+
*
|
|
807
|
+
* Version bumping
|
|
808
|
+
* ───────────────
|
|
809
|
+
* NodeView bumps its own version on EITHER:
|
|
810
|
+
* (a) a Flow/Scope event arrives (own state mutates), OR
|
|
811
|
+
* (b) the structure translator's version bumps (structural data
|
|
812
|
+
* the index projects from changed).
|
|
813
|
+
*
|
|
814
|
+
* So `useTranslator(nodeView, ...)` re-renders when EITHER changes —
|
|
815
|
+
* one subscription, full reactive coverage.
|
|
816
|
+
*
|
|
817
|
+
* @example
|
|
818
|
+
* ```tsx
|
|
819
|
+
* import { createTraceStructureRecorder, createNodeViewRecorder, useTranslator } from 'footprint-explainable-ui/flowchart';
|
|
820
|
+
*
|
|
821
|
+
* const trace = useMemo(() => createTraceStructureRecorder(), []);
|
|
822
|
+
* const nodeView = useMemo(() => createNodeViewRecorder({ structure: trace }), [trace]);
|
|
823
|
+
*
|
|
824
|
+
* useEffect(() => {
|
|
825
|
+
* executor.attachFlowRecorder(nodeView.recorder);
|
|
826
|
+
* executor.attachScopeRecorder(nodeView.recorder);
|
|
827
|
+
* }, [nodeView]);
|
|
828
|
+
*
|
|
829
|
+
* const index = useTranslator(nodeView, () => nodeView.getIndex());
|
|
830
|
+
* const finalize = index.byStageId.get(asStageId('FinalizeOrder'));
|
|
831
|
+
* // finalize.prevIds → ['CheckInventory', 'RunFraudCheck'] (from structure)
|
|
832
|
+
* // finalize.executions → [{ runtimeStageId, iteration, startMs, endMs, ... }]
|
|
833
|
+
* // finalize.visitedInRun → true
|
|
834
|
+
* // finalize.commitRuntimeStageIds → ['finalize-order#3', ...]
|
|
835
|
+
* ```
|
|
268
836
|
*/
|
|
269
|
-
declare const StageNode: react.NamedExoticComponent<Pick<_xyflow_react.Node<Record<string, unknown>, string | undefined>, "id" | "data" | "width" | "height" | "sourcePosition" | "targetPosition" | "dragHandle" | "parentId"> & Required<Pick<_xyflow_react.Node<Record<string, unknown>, string | undefined>, "type" | "dragging" | "zIndex" | "selectable" | "deletable" | "selected" | "draggable">> & {
|
|
270
|
-
isConnectable: boolean;
|
|
271
|
-
positionAbsoluteX: number;
|
|
272
|
-
positionAbsoluteY: number;
|
|
273
|
-
} & {
|
|
274
|
-
data: StageNodeData;
|
|
275
|
-
}>;
|
|
276
837
|
|
|
277
|
-
interface
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
838
|
+
interface TraversalContext$1 {
|
|
839
|
+
readonly runtimeStageId: string;
|
|
840
|
+
readonly iteration?: number;
|
|
841
|
+
readonly runId?: string;
|
|
842
|
+
}
|
|
843
|
+
interface FlowStageExecutedEvent$1 {
|
|
844
|
+
readonly stageName: string;
|
|
845
|
+
readonly stageId?: string;
|
|
846
|
+
readonly traversalContext: TraversalContext$1;
|
|
847
|
+
readonly startTime?: number;
|
|
848
|
+
readonly endTime?: number;
|
|
849
|
+
}
|
|
850
|
+
interface FlowErrorEvent$1 {
|
|
851
|
+
readonly stageName: string;
|
|
852
|
+
readonly stageId?: string;
|
|
853
|
+
readonly message?: string;
|
|
854
|
+
readonly traversalContext?: TraversalContext$1;
|
|
855
|
+
}
|
|
856
|
+
interface FlowRunLifecycleEvent$1 {
|
|
857
|
+
readonly traversalContext?: TraversalContext$1;
|
|
858
|
+
}
|
|
859
|
+
interface ScopeCommitEvent$1 {
|
|
860
|
+
readonly stage?: string;
|
|
861
|
+
readonly stageId?: string;
|
|
862
|
+
readonly runtimeStageId?: string;
|
|
863
|
+
readonly updates?: Record<string, unknown>;
|
|
864
|
+
readonly reads?: readonly string[];
|
|
290
865
|
}
|
|
291
866
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
867
|
+
* Minimal CombinedRecorder interface mirror — subset of footprintjs's
|
|
868
|
+
* Flow + Scope recorder interfaces NodeView consumes. Implements both
|
|
869
|
+
* channels in one object; consumer attaches via
|
|
870
|
+
* `executor.attachFlowRecorder()` AND `executor.attachScopeRecorder()`
|
|
871
|
+
* (or `executor.attachCombinedRecorder()` if available).
|
|
294
872
|
*/
|
|
295
|
-
|
|
873
|
+
interface MinimalNodeViewRecorder {
|
|
874
|
+
readonly id: string;
|
|
875
|
+
onStageExecuted?(event: FlowStageExecutedEvent$1): void;
|
|
876
|
+
onError?(event: FlowErrorEvent$1): void;
|
|
877
|
+
onRunStart?(event: FlowRunLifecycleEvent$1): void;
|
|
878
|
+
onRunEnd?(event: FlowRunLifecycleEvent$1): void;
|
|
879
|
+
onCommit?(event: ScopeCommitEvent$1): void;
|
|
880
|
+
}
|
|
881
|
+
/** One execution of a stage. A stage that runs N times in a loop
|
|
882
|
+
* produces N `ExecutionRecord` entries on the NodeView.executions[]. */
|
|
883
|
+
interface ExecutionRecord {
|
|
884
|
+
/** `[subflowPath/]stageId#executionIndex` — universal join key. */
|
|
885
|
+
readonly runtimeStageId: RuntimeStageId;
|
|
886
|
+
/** Loop iteration (0-indexed). undefined when the engine didn't
|
|
887
|
+
* populate it (rare). */
|
|
888
|
+
readonly iteration?: number;
|
|
889
|
+
/** ms since executor start. */
|
|
890
|
+
readonly startMs?: number;
|
|
891
|
+
/** ms since executor start. */
|
|
892
|
+
readonly endMs?: number;
|
|
893
|
+
/** Set when `onError` fired for this execution. */
|
|
894
|
+
readonly errorMessage?: string;
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Per-stage summary. Joins STRUCTURAL data (from the structure
|
|
898
|
+
* translator's TraceNode.data) with RUNTIME data (executions + commit
|
|
899
|
+
* refs accumulated by this translator).
|
|
900
|
+
*/
|
|
901
|
+
interface NodeView {
|
|
902
|
+
readonly stageId: StageId;
|
|
903
|
+
readonly label: string;
|
|
904
|
+
readonly type: "stage" | "decider" | "selector" | "fork" | "streaming" | "subflow" | "loop";
|
|
905
|
+
readonly isDecider: boolean;
|
|
906
|
+
readonly isFork: boolean;
|
|
907
|
+
readonly isSubflow: boolean;
|
|
908
|
+
readonly isStreaming: boolean;
|
|
909
|
+
readonly isPausable: boolean;
|
|
910
|
+
readonly description?: string;
|
|
911
|
+
readonly icon?: string;
|
|
912
|
+
readonly subflowId?: string;
|
|
913
|
+
readonly branchIds?: readonly string[];
|
|
914
|
+
readonly defaultBranch?: string;
|
|
915
|
+
/** From the structure translator (loop-excluded, convergence-correct). */
|
|
916
|
+
readonly prevIds: StageId[];
|
|
917
|
+
readonly nextIds: StageId[];
|
|
918
|
+
readonly executions: ExecutionRecord[];
|
|
919
|
+
/** Derived: `executions.length > 0`. */
|
|
920
|
+
readonly visitedInRun: boolean;
|
|
921
|
+
/** Derived: `executions.length` (loop iteration count). */
|
|
922
|
+
readonly executionCount: number;
|
|
923
|
+
/** Derived: first execution `startMs`, or `null` if not yet executed. */
|
|
924
|
+
readonly firstExecutedAt: number | null;
|
|
925
|
+
/** Derived: last execution `endMs`, or `null`. */
|
|
926
|
+
readonly lastExecutedAt: number | null;
|
|
927
|
+
/** Derived: sum of `endMs - startMs` across executions. */
|
|
928
|
+
readonly totalDurationMs: number;
|
|
929
|
+
/** Derived: count of executions with an errorMessage. */
|
|
930
|
+
readonly errorCount: number;
|
|
931
|
+
/** Run-time stage ids of commits made by this stage. Look up the
|
|
932
|
+
* canonical CommitView via `commitFlow.byRuntimeStageId.get(id)`. */
|
|
933
|
+
readonly commitRuntimeStageIds: RuntimeStageId[];
|
|
934
|
+
}
|
|
935
|
+
/** Index of NodeView keyed by both StageId and RuntimeStageId. The
|
|
936
|
+
* branded types prevent `byStageId.get(runtimeStageId)` silent-undefined. */
|
|
937
|
+
interface NodeViewIndex {
|
|
938
|
+
readonly byStageId: ReadonlyMap<StageId, NodeView>;
|
|
939
|
+
readonly byRuntimeStageId: ReadonlyMap<RuntimeStageId, NodeView>;
|
|
940
|
+
/** All NodeViews — insertion order matches structure translator. */
|
|
941
|
+
readonly all: readonly NodeView[];
|
|
942
|
+
}
|
|
943
|
+
interface NodeViewRecorderHandle {
|
|
944
|
+
/** CombinedRecorder — attach via BOTH `executor.attachFlowRecorder()`
|
|
945
|
+
* AND `executor.attachScopeRecorder()` (footprintjs routes by
|
|
946
|
+
* method-shape detection). */
|
|
947
|
+
recorder: MinimalNodeViewRecorder;
|
|
948
|
+
/** Returns a defensive copy of the current index state. */
|
|
949
|
+
getIndex(): NodeViewIndex;
|
|
950
|
+
/** Pub-sub: returns unsubscribe. Designed for `useSyncExternalStore`. */
|
|
951
|
+
subscribe(listener: () => void): () => void;
|
|
952
|
+
/** Monotonic version counter — bumps on Flow/Scope event OR when
|
|
953
|
+
* the structure translator's version bumps (since NodeView projects
|
|
954
|
+
* from its data). */
|
|
955
|
+
version(): number;
|
|
956
|
+
/** Reset runtime state. Does NOT bump version or notify (consistent
|
|
957
|
+
* with traceStructureRecorder + createTraceRuntimeOverlay). */
|
|
958
|
+
reset(): void;
|
|
959
|
+
}
|
|
960
|
+
interface CreateNodeViewRecorderOptions {
|
|
961
|
+
/** Recorder id (surfaces in error attribution). */
|
|
962
|
+
id?: string;
|
|
963
|
+
/** REQUIRED — the structure translator handle. NodeView projects
|
|
964
|
+
* structural fields (`prevIds`, `nextIds`, `label`, etc.) from
|
|
965
|
+
* this on every `getIndex()` call. */
|
|
966
|
+
structure: TraceStructureRecorderHandle;
|
|
967
|
+
}
|
|
968
|
+
declare function createNodeViewRecorder(options: CreateNodeViewRecorderOptions): NodeViewRecorderHandle;
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* createCommitFlowRecorder — per-commit summary translator.
|
|
972
|
+
*
|
|
973
|
+
* Owns the canonical `CommitView[]` — captured from Scope `onCommit`
|
|
974
|
+
* events as the chart runs. Each CommitView joins:
|
|
975
|
+
*
|
|
976
|
+
* - **Identity**: runtimeStageId, stageId, commitIdx
|
|
977
|
+
* - **Structural**: prev/next stage ids (read from structure
|
|
978
|
+
* translator's `TraceNode.data.prevIds`/`nextIds`)
|
|
979
|
+
* - **Runtime prev/next**: derived via "most-recent-per-structural-
|
|
980
|
+
* prev" rule over the commitLog (loop-safe, convergence-aware)
|
|
981
|
+
* - **Data dependencies**: per-read-key lineage via
|
|
982
|
+
* `findLastWriter` over the commitLog
|
|
983
|
+
* - **Payload**: updates + reads
|
|
984
|
+
*
|
|
985
|
+
* Composition (panel guidance, L8.1 review)
|
|
986
|
+
* ─────────────────────────────────────────
|
|
987
|
+
* Per Panel 1 rule "translators MUST subscribe to structure
|
|
988
|
+
* directly, never to peer translators", CommitFlow subscribes to
|
|
989
|
+
* the structure handle (NOT to NodeView). Linear dependency tree
|
|
990
|
+
* keeps notify cascades O(depth) instead of O(N²).
|
|
991
|
+
*
|
|
992
|
+
* NodeView (L8.1) stores `commitRuntimeStageIds[]` — refs only —
|
|
993
|
+
* pointing into CommitFlow's `byRuntimeStageId`. The canonical
|
|
994
|
+
* payload lives HERE; NodeView is the per-stage projection.
|
|
995
|
+
*
|
|
996
|
+
* @example
|
|
997
|
+
* ```tsx
|
|
998
|
+
* const trace = useMemo(() => createTraceStructureRecorder(), []);
|
|
999
|
+
* const commitFlow = useMemo(
|
|
1000
|
+
* () => createCommitFlowRecorder({ structure: trace }),
|
|
1001
|
+
* [trace],
|
|
1002
|
+
* );
|
|
1003
|
+
* useEffect(() => {
|
|
1004
|
+
* executor.attachFlowRecorder(commitFlow.recorder);
|
|
1005
|
+
* executor.attachScopeRecorder(commitFlow.recorder);
|
|
1006
|
+
* }, [commitFlow]);
|
|
1007
|
+
*
|
|
1008
|
+
* const index = useTranslator(commitFlow, () => commitFlow.getIndex());
|
|
1009
|
+
* const c = index.byRuntimeStageId.get(asRuntimeStageId('finalize-order#3'));
|
|
1010
|
+
* c.structuralPrevIds // ['CheckInventory', 'RunFraudCheck']
|
|
1011
|
+
* c.runtimePrevIds // ['check-inventory#1', 'run-fraud-check#2']
|
|
1012
|
+
* c.dataDependencies // [{ key:'inStock', sourceRuntimeStageId:'check-inventory#1', sourceCommitIdx:1 }, ...]
|
|
1013
|
+
*
|
|
1014
|
+
* const lineage = backtraceDataFlow(index, asRuntimeStageId('finalize-order#3'));
|
|
1015
|
+
* // → ancestry chain of commits whose writes contributed to this commit's reads
|
|
1016
|
+
* ```
|
|
1017
|
+
*/
|
|
1018
|
+
|
|
1019
|
+
interface TraversalContext {
|
|
1020
|
+
readonly runtimeStageId: string;
|
|
1021
|
+
readonly iteration?: number;
|
|
1022
|
+
readonly runId?: string;
|
|
1023
|
+
}
|
|
1024
|
+
interface FlowStageExecutedEvent {
|
|
1025
|
+
readonly stageName: string;
|
|
1026
|
+
readonly stageId?: string;
|
|
1027
|
+
readonly traversalContext: TraversalContext;
|
|
1028
|
+
}
|
|
1029
|
+
interface FlowErrorEvent {
|
|
1030
|
+
readonly stageName: string;
|
|
1031
|
+
readonly stageId?: string;
|
|
1032
|
+
readonly message?: string;
|
|
1033
|
+
readonly traversalContext?: TraversalContext;
|
|
1034
|
+
}
|
|
1035
|
+
interface FlowRunLifecycleEvent {
|
|
1036
|
+
readonly traversalContext?: TraversalContext;
|
|
1037
|
+
}
|
|
1038
|
+
interface ScopeReadEvent {
|
|
1039
|
+
readonly stage?: string;
|
|
1040
|
+
readonly stageId?: string;
|
|
1041
|
+
readonly runtimeStageId?: string;
|
|
1042
|
+
readonly key?: string;
|
|
1043
|
+
}
|
|
1044
|
+
interface ScopeCommitEvent {
|
|
1045
|
+
readonly stage?: string;
|
|
1046
|
+
readonly stageId?: string;
|
|
1047
|
+
readonly runtimeStageId?: string;
|
|
1048
|
+
readonly updates?: Record<string, unknown>;
|
|
1049
|
+
readonly reads?: readonly string[];
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Minimal CombinedRecorder mirror — subset of footprintjs's Flow +
|
|
1053
|
+
* Scope channels CommitFlow consumes.
|
|
1054
|
+
*/
|
|
1055
|
+
interface MinimalCommitFlowRecorder {
|
|
1056
|
+
readonly id: string;
|
|
1057
|
+
onStageExecuted?(event: FlowStageExecutedEvent): void;
|
|
1058
|
+
onError?(event: FlowErrorEvent): void;
|
|
1059
|
+
onRunStart?(event: FlowRunLifecycleEvent): void;
|
|
1060
|
+
onRunEnd?(event: FlowRunLifecycleEvent): void;
|
|
1061
|
+
onRead?(event: ScopeReadEvent): void;
|
|
1062
|
+
onCommit?(event: ScopeCommitEvent): void;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* One data-dependency edge — a read key in this commit AND the commit
|
|
1066
|
+
* whose `updates` last wrote that key before this commit's index.
|
|
1067
|
+
*
|
|
1068
|
+
* `sourceRuntimeStageId` and `sourceCommitIdx` are BOTH `null` when no
|
|
1069
|
+
* prior commit wrote the key (panel-flagged: NEVER `undefined` — forces
|
|
1070
|
+
* consumers to handle absence as load-bearing).
|
|
1071
|
+
*/
|
|
1072
|
+
interface DataDependency {
|
|
1073
|
+
readonly key: string;
|
|
1074
|
+
readonly sourceRuntimeStageId: RuntimeStageId | null;
|
|
1075
|
+
readonly sourceCommitIdx: number | null;
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Per-commit summary. Joins structural data (from the structure
|
|
1079
|
+
* translator) with runtime data (commitLog timeline) into a single
|
|
1080
|
+
* consumer-friendly shape.
|
|
1081
|
+
*
|
|
1082
|
+
* Three "prev" views — pick the right one for your UI:
|
|
1083
|
+
* ─────────────────────────────────────────────────────
|
|
1084
|
+
* | Field | Answers |
|
|
1085
|
+
* |----------------------|--------------------------------------------|
|
|
1086
|
+
* | `structuralPrevIds` | "what COULD flow in" (chart shape) |
|
|
1087
|
+
* | `runtimePrevIds` | "what DID flow in by stage" (execution |
|
|
1088
|
+
* | | timeline; loop-aware) |
|
|
1089
|
+
* | `dataDependencies` | "which commit wrote the bytes I read" |
|
|
1090
|
+
* | | (causal data lineage; per-key) |
|
|
1091
|
+
*
|
|
1092
|
+
* The three answers usually align on simple charts (linear; single
|
|
1093
|
+
* fork-merge). They DIVERGE in interesting ways under loops (runtime
|
|
1094
|
+
* differs by iteration; data-deps may skip uninvolved stages) and
|
|
1095
|
+
* conditional branches (structural sees all options; runtime + data
|
|
1096
|
+
* see only the chosen path).
|
|
1097
|
+
*/
|
|
1098
|
+
interface CommitView {
|
|
1099
|
+
readonly runtimeStageId: RuntimeStageId;
|
|
1100
|
+
readonly stageId: StageId;
|
|
1101
|
+
/** Position in the chronologically-ordered commitLog. */
|
|
1102
|
+
readonly commitIdx: number;
|
|
1103
|
+
/** Stage ids that lead into this stage in the chart (loop-excluded). */
|
|
1104
|
+
readonly structuralPrevIds: StageId[];
|
|
1105
|
+
readonly structuralNextIds: StageId[];
|
|
1106
|
+
/**
|
|
1107
|
+
* For each structural prev stage that EXECUTED IN THIS RUN before
|
|
1108
|
+
* this commit, the most-recent runtimeStageId of that prev's commit.
|
|
1109
|
+
* Loop-safe: in iteration 2, runtime prev points at iteration 2's
|
|
1110
|
+
* commits (not iteration 1's).
|
|
1111
|
+
*/
|
|
1112
|
+
readonly runtimePrevIds: RuntimeStageId[];
|
|
1113
|
+
/**
|
|
1114
|
+
* For each structural next stage that EXECUTED IN THIS RUN after
|
|
1115
|
+
* this commit, the soonest runtimeStageId. Symmetric to prev.
|
|
1116
|
+
*/
|
|
1117
|
+
readonly runtimeNextIds: RuntimeStageId[];
|
|
1118
|
+
/**
|
|
1119
|
+
* One entry per key in `reads`. `sourceRuntimeStageId` points at the
|
|
1120
|
+
* commit whose `updates` last set this key BEFORE this commit's
|
|
1121
|
+
* index; `null` when no prior writer (typically: read of a key
|
|
1122
|
+
* declared as default but never written by another stage).
|
|
1123
|
+
*/
|
|
1124
|
+
readonly dataDependencies: DataDependency[];
|
|
1125
|
+
/**
|
|
1126
|
+
* The commit's writes. **Shallow-copied** at intake — top-level
|
|
1127
|
+
* keys are owned, but nested object values are SHARED references
|
|
1128
|
+
* to whatever the upstream `onCommit` payload carried. Consumers
|
|
1129
|
+
* MUST NOT mutate nested values; doing so corrupts the recorder's
|
|
1130
|
+
* internal state and propagates through `findLastWriter` to every
|
|
1131
|
+
* subsequent `getIndex()` call.
|
|
1132
|
+
*/
|
|
1133
|
+
readonly updates: Record<string, unknown>;
|
|
1134
|
+
readonly reads: string[];
|
|
1135
|
+
}
|
|
1136
|
+
/** Indexed CommitView access. */
|
|
1137
|
+
interface CommitFlowIndex {
|
|
1138
|
+
/** Ordered list — index === commitIdx. */
|
|
1139
|
+
readonly commits: readonly CommitView[];
|
|
1140
|
+
/** Lookup by runtimeStageId (the universal join key). */
|
|
1141
|
+
readonly byRuntimeStageId: ReadonlyMap<RuntimeStageId, CommitView>;
|
|
1142
|
+
/**
|
|
1143
|
+
* Data-dependency edges — `from` and `to` are commitIdx ints; `key`
|
|
1144
|
+
* is the data key that flowed from one commit to the other. Same
|
|
1145
|
+
* data as walking each CommitView's `dataDependencies` — exposed as
|
|
1146
|
+
* a flat edge list for graph algorithms and rendering.
|
|
1147
|
+
*
|
|
1148
|
+
* **When to use which**:
|
|
1149
|
+
* - `view.dataDependencies` — render ONE commit's inputs in a
|
|
1150
|
+
* detail panel (e.g., `<CommitInspector>`).
|
|
1151
|
+
* - `index.dataEdges` — draw the FULL data-flow DAG (React Flow
|
|
1152
|
+
* edges, GraphViz, dependency matrix, etc.). One pass over the
|
|
1153
|
+
* flat list = the whole graph.
|
|
1154
|
+
*
|
|
1155
|
+
* Edges are `Object.freeze()`d at construction — type `readonly` is
|
|
1156
|
+
* compile-time; freeze is the runtime enforcement.
|
|
1157
|
+
*/
|
|
1158
|
+
readonly dataEdges: readonly {
|
|
1159
|
+
readonly from: number;
|
|
1160
|
+
readonly to: number;
|
|
1161
|
+
readonly key: string;
|
|
1162
|
+
}[];
|
|
1163
|
+
}
|
|
1164
|
+
interface CommitFlowRecorderHandle {
|
|
1165
|
+
/** CombinedRecorder — attach via BOTH `executor.attachFlowRecorder()`
|
|
1166
|
+
* AND `executor.attachScopeRecorder()`. */
|
|
1167
|
+
recorder: MinimalCommitFlowRecorder;
|
|
1168
|
+
/** Returns a defensive copy of the current index state. */
|
|
1169
|
+
getIndex(): CommitFlowIndex;
|
|
1170
|
+
/** Pub-sub: returns unsubscribe. */
|
|
1171
|
+
subscribe(listener: () => void): () => void;
|
|
1172
|
+
/** Monotonic version counter — bumps on Scope/Flow event OR when
|
|
1173
|
+
* the structure translator's version bumps. */
|
|
1174
|
+
version(): number;
|
|
1175
|
+
/** Reset runtime state. Does NOT bump version or notify (consistent
|
|
1176
|
+
* with other translators' reset contracts). */
|
|
1177
|
+
reset(): void;
|
|
1178
|
+
}
|
|
1179
|
+
interface CreateCommitFlowRecorderOptions {
|
|
1180
|
+
/** Recorder id (surfaces in error attribution). */
|
|
1181
|
+
id?: string;
|
|
1182
|
+
/** REQUIRED — structure translator handle for structural prev/next
|
|
1183
|
+
* projection. CommitFlow subscribes to its version. */
|
|
1184
|
+
structure: TraceStructureRecorderHandle;
|
|
1185
|
+
}
|
|
1186
|
+
declare function createCommitFlowRecorder(options: CreateCommitFlowRecorderOptions): CommitFlowRecorderHandle;
|
|
1187
|
+
/**
|
|
1188
|
+
* Walk data-dependency edges BACKWARD from a starting commit. Returns
|
|
1189
|
+
* the lineage chain — every ancestor commit whose writes transitively
|
|
1190
|
+
* contributed to the starting commit's reads.
|
|
1191
|
+
*
|
|
1192
|
+
* BFS order, commitIdx-ascending (oldest first). Includes the starting
|
|
1193
|
+
* commit at the END of the returned array.
|
|
1194
|
+
*
|
|
1195
|
+
* Cycle defense: visited set + recursion cap at `index.commits.length`
|
|
1196
|
+
* (per L8 panel must-fix — defends against future malformed graphs).
|
|
1197
|
+
*
|
|
1198
|
+
* @example
|
|
1199
|
+
* ```ts
|
|
1200
|
+
* const lineage = backtraceDataFlow(commitIndex, asRuntimeStageId('finalize-order#3'));
|
|
1201
|
+
* // → [load-order#0, check-inventory#1, run-fraud-check#2, finalize-order#3]
|
|
1202
|
+
* // (the commits whose updates fed FinalizeOrder's reads)
|
|
1203
|
+
* ```
|
|
1204
|
+
*/
|
|
1205
|
+
declare function backtraceDataFlow(index: CommitFlowIndex, startRuntimeStageId: RuntimeStageId): CommitView[];
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* `createTraceBundle` — one factory wires every shipped translator
|
|
1209
|
+
* + provides a single `attachTo(executor)` call that registers all
|
|
1210
|
+
* runtime recorders.
|
|
1211
|
+
*
|
|
1212
|
+
* Bundle shape (as of L8.2)
|
|
1213
|
+
* ─────────────────────────
|
|
1214
|
+
* - `structure` — build-time `TraceGraph` (StructureRecorder).
|
|
1215
|
+
* - `runtimeOverlay` — execution overlay for time-travel (FlowRecorder).
|
|
1216
|
+
* - `nodeView` — per-stage summary index, CombinedRecorder
|
|
1217
|
+
* (FlowRecorder + ScopeRecorder).
|
|
1218
|
+
* - `commitFlow` — per-commit summary + data-lineage,
|
|
1219
|
+
* CombinedRecorder (ScopeRecorder + FlowRecorder).
|
|
1220
|
+
*
|
|
1221
|
+
* Goal: collapse the 5-line "create + attach + subscribe" boilerplate
|
|
1222
|
+
* into one call for the common case. Consumers that want à-la-carte
|
|
1223
|
+
* setup can still construct each translator directly (the underlying
|
|
1224
|
+
* factories are unchanged).
|
|
1225
|
+
*
|
|
1226
|
+
* Independence: each translator subscribes ONLY to `structure` (per
|
|
1227
|
+
* L8.1 Panel 1 rule — translators must never subscribe to peers).
|
|
1228
|
+
* Linear dependency tree keeps notify cascades O(depth), not O(N²).
|
|
1229
|
+
*
|
|
1230
|
+
* @example
|
|
1231
|
+
* ```tsx
|
|
1232
|
+
* import { createTraceBundle } from 'footprint-explainable-ui/flowchart';
|
|
1233
|
+
*
|
|
1234
|
+
* function MyTraceUI() {
|
|
1235
|
+
* const bundle = useMemo(() => createTraceBundle(), []);
|
|
1236
|
+
* useEffect(() => {
|
|
1237
|
+
* const chart = flowChart('seed', fn, 'seed', {
|
|
1238
|
+
* structureRecorders: [bundle.structure.recorder],
|
|
1239
|
+
* }).addFunction('a', fnA, 'a').build();
|
|
1240
|
+
* const executor = new FlowChartExecutor(chart);
|
|
1241
|
+
* bundle.attachTo(executor);
|
|
1242
|
+
* executor.run({ input });
|
|
1243
|
+
* }, [bundle]);
|
|
1244
|
+
*
|
|
1245
|
+
* return <TracedFlow recorder={bundle.structure} overlay={bundle.runtimeOverlay} />;
|
|
1246
|
+
* }
|
|
1247
|
+
* ```
|
|
1248
|
+
*
|
|
1249
|
+
* **Recorder-attach lifecycle**: `attachTo(executor)` attaches THREE
|
|
1250
|
+
* FlowRecorders (`runtimeOverlay`, `nodeView`, `commitFlow`) and TWO
|
|
1251
|
+
* ScopeRecorders (`nodeView`, `commitFlow`). Each translator has a
|
|
1252
|
+
* distinct `recorder.id`, so footprintjs's id-idempotency rule
|
|
1253
|
+
* doesn't collide. The build-time `structure.recorder` is NOT
|
|
1254
|
+
* auto-attached — it must be passed to `flowChart(..., {
|
|
1255
|
+
* structureRecorders: [...] })` because builder recorders register
|
|
1256
|
+
* BEFORE `executor` exists. Consumers wire that one explicitly (see
|
|
1257
|
+
* example above). This matches footprintjs's two-phase recorder
|
|
1258
|
+
* model (build vs runtime).
|
|
1259
|
+
*/
|
|
1260
|
+
|
|
1261
|
+
/** Minimal executor surface — mirrors the bits of footprintjs's
|
|
1262
|
+
* `FlowChartExecutor` the bundle calls into. Local to keep
|
|
1263
|
+
* explainable-ui dep-free from footprintjs. */
|
|
1264
|
+
interface MinimalExecutor {
|
|
1265
|
+
attachFlowRecorder(recorder: unknown): void;
|
|
1266
|
+
attachScopeRecorder?(recorder: unknown): void;
|
|
1267
|
+
}
|
|
1268
|
+
interface TraceBundle {
|
|
1269
|
+
structure: TraceStructureRecorderHandle;
|
|
1270
|
+
runtimeOverlay: TraceRuntimeOverlayHandle;
|
|
1271
|
+
/** L8.1 — per-stage summary translator. Reads `prevIds`/`nextIds`
|
|
1272
|
+
* from `structure` (no duplication), accumulates `executions[]`
|
|
1273
|
+
* + commit refs from Flow + Scope events. */
|
|
1274
|
+
nodeView: NodeViewRecorderHandle;
|
|
1275
|
+
/** L8.2 — per-commit summary translator. Owns canonical CommitView[].
|
|
1276
|
+
* Derives structural prev/next from `structure`, runtimePrev/Next
|
|
1277
|
+
* from "most-recent-per-prev" over commitLog, and dataDependencies
|
|
1278
|
+
* via findLastWriter per read key. */
|
|
1279
|
+
commitFlow: CommitFlowRecorderHandle;
|
|
1280
|
+
/**
|
|
1281
|
+
* Attach RUNTIME recorders (FlowRecorder, ScopeRecorder) to a
|
|
1282
|
+
* footprintjs executor. The build-time `structure.recorder` is NOT
|
|
1283
|
+
* covered — pass it to `flowChart(..., { structureRecorders: [...] })`
|
|
1284
|
+
* at construction time instead (see file header example).
|
|
1285
|
+
*/
|
|
1286
|
+
attachTo(executor: MinimalExecutor): void;
|
|
1287
|
+
}
|
|
1288
|
+
interface CreateTraceBundleOptions {
|
|
1289
|
+
/** Per-translator options. All optional. */
|
|
1290
|
+
structure?: CreateTraceStructureRecorderOptions;
|
|
1291
|
+
runtimeOverlay?: CreateTraceRuntimeOverlayOptions;
|
|
1292
|
+
nodeView?: Omit<CreateNodeViewRecorderOptions, "structure">;
|
|
1293
|
+
commitFlow?: Omit<CreateCommitFlowRecorderOptions, "structure">;
|
|
1294
|
+
}
|
|
1295
|
+
declare function createTraceBundle(options?: CreateTraceBundleOptions): TraceBundle;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* `useTranslator(handle, getSnapshot)` — React adapter for any translator
|
|
1299
|
+
* exposing the standard `subscribe()` + `version()` shape.
|
|
1300
|
+
*
|
|
1301
|
+
* Wraps `useSyncExternalStore` so consumers don't repeat the boilerplate
|
|
1302
|
+
* for every translator. The `version` is the snapshot identity — when it
|
|
1303
|
+
* changes, React re-renders; `getSnapshot` then returns the consumer-
|
|
1304
|
+
* chosen view (full graph, slice, etc.).
|
|
1305
|
+
*
|
|
1306
|
+
* @example
|
|
1307
|
+
* ```tsx
|
|
1308
|
+
* const trace = useMemo(() => createTraceStructureRecorder(), []);
|
|
1309
|
+
* const graph = useTranslator(trace, () => trace.getGraph());
|
|
1310
|
+
* return <TraceFlow graph={graph} />;
|
|
1311
|
+
* ```
|
|
1312
|
+
*
|
|
1313
|
+
* **Why a hook (vs each component wiring `useSyncExternalStore` itself)**:
|
|
1314
|
+
* three reasons. (1) Reduces 4 lines of `useSyncExternalStore + useMemo`
|
|
1315
|
+
* boilerplate to one line. (2) Standardises the snapshot-identity
|
|
1316
|
+
* convention (version int) — consumers can't accidentally pass a getter
|
|
1317
|
+
* whose return identity changes every call (which would re-render
|
|
1318
|
+
* forever). (3) Stable subscribe/getVersion refs via memoization on the
|
|
1319
|
+
* handle identity — no re-subscribe on parent re-renders.
|
|
1320
|
+
*/
|
|
1321
|
+
interface TranslatorHandleLike {
|
|
1322
|
+
subscribe(listener: () => void): () => void;
|
|
1323
|
+
version(): number;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Subscribe to a translator + return a typed snapshot computed by the
|
|
1327
|
+
* caller. The snapshot recomputes whenever `version()` changes.
|
|
1328
|
+
*
|
|
1329
|
+
* `getSnapshot` is called inside a `useMemo` keyed on the version
|
|
1330
|
+
* integer — so consumers can return fresh objects from `getSnapshot`
|
|
1331
|
+
* (e.g., `handle.getGraph()` returns a defensive copy) without
|
|
1332
|
+
* triggering infinite re-renders.
|
|
1333
|
+
*/
|
|
1334
|
+
declare function useTranslator<T>(handle: TranslatorHandleLike, getSnapshot: () => T): T;
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* Graph traversal helpers — pure functions over a `NodeViewIndex`.
|
|
1338
|
+
*
|
|
1339
|
+
* Bi-directional structural walks (`walkForward` / `walkBackward`) plus
|
|
1340
|
+
* convenience wrappers (`backtraceStructural` / `forwardtraceStructural`).
|
|
1341
|
+
*
|
|
1342
|
+
* Cycle defense (panel L8 must-fix)
|
|
1343
|
+
* ──────────────────────────────────
|
|
1344
|
+
* Every traversal carries a `visited: Set<StageId>` and caps recursion
|
|
1345
|
+
* at `index.all.length` (the total node count). Defends against:
|
|
1346
|
+
* - Future bugs where a malformed graph includes a non-loop cycle
|
|
1347
|
+
* - Manual edge injection bypassing the structure-recorder fix
|
|
1348
|
+
* - Duplicate edges that survive dedupe due to source/target
|
|
1349
|
+
* collisions
|
|
1350
|
+
* Loop back-edges (`data.kind === 'loop'`) already excluded at the
|
|
1351
|
+
* structure-recorder layer (invariant I1), so they never enter
|
|
1352
|
+
* `prevIds`/`nextIds` — no extra filtering here.
|
|
1353
|
+
*
|
|
1354
|
+
* BFS order
|
|
1355
|
+
* ─────────
|
|
1356
|
+
* Walks return nodes in BFS order — root first, then immediate
|
|
1357
|
+
* neighbors, then their neighbors. Matches breadcrumb-friendly
|
|
1358
|
+
* "closest-first" display order.
|
|
1359
|
+
*
|
|
1360
|
+
* Always-arrays returns
|
|
1361
|
+
* ─────────────────────
|
|
1362
|
+
* All helpers return `NodeView[]`. Empty array (NOT null/undefined)
|
|
1363
|
+
* means "no path" or "node not found". Consumers `.map()` /
|
|
1364
|
+
* `.length`-check without null-handling boilerplate.
|
|
1365
|
+
*/
|
|
1366
|
+
|
|
1367
|
+
interface WalkOptions {
|
|
1368
|
+
/** Hard cap on hops. Default: `index.all.length` (one full traversal). */
|
|
1369
|
+
maxDepth?: number;
|
|
1370
|
+
/** When true, only return nodes that have `visitedInRun === true`.
|
|
1371
|
+
* Useful for "in THIS run, what fed me?" queries (runtime-aware walk). */
|
|
1372
|
+
onlyVisited?: boolean;
|
|
1373
|
+
/** When true, INCLUDE the start node in the returned array. Default:
|
|
1374
|
+
* false (caller already has the start node; the walk returns its
|
|
1375
|
+
* ancestors/descendants). */
|
|
1376
|
+
includeStart?: boolean;
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Walk forward (`nextIds`) from `startId`. BFS order. Cycle-safe.
|
|
1380
|
+
* Returns `[]` if `startId` is unknown.
|
|
1381
|
+
*
|
|
1382
|
+
* Convergence semantics: a node reachable via multiple paths appears
|
|
1383
|
+
* ONCE in the result (visited-set dedupes).
|
|
1384
|
+
*/
|
|
1385
|
+
declare function walkForward(index: NodeViewIndex, startId: StageId, options?: WalkOptions): NodeView[];
|
|
1386
|
+
/**
|
|
1387
|
+
* Walk backward (`prevIds`) from `startId`. BFS order. Cycle-safe.
|
|
1388
|
+
* Returns `[]` if `startId` is unknown.
|
|
1389
|
+
*
|
|
1390
|
+
* Convergence semantics: a fork-join's `walkBackward` returns BOTH
|
|
1391
|
+
* branch parents (and their ancestors), each once.
|
|
1392
|
+
*/
|
|
1393
|
+
declare function walkBackward(index: NodeViewIndex, startId: StageId, options?: WalkOptions): NodeView[];
|
|
1394
|
+
/**
|
|
1395
|
+
* Backtrace from `stageId` to a structural root (no prev) OR a split
|
|
1396
|
+
* point (multiple prevs). Returns the chain from `stageId` BACK to
|
|
1397
|
+
* the root, root-first. Includes `stageId` at the END.
|
|
1398
|
+
*
|
|
1399
|
+
* Defines "split" as `prevIds.length > 1 OR prevIds.length === 0`
|
|
1400
|
+
* (per L8 panel recommendation) — stops at convergence points AND at
|
|
1401
|
+
* the seed. Useful for breadcrumb display where "innermost serial
|
|
1402
|
+
* run" is the meaningful path.
|
|
1403
|
+
*/
|
|
1404
|
+
declare function backtraceStructural(index: NodeViewIndex, stageId: StageId, options?: Pick<WalkOptions, "maxDepth" | "onlyVisited">): NodeView[];
|
|
1405
|
+
/**
|
|
1406
|
+
* Forward-trace from `stageId` to a structural leaf (no next) OR a
|
|
1407
|
+
* fork point (multiple nexts). Returns the chain from `stageId`
|
|
1408
|
+
* FORWARD to the leaf/fork, with `stageId` at the START.
|
|
1409
|
+
*/
|
|
1410
|
+
declare function forwardtraceStructural(index: NodeViewIndex, stageId: StageId, options?: Pick<WalkOptions, "maxDepth" | "onlyVisited">): NodeView[];
|
|
1411
|
+
|
|
1412
|
+
interface NodeInspectorProps extends BaseComponentProps {
|
|
1413
|
+
/** The NodeView index (from `nodeView.getIndex()` or `useTranslator`).
|
|
1414
|
+
*
|
|
1415
|
+
* **Pure data prop** (NOT the recorder handle) — by design. Lets
|
|
1416
|
+
* consumers wrap in `useTranslator(nodeView, () => nodeView.getIndex())`
|
|
1417
|
+
* at the parent, or pass a static index from tests / Storybook /
|
|
1418
|
+
* SSR. Mirrors `<CommitInspector index={...}>` for consistency. */
|
|
1419
|
+
index: NodeViewIndex;
|
|
1420
|
+
/** Which stage to inspect. When `null`, renders the placeholder. */
|
|
1421
|
+
selectedId: StageId | null;
|
|
1422
|
+
/** Click handler — receives a stageId for breadcrumb navigation. */
|
|
1423
|
+
onNavigate?: (stageId: StageId) => void;
|
|
1424
|
+
/** When true, the prev/next chains include only `visitedInRun` nodes
|
|
1425
|
+
* (runtime-filtered view). */
|
|
1426
|
+
onlyVisited?: boolean;
|
|
1427
|
+
}
|
|
1428
|
+
declare function NodeInspector({ index, selectedId, onNavigate, onlyVisited, className, style, }: NodeInspectorProps): react_jsx_runtime.JSX.Element;
|
|
1429
|
+
|
|
1430
|
+
interface CommitInspectorProps extends BaseComponentProps {
|
|
1431
|
+
/** The CommitFlow index (from `commitFlow.getIndex()` or `useTranslator`).
|
|
1432
|
+
*
|
|
1433
|
+
* **Pure data prop** (NOT the recorder handle) — by design. Lets
|
|
1434
|
+
* consumers wrap in `useTranslator(commitFlow, () => commitFlow.getIndex())`
|
|
1435
|
+
* at the parent, or pass a static index from tests / Storybook /
|
|
1436
|
+
* SSR. Mirrors `<NodeInspector index={...}>` for consistency. */
|
|
1437
|
+
index: CommitFlowIndex;
|
|
1438
|
+
/** Which commit to inspect (by runtimeStageId). null → placeholder. */
|
|
1439
|
+
selectedRuntimeStageId: RuntimeStageId | null;
|
|
1440
|
+
/** Click handler — receives a runtimeStageId for time-travel
|
|
1441
|
+
* navigation. Used by all clickable references (runtime prev/next,
|
|
1442
|
+
* data-dep sources, lineage chain). */
|
|
1443
|
+
onNavigate?: (runtimeStageId: RuntimeStageId) => void;
|
|
1444
|
+
}
|
|
1445
|
+
declare function CommitInspector({ index, selectedRuntimeStageId, onNavigate, className, style, }: CommitInspectorProps): react_jsx_runtime.JSX.Element;
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Series-parallel chain tree builders — pure functions over `TraceGraph`
|
|
1449
|
+
* (structural) + `CommitFlowIndex` (runtime).
|
|
1450
|
+
*
|
|
1451
|
+
* When to reach for this vs. flat commitLog
|
|
1452
|
+
* ─────────────────────────────────────────
|
|
1453
|
+
* Use `commitLog` when you want chronological replay / slider scrubbing
|
|
1454
|
+
* ("what happened in order"). Use the chain tree when you want to
|
|
1455
|
+
* render parallel structure visually or answer "which branch ran?"
|
|
1456
|
+
* questions — flat `commitLog[idx-1]` loses the fork shape.
|
|
1457
|
+
*
|
|
1458
|
+
* Pairs with `<CommitInspector>` for git-log-style master/detail —
|
|
1459
|
+
* `<CommitChainView>` picks, `<CommitInspector>` inspects.
|
|
1460
|
+
* Complements the per-stage view from `createNodeViewRecorder`.
|
|
1461
|
+
*
|
|
1462
|
+
* Why a chain tree
|
|
1463
|
+
* ────────────────
|
|
1464
|
+
* Footprintjs charts are **series-parallel DAGs by construction** —
|
|
1465
|
+
* the builder primitives compose serially (`addFunction`) or in
|
|
1466
|
+
* parallel (`addListOfFunction` / `addDeciderFunction`). Series-
|
|
1467
|
+
* parallel DAGs are provably representable as expression trees,
|
|
1468
|
+
* so we can decompose any footprintjs chart into a nested
|
|
1469
|
+
* `(serial | parallel | leaf)` shape that renders cleanly as
|
|
1470
|
+
* swim lanes.
|
|
1471
|
+
*
|
|
1472
|
+
* Two builders, one tree shape
|
|
1473
|
+
* ────────────────────────────
|
|
1474
|
+
* - `structureAsChainTree(graph, options?)` — STRUCTURAL view.
|
|
1475
|
+
* No commitLog. Decomposes the chart shape into S-P primitives.
|
|
1476
|
+
* Renders the full chart (all branches of a decider, etc.) even
|
|
1477
|
+
* before running.
|
|
1478
|
+
*
|
|
1479
|
+
* - `buildCommitChainTree(graph, commitFlow, options?)` — RUNTIME
|
|
1480
|
+
* view. Each leaf carries the commits[] that fired for that stage
|
|
1481
|
+
* in this run. Loops produce a leaf with N commits. Branches that
|
|
1482
|
+
* never executed in this run appear as empty `commits[]` leaves
|
|
1483
|
+
* (consumer can filter).
|
|
1484
|
+
*
|
|
1485
|
+
* Algorithm scope (correctness contract)
|
|
1486
|
+
* ──────────────────────────────────────
|
|
1487
|
+
* These builders are tuned for **series-parallel charts** — every
|
|
1488
|
+
* fork in footprintjs's builder has a matching merge point. For
|
|
1489
|
+
* non-S-P or malformed graphs the tree is still finite and stable
|
|
1490
|
+
* (cycle defense guarantees termination), but the shape may not
|
|
1491
|
+
* reflect a clean S-P decomposition.
|
|
1492
|
+
*
|
|
1493
|
+
* - Forks with **no common descendant** drop the outer-walk tail at
|
|
1494
|
+
* the fork node. Real footprintjs charts always re-merge.
|
|
1495
|
+
* - Edges with `data.kind === undefined` are treated as `'next'`.
|
|
1496
|
+
* Multiple linear-next edges from a single source follow only the
|
|
1497
|
+
* first (deterministic by insertion order); a dev-mode warning
|
|
1498
|
+
* fires when this is observed (`isDevMode()` from footprintjs).
|
|
1499
|
+
*
|
|
1500
|
+
* Cycle defense (L8 panel must-fix carried forward)
|
|
1501
|
+
* ─────────────────────────────────────────────────
|
|
1502
|
+
* Every recursive walk carries a `visited: Set<StageId>` to defend
|
|
1503
|
+
* against malformed graphs (manual edge injection, future builder
|
|
1504
|
+
* bugs). Loop edges (`kind === 'loop'`) are excluded at the
|
|
1505
|
+
* structure-recorder layer per invariant I1, so they never enter
|
|
1506
|
+
* prev/next — no extra filtering needed here. Convergence detection
|
|
1507
|
+
* uses BFS bounded by `graph.nodes.length`.
|
|
1508
|
+
*
|
|
1509
|
+
* @example
|
|
1510
|
+
* ```ts
|
|
1511
|
+
* const trace = createTraceStructureRecorder();
|
|
1512
|
+
* // ... build a fork chart ...
|
|
1513
|
+
*
|
|
1514
|
+
* const sChain = structureAsChainTree(trace.getGraph());
|
|
1515
|
+
* // → { kind: 'serial', items: [
|
|
1516
|
+
* // { kind: 'leaf', stageId: 'LoadOrder' },
|
|
1517
|
+
* // { kind: 'parallel', branches: [
|
|
1518
|
+
* // { kind: 'leaf', stageId: 'CheckInventory' },
|
|
1519
|
+
* // { kind: 'leaf', stageId: 'RunFraudCheck' },
|
|
1520
|
+
* // ]},
|
|
1521
|
+
* // { kind: 'leaf', stageId: 'FinalizeOrder' },
|
|
1522
|
+
* // ]}
|
|
1523
|
+
*
|
|
1524
|
+
* const commitFlow = createCommitFlowRecorder({ structure: trace });
|
|
1525
|
+
* // ... run chart ...
|
|
1526
|
+
* const cChain = buildCommitChainTree(trace.getGraph(), commitFlow.getIndex());
|
|
1527
|
+
* // → same shape, but each leaf carries `commits: CommitView[]`
|
|
1528
|
+
* ```
|
|
1529
|
+
*/
|
|
1530
|
+
|
|
1531
|
+
/** Structural chain tree leaf (no commits). */
|
|
1532
|
+
interface StructureChainLeaf {
|
|
1533
|
+
readonly kind: "leaf";
|
|
1534
|
+
readonly stageId: StageId;
|
|
1535
|
+
}
|
|
1536
|
+
/** Structural chain tree node — recursive S-P composition over stage ids. */
|
|
1537
|
+
type StructureChain = StructureChainLeaf | {
|
|
1538
|
+
readonly kind: "serial";
|
|
1539
|
+
readonly items: readonly StructureChain[];
|
|
1540
|
+
} | {
|
|
1541
|
+
readonly kind: "parallel";
|
|
1542
|
+
readonly branches: readonly StructureChain[];
|
|
1543
|
+
};
|
|
1544
|
+
/** Commit chain tree leaf — carries all commits for the stage (loop-aware). */
|
|
1545
|
+
interface CommitChainLeaf {
|
|
1546
|
+
readonly kind: "leaf";
|
|
1547
|
+
readonly stageId: StageId;
|
|
1548
|
+
/** All commits that fired for this stage in this run, in commitIdx
|
|
1549
|
+
* ascending order. Loop iteration N has commits[N]. Empty array
|
|
1550
|
+
* when the stage never executed in this run.
|
|
1551
|
+
*
|
|
1552
|
+
* **Ownership**: references are shared with
|
|
1553
|
+
* `CommitFlowIndex.commits` (no deep copy). The same shallow-copy /
|
|
1554
|
+
* nested-shared-value contract documented on `CommitView.updates`
|
|
1555
|
+
* applies. Do not mutate. */
|
|
1556
|
+
readonly commits: readonly CommitView[];
|
|
1557
|
+
}
|
|
1558
|
+
/** Commit chain tree — recursive S-P composition with commit leaves. */
|
|
1559
|
+
type CommitChain = CommitChainLeaf | {
|
|
1560
|
+
readonly kind: "serial";
|
|
1561
|
+
readonly items: readonly CommitChain[];
|
|
1562
|
+
} | {
|
|
1563
|
+
readonly kind: "parallel";
|
|
1564
|
+
readonly branches: readonly CommitChain[];
|
|
1565
|
+
};
|
|
1566
|
+
interface ChainTreeOptions {
|
|
1567
|
+
/** Stage id to start the walk from. Defaults to the chart's seed
|
|
1568
|
+
* (first node with no incoming non-loop edge). */
|
|
1569
|
+
rootStageId?: StageId;
|
|
1570
|
+
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Build a structural chain tree for a chart. Pure function over
|
|
1573
|
+
* `TraceGraph` — no runtime data required. Useful for rendering the
|
|
1574
|
+
* chart shape before any execution (static documentation, etc.).
|
|
1575
|
+
*/
|
|
1576
|
+
declare function structureAsChainTree(graph: TraceGraph, options?: ChainTreeOptions): StructureChain | null;
|
|
1577
|
+
/**
|
|
1578
|
+
* Build a commit-decorated chain tree. Same S-P shape as
|
|
1579
|
+
* `structureAsChainTree`, but each leaf carries the commits[] that
|
|
1580
|
+
* fired for that stage in this run (in commitIdx ascending order).
|
|
1581
|
+
*
|
|
1582
|
+
* Loop semantics: a stage that ran N times has a leaf with
|
|
1583
|
+
* `commits.length === N`. The consumer's renderer decides whether to
|
|
1584
|
+
* display them as a single "ran 3×" badge or unroll into N visible
|
|
1585
|
+
* boxes — both views are supported by the same data.
|
|
1586
|
+
*
|
|
1587
|
+
* Branches that never executed in this run appear with empty
|
|
1588
|
+
* `commits[]`. Consumers wanting "runtime-only" view can filter them
|
|
1589
|
+
* out.
|
|
1590
|
+
*/
|
|
1591
|
+
declare function buildCommitChainTree(graph: TraceGraph, commitFlow: CommitFlowIndex, options?: ChainTreeOptions): CommitChain | null;
|
|
1592
|
+
|
|
1593
|
+
interface CommitChainViewProps extends BaseComponentProps {
|
|
1594
|
+
/** The chain tree. Accepts BOTH `CommitChain` (from
|
|
1595
|
+
* `buildCommitChainTree`) and `StructureChain` (from
|
|
1596
|
+
* `structureAsChainTree` — pre-execution view). null → placeholder. */
|
|
1597
|
+
chain: CommitChain | StructureChain | null;
|
|
1598
|
+
/** Highlighted commit (by runtimeStageId). Only meaningful for
|
|
1599
|
+
* `CommitChain` input (StructureChain has no commits to select). */
|
|
1600
|
+
selectedRuntimeStageId?: RuntimeStageId | null;
|
|
1601
|
+
/** Fires when a commit box is clicked — pass to `<CommitInspector>`
|
|
1602
|
+
* for master/detail wiring. Not fired for "not executed" leaves. */
|
|
1603
|
+
onSelectCommit?: (runtimeStageId: RuntimeStageId) => void;
|
|
1604
|
+
/** Optional label resolver — default uses the bare stageId. Consumers
|
|
1605
|
+
* can map id → friendly name (e.g., from a NodeViewIndex). */
|
|
1606
|
+
resolveLabel?: (stageId: StageId) => string;
|
|
1607
|
+
/** Time-travel cursor: commits with `commitIdx > revealedThroughCommitIdx`
|
|
1608
|
+
* render dimmed (still visible — keeps layout stable). Pairs with
|
|
1609
|
+
* `<RunSlider>` in the ONE-CURSOR model.
|
|
1610
|
+
*
|
|
1611
|
+
* Semantics:
|
|
1612
|
+
* - `null` (default) — reveal ALL commits (no time-travel applied).
|
|
1613
|
+
* - `N >= 0` — reveal commits 0..N inclusive; dim commits with
|
|
1614
|
+
* `commitIdx > N`.
|
|
1615
|
+
* - `N < 0` (e.g., `-1`) — reveal NONE; all commits dim. Use this
|
|
1616
|
+
* sentinel for "cursor is stale / unresolved" so the chain doesn't
|
|
1617
|
+
* accidentally fall back to fully-revealed when the consumer wants
|
|
1618
|
+
* no-reveal. */
|
|
1619
|
+
revealedThroughCommitIdx?: number | null;
|
|
1620
|
+
}
|
|
1621
|
+
declare function CommitChainView({ chain, selectedRuntimeStageId, onSelectCommit, resolveLabel, revealedThroughCommitIdx, className, style, }: CommitChainViewProps): react_jsx_runtime.JSX.Element;
|
|
1622
|
+
|
|
1623
|
+
/**
|
|
1624
|
+
* Slot prop bags — slots receive PURE DATA (chain/index), NOT the
|
|
1625
|
+
* bundle. Mirrors the sibling-inspector convention so slot bodies are
|
|
1626
|
+
* usable from tests / Storybook / SSR and avoid re-subscribing to
|
|
1627
|
+
* translators. Each callback is wired by the shell to the shared
|
|
1628
|
+
* selection cursor.
|
|
1629
|
+
*/
|
|
1630
|
+
interface ChainSlotProps {
|
|
1631
|
+
/** Live chain tree, derived from the bundle. `null` before any
|
|
1632
|
+
* commits arrive. */
|
|
1633
|
+
chain: CommitChain | null;
|
|
1634
|
+
/** Currently selected commit (shared cursor). */
|
|
1635
|
+
selectedRuntimeStageId: RuntimeStageId | null;
|
|
1636
|
+
/** Wire to the shared selection cursor. */
|
|
1637
|
+
onSelectCommit: (rsid: RuntimeStageId) => void;
|
|
1638
|
+
/** Time-travel reveal cursor (derived from selectedRuntimeStageId
|
|
1639
|
+
* via ONE-CURSOR model). Commits with commitIdx > this value
|
|
1640
|
+
* render dimmed. `null` reveals all, `-1` reveals none, `N >= 0`
|
|
1641
|
+
* reveals up to commit N. **Optional in custom slots** — slot
|
|
1642
|
+
* authors that don't implement time-travel dimming can ignore it
|
|
1643
|
+
* (matches the standalone `<CommitChainView>` prop optionality). */
|
|
1644
|
+
revealedThroughCommitIdx?: number | null;
|
|
1645
|
+
}
|
|
1646
|
+
interface CommitInspectorSlotProps {
|
|
1647
|
+
/** Live commitFlow index. */
|
|
1648
|
+
index: CommitFlowIndex;
|
|
1649
|
+
/** Currently selected commit (shared cursor). */
|
|
1650
|
+
selectedRuntimeStageId: RuntimeStageId | null;
|
|
1651
|
+
/** Wire to the shared selection cursor (e.g., for clickable
|
|
1652
|
+
* data-dep / lineage breadcrumbs). */
|
|
1653
|
+
onNavigate: (rsid: RuntimeStageId) => void;
|
|
1654
|
+
}
|
|
1655
|
+
interface NodeInspectorSlotProps {
|
|
1656
|
+
/** Live nodeView index. */
|
|
1657
|
+
index: NodeViewIndex;
|
|
1658
|
+
/** StageId derived from `selectedRuntimeStageId`. */
|
|
1659
|
+
selectedStageId: StageId | null;
|
|
1660
|
+
/** Wire to the shared selection cursor (e.g., for prev/next chain
|
|
1661
|
+
* breadcrumbs). Resolves the stage to its first commit. */
|
|
1662
|
+
onNavigate: (stageId: StageId) => void;
|
|
1663
|
+
}
|
|
1664
|
+
interface SliderSlotProps {
|
|
1665
|
+
/** Live commitFlow index. */
|
|
1666
|
+
index: CommitFlowIndex;
|
|
1667
|
+
/** Currently selected commit — same cursor as the chain selection
|
|
1668
|
+
* per the ONE-CURSOR model. */
|
|
1669
|
+
cursorRuntimeStageId: RuntimeStageId | null;
|
|
1670
|
+
/** Wire to the shared selection cursor. */
|
|
1671
|
+
onCursorChange: (rsid: RuntimeStageId | null) => void;
|
|
1672
|
+
}
|
|
1673
|
+
interface TraceExplorerSlots {
|
|
1674
|
+
/** Override the chain (master) pane. Use to swap the swim-lane
|
|
1675
|
+
* renderer for a timeline/Gantt/custom layout while keeping the
|
|
1676
|
+
* same selection contract. */
|
|
1677
|
+
chain?: React.ComponentType<ChainSlotProps>;
|
|
1678
|
+
/** Override the commit (detail) pane. Use to add custom commit-
|
|
1679
|
+
* detail formatting (e.g., domain-specific updates rendering). */
|
|
1680
|
+
commitInspector?: React.ComponentType<CommitInspectorSlotProps>;
|
|
1681
|
+
/** Override the node (stage-summary) pane. Use to surface per-stage
|
|
1682
|
+
* custom KPIs / annotations alongside the default execution data. */
|
|
1683
|
+
nodeInspector?: React.ComponentType<NodeInspectorSlotProps>;
|
|
1684
|
+
/** Override the slider (top time-travel bar). Pass `null` to HIDE
|
|
1685
|
+
* the slider entirely (e.g., for static post-run views with no
|
|
1686
|
+
* scrub affordance). */
|
|
1687
|
+
slider?: React.ComponentType<SliderSlotProps> | null;
|
|
1688
|
+
}
|
|
1689
|
+
interface TraceExplorerShellProps extends BaseComponentProps {
|
|
1690
|
+
/** The translator bundle from `createTraceBundle()`. MUST have had
|
|
1691
|
+
* `bundle.attachTo(executor)` called before any commits arrive,
|
|
1692
|
+
* otherwise every pane shows its placeholder. */
|
|
1693
|
+
bundle: TraceBundle;
|
|
1694
|
+
/** Controlled selection (omit for uncontrolled). `null` clears. */
|
|
1695
|
+
selectedRuntimeStageId?: RuntimeStageId | null;
|
|
1696
|
+
/** Fires on selection changes. In controlled mode, required for
|
|
1697
|
+
* selection to take effect. In uncontrolled mode, fires as an
|
|
1698
|
+
* observation hook (logging/analytics) — the shell still owns the
|
|
1699
|
+
* state.
|
|
1700
|
+
*
|
|
1701
|
+
* Memoize via `useCallback` to avoid downstream re-renders. */
|
|
1702
|
+
onSelectionChange?: (rsid: RuntimeStageId | null) => void;
|
|
1703
|
+
/** Optional slot overrides. Should be stable across renders
|
|
1704
|
+
* (define at module scope or `useMemo`). */
|
|
1705
|
+
slots?: TraceExplorerSlots;
|
|
1706
|
+
}
|
|
1707
|
+
declare function TraceExplorerShell({ bundle, selectedRuntimeStageId: controlledSel, onSelectionChange, slots, className, style, }: TraceExplorerShellProps): react_jsx_runtime.JSX.Element;
|
|
1708
|
+
|
|
1709
|
+
interface RunSliderProps extends BaseComponentProps {
|
|
1710
|
+
/** Live commit flow index (pure data prop — pass via
|
|
1711
|
+
* `useTranslator(commitFlow, () => commitFlow.getIndex())`).
|
|
1712
|
+
* Source: `createCommitFlowRecorder` — see `<CommitInspector>` for
|
|
1713
|
+
* the canonical consumer pattern. */
|
|
1714
|
+
index: CommitFlowIndex;
|
|
1715
|
+
/** The shared time cursor. `null` = no commit yet selected (slider
|
|
1716
|
+
* sits at value 0). */
|
|
1717
|
+
cursorRuntimeStageId: RuntimeStageId | null;
|
|
1718
|
+
/** Fires when the slider moves. */
|
|
1719
|
+
onCursorChange: (rsid: RuntimeStageId | null) => void;
|
|
1720
|
+
/** Optional label callback. Default renders `"#commitIdx · stageId"`.
|
|
1721
|
+
* Receives the full resolved `commit` + the `index` for richer
|
|
1722
|
+
* labels (step name, duration from a sibling translator, etc.). */
|
|
1723
|
+
renderLabel?: (info: {
|
|
1724
|
+
commitIdx: number;
|
|
1725
|
+
total: number;
|
|
1726
|
+
runtimeStageId: RuntimeStageId | null;
|
|
1727
|
+
/** Resolved commit at the cursor, or `null` when index is empty. */
|
|
1728
|
+
commit: CommitView | null;
|
|
1729
|
+
/** Full index — for cross-lookups (e.g., finding the previous
|
|
1730
|
+
* commit's stage label). */
|
|
1731
|
+
index: CommitFlowIndex;
|
|
1732
|
+
}) => React.ReactNode;
|
|
1733
|
+
}
|
|
1734
|
+
declare function RunSlider({ index, cursorRuntimeStageId, onCursorChange, renderLabel, className, style, }: RunSliderProps): react_jsx_runtime.JSX.Element;
|
|
296
1735
|
|
|
297
|
-
export { type BreadcrumbEntry, type
|
|
1736
|
+
export { type BreadcrumbEntry, type ChainSlotProps, type ChainTreeOptions, type CommitChain, type CommitChainLeaf, CommitChainView, type CommitChainViewProps, type CommitFlowIndex, type CommitFlowRecorderHandle, CommitInspector, type CommitInspectorProps, type CommitInspectorSlotProps, type CommitView, type CreateCommitFlowRecorderOptions, type CreateNodeViewRecorderOptions, type CreateTraceBundleOptions, type CreateTraceRuntimeOverlayOptions, type CreateTraceStructureRecorderOptions, type DataDependency, type ExecutionRecord, type MinimalCommitFlowRecorder, type MinimalFlowRecorder, type MinimalNodeViewRecorder, type MinimalStructureRecorder, NodeInspector, type NodeInspectorProps, type NodeInspectorSlotProps, type NodeView, type NodeViewIndex, type NodeViewRecorderHandle, RunSlider, type RunSliderProps, type RuntimeExecutionStep, type RuntimeOverlay, type RuntimeOverlaySlice, type RuntimeStageId, type SliderSlotProps, type StageId, StageNode, type StageNodeData, type StructureChain, type StructureChainLeaf, SubflowBreadcrumb, type SubflowBreadcrumbProps, type SubflowNavigation, SubflowTree, type SubflowTreeEntry, type SubflowTreeProps, TimeTravelDebugger, type TimeTravelDebuggerProps, type TraceBundle, type TraceEdge, type TraceEdgeData, TraceExplorerShell, type TraceExplorerShellProps, type TraceExplorerSlots, TraceFlow, type TraceFlowEdgeColors, type TraceFlowLayout, type TraceFlowProps, type TraceGraph, type TraceNode, type TraceNodeData, type TraceRuntimeOverlayHandle, type TraceStructureRecorderHandle, TracedFlow, type TracedFlowColors, type TracedFlowProps, type TranslatorHandleLike, type WalkOptions, asRuntimeStageId, asStageId, backtraceDataFlow, backtraceStructural, buildCommitChainTree, createCommitFlowRecorder, createNodeViewRecorder, createTraceBundle, createTraceRuntimeOverlay, createTraceStructureRecorder, defaultTraceFlowLayout, forwardtraceStructural, sliceOverlay, structureAsChainTree, useSubflowNavigation, useTranslator, walkBackward, walkForward };
|