kernl 0.12.4 → 0.12.6

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 (95) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/README.md +29 -0
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +2 -0
  6. package/dist/kernl/kernl.d.ts +6 -0
  7. package/dist/kernl/kernl.d.ts.map +1 -1
  8. package/dist/kernl/kernl.js +19 -0
  9. package/dist/kernl/types.d.ts +6 -0
  10. package/dist/kernl/types.d.ts.map +1 -1
  11. package/dist/lib/env.d.ts +2 -2
  12. package/dist/mcp/http.d.ts.map +1 -1
  13. package/dist/mcp/http.js +1 -5
  14. package/dist/mcp/sse.d.ts.map +1 -1
  15. package/dist/mcp/sse.js +1 -5
  16. package/dist/mcp/stdio.d.ts.map +1 -1
  17. package/dist/mcp/stdio.js +1 -5
  18. package/dist/task.d.ts.map +1 -1
  19. package/dist/task.js +0 -1
  20. package/dist/thread/thread.d.ts +5 -4
  21. package/dist/thread/thread.d.ts.map +1 -1
  22. package/dist/thread/thread.js +91 -22
  23. package/dist/thread/types.d.ts +5 -0
  24. package/dist/thread/types.d.ts.map +1 -1
  25. package/dist/tracing/__tests__/composite.test.d.ts +2 -0
  26. package/dist/tracing/__tests__/composite.test.d.ts.map +1 -0
  27. package/dist/tracing/__tests__/composite.test.js +146 -0
  28. package/dist/tracing/__tests__/dispatch.test.d.ts +2 -0
  29. package/dist/tracing/__tests__/dispatch.test.d.ts.map +1 -0
  30. package/dist/tracing/__tests__/dispatch.test.js +160 -0
  31. package/dist/tracing/__tests__/helpers.d.ts +69 -0
  32. package/dist/tracing/__tests__/helpers.d.ts.map +1 -0
  33. package/dist/tracing/__tests__/helpers.js +109 -0
  34. package/dist/tracing/__tests__/integration.test.d.ts +2 -0
  35. package/dist/tracing/__tests__/integration.test.d.ts.map +1 -0
  36. package/dist/tracing/__tests__/integration.test.js +675 -0
  37. package/dist/tracing/__tests__/span.test.d.ts +2 -0
  38. package/dist/tracing/__tests__/span.test.d.ts.map +1 -0
  39. package/dist/tracing/__tests__/span.test.js +188 -0
  40. package/dist/tracing/dispatch.d.ts +43 -0
  41. package/dist/tracing/dispatch.d.ts.map +1 -0
  42. package/dist/tracing/dispatch.js +70 -0
  43. package/dist/tracing/index.d.ts +8 -0
  44. package/dist/tracing/index.d.ts.map +1 -0
  45. package/dist/tracing/index.js +6 -0
  46. package/dist/tracing/span.d.ts +69 -0
  47. package/dist/tracing/span.d.ts.map +1 -0
  48. package/dist/tracing/span.js +64 -0
  49. package/dist/tracing/subscriber.d.ts +53 -0
  50. package/dist/tracing/subscriber.d.ts.map +1 -0
  51. package/dist/tracing/subscriber.js +1 -0
  52. package/dist/tracing/subscribers/composite.d.ts +26 -0
  53. package/dist/tracing/subscribers/composite.d.ts.map +1 -0
  54. package/dist/tracing/subscribers/composite.js +96 -0
  55. package/dist/tracing/subscribers/console.d.ts +22 -0
  56. package/dist/tracing/subscribers/console.d.ts.map +1 -0
  57. package/dist/tracing/subscribers/console.js +82 -0
  58. package/dist/tracing/types.d.ts +77 -0
  59. package/dist/tracing/types.d.ts.map +1 -0
  60. package/dist/tracing/types.js +1 -0
  61. package/package.json +5 -1
  62. package/src/agent.ts +2 -0
  63. package/src/index.ts +1 -0
  64. package/src/kernl/kernl.ts +21 -0
  65. package/src/kernl/types.ts +7 -0
  66. package/src/mcp/http.ts +1 -9
  67. package/src/mcp/sse.ts +1 -10
  68. package/src/mcp/stdio.ts +1 -10
  69. package/src/task.ts +0 -1
  70. package/src/thread/thread.ts +111 -24
  71. package/src/thread/types.ts +5 -0
  72. package/src/tracing/__tests__/composite.test.ts +218 -0
  73. package/src/tracing/__tests__/dispatch.test.ts +222 -0
  74. package/src/tracing/__tests__/helpers.ts +138 -0
  75. package/src/tracing/__tests__/integration.test.ts +808 -0
  76. package/src/tracing/__tests__/span.test.ts +250 -0
  77. package/src/tracing/dispatch.ts +114 -0
  78. package/src/tracing/index.ts +39 -0
  79. package/src/tracing/span.ts +115 -0
  80. package/src/tracing/subscriber.ts +62 -0
  81. package/src/tracing/subscribers/composite.ts +102 -0
  82. package/src/tracing/subscribers/console.ts +101 -0
  83. package/src/tracing/types.ts +115 -0
  84. package/dist/trace/processor.d.ts +0 -1
  85. package/dist/trace/processor.d.ts.map +0 -1
  86. package/dist/trace/processor.js +0 -1
  87. package/dist/trace/traces.d.ts +0 -1
  88. package/dist/trace/traces.d.ts.map +0 -1
  89. package/dist/trace/traces.js +0 -73
  90. package/dist/trace/utils.d.ts +0 -22
  91. package/dist/trace/utils.d.ts.map +0 -1
  92. package/dist/trace/utils.js +0 -30
  93. package/src/trace/processor.ts +0 -0
  94. package/src/trace/traces.ts +0 -86
  95. package/src/trace/utils.ts +0 -38
