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.
Files changed (68) hide show
  1. package/dist/CausalInspector.css +124 -0
  2. package/dist/CausalInspector.d.ts +13 -0
  3. package/dist/CausalInspector.js +257 -0
  4. package/dist/components/CopyablePayload.d.ts +4 -0
  5. package/dist/components/CopyablePayload.js +44 -0
  6. package/dist/components/FilterBar.d.ts +1 -0
  7. package/dist/components/FilterBar.js +23 -0
  8. package/dist/components/GlobalScrubber.d.ts +1 -0
  9. package/dist/components/GlobalScrubber.js +148 -0
  10. package/dist/components/JsonSyntax.d.ts +3 -0
  11. package/dist/components/JsonSyntax.js +40 -0
  12. package/dist/context.d.ts +26 -0
  13. package/dist/context.js +28 -0
  14. package/dist/engines/index.d.ts +22 -0
  15. package/dist/engines/index.js +29 -0
  16. package/dist/engines/query.d.ts +14 -0
  17. package/dist/engines/query.js +241 -0
  18. package/dist/engines/scrubber.d.ts +12 -0
  19. package/dist/engines/scrubber.js +69 -0
  20. package/dist/engines/storage.d.ts +15 -0
  21. package/dist/engines/storage.js +16 -0
  22. package/dist/engines/subscription.d.ts +17 -0
  23. package/dist/engines/subscription.js +44 -0
  24. package/dist/engines/url.d.ts +13 -0
  25. package/dist/engines/url.js +64 -0
  26. package/dist/events.d.ts +77 -0
  27. package/dist/events.js +1 -0
  28. package/dist/index.d.ts +30 -0
  29. package/dist/index.js +29 -0
  30. package/dist/machine/core.d.ts +22 -0
  31. package/dist/machine/core.js +34 -0
  32. package/dist/machine/engine.d.ts +17 -0
  33. package/dist/machine/engine.js +21 -0
  34. package/dist/machine/events.d.ts +18 -0
  35. package/dist/machine/events.js +1 -0
  36. package/dist/machine/hooks.d.ts +23 -0
  37. package/dist/machine/hooks.js +52 -0
  38. package/dist/machine/index.d.ts +5 -0
  39. package/dist/machine/index.js +4 -0
  40. package/dist/machine/store.d.ts +27 -0
  41. package/dist/machine/store.js +42 -0
  42. package/dist/panes/AggregateTimelinePane.d.ts +2 -0
  43. package/dist/panes/AggregateTimelinePane.js +224 -0
  44. package/dist/panes/CausalFlowPane.d.ts +7 -0
  45. package/dist/panes/CausalFlowPane.js +596 -0
  46. package/dist/panes/CausalTreePane.d.ts +5 -0
  47. package/dist/panes/CausalTreePane.js +158 -0
  48. package/dist/panes/CorrelationExplorerPane.d.ts +2 -0
  49. package/dist/panes/CorrelationExplorerPane.js +46 -0
  50. package/dist/panes/LogsPane.d.ts +6 -0
  51. package/dist/panes/LogsPane.js +65 -0
  52. package/dist/panes/TimelinePane.d.ts +6 -0
  53. package/dist/panes/TimelinePane.js +121 -0
  54. package/dist/panes/WaterfallPane.d.ts +2 -0
  55. package/dist/panes/WaterfallPane.js +202 -0
  56. package/dist/queries.d.ts +15 -0
  57. package/dist/queries.js +175 -0
  58. package/dist/reducer.d.ts +4 -0
  59. package/dist/reducer.js +177 -0
  60. package/dist/state.d.ts +34 -0
  61. package/dist/state.js +39 -0
  62. package/dist/theme.d.ts +7 -0
  63. package/dist/theme.js +34 -0
  64. package/dist/types.d.ts +140 -0
  65. package/dist/types.js +1 -0
  66. package/dist/utils.d.ts +14 -0
  67. package/dist/utils.js +91 -0
  68. package/package.json +43 -0
