autotel-devtools 5.1.0 → 6.0.1

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.
@@ -105,6 +105,11 @@ var DevtoolsSpanExporter = class {
105
105
  timestamp: event.time[0] * 1e3 + event.time[1] / 1e6,
106
106
  attributes: event.attributes ? Object.fromEntries(Object.entries(event.attributes)) : void 0
107
107
  }));
108
+ const links = span.links.map((link) => ({
109
+ traceId: link.context.traceId,
110
+ spanId: link.context.spanId,
111
+ attributes: link.attributes ? Object.fromEntries(Object.entries(link.attributes)) : void 0
112
+ }));
108
113
  return {
109
114
  traceId: spanContext.traceId,
110
115
  spanId: spanContext.spanId,
@@ -119,9 +124,15 @@ var DevtoolsSpanExporter = class {
119
124
  code: status,
120
125
  message: span.status.message
121
126
  },
122
- events: events.length > 0 ? events : void 0
127
+ events: events.length > 0 ? events : void 0,
128
+ links: links.length > 0 ? links : void 0,
129
+ scope: this.convertScope(span)
123
130
  };
124
131
  }
132
+ convertScope(span) {
133
+ const s = span.instrumentationScope ?? span.instrumentationLibrary;
134
+ return s?.name ? { name: s.name, version: s.version || void 0 } : void 0;
135
+ }
125
136
  /**
126
137
  * Convert OpenTelemetry SpanKind to string
127
138
  */
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/exporter.ts"],"names":[],"mappings":"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","file":"exporter.cjs","sourcesContent":["/**\n * OpenTelemetry SpanExporter that streams spans to DevtoolsServer\n */\n\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport type { ExportResult, ExportResultCode } from '@opentelemetry/core';\nimport type { DevtoolsServer } from './server';\nimport type { TraceData, SpanData } from './types';\n\nexport class DevtoolsSpanExporter implements SpanExporter {\n private server: DevtoolsServer;\n private serviceName: string;\n\n constructor(server: DevtoolsServer, serviceName: string = 'unknown-service') {\n this.server = server;\n this.serviceName = serviceName;\n }\n\n /**\n * Export spans to the WebSocket server\n */\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n // Immediately call the callback to unblock the span processor\n // Then process the spans asynchronously\n resultCallback({ code: 0 as ExportResultCode });\n\n // Process spans asynchronously without blocking\n Promise.resolve().then(() => {\n try {\n console.log(`[Autotel Exporter] Exporting ${spans.length} span(s)`);\n\n // Group spans by trace ID\n const traceMap = new Map<string, ReadableSpan[]>();\n\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n if (!traceMap.has(traceId)) {\n traceMap.set(traceId, []);\n }\n traceMap.get(traceId)!.push(span);\n }\n\n // Convert each trace and send to server\n for (const [traceId, traceSpans] of traceMap) {\n const trace = this.convertToTraceData(traceId, traceSpans);\n console.log(\n `[Autotel Exporter] Adding trace ${traceId.slice(0, 16)} with ${traceSpans.length} spans`,\n );\n this.server.addTrace(trace);\n }\n } catch (error) {\n console.error('[Autotel Exporter] Export error:', error);\n }\n });\n }\n\n /**\n * Shutdown the exporter\n */\n async shutdown(): Promise<void> {\n // Nothing to clean up\n }\n\n /**\n * Force flush any buffered spans\n */\n async forceFlush(): Promise<void> {\n // Nothing to flush\n }\n\n /**\n * Convert OpenTelemetry spans to TraceData\n */\n private convertToTraceData(\n traceId: string,\n spans: ReadableSpan[],\n ): TraceData {\n // Convert spans\n const spanData: SpanData[] = spans.map((span) => this.convertSpan(span));\n\n // Find root span (no parent)\n const rootSpan = spanData.find((s) => !s.parentSpanId) || spanData[0];\n\n // Sort spans by start time\n spanData.sort((a, b) => a.startTime - b.startTime);\n\n const startTime = Math.min(...spanData.map((s) => s.startTime));\n const endTime = Math.max(...spanData.map((s) => s.endTime));\n\n // Determine overall status (ERROR if any span errored)\n const hasError = spanData.some((s) => s.status.code === 'ERROR');\n const status = hasError ? 'ERROR' : 'OK';\n\n return {\n traceId,\n correlationId: traceId.slice(0, 16), // First 16 chars\n rootSpan,\n spans: spanData,\n startTime,\n endTime,\n duration: endTime - startTime,\n status: status as 'OK' | 'ERROR' | 'UNSET',\n service: this.serviceName,\n };\n }\n\n /**\n * Convert OpenTelemetry span to SpanData\n */\n private convertSpan(span: ReadableSpan): SpanData {\n const spanContext = span.spanContext();\n const startTime = span.startTime[0] * 1000 + span.startTime[1] / 1_000_000;\n const endTime = span.endTime[0] * 1000 + span.endTime[1] / 1_000_000;\n\n // Convert attributes\n const attributes: Record<string, any> = {};\n for (const [key, value] of Object.entries(span.attributes)) {\n attributes[key] = value;\n }\n\n // Convert status\n const statusCode = span.status.code;\n let status: 'OK' | 'ERROR' | 'UNSET';\n switch (statusCode) {\n case 0: {\n status = 'UNSET';\n break;\n }\n case 1: {\n status = 'OK';\n break;\n }\n case 2: {\n status = 'ERROR';\n break;\n }\n default: {\n status = 'UNSET';\n }\n }\n\n // Convert events\n const events = span.events.map((event) => ({\n name: event.name,\n timestamp: event.time[0] * 1000 + event.time[1] / 1_000_000,\n attributes: event.attributes\n ? Object.fromEntries(Object.entries(event.attributes))\n : undefined,\n }));\n\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n parentSpanId: (span as any).parentSpanId,\n name: span.name,\n kind: this.convertSpanKind(span.kind),\n startTime,\n endTime,\n duration: endTime - startTime,\n attributes,\n status: {\n code: status,\n message: span.status.message,\n },\n events: events.length > 0 ? events : undefined,\n };\n }\n\n /**\n * Convert OpenTelemetry SpanKind to string\n */\n private convertSpanKind(\n kind: number,\n ): 'INTERNAL' | 'SERVER' | 'CLIENT' | 'PRODUCER' | 'CONSUMER' {\n switch (kind) {\n case 0: {\n return 'INTERNAL';\n }\n case 1: {\n return 'SERVER';\n }\n case 2: {\n return 'CLIENT';\n }\n case 3: {\n return 'PRODUCER';\n }\n case 4: {\n return 'CONSUMER';\n }\n default: {\n return 'INTERNAL';\n }\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/exporter.ts"],"names":[],"mappings":";;;AASO,IAAM,uBAAN,MAAmD;AAAA,EAChD,MAAA;AAAA,EACA,WAAA;AAAA,EAER,WAAA,CAAY,MAAA,EAAwB,WAAA,GAAsB,iBAAA,EAAmB;AAC3E,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAA,CACJ,KAAA,EACA,cAAA,EACe;AAGf,IAAA,cAAA,CAAe,EAAE,IAAA,EAAM,CAAA,EAAuB,CAAA;AAG9C,IAAA,OAAA,CAAQ,OAAA,EAAQ,CAAE,IAAA,CAAK,MAAM;AAC3B,MAAA,IAAI;AACF,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,6BAAA,EAAgC,KAAA,CAAM,MAAM,CAAA,QAAA,CAAU,CAAA;AAGlE,QAAA,MAAM,QAAA,uBAAe,GAAA,EAA4B;AAEjD,QAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,UAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,EAAY,CAAE,OAAA;AACnC,UAAA,IAAI,CAAC,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA,EAAG;AAC1B,YAAA,QAAA,CAAS,GAAA,CAAI,OAAA,EAAS,EAAE,CAAA;AAAA,UAC1B;AACA,UAAA,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA,CAAG,IAAA,CAAK,IAAI,CAAA;AAAA,QAClC;AAGA,QAAA,KAAA,MAAW,CAAC,OAAA,EAAS,UAAU,CAAA,IAAK,QAAA,EAAU;AAC5C,UAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,kBAAA,CAAmB,OAAA,EAAS,UAAU,CAAA;AACzD,UAAA,OAAA,CAAQ,GAAA;AAAA,YACN,CAAA,gCAAA,EAAmC,QAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,MAAA,EAAS,WAAW,MAAM,CAAA,MAAA;AAAA,WACnF;AACA,UAAA,IAAA,CAAK,MAAA,CAAO,SAAS,KAAK,CAAA;AAAA,QAC5B;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,KAAA,CAAM,oCAAoC,KAAK,CAAA;AAAA,MACzD;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAA,GAA0B;AAAA,EAEhC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAA,GAA4B;AAAA,EAElC;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAA,CACN,SACA,KAAA,EACW;AAEX,IAAA,MAAM,QAAA,GAAuB,MAAM,GAAA,CAAI,CAAC,SAAS,IAAA,CAAK,WAAA,CAAY,IAAI,CAAC,CAAA;AAGvE,IAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,YAAY,CAAA,IAAK,QAAA,CAAS,CAAC,CAAA;AAGpE,IAAA,QAAA,CAAS,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,SAAA,GAAY,EAAE,SAAS,CAAA;AAEjD,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,CAAI,GAAG,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,CAAC,CAAA;AAC9D,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,CAAI,GAAG,QAAA,CAAS,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,CAAC,CAAA;AAG1D,IAAA,MAAM,QAAA,GAAW,SAAS,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,MAAA,CAAO,SAAS,OAAO,CAAA;AAC/D,IAAA,MAAM,MAAA,GAAS,WAAW,OAAA,GAAU,IAAA;AAEpC,IAAA,OAAO;AAAA,MACL,OAAA;AAAA,MACA,aAAA,EAAe,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAAA;AAAA,MAClC,QAAA;AAAA,MACA,KAAA,EAAO,QAAA;AAAA,MACP,SAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAU,OAAA,GAAU,SAAA;AAAA,MACpB,MAAA;AAAA,MACA,SAAS,IAAA,CAAK;AAAA,KAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,IAAA,EAA8B;AAChD,IAAA,MAAM,WAAA,GAAc,KAAK,WAAA,EAAY;AACrC,IAAA,MAAM,SAAA,GAAY,KAAK,SAAA,CAAU,CAAC,IAAI,GAAA,GAAO,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA,GAAI,GAAA;AACjE,IAAA,MAAM,OAAA,GAAU,KAAK,OAAA,CAAQ,CAAC,IAAI,GAAA,GAAO,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA,GAAI,GAAA;AAG3D,IAAA,MAAM,aAAkC,EAAC;AACzC,IAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,IAAA,CAAK,UAAU,CAAA,EAAG;AAC1D,MAAA,UAAA,CAAW,GAAG,CAAA,GAAI,KAAA;AAAA,IACpB;AAGA,IAAA,MAAM,UAAA,GAAa,KAAK,MAAA,CAAO,IAAA;AAC/B,IAAA,IAAI,MAAA;AACJ,IAAA,QAAQ,UAAA;AAAY,MAClB,KAAK,CAAA,EAAG;AACN,QAAA,MAAA,GAAS,OAAA;AACT,QAAA;AAAA,MACF;AAAA,MACA,KAAK,CAAA,EAAG;AACN,QAAA,MAAA,GAAS,IAAA;AACT,QAAA;AAAA,MACF;AAAA,MACA,KAAK,CAAA,EAAG;AACN,QAAA,MAAA,GAAS,OAAA;AACT,QAAA;AAAA,MACF;AAAA,MACA,SAAS;AACP,QAAA,MAAA,GAAS,OAAA;AAAA,MACX;AAAA;AAIF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MACzC,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,SAAA,EAAW,MAAM,IAAA,CAAK,CAAC,IAAI,GAAA,GAAO,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,GAAI,GAAA;AAAA,MAClD,UAAA,EAAY,KAAA,CAAM,UAAA,GACd,MAAA,CAAO,WAAA,CAAY,OAAO,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAC,CAAA,GACnD;AAAA,KACN,CAAE,CAAA;AAGF,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,MACtC,OAAA,EAAS,KAAK,OAAA,CAAQ,OAAA;AAAA,MACtB,MAAA,EAAQ,KAAK,OAAA,CAAQ,MAAA;AAAA,MACrB,UAAA,EAAY,IAAA,CAAK,UAAA,GACb,MAAA,CAAO,WAAA,CAAY,OAAO,OAAA,CAAQ,IAAA,CAAK,UAAU,CAAC,CAAA,GAClD;AAAA,KACN,CAAE,CAAA;AAEF,IAAA,OAAO;AAAA,MACL,SAAS,WAAA,CAAY,OAAA;AAAA,MACrB,QAAQ,WAAA,CAAY,MAAA;AAAA,MACpB,cAAe,IAAA,CAAa,YAAA;AAAA,MAC5B,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,IAAA,EAAM,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,IAAI,CAAA;AAAA,MACpC,SAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAU,OAAA,GAAU,SAAA;AAAA,MACpB,UAAA;AAAA,MACA,MAAA,EAAQ;AAAA,QACN,IAAA,EAAM,MAAA;AAAA,QACN,OAAA,EAAS,KAAK,MAAA,CAAO;AAAA,OACvB;AAAA,MACA,MAAA,EAAQ,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,MAAA,GAAS,MAAA;AAAA,MACrC,KAAA,EAAO,KAAA,CAAM,MAAA,GAAS,CAAA,GAAI,KAAA,GAAQ,MAAA;AAAA,MAClC,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,IAAI;AAAA,KAC/B;AAAA,EACF;AAAA,EAEQ,aAAa,IAAA,EAAuC;AAC1D,IAAA,MAAM,CAAA,GACH,IAAA,CAAa,oBAAA,IACb,IAAA,CAAa,sBAAA;AAChB,IAAA,OAAO,CAAA,EAAG,IAAA,GAAO,EAAE,IAAA,EAAM,CAAA,CAAE,MAAM,OAAA,EAAS,CAAA,CAAE,OAAA,IAAW,MAAA,EAAU,GAAI,MAAA;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA,EAKQ,gBACN,IAAA,EAC4D;AAC5D,IAAA,QAAQ,IAAA;AAAM,MACZ,KAAK,CAAA,EAAG;AACN,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,MACA,KAAK,CAAA,EAAG;AACN,QAAA,OAAO,QAAA;AAAA,MACT;AAAA,MACA,KAAK,CAAA,EAAG;AACN,QAAA,OAAO,QAAA;AAAA,MACT;AAAA,MACA,KAAK,CAAA,EAAG;AACN,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,MACA,KAAK,CAAA,EAAG;AACN,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,MACA,SAAS;AACP,QAAA,OAAO,UAAA;AAAA,MACT;AAAA;AACF,EACF;AACF","file":"exporter.cjs","sourcesContent":["/**\n * OpenTelemetry SpanExporter that streams spans to DevtoolsServer\n */\n\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport type { ExportResult, ExportResultCode } from '@opentelemetry/core';\nimport type { DevtoolsServer } from './server';\nimport type { TraceData, SpanData } from './types';\n\nexport class DevtoolsSpanExporter implements SpanExporter {\n private server: DevtoolsServer;\n private serviceName: string;\n\n constructor(server: DevtoolsServer, serviceName: string = 'unknown-service') {\n this.server = server;\n this.serviceName = serviceName;\n }\n\n /**\n * Export spans to the WebSocket server\n */\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n // Immediately call the callback to unblock the span processor\n // Then process the spans asynchronously\n resultCallback({ code: 0 as ExportResultCode });\n\n // Process spans asynchronously without blocking\n Promise.resolve().then(() => {\n try {\n console.log(`[Autotel Exporter] Exporting ${spans.length} span(s)`);\n\n // Group spans by trace ID\n const traceMap = new Map<string, ReadableSpan[]>();\n\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n if (!traceMap.has(traceId)) {\n traceMap.set(traceId, []);\n }\n traceMap.get(traceId)!.push(span);\n }\n\n // Convert each trace and send to server\n for (const [traceId, traceSpans] of traceMap) {\n const trace = this.convertToTraceData(traceId, traceSpans);\n console.log(\n `[Autotel Exporter] Adding trace ${traceId.slice(0, 16)} with ${traceSpans.length} spans`,\n );\n this.server.addTrace(trace);\n }\n } catch (error) {\n console.error('[Autotel Exporter] Export error:', error);\n }\n });\n }\n\n /**\n * Shutdown the exporter\n */\n async shutdown(): Promise<void> {\n // Nothing to clean up\n }\n\n /**\n * Force flush any buffered spans\n */\n async forceFlush(): Promise<void> {\n // Nothing to flush\n }\n\n /**\n * Convert OpenTelemetry spans to TraceData\n */\n private convertToTraceData(\n traceId: string,\n spans: ReadableSpan[],\n ): TraceData {\n // Convert spans\n const spanData: SpanData[] = spans.map((span) => this.convertSpan(span));\n\n // Find root span (no parent)\n const rootSpan = spanData.find((s) => !s.parentSpanId) || spanData[0];\n\n // Sort spans by start time\n spanData.sort((a, b) => a.startTime - b.startTime);\n\n const startTime = Math.min(...spanData.map((s) => s.startTime));\n const endTime = Math.max(...spanData.map((s) => s.endTime));\n\n // Determine overall status (ERROR if any span errored)\n const hasError = spanData.some((s) => s.status.code === 'ERROR');\n const status = hasError ? 'ERROR' : 'OK';\n\n return {\n traceId,\n correlationId: traceId.slice(0, 16), // First 16 chars\n rootSpan,\n spans: spanData,\n startTime,\n endTime,\n duration: endTime - startTime,\n status: status as 'OK' | 'ERROR' | 'UNSET',\n service: this.serviceName,\n };\n }\n\n /**\n * Convert OpenTelemetry span to SpanData\n */\n private convertSpan(span: ReadableSpan): SpanData {\n const spanContext = span.spanContext();\n const startTime = span.startTime[0] * 1000 + span.startTime[1] / 1_000_000;\n const endTime = span.endTime[0] * 1000 + span.endTime[1] / 1_000_000;\n\n // Convert attributes\n const attributes: Record<string, any> = {};\n for (const [key, value] of Object.entries(span.attributes)) {\n attributes[key] = value;\n }\n\n // Convert status\n const statusCode = span.status.code;\n let status: 'OK' | 'ERROR' | 'UNSET';\n switch (statusCode) {\n case 0: {\n status = 'UNSET';\n break;\n }\n case 1: {\n status = 'OK';\n break;\n }\n case 2: {\n status = 'ERROR';\n break;\n }\n default: {\n status = 'UNSET';\n }\n }\n\n // Convert events\n const events = span.events.map((event) => ({\n name: event.name,\n timestamp: event.time[0] * 1000 + event.time[1] / 1_000_000,\n attributes: event.attributes\n ? Object.fromEntries(Object.entries(event.attributes))\n : undefined,\n }));\n\n // Convert links\n const links = span.links.map((link) => ({\n traceId: link.context.traceId,\n spanId: link.context.spanId,\n attributes: link.attributes\n ? Object.fromEntries(Object.entries(link.attributes))\n : undefined,\n }));\n\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n parentSpanId: (span as any).parentSpanId,\n name: span.name,\n kind: this.convertSpanKind(span.kind),\n startTime,\n endTime,\n duration: endTime - startTime,\n attributes,\n status: {\n code: status,\n message: span.status.message,\n },\n events: events.length > 0 ? events : undefined,\n links: links.length > 0 ? links : undefined,\n scope: this.convertScope(span),\n };\n }\n\n private convertScope(span: ReadableSpan): SpanData['scope'] {\n const s =\n (span as any).instrumentationScope ??\n (span as any).instrumentationLibrary;\n return s?.name ? { name: s.name, version: s.version || undefined } : undefined;\n }\n\n /**\n * Convert OpenTelemetry SpanKind to string\n */\n private convertSpanKind(\n kind: number,\n ): 'INTERNAL' | 'SERVER' | 'CLIENT' | 'PRODUCER' | 'CONSUMER' {\n switch (kind) {\n case 0: {\n return 'INTERNAL';\n }\n case 1: {\n return 'SERVER';\n }\n case 2: {\n return 'CLIENT';\n }\n case 3: {\n return 'PRODUCER';\n }\n case 4: {\n return 'CONSUMER';\n }\n default: {\n return 'INTERNAL';\n }\n }\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
1
  import '@opentelemetry/sdk-trace-base';
2
2
  import '@opentelemetry/core';
3
- export { a as DevtoolsSpanExporter } from '../exporter-qIQPDw29.cjs';
3
+ export { a as DevtoolsSpanExporter } from '../exporter-DjLkU621.cjs';
4
4
  import 'node:http';
@@ -1,4 +1,4 @@
1
1
  import '@opentelemetry/sdk-trace-base';
2
2
  import '@opentelemetry/core';
3
- export { a as DevtoolsSpanExporter } from '../exporter-qIQPDw29.js';
3
+ export { a as DevtoolsSpanExporter } from '../exporter-DjLkU621.js';
4
4
  import 'node:http';
@@ -103,6 +103,11 @@ var DevtoolsSpanExporter = class {
103
103
  timestamp: event.time[0] * 1e3 + event.time[1] / 1e6,
104
104
  attributes: event.attributes ? Object.fromEntries(Object.entries(event.attributes)) : void 0
105
105
  }));
