live-traces 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 (63) hide show
  1. package/README.md +193 -0
  2. package/dist/index.d.ts +19 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +24 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/react/hooks.d.ts +22 -0
  7. package/dist/react/hooks.d.ts.map +1 -0
  8. package/dist/react/hooks.js +68 -0
  9. package/dist/react/hooks.js.map +1 -0
  10. package/dist/react/index.d.ts +26 -0
  11. package/dist/react/index.d.ts.map +1 -0
  12. package/dist/react/index.js +27 -0
  13. package/dist/react/index.js.map +1 -0
  14. package/dist/react/store.d.ts +77 -0
  15. package/dist/react/store.d.ts.map +1 -0
  16. package/dist/react/store.js +273 -0
  17. package/dist/react/store.js.map +1 -0
  18. package/dist/src/LiveTrace.d.ts +55 -0
  19. package/dist/src/LiveTrace.d.ts.map +1 -0
  20. package/dist/src/LiveTrace.js +66 -0
  21. package/dist/src/LiveTrace.js.map +1 -0
  22. package/dist/src/Logger.d.ts +3 -0
  23. package/dist/src/Logger.d.ts.map +1 -0
  24. package/dist/src/Logger.js +30 -0
  25. package/dist/src/Logger.js.map +1 -0
  26. package/dist/src/Schema.d.ts +97 -0
  27. package/dist/src/Schema.d.ts.map +1 -0
  28. package/dist/src/Schema.js +60 -0
  29. package/dist/src/Schema.js.map +1 -0
  30. package/dist/src/Sink.d.ts +36 -0
  31. package/dist/src/Sink.d.ts.map +1 -0
  32. package/dist/src/Sink.js +55 -0
  33. package/dist/src/Sink.js.map +1 -0
  34. package/dist/src/Tracer.d.ts +24 -0
  35. package/dist/src/Tracer.d.ts.map +1 -0
  36. package/dist/src/Tracer.js +154 -0
  37. package/dist/src/Tracer.js.map +1 -0
  38. package/dist/src/WrappedSpan.d.ts +44 -0
  39. package/dist/src/WrappedSpan.d.ts.map +1 -0
  40. package/dist/src/WrappedSpan.js +104 -0
  41. package/dist/src/WrappedSpan.js.map +1 -0
  42. package/dist/src/transports/sse.d.ts +26 -0
  43. package/dist/src/transports/sse.d.ts.map +1 -0
  44. package/dist/src/transports/sse.js +118 -0
  45. package/dist/src/transports/sse.js.map +1 -0
  46. package/dist/src/types.d.ts +73 -0
  47. package/dist/src/types.d.ts.map +1 -0
  48. package/dist/src/types.js +69 -0
  49. package/dist/src/types.js.map +1 -0
  50. package/index.ts +58 -0
  51. package/package.json +87 -0
  52. package/react/hooks.ts +73 -0
  53. package/react/index.ts +30 -0
  54. package/react/store.ts +357 -0
  55. package/src/LiveTrace.ts +99 -0
  56. package/src/Logger.ts +33 -0
  57. package/src/Schema.ts +70 -0
  58. package/src/Sink.ts +108 -0
  59. package/src/Tracer.ts +176 -0
  60. package/src/WrappedSpan.ts +138 -0
  61. package/src/__tests__/tracer.test.ts +238 -0
  62. package/src/transports/sse.ts +127 -0
  63. package/src/types.ts +151 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * live-traces — Wire Format Types
