causal-inspector 0.1.6 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +71 -0
  2. package/dist/CausalInspector.css +20 -447
  3. package/dist/CausalInspector.d.ts +8 -1
  4. package/dist/CausalInspector.js +32 -9
  5. package/dist/causal-inspector.css +2899 -0
  6. package/dist/components/CopyablePayload.js +8 -8
  7. package/dist/components/EffectList.d.ts +4 -0
  8. package/dist/components/EffectList.js +15 -0
  9. package/dist/components/FilterBar.js +7 -10
  10. package/dist/components/GlobalScrubber.js +6 -6
  11. package/dist/components/JsonSyntax.js +8 -8
  12. package/dist/engines/query.js +131 -52
  13. package/dist/engines/scrubber.js +1 -1
  14. package/dist/engines/url.d.ts +5 -2
  15. package/dist/engines/url.js +50 -22
  16. package/dist/events.d.ts +39 -13
  17. package/dist/index.d.ts +5 -3
  18. package/dist/index.js +4 -2
  19. package/dist/panes/AggregateTimelinePane.js +4 -4
  20. package/dist/panes/CausalFlowPane.js +39 -27
  21. package/dist/panes/CausalTreePane.js +43 -34
  22. package/dist/panes/LogsPane.js +9 -17
  23. package/dist/panes/SubjectChainPane.d.ts +1 -0
  24. package/dist/panes/SubjectChainPane.js +50 -0
  25. package/dist/panes/TimelinePane.js +33 -19
  26. package/dist/panes/WaterfallPane.js +5 -5
  27. package/dist/panes/WorkflowExplorerPane.d.ts +2 -0
  28. package/dist/panes/WorkflowExplorerPane.js +49 -0
  29. package/dist/queries.d.ts +16 -12
  30. package/dist/queries.js +103 -27
  31. package/dist/reducer.js +134 -38
  32. package/dist/state.d.ts +18 -5
  33. package/dist/state.js +17 -8
  34. package/dist/theme.js +4 -4
  35. package/dist/types.d.ts +52 -12
  36. package/dist/utils.js +1 -1
  37. package/package.json +18 -3
  38. package/dist/panes/CorrelationExplorerPane.d.ts +0 -2
  39. package/dist/panes/CorrelationExplorerPane.js +0 -51
@@ -9,8 +9,9 @@ import { Search, ChevronRight, ChevronDown } from "lucide-react";
9
9
  // ---------------------------------------------------------------------------
