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.
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- import { D as DevtoolsServer, a as DevtoolsSpanExporter } from './exporter-qIQPDw29.cjs';
2
- export { b as DevtoolsData, E as ErrorGroup, L as LogData, M as MetricData, S as SpanData, T as TraceData } from './exporter-qIQPDw29.cjs';
1
+ import { D as DevtoolsServer, a as DevtoolsSpanExporter } from './exporter-DjLkU621.cjs';
2
+ export { b as DevtoolsData, E as ErrorGroup, L as LogData, M as MetricData, S as SpanData, T as TraceData } from './exporter-DjLkU621.cjs';
3
3
  import { Server } from 'node:http';
4
4
  export { DevtoolsLogExporter } from './server/log-exporter.cjs';
5
5
  export { DevtoolsRemoteExporter } from './server/remote-exporter.cjs';
6
- export { E as ErrorAggregator } from './error-aggregator-CtZmjm-k.cjs';
6
+ export { E as ErrorAggregator } from './error-aggregator-CbLiuot4.cjs';
7
7
  import '@opentelemetry/sdk-trace-base';
8
8
  import '@opentelemetry/core';
9
9
  import '@opentelemetry/sdk-logs';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { D as DevtoolsServer, a as DevtoolsSpanExporter } from './exporter-qIQPDw29.js';
2
- export { b as DevtoolsData, E as ErrorGroup, L as LogData, M as MetricData, S as SpanData, T as TraceData } from './exporter-qIQPDw29.js';
1
+ import { D as DevtoolsServer, a as DevtoolsSpanExporter } from './exporter-DjLkU621.js';
2
+ export { b as DevtoolsData, E as ErrorGroup, L as LogData, M as MetricData, S as SpanData, T as TraceData } from './exporter-DjLkU621.js';
3
3
  import { Server } from 'node:http';
4
4
  export { DevtoolsLogExporter } from './server/log-exporter.js';
5
5
  export { DevtoolsRemoteExporter } from './server/remote-exporter.js';
6
- export { E as ErrorAggregator } from './error-aggregator-BkO0l8ak.js';
6
+ export { E as ErrorAggregator } from './error-aggregator-CAk_pt3Z.js';
7
7
  import '@opentelemetry/sdk-trace-base';
8
8
  import '@opentelemetry/core';
9
9
  import '@opentelemetry/sdk-logs';
package/dist/index.js CHANGED
@@ -360,12 +360,17 @@ var DevtoolsServer = class {
360
360
  limits;
361
361
  verbose;
362
362
  _port;
363
+ onData;
363
364
  constructor(options = {}) {
364
365
  this.limits = resolveTelemetryLimits(options);
365
366
  this.verbose = options.verbose ?? false;
366
367
  this._port = options.port ?? 4318;
368
+ this.onData = options.onData;
367
369
  this.httpServer = options.server ?? createServer();
368
370
  this.wss = new WebSocketServer({ server: this.httpServer, path: options.path ?? "/ws" });
371
+ this.wss.on("error", (err) => {
372
+ if (this.httpServer.listening) throw err;
373
+ });
369
374
  this.wss.on("connection", (ws) => {
370
375
  this.clients.add(ws);
371
376
  this.log(`Client connected (${this.clients.size} total)`);
@@ -457,6 +462,12 @@ var DevtoolsServer = class {
457
462
  client.send(msg);
458
463
  }
459
464
  }
465
+ if (this.onData) {
466
+ try {
467
+ this.onData(data);
468
+ } catch {
469
+ }
470
+ }
460
471
  }
461
472
  log(message) {
462
473
  if (this.verbose) console.log(`[autotel-devtools] ${message}`);
@@ -510,7 +521,10 @@ function flattenAttributes(attrs) {
510
521
  }
511
522
  function nanoToMs(nano) {
512
523
  if (!nano) return 0;
513
- return Number(BigInt(nano) / 1000000n);
524
+ const ns = BigInt(nano);
525
+ const ms = ns / 1000000n;
526
+ const remNs = ns % 1000000n;
527
+ return Number(ms) + Number(remNs) / 1e6;
514
528
  }
515
529
  var SPAN_KIND_MAP = {
516
530
  0: "INTERNAL",
@@ -548,6 +562,7 @@ function parseOtlpTraces(payload) {
548
562
  const service = String(resourceAttrs["service.name"] || "unknown");
549
563
  const scopeSpans = rs.scopeSpans || [];
550
564
  for (const ss of scopeSpans) {
565
+ const scope = ss.scope?.name ? { name: ss.scope.name, version: ss.scope.version || void 0 } : void 0;
551
566
  for (const span of ss.spans || []) {
552
567
  const traceId = normalizeHexId(span.traceId);
553
568
  if (!traceId) continue;
@@ -572,7 +587,13 @@ function parseOtlpTraces(payload) {
572
587
  name: e.name || "",
573
588
  timestamp: nanoToMs(e.timeUnixNano),
574
589
  attributes: flattenAttributes(e.attributes)
575
- }))
590
+ })),
591
+ links: (span.links || []).map((l) => ({
592
+ traceId: normalizeHexId(l.traceId),
593
+ spanId: normalizeHexId(l.spanId),
594
+ attributes: flattenAttributes(l.attributes)
595
+ })),
596
+ scope
576
597
  };
577
598
  const existing = traceMap.get(traceId);
578
599
  if (existing) {
@@ -1011,43 +1032,79 @@ function attachDevtoolsRoutes(httpServer, devtools) {
1011
1032
  });
1012
1033
  }
1013
1034
  var LOOPBACK = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
1035
+ var DEFAULT_MAX_PORT_TRIES = 20;
1014
1036
  function formatAddress(host, port) {
1015
1037
  return host.includes(":") ? `[${host}]:${port}` : `${host}:${port}`;
1016
1038
  }