106
+ const links = span.links.map((link) => ({
107
+ traceId: link.context.traceId,
108
+ spanId: link.context.spanId,
109
+ attributes: link.attributes ? Object.fromEntries(Object.entries(link.attributes)) : void 0
110
+ }));
106
111
  return {
107
112
  traceId: spanContext.traceId,
108
113
  spanId: spanContext.spanId,
@@ -117,9 +122,15 @@ var DevtoolsSpanExporter = class {
117
122
  code: status,
118
123
  message: span.status.message
119
124
  },
120
- events: events.length > 0 ? events : void 0
125
+ events: events.length > 0 ? events : void 0,
126
+ links: links.length > 0 ? links : void 0,
127
+ scope: this.convertScope(span)
121
128
  };
122
129
  }
130
+ convertScope(span) {
131
+ const s = span.instrumentationScope ?? span.instrumentationLibrary;
132
+ return s?.name ? { name: s.name, version: s.version || void 0 } : void 0;
133
+ }
123
134
  /**
124
135
  * Convert OpenTelemetry SpanKind to string
125
136
  */
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/exporter.ts"],"names":[],"mappings":"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","file":"exporter.js","sourcesContent":["/**\n * OpenTelemetry SpanExporter that streams spans to DevtoolsServer\n */\n\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport type { ExportResult, ExportResultCode } from '@opentelemetry/core';\nimport type { DevtoolsServer } from './server';\nimport type { TraceData, SpanData } from './types';\n\nexport class DevtoolsSpanExporter implements SpanExporter {\n private server: DevtoolsServer;\n private serviceName: string;\n\n constructor(server: DevtoolsServer, serviceName: string = 'unknown-service') {\n this.server = server;\n this.serviceName = serviceName;\n }\n\n /**\n * Export spans to the WebSocket server\n */\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n // Immediately call the callback to unblock the span processor\n // Then process the spans asynchronously\n resultCallback({ code: 0 as ExportResultCode });\n\n // Process spans asynchronously without blocking\n Promise.resolve().then(() => {\n try {\n console.log(`[Autotel Exporter] Exporting ${spans.length} span(s)`);\n\n // Group spans by trace ID\n const traceMap = new Map<string, ReadableSpan[]>();\n\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n if (!traceMap.has(traceId)) {\n traceMap.set(traceId, []);\n }\n traceMap.get(traceId)!.push(span);\n }\n\n // Convert each trace and send to server\n for (const [traceId, traceSpans] of traceMap) {\n const trace = this.convertToTraceData(traceId, traceSpans);\n console.log(\n `[Autotel Exporter] Adding trace ${traceId.slice(0, 16)} with ${traceSpans.length} spans`,\n );\n this.server.addTrace(trace);\n }\n } catch (error) {\n console.error('[Autotel Exporter] Export error:', error);\n }\n });\n }\n\n /**\n * Shutdown the exporter\n */\n async shutdown(): Promise<void> {\n // Nothing to clean up\n }\n\n /**\n * Force flush any buffered spans\n */\n async forceFlush(): Promise<void> {\n // Nothing to flush\n }\n\n /**\n * Convert OpenTelemetry spans to TraceData\n */\n private convertToTraceData(\n traceId: string,\n spans: ReadableSpan[],\n ): TraceData {\n // Convert spans\n const spanData: SpanData[] = spans.map((span) => this.convertSpan(span));\n\n // Find root span (no parent)\n const rootSpan = spanData.find((s) => !s.parentSpanId) || spanData[0];\n\n // Sort spans by start time\n spanData.sort((a, b) => a.startTime - b.startTime);\n\n const startTime = Math.min(...spanData.map((s) => s.startTime));\n const endTime = Math.max(...spanData.map((s) => s.endTime));\n\n // Determine overall status (ERROR if any span errored)\n const hasError = spanData.some((s) => s.status.code === 'ERROR');\n const status = hasError ? 'ERROR' : 'OK';\n\n return {\n traceId,\n correlationId: traceId.slice(0, 16), // First 16 chars\n rootSpan,\n spans: spanData,\n startTime,\n endTime,\n duration: endTime - startTime,\n status: status as 'OK' | 'ERROR' | 'UNSET',\n service: this.serviceName,\n };\n }\n\n /**\n * Convert OpenTelemetry span to SpanData\n */\n private convertSpan(span: ReadableSpan): SpanData {\n const spanContext = span.spanContext();\n const startTime = span.startTime[0] * 1000 + span.startTime[1] / 1_000_000;\n const endTime = span.endTime[0] * 1000 + span.endTime[1] / 1_000_000;\n\n // Convert attributes\n const attributes: Record<string, any> = {};\n for (const [key, value] of Object.entries(span.attributes)) {\n attributes[key] = value;\n }\n\n // Convert status\n const statusCode = span.status.code;\n let status: 'OK' | 'ERROR' | 'UNSET';\n switch (statusCode) {\n case 0: {\n status = 'UNSET';\n break;\n }\n case 1: {\n status = 'OK';\n break;\n }\n case 2: {\n status = 'ERROR';\n break;\n }\n default: {\n status = 'UNSET';\n }\n }\n\n // Convert events\n const events = span.events.map((event) => ({\n name: event.name,\n timestamp: event.time[0] * 1000 + event.time[1] / 1_000_000,\n attributes: event.attributes\n ? Object.fromEntries(Object.entries(event.attributes))\n : undefined,\n }));\n\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n parentSpanId: (span as any).parentSpanId,\n name: span.name,\n kind: this.convertSpanKind(span.kind),\n startTime,\n endTime,\n duration: endTime - startTime,\n attributes,\n status: {\n code: status,\n message: span.status.message,\n },\n events: events.length > 0 ? events : undefined,\n };\n }\n\n /**\n * Convert OpenTelemetry SpanKind to string\n */\n private convertSpanKind(\n kind: number,\n ): 'INTERNAL' | 'SERVER' | 'CLIENT' | 'PRODUCER' | 'CONSUMER' {\n switch (kind) {\n case 0: {\n return 'INTERNAL';\n }\n case 1: {\n return 'SERVER';\n }\n case 2: {\n return 'CLIENT';\n }\n case 3: {\n return 'PRODUCER';\n }\n case 4: {\n return 'CONSUMER';\n }\n default: {\n return 'INTERNAL';\n }\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/exporter.ts"],"names":[],"mappings":"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","file":"exporter.js","sourcesContent":["/**\n * OpenTelemetry SpanExporter that streams spans to DevtoolsServer\n */\n\nimport type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';\nimport type { ExportResult, ExportResultCode } from '@opentelemetry/core';\nimport type { DevtoolsServer } from './server';\nimport type { TraceData, SpanData } from './types';\n\nexport class DevtoolsSpanExporter implements SpanExporter {\n private server: DevtoolsServer;\n private serviceName: string;\n\n constructor(server: DevtoolsServer, serviceName: string = 'unknown-service') {\n this.server = server;\n this.serviceName = serviceName;\n }\n\n /**\n * Export spans to the WebSocket server\n */\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n // Immediately call the callback to unblock the span processor\n // Then process the spans asynchronously\n resultCallback({ code: 0 as ExportResultCode });\n\n // Process spans asynchronously without blocking\n Promise.resolve().then(() => {\n try {\n console.log(`[Autotel Exporter] Exporting ${spans.length} span(s)`);\n\n // Group spans by trace ID\n const traceMap = new Map<string, ReadableSpan[]>();\n\n for (const span of spans) {\n const traceId = span.spanContext().traceId;\n if (!traceMap.has(traceId)) {\n traceMap.set(traceId, []);\n }\n traceMap.get(traceId)!.push(span);\n }\n\n // Convert each trace and send to server\n for (const [traceId, traceSpans] of traceMap) {\n const trace = this.convertToTraceData(traceId, traceSpans);\n console.log(\n `[Autotel Exporter] Adding trace ${traceId.slice(0, 16)} with ${traceSpans.length} spans`,\n );\n this.server.addTrace(trace);\n }\n } catch (error) {\n console.error('[Autotel Exporter] Export error:', error);\n }\n });\n }\n\n /**\n * Shutdown the exporter\n */\n async shutdown(): Promise<void> {\n // Nothing to clean up\n }\n\n /**\n * Force flush any buffered spans\n */\n async forceFlush(): Promise<void> {\n // Nothing to flush\n }\n\n /**\n * Convert OpenTelemetry spans to TraceData\n */\n private convertToTraceData(\n traceId: string,\n spans: ReadableSpan[],\n ): TraceData {\n // Convert spans\n const spanData: SpanData[] = spans.map((span) => this.convertSpan(span));\n\n // Find root span (no parent)\n const rootSpan = spanData.find((s) => !s.parentSpanId) || spanData[0];\n\n // Sort spans by start time\n spanData.sort((a, b) => a.startTime - b.startTime);\n\n const startTime = Math.min(...spanData.map((s) => s.startTime));\n const endTime = Math.max(...spanData.map((s) => s.endTime));\n\n // Determine overall status (ERROR if any span errored)\n const hasError = spanData.some((s) => s.status.code === 'ERROR');\n const status = hasError ? 'ERROR' : 'OK';\n\n return {\n traceId,\n correlationId: traceId.slice(0, 16), // First 16 chars\n rootSpan,\n spans: spanData,\n startTime,\n endTime,\n duration: endTime - startTime,\n status: status as 'OK' | 'ERROR' | 'UNSET',\n service: this.serviceName,\n };\n }\n\n /**\n * Convert OpenTelemetry span to SpanData\n */\n private convertSpan(span: ReadableSpan): SpanData {\n const spanContext = span.spanContext();\n const startTime = span.startTime[0] * 1000 + span.startTime[1] / 1_000_000;\n const endTime = span.endTime[0] * 1000 + span.endTime[1] / 1_000_000;\n\n // Convert attributes\n const attributes: Record<string, any> = {};\n for (const [key, value] of Object.entries(span.attributes)) {\n attributes[key] = value;\n }\n\n // Convert status\n const statusCode = span.status.code;\n let status: 'OK' | 'ERROR' | 'UNSET';\n switch (statusCode) {\n case 0: {\n status = 'UNSET';\n break;\n }\n case 1: {\n status = 'OK';\n break;\n }\n case 2: {\n status = 'ERROR';\n break;\n }\n default: {\n status = 'UNSET';\n }\n }\n\n // Convert events\n const events = span.events.map((event) => ({\n name: event.name,\n timestamp: event.time[0] * 1000 + event.time[1] / 1_000_000,\n attributes: event.attributes\n ? Object.fromEntries(Object.entries(event.attributes))\n : undefined,\n }));\n\n // Convert links\n const links = span.links.map((link) => ({\n traceId: link.context.traceId,\n spanId: link.context.spanId,\n attributes: link.attributes\n ? Object.fromEntries(Object.entries(link.attributes))\n : undefined,\n }));\n\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n parentSpanId: (span as any).parentSpanId,\n name: span.name,\n kind: this.convertSpanKind(span.kind),\n startTime,\n endTime,\n duration: endTime - startTime,\n attributes,\n status: {\n code: status,\n message: span.status.message,\n },\n events: events.length > 0 ? events : undefined,\n links: links.length > 0 ? links : undefined,\n scope: this.convertScope(span),\n };\n }\n\n private convertScope(span: ReadableSpan): SpanData['scope'] {\n const s =\n (span as any).instrumentationScope ??\n (span as any).instrumentationLibrary;\n return s?.name ? { name: s.name, version: s.version || undefined } : undefined;\n }\n\n /**\n * Convert OpenTelemetry SpanKind to string\n */\n private convertSpanKind(\n kind: number,\n ): 'INTERNAL' | 'SERVER' | 'CLIENT' | 'PRODUCER' | 'CONSUMER' {\n switch (kind) {\n case 0: {\n return 'INTERNAL';\n }\n case 1: {\n return 'SERVER';\n }\n case 2: {\n return 'CLIENT';\n }\n case 3: {\n return 'PRODUCER';\n }\n case 4: {\n return 'CONSUMER';\n }\n default: {\n return 'INTERNAL';\n }\n }\n }\n}\n"]}
@@ -375,12 +375,17 @@ var DevtoolsServer = class {
375
375
  limits;
376
376
  verbose;
377
377
  _port;
378
+ onData;
378
379
  constructor(options = {}) {
379
380
  this.limits = resolveTelemetryLimits(options);
380
381
  this.verbose = options.verbose ?? false;
381
382
  this._port = options.port ?? 4318;
383
+ this.onData = options.onData;
382
384
  this.httpServer = options.server ?? http.createServer();
383
385
  this.wss = new ws.WebSocketServer({ server: this.httpServer, path: options.path ?? "/ws" });
386
+ this.wss.on("error", (err) => {
387
+ if (this.httpServer.listening) throw err;
388
+ });
384
389
  this.wss.on("connection", (ws) => {
385
390
  this.clients.add(ws);
386
391
  this.log(`Client connected (${this.clients.size} total)`);
@@ -472,6 +477,12 @@ var DevtoolsServer = class {
472
477
  client.send(msg);
473
478
  }
474
479
  }
480
+ if (this.onData) {
481
+ try {
482
+ this.onData(data);
483
+ } catch {
484
+ }
485
+ }
475
486
  }
476
487
  log(message) {
477
488
  if (this.verbose) console.log(`[autotel-devtools] ${message}`);
@@ -589,6 +600,11 @@ var DevtoolsSpanExporter = class {
589
600
  timestamp: event.time[0] * 1e3 + event.time[1] / 1e6,
590
601
  attributes: event.attributes ? Object.fromEntries(Object.entries(event.attributes)) : void 0
591
602
  }));
603
+ const links = span.links.map((link) => ({
604
+ traceId: link.context.traceId,
605
+ spanId: link.context.spanId,
606
+ attributes: link.attributes ? Object.fromEntries(Object.entries(link.attributes)) : void 0
607
+ }));
592
608
  return {
593
609
  traceId: spanContext.traceId,
594
610
  spanId: spanContext.spanId,
@@ -603,9 +619,15 @@ var DevtoolsSpanExporter = class {
603
619
  code: status,
604
620
  message: span.status.message
605
621
  },
606
- events: events.length > 0 ? events : void 0
622
+ events: events.length > 0 ? events : void 0,
623
+ links: links.length > 0 ? links : void 0,
624
+ scope: this.convertScope(span)
607
625
  };
608
626
  }
627
+ convertScope(span) {
628
+ const s = span.instrumentationScope ?? span.instrumentationLibrary;
629
+ return s?.name ? { name: s.name, version: s.version || void 0 } : void 0;
630
+ }
609
631
  /**
610
632
  * Convert OpenTelemetry SpanKind to string
611
633
  */
@@ -974,7 +996,10 @@ function flattenAttributes(attrs) {
974
996
  }
975
997
  function nanoToMs(nano) {
976
998
  if (!nano) return 0;
977
- return Number(BigInt(nano) / 1000000n);
999
+ const ns = BigInt(nano);
1000
+ const ms = ns / 1000000n;
1001
+ const remNs = ns % 1000000n;
1002
+ return Number(ms) + Number(remNs) / 1e6;
978
1003
  }
979
1004
  var SPAN_KIND_MAP = {
980
1005
  0: "INTERNAL",
@@ -1012,6 +1037,7 @@ function parseOtlpTraces(payload) {
1012
1037
  const service = String(resourceAttrs["service.name"] || "unknown");
1013
1038
  const scopeSpans = rs.scopeSpans || [];
1014
1039
  for (const ss of scopeSpans) {
1040
+ const scope = ss.scope?.name ? { name: ss.scope.name, version: ss.scope.version || void 0 } : void 0;
1015
1041
  for (const span of ss.spans || []) {
1016
1042
  const traceId = normalizeHexId(span.traceId);
1017
1043
  if (!traceId) continue;
@@ -1036,7 +1062,13 @@ function parseOtlpTraces(payload) {
1036
1062
  name: e.name || "",
1037
1063
  timestamp: nanoToMs(e.timeUnixNano),
1038
1064
  attributes: flattenAttributes(e.attributes)
1039
- }))
1065
+ })),
1066
+ links: (span.links || []).map((l) => ({
1067
+ traceId: normalizeHexId(l.traceId),
1068
+ spanId: normalizeHexId(l.spanId),
1069
+ attributes: flattenAttributes(l.attributes)
1070
+ })),
1071
+ scope
1040
1072
  };
1041
1073
  const existing = traceMap.get(traceId);
1042
1074
  if (existing) {