10
10
  function LogRow({ log, showReactor }) {
11
11
  const [expanded, setExpanded] = useState(false);
12
- const levelColor = LOG_LEVEL_COLORS[log.level] ?? "ci-log-debug";
13
- return (_jsxs("div", { style: { padding: "6px 8px", borderRadius: "var(--ci-radius-md)", transition: "background 100ms" }, className: "ci-list-row", children: [_jsxs("div", { className: "ci-row", style: { gap: 8, minWidth: 0 }, children: [_jsx("span", { className: levelColor, style: { padding: "2px 6px", borderRadius: "var(--ci-radius-sm)", fontSize: 9, fontWeight: 600, textTransform: "uppercase", flexShrink: 0 }, children: log.level }), _jsx("span", { className: "ci-tabular", style: { fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.5, flexShrink: 0 }, children: formatTs(log.loggedAt) }), showReactor && (_jsx("span", { className: "ci-mono", style: { fontSize: 10, color: "var(--ci-text-dimmer)", flexShrink: 0 }, children: log.reactorId })), _jsx("span", { className: "ci-truncate", style: { fontSize: 11, color: "var(--ci-text)", opacity: 0.8 }, children: log.message }), log.data != null && (_jsx("button", { onClick: () => setExpanded((v) => !v), className: "ci-btn", style: { marginLeft: "auto", flexShrink: 0, fontSize: 10 }, children: expanded ? _jsx(ChevronDown, { size: 10 }) : _jsx(ChevronRight, { size: 10 }) }))] }), expanded && log.data != null && (_jsx("pre", { className: "ci-mono", style: { marginTop: 6, marginLeft: 16, fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.7, background: "rgba(255, 255, 255, 0.02)", borderRadius: "var(--ci-radius-md)", padding: 10, maxHeight: 128, overflow: "auto", whiteSpace: "pre-wrap", border: "1px solid var(--ci-border)" }, children: typeof log.data === "string" ? log.data : JSON.stringify(log.data, null, 2) }))] }));
12
+ const levelColor = LOG_LEVEL_COLORS[log.level] ?? "bg-zinc-600/20 text-zinc-400";
13
+ const hasPayload = log.data != null;
14
+ return (_jsxs("div", { onClick: hasPayload ? () => setExpanded((v) => !v) : undefined, className: `px-2 py-1.5 hover:bg-white/[0.02] rounded-md transition-colors duration-100 ${hasPayload ? "cursor-pointer" : ""}`, children: [_jsxs("div", { className: "flex items-start gap-2 min-w-0", children: [_jsx("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-semibold uppercase shrink-0 ${levelColor}`, children: log.level }), _jsx("span", { className: "text-[10px] text-muted-foreground/50 shrink-0 tabular-nums leading-[1.4]", children: formatTs(log.loggedAt) }), showReactor && (_jsx("span", { className: "text-[10px] font-mono text-muted-foreground/40 shrink-0 leading-[1.4]", children: log.reactorId })), _jsx("span", { className: `text-[11px] text-foreground/80 leading-[1.4] ${expanded ? "break-words whitespace-pre-wrap" : "truncate"}`, children: log.message }), hasPayload && (_jsx("span", { className: "ml-auto shrink-0 text-muted-foreground/50", children: expanded ? _jsx(ChevronDown, { size: 10 }) : _jsx(ChevronRight, { size: 10 }) }))] }), expanded && log.data != null && (_jsx("pre", { className: "mt-1.5 ml-4 text-[10px] font-mono text-muted-foreground/70 bg-white/[0.02] rounded-md p-2.5 max-h-32 overflow-auto whitespace-pre-wrap border border-border", children: typeof log.data === "string" ? log.data : JSON.stringify(log.data, null, 2) }))] }));
14
15
  }
15
16
  export function LogsPane({ onInvestigate } = {}) {
16
17
  const logs = useSelector((s) => s.logs);
@@ -20,8 +21,8 @@ export function LogsPane({ onInvestigate } = {}) {
20
21
  const scrubberEnd = useSelector((s) => s.scrubberEnd);
21
22
  const [levelFilter, setLevelFilter] = useState(new Set(["debug", "info", "warn", "error"]));
22
23
  const [searchText, setSearchText] = useState("");
23
- const isCorrelationScope = logsFilter.scope === "correlation" && logsFilter.correlationId != null;
24
- const hasFilter = logsFilter.reactorId != null || isCorrelationScope;
24
+ const isWorkflowScope = logsFilter.scope === "workflow" && logsFilter.workflowId != null;
25
+ const hasFilter = logsFilter.reactorId != null || isWorkflowScope;
25
26
  // Set of event IDs visible within scrubber range
26
27
  const visibleEventIds = useMemo(() => {
27
28
  if (scrubberStart == null && scrubberEnd == null)
@@ -47,7 +48,7 @@ export function LogsPane({ onInvestigate } = {}) {
47
48
  return filtered;
48
49
  }, [logs, logsFilter, levelFilter, searchText, visibleEventIds]);
49
50
  if (!hasFilter) {
50
- return (_jsx("div", { className: "ci-empty", children: "Click a reactor node in the causal tree to view logs" }));
51
+ return (_jsx("div", { className: "flex items-center justify-center h-full text-xs text-muted-foreground/50 tracking-wide", children: "Click a reactor node in the causal tree to view logs" }));
51
52
  }
52
53
  const toggleLevel = (level) => {
53
54
  setLevelFilter((prev) => {
@@ -59,16 +60,7 @@ export function LogsPane({ onInvestigate } = {}) {
59
60
  return next;
60
61
  });
61
62
  };
62
- return (_jsxs("div", { className: "ci-col", children: [_jsxs("div", { className: "ci-row ci-surface", style: { padding: "8px 12px", borderBottom: "1px solid var(--ci-border)", gap: 12, flexWrap: "wrap" }, children: [_jsx("div", { className: "ci-row", style: { gap: 4, fontSize: 10 }, children: ["debug", "info", "warn", "error"].map((level) => (_jsx("button", { onClick: () => toggleLevel(level), className: levelFilter.has(level) ? LOG_LEVEL_COLORS[level] : "", style: {
63
- padding: "2px 8px",
64
- borderRadius: "var(--ci-radius-md)",
65
- textTransform: "uppercase",
66
- fontWeight: 600,
67
- border: "none",
68
- cursor: "pointer",
69
- background: levelFilter.has(level) ? undefined : "transparent",
70
- color: levelFilter.has(level) ? undefined : "var(--ci-text-dimmer)",
71
- textDecoration: levelFilter.has(level) ? undefined : "line-through",
72
- transition: "all 150ms",
73
- }, children: level }, level))) }), _jsxs("div", { className: "ci-row", style: { position: "relative", marginLeft: "auto" }, children: [_jsx(Search, { size: 10, style: { position: "absolute", left: 10, color: "var(--ci-text-dimmer)", pointerEvents: "none" } }), _jsx("input", { type: "text", placeholder: "Search logs...", value: searchText, onChange: (e) => setSearchText(e.target.value), className: "ci-input", style: { paddingLeft: 28, paddingRight: 8, fontSize: 11, paddingTop: 4, paddingBottom: 4, width: 176 } })] }), onInvestigate && (_jsx("button", { onClick: () => onInvestigate(logsFilter), className: "ci-btn ci-row", style: { gap: 4, fontSize: 11 }, title: "Investigate logs", children: _jsx(Search, { size: 12 }) }))] }), _jsxs("div", { style: { padding: "6px 12px", fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.5 }, children: [logsFilter.reactorId ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "ci-mono", style: { color: "var(--ci-text)", opacity: 0.6 }, children: logsFilter.reactorId }), isCorrelationScope && _jsx("span", { style: { marginLeft: 4 }, children: "(all reactors in correlation)" })] })) : (_jsx("span", { children: "All reactors in correlation" })), _jsxs("span", { className: "ci-tabular", style: { marginLeft: 8 }, children: [filteredLogs.length, " logs"] })] }), _jsxs("div", { className: "ci-scroll", style: { padding: "0 4px" }, children: [filteredLogs.length === 0 && (_jsx("div", { style: { padding: 12, fontSize: 11, color: "var(--ci-text-dimmer)" }, children: "No logs match filters" })), filteredLogs.map((log, i) => (_jsx(LogRow, { log: log, showReactor: isCorrelationScope }, i)))] })] }));
63
+ return (_jsxs("div", { className: "h-full flex flex-col", children: [_jsxs("div", { className: "px-3 py-2 border-b border-border flex items-center gap-3 flex-wrap", style: { background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: [_jsx("div", { className: "flex items-center gap-1 text-[10px]", children: ["debug", "info", "warn", "error"].map((level) => (_jsx("button", { onClick: () => toggleLevel(level), className: `px-2 py-0.5 rounded-md uppercase font-semibold transition-all duration-150 ${levelFilter.has(level)
64
+ ? LOG_LEVEL_COLORS[level]
65
+ : "text-muted-foreground/30 line-through"}`, children: level }, level))) }), _jsxs("div", { className: "relative flex items-center", children: [_jsx(Search, { size: 10, className: "absolute left-2.5 text-muted-foreground/40 pointer-events-none" }), _jsx("input", { type: "text", placeholder: "Search logs...", value: searchText, onChange: (e) => setSearchText(e.target.value), className: "pl-7 pr-2 text-[11px] bg-background/50 border border-border rounded-md py-1 w-44 focus:outline-none focus:ring-1 focus:ring-indigo-500/40 focus:border-indigo-500/30 text-foreground placeholder:text-muted-foreground/40 transition-all" })] }), onInvestigate && (_jsx("button", { onClick: () => onInvestigate(logsFilter), className: "flex items-center gap-1 px-2 py-1 rounded-md text-[11px] text-muted-foreground/50 hover:text-foreground hover:bg-white/[0.04] transition-all duration-150", title: "Investigate logs", children: _jsx(Search, { size: 12 }) }))] }), _jsxs("div", { className: "px-3 py-1.5 text-[10px] text-muted-foreground/50", children: [logsFilter.reactorId ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "font-mono text-foreground/60", children: logsFilter.reactorId }), isWorkflowScope && _jsx("span", { className: "ml-1", children: "(all reactors in workflow)" })] })) : (_jsx("span", { children: "All reactors in workflow" })), _jsxs("span", { className: "ml-2 tabular-nums", children: [filteredLogs.length, " logs"] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-1", children: [filteredLogs.length === 0 && (_jsx("div", { className: "p-3 text-[11px] text-muted-foreground/40", children: "No logs match filters" })), filteredLogs.map((log, i) => (_jsx(LogRow, { log: log, showReactor: isWorkflowScope }, i)))] })] }));
74
66
  }
@@ -0,0 +1 @@
1
+ export declare function SubjectChainPane(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useSelector, useDispatch } from "../machine";
4
+ import { CopyablePayload } from "../components/CopyablePayload";
5
+ import { EffectList } from "../components/EffectList";
6
+ import { eventTextColor, eventBg } from "../theme";
7
+ import { formatTs, compactPayload } from "../utils";
8
+ import { AlertTriangle, ChevronRight, Zap } from "lucide-react";
9
+ // ── SubjectChainEventRow ──────────────────────────────────────────────────
10
+ function SubjectChainEventRow({ event, showSourceBadge, effects, loadingEffects, dispatch, }) {
11
+ const [payloadOpen, setPayloadOpen] = useState(false);
12
+ const [effectsOpen, setEffectsOpen] = useState(false);
13
+ const handleEffectsToggle = (e) => {
14
+ e.stopPropagation();
15
+ if (!effectsOpen && effects === undefined && event.id) {
16
+ dispatch({ type: "ui/event_effects_requested", payload: { eventId: event.id } });
17
+ }
18
+ setEffectsOpen((v) => !v);
19
+ };
20
+ return (_jsxs("div", { className: "group px-3 py-2 border-b border-border hover:bg-white/[0.02] transition-all duration-150", children: [_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("span", { className: "text-[10px] font-mono text-muted-foreground/60 w-10 shrink-0 text-right tabular-nums", children: event.seq }), _jsx("span", { className: "text-[10px] text-muted-foreground/70 shrink-0 w-28 tabular-nums", children: formatTs(event.ts) }), showSourceBadge && (_jsx("span", { className: `px-1.5 py-0.5 rounded text-[8px] font-medium shrink-0 border ${event.sourceMode === "stream"
21
+ ? "bg-indigo-500/10 text-indigo-400/80 border-indigo-500/20"
22
+ : "bg-zinc-500/10 text-zinc-400/60 border-zinc-500/15"}`, children: event.sourceMode })), _jsx("span", { className: "text-xs font-mono shrink-0 px-1.5 py-0.5 rounded", style: { color: eventTextColor(event.name), background: eventBg(event.name) }, children: event.name }), _jsxs("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "flex items-center gap-1 text-[10px] font-mono text-muted-foreground/60 hover:text-muted-foreground truncate text-left min-w-0 transition-colors", children: [_jsx(ChevronRight, { size: 10, className: `shrink-0 transition-transform duration-150 ${payloadOpen ? "rotate-90" : ""}` }), _jsx("span", { className: "truncate", children: event.summary ?? compactPayload(event.payload) })] }), event.id && (_jsxs("button", { onClick: handleEffectsToggle, className: `ml-auto opacity-0 group-hover:opacity-100 transition-all duration-150 flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] shrink-0 ${effectsOpen
23
+ ? "opacity-100 bg-indigo-500/10 text-indigo-400/70 border border-indigo-500/20"
24
+ : "hover:bg-white/[0.05] text-muted-foreground/50 border border-transparent"}`, title: "Show effects", children: [_jsx(Zap, { size: 9 }), effects !== undefined && effects.length > 0 && (_jsx("span", { children: effects.length }))] }))] }), payloadOpen && (_jsx(CopyablePayload, { payload: event.payload, className: "mt-2 ml-12 max-h-48" })), effectsOpen && (_jsx("div", { className: "mt-1 ml-12", children: loadingEffects ? (_jsx("div", { className: "text-[9px] text-muted-foreground/40 italic py-1", children: "Loading\u2026" })) : (_jsx(EffectList, { effects: effects ?? [] })) }))] }));
25
+ }
26
+ // ── SubjectChainPane ──────────────────────────────────────────────────────
27
+ export function SubjectChainPane() {
28
+ const dispatch = useDispatch();
29
+ const subjectType = useSelector((s) => s.subjectType);
30
+ const subjectId = useSelector((s) => s.subjectId);
31
+ const subjectMode = useSelector((s) => s.subjectMode);
32
+ const subjectChain = useSelector((s) => s.subjectChain);
33
+ const loading = useSelector((s) => s.subjectChainLoading);
34
+ const hasMore = useSelector((s) => s.subjectChainHasMore);
35
+ const depthCapped = useSelector((s) => s.subjectDepthCapped);
36
+ const expandedEffects = useSelector((s) => s.expandedEffects);
37
+ const loadingEffectsIds = useSelector((s) => s.loadingEffects);
38
+ if (!subjectType || !subjectId) {
39
+ return (_jsx("div", { className: "flex items-center justify-center h-full text-xs text-muted-foreground/50 tracking-wide", children: "Select an entity to view its subject chain" }));
40
+ }
41
+ const modes = [
42
+ { label: "Stream", value: "stream" },
43
+ { label: "Descendants", value: "descendants" },
44
+ { label: "Both", value: "both" },
45
+ ];
46
+ const shortId = subjectId.length > 8 ? subjectId.slice(0, 8) + "…" : subjectId;
47
+ return (_jsxs("div", { className: "h-full flex flex-col", children: [_jsxs("div", { className: "px-3 py-2 border-b border-border shrink-0 space-y-1.5", children: [_jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [_jsx("span", { className: "text-[10px] font-mono text-muted-foreground/60 shrink-0", children: subjectType }), _jsx("span", { className: "text-[10px] font-mono text-foreground/80 truncate", title: subjectId, children: shortId })] }), _jsx("div", { className: "flex items-center gap-1", children: modes.map(({ label, value }) => (_jsx("button", { onClick: () => dispatch({ type: "ui/subject_mode_changed", payload: { mode: value } }), className: `px-2 py-0.5 rounded text-[10px] transition-all ${subjectMode === value
48
+ ? "bg-indigo-500/20 text-indigo-300 border border-indigo-500/30"
49
+ : "text-muted-foreground/60 hover:text-foreground border border-transparent hover:border-border"}`, children: label }, value))) })] }), depthCapped && (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1.5 bg-yellow-500/8 border-b border-yellow-500/15 text-[10px] text-yellow-400/80 shrink-0", children: [_jsx(AlertTriangle, { size: 10, className: "shrink-0" }), "Descendants truncated at depth 10. Some events may not be shown."] })), _jsx("div", { className: "flex-1 overflow-y-auto", children: loading && subjectChain.length === 0 ? (_jsx("div", { className: "p-3 space-y-2 animate-pulse", children: [...Array(5)].map((_, i) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-3 w-10 bg-muted rounded" }), _jsx("div", { className: "h-3 w-24 bg-muted rounded" }), _jsx("div", { className: "h-3 w-20 bg-muted rounded" }), _jsx("div", { className: "h-3 w-36 bg-muted rounded" })] }, i))) })) : subjectChain.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32 text-xs text-muted-foreground/50", children: "No events found" })) : (_jsxs(_Fragment, { children: [subjectChain.map((event) => (_jsx(SubjectChainEventRow, { event: event, showSourceBadge: subjectMode === "both", effects: event.id ? expandedEffects[event.id] : undefined, loadingEffects: event.id ? loadingEffectsIds.includes(event.id) : false, dispatch: dispatch }, event.seq))), hasMore && (_jsx("div", { className: "flex items-center justify-center py-3", children: _jsx("button", { onClick: () => dispatch({ type: "ui/subject_chain_load_more" }), disabled: loading, className: "text-[10px] text-muted-foreground/60 hover:text-foreground px-3 py-1.5 rounded border border-border hover:border-foreground/20 transition-all disabled:opacity-40", children: loading ? "Loading…" : "Load more" }) }))] })) })] }));
50
+ }
@@ -3,19 +3,25 @@ import { useState, useCallback, useEffect, useRef, useMemo } from "react";
3
3
  import { useSelector, useDispatch } from "../machine";
4
4
  import { FilterBar } from "../components/FilterBar";
5
5
  import { CopyablePayload } from "../components/CopyablePayload";
6
+ import { EffectList } from "../components/EffectList";
6
7
  import { eventTextColor, eventBg } from "../theme";
7
8
  import { formatTs, compactPayload, inScrubberRange } from "../utils";
8
- import { Search, ChevronRight } from "lucide-react";
9
- function EventRow({ event, isSelected, onClick, onFilterCorrelation, onInvestigate, }) {
9
+ import { Search, ChevronRight, Zap } from "lucide-react";
10
+ function EventRow({ event, isSelected, onClick, onFilterWorkflow, onInvestigate, onSubjectSelected, onEffectsRequested, effects, loadingEffects, }) {
10
11
  const [payloadOpen, setPayloadOpen] = useState(false);
11
- return (_jsxs("div", { className: `ci-event-row${isSelected ? " ci-selected" : ""}`, children: [_jsx("div", { onClick: onClick, role: "button", tabIndex: 0, style: { width: "100%", textAlign: "left", cursor: "pointer" }, children: _jsxs("div", { className: "ci-row", style: { gap: 10, minWidth: 0 }, children: [_jsx("span", { className: "ci-mono ci-tabular", style: { fontSize: 10, color: "var(--ci-text-dim)", width: 40, flexShrink: 0, textAlign: "right" }, children: event.seq }), _jsx("span", { className: "ci-tabular", style: { fontSize: 10, color: "var(--ci-text-muted)", opacity: 0.7, flexShrink: 0, width: 128 }, children: formatTs(event.ts) }), event.correlationId && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onFilterCorrelation(event.correlationId); }, className: "ci-corr-pill", title: `Filter by correlation ${event.correlationId}`, style: { flexShrink: 0 }, children: event.correlationId.slice(0, 8) })), _jsx("span", { className: "ci-mono", style: {
12
- fontSize: 12,
13
- flexShrink: 0,
14
- padding: "2px 6px",
15
- borderRadius: "var(--ci-radius-sm)",
16
- color: eventTextColor(event.name),
17
- background: eventBg(event.name),
18
- }, children: event.name }), _jsxs("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "ci-mono ci-truncate", style: { display: "flex", alignItems: "center", gap: 4, fontSize: 10, color: "var(--ci-text-dim)", textAlign: "left", minWidth: 0, background: "none", border: "none", cursor: "pointer", transition: "color 150ms" }, title: "Click to expand payload", children: [_jsx(ChevronRight, { size: 10, style: { flexShrink: 0, transition: "transform 150ms", transform: payloadOpen ? "rotate(90deg)" : undefined } }), _jsx("span", { className: "ci-truncate", children: event.summary ?? compactPayload(event.payload) })] }), onInvestigate && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onInvestigate(); }, className: "ci-copy-btn ci-btn", style: { marginLeft: "auto", flexShrink: 0 }, title: "Investigate", children: _jsx(Search, { size: 12 }) }))] }) }), payloadOpen && (_jsx("div", { style: { marginTop: 8, marginLeft: 48, maxHeight: 256 }, children: _jsx(CopyablePayload, { payload: event.payload }) }))] }));
12
+ const [effectsOpen, setEffectsOpen] = useState(false);
13
+ const handleEffectsToggle = (e) => {
14
+ e.stopPropagation();
15
+ setEffectsOpen((v) => !v);
16
+ };
17
+ return (_jsxs("div", { className: `group w-full text-left px-3 py-2 border-b border-border transition-all duration-150 ${isSelected ? "bg-indigo-500/15" : "hover:bg-white/[0.02]"}`, children: [_jsx("div", { onClick: onClick, role: "button", tabIndex: 0, className: "w-full text-left cursor-pointer", children: _jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [_jsx("span", { className: "text-[10px] font-mono text-muted-foreground/60 w-10 shrink-0 text-right tabular-nums", children: event.seq }), _jsx("span", { className: "text-[10px] text-muted-foreground/70 shrink-0 w-32 tabular-nums", children: formatTs(event.ts) }), event.workflowId && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onFilterWorkflow(event.workflowId); }, className: "px-1.5 py-0.5 rounded-full text-[9px] font-mono bg-purple-500/8 text-purple-400/80 hover:bg-purple-500/15 hover:text-purple-400 shrink-0 transition-all border border-purple-500/10", title: `Filter by workflow ${event.workflowId}`, children: event.workflowId.slice(0, 8) })), event.aggregateType && event.aggregateId && onSubjectSelected && (_jsxs("button", { onClick: (e) => { e.stopPropagation(); onSubjectSelected(event.aggregateType, event.aggregateId); }, className: "px-1.5 py-0.5 rounded-full text-[9px] font-mono bg-teal-500/8 text-teal-400/80 hover:bg-teal-500/15 hover:text-teal-400 shrink-0 transition-all border border-teal-500/10", title: `View subject ${event.aggregateType}:${event.aggregateId}`, children: [event.aggregateType, ":", event.aggregateId.slice(0, 8)] })), _jsx("span", { className: "text-xs font-mono shrink-0 px-1.5 py-0.5 rounded", style: { color: eventTextColor(event.name), background: eventBg(event.name) }, children: event.name }), _jsxs("button", { onClick: (e) => { e.stopPropagation(); setPayloadOpen((v) => !v); }, className: "flex items-center gap-1 text-[10px] font-mono text-muted-foreground/60 hover:text-muted-foreground truncate text-left min-w-0 transition-colors", title: "Click to expand payload", children: [_jsx(ChevronRight, { size: 10, className: `shrink-0 transition-transform duration-150 ${payloadOpen ? "rotate-90" : ""}` }), _jsx("span", { className: "truncate", children: event.summary ?? compactPayload(event.payload) })] }), _jsxs("div", { className: "ml-auto flex items-center gap-1 shrink-0", children: [event.id && (_jsxs("button", { onClick: (e) => {
18
+ handleEffectsToggle(e);
19
+ if (!effectsOpen && effects === undefined && event.id) {
20
+ onEffectsRequested?.(event.id);
21
+ }
22
+ }, className: `opacity-0 group-hover:opacity-100 transition-all duration-150 flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] ${effectsOpen
23
+ ? "opacity-100 bg-indigo-500/10 text-indigo-400/70 border border-indigo-500/20"
24
+ : "hover:bg-white/[0.05] text-muted-foreground/50 border border-transparent"}`, title: "Show effects", children: [_jsx(Zap, { size: 9 }), effects !== undefined && effects.length > 0 && _jsx("span", { children: effects.length })] })), onInvestigate && (_jsx("button", { onClick: (e) => { e.stopPropagation(); onInvestigate(); }, className: "opacity-0 group-hover:opacity-100 transition-opacity duration-150 p-1 rounded-md hover:bg-white/[0.05] shrink-0 text-muted-foreground", title: "Investigate", children: _jsx(Search, { size: 12 }) }))] })] }) }), payloadOpen && (_jsx(CopyablePayload, { payload: event.payload, className: "mt-2 ml-12 max-h-64" })), effectsOpen && (_jsx("div", { className: "mt-1 ml-12", children: loadingEffects ? (_jsx("div", { className: "text-[9px] text-muted-foreground/40 italic py-1", children: "Loading\u2026" })) : (_jsx(EffectList, { effects: effects ?? [] })) }))] }));
19
25
  }
20
26
  function InfiniteScrollSentinel({ onVisible, loading }) {
21
27
  const ref = useRef(null);
@@ -32,19 +38,19 @@ function InfiniteScrollSentinel({ onVisible, loading }) {
32
38
  observer.observe(el);
33
39
  return () => observer.disconnect();
34
40
  }, [loading]);
35
- return (_jsx("div", { ref: ref, className: "ci-row", style: { justifyContent: "center", padding: "16px 0" }, children: loading && (_jsxs("div", { className: "ci-row", style: { gap: 8 }, children: [_jsx("div", { className: "ci-pulse", style: { width: 6, height: 6, borderRadius: "50%", background: "rgba(99, 102, 241, 0.5)" } }), _jsx("span", { style: { fontSize: 10, color: "var(--ci-text-dim)" }, children: "Loading" })] })) }));
41
+ return (_jsx("div", { ref: ref, className: "flex items-center justify-center py-4", children: loading && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-indigo-500/50 animate-pulse" }), _jsx("span", { className: "text-[10px] text-muted-foreground/60", children: "Loading" })] })) }));
36
42
  }
37
43
  export function TimelinePane({ onInvestigate } = {}) {
38
44
  const events = useSelector((s) => {
39
45
  let result = s.events;
40
- const cid = s.filters.correlationId;
46
+ const cid = s.filters.workflowId;
41
47
  if (cid)
42
- result = result.filter((e) => e.correlationId === cid);
48
+ result = result.filter((e) => e.workflowId === cid);
43
49
  const search = s.filters.search?.toLowerCase();
44
50
  if (search) {
45
51
  result = result.filter((e) => e.name.toLowerCase().includes(search) ||
46
52
  e.payload.toLowerCase().includes(search) ||
47
- (e.correlationId ?? "").toLowerCase().includes(search));
53
+ (e.workflowId ?? "").toLowerCase().includes(search));
48
54
  }
49
55
  return result;
50
56
  });
@@ -53,6 +59,8 @@ export function TimelinePane({ onInvestigate } = {}) {
53
59
  const selectedSeq = useSelector((s) => s.selectedSeq);
54
60
  const scrubberStart = useSelector((s) => s.scrubberStart);
55
61
  const scrubberEnd = useSelector((s) => s.scrubberEnd);
62
+ const expandedEffects = useSelector((s) => s.expandedEffects);
63
+ const loadingEffectsIds = useSelector((s) => s.loadingEffects);
56
64
  const dispatch = useDispatch();
57
65
  const displayedEvents = useMemo(() => {
58
66
  if (scrubberStart == null && scrubberEnd == null)
@@ -61,15 +69,21 @@ export function TimelinePane({ onInvestigate } = {}) {
61
69
  }, [events, scrubberStart, scrubberEnd]);
62
70
  const handleSelect = useCallback((event) => {
63
71
  dispatch({ type: "ui/event_selected", payload: { seq: event.seq } });
64
- if (event.correlationId) {
65
- dispatch({ type: "ui/flow_opened", payload: { correlationId: event.correlationId } });
72
+ if (event.workflowId) {
73
+ dispatch({ type: "ui/flow_opened", payload: { workflowId: event.workflowId } });
66
74
  }
67
75
  }, [dispatch]);
68
- const handleFilterCorrelation = useCallback((correlationId) => {
69
- dispatch({ type: "ui/filter_changed", payload: { correlationId } });
76
+ const handleFilterWorkflow = useCallback((workflowId) => {
77
+ dispatch({ type: "ui/filter_changed", payload: { workflowId } });
70
78
  }, [dispatch]);
71
79
  const handleLoadMore = useCallback(() => {
72
80
  dispatch({ type: "ui/load_more_requested" });
73
81
  }, [dispatch]);
74
- return (_jsxs("div", { className: "ci-col", children: [_jsx(FilterBar, {}), loading && events.length === 0 ? (_jsx("div", { className: "ci-pulse", style: { padding: 4 }, children: Array.from({ length: 12 }).map((_, i) => (_jsxs("div", { className: "ci-row", style: { gap: 8, padding: "10px 12px", borderBottom: "1px solid var(--ci-border)" }, children: [_jsx("div", { className: "ci-skeleton", style: { height: 12, width: 40, flexShrink: 0 } }), _jsx("div", { className: "ci-skeleton", style: { height: 12, width: 128, flexShrink: 0 } }), _jsx("div", { className: "ci-skeleton", style: { height: 12, flex: 1, maxWidth: `${150 + (i * 37) % 200}px` } })] }, i))) })) : events.length === 0 ? (_jsx("div", { className: "ci-empty", style: { height: 128, fontSize: 14 }, children: "No events found" })) : (_jsxs("div", { className: "ci-scroll", children: [displayedEvents.map((event) => (_jsx(EventRow, { event: event, isSelected: event.seq === selectedSeq, onClick: () => handleSelect(event), onFilterCorrelation: handleFilterCorrelation, onInvestigate: onInvestigate ? () => onInvestigate(event) : undefined }, event.seq))), hasMore && (_jsx(InfiniteScrollSentinel, { onVisible: handleLoadMore, loading: loading }))] }))] }));
82
+ const handleSubjectSelected = useCallback((aggregateType, aggregateId) => {
83
+ dispatch({ type: "ui/subject_selected", payload: { aggregateType, aggregateId, mode: "both" } });
84
+ }, [dispatch]);
85
+ const handleEffectsRequested = useCallback((eventId) => {
86
+ dispatch({ type: "ui/event_effects_requested", payload: { eventId } });
87
+ }, [dispatch]);
88
+ return (_jsxs("div", { className: "flex flex-col h-full", children: [_jsx(FilterBar, {}), loading && events.length === 0 ? (_jsx("div", { className: "animate-pulse p-1", children: Array.from({ length: 12 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-2.5 border-b border-border", children: [_jsx("div", { className: "h-3 w-10 bg-white/[0.03] rounded shrink-0" }), _jsx("div", { className: "h-3 w-32 bg-white/[0.03] rounded shrink-0" }), _jsx("div", { className: "h-3 bg-white/[0.03] rounded flex-1", style: { maxWidth: `${150 + (i * 37) % 200}px` } })] }, i))) })) : events.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32 text-sm text-muted-foreground/60", children: "No events found" })) : (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [displayedEvents.map((event) => (_jsx(EventRow, { event: event, isSelected: event.seq === selectedSeq, onClick: () => handleSelect(event), onFilterWorkflow: handleFilterWorkflow, onInvestigate: onInvestigate ? () => onInvestigate(event) : undefined, onSubjectSelected: handleSubjectSelected, onEffectsRequested: handleEffectsRequested, effects: event.id ? expandedEffects[event.id] : undefined, loadingEffects: event.id ? loadingEffectsIds.includes(event.id) : false }, event.seq))), hasMore && (_jsx(InfiniteScrollSentinel, { onVisible: handleLoadMore, loading: loading }))] }))] }));
75
89
  }
@@ -97,9 +97,9 @@ const LABEL_WIDTH = 160;
97
97
  const BAR_MIN_WIDTH = 4;
98
98
  const PADDING_X = 12;
99
99
  export function WaterfallPane() {
100
- const correlationId = useSelector((s) => s.flowCorrelationId);
101
- const outcomes = useSelector((s) => correlationId ? s.outcomes[correlationId] ?? [] : []);
102
- const attempts = useSelector((s) => correlationId ? s.attempts[correlationId] ?? [] : []);
100
+ const workflowId = useSelector((s) => s.flowWorkflowId);
101
+ const outcomes = useSelector((s) => workflowId ? s.outcomes[workflowId] ?? [] : []);
102
+ const attempts = useSelector((s) => workflowId ? s.attempts[workflowId] ?? [] : []);
103
103
  const flowData = useSelector((s) => s.flowData);
104
104
  const scrubberStart = useSelector((s) => s.scrubberStart);
105
105
  const scrubberEnd = useSelector((s) => s.scrubberEnd);
@@ -138,11 +138,11 @@ export function WaterfallPane() {
138
138
  return null;
139
139
  return new Date(event.ts).getTime();
140
140
  }, [scrubberEnd, flowData]);
141
- if (!correlationId) {
141
+ if (!workflowId) {
142
142
  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 reactor waterfall" }));
143
143
  }
144
144
  if (bars.length === 0) {
145
- return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "No reactor execution data for this correlation" }));
145
+ return (_jsx("div", { style: { height: "100%", display: "flex", alignItems: "center", justifyContent: "center", color: "#50506a", fontSize: 12, letterSpacing: "0.03em" }, children: "No reactor execution data for this workflow" }));
146
146
  }
147
147
  const hasReactorFilter = logsFilter.reactorId != null;
148
148
  return (_jsxs("div", { style: { height: "100%", display: "flex", flexDirection: "column" }, children: [hasReactorFilter && (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, padding: "5px 12px", borderBottom: "1px solid rgba(255,255,255,0.06)", flexShrink: 0, background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: [_jsx("span", { style: { fontSize: 10, color: "#818cf8", fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }, children: logsFilter.reactorId }), _jsx("button", { onClick: () => {
@@ -0,0 +1,2 @@
1
+ export type WorkflowExplorerPaneProps = Record<string, never>;
2
+ export declare function WorkflowExplorerPane(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,49 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useCallback, useEffect, useRef } from "react";
3
+ import { useSelector, useDispatch } from "../machine";
4
+ import { eventTextColor, eventBg } from "../theme";
5
+ import { formatTs } from "../utils";
6
+ function RelativeDuration({ firstTs, lastTs }) {
7
+ const first = new Date(firstTs).getTime();
8
+ const last = new Date(lastTs).getTime();
9
+ const diffMs = last - first;
10
+ if (diffMs < 1000)
11
+ return _jsxs("span", { children: [diffMs, "ms"] });
12
+ if (diffMs < 60_000)
13
+ return _jsxs("span", { children: [(diffMs / 1000).toFixed(1), "s"] });
14
+ if (diffMs < 3_600_000)
15
+ return _jsxs("span", { children: [(diffMs / 60_000).toFixed(1), "m"] });
16
+ return _jsxs("span", { children: [(diffMs / 3_600_000).toFixed(1), "h"] });
17
+ }
18
+ export function WorkflowExplorerPane() {
19
+ const workflows = useSelector((s) => s.workflows);
20
+ const loading = useSelector((s) => s.workflowsLoading);
21
+ const dispatch = useDispatch();
22
+ const [search, setSearch] = useState("");
23
+ const searchTimerRef = useRef(null);
24
+ // Request workflows on mount
25
+ useEffect(() => {
26
+ dispatch({ type: "ui/workflows_requested", payload: {} });
27
+ }, [dispatch]);
28
+ const handleSearchChange = useCallback((value) => {
29
+ setSearch(value);
30
+ if (searchTimerRef.current)
31
+ clearTimeout(searchTimerRef.current);
32
+ searchTimerRef.current = setTimeout(() => {
33
+ dispatch({ type: "ui/workflows_requested", payload: { search: value || undefined } });
34
+ }, 300);
35
+ }, [dispatch]);
36
+ const handleRowClick = useCallback((workflowId) => {
37
+ dispatch({ type: "ui/flow_opened", payload: { workflowId } });
38
+ }, [dispatch]);
39
+ const handleCopy = useCallback((text) => {
40
+ navigator.clipboard.writeText(text).catch(() => { });
41
+ }, []);
42
+ return (_jsxs("div", { className: "flex flex-col h-full", children: [_jsx("div", { className: "px-3 py-2.5 border-b border-border", style: { background: "rgba(15, 15, 20, 0.6)", backdropFilter: "blur(8px)" }, children: _jsx("input", { type: "text", placeholder: "Search by workflow ID or event type...", value: search, onChange: (e) => handleSearchChange(e.target.value), className: "w-full px-3 py-1.5 text-xs bg-background/50 border border-border rounded-md text-foreground placeholder:text-muted-foreground/40 focus:outline-none focus:ring-1 focus:ring-indigo-500/40 focus:border-indigo-500/30 transition-all" }) }), _jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border text-[9px] font-semibold text-muted-foreground/40 uppercase tracking-widest", children: [_jsx("span", { className: "w-28 shrink-0", children: "Root Event" }), _jsx("span", { className: "w-24 shrink-0", children: "Workflow" }), _jsx("span", { className: "w-12 shrink-0 text-right", children: "Events" }), _jsx("span", { className: "w-20 shrink-0 text-right", children: "Duration" }), _jsx("span", { className: "flex-1", children: "Last Activity" })] }), loading && workflows.length === 0 ? (_jsx("div", { className: "animate-pulse p-3", children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "flex items-center gap-2 py-2.5", children: [_jsx("div", { className: "h-3 w-28 bg-white/[0.03] rounded" }), _jsx("div", { className: "h-3 w-24 bg-white/[0.03] rounded" }), _jsx("div", { className: "h-3 w-12 bg-white/[0.03] rounded" })] }, i))) })) : workflows.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-32 text-xs text-muted-foreground/50 tracking-wide", children: "No workflows found" })) : (_jsx("div", { className: "flex-1 overflow-y-auto", children: workflows.map((corr) => (_jsxs("button", { onClick: () => handleRowClick(corr.workflowId), className: "group w-full text-left flex items-center gap-2 px-3 py-2.5 border-b border-border hover:bg-indigo-500/8 transition-all duration-150", children: [_jsx("span", { className: "text-[10px] font-mono shrink-0 w-28 truncate px-1.5 py-0.5 rounded", style: {
43
+ color: eventTextColor(corr.rootEventType),
44
+ background: eventBg(corr.rootEventType),
45
+ }, title: corr.rootEventType, children: corr.rootEventType }), _jsx("span", { className: "text-[10px] font-mono text-purple-400/70 w-24 shrink-0 truncate cursor-pointer hover:text-purple-400 transition-colors", title: `Click to copy: ${corr.workflowId}`, onClick: (e) => { e.stopPropagation(); handleCopy(corr.workflowId); }, children: corr.workflowId.slice(0, 8) }), _jsx("span", { className: "text-[11px] font-mono text-foreground/70 w-12 shrink-0 text-right tabular-nums", children: corr.eventCount }), _jsx("span", { className: "text-[10px] text-muted-foreground/50 w-20 shrink-0 text-right font-mono tabular-nums", children: _jsx(RelativeDuration, { firstTs: corr.firstTs, lastTs: corr.lastTs }) }), _jsx("span", { className: "text-[10px] text-muted-foreground/40 flex-1 truncate tabular-nums", children: formatTs(corr.lastTs) }), corr.hasErrors && (_jsx("span", { className: "flex items-center gap-1 px-1.5 py-0.5 rounded text-[9px] font-semibold shrink-0 bg-red-500/10 text-red-400/80 border border-red-500/20 cursor-pointer hover:bg-red-500/20 hover:text-red-400 transition-colors", style: { boxShadow: "0 0 6px rgba(239, 68, 68, 0.15)" }, title: "View error \u2014 open this workflow and select the failed reactor", onClick: (e) => {
46
+ e.stopPropagation();
47
+ dispatch({ type: "ui/flow_opened", payload: { workflowId: corr.workflowId, focusError: true } });
48
+ }, children: "error" }))] }, corr.workflowId))) }))] }));
49
+ }
package/dist/queries.d.ts CHANGED
@@ -1,16 +1,20 @@
1
1
  /** GraphQL query and subscription documents for the causal inspector API. */
2
- export declare const EVENTS_SUBSCRIPTION = "\n subscription Events($lastSeq: Int) {\n inspectorEventAdded(lastSeq: $lastSeq) {\n \n seq\n ts\n type\n name\n id\n parentId\n correlationId\n reactorId\n aggregateType\n aggregateId\n streamVersion\n summary\n payload\n\n }\n }\n";
3
- export declare const INSPECTOR_EVENTS = "\n query InspectorEvents(\n $limit: Int!\n $cursor: Int\n $search: String\n $correlationId: String\n $aggregateKey: String\n ) {\n inspectorEvents(\n limit: $limit\n cursor: $cursor\n search: $search\n correlationId: $correlationId\n aggregateKey: $aggregateKey\n ) {\n events {\n \n seq\n ts\n type\n name\n id\n parentId\n correlationId\n reactorId\n aggregateType\n aggregateId\n streamVersion\n summary\n payload\n\n }\n nextCursor\n }\n }\n";
4
- export declare const INSPECTOR_CAUSAL_TREE = "\n query InspectorCausalTree($seq: Int!) {\n inspectorCausalTree(seq: $seq) {\n events {\n \n seq\n ts\n type\n name\n id\n parentId\n correlationId\n reactorId\n aggregateType\n aggregateId\n streamVersion\n summary\n payload\n\n }\n rootSeq\n }\n }\n";
5
- export declare const INSPECTOR_CAUSAL_FLOW = "\n query InspectorCausalFlow($correlationId: String!) {\n inspectorCausalFlow(correlationId: $correlationId) {\n events {\n \n seq\n ts\n type\n name\n id\n parentId\n correlationId\n reactorId\n aggregateType\n aggregateId\n streamVersion\n summary\n payload\n\n }\n }\n }\n";
2
+ export declare const EVENTS_SUBSCRIPTION = "\n subscription Events($lastSeq: Int) {\n inspectorEventAdded(lastSeq: $lastSeq) {\n \n seq\n ts\n type\n name\n id\n causationId\n workflowId\n reactorId\n aggregateType\n aggregateId\n streamRevision\n summary\n payload\n\n }\n }\n";
3
+ export declare const INSPECTOR_EVENTS = "\n query InspectorEvents(\n $limit: Int!\n $cursor: Int\n $search: String\n $workflowId: String\n $aggregateKey: String\n ) {\n inspectorEvents(\n limit: $limit\n cursor: $cursor\n search: $search\n workflowId: $workflowId\n aggregateKey: $aggregateKey\n ) {\n events {\n \n seq\n ts\n type\n name\n id\n causationId\n workflowId\n reactorId\n aggregateType\n aggregateId\n streamRevision\n summary\n payload\n\n }\n nextCursor\n }\n }\n";
4
+ export declare const INSPECTOR_CAUSAL_TREE = "\n query InspectorCausalTree($seq: Int!) {\n inspectorCausalTree(seq: $seq) {\n events {\n \n seq\n ts\n type\n name\n id\n causationId\n workflowId\n reactorId\n aggregateType\n aggregateId\n streamRevision\n summary\n payload\n\n }\n rootSeq\n }\n }\n";
5
+ export declare const INSPECTOR_CAUSAL_FLOW = "\n query InspectorCausalFlow($workflowId: String!) {\n inspectorCausalFlow(workflowId: $workflowId) {\n events {\n \n seq\n ts\n type\n name\n id\n causationId\n workflowId\n reactorId\n aggregateType\n aggregateId\n streamRevision\n summary\n payload\n\n }\n }\n }\n";
6
6
  export declare const INSPECTOR_REACTOR_LOGS = "\n query InspectorReactorLogs($eventId: String!, $reactorId: String!) {\n inspectorReactorLogs(eventId: $eventId, reactorId: $reactorId) {\n eventId\n reactorId\n level\n message\n data\n loggedAt\n }\n }\n";
7
- export declare const INSPECTOR_REACTOR_LOGS_BY_CORRELATION = "\n query InspectorReactorLogsByCorrelation($correlationId: String!) {\n inspectorReactorLogsByCorrelation(correlationId: $correlationId) {\n eventId\n reactorId\n level\n message\n data\n loggedAt\n }\n }\n";
8
- export declare const INSPECTOR_REACTOR_DESCRIPTIONS = "\n query InspectorReactorDescriptions($correlationId: String!) {\n inspectorReactorDescriptions(correlationId: $correlationId) {\n reactorId\n blocks\n }\n }\n";
9
- export declare const INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS = "\n query InspectorReactorDescriptionSnapshots($correlationId: String!) {\n inspectorReactorDescriptionSnapshots(correlationId: $correlationId) {\n seq\n eventId\n reactorId\n blocks\n }\n }\n";
10
- export declare const INSPECTOR_AGGREGATE_TIMELINE = "\n query InspectorAggregateTimeline($correlationId: String!) {\n inspectorAggregateTimeline(correlationId: $correlationId) {\n seq\n eventId\n eventType\n aggregates {\n key\n state\n }\n }\n }\n";
7
+ export declare const INSPECTOR_REACTOR_LOGS_BY_WORKFLOW = "\n query InspectorReactorLogsByWorkflow($workflowId: String!) {\n inspectorReactorLogsByWorkflow(workflowId: $workflowId) {\n eventId\n reactorId\n level\n message\n data\n loggedAt\n }\n }\n";
8
+ export declare const INSPECTOR_REACTOR_DESCRIPTIONS = "\n query InspectorReactorDescriptions($workflowId: String!) {\n inspectorReactorDescriptions(workflowId: $workflowId) {\n reactorId\n blocks\n }\n }\n";
9
+ export declare const INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS = "\n query InspectorReactorDescriptionSnapshots($workflowId: String!) {\n inspectorReactorDescriptionSnapshots(workflowId: $workflowId) {\n seq\n eventId\n reactorId\n blocks\n }\n }\n";
10
+ export declare const INSPECTOR_AGGREGATE_TIMELINE = "\n query InspectorAggregateTimeline($workflowId: String!) {\n inspectorAggregateTimeline(workflowId: $workflowId) {\n seq\n eventId\n eventType\n aggregates {\n key\n state\n }\n }\n }\n";
11
11
  export declare const INSPECTOR_REACTOR_DEPENDENCIES = "\n query InspectorReactorDependencies {\n inspectorReactorDependencies {\n reactorId\n inputEventTypes\n outputEventTypes\n }\n }\n";
12
12
  export declare const INSPECTOR_AGGREGATE_KEYS = "\n query InspectorAggregateKeys {\n inspectorAggregateKeys\n }\n";
13
- export declare const INSPECTOR_AGGREGATE_LIFECYCLE = "\n query InspectorAggregateLifecycle($aggregateKey: String!, $limit: Int) {\n inspectorAggregateLifecycle(aggregateKey: $aggregateKey, limit: $limit) {\n seq\n eventId\n eventType\n ts\n correlationId\n aggregateKey\n state\n }\n }\n";
14
- export declare const INSPECTOR_CORRELATIONS = "\n query InspectorCorrelations($search: String, $limit: Int, $cursor: String) {\n inspectorCorrelations(search: $search, limit: $limit, cursor: $cursor) {\n correlations {\n correlationId\n eventCount\n firstTs\n lastTs\n rootEventType\n hasErrors\n }\n nextCursor\n }\n }\n";
15
- export declare const INSPECTOR_REACTOR_OUTCOMES = "\n query InspectorReactorOutcomes($correlationId: String!) {\n inspectorReactorOutcomes(correlationId: $correlationId) {\n reactorId\n status\n error\n attempts\n startedAt\n completedAt\n triggeringEventIds\n }\n }\n";
16
- export declare const INSPECTOR_REACTOR_ATTEMPTS = "\n query InspectorReactorAttempts($correlationId: String!) {\n inspectorReactorAttempts(correlationId: $correlationId) {\n eventId\n reactorId\n correlationId\n attempt\n status\n error\n startedAt\n completedAt\n }\n }\n";
13
+ export declare const INSPECTOR_AGGREGATE_LIFECYCLE = "\n query InspectorAggregateLifecycle($aggregateKey: String!, $limit: Int) {\n inspectorAggregateLifecycle(aggregateKey: $aggregateKey, limit: $limit) {\n seq\n eventId\n eventType\n ts\n workflowId\n aggregateKey\n state\n }\n }\n";
14
+ export declare const INSPECTOR_WORKFLOWS = "\n query InspectorWorkflows($search: String, $limit: Int, $cursor: String) {\n inspectorWorkflows(search: $search, limit: $limit, cursor: $cursor) {\n workflows {\n workflowId\n eventCount\n firstTs\n lastTs\n rootEventType\n hasErrors\n }\n nextCursor\n }\n }\n";
15
+ export declare const INSPECTOR_REACTOR_OUTCOMES = "\n query InspectorReactorOutcomes($workflowId: String!) {\n inspectorReactorOutcomes(workflowId: $workflowId) {\n reactorId\n status\n error\n attempts\n startedAt\n completedAt\n triggeringEventIds\n }\n }\n";
16
+ export declare const INSPECTOR_REACTOR_ATTEMPTS = "\n query InspectorReactorAttempts($workflowId: String!) {\n inspectorReactorAttempts(workflowId: $workflowId) {\n eventId\n reactorId\n workflowId\n attempt\n status\n error\n startedAt\n completedAt\n }\n }\n";
17
+ export declare const INSPECTOR_SUBJECT_CHAIN = "\n query InspectorSubjectChain(\n $aggregateType: String!\n $aggregateId: String!\n $mode: SubjectChainMode!\n $limit: Int\n $cursor: Int\n ) {\n inspectorSubjectChain(\n aggregateType: $aggregateType\n aggregateId: $aggregateId\n mode: $mode\n limit: $limit\n cursor: $cursor\n ) {\n events {\n \n seq\n ts\n type\n name\n id\n causationId\n workflowId\n reactorId\n aggregateType\n aggregateId\n streamRevision\n summary\n payload\n sourceMode\n\n }\n nextCursor\n depthCapReached\n }\n }\n";
18
+ export declare const INSPECTOR_EFFECTS_FOR_EVENT = "\n query InspectorEffectsForEvent($eventId: String!) {\n inspectorEffectsForEvent(eventId: $eventId) {\n consumer\n label\n value\n createdAt\n }\n }\n";
19
+ export declare const INSPECTOR_AGGREGATE_TYPES = "\n query InspectorAggregateTypes($search: String, $limit: Int) {\n inspectorAggregateTypes(search: $search, limit: $limit)\n }\n";
20
+ export declare const INSPECTOR_AGGREGATE_KEYS_BY_TYPE = "\n query InspectorAggregateKeysByType(\n $aggregateType: String!\n $search: String\n $limit: Int\n $cursor: String\n ) {\n inspectorAggregateKeysByType(\n aggregateType: $aggregateType\n search: $search\n limit: $limit\n cursor: $cursor\n ) {\n entries {\n aggregateId\n displayLabel\n }\n nextCursor\n }\n }\n";
package/dist/queries.js CHANGED
@@ -5,12 +5,12 @@ const EVENT_FIELDS = `
5
5
  type
6
6
  name
7
7
  id
8
- parentId
9
- correlationId
8
+ causationId
9
+ workflowId
10
10
  reactorId
11
11
  aggregateType
12
12
  aggregateId
13
- streamVersion
13
+ streamRevision
14
14
  summary
15
15
  payload
16
16
  `;
@@ -26,14 +26,14 @@ export const INSPECTOR_EVENTS = `
26
26
  $limit: Int!
27
27
  $cursor: Int
28
28
  $search: String
29
- $correlationId: String
29
+ $workflowId: String
30
30
  $aggregateKey: String
31
31
  ) {
32
32
  inspectorEvents(
33
33
  limit: $limit
34
34
  cursor: $cursor
35
35
  search: $search
36
- correlationId: $correlationId
36
+ workflowId: $workflowId
37
37
  aggregateKey: $aggregateKey
38
38
  ) {
39
39
  events {
@@ -54,8 +54,8 @@ export const INSPECTOR_CAUSAL_TREE = `
54
54
  }
55
55
  `;
56
56
  export const INSPECTOR_CAUSAL_FLOW = `
57
- query InspectorCausalFlow($correlationId: String!) {
58
- inspectorCausalFlow(correlationId: $correlationId) {
57
+ query InspectorCausalFlow($workflowId: String!) {
58
+ inspectorCausalFlow(workflowId: $workflowId) {
59
59
  events {
60
60
  ${EVENT_FIELDS}
61
61
  }
@@ -74,9 +74,9 @@ export const INSPECTOR_REACTOR_LOGS = `
74
74
  }
75
75
  }
76
76
  `;
77
- export const INSPECTOR_REACTOR_LOGS_BY_CORRELATION = `
78
- query InspectorReactorLogsByCorrelation($correlationId: String!) {
79
- inspectorReactorLogsByCorrelation(correlationId: $correlationId) {
77
+ export const INSPECTOR_REACTOR_LOGS_BY_WORKFLOW = `
78
+ query InspectorReactorLogsByWorkflow($workflowId: String!) {
79
+ inspectorReactorLogsByWorkflow(workflowId: $workflowId) {
80
80
  eventId
81
81
  reactorId
82
82
  level
@@ -87,16 +87,16 @@ export const INSPECTOR_REACTOR_LOGS_BY_CORRELATION = `
87
87
  }
88
88
  `;
89
89
  export const INSPECTOR_REACTOR_DESCRIPTIONS = `
90
- query InspectorReactorDescriptions($correlationId: String!) {
91
- inspectorReactorDescriptions(correlationId: $correlationId) {
90
+ query InspectorReactorDescriptions($workflowId: String!) {
91
+ inspectorReactorDescriptions(workflowId: $workflowId) {
92
92
  reactorId
93
93
  blocks
94
94
  }
95
95
  }
96
96
  `;
97
97
  export const INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS = `
98
- query InspectorReactorDescriptionSnapshots($correlationId: String!) {
99
- inspectorReactorDescriptionSnapshots(correlationId: $correlationId) {
98
+ query InspectorReactorDescriptionSnapshots($workflowId: String!) {
99
+ inspectorReactorDescriptionSnapshots(workflowId: $workflowId) {
100
100
  seq
101
101
  eventId
102
102
  reactorId
@@ -105,8 +105,8 @@ export const INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS = `
105
105
  }
106
106
  `;
107
107
  export const INSPECTOR_AGGREGATE_TIMELINE = `
108
- query InspectorAggregateTimeline($correlationId: String!) {
109
- inspectorAggregateTimeline(correlationId: $correlationId) {
108
+ query InspectorAggregateTimeline($workflowId: String!) {
109
+ inspectorAggregateTimeline(workflowId: $workflowId) {
110
110
  seq
111
111
  eventId
112
112
  eventType
@@ -138,17 +138,17 @@ export const INSPECTOR_AGGREGATE_LIFECYCLE = `
138
138
  eventId
139
139
  eventType
140
140
  ts
141
- correlationId
141
+ workflowId
142
142
  aggregateKey
143
143
  state
144
144
  }
145
145
  }
146
146
  `;
147
- export const INSPECTOR_CORRELATIONS = `
148
- query InspectorCorrelations($search: String, $limit: Int, $cursor: String) {
149
- inspectorCorrelations(search: $search, limit: $limit, cursor: $cursor) {
150
- correlations {
151
- correlationId
147
+ export const INSPECTOR_WORKFLOWS = `
148
+ query InspectorWorkflows($search: String, $limit: Int, $cursor: String) {
149
+ inspectorWorkflows(search: $search, limit: $limit, cursor: $cursor) {
150
+ workflows {
151
+ workflowId
152
152
  eventCount
153
153
  firstTs
154
154
  lastTs
@@ -160,8 +160,8 @@ export const INSPECTOR_CORRELATIONS = `
160
160
  }
161
161
  `;
162
162
  export const INSPECTOR_REACTOR_OUTCOMES = `
163
- query InspectorReactorOutcomes($correlationId: String!) {
164
- inspectorReactorOutcomes(correlationId: $correlationId) {
163
+ query InspectorReactorOutcomes($workflowId: String!) {
164
+ inspectorReactorOutcomes(workflowId: $workflowId) {
165
165
  reactorId
166
166
  status
167
167
  error
@@ -173,11 +173,11 @@ export const INSPECTOR_REACTOR_OUTCOMES = `
173
173
  }
174
174
  `;
175
175
  export const INSPECTOR_REACTOR_ATTEMPTS = `
176
- query InspectorReactorAttempts($correlationId: String!) {
177
- inspectorReactorAttempts(correlationId: $correlationId) {
176
+ query InspectorReactorAttempts($workflowId: String!) {
177
+ inspectorReactorAttempts(workflowId: $workflowId) {
178
178
  eventId
179
179
  reactorId
180
- correlationId
180
+ workflowId
181
181
  attempt
182
182
  status
183
183
  error
@@ -186,3 +186,79 @@ export const INSPECTOR_REACTOR_ATTEMPTS = `
186
186
  }
187
187
  }
188
188
  `;
189
+ // ── Entity-scoped inspection queries ─────────────────────────────────────
190
+ const SUBJECT_CHAIN_EVENT_FIELDS = `
191
+ seq
192
+ ts
193
+ type
194
+ name
195
+ id
196
+ causationId
197
+ workflowId
198
+ reactorId
199
+ aggregateType
200
+ aggregateId
201
+ streamRevision
202
+ summary
203
+ payload
204
+ sourceMode
205
+ `;
206
+ export const INSPECTOR_SUBJECT_CHAIN = `
207
+ query InspectorSubjectChain(
208
+ $aggregateType: String!
209
+ $aggregateId: String!
210
+ $mode: SubjectChainMode!
211
+ $limit: Int
212
+ $cursor: Int
213
+ ) {
214
+ inspectorSubjectChain(
215
+ aggregateType: $aggregateType
216
+ aggregateId: $aggregateId
217
+ mode: $mode
218
+ limit: $limit
219
+ cursor: $cursor
220
+ ) {
221
+ events {
222
+ ${SUBJECT_CHAIN_EVENT_FIELDS}
223
+ }
224
+ nextCursor
225
+ depthCapReached
226
+ }
227
+ }
228
+ `;
229
+ export const INSPECTOR_EFFECTS_FOR_EVENT = `
230
+ query InspectorEffectsForEvent($eventId: String!) {
231
+ inspectorEffectsForEvent(eventId: $eventId) {
232
+ consumer
233
+ label
234
+ value
235
+ createdAt
236
+ }
237
+ }
238
+ `;
239
+ export const INSPECTOR_AGGREGATE_TYPES = `
240
+ query InspectorAggregateTypes($search: String, $limit: Int) {
241
+ inspectorAggregateTypes(search: $search, limit: $limit)
242
+ }
243
+ `;
244
+ export const INSPECTOR_AGGREGATE_KEYS_BY_TYPE = `
245
+ query InspectorAggregateKeysByType(
246
+ $aggregateType: String!
247
+ $search: String
248
+ $limit: Int
249
+ $cursor: String
250
+ ) {
251
+ inspectorAggregateKeysByType(
252
+ aggregateType: $aggregateType
253
+ search: $search
254
+ limit: $limit
255
+ cursor: $cursor
256
+ ) {
257
+ entries {
258
+ aggregateId
259
+ displayLabel
260
+ }
261
+ nextCursor
262
+ }
263
+ }
264
+ `;