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
package/dist/theme.js ADDED
@@ -0,0 +1,34 @@
1
+ /** Deterministic hue from event name — consistent colors across renders. */
2
+ export function eventHue(name) {
3
+ let hash = 0;
4
+ for (const ch of name)
5
+ hash = (hash * 31 + ch.charCodeAt(0)) | 0;
6
+ return ((hash % 360) + 360) % 360;
7
+ }
8
+ /** Background color blended onto dark background — more saturated. */
9
+ export function eventBg(name) {
10
+ const h = eventHue(name);
11
+ const [r, g, b] = hslToRgb(h, 75, 50);
12
+ const br = 10, bg = 10, bb = 15; // #0a0a0f
13
+ const a = 0.15;
14
+ return `rgb(${Math.round(br + (r - br) * a)}, ${Math.round(bg + (g - bg) * a)}, ${Math.round(bb + (b - bb) * a)})`;
15
+ }
16
+ export function eventBorder(name) {
17
+ return `hsla(${eventHue(name)}, 65%, 55%, 0.5)`;
18
+ }
19
+ export function eventTextColor(name) {
20
+ return `hsl(${eventHue(name)}, 70%, 70%)`;
21
+ }
22
+ function hslToRgb(h, s, l) {
23
+ s /= 100;
24
+ l /= 100;
25
+ const k = (n) => (n + h / 30) % 12;
26
+ const a = s * Math.min(l, 1 - l);
27
+ const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, 9 - k(n), 1));
28
+ return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];
29
+ }
30
+ export const LOG_LEVEL_COLORS = {
31
+ debug: "bg-zinc-600/20 text-zinc-400",
32
+ info: "bg-indigo-500/15 text-indigo-400",
33
+ warn: "bg-amber-500/15 text-amber-400",
34
+ };
@@ -0,0 +1,140 @@
1
+ /** Processed event from the causal inspector backend. */
2
+ export type InspectorEvent = {
3
+ seq: number;
4
+ ts: string;
5
+ type: string;
6
+ name: string;
7
+ id: string | null;
8
+ parentId: string | null;
9
+ correlationId: string | null;
10
+ reactorId: string | null;
11
+ aggregateType: string | null;
12
+ aggregateId: string | null;
13
+ streamVersion: number | null;
14
+ summary: string | null;
15
+ payload: string;
16
+ };
17
+ export type InspectorEventsPage = {
18
+ events: InspectorEvent[];
19
+ nextCursor: number | null;
20
+ };
21
+ export type InspectorCausalTree = {
22
+ events: InspectorEvent[];
23
+ rootSeq: number;
24
+ };
25
+ export type InspectorCausalFlow = {
26
+ events: InspectorEvent[];
27
+ };
28
+ export type ReactorLog = {
29
+ eventId: string;
30
+ reactorId: string;
31
+ level: string;
32
+ message: string;
33
+ data: unknown;
34
+ loggedAt: string;
35
+ };
36
+ /** Structured block within a reactor description (mirrors causal reactor DSL). */
37
+ export type Block = {
38
+ type: "label";
39
+ text: string;
40
+ } | {
41
+ type: "counter";
42
+ label: string;
43
+ value: number;
44
+ total: number;
45
+ } | {
46
+ type: "progress";
47
+ label: string;
48
+ fraction: number;
49
+ } | {
50
+ type: "checklist";
51
+ label: string;
52
+ items: {
53
+ text: string;
54
+ done: boolean;
55
+ }[];
56
+ } | {
57
+ type: "key_value";
58
+ key: string;
59
+ value: string;
60
+ } | {
61
+ type: "status";
62
+ label: string;
63
+ state: "waiting" | "running" | "done" | "error";
64
+ };
65
+ export type ReactorDescription = {
66
+ reactorId: string;
67
+ blocks: Block[];
68
+ };
69
+ export type ReactorOutcome = {
70
+ reactorId: string;
71
+ status: string;
72
+ error: string | null;
73
+ attempts: number;
74
+ startedAt: string | null;
75
+ completedAt: string | null;
76
+ triggeringEventIds: string[];
77
+ };
78
+ export type ReactorDescriptionSnapshot = {
79
+ seq: number;
80
+ eventId: string;
81
+ reactorId: string;
82
+ blocks: Block[];
83
+ };
84
+ export type AggregateStateEntry = {
85
+ key: string;
86
+ state: unknown;
87
+ };
88
+ export type AggregateTimelineEntry = {
89
+ seq: number;
90
+ eventId: string;
91
+ eventType: string;
92
+ aggregates: AggregateStateEntry[];
93
+ };
94
+ export type ReactorDependency = {
95
+ reactorId: string;
96
+ inputEventTypes: string[];
97
+ outputEventTypes: string[];
98
+ };
99
+ export type AggregateLifecycleEntry = {
100
+ seq: number;
101
+ eventId: string;
102
+ eventType: string;
103
+ ts: string;
104
+ correlationId: string;
105
+ aggregateKey: string;
106
+ state: unknown;
107
+ };
108
+ export type CorrelationSummary = {
109
+ correlationId: string;
110
+ eventCount: number;
111
+ firstTs: string;
112
+ lastTs: string;
113
+ rootEventType: string;
114
+ hasErrors: boolean;
115
+ };
116
+ export type FilterState = {
117
+ search: string;
118
+ from: string | null;
119
+ to: string | null;
120
+ correlationId: string | null;
121
+ aggregateKey: string | null;
122
+ };
123
+ export type LogsFilter = {
124
+ scope: "reactor" | "correlation";
125
+ reactorId: string | null;
126
+ correlationId: string | null;
127
+ };
128
+ export type FlowSelection = {
129
+ kind: "event-type";
130
+ name: string;
131
+ } | {
132
+ kind: "reactor";
133
+ reactorId: string;
134
+ } | null;
135
+ /**
136
+ * Serialized pane layout — opaque JSON structure stored in state.
137
+ * Consumers (e.g. flexlayout-react) interpret this; the inspector
138
+ * just stores and round-trips it.
139
+ */
140
+ export type PaneLayout = Record<string, unknown>;
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { InspectorState } from "./state";
2
+ import type { InspectorEvent } from "./types";
3
+ /** Check if a seq falls within the scrubber range. */
4
+ export declare function inScrubberRange(seq: number, start: number | null, end: number | null): boolean;
5
+ /** Derive the walkable seq list based on current context (flow, selection, or global). */
6
+ export declare function getScrubberSequence(state: InspectorState): number[];
7
+ /** Format a timestamp for compact display. */
8
+ export declare function formatTs(ts: string): string;
9
+ /** Compact JSON payload for inline display. */
10
+ export declare function compactPayload(raw: string, maxLen?: number): string;
11
+ /** Composite aggregate key from event fields, or null. */
12
+ export declare function aggregateKey(event: InspectorEvent): string | null;
13
+ /** Copy text to clipboard with fallback for older browsers. */
14
+ export declare function copyToClipboard(text: string): Promise<void>;
package/dist/utils.js ADDED
@@ -0,0 +1,91 @@
1
+ /** Check if a seq falls within the scrubber range. */
2
+ export function inScrubberRange(seq, start, end) {
3
+ if (start != null && seq < start)
4
+ return false;
5
+ if (end != null && seq > end)
6
+ return false;
7
+ return true;
8
+ }
9
+ /** Derive the walkable seq list based on current context (flow, selection, or global). */
10
+ export function getScrubberSequence(state) {
11
+ let events;
12
+ if (state.flowCorrelationId) {
13
+ events = state.flowData;
14
+ const sel = state.flowSelection;
15
+ if (sel) {
16
+ if (sel.kind === "event-type") {
17
+ events = events.filter(e => e.name === sel.name);
18
+ }
19
+ else {
20
+ events = events.filter(e => e.reactorId === sel.reactorId);
21
+ }
22
+ }
23
+ }
24
+ else {
25
+ events = state.events;
26
+ }
27
+ return events.map(e => e.seq).sort((a, b) => a - b);
28
+ }
29
+ /** Format a timestamp for compact display. */
30
+ export function formatTs(ts) {
31
+ return new Date(ts).toLocaleString(undefined, {
32
+ month: "short",
33
+ day: "numeric",
34
+ hour: "2-digit",
35
+ minute: "2-digit",
36
+ second: "2-digit",
37
+ });
38
+ }
39
+ /** Compact JSON payload for inline display. */
40
+ export function compactPayload(raw, maxLen = 200) {
41
+ try {
42
+ const obj = JSON.parse(raw);
43
+ if (typeof obj !== "object" || obj === null)
44
+ return raw.slice(0, maxLen);
45
+ const entries = Object.entries(obj).filter(([k]) => k !== "type");
46
+ if (entries.length === 0)
47
+ return "{}";
48
+ const parts = [];
49
+ let len = 2;
50
+ for (const [k, v] of entries) {
51
+ const val = typeof v === "string"
52
+ ? v.length > 60
53
+ ? `"${v.slice(0, 57)}..."`
54
+ : `"${v}"`
55
+ : JSON.stringify(v);
56
+ const part = `${k}: ${val}`;
57
+ if (len + part.length + 2 > maxLen) {
58
+ parts.push("...");
59
+ break;
60
+ }
61
+ parts.push(part);
62
+ len += part.length + 2;
63
+ }
64
+ return `{ ${parts.join(", ")} }`;
65
+ }
66
+ catch {
67
+ return raw.slice(0, maxLen);
68
+ }
69
+ }
70
+ /** Composite aggregate key from event fields, or null. */
71
+ export function aggregateKey(event) {
72
+ return event.aggregateType && event.aggregateId
73
+ ? `${event.aggregateType}:${event.aggregateId}`
74
+ : null;
75
+ }
76
+ /** Copy text to clipboard with fallback for older browsers. */
77
+ export async function copyToClipboard(text) {
78
+ try {
79
+ await navigator.clipboard.writeText(text);
80
+ }
81
+ catch {
82
+ const ta = document.createElement("textarea");
83
+ ta.value = text;
84
+ ta.style.position = "fixed";
85
+ ta.style.opacity = "0";
86
+ document.body.appendChild(ta);
87
+ ta.select();
88
+ document.execCommand("copy");
89
+ document.body.removeChild(ta);
90
+ }
91
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "causal-inspector",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": ["dist"],
8
+ "scripts": {
9
+ "build": "tsc && cp src/*.css dist/",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "graphql-ws": "^6.0.0",
14
+ "immer": "^10.0.0",
15
+ "lucide-react": "^0.575.0"
16
+ },
17
+ "peerDependencies": {
18
+ "@dagrejs/dagre": "^1.0.0",
19
+ "@xyflow/react": "^12.0.0",
20
+ "flexlayout-react": ">=0.7.0",
21
+ "react": "^18.0.0 || ^19.0.0",
22
+ "react-dom": "^18.0.0 || ^19.0.0"
23
+ },
24
+ "peerDependenciesMeta": {
25
+ "@xyflow/react": {
26
+ "optional": true
27
+ },
28
+ "@dagrejs/dagre": {
29
+ "optional": true
30
+ },
31
+ "flexlayout-react": {
32
+ "optional": true
33
+ }
34
+ },
35
+ "devDependencies": {
36
+ "@dagrejs/dagre": "^2.0.4",
37
+ "@types/react": "^19.0.0",
38
+ "@types/react-dom": "^19.2.3",
39
+ "@xyflow/react": "^12.10.1",
40
+ "flexlayout-react": "^0.8.19",
41
+ "typescript": "^5.0.0"
42
+ }
43
+ }