autotel 7.1.0 → 7.3.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.
Files changed (62) hide show
  1. package/README.md +41 -14
  2. package/dist/analysis.cjs +17 -0
  3. package/dist/analysis.d.cts +9 -1
  4. package/dist/analysis.d.ts +9 -1
  5. package/dist/analysis.js +17 -1
  6. package/dist/auto.cjs +2 -3
  7. package/dist/auto.js +1 -2
  8. package/dist/correlation-id.cjs +1 -1
  9. package/dist/correlation-id.js +1 -1
  10. package/dist/decorators.cjs +1 -1
  11. package/dist/decorators.d.cts +1 -1
  12. package/dist/decorators.d.ts +1 -1
  13. package/dist/decorators.js +1 -1
  14. package/dist/event.cjs +1 -1
  15. package/dist/event.js +1 -1
  16. package/dist/{functional-Cub1ku-C.js → functional-C4js2vTM.js} +44 -7
  17. package/dist/{functional-CJn2zY4o.d.cts → functional-CgWcRXGX.d.cts} +12 -5
  18. package/dist/{functional-BhhP4Er_.cjs → functional-DHlD7V0x.cjs} +47 -10
  19. package/dist/{functional-DXy1cHFF.d.ts → functional-RFFpQzAV.d.ts} +12 -5
  20. package/dist/functional.cjs +1 -1
  21. package/dist/functional.d.cts +1 -1
  22. package/dist/functional.d.ts +1 -1
  23. package/dist/functional.js +1 -1
  24. package/dist/http.cjs +1 -1
  25. package/dist/http.js +1 -1
  26. package/dist/index.cjs +53 -15
  27. package/dist/index.d.cts +24 -4
  28. package/dist/index.d.ts +24 -4
  29. package/dist/index.js +40 -5
  30. package/dist/{init-DrlL1eis.d.cts → init-8GSy2bza.d.cts} +1 -1
  31. package/dist/{init-DDyaLzeg.js → init-CsKK9vZO.js} +2196 -1298
  32. package/dist/{init-C5aHRqi2.cjs → init-DmM7dprX.cjs} +2356 -1300
  33. package/dist/{init-BgnYi2sm.d.ts → init-UnKrXhpq.d.ts} +1 -1
  34. package/dist/instrumentation.cjs +2 -3
  35. package/dist/instrumentation.js +1 -2
  36. package/dist/messaging.cjs +5 -5
  37. package/dist/messaging.js +2 -2
  38. package/dist/{sampling-CM5S2Lp3.d.ts → sampling-DBng4Foa.d.ts} +20 -1
  39. package/dist/{sampling-CmRdnXUi.d.cts → sampling-Er217Mge.d.cts} +20 -1
  40. package/dist/sampling.cjs +22 -601
  41. package/dist/sampling.d.cts +2 -2
  42. package/dist/sampling.d.ts +2 -2
  43. package/dist/sampling.js +2 -585
  44. package/dist/semantic-helpers.cjs +1 -1
  45. package/dist/semantic-helpers.js +1 -1
  46. package/dist/tail-sampling-processor.cjs +2 -26
  47. package/dist/tail-sampling-processor.js +1 -25
  48. package/dist/testing.cjs +32 -0
  49. package/dist/testing.d.cts +26 -1
  50. package/dist/testing.d.ts +26 -1
  51. package/dist/testing.js +32 -1
  52. package/dist/webhook.cjs +1 -1
  53. package/dist/webhook.js +1 -1
  54. package/dist/workflow-distributed.cjs +1 -1
  55. package/dist/workflow-distributed.js +1 -1
  56. package/dist/workflow.cjs +1 -1
  57. package/dist/workflow.js +1 -1
  58. package/dist/yaml-config.cjs +4 -248
  59. package/dist/yaml-config.d.cts +2 -2
  60. package/dist/yaml-config.d.ts +2 -2
  61. package/dist/yaml-config.js +1 -242
  62. package/package.json +1 -1
package/README.md CHANGED
@@ -143,20 +143,20 @@ Replace `NODE_OPTIONS` and 30+ lines of SDK boilerplate with `init()`, wrap func
143
143
 
144
144
  `autotel` has ~35 subpath exports. For most scenarios you only need one or two. Cloudflare Workers and other edge runtimes should use [`autotel-cloudflare`](../autotel-cloudflare) or [`autotel-edge`](../autotel-edge), not `autotel` (which expects Node).
145
145
 