@@ -0,0 +1,188 @@
1
+ import { describe, it, expect, beforeEach } from "vitest";
2
+ import { SpanImpl, NoopSpan } from "../span.js";
3
+ import { TestSubscriber } from "./helpers.js";
4
+ describe("SpanImpl", () => {
5
+ let subscriber;
6
+ let span;
7
+ beforeEach(() => {
8
+ subscriber = new TestSubscriber();
9
+ span = new SpanImpl("test_span_1", subscriber);
10
+ });
11
+ describe("id", () => {
12
+ it("should return the span id", () => {
13
+ expect(span.id).toBe("test_span_1");
14
+ });
15
+ });
16
+ describe("noop", () => {
17
+ it("should return false", () => {
18
+ expect(span.noop()).toBe(false);
19
+ });
20
+ });
21
+ describe("enter", () => {
22
+ it("should call subscriber.enter", () => {
23
+ span.enter();
24
+ expect(subscriber.entered.has("test_span_1")).toBe(true);
25
+ });
26
+ it("should be idempotent (double enter does nothing)", () => {
27
+ span.enter();
28
+ span.enter();
29
+ const enterCalls = subscriber.calls.filter((c) => c.method === "enter" && c.spanId === "test_span_1");
30
+ expect(enterCalls).toHaveLength(1);
31
+ });
32
+ it("should not enter after close", () => {
33
+ span.close();
34
+ span.enter();
35
+ expect(subscriber.entered.has("test_span_1")).toBe(false);
36
+ });
37
+ });
38
+ describe("exit", () => {
39
+ it("should call subscriber.exit after enter", () => {
40
+ span.enter();
41
+ span.exit();
42
+ expect(subscriber.exited.has("test_span_1")).toBe(true);
43
+ });
44
+ it("should not exit if not entered", () => {
45
+ span.exit();
46
+ expect(subscriber.exited.has("test_span_1")).toBe(false);
47
+ });
48
+ it("should be idempotent (double exit does nothing)", () => {
49
+ span.enter();
50
+ span.exit();
51
+ span.exit();
52
+ const exitCalls = subscriber.calls.filter((c) => c.method === "exit" && c.spanId === "test_span_1");
53
+ expect(exitCalls).toHaveLength(1);
54
+ });
55
+ it("should not exit after close", () => {
56
+ span.enter();
57
+ span.close();
58
+ subscriber.exited.clear(); // clear the exit from close
59
+ subscriber.calls = [];
60
+ span.exit();
61
+ expect(subscriber.exited.has("test_span_1")).toBe(false);
62
+ });
63
+ });
64
+ describe("record", () => {
65
+ it("should call subscriber.record", () => {
66
+ span.record({ state: "running" });
67
+ const recorded = subscriber.getRecorded("test_span_1");
68
+ expect(recorded).toHaveLength(1);
69
+ expect(recorded[0]).toEqual({ state: "running" });
70
+ });
71
+ it("should allow multiple records", () => {
72
+ span.record({ state: "running" });
73
+ span.record({ result: "done" });
74
+ const recorded = subscriber.getRecorded("test_span_1");
75
+ expect(recorded).toHaveLength(2);
76
+ });
77
+ it("should not record after close", () => {
78
+ span.close();
79
+ span.record({ state: "running" });
80
+ const recorded = subscriber.getRecorded("test_span_1");
81
+ expect(recorded).toHaveLength(0);
82
+ });
83
+ });
84
+ describe("error", () => {
85
+ it("should call subscriber.error", () => {
86
+ const err = new Error("test error");
87
+ span.error(err);
88
+ const errors = subscriber.errors.get("test_span_1");
89
+ expect(errors).toHaveLength(1);
90
+ expect(errors[0]).toBe(err);
91
+ });
92
+ it("should allow multiple errors", () => {
93
+ span.error(new Error("error 1"));
94
+ span.error(new Error("error 2"));
95
+ const errors = subscriber.errors.get("test_span_1");
96
+ expect(errors).toHaveLength(2);
97
+ });
98
+ it("should not error after close", () => {
99
+ span.close();
100
+ span.error(new Error("test"));
101
+ const errors = subscriber.errors.get("test_span_1");
102
+ expect(errors).toBeUndefined();
103
+ });
104
+ });
105
+ describe("close", () => {
106
+ it("should call subscriber.close", () => {
107
+ span.close();
108
+ expect(subscriber.closed.has("test_span_1")).toBe(true);
109
+ });
110
+ it("should be idempotent (double close does nothing)", () => {
111
+ span.close();
112
+ span.close();
113
+ const closeCalls = subscriber.calls.filter((c) => c.method === "close" && c.spanId === "test_span_1");
114
+ expect(closeCalls).toHaveLength(1);
115
+ });
116
+ it("should auto-exit if entered but not exited", () => {
117
+ span.enter();
118
+ span.close();
119
+ expect(subscriber.exited.has("test_span_1")).toBe(true);
120
+ expect(subscriber.closed.has("test_span_1")).toBe(true);
121
+ // Verify order: exit before close
122
+ const exitIndex = subscriber.calls.findIndex((c) => c.method === "exit" && c.spanId === "test_span_1");
123
+ const closeIndex = subscriber.calls.findIndex((c) => c.method === "close" && c.spanId === "test_span_1");
124
+ expect(exitIndex).toBeLessThan(closeIndex);
125
+ });
126
+ it("should not exit if already exited", () => {
127
+ span.enter();
128
+ span.exit();
129
+ subscriber.calls = []; // clear calls
130
+ span.close();
131
+ const exitCalls = subscriber.calls.filter((c) => c.method === "exit" && c.spanId === "test_span_1");
132
+ expect(exitCalls).toHaveLength(0);
133
+ });
134
+ it("should not exit if never entered", () => {
135
+ span.close();
136
+ expect(subscriber.exited.has("test_span_1")).toBe(false);
137
+ expect(subscriber.closed.has("test_span_1")).toBe(true);
138
+ });
139
+ });
140
+ describe("full lifecycle", () => {
141
+ it("should handle enter -> record -> exit -> close", () => {
142
+ span.enter();
143
+ span.record({ state: "running" });
144
+ span.exit();
145
+ span.close();
146
+ expect(subscriber.isComplete("test_span_1")).toBe(true);
147
+ const methods = subscriber.calls
148
+ .filter((c) => c.spanId === "test_span_1")
149
+ .map((c) => c.method);
150
+ expect(methods).toEqual(["enter", "record", "exit", "close"]);
151
+ });
152
+ it("should handle enter -> error -> close (auto-exit)", () => {
153
+ span.enter();
154
+ span.error(new Error("oops"));
155
+ span.close();
156
+ expect(subscriber.isComplete("test_span_1")).toBe(true);
157
+ const methods = subscriber.calls
158
+ .filter((c) => c.spanId === "test_span_1")
159
+ .map((c) => c.method);
160
+ expect(methods).toEqual(["enter", "error", "exit", "close"]);
161
+ });
162
+ });
163
+ });
164
+ describe("NoopSpan", () => {
165
+ let span;
166
+ beforeEach(() => {
167
+ span = new NoopSpan();
168
+ });
169
+ describe("id", () => {
170
+ it("should return null", () => {
171
+ expect(span.id).toBeNull();
172
+ });
173
+ });
174
+ describe("noop", () => {
175
+ it("should return true", () => {
176
+ expect(span.noop()).toBe(true);
177
+ });
178
+ });
179
+ describe("methods", () => {
180
+ it("should not throw on any method", () => {
181
+ expect(() => span.enter()).not.toThrow();
182
+ expect(() => span.exit()).not.toThrow();
183
+ expect(() => span.record({ state: "running" })).not.toThrow();
184
+ expect(() => span.error(new Error("test"))).not.toThrow();
185
+ expect(() => span.close()).not.toThrow();
186
+ });
187
+ });
188
+ });
@@ -0,0 +1,43 @@
1
+ import type { SpanId, SpanData, EventData } from "./types.js";
2
+ import type { Subscriber } from "./subscriber.js";
3
+ import { Span } from "./span.js";
4
+ /**
5
+ * Get the current span ID from async context.
6
+ */
7
+ export declare function current(): SpanId | null;
8
+ /**
9
+ * Run a function within a span context.
10
+ * All spans created within `fn` will have `spanId` as their parent (unless overridden).
11
+ */
12
+ export declare function run<T>(spanId: SpanId | null, fn: () => T): T;
13
+ /**
14
+ * Set the global subscriber. Can only be set once.
15
+ */
16
+ export declare function setSubscriber(subscriber: Subscriber): void;
17
+ /**
18
+ * Clear the global subscriber.
19
+ */
20
+ export declare function clearSubscriber(): void;
21
+ /**
22
+ * Get the current global subscriber.
23
+ */
24
+ export declare function getSubscriber(): Subscriber | null;
25
+ type ParentOption = SpanId | "current" | null;
26
+ /**
27
+ * Create a new span.
28
+ *
29
+ * @param data - The span data (must include `kind`)
30
+ * @param parent - Parent span: SpanId, "current" (from async context), or null (no parent)
31
+ * @returns A Span object for managing the span lifecycle
32
+ */
33
+ export declare function span<T extends SpanData>(data: T, parent?: ParentOption): Span<T>;
34
+ type EventParentOption = SpanId | "current" | null;
35
+ /**
36
+ * Emit an event (moment in time, no duration).
37
+ *
38
+ * @param data - The event data (must include `kind`)
39
+ * @param parent - Parent span: SpanId, "current" (from async context), or null (no parent)
40
+ */
41
+ export declare function event(data: EventData, parent?: EventParentOption): void;
42
+ export {};
43
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/tracing/dispatch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAsB,MAAM,QAAQ,CAAC;AAYlD;;GAEG;AACH,wBAAgB,OAAO,IAAI,MAAM,GAAG,IAAI,CAEvC;AAED;;;GAGG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5D;AAQD;;GAEG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAK1D;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AAMD,KAAK,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;AAE9C;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,QAAQ,EACrC,IAAI,EAAE,CAAC,EACP,MAAM,GAAE,YAAwB,GAC/B,IAAI,CAAC,CAAC,CAAC,CAYT;AAMD,KAAK,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,KAAK,CACnB,IAAI,EAAE,SAAS,EACf,MAAM,GAAE,iBAA6B,GACpC,IAAI,CAON"}
@@ -0,0 +1,70 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { SpanImpl, NoopSpan } from "./span.js";
3
+ const gctx = new AsyncLocalStorage();
4
+ /**
5
+ * Get the current span ID from async context.
6
+ */
7
+ export function current() {
8
+ return gctx.getStore()?.spanId ?? null;
9
+ }
10
+ /**
11
+ * Run a function within a span context.
12
+ * All spans created within `fn` will have `spanId` as their parent (unless overridden).
13
+ */
14
+ export function run(spanId, fn) {
15
+ return gctx.run({ spanId }, fn);
16
+ }
17
+ // -----------------------------------------------------------------------------
18
+ // Global Subscriber
19
+ // -----------------------------------------------------------------------------
20
+ let globalSubscriber = null;
21
+ /**
22
+ * Set the global subscriber. Can only be set once.
23
+ */
24
+ export function setSubscriber(subscriber) {
25
+ if (globalSubscriber !== null) {
26
+ throw new Error("Global subscriber already set");
27
+ }
28
+ globalSubscriber = subscriber;
29
+ }
30
+ /**
31
+ * Clear the global subscriber.
32
+ */
33
+ export function clearSubscriber() {
34
+ globalSubscriber = null;
35
+ }
36
+ /**
37
+ * Get the current global subscriber.
38
+ */
39
+ export function getSubscriber() {
40
+ return globalSubscriber;
41
+ }
42
+ /**
43
+ * Create a new span.
44
+ *
45
+ * @param data - The span data (must include `kind`)
46
+ * @param parent - Parent span: SpanId, "current" (from async context), or null (no parent)
47
+ * @returns A Span object for managing the span lifecycle
48
+ */
49
+ export function span(data, parent = "current") {
50
+ const subscriber = globalSubscriber;
51
+ if (!subscriber || !subscriber.enabled(data)) {
52
+ return new NoopSpan();
53
+ }
54
+ const resolvedParent = parent === "current" ? current() : parent;
55
+ const id = subscriber.span(data, resolvedParent);
56
+ return new SpanImpl(id, subscriber);
57
+ }
58
+ /**
59
+ * Emit an event (moment in time, no duration).
60
+ *
61
+ * @param data - The event data (must include `kind`)
62
+ * @param parent - Parent span: SpanId, "current" (from async context), or null (no parent)
63
+ */
64
+ export function event(data, parent = "current") {
65
+ const subscriber = globalSubscriber;
66
+ if (!subscriber)
67
+ return;
68
+ const resolvedParent = parent === "current" ? current() : parent;
69
+ subscriber.event(data, resolvedParent);
70
+ }
@@ -0,0 +1,8 @@
1
+ export type { SpanId, SpanData, SpanKind, ThreadSpan, ModelCallSpan, ToolCallSpan, EventData, EventKind, ThreadErrorEvent, ThreadAbortedEvent, ThreadGuardrailTriggeredEvent, ToolApprovalRequestedEvent, ToolApprovalGrantedEvent, ToolApprovalDeniedEvent, } from "./types.js";
2
+ export type { Subscriber } from "./subscriber.js";
3
+ export type { Span } from "./span.js";
4
+ export { SpanImpl, NoopSpan } from "./span.js";
5
+ export { span, event, run, current, setSubscriber, clearSubscriber, getSubscriber, } from "./dispatch.js";
6
+ export { CompositeSubscriber } from "./subscribers/composite.js";
7
+ export { ConsoleSubscriber } from "./subscribers/console.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tracing/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,aAAa,EACb,YAAY,EACZ,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,6BAA6B,EAC7B,0BAA0B,EAC1B,wBAAwB,EACxB,uBAAuB,GACxB,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,YAAY,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAG5C,OAAO,EACL,IAAI,EACJ,KAAK,EACL,GAAG,EACH,OAAO,EACP,aAAa,EACb,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { SpanImpl, NoopSpan } from "./span.js";
2
+ // Dispatch
3
+ export { span, event, run, current, setSubscriber, clearSubscriber, getSubscriber, } from "./dispatch.js";
4
+ // Subscribers
5
+ export { CompositeSubscriber } from "./subscribers/composite.js";
6
+ export { ConsoleSubscriber } from "./subscribers/console.js";
@@ -0,0 +1,69 @@
1
+ import type { SpanId, SpanData } from "./types.js";
2
+ import type { Subscriber } from "./subscriber.js";
3
+ /**
4
+ * A Span represents a unit of work with duration.
5
+ *
6
+ * Spans track enter/exit for timing, can record additional fields,
7
+ * and must be closed when complete.
8
+ */
9
+ export interface Span<T extends SpanData = SpanData> {
10
+ /**
11
+ * The unique identifier for this span, or null if this is a noop span.
12
+ */
13
+ readonly id: SpanId | null;
14
+ /**
15
+ * Mark the span as entered (execution begins).
16
+ */
17
+ enter(): void;
18
+ /**
19
+ * Mark the span as exited (execution paused/left).
20
+ */
21
+ exit(): void;
22
+ /**
23
+ * Record additional fields on the span.
24
+ */
25
+ record(fields: Partial<T>): void;
26
+ /**
27
+ * Record an error on the span.
28
+ */
29
+ error(err: Error): void;
30
+ /**
31
+ * Close the span. Also calls exit() if not already exited.
32
+ */
33
+ close(): void;
34
+ /**
35
+ * Returns true if this is a noop span (tracing disabled).
36
+ */
37
+ noop(): boolean;
38
+ }
39
+ /**
40
+ * A real span that dispatches to a subscriber.
41
+ */
42
+ export declare class SpanImpl<T extends SpanData = SpanData> implements Span<T> {
43
+ readonly id: SpanId;
44
+ private readonly subscriber;
45
+ private entered;
46
+ private exited;
47
+ private closed;
48
+ constructor(id: SpanId, subscriber: Subscriber);
49
+ enter(): void;
50
+ exit(): void;
51
+ record(fields: Partial<T>): void;
52
+ error(err: Error): void;
53
+ close(): void;
54
+ noop(): boolean;
55
+ }
56
+ /**
57
+ * A noop span that does nothing. Used when tracing is disabled
58
+ * or the subscriber is not interested in this span.
59
+ */
60
+ export declare class NoopSpan<T extends SpanData = SpanData> implements Span<T> {
61
+ readonly id: SpanId | null;
62
+ enter(): void;
63
+ exit(): void;
64
+ record(_fields: Partial<T>): void;
65
+ error(_err: Error): void;
66
+ close(): void;
67
+ noop(): boolean;
68
+ }
69
+ //# sourceMappingURL=span.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span.d.ts","sourceRoot":"","sources":["../../src/tracing/span.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IACjD;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAE3B;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,IAAI,IAAI,IAAI,CAAC;IAEb;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAEjC;;OAEG;IACH,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAExB;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,CAAE,YAAW,IAAI,CAAC,CAAC,CAAC;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;gBAEX,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;IAK9C,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;IAMZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAKhC,KAAK,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI;IAKvB,KAAK,IAAI,IAAI;IASb,IAAI,IAAI,OAAO;CAGhB;AAED;;;GAGG;AACH,qBAAa,QAAQ,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,CAAE,YAAW,IAAI,CAAC,CAAC,CAAC;IACrE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAQ;IAElC,KAAK,IAAI,IAAI;IACb,IAAI,IAAI,IAAI;IACZ,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IACjC,KAAK,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI;IACxB,KAAK,IAAI,IAAI;IAEb,IAAI,IAAI,OAAO;CAGhB"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * A real span that dispatches to a subscriber.
3
+ */
4
+ export class SpanImpl {
5
+ id;
6
+ subscriber;
7
+ entered = false;
8
+ exited = false;
9
+ closed = false;
10
+ constructor(id, subscriber) {
11
+ this.id = id;
12
+ this.subscriber = subscriber;
13
+ }
14
+ enter() {
15
+ if (this.closed || this.entered)
16
+ return;
17
+ this.entered = true;
18
+ this.exited = false;
19
+ this.subscriber.enter(this.id);
20
+ }
21
+ exit() {
22
+ if (this.closed || !this.entered || this.exited)
23
+ return;
24
+ this.exited = true;
25
+ this.subscriber.exit(this.id);
26
+ }
27
+ record(fields) {
28
+ if (this.closed)
29
+ return;
30
+ this.subscriber.record(this.id, fields);
31
+ }
32
+ error(err) {
33
+ if (this.closed)
34
+ return;
35
+ this.subscriber.error(this.id, err);
36
+ }
37
+ close() {
38
+ if (this.closed)
39
+ return;
40
+ this.closed = true;
41
+ if (this.entered && !this.exited) {
42
+ this.subscriber.exit(this.id);
43
+ }
44
+ this.subscriber.close(this.id);
45
+ }
46
+ noop() {
47
+ return false;
48
+ }
49
+ }
50
+ /**
51
+ * A noop span that does nothing. Used when tracing is disabled
52
+ * or the subscriber is not interested in this span.
53
+ */
54
+ export class NoopSpan {
55
+ id = null;
56
+ enter() { }
57
+ exit() { }
58
+ record(_fields) { }
59
+ error(_err) { }
60
+ close() { }
61
+ noop() {
62
+ return true;
63
+ }
64
+ }
@@ -0,0 +1,53 @@
1
+ import type { SpanId, SpanData, EventData } from "./types.js";
2
+ /**
3
+ * A Subscriber receives span and event notifications from the tracing system.
4
+ *
5
+ * Implementations can export data to backends like Laminar, log to console,
6
+ * collect metrics, etc.
7
+ */
8
+ export interface Subscriber {
9
+ /**
10
+ * Returns whether this subscriber is interested in spans of this kind.
11
+ * If false, a noop span is returned and no further methods are called.
12
+ */
13
+ enabled(data: SpanData): boolean;
14
+ /**
15
+ * Called when a new span is created. Returns a unique SpanId for this span.
16
+ */
17
+ span(data: SpanData, parent: SpanId | null): SpanId;
18
+ /**
19
+ * Called when a span is entered (execution begins within the span).
20
+ */
21
+ enter(span: SpanId): void;
22
+ /**
23
+ * Called when a span is exited (execution leaves the span, but span may not be done).
24
+ */
25
+ exit(span: SpanId): void;
26
+ /**
27
+ * Called to record additional data on an existing span.
28
+ */
29
+ record(span: SpanId, delta: Partial<SpanData>): void;
30
+ /**
31
+ * Called when an error occurs within a span.
32
+ */
33
+ error(span: SpanId, error: Error): void;
34
+ /**
35
+ * Called when a span is closed (span is complete and will not receive more data).
36
+ * Implicitly calls exit() if the span was entered but not yet exited.
37
+ */
38
+ close(span: SpanId): void;
39
+ /**
40
+ * Called when an event (moment in time, no duration) occurs.
41
+ */
42
+ event(data: EventData, parent: SpanId | null): void;
43
+ /**
44
+ * Flush any buffered data to the backend.
45
+ */
46
+ flush(): Promise<void>;
47
+ /**
48
+ * Gracefully shutdown the subscriber, flushing remaining data.
49
+ * @param timeout Optional timeout in milliseconds
50
+ */
51
+ shutdown(timeout?: number): Promise<void>;
52
+ }
53
+ //# sourceMappingURL=subscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriber.d.ts","sourceRoot":"","sources":["../../src/tracing/subscriber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IAEjC;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;IAEpD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAErD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAExC;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAEpD;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import type { SpanId, SpanData, EventData } from "../types.js";
2
+ import type { Subscriber } from "../subscriber.js";
3
+ /**
4
+ * A subscriber that fans out to multiple subscribers.
5
+ *
6
+ * - `enabled()` returns true if ANY subscriber is interested
7
+ * - `span()` calls all subscribers and returns a composite SpanId
8
+ * - All other methods dispatch to all subscribers
9
+ */
10
+ export declare class CompositeSubscriber implements Subscriber {
11
+ private readonly subscribers;
12
+ private readonly spanMap;
13
+ private nextId;
14
+ constructor(subscribers: Subscriber[]);
15
+ enabled(data: SpanData): boolean;
16
+ span(data: SpanData, parent: SpanId | null): SpanId;
17
+ enter(span: SpanId): void;
18
+ exit(span: SpanId): void;
19
+ record(span: SpanId, delta: Partial<SpanData>): void;
20
+ error(span: SpanId, error: Error): void;
21
+ close(span: SpanId): void;
22
+ event(data: EventData, parent: SpanId | null): void;
23
+ flush(): Promise<void>;
24
+ shutdown(timeout?: number): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=composite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../../src/tracing/subscribers/composite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;;;GAMG;AACH,qBAAa,mBAAoB,YAAW,UAAU;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,MAAM,CAAK;gBAEP,WAAW,EAAE,UAAU,EAAE;IAIrC,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO;IAIhC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAsBnD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQxB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI;IAQpD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAQvC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IASzB,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAQ7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhD"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * A subscriber that fans out to multiple subscribers.
3
+ *
4
+ * - `enabled()` returns true if ANY subscriber is interested
5
+ * - `span()` calls all subscribers and returns a composite SpanId
6
+ * - All other methods dispatch to all subscribers
7
+ */
8
+ export class CompositeSubscriber {
9
+ subscribers;
10
+ spanMap = new Map();
11
+ nextId = 0;
12
+ constructor(subscribers) {
13
+ this.subscribers = subscribers;
14
+ }
15
+ enabled(data) {
16
+ return this.subscribers.some((s) => s.enabled(data));
17
+ }
18
+ span(data, parent) {
19
+ const compositeId = `composite_${this.nextId++}`;
20
+ const childIds = [];
21
+ // Resolve parent for each subscriber
22
+ const parentIds = parent ? this.spanMap.get(parent) : null;
23
+ for (let i = 0; i < this.subscribers.length; i++) {
24
+ const sub = this.subscribers[i];
25
+ if (sub.enabled(data)) {
26
+ const parentId = parentIds?.[i] ?? null;
27
+ const childId = sub.span(data, parentId);
28
+ childIds.push(childId);
29
+ }
30
+ else {
31
+ childIds.push(""); // placeholder for disabled subscriber
32
+ }
33
+ }
34
+ this.spanMap.set(compositeId, childIds);
35
+ return compositeId;
36
+ }
37
+ enter(span) {
38
+ const ids = this.spanMap.get(span);
39
+ if (!ids)
40
+ return;
41
+ for (let i = 0; i < this.subscribers.length; i++) {
42
+ if (ids[i])
43
+ this.subscribers[i].enter(ids[i]);
44
+ }
45
+ }
46
+ exit(span) {
47
+ const ids = this.spanMap.get(span);
48
+ if (!ids)
49
+ return;
50
+ for (let i = 0; i < this.subscribers.length; i++) {
51
+ if (ids[i])
52
+ this.subscribers[i].exit(ids[i]);
53
+ }
54
+ }
55
+ record(span, delta) {
56
+ const ids = this.spanMap.get(span);
57
+ if (!ids)
58
+ return;
59
+ for (let i = 0; i < this.subscribers.length; i++) {
60
+ if (ids[i])
61
+ this.subscribers[i].record(ids[i], delta);
62
+ }
63
+ }
64
+ error(span, error) {
65
+ const ids = this.spanMap.get(span);
66
+ if (!ids)
67
+ return;
68
+ for (let i = 0; i < this.subscribers.length; i++) {
69
+ if (ids[i])
70
+ this.subscribers[i].error(ids[i], error);
71
+ }
72
+ }
73
+ close(span) {
74
+ const ids = this.spanMap.get(span);
75
+ if (!ids)
76
+ return;
77
+ for (let i = 0; i < this.subscribers.length; i++) {
78
+ if (ids[i])
79
+ this.subscribers[i].close(ids[i]);
80
+ }
81
+ this.spanMap.delete(span);
82
+ }
83
+ event(data, parent) {
84
+ const parentIds = parent ? this.spanMap.get(parent) : null;
85
+ for (let i = 0; i < this.subscribers.length; i++) {
86
+ const parentId = parentIds?.[i] ?? null;
87
+ this.subscribers[i].event(data, parentId);
88
+ }
89
+ }
90
+ async flush() {
91
+ await Promise.all(this.subscribers.map((s) => s.flush()));
92
+ }
93
+ async shutdown(timeout) {
94
+ await Promise.all(this.subscribers.map((s) => s.shutdown(timeout)));
95
+ }
96
+ }
@@ -0,0 +1,22 @@
1
+ import type { SpanId, SpanData, EventData } from "../types.js";
2
+ import type { Subscriber } from "../subscriber.js";
3
+ /**
4
+ * A simple console subscriber for development and debugging.
5
+ * Logs span lifecycle events and events to the console.
6
+ */
7
+ export declare class ConsoleSubscriber implements Subscriber {
8
+ private spans;
9
+ private nextId;
10
+ enabled(_data: SpanData): boolean;
11
+ span(data: SpanData, parent: SpanId | null): SpanId;
12
+ enter(spanId: SpanId): void;
13
+ exit(spanId: SpanId): void;
14
+ record(spanId: SpanId, delta: Partial<SpanData>): void;
15
+ error(spanId: SpanId, error: Error): void;
16
+ close(spanId: SpanId): void;
17
+ event(data: EventData, parent: SpanId | null): void;
18
+ flush(): Promise<void>;
19
+ shutdown(_timeout?: number): Promise<void>;
20
+ private fmt;
21
+ }
22
+ //# sourceMappingURL=console.d.ts.map