@ereo/trace 0.1.28

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 (53) hide show
  1. package/README.md +447 -0
  2. package/dist/cli-reporter.d.ts +37 -0
  3. package/dist/cli-reporter.d.ts.map +1 -0
  4. package/dist/client.d.ts +59 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +193 -0
  7. package/dist/collector.d.ts +29 -0
  8. package/dist/collector.d.ts.map +1 -0
  9. package/dist/context.d.ts +17 -0
  10. package/dist/context.d.ts.map +1 -0
  11. package/dist/index.d.ts +24 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +1367 -0
  14. package/dist/instrumentors/auth.d.ts +15 -0
  15. package/dist/instrumentors/auth.d.ts.map +1 -0
  16. package/dist/instrumentors/build.d.ts +18 -0
  17. package/dist/instrumentors/build.d.ts.map +1 -0
  18. package/dist/instrumentors/data.d.ts +31 -0
  19. package/dist/instrumentors/data.d.ts.map +1 -0
  20. package/dist/instrumentors/database.d.ts +26 -0
  21. package/dist/instrumentors/database.d.ts.map +1 -0
  22. package/dist/instrumentors/errors.d.ts +17 -0
  23. package/dist/instrumentors/errors.d.ts.map +1 -0
  24. package/dist/instrumentors/forms.d.ts +22 -0
  25. package/dist/instrumentors/forms.d.ts.map +1 -0
  26. package/dist/instrumentors/index.d.ts +15 -0
  27. package/dist/instrumentors/index.d.ts.map +1 -0
  28. package/dist/instrumentors/islands.d.ts +19 -0
  29. package/dist/instrumentors/islands.d.ts.map +1 -0
  30. package/dist/instrumentors/request.d.ts +24 -0
  31. package/dist/instrumentors/request.d.ts.map +1 -0
  32. package/dist/instrumentors/routing.d.ts +24 -0
  33. package/dist/instrumentors/routing.d.ts.map +1 -0
  34. package/dist/instrumentors/rpc.d.ts +16 -0
  35. package/dist/instrumentors/rpc.d.ts.map +1 -0
  36. package/dist/instrumentors/signals.d.ts +21 -0
  37. package/dist/instrumentors/signals.d.ts.map +1 -0
  38. package/dist/noop.d.ts +24 -0
  39. package/dist/noop.d.ts.map +1 -0
  40. package/dist/noop.js +45 -0
  41. package/dist/ring-buffer.d.ts +27 -0
  42. package/dist/ring-buffer.d.ts.map +1 -0
  43. package/dist/span.d.ts +45 -0
  44. package/dist/span.d.ts.map +1 -0
  45. package/dist/tracer.d.ts +42 -0
  46. package/dist/tracer.d.ts.map +1 -0
  47. package/dist/transport.d.ts +69 -0
  48. package/dist/transport.d.ts.map +1 -0
  49. package/dist/types.d.ts +120 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/viewer.d.ts +20 -0
  52. package/dist/viewer.d.ts.map +1 -0
  53. package/package.json +50 -0