146
- | Scenario | Package | Import |
147
- | -------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------- |
148
- | Node service: init + spans | `autotel` | `import { init, trace, span, withTracing, span } from 'autotel'` |
149
- | Node service: pino-style logger with trace context | `autotel` | `import { createLogger } from 'autotel/logger'` |
150
- | Node service: class decorators | `autotel` | `import { Trace, Span } from 'autotel/decorators'` |
151
- | Node service: Drizzle ORM spans | `autotel-drizzle` | `import { instrumentDrizzleClient } from 'autotel-drizzle'` |
152
- | Node service: testing assertions | `autotel` | `import { createTraceCollector } from 'autotel/testing'` |
153
- | Cloudflare Worker: fetch with spans | `autotel-cloudflare` | `import { wrapModule, trace, span, withTracing } from 'autotel-cloudflare'` |
154
- | Cloudflare Worker: logs only, no `nodejs_compat` | `autotel-cloudflare` | `import { createEdgeLogger } from 'autotel-cloudflare/logger'` |
155
- | Cloudflare Worker: queue consumer | `autotel-cloudflare` | `import { wrapModule, getQueueLogger } from 'autotel-cloudflare'` |
156
- | Cloudflare Worker: Durable Object | `autotel-cloudflare` | `import { wrapDurableObject } from 'autotel-cloudflare'` |
157
- | Hono on Workers | `autotel-cloudflare` + `autotel-hono` | `wrapModule` from `autotel-cloudflare`, middleware from `autotel-hono` |
158
- | Vercel Edge / Netlify Edge / Deno Deploy | `autotel-edge` | `import { init, trace, span, withTracing } from 'autotel-edge'` |
159
- | Edge runtime: logs only | `autotel-edge` | `import { createEdgeLogger } from 'autotel-edge/logger'` |
146
+ | Scenario | Package | Import |
147
+ | -------------------------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------ |
148
+ | Node service: init + spans | `autotel` | `import { init, trace, span, withTracing, span } from 'autotel'` |
149
+ | Node service: pino-style logger with trace context | `autotel` | `import { createLogger } from 'autotel/logger'` |
150
+ | Node service: class decorators | `autotel` | `import { Trace, Span } from 'autotel/decorators'` |
151
+ | Node service: Drizzle ORM spans | `autotel-drizzle` | `import { instrumentDrizzleClient } from 'autotel-drizzle'` |
152
+ | Node service: testing assertions | `autotel` | `import { createTraceCollector, createMemoryExporter } from 'autotel/testing'` |
153
+ | Cloudflare Worker: fetch with spans | `autotel-cloudflare` | `import { wrapModule, trace, span, withTracing } from 'autotel-cloudflare'` |
154
+ | Cloudflare Worker: logs only, no `nodejs_compat` | `autotel-cloudflare` | `import { createEdgeLogger } from 'autotel-cloudflare/logger'` |
155
+ | Cloudflare Worker: queue consumer | `autotel-cloudflare` | `import { wrapModule, getQueueLogger } from 'autotel-cloudflare'` |
156
+ | Cloudflare Worker: Durable Object | `autotel-cloudflare` | `import { wrapDurableObject } from 'autotel-cloudflare'` |
157
+ | Hono on Workers | `autotel-cloudflare` + `autotel-hono` | `wrapModule` from `autotel-cloudflare`, middleware from `autotel-hono` |
158
+ | Vercel Edge / Netlify Edge / Deno Deploy | `autotel-edge` | `import { init, trace, span, withTracing } from 'autotel-edge'` |
159
+ | Edge runtime: logs only | `autotel-edge` | `import { createEdgeLogger } from 'autotel-edge/logger'` |
160
160
 
161
161
  ## Quick Start
162
162
 
@@ -434,6 +434,8 @@ calling a model, this is why.
434
434
 
435
435
  Autotel defaults to production-ready adaptive sampling: a 10% baseline, with errors and slow requests kept automatically.
436
436
 
437
+ A sampler configured here also governs the functions you wrap. `trace()`, `withTracing()` and `instrument()` resolve their sampler per call as `options.sampler ?? init()'s sampler ?? keep everything`, so `sampling: 'production'` applies to wrapped spans and not only to the SDK. Resolution is per call because a wrapper is usually created at module load, before `init()` has run. A sampler passed at the wrapper still wins.
438
+
437
439
  ### Preset Shorthand
438
440
 
439
441
  Use the `sampling` field on `init()` when you want the shortest path:
@@ -775,6 +777,31 @@ app.use((req, res, next) => {
775
777
  - Baggage values are strings (convert numbers/objects before setting)
776
778
  - Never put PII in baggage - it propagates in HTTP headers across services!
777
779
 
780
+ ### Getting the trace id, and a link to it
781
+
782
+ `getActiveTraceContext()` reads the trace currently in scope, and
783
+ `resolveTraceUrl()` turns its id into a link to wherever you view traces. Printing
784
+ that link beside a slow request is the shortest path from "this was slow" to
785
+ looking at why:
786
+
787
+ ```typescript
788
+ import { getActiveTraceContext, resolveTraceUrl, span } from 'autotel';
789
+
790
+ await span('GET /feed', async () => {
791
+ const traceId = getActiveTraceContext()?.traceId;
792
+ const url = resolveTraceUrl(
793
+ 'http://localhost:4318/#trace={traceId}',
794
+ traceId,
795
+ );
796
+ console.log(`feed request ${url ?? traceId}`);
797
+ });
798
+ ```
799
+
800
+ `resolveTraceUrl(template, traceId)` substitutes `{traceId}`. Pass `undefined`
801
+ as the template and it falls back to `OTEL_TRACE_URL_TEMPLATE`, so the same code
802
+ points at devtools locally and at your vendor in production without a branch.
803
+ It returns `undefined` when neither is set.
804
+
778
805
  ### Reusable Middleware Helpers
779
806
 
780
807
  - `withTracing(options)` : create a preconfigured wrapper (service name, default attributes, skip rules).
package/dist/analysis.cjs CHANGED
@@ -81,6 +81,23 @@ function compareCohorts(options) {
81
81
  results.sort((a, b) => Math.abs(b.difference) - Math.abs(a.difference) || b.difference - a.difference || a.field.localeCompare(b.field) || a.value.localeCompare(b.value));
82
82
  return results.slice(0, limit);
83
83
  }
84
+ /**
85
+ * Label a numeric value with the range it falls in.
86
+ *
87
+ * `compareCohorts` skips a field whose values never repeat, which is what a
88
+ * raw duration or byte count does. Bucketing at instrumentation time turns
89
+ * such a field into one that can describe a cohort.
90
+ */
91
+ function bucket(value, boundaries) {
92
+ if (!Number.isFinite(value) || boundaries.length === 0) return "unknown";
93
+ const ordered = [...boundaries].sort((a, b) => a - b);
94
+ for (const [index, boundary] of ordered.entries()) if (value < boundary) {
95
+ const lower = ordered[index - 1];
96
+ return lower === void 0 ? `<${boundary}` : `${lower}-${boundary}`;
97
+ }
98
+ return `>=${ordered.at(-1)}`;
99
+ }
84
100
 
85
101
  //#endregion
102
+ exports.bucket = bucket;
86
103
  exports.compareCohorts = compareCohorts;
@@ -82,5 +82,13 @@ interface CompareCohortsOptions {
82
82
  * @returns Differences sorted by absolute strength, strongest first
83
83
  */
84
84
  declare function compareCohorts(options: CompareCohortsOptions): CohortDifference[];
85
+ /**
86
+ * Label a numeric value with the range it falls in.
87
+ *
88
+ * `compareCohorts` skips a field whose values never repeat, which is what a
89
+ * raw duration or byte count does. Bucketing at instrumentation time turns
90
+ * such a field into one that can describe a cohort.
91
+ */
92
+ declare function bucket(value: number, boundaries: readonly number[]): string;
85
93
  //#endregion
86
- export { AnalysisEvent, CohortDifference, CompareCohortsOptions, compareCohorts };
94
+ export { AnalysisEvent, CohortDifference, CompareCohortsOptions, bucket, compareCohorts };
@@ -82,5 +82,13 @@ interface CompareCohortsOptions {
82
82
  * @returns Differences sorted by absolute strength, strongest first
83
83
  */
84
84
  declare function compareCohorts(options: CompareCohortsOptions): CohortDifference[];
85
+ /**
86
+ * Label a numeric value with the range it falls in.
87
+ *
88
+ * `compareCohorts` skips a field whose values never repeat, which is what a
89
+ * raw duration or byte count does. Bucketing at instrumentation time turns
90
+ * such a field into one that can describe a cohort.
91
+ */
92
+ declare function bucket(value: number, boundaries: readonly number[]): string;
85
93
  //#endregion
86
- export { AnalysisEvent, CohortDifference, CompareCohortsOptions, compareCohorts };
94
+ export { AnalysisEvent, CohortDifference, CompareCohortsOptions, bucket, compareCohorts };
package/dist/analysis.js CHANGED
@@ -79,6 +79,22 @@ function compareCohorts(options) {
79
79
  results.sort((a, b) => Math.abs(b.difference) - Math.abs(a.difference) || b.difference - a.difference || a.field.localeCompare(b.field) || a.value.localeCompare(b.value));
80
80
  return results.slice(0, limit);
81
81
  }
82
+ /**
83
+ * Label a numeric value with the range it falls in.
84
+ *
85
+ * `compareCohorts` skips a field whose values never repeat, which is what a
86
+ * raw duration or byte count does. Bucketing at instrumentation time turns
87
+ * such a field into one that can describe a cohort.
88
+ */
89
+ function bucket(value, boundaries) {
90
+ if (!Number.isFinite(value) || boundaries.length === 0) return "unknown";
91
+ const ordered = [...boundaries].sort((a, b) => a - b);
92
+ for (const [index, boundary] of ordered.entries()) if (value < boundary) {
93
+ const lower = ordered[index - 1];
94
+ return lower === void 0 ? `<${boundary}` : `${lower}-${boundary}`;
95
+ }
96
+ return `>=${ordered.at(-1)}`;
97
+ }
82
98
 
83
99
  //#endregion
84
- export { compareCohorts };
100
+ export { bucket, compareCohorts };
package/dist/auto.cjs CHANGED
@@ -1,6 +1,5 @@
1
1
  const require_rolldown_runtime = require('./rolldown-runtime-C_NdSu1c.cjs');
2
- const require_init = require('./init-C5aHRqi2.cjs');
3
- const require_yaml_config = require('./yaml-config.cjs');
2
+ const require_init = require('./init-DmM7dprX.cjs');
4
3
  let node_module = require("node:module");
5
4
  node_module = require_rolldown_runtime.__toESM(node_module, 1);
6
5
  let import_in_the_middle = require("import-in-the-middle");
@@ -42,7 +41,7 @@ let import_in_the_middle = require("import-in-the-middle");
42
41
  */
43
42
  const { registerOptions } = (0, import_in_the_middle.createAddHookMessageChannel)();
44
43
  node_module.register("import-in-the-middle/hook.mjs", require("url").pathToFileURL(__filename).href, registerOptions);
45
- const yamlConfig = require_yaml_config.loadYamlConfig();
44
+ const yamlConfig = require_init.loadYamlConfig();
46
45
  const autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;
47
46
  const autoInstrumentations = autoInstrumentationsEnv === "true" ? true : autoInstrumentationsEnv ? autoInstrumentationsEnv.split(",").map((s) => s.trim()) : yamlConfig?.autoInstrumentations ?? ["http", "express"];
48
47
  require_init.init({
package/dist/auto.js CHANGED
@@ -1,5 +1,4 @@
1
- import { i as init } from "./init-DDyaLzeg.js";
2
- import { loadYamlConfig } from "./yaml-config.js";
1
+ import { _ as loadYamlConfig, a as init } from "./init-CsKK9vZO.js";
3
2
  import * as nodeModule from "node:module";
4
3
  import { createAddHookMessageChannel } from "import-in-the-middle";
5
4
 
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_init = require('./init-C5aHRqi2.cjs');
2
+ const require_init = require('./init-DmM7dprX.cjs');
3
3
 
4
4
  exports.CORRELATION_ID_BAGGAGE_KEY = require_init.CORRELATION_ID_BAGGAGE_KEY;
5
5
  exports.generateCorrelationId = require_init.generateCorrelationId;
@@ -1,3 +1,3 @@
1
- import { C as setCorrelationId, S as runWithCorrelationId, _ as CORRELATION_ID_BAGGAGE_KEY, b as getCorrelationStorage, v as generateCorrelationId, w as setCorrelationIdInBaggage, x as getOrCreateCorrelationId, y as getCorrelationId } from "./init-DDyaLzeg.js";
1
+ import { $ as getCorrelationStorage, Q as getCorrelationId, X as CORRELATION_ID_BAGGAGE_KEY, Z as generateCorrelationId, et as getOrCreateCorrelationId, nt as setCorrelationId, rt as setCorrelationIdInBaggage, tt as runWithCorrelationId } from "./init-CsKK9vZO.js";
2
2
 
3
3
  export { CORRELATION_ID_BAGGAGE_KEY, generateCorrelationId, getCorrelationId, getCorrelationStorage, getOrCreateCorrelationId, runWithCorrelationId, setCorrelationId, setCorrelationIdInBaggage };
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_values = require('./values-DApyq53y.cjs');
3
3
  const require_config = require('./config.cjs');
4
- const require_init = require('./init-C5aHRqi2.cjs');
4
+ const require_init = require('./init-DmM7dprX.cjs');
5
5
  let _opentelemetry_api = require("@opentelemetry/api");
6
6
 
7
7
  //#region src/decorators.ts
@@ -1,5 +1,5 @@
1
1
  import { n as TraceContext } from "./trace-context-C1Cn1q0y.cjs";
2
- import { m as TracingOptions } from "./functional-CJn2zY4o.cjs";
2
+ import { m as TracingOptions } from "./functional-CgWcRXGX.cjs";
3
3
  //#region src/decorators.d.ts
4
4
  /**
5
5
  * Options for @Trace method decorator
@@ -1,5 +1,5 @@
1
1
  import { n as TraceContext } from "./trace-context-f89xPJbK.js";
2
- import { m as TracingOptions } from "./functional-DXy1cHFF.js";
2
+ import { m as TracingOptions } from "./functional-RFFpQzAV.js";
3
3
  //#region src/decorators.d.ts
4
4
  /**
5
5
  * Options for @Trace method decorator
@@ -1,6 +1,6 @@
1
1
  import { y as splitNameOrOptions } from "./values-BenyvWKz.js";
2
2
  import { getConfig } from "./config.js";
3
- import { T as createTraceContext } from "./init-DDyaLzeg.js";
3
+ import { U as createTraceContext } from "./init-CsKK9vZO.js";
4
4
  import { SpanStatusCode } from "@opentelemetry/api";
5
5
 
6
6
  //#region src/decorators.ts
package/dist/event.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_init = require('./init-C5aHRqi2.cjs');
2
+ const require_init = require('./init-DmM7dprX.cjs');
3
3
 
4
4
  exports.Event = require_init.Event;
5
5
  exports.getEvents = require_init.getEvents;
package/dist/event.js CHANGED
@@ -1,3 +1,3 @@
1
- import { d as getEvents, f as resetEvents, u as Event } from "./init-DDyaLzeg.js";
1
+ import { d as Event, f as getEvents, p as resetEvents } from "./init-CsKK9vZO.js";
2
2
 
3
3
  export { Event, getEvents, resetEvents };
@@ -1,14 +1,43 @@
1
1
  import { l as asString, m as isDevelopment } from "./values-BenyvWKz.js";
2
2
  import { getForceFlushableProvider } from "./tracer-provider.js";
3
3
  import { getConfig } from "./config.js";
4
- import { AUTOTEL_SAMPLING_RATE, AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AlwaysSampler } from "./sampling.js";
5
- import { D as getActiveContextWithBaggage, O as getContextStorage, T as createTraceContext, h as getEventQueue, k as hasExplicitSpanStatus, m as runInOperationContext, r as getSdk, t as getConfig$1 } from "./init-DDyaLzeg.js";
4
+ import { C as AUTOTEL_SAMPLING_RATE, D as AlwaysSampler, G as getActiveContextWithBaggage, I as debugCaptureRequested, J as getEventQueue, K as getContextStorage, T as AUTOTEL_SAMPLING_TAIL_KEEP, U as createTraceContext, h as runInOperationContext, i as getSdk, n as getConfiguredSampler, q as hasExplicitSpanStatus, t as getConfig$1, w as AUTOTEL_SAMPLING_TAIL_EVALUATED, z as isForceKept } from "./init-CsKK9vZO.js";
6
5
  import { evidenceAttribute } from "./evidence.js";
7
6
  import { setSpanName } from "./trace-helpers.js";
8
7
  import { SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
9
8
  import * as nodeFs from "node:fs";
10
9
  import * as nodeUrl from "node:url";
11
10
 
11
+ //#region src/is-thenable.ts
12
+ /**
13
+ * Convert anything `await` would wait on into a native Promise, or return
14
+ * `undefined` for a synchronous value.
15
+ *
16
+ * `instanceof Promise` is not enough. ORM query builders (drizzle, knex,
17
+ * Prisma, Sequelize) are lazy thenables that are not native Promises, and a
18
+ * promise created in another realm fails the check too. Treating those as
19
+ * synchronous ends the span before the work runs, which produces a short
20
+ * parent span with its real children orphaned outside it.
21
+ */
22
+ function promiseFromThenable(value) {
23
+ if (typeof value !== "object" && typeof value !== "function" || value === null) return;
24
+ let then;
25
+ try {
26
+ then = value.then;
27
+ } catch (error) {
28
+ return Promise.reject(error);
29
+ }
30
+ if (typeof then !== "function") return void 0;
31
+ return new Promise((resolve, reject) => {
32
+ try {
33
+ then.call(value, resolve, reject);
34
+ } catch (error) {
35
+ reject(error);
36
+ }
37
+ });
38
+ }
39
+
40
+ //#endregion
12
41
  //#region src/variable-name-inference.ts
13
42
  /**
14
43
  * Variable Name Inference Utility
@@ -314,9 +343,11 @@ function runWithTraceContextStorage(fn) {
314
343
  if (storage.getStore()) return fn();
315
344
  return storage.run({ value: context.active() }, fn);
316
345
  }
346
+ /** Used when neither the wrapper nor `init()` chose a sampler. */
347
+ const FALLBACK_SAMPLER = new AlwaysSampler();
317
348
  function wrapWithTracingSync(fnFactory, options, variableName) {
318
349
  const { tracer, meter } = getConfig();
319
- const sampler = options.sampler || new AlwaysSampler();
350
+ const resolveSampler = () => options.sampler ?? getConfiguredSampler() ?? FALLBACK_SAMPLER;
320
351
  const spanName = getSpanName(options, fnFactory, variableName);
321
352
  const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, {
322
353
  description: `Call count for ${spanName}`,
@@ -332,10 +363,14 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
332
363
  args,
333
364
  metadata: {}
334
365
  };
366
+ const sampler = resolveSampler();
335
367
  const shouldSample = sampler.shouldSample(samplingContext);
336
368
  const sampleRate = sampler.sampleRate?.(samplingContext);
337
369
  const needsTailSampling = sampler.needsTailSampling?.() ?? false;
338
- if (!shouldSample && !needsTailSampling) return fnFactory(createDummyCtx()).call(this, ...args);
370
+ if (!shouldSample && !needsTailSampling) {
371
+ const unsampled = fnFactory(createDummyCtx()).call(this, ...args);
372
+ return promiseFromThenable(unsampled) ?? unsampled;
373
+ }
339
374
  const startTime = performance.now();
340
375
  const flushRootTelemetry = createRootTelemetryFlusher(options, options.startNewRoot || trace.getActiveSpan() === void 0);
341
376
  const spanOptions = {};
@@ -351,7 +386,7 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
351
386
  ...options.attributesFromArgs?.(args)
352
387
  };
353
388
  const handleTailSampling = (success, duration, error) => {
354
- if (needsTailSampling && sampler.shouldKeepTrace) {
389
+ if (needsTailSampling && sampler.shouldKeepTrace && !isForceKept(span) && !debugCaptureRequested()) {
355
390
  shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, {
356
391
  success,
357
392
  duration,
@@ -406,7 +441,8 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
406
441
  status: "started"
407
442
  });
408
443
  const result = context.with(trace.setSpan(getActiveContextWithBaggage(), span), () => fn.call(this, ...args));
409
- if (result instanceof Promise) return result.then(async (value) => {
444
+ const resultPromise = promiseFromThenable(result);
445
+ if (resultPromise) return resultPromise.then(async (value) => {
410
446
  const completed = onSuccess(value);
411
447
  await flushRootTelemetry();
412
448
  return completed;
@@ -656,7 +692,8 @@ function span(nameOrOptions, fn) {
656
692
  try {
657
693
  if (attributes) for (const [key, value] of Object.entries(attributes)) span.setAttribute(key, value);
658
694
  const result = fn(span);
659
- if (result instanceof Promise) return result.then((resolved) => {
695
+ const resultPromise = promiseFromThenable(result);
696
+ if (resultPromise) return resultPromise.then((resolved) => {
660
697
  span.setStatus({ code: SpanStatusCode.OK });
661
698
  span.end();
662
699
  return resolved;
@@ -1,5 +1,5 @@
1
1
  import { n as TraceContext } from "./trace-context-C1Cn1q0y.cjs";
2
- import { p as Sampler } from "./sampling-CmRdnXUi.cjs";
2
+ import { m as Sampler } from "./sampling-Er217Mge.cjs";
3
3
  import { Attributes, Span, SpanKind } from "@opentelemetry/api";
4
4
  //#region src/functional-wrapper.d.ts
5
5
  type WrappedFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn | Promise<TReturn>;
@@ -192,7 +192,9 @@ declare const ctx: TraceContext;
192
192
  * })
193
193
  * ```
194
194
  */
195
+ declare function traceImpl<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => PromiseLike<TReturn>): (...args: TArgs) => Promise<TReturn>;
195
196
  declare function traceImpl<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
197
+ declare function traceImpl<TArgs extends unknown[], TReturn>(name: string, fn: (...args: TArgs) => PromiseLike<TReturn>): (...args: TArgs) => Promise<TReturn>;
196
198
  declare function traceImpl<TArgs extends unknown[], TReturn>(name: string, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
197
199
  declare function traceImpl<TArgs extends unknown[], TReturn>(options: TracingOptions<TArgs, TReturn>, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
198
200
  declare function traceImpl(name: string): <TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn) => (...args: TArgs) => TReturn;
@@ -258,7 +260,7 @@ declare const trace$1: typeof traceImpl & {
258
260
  * )(ctx => async (data) => { })
259
261
  * ```
260
262
  */
261
- declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn = unknown>(options?: TracingOptions<TCfgArgs, TCfgReturn>): <TArgs extends TCfgArgs, TReturn extends TCfgReturn>(fnFactory: (ctx: TraceContext) => (...args: TArgs) => TReturn | Promise<TReturn>) => WrappedFunction<TArgs, TReturn>;
263
+ declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn = unknown>(options?: TracingOptions<TCfgArgs, TCfgReturn>): <TArgs extends TCfgArgs, TReturn extends TCfgReturn>(fnFactory: (ctx: TraceContext) => (...args: TArgs) => TReturn | PromiseLike<TReturn>) => WrappedFunction<TArgs, TReturn>;
262
264
  /**
263
265
  * Approach 3: instrument() - Batch auto-instrumentation
264
266
  *
@@ -309,8 +311,13 @@ declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn
309
311
  * })
310
312
  * ```
311
313
  */
314
+ type InstrumentedFunction<TFunction extends AnyInstrumentable> = TFunction extends ((...args: infer TArgs) => infer TReturn) ? (...args: TArgs) => TReturn extends PromiseLike<infer TValue> ? Promise<TValue> : TReturn : never;
315
+ type InstrumentedFunctions<T extends Record<string, AnyInstrumentable>> = { [TKey in keyof T]: InstrumentedFunction<T[TKey]>; };
316
+ declare function instrument<TFunction extends AnyInstrumentable>(options: SingleInstrumentOptions<TFunction> & {
317
+ fn: TFunction & ((...args: Parameters<TFunction>) => PromiseLike<unknown>);
318
+ }): (...args: Parameters<TFunction>) => Promise<Awaited<ReturnType<TFunction>>>;
312
319
  declare function instrument<TFunction extends AnyInstrumentable>(options: SingleInstrumentOptions<TFunction>): TFunction;
313
- declare function instrument<T extends Record<string, AnyInstrumentable>>(options: InstrumentOptions<T>): T;
320
+ declare function instrument<T extends Record<string, AnyInstrumentable>>(options: InstrumentOptions<T>): InstrumentedFunctions<T>;
314
321
  /**
315
322
  * Options for span() function
316
323
  */
@@ -352,10 +359,10 @@ interface SpanOptions {
352
359
  * })
353
360
  * ```
354
361
  */
362
+ declare function span<T = unknown>(name: string, fn: (span: Span) => PromiseLike<T>): Promise<T>;
363
+ declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => PromiseLike<T>): Promise<T>;
355
364
  declare function span<T = unknown>(name: string, fn: (span: Span) => T): T;
356
- declare function span<T = unknown>(name: string, fn: (span: Span) => Promise<T>): Promise<T>;
357
365
  declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => T): T;
358
- declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => Promise<T>): Promise<T>;
359
366
  /**
360
367
  * Options for withNewContext() function
361
368
  */
@@ -2,8 +2,7 @@ const require_rolldown_runtime = require('./rolldown-runtime-C_NdSu1c.cjs');
2
2
  const require_values = require('./values-DApyq53y.cjs');
3
3
  const require_tracer_provider = require('./tracer-provider.cjs');
4
4
  const require_config = require('./config.cjs');
5
- const require_sampling = require('./sampling.cjs');
6
- const require_init = require('./init-C5aHRqi2.cjs');
5
+ const require_init = require('./init-DmM7dprX.cjs');
7
6
  const require_evidence = require('./evidence.cjs');
8
7
  const require_trace_helpers = require('./trace-helpers.cjs');
9
8
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -12,6 +11,36 @@ node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
12
11
  let node_url = require("node:url");
13
12
  node_url = require_rolldown_runtime.__toESM(node_url, 1);
14
13
 
14
+ //#region src/is-thenable.ts
15
+ /**
16
+ * Convert anything `await` would wait on into a native Promise, or return
17
+ * `undefined` for a synchronous value.
18
+ *
19
+ * `instanceof Promise` is not enough. ORM query builders (drizzle, knex,
20
+ * Prisma, Sequelize) are lazy thenables that are not native Promises, and a
21
+ * promise created in another realm fails the check too. Treating those as
22
+ * synchronous ends the span before the work runs, which produces a short
23
+ * parent span with its real children orphaned outside it.
24
+ */
25
+ function promiseFromThenable(value) {
26
+ if (typeof value !== "object" && typeof value !== "function" || value === null) return;
27
+ let then;
28
+ try {
29
+ then = value.then;
30
+ } catch (error) {
31
+ return Promise.reject(error);
32
+ }
33
+ if (typeof then !== "function") return void 0;
34
+ return new Promise((resolve, reject) => {
35
+ try {
36
+ then.call(value, resolve, reject);
37
+ } catch (error) {
38
+ reject(error);
39
+ }
40
+ });
41
+ }
42
+
43
+ //#endregion
15
44
  //#region src/variable-name-inference.ts
16
45
  /**
17
46
  * Variable Name Inference Utility
@@ -317,9 +346,11 @@ function runWithTraceContextStorage(fn) {
317
346
  if (storage.getStore()) return fn();
318
347
  return storage.run({ value: _opentelemetry_api.context.active() }, fn);
319
348
  }
349
+ /** Used when neither the wrapper nor `init()` chose a sampler. */
350
+ const FALLBACK_SAMPLER = new require_init.AlwaysSampler();
320
351
  function wrapWithTracingSync(fnFactory, options, variableName) {
321
352
  const { tracer, meter } = require_config.getConfig();
322
- const sampler = options.sampler || new require_sampling.AlwaysSampler();
353
+ const resolveSampler = () => options.sampler ?? require_init.getConfiguredSampler() ?? FALLBACK_SAMPLER;
323
354
  const spanName = getSpanName(options, fnFactory, variableName);
324
355
  const callCounter = options.withMetrics ? meter.createCounter(`${spanName}.calls`, {
325
356
  description: `Call count for ${spanName}`,
@@ -335,10 +366,14 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
335
366
  args,
336
367
  metadata: {}
337
368
  };
369
+ const sampler = resolveSampler();
338
370
  const shouldSample = sampler.shouldSample(samplingContext);
339
371
  const sampleRate = sampler.sampleRate?.(samplingContext);
340
372
  const needsTailSampling = sampler.needsTailSampling?.() ?? false;
341
- if (!shouldSample && !needsTailSampling) return fnFactory(createDummyCtx()).call(this, ...args);
373
+ if (!shouldSample && !needsTailSampling) {
374
+ const unsampled = fnFactory(createDummyCtx()).call(this, ...args);
375
+ return promiseFromThenable(unsampled) ?? unsampled;
376
+ }
342
377
  const startTime = performance.now();
343
378
  const flushRootTelemetry = createRootTelemetryFlusher(options, options.startNewRoot || _opentelemetry_api.trace.getActiveSpan() === void 0);
344
379
  const spanOptions = {};
@@ -347,21 +382,21 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
347
382
  return tracer.startActiveSpan(spanName, spanOptions, require_init.getActiveContextWithBaggage(), (span) => require_init.runInOperationContext(spanName, () => runWithTraceContextStorage(() => {
348
383
  let shouldKeepSpan = true;
349
384
  require_trace_helpers.setSpanName(span, spanName);
350
- if (sampleRate !== void 0 && sampleRate > 1) span.setAttribute(require_sampling.AUTOTEL_SAMPLING_RATE, sampleRate);
385
+ if (sampleRate !== void 0 && sampleRate > 1) span.setAttribute(require_init.AUTOTEL_SAMPLING_RATE, sampleRate);
351
386
  const fn = fnFactory(require_init.createTraceContext(span));
352
387
  const argsAttributes = {
353
388
  ...captureInputAttrs(args, options.captureInput),
354
389
  ...options.attributesFromArgs?.(args)
355
390
  };
356
391
  const handleTailSampling = (success, duration, error) => {
357
- if (needsTailSampling && sampler.shouldKeepTrace) {
392
+ if (needsTailSampling && sampler.shouldKeepTrace && !require_init.isForceKept(span) && !require_init.debugCaptureRequested()) {
358
393
  shouldKeepSpan = sampler.shouldKeepTrace(samplingContext, {
359
394
  success,
360
395
  duration,
361
396
  error
362
397
  });
363
- span.setAttribute(require_sampling.AUTOTEL_SAMPLING_TAIL_KEEP, shouldKeepSpan);
364
- span.setAttribute(require_sampling.AUTOTEL_SAMPLING_TAIL_EVALUATED, true);
398
+ span.setAttribute(require_init.AUTOTEL_SAMPLING_TAIL_KEEP, shouldKeepSpan);
399
+ span.setAttribute(require_init.AUTOTEL_SAMPLING_TAIL_EVALUATED, true);
365
400
  }
366
401
  };
367
402
  const onSuccess = (result) => {
@@ -409,7 +444,8 @@ function wrapWithTracingSync(fnFactory, options, variableName) {
409
444
  status: "started"
410
445
  });
411
446
  const result = _opentelemetry_api.context.with(_opentelemetry_api.trace.setSpan(require_init.getActiveContextWithBaggage(), span), () => fn.call(this, ...args));
412
- if (result instanceof Promise) return result.then(async (value) => {
447
+ const resultPromise = promiseFromThenable(result);
448
+ if (resultPromise) return resultPromise.then(async (value) => {
413
449
  const completed = onSuccess(value);
414
450
  await flushRootTelemetry();
415
451
  return completed;
@@ -659,7 +695,8 @@ function span(nameOrOptions, fn) {
659
695
  try {
660
696
  if (attributes) for (const [key, value] of Object.entries(attributes)) span.setAttribute(key, value);
661
697
  const result = fn(span);
662
- if (result instanceof Promise) return result.then((resolved) => {
698
+ const resultPromise = promiseFromThenable(result);
699
+ if (resultPromise) return resultPromise.then((resolved) => {
663
700
  span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
664
701
  span.end();
665
702
  return resolved;
@@ -1,5 +1,5 @@
1
1
  import { n as TraceContext } from "./trace-context-f89xPJbK.js";
2
- import { p as Sampler } from "./sampling-CM5S2Lp3.js";
2
+ import { m as Sampler } from "./sampling-DBng4Foa.js";
3
3
  import { Attributes, Span, SpanKind } from "@opentelemetry/api";
4
4
  //#region src/functional-wrapper.d.ts
5
5
  type WrappedFunction<TArgs extends unknown[], TReturn> = (...args: TArgs) => TReturn | Promise<TReturn>;
@@ -192,7 +192,9 @@ declare const ctx: TraceContext;
192
192
  * })
193
193
  * ```
194
194
  */
195
+ declare function traceImpl<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => PromiseLike<TReturn>): (...args: TArgs) => Promise<TReturn>;
195
196
  declare function traceImpl<TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
197
+ declare function traceImpl<TArgs extends unknown[], TReturn>(name: string, fn: (...args: TArgs) => PromiseLike<TReturn>): (...args: TArgs) => Promise<TReturn>;
196
198
  declare function traceImpl<TArgs extends unknown[], TReturn>(name: string, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
197
199
  declare function traceImpl<TArgs extends unknown[], TReturn>(options: TracingOptions<TArgs, TReturn>, fn: (...args: TArgs) => TReturn): (...args: TArgs) => TReturn;
198
200
  declare function traceImpl(name: string): <TArgs extends unknown[], TReturn>(fn: (...args: TArgs) => TReturn) => (...args: TArgs) => TReturn;
@@ -258,7 +260,7 @@ declare const trace$1: typeof traceImpl & {
258
260
  * )(ctx => async (data) => { })
259
261
  * ```
260
262
  */
261
- declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn = unknown>(options?: TracingOptions<TCfgArgs, TCfgReturn>): <TArgs extends TCfgArgs, TReturn extends TCfgReturn>(fnFactory: (ctx: TraceContext) => (...args: TArgs) => TReturn | Promise<TReturn>) => WrappedFunction<TArgs, TReturn>;
263
+ declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn = unknown>(options?: TracingOptions<TCfgArgs, TCfgReturn>): <TArgs extends TCfgArgs, TReturn extends TCfgReturn>(fnFactory: (ctx: TraceContext) => (...args: TArgs) => TReturn | PromiseLike<TReturn>) => WrappedFunction<TArgs, TReturn>;
262
264
  /**
263
265
  * Approach 3: instrument() - Batch auto-instrumentation
264
266
  *
@@ -309,8 +311,13 @@ declare function withTracing<TCfgArgs extends unknown[] = unknown[], TCfgReturn
309
311
  * })
310
312
  * ```
311
313
  */
314
+ type InstrumentedFunction<TFunction extends AnyInstrumentable> = TFunction extends ((...args: infer TArgs) => infer TReturn) ? (...args: TArgs) => TReturn extends PromiseLike<infer TValue> ? Promise<TValue> : TReturn : never;
315
+ type InstrumentedFunctions<T extends Record<string, AnyInstrumentable>> = { [TKey in keyof T]: InstrumentedFunction<T[TKey]>; };
316
+ declare function instrument<TFunction extends AnyInstrumentable>(options: SingleInstrumentOptions<TFunction> & {
317
+ fn: TFunction & ((...args: Parameters<TFunction>) => PromiseLike<unknown>);
318
+ }): (...args: Parameters<TFunction>) => Promise<Awaited<ReturnType<TFunction>>>;
312
319
  declare function instrument<TFunction extends AnyInstrumentable>(options: SingleInstrumentOptions<TFunction>): TFunction;
313
- declare function instrument<T extends Record<string, AnyInstrumentable>>(options: InstrumentOptions<T>): T;
320
+ declare function instrument<T extends Record<string, AnyInstrumentable>>(options: InstrumentOptions<T>): InstrumentedFunctions<T>;
314
321
  /**
315
322
  * Options for span() function
316
323
  */
@@ -352,10 +359,10 @@ interface SpanOptions {
352
359
  * })
353
360
  * ```
354
361
  */
362
+ declare function span<T = unknown>(name: string, fn: (span: Span) => PromiseLike<T>): Promise<T>;
363
+ declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => PromiseLike<T>): Promise<T>;
355
364
  declare function span<T = unknown>(name: string, fn: (span: Span) => T): T;
356
- declare function span<T = unknown>(name: string, fn: (span: Span) => Promise<T>): Promise<T>;
357
365
  declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => T): T;
358
- declare function span<T = unknown>(options: SpanOptions, fn: (span: Span) => Promise<T>): Promise<T>;
359
366
  /**
360
367
  * Options for withNewContext() function
361
368
  */
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_functional = require('./functional-BhhP4Er_.cjs');
2
+ const require_functional = require('./functional-DHlD7V0x.cjs');
3
3
 
4
4
  exports.ctx = require_functional.ctx;
5
5
  exports.getActiveTraceContext = require_functional.getActiveTraceContext;
@@ -1,3 +1,3 @@
1
1
  import { n as TraceContext } from "./trace-context-C1Cn1q0y.cjs";
2
- import { a as getActiveTraceContext, c as trace, d as withTracing, f as InstrumentOptions, i as ctx, l as withBaggage, m as TracingOptions, n as WithBaggageOptions, o as instrument, p as SingleInstrumentOptions, r as WithNewContextOptions, s as span, t as SpanOptions, u as withNewContext } from "./functional-CJn2zY4o.cjs";
2
+ import { a as getActiveTraceContext, c as trace, d as withTracing, f as InstrumentOptions, i as ctx, l as withBaggage, m as TracingOptions, n as WithBaggageOptions, o as instrument, p as SingleInstrumentOptions, r as WithNewContextOptions, s as span, t as SpanOptions, u as withNewContext } from "./functional-CgWcRXGX.cjs";
3
3
  export { type InstrumentOptions, type SingleInstrumentOptions, SpanOptions, type TraceContext, type TracingOptions, WithBaggageOptions, WithNewContextOptions, ctx, getActiveTraceContext, instrument, span, trace, withBaggage, withNewContext, withTracing };
@@ -1,3 +1,3 @@
1
1
  import { n as TraceContext } from "./trace-context-f89xPJbK.js";
2
- import { a as getActiveTraceContext, c as trace, d as withTracing, f as InstrumentOptions, i as ctx, l as withBaggage, m as TracingOptions, n as WithBaggageOptions, o as instrument, p as SingleInstrumentOptions, r as WithNewContextOptions, s as span, t as SpanOptions, u as withNewContext } from "./functional-DXy1cHFF.js";
2
+ import { a as getActiveTraceContext, c as trace, d as withTracing, f as InstrumentOptions, i as ctx, l as withBaggage, m as TracingOptions, n as WithBaggageOptions, o as instrument, p as SingleInstrumentOptions, r as WithNewContextOptions, s as span, t as SpanOptions, u as withNewContext } from "./functional-RFFpQzAV.js";
3
3
  export { type InstrumentOptions, type SingleInstrumentOptions, SpanOptions, type TraceContext, type TracingOptions, WithBaggageOptions, WithNewContextOptions, ctx, getActiveTraceContext, instrument, span, trace, withBaggage, withNewContext, withTracing };
@@ -1,3 +1,3 @@
1
- import { a as trace, c as withTracing, i as span, n as getActiveTraceContext, o as withBaggage, r as instrument, s as withNewContext, t as ctx } from "./functional-Cub1ku-C.js";
1
+ import { a as trace, c as withTracing, i as span, n as getActiveTraceContext, o as withBaggage, r as instrument, s as withNewContext, t as ctx } from "./functional-C4js2vTM.js";
2
2
 
3
3
  export { ctx, getActiveTraceContext, instrument, span, trace, withBaggage, withNewContext, withTracing };
package/dist/http.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_values = require('./values-DApyq53y.cjs');
3
3
  const require_config = require('./config.cjs');
4
- const require_init = require('./init-C5aHRqi2.cjs');
4
+ const require_init = require('./init-DmM7dprX.cjs');
5
5
  let _opentelemetry_api = require("@opentelemetry/api");
6
6
 
7
7
  //#region src/http.ts