3
+ *
4
+ * Plain TypeScript types with zero dependencies.
5
+ * Any backend (Go, Python, Rust, Node) can emit these as JSON
6
+ * and the React frontend will render them.
7
+ *
8
+ * Import from "live-traces/types" — no Effect required.
9
+ */
10
+ export interface TraceScope {
11
+ readonly type: "team" | "org" | "user";
12
+ readonly id: string;
13
+ }
14
+ export interface TraceStart {
15
+ readonly _tag: "TraceStart";
16
+ readonly traceId: string;
17
+ readonly label: string;
18
+ readonly scope: TraceScope;
19
+ readonly timestamp: number;
20
+ }
21
+ export interface SpanStart {
22
+ readonly _tag: "SpanStart";
23
+ readonly traceId: string;
24
+ readonly spanId: string;
25
+ readonly parentSpanId?: string | undefined;
26
+ readonly name: string;
27
+ readonly attributes: Record<string, unknown>;
28
+ readonly timestamp: number;
29
+ }
30
+ export interface SpanEnd {
31
+ readonly _tag: "SpanEnd";
32
+ readonly traceId: string;
33
+ readonly spanId: string;
34
+ readonly status: "ok" | "error";
35
+ readonly durationMs: number;
36
+ readonly timestamp: number;
37
+ }
38
+ export interface SpanEvent {
39
+ readonly _tag: "SpanEvent";
40
+ readonly traceId: string;
41
+ readonly spanId: string;
42
+ readonly name: string;
43
+ readonly level?: "Debug" | "Info" | "Warning" | "Error" | undefined;
44
+ readonly attributes?: Record<string, unknown> | undefined;
45
+ readonly timestamp: number;
46
+ }
47
+ export interface TraceEnd {
48
+ readonly _tag: "TraceEnd";
49
+ readonly traceId: string;
50
+ readonly status: "completed" | "failed";
51
+ readonly durationMs: number;
52
+ readonly error?: string | undefined;
53
+ readonly timestamp: number;
54
+ }
55
+ export type TraceEvent = TraceStart | SpanStart | SpanEnd | SpanEvent | TraceEnd;
56
+ export declare const traceStart: (traceId: string, label: string, scope: TraceScope) => TraceStart;
57
+ export declare const spanStart: (traceId: string, spanId: string, name: string, attributes?: Record<string, unknown>, parentSpanId?: string) => SpanStart;
58
+ export declare const spanEnd: (traceId: string, spanId: string, status: "ok" | "error", durationMs: number) => SpanEnd;
59
+ export declare const spanEvent: (traceId: string, spanId: string, name: string, level?: "Debug" | "Info" | "Warning" | "Error", attributes?: Record<string, unknown>) => SpanEvent;
60
+ export declare const traceEnd: (traceId: string, status: "completed" | "failed", durationMs: number, error?: string) => TraceEnd;
61
+ /** Mark a span as a user-visible step in the trace UI */
62
+ export declare const UI_STEP: "ui.step";
63
+ /** Mark a span as internal (excluded from live trace capture) */
64
+ export declare const TRACE_INTERNAL: "trace.internal";
65
+ /** Root marker — set automatically by LiveTrace.withTrace() */
66
+ export declare const LIVE_TRACE: "live-trace";
67
+ export declare const LIVE_TRACE_ID: "live-trace.id";
68
+ export declare const LIVE_TRACE_LABEL: "live-trace.label";
69
+ export declare const LIVE_TRACE_SCOPE_TYPE: "live-trace.scope.type";
70
+ export declare const LIVE_TRACE_SCOPE_ID: "live-trace.scope.id";
71
+ /** Optional provider key for source/integration filtering (e.g. "notion", "google-drive") */
72
+ export declare const LIVE_TRACE_PROVIDER: "live-trace.provider";
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * live-traces — Wire Format Types
3
+ *
4
+ * Plain TypeScript types with zero dependencies.
5
+ * Any backend (Go, Python, Rust, Node) can emit these as JSON
6
+ * and the React frontend will render them.
7
+ *
8
+ * Import from "live-traces/types" — no Effect required.
9
+ */
10
+ // ============================================================================
11
+ // Factory helpers — plain functions, no Effect import
12
+ // ============================================================================
13
+ export const traceStart = (traceId, label, scope) => ({
14
+ _tag: "TraceStart",
15
+ traceId,
16
+ label,
17
+ scope,
18
+ timestamp: Date.now(),
19
+ });
20
+ export const spanStart = (traceId, spanId, name, attributes = {}, parentSpanId) => ({
21
+ _tag: "SpanStart",
22
+ traceId,
23
+ spanId,
24
+ parentSpanId,
25
+ name,
26
+ attributes,
27
+ timestamp: Date.now(),
28
+ });
29
+ export const spanEnd = (traceId, spanId, status, durationMs) => ({
30
+ _tag: "SpanEnd",
31
+ traceId,
32
+ spanId,
33
+ status,
34
+ durationMs,
35
+ timestamp: Date.now(),
36
+ });
37
+ export const spanEvent = (traceId, spanId, name, level, attributes) => ({
38
+ _tag: "SpanEvent",
39
+ traceId,
40
+ spanId,
41
+ name,
42
+ level,
43
+ attributes,
44
+ timestamp: Date.now(),
45
+ });
46
+ export const traceEnd = (traceId, status, durationMs, error) => ({
47
+ _tag: "TraceEnd",
48
+ traceId,
49
+ status,
50
+ durationMs,
51
+ error,
52
+ timestamp: Date.now(),
53
+ });
54
+ // ============================================================================
55
+ // Attribute constants — use with Effect.withSpan
56
+ // ============================================================================
57
+ /** Mark a span as a user-visible step in the trace UI */
58
+ export const UI_STEP = "ui.step";
59
+ /** Mark a span as internal (excluded from live trace capture) */
60
+ export const TRACE_INTERNAL = "trace.internal";
61
+ /** Root marker — set automatically by LiveTrace.withTrace() */
62
+ export const LIVE_TRACE = "live-trace";
63
+ export const LIVE_TRACE_ID = "live-trace.id";
64
+ export const LIVE_TRACE_LABEL = "live-trace.label";
65
+ export const LIVE_TRACE_SCOPE_TYPE = "live-trace.scope.type";
66
+ export const LIVE_TRACE_SCOPE_ID = "live-trace.scope.id";
67
+ /** Optional provider key for source/integration filtering (e.g. "notion", "google-drive") */
68
+ export const LIVE_TRACE_PROVIDER = "live-trace.provider";
69
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+DH,+EAA+E;AAC/E,sDAAsD;AACtD,+EAA+E;AAE/E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,KAAa,EAAE,KAAiB,EAAc,EAAE,CAAC,CAAC;IAC1F,IAAI,EAAE,YAAY;IAClB,OAAO;IACP,KAAK;IACL,KAAK;IACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CACrB,OAAe,EACf,MAAc,EACd,IAAY,EACZ,aAAsC,EAAE,EACxC,YAAqB,EACZ,EAAE,CAAC,CAAC;IACb,IAAI,EAAE,WAAW;IACjB,OAAO;IACP,MAAM;IACN,YAAY;IACZ,IAAI;IACJ,UAAU;IACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,MAAc,EAAE,MAAsB,EAAE,UAAkB,EAAW,EAAE,CAAC,CAAC;IAC9G,IAAI,EAAE,SAAS;IACf,OAAO;IACP,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CACrB,OAAe,EACf,MAAc,EACd,IAAY,EACZ,KAA8C,EAC9C,UAAoC,EAC3B,EAAE,CAAC,CAAC;IACb,IAAI,EAAE,WAAW;IACjB,OAAO;IACP,MAAM;IACN,IAAI;IACJ,KAAK;IACL,UAAU;IACV,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,MAA8B,EAAE,UAAkB,EAAE,KAAc,EAAY,EAAE,CAAC,CAAC;IACxH,IAAI,EAAE,UAAU;IAChB,OAAO;IACP,MAAM;IACN,UAAU;IACV,KAAK;IACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;CACxB,CAAC,CAAC;AAEH,+EAA+E;AAC/E,iDAAiD;AACjD,+EAA+E;AAE/E,yDAAyD;AACzD,MAAM,CAAC,MAAM,OAAO,GAAG,SAAkB,CAAC;AAE1C,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAyB,CAAC;AAExD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,UAAU,GAAG,YAAqB,CAAC;AAChD,MAAM,CAAC,MAAM,aAAa,GAAG,eAAwB,CAAC;AACtD,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAA2B,CAAC;AAC5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,uBAAgC,CAAC;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAA8B,CAAC;AAClE,6FAA6F;AAC7F,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAA8B,CAAC"}
package/index.ts ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * live-traces
3
+ *
4
+ * Real-time Effect span streaming to frontend UIs.
5
+ *
6
+ * Sub-exports:
7
+ * - "live-traces" — Effect Tracer decorator (this file)
8
+ * - "live-traces/types" — Plain TS types, zero deps
9
+ * - "live-traces/react" — React store + hooks, no Effect dep
10
+ */
11
+
12
+ // Core tracer
13
+ export { LiveTraceLayer } from "./src/Tracer.js";
14
+
15
+ // User-facing API
16
+ export { withTrace, step, LiveSpanRef, type LiveTraceConfig } from "./src/LiveTrace.js";
17
+
18
+ // Logger (bridges Effect.log → SpanEvent inside traced scopes)
19
+ export { liveTraceLogger } from "./src/Logger.js";
20
+
21
+ // Sink + transport
22
+ export {
23
+ TraceSink,
24
+ TraceSinkLive,
25
+ TraceTransportTag,
26
+ ConsoleTransportLayer,
27
+ type TraceTransport,
28
+ type TraceSinkHandle,
29
+ type TraceSinkConfig,
30
+ } from "./src/Sink.js";
31
+
32
+ // WrappedSpan (for advanced use / testing)
33
+ export { WrappedSpan, isWrappedSpan, LiveTraceSymbol } from "./src/WrappedSpan.js";
34
+
35
+ // Effect Schemas
36
+ export {
37
+ TraceEventSchema,
38
+ TraceStartSchema,
39
+ SpanStartSchema,
40
+ SpanEndSchema,
41
+ SpanEventSchema,
42
+ TraceEndSchema,
43
+ TraceScopeSchema,
44
+ } from "./src/Schema.js";
45
+
46
+ // Re-export types for convenience
47
+ export type { TraceEvent, TraceStart, SpanStart, SpanEnd, SpanEvent, TraceEnd, TraceScope } from "./src/types.js";
48
+
49
+ export {
50
+ UI_STEP,
51
+ TRACE_INTERNAL,
52
+ LIVE_TRACE,
53
+ LIVE_TRACE_ID,
54
+ LIVE_TRACE_LABEL,
55
+ LIVE_TRACE_SCOPE_TYPE,
56
+ LIVE_TRACE_SCOPE_ID,
57
+ LIVE_TRACE_PROVIDER,
58
+ } from "./src/types.js";
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "live-traces",
3
+ "version": "0.1.0",
4
+ "description": "Real-time Effect span streaming to frontend UIs. Stream traces from any backend to React with zero overhead.",
5
+ "keywords": [
6
+ "effect",
7
+ "tracing",
8
+ "telemetry",
9
+ "opentelemetry",
10
+ "spans",
11
+ "observability",
12
+ "react",
13
+ "live",
14
+ "streaming",
15
+ "sse",
16
+ "websocket"
17
+ ],
18
+ "license": "Apache-2.0",
19
+ "author": "Necmettin Karakaya <necmettin.karakaya@gmail.com>",
20
+ "homepage": "https://live-traces.necmttn.com",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/necmttn/live-traces.git",
24
+ "directory": "packages/live-traces"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/necmttn/live-traces/issues"
28
+ },
29
+ "type": "module",
30
+ "sideEffects": false,
31
+ "files": [
32
+ "dist",
33
+ "src",
34
+ "react",
35
+ "index.ts",
36
+ "README.md",
37
+ "LICENSE"
38
+ ],
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "import": "./dist/index.js"
43
+ },
44
+ "./types": {
45
+ "types": "./dist/src/types.d.ts",
46
+ "import": "./dist/src/types.js"
47
+ },
48
+ "./react": {
49
+ "types": "./dist/react/index.d.ts",
50
+ "import": "./dist/react/index.js"
51
+ },
52
+ "./transports/sse": {
53
+ "types": "./dist/src/transports/sse.d.ts",
54
+ "import": "./dist/src/transports/sse.js"
55
+ }
56
+ },
57
+ "main": "./dist/index.js",
58
+ "types": "./dist/index.d.ts",
59
+ "scripts": {
60
+ "build": "tsc -p tsconfig.build.json",
61
+ "clean": "rm -rf dist .tsbuildinfo",
62
+ "typecheck": "tsc --noEmit",
63
+ "test": "vitest run",
64
+ "test:watch": "vitest",
65
+ "lint": "echo 'no lint configured'",
66
+ "prepublishOnly": "bun run clean && bun run build"
67
+ },
68
+ "peerDependencies": {
69
+ "effect": ">=3.10.0",
70
+ "react": ">=18"
71
+ },
72
+ "peerDependenciesMeta": {
73
+ "react": {
74
+ "optional": true
75
+ }
76
+ },
77
+ "devDependencies": {
78
+ "@types/react": "^19.0.0",
79
+ "effect": "^3.18.0",
80
+ "react": "^19.0.0",
81
+ "typescript": "^5.7.0",
82
+ "vitest": "^3.0.0"
83
+ },
84
+ "publishConfig": {
85
+ "access": "public"
86
+ }
87
+ }
package/react/hooks.ts ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * React hooks for consuming trace data.
3
+ *
4
+ * Uses useSyncExternalStore for tear-free reads from the TraceStore.
5
+ * All hooks return STABLE references — derived data is computed in
6
+ * useMemo to prevent infinite render loops.
7
+ *
8
+ * No Effect dependency — works with any React 18+ app.
9
+ */
10
+ import { useMemo, useSyncExternalStore } from "react";
11
+
12
+ import type { SpanNode, TraceState } from "./store.js";
13
+
14
+ import { getTraceStore } from "./store.js";
15
+
16
+ /** Stable empty array to avoid creating new references */
17
+ const EMPTY_STEPS: SpanNode[] = [];
18
+
19
+ /**
20
+ * Get the stable Map snapshot from the store.
21
+ * This is the foundation for all hooks — the Map reference only changes
22
+ * when the store is actually updated via dispatch().
23
+ */
24
+ function useSnapshot() {
25
+ const store = getTraceStore();
26
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
27
+ }
28
+
29
+ /**
30
+ * Subscribe to all active traces.
31
+ * Returns traces sorted by startedAt descending.
32
+ */
33
+ export function useActiveTraces(): TraceState[] {
34
+ const snapshot = useSnapshot();
35
+ return useMemo(() => Array.from(snapshot.values()).toSorted((a, b) => b.startedAt - a.startedAt), [snapshot]);
36
+ }
37
+
38
+ /**
39
+ * Subscribe to a single trace by ID.
40
+ * Returns undefined if the trace doesn't exist (yet).
41
+ */
42
+ export function useTrace(traceId: string): TraceState | undefined {
43
+ const snapshot = useSnapshot();
44
+ return useMemo(() => snapshot.get(traceId), [snapshot, traceId]);
45
+ }
46
+
47
+ /**
48
+ * Get the step spans (ui.step=true) for a trace, in chronological order.
49
+ * These are the user-visible processing stages.
50
+ */
51
+ export function useTraceSteps(traceId: string): SpanNode[] {
52
+ const snapshot = useSnapshot();
53
+ return useMemo(() => {
54
+ const trace = snapshot.get(traceId);
55
+ if (!trace) return EMPTY_STEPS;
56
+ const steps = Array.from(trace.spans.values()).filter((s) => s.isStep);
57
+ if (steps.length === 0) return EMPTY_STEPS;
58
+ return steps.toSorted((a, b) => a.startedAt - b.startedAt);
59
+ }, [snapshot, traceId]);
60
+ }
61
+
62
+ /**
63
+ * Get the root span tree for a trace.
64
+ * Returns the root SpanNode with nested children.
65
+ */
66
+ export function useSpanTree(traceId: string): SpanNode | undefined {
67
+ const snapshot = useSnapshot();
68
+ return useMemo(() => {
69
+ const trace = snapshot.get(traceId);
70
+ if (!trace || !trace.rootSpanId) return undefined;
71
+ return trace.spans.get(trace.rootSpanId);
72
+ }, [snapshot, traceId]);
73
+ }
package/react/index.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * live-traces/react
3
+ *
4
+ * React store and hooks for consuming trace events.
5
+ * Zero Effect dependency — works with any React 18+ app.
6
+ *
7
+ * Usage:
8
+ * ```tsx
9
+ * import { useActiveTraces, useTrace, useTraceSteps } from "live-traces/react"
10
+ *
11
+ * function ActivityPanel() {
12
+ * const traces = useActiveTraces()
13
+ * return traces.map(t => <TraceCard key={t.traceId} trace={t} />)
14
+ * }
15
+ *
16
+ * function TraceCard({ trace }: { trace: TraceState }) {
17
+ * const steps = useTraceSteps(trace.traceId)
18
+ * return steps.map(s => <StepRow key={s.spanId} step={s} />)
19
+ * }
20
+ * ```
21
+ */
22
+
23
+ // Store
24
+ export { TraceStore, getTraceStore } from "./store.js";
25
+ export type { TraceState, SpanNode, SpanEventEntry, TraceStatus } from "./store.js";
26
+
27
+ // Hooks
28
+ export { useActiveTraces, useTrace, useTraceSteps, useSpanTree } from "./hooks.js";
29
+
30
+ export { LIVE_TRACE_PROVIDER, LIVE_TRACE_SCOPE_TYPE, LIVE_TRACE_SCOPE_ID } from "../src/types.js";