@@ -0,0 +1,202 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useCallback } from "react";
3
+ import { useSelector, useDispatch } from "../machine";
4
+ import { inScrubberRange } from "../utils";
5
+ // ---------------------------------------------------------------------------
6
+ // Timing helpers
7
+ // ---------------------------------------------------------------------------
8
+ function parseTime(ts) {
9
+ if (!ts)
10
+ return null;
11
+ return new Date(ts).getTime();
12
+ }
13
+ function formatDuration(ms) {
14
+ if (ms < 1)
15
+ return "<1ms";
16
+ if (ms < 1000)
17
+ return `${Math.round(ms)}ms`;
18
+ if (ms < 60_000)
19
+ return `${(ms / 1000).toFixed(1)}s`;
20
+ return `${(ms / 60_000).toFixed(1)}m`;
21
+ }
22
+ // ---------------------------------------------------------------------------
23
+ // Colors
24
+ // ---------------------------------------------------------------------------
25
+ const STATUS_COLORS = {
26
+ completed: { bar: "#22c55e", barEnd: "#16a34a", text: "#bbf7d0" },
27
+ running: { bar: "#eab308", barEnd: "#ca8a04", text: "#fef08a" },
28
+ error: { bar: "#ef4444", barEnd: "#dc2626", text: "#fecaca" },
29
+ };
30
+ function statusColor(status) {
31
+ return STATUS_COLORS[status] ?? STATUS_COLORS.running;
32
+ }
33
+ function buildBars(outcomes) {
34
+ const bars = [];
35
+ let minMs = Infinity;
36
+ let maxMs = -Infinity;
37
+ for (const o of outcomes) {
38
+ const start = parseTime(o.startedAt);
39
+ if (start == null)
40
+ continue; // can't render without start
41
+ const end = parseTime(o.completedAt) ?? Date.now();
42
+ minMs = Math.min(minMs, start);
43
+ maxMs = Math.max(maxMs, end);
44
+ bars.push({
45
+ reactorId: o.reactorId,
46
+ status: o.status,
47
+ error: o.error,
48
+ attempts: o.attempts,
49
+ startMs: start,
50
+ endMs: end,
51
+ triggeringEventIds: o.triggeringEventIds,
52
+ });
53
+ }
54
+ // Sort by start time
55
+ bars.sort((a, b) => a.startMs - b.startMs);
56
+ return { bars, minMs, maxMs };
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Constants
60
+ // ---------------------------------------------------------------------------
61
+ const ROW_HEIGHT = 36;
62
+ const LABEL_WIDTH = 160;
63
+ const BAR_MIN_WIDTH = 4;
64
+ const PADDING_X = 12;
65
+ export function WaterfallPane() {
66
+ const correlationId = useSelector((s) => s.flowCorrelationId);
67
+ const outcomes = useSelector((s) => correlationId ? s.outcomes[correlationId] ?? [] : []);
68
+ const flowData = useSelector((s) => s.flowData);
69
+ const scrubberStart = useSelector((s) => s.scrubberStart);
70
+ const scrubberEnd = useSelector((s) => s.scrubberEnd);
71
+ const logsFilter = useSelector((s) => s.logsFilter);
72
+ const dispatch = useDispatch();
73
+ const handleBarClick = useCallback((bar) => {
74
+ dispatch({ type: "ui/handler_selected", payload: { reactorId: bar.reactorId } });
75
+ }, [dispatch]);
76
+ const { bars, minMs, maxMs } = useMemo(() => buildBars(outcomes), [outcomes]);
77
+ const rangeMs = maxMs - minMs || 1;
78
+ // Map event id → seq for scrubber sync
79
+ const eventIdToSeq = useMemo(() => {
80
+ const map = new Map();
81
+ for (const e of flowData) {
82
+ if (e.id)
83
+ map.set(e.id, e.seq);
84
+ }
85
+ return map;
86
+ }, [flowData]);
87
+ // Map scrubber end seq → timestamp for cursor line
88
+ const scrubberMs = useMemo(() => {
89
+ if (scrubberEnd == null)
90
+ return null;
91
+ const event = flowData.find((e) => e.seq === scrubberEnd);
92
+ if (!event)
93
+ return null;
94
+ return new Date(event.ts).getTime();
95
+ }, [scrubberEnd, flowData]);
96
+ if (!correlationId) {
97
+ 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" }));
98
+ }
99
+ if (bars.length === 0) {
100
+ 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" }));
101
+ }
102
+ return (_jsxs("div", { style: { height: "100%", overflow: "auto", padding: `10px ${PADDING_X}px` }, children: [_jsxs("div", { style: { display: "flex", marginBottom: 6, marginLeft: LABEL_WIDTH, position: "relative", height: 16 }, children: [_jsx("span", { style: { fontSize: 9, color: "#40405a", letterSpacing: "0.04em" }, children: "0ms" }), _jsx("span", { style: { fontSize: 9, color: "#40405a", position: "absolute", right: 0, letterSpacing: "0.04em" }, children: formatDuration(rangeMs) }), scrubberMs != null && scrubberMs >= minMs && scrubberMs <= maxMs && (_jsx("div", { style: {
103
+ position: "absolute",
104
+ left: `${((scrubberMs - minMs) / rangeMs) * 100}%`,
105
+ top: 0,
106
+ bottom: -4,
107
+ width: 1,
108
+ background: "#6366f1",
109
+ pointerEvents: "none",
110
+ boxShadow: "0 0 4px rgba(99, 102, 241, 0.4)",
111
+ } }))] }), bars.map((bar) => {
112
+ const offsetPct = ((bar.startMs - minMs) / rangeMs) * 100;
113
+ const widthPct = Math.max(((bar.endMs - bar.startMs) / rangeMs) * 100, 0.5);
114
+ const colors = statusColor(bar.status);
115
+ const duration = bar.endMs - bar.startMs;
116
+ // Scrubber sync: dim bars whose triggering events are all outside range
117
+ const isFuture = (scrubberStart != null || scrubberEnd != null) &&
118
+ bar.triggeringEventIds.length > 0 &&
119
+ bar.triggeringEventIds.every((eid) => {
120
+ const seq = eventIdToSeq.get(eid);
121
+ return seq != null && !inScrubberRange(seq, scrubberStart, scrubberEnd);
122
+ });
123
+ // Scrubber cursor position within this bar's track
124
+ const cursorPct = scrubberMs != null && scrubberMs >= minMs && scrubberMs <= maxMs
125
+ ? ((scrubberMs - minMs) / rangeMs) * 100
126
+ : null;
127
+ const isSelected = logsFilter.reactorId === bar.reactorId;
128
+ return (_jsxs("div", { onClick: () => handleBarClick(bar), style: {
129
+ display: "flex",
130
+ alignItems: "center",
131
+ height: ROW_HEIGHT,
132
+ gap: 0,
133
+ opacity: isFuture ? 0.25 : 1,
134
+ transition: "opacity 200ms, background 150ms",
135
+ cursor: "pointer",
136
+ borderRadius: 6,
137
+ background: isSelected ? "rgba(99, 102, 241, 0.15)" : "transparent",
138
+ paddingLeft: 4,
139
+ paddingRight: 4,
140
+ }, children: [_jsx("div", { style: {
141
+ width: LABEL_WIDTH,
142
+ flexShrink: 0,
143
+ fontSize: 11,
144
+ color: "#c0c0d0",
145
+ fontWeight: 500,
146
+ overflow: "hidden",
147
+ textOverflow: "ellipsis",
148
+ whiteSpace: "nowrap",
149
+ paddingRight: 10,
150
+ letterSpacing: "0.01em",
151
+ }, title: bar.reactorId, children: bar.reactorId }), _jsxs("div", { style: {
152
+ flex: 1,
153
+ position: "relative",
154
+ height: 22,
155
+ background: "rgba(255, 255, 255, 0.02)",
156
+ borderRadius: 5,
157
+ overflow: "hidden",
158
+ }, children: [_jsx("div", { style: {
159
+ position: "absolute",
160
+ left: `${offsetPct}%`,
161
+ width: `${widthPct}%`,
162
+ minWidth: BAR_MIN_WIDTH,
163
+ height: "100%",
164
+ background: `linear-gradient(90deg, ${colors.bar}, ${colors.barEnd})`,
165
+ borderRadius: 4,
166
+ opacity: 0.8,
167
+ display: "flex",
168
+ alignItems: "center",
169
+ paddingLeft: 5,
170
+ paddingRight: 5,
171
+ boxShadow: `0 1px 4px ${colors.bar}30`,
172
+ }, title: `${bar.reactorId}: ${bar.status} (${formatDuration(duration)})${bar.attempts > 1 ? ` — ${bar.attempts} attempts` : ""}${bar.error ? `\nError: ${bar.error}` : ""}`, children: _jsx("span", { style: {
173
+ fontSize: 9,
174
+ fontWeight: 600,
175
+ color: "#0a0a0f",
176
+ whiteSpace: "nowrap",
177
+ overflow: "hidden",
178
+ textOverflow: "ellipsis",
179
+ }, children: formatDuration(duration) }) }), cursorPct != null && (_jsx("div", { style: {
180
+ position: "absolute",
181
+ left: `${cursorPct}%`,
182
+ top: 0,
183
+ bottom: 0,
184
+ width: 1,
185
+ background: "#6366f1",
186
+ pointerEvents: "none",
187
+ boxShadow: "0 0 4px rgba(99, 102, 241, 0.3)",
188
+ } }))] }), _jsxs("div", { style: {
189
+ width: 80,
190
+ flexShrink: 0,
191
+ display: "flex",
192
+ alignItems: "center",
193
+ gap: 4,
194
+ paddingLeft: 10,
195
+ }, children: [_jsx("span", { style: {
196
+ fontSize: 10,
197
+ fontWeight: 500,
198
+ color: colors.text,
199
+ opacity: 0.8,
200
+ }, children: bar.status }), bar.attempts > 1 && (_jsxs("span", { style: { fontSize: 9, color: "#50506a" }, children: ["x", bar.attempts] }))] })] }, bar.reactorId));
201
+ }), _jsxs("div", { style: { marginTop: 10, fontSize: 10, color: "#40405a", marginLeft: LABEL_WIDTH, letterSpacing: "0.03em" }, children: ["Total wall time: ", formatDuration(rangeMs)] })] }));
202
+ }
@@ -0,0 +1,15 @@
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 $from: DateTime\n $to: DateTime\n $correlationId: String\n $aggregateKey: String\n ) {\n inspectorEvents(\n limit: $limit\n cursor: $cursor\n search: $search\n from: $from\n to: $to\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";
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";
11
+ export declare const INSPECTOR_REACTOR_DEPENDENCIES = "\n query InspectorReactorDependencies {\n inspectorReactorDependencies {\n reactorId\n inputEventTypes\n outputEventTypes\n }\n }\n";
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) {\n inspectorCorrelations(search: $search, limit: $limit) {\n correlationId\n eventCount\n firstTs\n lastTs\n rootEventType\n hasErrors\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";
@@ -0,0 +1,175 @@
1
+ /** GraphQL query and subscription documents for the causal inspector API. */
2
+ const EVENT_FIELDS = `
3
+ seq
4
+ ts
5
+ type
6
+ name
7
+ id
8
+ parentId
9
+ correlationId
10
+ reactorId
11
+ aggregateType
12
+ aggregateId
13
+ streamVersion
14
+ summary
15
+ payload
16
+ `;
17
+ export const EVENTS_SUBSCRIPTION = `
18
+ subscription Events($lastSeq: Int) {
19
+ inspectorEventAdded(lastSeq: $lastSeq) {
20
+ ${EVENT_FIELDS}
21
+ }
22
+ }
23
+ `;
24
+ export const INSPECTOR_EVENTS = `
25
+ query InspectorEvents(
26
+ $limit: Int!
27
+ $cursor: Int
28
+ $search: String
29
+ $from: DateTime
30
+ $to: DateTime
31
+ $correlationId: String
32
+ $aggregateKey: String
33
+ ) {
34
+ inspectorEvents(
35
+ limit: $limit
36
+ cursor: $cursor
37
+ search: $search
38
+ from: $from
39
+ to: $to
40
+ correlationId: $correlationId
41
+ aggregateKey: $aggregateKey
42
+ ) {
43
+ events {
44
+ ${EVENT_FIELDS}
45
+ }
46
+ nextCursor
47
+ }
48
+ }
49
+ `;
50
+ export const INSPECTOR_CAUSAL_TREE = `
51
+ query InspectorCausalTree($seq: Int!) {
52
+ inspectorCausalTree(seq: $seq) {
53
+ events {
54
+ ${EVENT_FIELDS}
55
+ }
56
+ rootSeq
57
+ }
58
+ }
59
+ `;
60
+ export const INSPECTOR_CAUSAL_FLOW = `
61
+ query InspectorCausalFlow($correlationId: String!) {
62
+ inspectorCausalFlow(correlationId: $correlationId) {
63
+ events {
64
+ ${EVENT_FIELDS}
65
+ }
66
+ }
67
+ }
68
+ `;
69
+ export const INSPECTOR_REACTOR_LOGS = `
70
+ query InspectorReactorLogs($eventId: String!, $reactorId: String!) {
71
+ inspectorReactorLogs(eventId: $eventId, reactorId: $reactorId) {
72
+ eventId
73
+ reactorId
74
+ level
75
+ message
76
+ data
77
+ loggedAt
78
+ }
79
+ }
80
+ `;
81
+ export const INSPECTOR_REACTOR_LOGS_BY_CORRELATION = `
82
+ query InspectorReactorLogsByCorrelation($correlationId: String!) {
83
+ inspectorReactorLogsByCorrelation(correlationId: $correlationId) {
84
+ eventId
85
+ reactorId
86
+ level
87
+ message
88
+ data
89
+ loggedAt
90
+ }
91
+ }
92
+ `;
93
+ export const INSPECTOR_REACTOR_DESCRIPTIONS = `
94
+ query InspectorReactorDescriptions($correlationId: String!) {
95
+ inspectorReactorDescriptions(correlationId: $correlationId) {
96
+ reactorId
97
+ blocks
98
+ }
99
+ }
100
+ `;
101
+ export const INSPECTOR_REACTOR_DESCRIPTION_SNAPSHOTS = `
102
+ query InspectorReactorDescriptionSnapshots($correlationId: String!) {
103
+ inspectorReactorDescriptionSnapshots(correlationId: $correlationId) {
104
+ seq
105
+ eventId
106
+ reactorId
107
+ blocks
108
+ }
109
+ }
110
+ `;
111
+ export const INSPECTOR_AGGREGATE_TIMELINE = `
112
+ query InspectorAggregateTimeline($correlationId: String!) {
113
+ inspectorAggregateTimeline(correlationId: $correlationId) {
114
+ seq
115
+ eventId
116
+ eventType
117
+ aggregates {
118
+ key
119
+ state
120
+ }
121
+ }
122
+ }
123
+ `;
124
+ export const INSPECTOR_REACTOR_DEPENDENCIES = `
125
+ query InspectorReactorDependencies {
126
+ inspectorReactorDependencies {
127
+ reactorId
128
+ inputEventTypes
129
+ outputEventTypes
130
+ }
131
+ }
132
+ `;
133
+ export const INSPECTOR_AGGREGATE_KEYS = `
134
+ query InspectorAggregateKeys {
135
+ inspectorAggregateKeys
136
+ }
137
+ `;
138
+ export const INSPECTOR_AGGREGATE_LIFECYCLE = `
139
+ query InspectorAggregateLifecycle($aggregateKey: String!, $limit: Int) {
140
+ inspectorAggregateLifecycle(aggregateKey: $aggregateKey, limit: $limit) {
141
+ seq
142
+ eventId
143
+ eventType
144
+ ts
145
+ correlationId
146
+ aggregateKey
147
+ state
148
+ }
149
+ }
150
+ `;
151
+ export const INSPECTOR_CORRELATIONS = `
152
+ query InspectorCorrelations($search: String, $limit: Int) {
153
+ inspectorCorrelations(search: $search, limit: $limit) {
154
+ correlationId
155
+ eventCount
156
+ firstTs
157
+ lastTs
158
+ rootEventType
159
+ hasErrors
160
+ }
161
+ }
162
+ `;
163
+ export const INSPECTOR_REACTOR_OUTCOMES = `
164
+ query InspectorReactorOutcomes($correlationId: String!) {
165
+ inspectorReactorOutcomes(correlationId: $correlationId) {
166
+ reactorId
167
+ status
168
+ error
169
+ attempts
170
+ startedAt
171
+ completedAt
172
+ triggeringEventIds
173
+ }
174
+ }
175
+ `;
@@ -0,0 +1,4 @@
1
+ import type { Reducer } from "./machine";
2
+ import type { InspectorMachineEvent } from "./events";
3
+ import type { InspectorState } from "./state";
4
+ export declare const reducer: Reducer<InspectorState, InspectorMachineEvent>;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Shared navigation logic used by both user-initiated facts
3
+ * (ui/flow_opened, ui/handler_selected) and browser-initiated
4
+ * navigation (location/changed from popstate).
5
+ */
6
+ function applyNavigation(draft, correlationId, handler) {
7
+ // Correlation changed → reset flow state
8
+ if (correlationId !== draft.flowCorrelationId) {
9
+ if (correlationId) {
10
+ draft.flowCorrelationId = correlationId;
11
+ draft.flowData = [];
12
+ draft.flowSelection = null;
13
+ draft.scrubberStart = null;
14
+ draft.scrubberEnd = null;
15
+ draft.scrubberPlaying = false;
16
+ draft.logsFilter = {
17
+ scope: "correlation",
18
+ reactorId: null,
19
+ correlationId,
20
+ };
21
+ }
22
+ else {
23
+ draft.flowCorrelationId = null;
24
+ draft.flowData = [];
25
+ draft.flowSelection = null;
26
+ draft.scrubberStart = null;
27
+ draft.scrubberEnd = null;
28
+ draft.scrubberPlaying = false;
29
+ draft.logsFilter = {
30
+ scope: "reactor",
31
+ reactorId: null,
32
+ correlationId: null,
33
+ };
34
+ }
35
+ }
36
+ // Handler changed → update logs filter
37
+ if (handler && handler !== draft.logsFilter.reactorId) {
38
+ draft.logsFilter = {
39
+ scope: "reactor",
40
+ reactorId: handler,
41
+ correlationId: draft.flowCorrelationId,
42
+ };
43
+ }
44
+ }
45
+ export const reducer = (draft, event) => {
46
+ switch (event.type) {
47
+ // ── Subscription ──
48
+ case "events/received": {
49
+ const newEvents = event.payload;
50
+ // Filter subscription events against active filters so they don't
51
+ // pollute the view when the user has a search or correlation filter.
52
+ const filtered = newEvents.filter((e) => {
53
+ if (draft.filters.correlationId && e.correlationId !== draft.filters.correlationId) {
54
+ return false;
55
+ }
56
+ if (draft.filters.search) {
57
+ const s = draft.filters.search.toLowerCase();
58
+ const matches = e.name.toLowerCase().includes(s) ||
59
+ e.payload.toLowerCase().includes(s) ||
60
+ (e.correlationId ?? "").toLowerCase().includes(s);
61
+ if (!matches)
62
+ return false;
63
+ }
64
+ return true;
65
+ });
66
+ if (filtered.length > 0) {
67
+ draft.events.unshift(...filtered);
68
+ }
69
+ break;
70
+ }
71
+ case "events/subscription_connected":
72
+ draft.subscription = "connected";
73
+ break;
74
+ case "events/subscription_error":
75
+ draft.subscription = "error";
76
+ break;
77
+ // ── Query results ──
78
+ case "events/page_loaded": {
79
+ const { events, hasMore } = event.payload;
80
+ draft.events.push(...events);
81
+ draft.hasMore = hasMore;
82
+ draft.loading = false;
83
+ break;
84
+ }
85
+ case "events/causal_tree_loaded":
86
+ draft.causalTree = event.payload;
87
+ break;
88
+ case "events/flow_loaded":
89
+ draft.flowData = event.payload;
90
+ break;
91
+ case "events/logs_loaded":
92
+ draft.logs = event.payload;
93
+ break;
94
+ case "events/descriptions_loaded": {
95
+ const { correlationId, descriptions } = event.payload;
96
+ draft.descriptions[correlationId] = descriptions;
97
+ break;
98
+ }
99
+ case "events/description_snapshots_loaded": {
100
+ const { correlationId, snapshots } = event.payload;
101
+ draft.descriptionSnapshots[correlationId] = snapshots;
102
+ break;
103
+ }
104
+ case "events/aggregate_timeline_loaded": {
105
+ const { correlationId, entries } = event.payload;
106
+ draft.aggregateTimeline[correlationId] = entries;
107
+ break;
108
+ }
109
+ case "events/outcomes_loaded": {
110
+ const { correlationId, outcomes } = event.payload;
111
+ draft.outcomes[correlationId] = outcomes;
112
+ break;
113
+ }
114
+ case "events/correlations_loaded":
115
+ draft.correlations = event.payload;
116
+ draft.correlationsLoading = false;
117
+ break;
118
+ case "events/reactor_dependencies_loaded":
119
+ draft.reactorDependencies = event.payload;
120
+ break;
121
+ case "events/aggregate_keys_loaded":
122
+ draft.aggregateKeys = event.payload;
123
+ break;
124
+ case "events/aggregate_lifecycle_loaded":
125
+ draft.aggregateLifecycleKey = event.payload.key;
126
+ draft.aggregateLifecycle = event.payload.entries;
127
+ break;
128
+ // ── Navigation (user facts + browser popstate) ──
129
+ case "ui/flow_opened":
130
+ applyNavigation(draft, event.payload.correlationId, null);
131
+ break;
132
+ case "ui/flow_closed":
133
+ applyNavigation(draft, null, null);
134
+ break;
135
+ case "ui/handler_selected":
136
+ applyNavigation(draft, draft.flowCorrelationId, event.payload.reactorId);
137
+ break;
138
+ case "location/changed":
139
+ applyNavigation(draft, event.payload.correlationId, event.payload.handler);
140
+ break;
141
+ // ── UI ──
142
+ case "ui/event_selected":
143
+ draft.selectedSeq = event.payload.seq;
144
+ break;
145
+ case "ui/event_deselected":
146
+ draft.selectedSeq = null;
147
+ draft.causalTree = null;
148
+ break;
149
+ case "ui/flow_node_selected":
150
+ draft.flowSelection = event.payload;
151
+ break;
152
+ case "ui/filter_changed":
153
+ Object.assign(draft.filters, event.payload);
154
+ break;
155
+ case "ui/load_more_requested":
156
+ draft.loading = true;
157
+ break;
158
+ case "ui/layout_changed":
159
+ draft.paneLayout = event.payload;
160
+ break;
161
+ case "ui/scrubber_start_changed":
162
+ draft.scrubberStart = event.payload.start;
163
+ break;
164
+ case "ui/scrubber_end_changed":
165
+ draft.scrubberEnd = event.payload.end;
166
+ break;
167
+ case "ui/scrubber_play_toggled":
168
+ draft.scrubberPlaying = !draft.scrubberPlaying;
169
+ break;
170
+ case "ui/scrubber_speed_changed":
171
+ draft.scrubberSpeed = event.payload.speed;
172
+ break;
173
+ case "ui/correlations_requested":
174
+ draft.correlationsLoading = true;
175
+ break;
176
+ }
177
+ };
@@ -0,0 +1,34 @@
1
+ import type { InspectorEvent, CorrelationSummary, ReactorDependency, AggregateLifecycleEntry, FilterState, FlowSelection, ReactorDescription, ReactorDescriptionSnapshot, AggregateTimelineEntry, ReactorLog, ReactorOutcome, LogsFilter, PaneLayout } from "./types";
2
+ export type InspectorState = {
3
+ events: InspectorEvent[];
4
+ hasMore: boolean;
5
+ loading: boolean;
6
+ selectedSeq: number | null;
7
+ flowCorrelationId: string | null;
8
+ flowData: InspectorEvent[];
9
+ flowSelection: FlowSelection;
10
+ scrubberStart: number | null;
11
+ scrubberEnd: number | null;
12
+ scrubberPlaying: boolean;
13
+ scrubberSpeed: number;
14
+ causalTree: {
15
+ events: InspectorEvent[];
16
+ rootSeq: number;
17
+ } | null;
18
+ filters: FilterState;
19
+ logs: ReactorLog[];
20
+ logsFilter: LogsFilter;
21
+ descriptions: Record<string, ReactorDescription[]>;
22
+ descriptionSnapshots: Record<string, ReactorDescriptionSnapshot[]>;
23
+ aggregateTimeline: Record<string, AggregateTimelineEntry[]>;
24
+ outcomes: Record<string, ReactorOutcome[]>;
25
+ correlations: CorrelationSummary[];
26
+ correlationsLoading: boolean;
27
+ reactorDependencies: ReactorDependency[];
28
+ aggregateKeys: string[];
29
+ aggregateLifecycle: AggregateLifecycleEntry[];
30
+ aggregateLifecycleKey: string | null;
31
+ subscription: "connected" | "disconnected" | "error";
32
+ paneLayout: PaneLayout | null;
33
+ };
34
+ export declare const initialState: InspectorState;
package/dist/state.js ADDED
@@ -0,0 +1,39 @@
1
+ export const initialState = {
2
+ events: [],
3
+ hasMore: true,
4
+ loading: false,
5
+ selectedSeq: null,
6
+ flowCorrelationId: null,
7
+ flowData: [],
8
+ flowSelection: null,
9
+ scrubberStart: null,
10
+ scrubberEnd: null,
11
+ scrubberPlaying: false,
12
+ scrubberSpeed: 300,
13
+ causalTree: null,
14
+ filters: {
15
+ search: "",
16
+ from: null,
17
+ to: null,
18
+ correlationId: null,
19
+ aggregateKey: null,
20
+ },
21
+ logs: [],
22
+ logsFilter: {
23
+ scope: "reactor",
24
+ reactorId: null,
25
+ correlationId: null,
26
+ },
27
+ descriptions: {},
28
+ descriptionSnapshots: {},
29
+ aggregateTimeline: {},
30
+ outcomes: {},
31
+ correlations: [],
32
+ correlationsLoading: false,
33
+ reactorDependencies: [],
34
+ aggregateKeys: [],
35
+ aggregateLifecycle: [],
36
+ aggregateLifecycleKey: null,
37
+ subscription: "disconnected",
38
+ paneLayout: null,
39
+ };
@@ -0,0 +1,7 @@
1
+ /** Deterministic hue from event name — consistent colors across renders. */
2
+ export declare function eventHue(name: string): number;
3
+ /** Background color blended onto dark background — more saturated. */
4
+ export declare function eventBg(name: string): string;
5
+ export declare function eventBorder(name: string): string;
6
+ export declare function eventTextColor(name: string): string;
7
+ export declare const LOG_LEVEL_COLORS: Record<string, string>;