@autter/runtime-node 1.6.1 → 1.7.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.
package/dist/index.d.cts CHANGED
@@ -198,8 +198,9 @@ type RuntimeContextHandle = typeof runtimeContext;
198
198
  * call)
199
199
  * successful traces : 1% (head sampling, configurable)
200
200
  * request metrics : aggregated every 60 s, keyed by route
201
- * template (Express routes detected
202
- * out of the box — see captureExpressRoute)
201
+ * template (Express and Next.js routes
202
+ * detected out of the box — see
203
+ * captureRequestRoute, route-label.ts)
203
204
  * operation / request summaries: 100% (withRuntimeOperation,
204
205
  * autterRequests, … — one wide
205
206
  * record per unit of work, never
package/dist/index.d.ts CHANGED
@@ -198,8 +198,9 @@ type RuntimeContextHandle = typeof runtimeContext;
198
198
  * call)
199
199
  * successful traces : 1% (head sampling, configurable)
200
200
  * request metrics : aggregated every 60 s, keyed by route
201
- * template (Express routes detected
202
- * out of the box — see captureExpressRoute)
201
+ * template (Express and Next.js routes
202
+ * detected out of the box — see
203
+ * captureRequestRoute, route-label.ts)
203
204
  * operation / request summaries: 100% (withRuntimeOperation,
204
205
  * autterRequests, … — one wide
205
206
  * record per unit of work, never
package/dist/index.js CHANGED
@@ -45,10 +45,10 @@ import {
45
45
  splitFingerprint,
46
46
  toClientError,
47
47
  withRuntimeOperation
48
- } from "./chunk-5KISVAVB.js";
48
+ } from "./chunk-KA4AOZX2.js";
49
49
  import {
50
50
  runDoctor
51
- } from "./chunk-L5ENELB2.js";
51
+ } from "./chunk-QUC6OUE7.js";
52
52
  import {
53
53
  COMPAT_MANIFEST,
54
54
  CountingExporter,
@@ -70,7 +70,7 @@ import {
70
70
  sharedSlot,
71
71
  telemetryStats,
72
72
  unregisterFlushTargets
73
- } from "./chunk-HRDELHZP.js";
73
+ } from "./chunk-LG52CW3V.js";
74
74
 
75
75
  // src/relay.ts