package/dist/client.js ADDED
@@ -0,0 +1,193 @@
1
+ // @bun
2
+ // src/client.ts
3
+ function randomHex(len) {
4
+ let s = "";
5
+ while (s.length < len) {
6
+ s += Math.random().toString(16).slice(2);
7
+ }
8
+ return s.slice(0, len);
9
+ }
10
+ function browserSpanId() {
11
+ return randomHex(16);
12
+ }
13
+
14
+ class ClientSpan {
15
+ id;
16
+ traceId;
17
+ parentId;
18
+ name;
19
+ layer;
20
+ startTime;
21
+ endTime = 0;
22
+ attributes = {};
23
+ status = "ok";
24
+ events = [];
25
+ children = [];
26
+ ended = false;
27
+ constructor(traceId, parentId, name, layer) {
28
+ this.id = browserSpanId();
29
+ this.traceId = traceId;
30
+ this.parentId = parentId;
31
+ this.name = name;
32
+ this.layer = layer;
33
+ this.startTime = performance.now();
34
+ }
35
+ setAttribute(key, value) {
36
+ if (!this.ended)
37
+ this.attributes[key] = value;
38
+ }
39
+ event(name, attrs) {
40
+ if (!this.ended)
41
+ this.events.push({ name, time: performance.now(), attributes: attrs });
42
+ }
43
+ end() {
44
+ if (this.ended)
45
+ return;
46
+ this.ended = true;
47
+ this.endTime = performance.now();
48
+ }
49
+ error(err) {
50
+ if (this.ended)
51
+ return;
52
+ this.status = "error";
53
+ this.attributes["error.message"] = err instanceof Error ? err.message : String(err);
54
+ }
55
+ addChild(childId) {
56
+ this.children.push(childId);
57
+ }
58
+ toData() {
59
+ return {
60
+ id: this.id,
61
+ traceId: this.traceId,
62
+ parentId: this.parentId,
63
+ name: this.name,
64
+ layer: this.layer,
65
+ status: this.status,
66
+ startTime: this.startTime,
67
+ endTime: this.endTime || performance.now(),
68
+ duration: (this.endTime || performance.now()) - this.startTime,
69
+ attributes: { ...this.attributes },
70
+ events: [...this.events],
71
+ children: [...this.children]
72
+ };
73
+ }
74
+ }
75
+
76
+ class ClientTracer {
77
+ ws = null;
78
+ pendingSpans = [];
79
+ currentTraceId = null;
80
+ connected = false;
81
+ init() {
82
+ if (typeof window === "undefined")
83
+ return;
84
+ this.currentTraceId = window.__EREO_TRACE_ID__ || null;
85
+ this.connect();
86
+ this.interceptFetch();
87
+ }
88
+ connect() {
89
+ if (typeof WebSocket === "undefined")
90
+ return;
91
+ const protocol = location.protocol === "https:" ? "wss:" : "ws:";
92
+ const url = `${protocol}//${location.host}/__ereo/trace-ws`;
93
+ try {
94
+ this.ws = new WebSocket(url);
95
+ this.ws.onopen = () => {
96
+ this.connected = true;
97
+ this.flushPending();
98
+ };
99
+ this.ws.onclose = () => {
100
+ this.connected = false;
101
+ this.ws = null;
102
+ };
103
+ this.ws.onerror = () => {};
104
+ } catch {}
105
+ }
106
+ interceptFetch() {
107
+ if (typeof window === "undefined")
108
+ return;
109
+ const originalFetch = window.fetch.bind(window);
110
+ const self = this;
111
+ const patchedFetch = function(input, init) {
112
+ if (self.currentTraceId) {
113
+ const headers = new Headers(init?.headers);
114
+ if (!headers.has("X-Ereo-Trace-Id")) {
115
+ headers.set("X-Ereo-Trace-Id", self.currentTraceId);
116
+ }
117
+ init = { ...init, headers };
118
+ }
119
+ return originalFetch(input, init).then((response) => {
120
+ const newTraceId = response.headers.get("X-Ereo-Trace-Id");
121
+ if (newTraceId) {
122
+ self.currentTraceId = newTraceId;
123
+ }
124
+ return response;
125
+ });
126
+ };
127
+ Object.assign(patchedFetch, originalFetch);
128
+ window.fetch = patchedFetch;
129
+ }
130
+ startSpan(name, layer, parentId) {
131
+ const traceId = this.currentTraceId || browserSpanId() + browserSpanId();
132
+ return new ClientSpan(traceId, parentId || null, name, layer);
133
+ }
134
+ submitSpan(span) {
135
+ const data = span.toData();
136
+ this.pendingSpans.push(data);
137
+ if (this.connected) {
138
+ this.flushPending();
139
+ }
140
+ }
141
+ setTraceId(traceId) {
142
+ this.currentTraceId = traceId;
143
+ }
144
+ getTraceId() {
145
+ return this.currentTraceId;
146
+ }
147
+ flushPending() {
148
+ if (!this.ws || !this.connected || this.pendingSpans.length === 0)
149
+ return;
150
+ const traceGroups = new Map;
151
+ for (const span of this.pendingSpans) {
152
+ const group = traceGroups.get(span.traceId) || [];
153
+ group.push(span);
154
+ traceGroups.set(span.traceId, group);
155
+ }
156
+ for (const [traceId, spans] of traceGroups) {
157
+ try {
158
+ this.ws.send(JSON.stringify({
159
+ type: "client:spans",
160
+ traceId,
161
+ spans
162
+ }));
163
+ } catch {
164
+ return;
165
+ }
166
+ }
167
+ this.pendingSpans = [];
168
+ }
169
+ destroy() {
170
+ if (this.ws) {
171
+ this.ws.close();
172
+ this.ws = null;
173
+ }
174
+ this.connected = false;
175
+ this.pendingSpans = [];
176
+ }
177
+ }
178
+ var clientTracer = null;
179
+ function getClientTracer() {
180
+ if (!clientTracer) {
181
+ clientTracer = new ClientTracer;
182
+ }
183
+ return clientTracer;
184
+ }
185
+ function initClientTracing() {
186
+ getClientTracer().init();
187
+ }
188
+ export {
189
+ initClientTracing,
190
+ getClientTracer,
191
+ ClientTracer,
192
+ ClientSpan
193
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @ereo/trace - Collector
3
+ *
4
+ * Merges server + client spans into unified traces.
5
+ * Client spans arrive via WebSocket with their traceId,
6
+ * and are stitched into the corresponding server trace.
7
+ */
8
+ import type { SpanData, TraceData, TraceId, Tracer } from './types';
9
+ /**
10
+ * Collector that merges client-side spans into server traces.
11
+ */
12
+ export declare class TraceCollector {
13
+ private tracer;
14
+ constructor(tracer: Tracer);
15
+ /**
16
+ * Merge client spans into an existing server trace.
17
+ * Called when client spans arrive via WebSocket.
18
+ */
19
+ mergeClientSpans(traceId: TraceId, clientSpans: SpanData[]): void;
20
+ /**
21
+ * Get a unified trace with both server and client spans.
22
+ */
23
+ getUnifiedTrace(traceId: TraceId): TraceData | undefined;
24
+ }
25
+ /**
26
+ * Create a collector instance.
27
+ */
28
+ export declare function createCollector(tracer: Tracer): TraceCollector;
29
+ //# sourceMappingURL=collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEpE;;GAEG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAElC;;;OAGG;IACH,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,IAAI;IAIjE;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS;CAGzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAE9D"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @ereo/trace - Context Integration
3
+ *
4
+ * Attach/retrieve tracer from RequestContext store.
5
+ * Uses the existing ctx.set()/ctx.get() mechanism — no changes to @ereo/core needed.
6
+ */
7
+ import type { AppContext } from '@ereo/core';
8
+ import type { Tracer, Span } from './types';
9
+ /** Attach a tracer to the request context */
10
+ export declare function setTracer(context: AppContext, tracer: Tracer): void;
11
+ /** Retrieve the tracer from the request context */
12
+ export declare function getTracer(context: AppContext): Tracer | undefined;
13
+ /** Store the active root span on the context (for child span creation by instrumentors) */
14
+ export declare function setActiveSpan(context: AppContext, span: Span): void;
15
+ /** Retrieve the active root span from the context */
16
+ export declare function getActiveSpan(context: AppContext): Span | undefined;
17
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAK5C,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAEnE;AAED,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAEjE;AAED,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAEnE;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,CAEnE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @ereo/trace - Full-stack developer observability for EreoJS
3
+ *
4
+ * Provides unified tracing across all 11 framework layers:
5
+ * request, routing, data, forms, signals, rpc, database, auth, islands, build, errors.
6
+ *
7
+ * Usage:
8
+ * import { createTracer, traceMiddleware, createCLIReporter } from '@ereo/trace';
9
+ * const tracer = createTracer();
10
+ * server.use(traceMiddleware(tracer));
11
+ * createCLIReporter(tracer);
12
+ */
13
+ export { createTracer, TracerImpl } from './tracer';
14
+ export { SpanImpl, generateSpanId, generateTraceId } from './span';
15
+ export { RingBuffer } from './ring-buffer';
16
+ export { setTracer, getTracer, setActiveSpan, getActiveSpan } from './context';
17
+ export { noopTracer, noopSpan, NoopTracer } from './noop';
18
+ export { createCLIReporter, type CLIReporterOptions } from './cli-reporter';
19
+ export { createTraceWebSocket, createTracesAPIHandler, serializeTrace, deserializeTrace, type SerializedTraceData, type SerializedTraceStreamEvent, } from './transport';
20
+ export { createCollector, TraceCollector } from './collector';
21
+ export { createViewerHandler, exportTracesHTML, generateViewerHTML } from './viewer';
22
+ export { traceMiddleware, type TraceMiddlewareOptions, traceRouteMatch, recordRouteMatch, traceLoader, recordLoaderMetrics, traceCacheOperation, type LoaderTraceInfo, traceFormSubmit, recordFormValidation, recordSignalUpdate, recordSignalBatch, traceRPCCall, recordRPCValidation, tracedAdapter, traceQuery, type TracedAdapterMethods, traceAuthCheck, traceHydration, recordHydration, traceBuildStage, traceBuild, traceError, withErrorCapture, type ErrorPhase, } from './instrumentors';
23
+ export type { TraceId, SpanId, SpanLayer, SpanStatus, SpanEvent, SpanData, TraceOrigin, TraceMetadata, TraceData, Span, Tracer, TraceStreamEvent, TracerConfig, } from './types';
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG3C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/E,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG1D,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG5E,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAGrF,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,EAC3B,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,eAAe,EACpB,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,KAAK,oBAAoB,EACzB,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EACV,OAAO,EACP,MAAM,EACN,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,WAAW,EACX,aAAa,EACb,SAAS,EACT,IAAI,EACJ,MAAM,EACN,gBAAgB,EAChB,YAAY,GACb,MAAM,SAAS,CAAC"}