causal-inspector 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CausalInspector.css +124 -0
- package/dist/CausalInspector.d.ts +13 -0
- package/dist/CausalInspector.js +257 -0
- package/dist/components/CopyablePayload.d.ts +4 -0
- package/dist/components/CopyablePayload.js +44 -0
- package/dist/components/FilterBar.d.ts +1 -0
- package/dist/components/FilterBar.js +23 -0
- package/dist/components/GlobalScrubber.d.ts +1 -0
- package/dist/components/GlobalScrubber.js +148 -0
- package/dist/components/JsonSyntax.d.ts +3 -0
- package/dist/components/JsonSyntax.js +40 -0
- package/dist/context.d.ts +26 -0
- package/dist/context.js +28 -0
- package/dist/engines/index.d.ts +22 -0
- package/dist/engines/index.js +29 -0
- package/dist/engines/query.d.ts +14 -0
- package/dist/engines/query.js +241 -0
- package/dist/engines/scrubber.d.ts +12 -0
- package/dist/engines/scrubber.js +69 -0
- package/dist/engines/storage.d.ts +15 -0
- package/dist/engines/storage.js +16 -0
- package/dist/engines/subscription.d.ts +17 -0
- package/dist/engines/subscription.js +44 -0
- package/dist/engines/url.d.ts +13 -0
- package/dist/engines/url.js +64 -0
- package/dist/events.d.ts +77 -0
- package/dist/events.js +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +29 -0
- package/dist/machine/core.d.ts +22 -0
- package/dist/machine/core.js +34 -0
- package/dist/machine/engine.d.ts +17 -0
- package/dist/machine/engine.js +21 -0
- package/dist/machine/events.d.ts +18 -0
- package/dist/machine/events.js +1 -0
- package/dist/machine/hooks.d.ts +23 -0
- package/dist/machine/hooks.js +52 -0
- package/dist/machine/index.d.ts +5 -0
- package/dist/machine/index.js +4 -0
- package/dist/machine/store.d.ts +27 -0
- package/dist/machine/store.js +42 -0
- package/dist/panes/AggregateTimelinePane.d.ts +2 -0
- package/dist/panes/AggregateTimelinePane.js +224 -0
- package/dist/panes/CausalFlowPane.d.ts +7 -0
- package/dist/panes/CausalFlowPane.js +596 -0
- package/dist/panes/CausalTreePane.d.ts +5 -0
- package/dist/panes/CausalTreePane.js +158 -0
- package/dist/panes/CorrelationExplorerPane.d.ts +2 -0
- package/dist/panes/CorrelationExplorerPane.js +46 -0
- package/dist/panes/LogsPane.d.ts +6 -0
- package/dist/panes/LogsPane.js +65 -0
- package/dist/panes/TimelinePane.d.ts +6 -0
- package/dist/panes/TimelinePane.js +121 -0
- package/dist/panes/WaterfallPane.d.ts +2 -0
- package/dist/panes/WaterfallPane.js +202 -0
- package/dist/queries.d.ts +15 -0
- package/dist/queries.js +175 -0
- package/dist/reducer.d.ts +4 -0
- package/dist/reducer.js +177 -0
- package/dist/state.d.ts +34 -0
- package/dist/state.js +39 -0
- package/dist/theme.d.ts +7 -0
- package/dist/theme.js +34 -0
- package/dist/types.d.ts +140 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.js +91 -0
- package/package.json +43 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useSelector, useDispatch } from "../machine";
|
|
4
|
+
import { eventBg, eventTextColor } from "../theme";
|
|
5
|
+
import { inScrubberRange } from "../utils";
|
|
6
|
+
function tokenizeJson(json) {
|
|
7
|
+
const tokens = [];
|
|
8
|
+
const re = /("(?:[^"\\]|\\.)*")\s*:|("(?:[^"\\]|\\.)*")|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b)|(\bnull\b)|([{}[\]:,])/g;
|
|
9
|
+
let lastIndex = 0;
|
|
10
|
+
let match;
|
|
11
|
+
while ((match = re.exec(json)) !== null) {
|
|
12
|
+
if (match.index > lastIndex) {
|
|
13
|
+
tokens.push({ text: json.slice(lastIndex, match.index), color: "" });
|
|
14
|
+
}
|
|
15
|
+
if (match[1] !== undefined) {
|
|
16
|
+
tokens.push({ text: match[1], color: "#60a5fa" }); // key — blue
|
|
17
|
+
tokens.push({ text: ":", color: "#71717a" });
|
|
18
|
+
}
|
|
19
|
+
else if (match[2] !== undefined) {
|
|
20
|
+
tokens.push({ text: match[2], color: "#4ade80" }); // string — green
|
|
21
|
+
}
|
|
22
|
+
else if (match[3] !== undefined) {
|
|
23
|
+
tokens.push({ text: match[3], color: "#fbbf24" }); // number — amber
|
|
24
|
+
}
|
|
25
|
+
else if (match[4] !== undefined) {
|
|
26
|
+
tokens.push({ text: match[4], color: "#c084fc" }); // bool — purple
|
|
27
|
+
}
|
|
28
|
+
else if (match[5] !== undefined) {
|
|
29
|
+
tokens.push({ text: match[5], color: "#71717a" }); // null — gray
|
|
30
|
+
}
|
|
31
|
+
else if (match[6] !== undefined) {
|
|
32
|
+
tokens.push({ text: match[6], color: "#71717a" }); // punctuation
|
|
33
|
+
}
|
|
34
|
+
lastIndex = re.lastIndex;
|
|
35
|
+
}
|
|
36
|
+
if (lastIndex < json.length) {
|
|
37
|
+
tokens.push({ text: json.slice(lastIndex), color: "" });
|
|
38
|
+
}
|
|
39
|
+
return tokens;
|
|
40
|
+
}
|
|
41
|
+
function InlineJson({ value }) {
|
|
42
|
+
const json = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
43
|
+
const tokens = useMemo(() => tokenizeJson(json), [json]);
|
|
44
|
+
return (_jsx("pre", { style: {
|
|
45
|
+
margin: 0,
|
|
46
|
+
fontSize: 11,
|
|
47
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
48
|
+
whiteSpace: "pre-wrap",
|
|
49
|
+
wordBreak: "break-word",
|
|
50
|
+
lineHeight: 1.5,
|
|
51
|
+
}, children: tokens.map((t, i) => (_jsx("span", { style: { color: t.color || undefined }, children: t.text }, i))) }));
|
|
52
|
+
}
|
|
53
|
+
function jsonDiff(prev, curr) {
|
|
54
|
+
const diffs = [];
|
|
55
|
+
if (prev == null && curr == null)
|
|
56
|
+
return diffs;
|
|
57
|
+
if (prev == null) {
|
|
58
|
+
// Everything is added
|
|
59
|
+
if (typeof curr === "object" && curr !== null && !Array.isArray(curr)) {
|
|
60
|
+
for (const key of Object.keys(curr)) {
|
|
61
|
+
diffs.push({ kind: "added", key, value: curr[key] });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return diffs;
|
|
65
|
+
}
|
|
66
|
+
if (curr == null) {
|
|
67
|
+
if (typeof prev === "object" && prev !== null && !Array.isArray(prev)) {
|
|
68
|
+
for (const key of Object.keys(prev)) {
|
|
69
|
+
diffs.push({ kind: "removed", key, value: prev[key] });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return diffs;
|
|
73
|
+
}
|
|
74
|
+
const prevObj = (typeof prev === "object" && !Array.isArray(prev) ? prev : {});
|
|
75
|
+
const currObj = (typeof curr === "object" && !Array.isArray(curr) ? curr : {});
|
|
76
|
+
const allKeys = new Set([...Object.keys(prevObj), ...Object.keys(currObj)]);
|
|
77
|
+
for (const key of allKeys) {
|
|
78
|
+
const inPrev = key in prevObj;
|
|
79
|
+
const inCurr = key in currObj;
|
|
80
|
+
if (inCurr && !inPrev) {
|
|
81
|
+
diffs.push({ kind: "added", key, value: currObj[key] });
|
|
82
|
+
}
|
|
83
|
+
else if (inPrev && !inCurr) {
|
|
84
|
+
diffs.push({ kind: "removed", key, value: prevObj[key] });
|
|
85
|
+
}
|
|
86
|
+
else if (JSON.stringify(prevObj[key]) !== JSON.stringify(currObj[key])) {
|
|
87
|
+
diffs.push({ kind: "changed", key, oldValue: prevObj[key], newValue: currObj[key] });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return diffs;
|
|
91
|
+
}
|
|
92
|
+
function DiffView({ diffs }) {
|
|
93
|
+
if (diffs.length === 0) {
|
|
94
|
+
return (_jsx("div", { style: { fontSize: 11, color: "#52525b", fontStyle: "italic", padding: "2px 0" }, children: "No changes" }));
|
|
95
|
+
}
|
|
96
|
+
const mono = {
|
|
97
|
+
fontSize: 11,
|
|
98
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
99
|
+
lineHeight: 1.5,
|
|
100
|
+
wordBreak: "break-word",
|
|
101
|
+
};
|
|
102
|
+
return (_jsx("div", { style: { display: "flex", flexDirection: "column", gap: 2 }, children: diffs.map((d) => {
|
|
103
|
+
if (d.kind === "added") {
|
|
104
|
+
return (_jsxs("div", { style: { ...mono, color: "#4ade80", background: "rgba(74,222,128,0.08)", borderRadius: 3, padding: "1px 4px" }, children: [_jsx("span", { style: { color: "#22c55e", fontWeight: 600 }, children: "+ " }), _jsx("span", { style: { color: "#60a5fa" }, children: d.key }), _jsx("span", { style: { color: "#71717a" }, children: ": " }), _jsx("span", { children: JSON.stringify(d.value) })] }, d.key));
|
|
105
|
+
}
|
|
106
|
+
if (d.kind === "removed") {
|
|
107
|
+
return (_jsxs("div", { style: { ...mono, color: "#f87171", background: "rgba(248,113,113,0.08)", borderRadius: 3, padding: "1px 4px" }, children: [_jsx("span", { style: { color: "#ef4444", fontWeight: 600 }, children: "- " }), _jsx("span", { style: { color: "#60a5fa" }, children: d.key }), _jsx("span", { style: { color: "#71717a" }, children: ": " }), _jsx("span", { children: JSON.stringify(d.value) })] }, d.key));
|
|
108
|
+
}
|
|
109
|
+
// changed
|
|
110
|
+
return (_jsxs("div", { style: { ...mono, background: "rgba(251,191,36,0.08)", borderRadius: 3, padding: "1px 4px" }, children: [_jsx("span", { style: { color: "#fbbf24", fontWeight: 600 }, children: "~ " }), _jsx("span", { style: { color: "#60a5fa" }, children: d.key }), _jsx("span", { style: { color: "#71717a" }, children: ": " }), _jsx("span", { style: { color: "#f87171", textDecoration: "line-through" }, children: JSON.stringify(d.oldValue) }), _jsx("span", { style: { color: "#71717a" }, children: " \u2192 " }), _jsx("span", { style: { color: "#4ade80" }, children: JSON.stringify(d.newValue) })] }, d.key));
|
|
111
|
+
}) }));
|
|
112
|
+
}
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Collapsible aggregate card
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
function AggregateCard({ aggregateKey, state, prevState, diffMode, }) {
|
|
117
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
118
|
+
// Parse "AggType:id" into type + id for display
|
|
119
|
+
const colonIdx = aggregateKey.indexOf(":");
|
|
120
|
+
const aggType = colonIdx > 0 ? aggregateKey.slice(0, colonIdx) : aggregateKey;
|
|
121
|
+
const aggId = colonIdx > 0 ? aggregateKey.slice(colonIdx + 1) : null;
|
|
122
|
+
const diffs = useMemo(() => {
|
|
123
|
+
if (!diffMode || prevState === undefined)
|
|
124
|
+
return [];
|
|
125
|
+
return jsonDiff(prevState, state);
|
|
126
|
+
}, [diffMode, prevState, state]);
|
|
127
|
+
const showDiff = diffMode && prevState !== undefined;
|
|
128
|
+
return (_jsxs("div", { style: {
|
|
129
|
+
background: "rgba(255, 255, 255, 0.02)",
|
|
130
|
+
border: "1px solid rgba(255, 255, 255, 0.06)",
|
|
131
|
+
borderRadius: 8,
|
|
132
|
+
overflow: "hidden",
|
|
133
|
+
flex: "1 1 280px",
|
|
134
|
+
maxWidth: 500,
|
|
135
|
+
}, children: [_jsxs("div", { onClick: () => setCollapsed(!collapsed), style: {
|
|
136
|
+
display: "flex",
|
|
137
|
+
alignItems: "center",
|
|
138
|
+
gap: 6,
|
|
139
|
+
padding: "4px 8px",
|
|
140
|
+
cursor: "pointer",
|
|
141
|
+
userSelect: "none",
|
|
142
|
+
borderBottom: collapsed ? "none" : "1px solid rgba(255,255,255,0.06)",
|
|
143
|
+
}, children: [_jsx("span", { style: { fontSize: 9, color: "#52525b", transform: collapsed ? "rotate(-90deg)" : "rotate(0deg)", transition: "transform 100ms" }, children: "\u25BC" }), _jsx("span", { style: { fontSize: 11, fontWeight: 500, color: "#e4e4e7" }, children: aggType }), aggId && (_jsx("span", { style: { fontSize: 10, color: "#71717a", fontFamily: "monospace" }, children: aggId })), showDiff && diffs.length > 0 && (_jsxs("span", { style: { fontSize: 9, color: "#fbbf24", marginLeft: "auto" }, children: [diffs.length, " change", diffs.length !== 1 ? "s" : ""] })), showDiff && diffs.length === 0 && (_jsx("span", { style: { fontSize: 9, color: "#52525b", marginLeft: "auto" }, children: "unchanged" }))] }), !collapsed && (_jsx("div", { style: { padding: "4px 8px 6px" }, children: showDiff ? _jsx(DiffView, { diffs: diffs }) : _jsx(InlineJson, { value: state }) }))] }));
|
|
144
|
+
}
|
|
145
|
+
export function AggregateTimelinePane() {
|
|
146
|
+
const correlationId = useSelector((s) => s.flowCorrelationId);
|
|
147
|
+
const entries = useSelector((s) => correlationId ? s.aggregateTimeline[correlationId] ?? [] : []);
|
|
148
|
+
const scrubberStart = useSelector((s) => s.scrubberStart);
|
|
149
|
+
const scrubberEnd = useSelector((s) => s.scrubberEnd);
|
|
150
|
+
const dispatch = useDispatch();
|
|
151
|
+
const [diffMode, setDiffMode] = useState(false);
|
|
152
|
+
// Build a lookup: seq → { aggKey → state } for computing diffs
|
|
153
|
+
const prevStateMap = useMemo(() => {
|
|
154
|
+
const map = new Map();
|
|
155
|
+
for (let i = 1; i < entries.length; i++) {
|
|
156
|
+
const prev = entries[i - 1];
|
|
157
|
+
const prevAggs = new Map();
|
|
158
|
+
for (const agg of prev.aggregates) {
|
|
159
|
+
prevAggs.set(agg.key, agg.state);
|
|
160
|
+
}
|
|
161
|
+
map.set(entries[i].seq, prevAggs);
|
|
162
|
+
}
|
|
163
|
+
return map;
|
|
164
|
+
}, [entries]);
|
|
165
|
+
// Auto-scroll to current row when scrubber moves
|
|
166
|
+
const currentRowRef = useRef(null);
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (scrubberEnd != null && currentRowRef.current) {
|
|
169
|
+
currentRowRef.current.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
170
|
+
}
|
|
171
|
+
}, [scrubberEnd]);
|
|
172
|
+
const handleRowClick = useCallback((seq) => {
|
|
173
|
+
dispatch({ type: "ui/scrubber_end_changed", payload: { end: seq } });
|
|
174
|
+
}, [dispatch]);
|
|
175
|
+
if (!correlationId) {
|
|
176
|
+
return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "Open a flow to see the aggregate state timeline" }));
|
|
177
|
+
}
|
|
178
|
+
if (entries.length === 0) {
|
|
179
|
+
return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "No aggregate state snapshots for this correlation" }));
|
|
180
|
+
}
|
|
181
|
+
return (_jsxs("div", { style: { height: "100%", display: "flex", flexDirection: "column" }, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 4, padding: "6px 12px", borderBottom: "1px solid rgba(255,255,255,0.06)", flexShrink: 0, background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: [_jsx("button", { onClick: () => setDiffMode(false), style: {
|
|
182
|
+
fontSize: 10,
|
|
183
|
+
padding: "3px 10px",
|
|
184
|
+
borderRadius: 6,
|
|
185
|
+
border: "1px solid",
|
|
186
|
+
borderColor: !diffMode ? "rgba(99,102,241,0.3)" : "rgba(255,255,255,0.06)",
|
|
187
|
+
background: !diffMode ? "rgba(99,102,241,0.12)" : "transparent",
|
|
188
|
+
color: !diffMode ? "#818cf8" : "#70708a",
|
|
189
|
+
cursor: "pointer",
|
|
190
|
+
transition: "all 150ms",
|
|
191
|
+
letterSpacing: "0.02em",
|
|
192
|
+
}, children: "Full State" }), _jsx("button", { onClick: () => setDiffMode(true), style: {
|
|
193
|
+
fontSize: 10,
|
|
194
|
+
padding: "3px 10px",
|
|
195
|
+
borderRadius: 6,
|
|
196
|
+
border: "1px solid",
|
|
197
|
+
borderColor: diffMode ? "rgba(99,102,241,0.3)" : "rgba(255,255,255,0.06)",
|
|
198
|
+
background: diffMode ? "rgba(99,102,241,0.12)" : "transparent",
|
|
199
|
+
color: diffMode ? "#818cf8" : "#70708a",
|
|
200
|
+
cursor: "pointer",
|
|
201
|
+
transition: "all 150ms",
|
|
202
|
+
letterSpacing: "0.02em",
|
|
203
|
+
}, children: "Diff" })] }), _jsx("div", { style: { flex: 1, overflow: "auto", padding: "8px 12px" }, children: entries.map((entry, entryIdx) => {
|
|
204
|
+
const isOutside = (scrubberStart != null || scrubberEnd != null) && !inScrubberRange(entry.seq, scrubberStart, scrubberEnd);
|
|
205
|
+
const isCurrent = scrubberEnd === entry.seq;
|
|
206
|
+
const prevAggs = prevStateMap.get(entry.seq);
|
|
207
|
+
return (_jsxs("div", { ref: isCurrent ? currentRowRef : undefined, onClick: () => handleRowClick(entry.seq), style: {
|
|
208
|
+
opacity: isOutside ? 0.3 : 1,
|
|
209
|
+
padding: "6px 8px",
|
|
210
|
+
marginBottom: 4,
|
|
211
|
+
borderRadius: 6,
|
|
212
|
+
background: isCurrent ? "rgba(99,102,241,0.15)" : "transparent",
|
|
213
|
+
cursor: "pointer",
|
|
214
|
+
transition: "opacity 150ms, background 150ms",
|
|
215
|
+
}, children: [_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }, children: [_jsxs("div", { style: { fontSize: 10, fontWeight: 600, color: "#a1a1aa", minWidth: 40 }, children: ["#", entry.seq] }), _jsx("div", { style: {
|
|
216
|
+
fontSize: 11,
|
|
217
|
+
fontWeight: 500,
|
|
218
|
+
color: eventTextColor(entry.eventType),
|
|
219
|
+
background: eventBg(entry.eventType),
|
|
220
|
+
borderRadius: 4,
|
|
221
|
+
padding: "1px 6px",
|
|
222
|
+
}, children: entry.eventType }), diffMode && entryIdx === 0 && (_jsx("span", { style: { fontSize: 9, color: "#52525b", fontStyle: "italic" }, children: "initial state" }))] }), _jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginLeft: 46 }, children: entry.aggregates.map((agg) => (_jsx(AggregateCard, { aggregateKey: agg.key, state: agg.state, prevState: prevAggs?.get(agg.key), diffMode: diffMode && entryIdx > 0 }, agg.key))) })] }, entry.seq));
|
|
223
|
+
}) })] }));
|
|
224
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CausalFlowPaneProps = {
|
|
2
|
+
/** Optional set of reactor IDs to hide by default. */
|
|
3
|
+
defaultHiddenReactors?: Set<string>;
|
|
4
|
+
/** Optional extra header content (e.g., domain-specific run stats). */
|
|
5
|
+
headerExtra?: React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare function CausalFlowPane({ defaultHiddenReactors, headerExtra }?: CausalFlowPaneProps): import("react/jsx-runtime").JSX.Element;
|