76
76
  var IpWindow = class {
@@ -441,6 +441,9 @@ function createBrowserRelayHandler(opts) {
441
441
  }
442
442
 
443
443
  // src/server.ts
444
+ import { errorMonitor } from "events";
445
+ import { createRequire } from "module";
446
+ import { join } from "path";
444
447
  import { ServerResponse } from "http";
445
448
  import { randomUUID } from "crypto";
446
449
  import {
@@ -450,7 +453,7 @@ import {
450
453
  SpanStatusCode,
451
454
  TraceFlags
452
455
  } from "@opentelemetry/api";
453
- import { getRPCMetadata as getRPCMetadata2, RPCType as RPCType2 } from "@opentelemetry/core";
456
+ import { getRPCMetadata as getRPCMetadata3, RPCType as RPCType3 } from "@opentelemetry/core";
454
457
  import {
455
458
  AggregationTemporalityPreference,
456
459
  OTLPMetricExporter
@@ -1276,6 +1279,40 @@ function installUnhandledRejectionCapture(capture, mode = unhandledRejectionsMod
1276
1279
  };
1277
1280
  }
1278
1281
 
1282
+ // src/route-label.ts
1283
+ import { getRPCMetadata as getRPCMetadata2, RPCType as RPCType2 } from "@opentelemetry/core";
1284
+ var ours = /* @__PURE__ */ new WeakMap();
1285
+ var RAW_PATH_SPAN_TYPES = /* @__PURE__ */ new Set(["BaseServer.renderToResponse"]);
1286
+ function noteFrameworkRoute(parent, attributes) {
1287
+ const route = attributes?.["next.route"];
1288
+ if (typeof route !== "string" || !route.startsWith("/")) return;
1289
+ if (RAW_PATH_SPAN_TYPES.has(String(attributes?.["next.span_type"] ?? ""))) return;
1290
+ const rpc = getRPCMetadata2(parent);
1291
+ if (!rpc || rpc.type !== RPCType2.HTTP) return;
1292
+ const mine = ours.get(rpc);
1293
+ if (rpc.route !== void 0 && rpc.route !== mine) return;
1294
+ if (mine !== void 0 && mine.includes("[") && !route.includes("[")) return;
1295
+ if (mine !== void 0 && (route === `${mine}/route` || route === `${mine}/page`)) return;
1296
+ rpc.route = route;
1297
+ ours.set(rpc, route);
1298
+ }
1299
+ var RouteLabelSampler = class {
1300
+ constructor(delegate) {
1301
+ this.delegate = delegate;
1302
+ }
1303
+ delegate;
1304
+ shouldSample(ctx, traceId, spanName, spanKind, attributes, links) {
1305
+ try {
1306
+ noteFrameworkRoute(ctx, attributes);
1307
+ } catch {
1308
+ }
1309
+ return this.delegate.shouldSample(ctx, traceId, spanName, spanKind, attributes, links);
1310
+ }
1311
+ toString() {
1312
+ return `RouteLabel(${this.delegate.toString()})`;
1313
+ }
1314
+ };
1315
+
1279
1316
  // src/server.ts
1280
1317
  var S = sharedSlot("server", () => ({
1281
1318
  active: null,
@@ -1597,17 +1634,33 @@ function recordError(span, err) {
1597
1634
  function redactWithActive(attributes) {
1598
1635
  return S.redactor(attributes);
1599
1636
  }
1600
- function captureExpressRoute(_span, response) {
1637
+ function patchLoadedHttpModules() {
1638
+ try {
1639
+ const load = createRequire(join(process.cwd(), "autter-runtime-http.cjs"));
1640
+ load("http");
1641
+ load("https");
1642
+ } catch {
1643
+ }
1644
+ }
1645
+ var METRIC_ROUTE_ATTR = "autter.metric_has_route";
1646
+ function captureRequestRoute(span, response) {
1601
1647
  if (!(response instanceof ServerResponse)) return;
1602
- const rpcMetadata = getRPCMetadata2(context2.active());
1603
- if (rpcMetadata?.type !== RPCType2.HTTP) return;
1648
+ const rpcMetadata = getRPCMetadata3(context2.active());
1649
+ if (rpcMetadata?.type !== RPCType3.HTTP) return;
1650
+ let failed = false;
1651
+ response.on(errorMonitor, () => {
1652
+ failed = true;
1653
+ });
1604
1654
  const setRoute = () => {
1605
1655
  if (rpcMetadata.route) return;
1606
1656
  const route = expressRouteOf(response.req);
1607
1657
  if (route) rpcMetadata.route = route;
1608
1658
  };
1609
1659
  response.prependListener("finish", setRoute);
1610
- response.prependListener("close", setRoute);
1660
+ response.prependListener("close", () => {
1661
+ setRoute();
1662
+ if (rpcMetadata.route && !failed) span.setAttribute(METRIC_ROUTE_ATTR, true);
1663
+ });
1611
1664
  if (S.hookRequests) startHookRequest(response);
1612
1665
  }
1613
1666
  function structuredErrorAttributes(error) {
@@ -1656,8 +1709,14 @@ function initAutterServer(options) {
1656
1709
  "telemetry.distro.version": distro.version,
1657
1710
  // Tells the ingester request metrics arrive on the metrics pipe, so
1658
1711
  // it must not also fold our server spans into usage rollups (that
1659
- // would double-count every sampled request).
1660
- "autter.metrics_wired": true
1712
+ // would double-count every sampled request)...
1713
+ "autter.metrics_wired": true,
1714
+ // ...for the requests whose metric carries their route: those
1715
+ // request spans say so (`autter.metric_has_route`, see
1716
+ // captureRequestRoute). Ingesters honour metrics_wired only with
1717
+ // this, so an older SDK, whose Next.js metrics have no route, keeps
1718
+ // its span-fed per-route counts.
1719
+ "autter.metrics_routes": true
1661
1720
  });
1662
1721
  const rootSampler = new TraceIdRatioBasedSampler(options.traceSampleRate ?? 0.01);
1663
1722
  const headSampler = new ParentBasedSampler({
@@ -1674,6 +1733,7 @@ function initAutterServer(options) {
1674
1733
  const retainTraces = retainOnError || slowThresholdMs > 0;
1675
1734
  let sampler = retainTraces ? new RecordUnsampledSampler(headSampler) : headSampler;
1676
1735
  if (options.llmTracing !== false) sampler = new LlmAwareSampler(sampler);
1736
+ sampler = new RouteLabelSampler(sampler);
1677
1737
  const errorTraceBuffer = retainTraces ? new ErrorTraceRetentionProcessor(
1678
1738
  new BatchSpanProcessor(traceExporter(), { scheduledDelayMillis: 2e3 }),
1679
1739
  retainOnError,
@@ -1712,11 +1772,12 @@ function initAutterServer(options) {
1712
1772
  ) })
1713
1773
  ],
1714
1774
  instrumentations: [
1715
- new HttpInstrumentation({ responseHook: captureExpressRoute }),
1775
+ new HttpInstrumentation({ responseHook: captureRequestRoute }),
1716
1776
  ...options.instrumentations ?? []
1717
1777
  ]
1718
1778
  });
1719
1779
  sdk.start();
1780
+ patchLoadedHttpModules();
1720
1781
  const stopMemoryMetrics = options.memoryMetrics === false ? null : startMemoryMetrics();
1721
1782
  noteCompatFeature("endpoint_latency");
1722
1783
  if (stopMemoryMetrics) noteCompatFeature("memory_metrics");
package/dist/testing.cjs CHANGED
@@ -2,22 +2,22 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkUXTIMS3Gcjs = require('./chunk-UXTIMS3G.cjs');
6
- require('./chunk-4MVWIRBI.cjs');
5
+ var _chunkRV4MJNM2cjs = require('./chunk-RV4MJNM2.cjs');
6
+ require('./chunk-GYTQLITW.cjs');
7
7
 
8
8
  // src/testing.ts
9
9
  function captureRuntime() {
10
- const sink = _chunkUXTIMS3Gcjs.memorySink.call(void 0, );
11
- const remove = _chunkUXTIMS3Gcjs.addRuntimeSink.call(void 0, sink);
10
+ const sink = _chunkRV4MJNM2cjs.memorySink.call(void 0, );
11
+ const remove = _chunkRV4MJNM2cjs.addRuntimeSink.call(void 0, sink);
12
12
  return {
13
13
  get events() {
14
14
  return [...sink.events];
15
15
  },
16
16
  get operations() {
17
- return sink.events.filter(_chunkUXTIMS3Gcjs.isSummary);
17
+ return sink.events.filter(_chunkRV4MJNM2cjs.isSummary);
18
18
  },
19
19
  get logs() {
20
- return sink.events.filter((event) => !_chunkUXTIMS3Gcjs.isSummary.call(void 0, event));
20
+ return sink.events.filter((event) => !_chunkRV4MJNM2cjs.isSummary.call(void 0, event));
21
21
  },
22
22
  get exceptions() {
23
23
  return [...sink.exceptions];
@@ -98,7 +98,7 @@ var OperationExpectation = class {
98
98
  }
99
99
  };
100
100
  function expectOperation(source, name) {
101
- const events = Array.isArray(source) ? source.filter(_chunkUXTIMS3Gcjs.isSummary) : source.operations;
101
+ const events = Array.isArray(source) ? source.filter(_chunkRV4MJNM2cjs.isSummary) : source.operations;
102
102
  const matches = events.filter((event2) => {
103
103
  const actual = String(_nullishCoalesce(event2.attributes["autter.operation.name"], () => ( "")));
104
104
  return typeof name === "string" ? actual === name : name.test(actual);
@@ -118,4 +118,4 @@ function expectOperation(source, name) {
118
118
 
119
119
 
120
120
 
121
- exports.captureRuntime = captureRuntime; exports.expectOperation = expectOperation; exports.memorySink = _chunkUXTIMS3Gcjs.memorySink;
121
+ exports.captureRuntime = captureRuntime; exports.expectOperation = expectOperation; exports.memorySink = _chunkRV4MJNM2cjs.memorySink;
package/dist/testing.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  addRuntimeSink,
3
3
  isSummary,
4
4
  memorySink
5
- } from "./chunk-5KISVAVB.js";
6
- import "./chunk-HRDELHZP.js";
5
+ } from "./chunk-KA4AOZX2.js";
6
+ import "./chunk-LG52CW3V.js";
7
7
 
8
8
  // src/testing.ts
9
9
  function captureRuntime() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autter/runtime-node",
3
- "version": "1.6.1",
3
+ "version": "1.7.0",
4
4
  "description": "Autter Runtime for Node.js: request wide events, coded errors, operation logging, same-origin browser relay and a curated OpenTelemetry server tracker",
5
5
  "license": "MIT",
6
6
  "type": "module",