1017
1039
  function listenLoopbackDualStack(args) {
1018
- const { primary, port, host, attachSecondary } = args;
1040
+ const { primary, port, host, attachSecondary, maxTries } = args;
1041
+ const maxAttempts = Math.max(1, maxTries ?? DEFAULT_MAX_PORT_TRIES);
1019
1042
  let sibling;
1020
1043
  const ready = new Promise(
1021
- (resolve2) => {
1044
+ (resolve2, reject) => {
1022
1045
  const addresses = [];
1023
1046
  const warnings = [];
1024
1047
  const primaryHost = host === "localhost" ? "127.0.0.1" : host;
1025
- primary.listen(port, primaryHost, () => {
1048
+ let candidate = port;
1049
+ let attempt = 0;
1050
+ const bindFailed = (atPort, msg) => reject(
1051
+ new Error(`could not bind ${formatAddress(primaryHost, atPort)}: ${msg}`)
1052
+ );
1053
+ const onError = (e) => {
1054
+ if (e.code !== "EADDRINUSE") return bindFailed(candidate, e.message);
1055
+ if (++attempt >= maxAttempts) {
1056
+ reject(
1057
+ new Error(
1058
+ `could not bind ${formatAddress(primaryHost, port)}: ${maxAttempts} consecutive ports in use`
1059
+ )
1060
+ );
1061
+ return;
1062
+ }
1063
+ candidate++;
1064
+ listen();
1065
+ };
1066
+ const onListening = () => {
1067
+ primary.removeListener("error", onError);
1068
+ if (candidate !== port) {
1069
+ warnings.push(`port ${port} was busy; using ${candidate} instead`);
1070
+ }
1026
1071
  const addr = primary.address();
1027
- const resolvedPort = addr && typeof addr === "object" ? addr.port : port;
1072
+ const resolvedPort = addr && typeof addr === "object" ? addr.port : candidate;
1028
1073
  addresses.push(formatAddress(primaryHost, resolvedPort));
1029
1074
  if (!LOOPBACK.has(host)) {
1030
- resolve2({ addresses, warnings });
1075
+ resolve2({ addresses, port: resolvedPort, warnings });
1031
1076
  return;
1032
1077
  }
1033
1078
  const siblingHost = primaryHost === "::1" ? "127.0.0.1" : "::1";
1034
1079
  const s = createServer();
1035
1080
  attachSecondary(s);
1036
- const onError = (e) => {
1081
+ const onSiblingError = (se) => {
1037
1082
  s.close();
1038
1083
  warnings.push(
1039
- `could not also bind ${formatAddress(siblingHost, resolvedPort)} (${e.message}); clients using the ${siblingHost === "::1" ? "IPv6" : "IPv4"} form of "localhost" may not connect.`
1084
+ `could not also bind ${formatAddress(siblingHost, resolvedPort)} (${se.message}); clients using the ${siblingHost === "::1" ? "IPv6" : "IPv4"} form of "localhost" may not connect.`
1040
1085
  );
1041
- resolve2({ addresses, warnings });
1086
+ resolve2({ addresses, port: resolvedPort, warnings });
1042
1087
  };
1043
- s.once("error", onError);
1088
+ s.once("error", onSiblingError);
1044
1089
  s.listen(resolvedPort, siblingHost, () => {
1045
- s.off("error", onError);
1090
+ s.off("error", onSiblingError);
1046
1091
  sibling = s;
1047
1092
  addresses.push(formatAddress(siblingHost, resolvedPort));
1048
- resolve2({ addresses, warnings });
1093
+ resolve2({ addresses, port: resolvedPort, warnings });
1049
1094
  });
1050
- });
1095
+ };
1096
+ const listen = () => {
1097
+ try {
1098
+ primary.listen(candidate, primaryHost);
1099
+ } catch (e) {
1100
+ primary.removeListener("error", onError);
1101
+ primary.removeListener("listening", onListening);
1102
+ bindFailed(candidate, e.message);
1103
+ }
1104
+ };
1105
+ primary.on("error", onError);
1106
+ primary.once("listening", onListening);
1107
+ listen();
1051
1108
  }
1052
1109
  );
1053
1110
  return {
@@ -1164,6 +1221,11 @@ var DevtoolsSpanExporter = class {
1164
1221
  timestamp: event.time[0] * 1e3 + event.time[1] / 1e6,
1165
1222
  attributes: event.attributes ? Object.fromEntries(Object.entries(event.attributes)) : void 0
1166
1223
  }));
1224
+ const links = span.links.map((link) => ({
1225
+ traceId: link.context.traceId,
1226
+ spanId: link.context.spanId,
1227
+ attributes: link.attributes ? Object.fromEntries(Object.entries(link.attributes)) : void 0
1228
+ }));
1167
1229
  return {
1168
1230
  traceId: spanContext.traceId,
1169
1231
  spanId: spanContext.spanId,
@@ -1178,9 +1240,15 @@ var DevtoolsSpanExporter = class {
1178
1240
  code: status,
1179
1241
  message: span.status.message
1180
1242
  },
1181
- events: events.length > 0 ? events : void 0
1243
+ events: events.length > 0 ? events : void 0,
1244
+ links: links.length > 0 ? links : void 0,
1245
+ scope: this.convertScope(span)
1182
1246
  };
1183
1247
  }
1248
+ convertScope(span) {
1249
+ const s = span.instrumentationScope ?? span.instrumentationLibrary;
1250
+ return s?.name ? { name: s.name, version: s.version || void 0 } : void 0;
1251
+ }
1184
1252
  /**
1185
1253
  * Convert OpenTelemetry SpanKind to string
1186
1254
  */