dd-trace 5.109.0 → 5.110.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 (128) hide show
  1. package/ci/init.js +4 -3
  2. package/index.d.ts +66 -24
  3. package/loader-hook.mjs +88 -2
  4. package/package.json +18 -14
  5. package/packages/datadog-esbuild/index.js +26 -0
  6. package/packages/datadog-esbuild/src/utils.js +46 -3
  7. package/packages/datadog-instrumentations/src/connect.js +4 -3
  8. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  9. package/packages/datadog-instrumentations/src/graphql.js +17 -6
  10. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  11. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  12. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  13. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  14. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  16. package/packages/datadog-instrumentations/src/koa.js +3 -2
  17. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  18. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  19. package/packages/datadog-instrumentations/src/next.js +10 -10
  20. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  21. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  22. package/packages/datadog-instrumentations/src/restify.js +4 -3
  23. package/packages/datadog-instrumentations/src/router.js +18 -5
  24. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  25. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  26. package/packages/datadog-instrumentations/src/vitest.js +264 -7
  27. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  28. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  29. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  30. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  31. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
  32. package/packages/datadog-plugin-http/src/client.js +8 -3
  33. package/packages/datadog-plugin-http2/src/client.js +5 -2
  34. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  35. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  36. package/packages/datadog-plugin-undici/src/index.js +5 -2
  37. package/packages/datadog-plugin-vitest/src/index.js +7 -1
  38. package/packages/datadog-webpack/index.js +17 -1
  39. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  40. package/packages/dd-trace/src/aiguard/sdk.js +13 -1
  41. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  42. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  43. package/packages/dd-trace/src/appsec/index.js +10 -6
  44. package/packages/dd-trace/src/appsec/reporter.js +1 -0
  45. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  46. package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
  47. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  48. package/packages/dd-trace/src/config/defaults.js +22 -3
  49. package/packages/dd-trace/src/config/generated-config-types.d.ts +535 -1
  50. package/packages/dd-trace/src/config/helper.js +133 -33
  51. package/packages/dd-trace/src/config/index.js +4 -1
  52. package/packages/dd-trace/src/config/parsers.js +19 -1
  53. package/packages/dd-trace/src/config/supported-configurations.json +21 -3
  54. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  55. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  56. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  57. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  58. package/packages/dd-trace/src/debugger/index.js +5 -0
  59. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  60. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  61. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  62. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  63. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  64. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  65. package/packages/dd-trace/src/guardrails/index.js +8 -5
  66. package/packages/dd-trace/src/index.js +6 -4
  67. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  68. package/packages/dd-trace/src/llmobs/index.js +16 -5
  69. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  70. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  71. package/packages/dd-trace/src/llmobs/sdk.js +5 -3
  72. package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
  73. package/packages/dd-trace/src/llmobs/tagger.js +55 -0
  74. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  75. package/packages/dd-trace/src/log/index.js +18 -20
  76. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  77. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  78. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  79. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  80. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  81. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  82. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  83. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  84. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  85. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  86. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
  87. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  88. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  89. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  90. package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
  91. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  92. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  93. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  94. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  95. package/packages/dd-trace/src/plugin_manager.js +7 -1
  96. package/packages/dd-trace/src/plugins/ci_plugin.js +9 -1
  97. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  98. package/packages/dd-trace/src/plugins/util/test.js +80 -0
  99. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  100. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  101. package/packages/dd-trace/src/priority_sampler.js +1 -13
  102. package/packages/dd-trace/src/profiling/config.js +129 -198
  103. package/packages/dd-trace/src/profiling/constants.js +4 -7
  104. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  105. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  106. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  107. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  108. package/packages/dd-trace/src/profiling/oom.js +71 -0
  109. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  110. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  111. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  112. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  113. package/packages/dd-trace/src/ritm.js +4 -2
  114. package/packages/dd-trace/src/serverless.js +1 -2
  115. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  116. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  117. package/packages/dd-trace/src/span_format.js +2 -1
  118. package/packages/dd-trace/src/span_processor.js +6 -0
  119. package/packages/dd-trace/src/telemetry/send-data.js +12 -10
  120. package/packages/dd-trace/src/tracer.js +15 -1
  121. package/packages/dd-trace/src/util.js +14 -0
  122. package/register.js +58 -1
  123. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  124. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  125. package/vendor/dist/pprof-format/index.js +1 -1
  126. package/vendor/dist/protobufjs/index.js +1 -1
  127. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  128. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
package/ci/init.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  /* eslint-disable no-console */
4
4
  const tracer = require('../packages/dd-trace')
5
- const { isTrue, isFalse } = require('../packages/dd-trace/src/util')
6
5
  const log = require('../packages/dd-trace/src/log')
7
6
  const { getEnvironmentVariable, getValueFromEnvSources } = require('../packages/dd-trace/src/config/helper')
8
7
 
@@ -43,8 +42,10 @@ const baseOptions = {
43
42
  flushInterval: isJestWorker ? JEST_FLUSH_INTERVAL : DEFAULT_FLUSH_INTERVAL,
44
43
  }
45
44
 
46
- let shouldInit = !isFalse(getValueFromEnvSources('DD_CIVISIBILITY_ENABLED'))
47
- const isAgentlessEnabled = isTrue(getValueFromEnvSources('DD_CIVISIBILITY_AGENTLESS_ENABLED'))
45
+ // skipDefault: CI visibility stays on unless DD_CIVISIBILITY_ENABLED is explicitly false; the
46
+ // registered default (false) would otherwise turn it off whenever the variable is unset.
47
+ let shouldInit = getValueFromEnvSources('DD_CIVISIBILITY_ENABLED', true) !== false
48
+ const isAgentlessEnabled = getValueFromEnvSources('DD_CIVISIBILITY_AGENTLESS_ENABLED')
48
49
 
49
50
  if (!isTestWorker && isPackageManager()) {
50
51
  log.debug('dd-trace is not initialized in a package manager.')
package/index.d.ts CHANGED
@@ -426,7 +426,7 @@ declare namespace tracer {
426
426
  */
427
427
  export interface SamplingRule {
428
428
  /**
429
- * Sampling rate for this rule.
429
+ * Sampling rate for this rule. A range between 0 and 1 representing the percent of traces sampled.
430
430
  */
431
431
  sampleRate: number
432
432
 
@@ -439,6 +439,22 @@ declare namespace tracer {
439
439
  * Operation name on which to apply this rule. The rule will apply to all operation names if not provided.
440
440
  */
441
441
  name?: string | RegExp
442
+
443
+ /**
444
+ * Resource name on which to apply this rule. The rule will apply to all resource names if not provided.
445
+ */
446
+ resource?: string | RegExp
447
+
448
+ /**
449
+ * Span tags on which to apply this rule, keyed by tag name. Each value is a glob pattern or regular
450
+ * expression, and the rule only applies when every entry matches the span's tags.
451
+ */
452
+ tags?: { [key: string]: string | RegExp }
453
+
454
+ /**
455
+ * Maximum number of traces matching this rule to sample per second.
456
+ */
457
+ maxPerSecond?: number
442
458
  }
443
459
 
444
460
  /**
@@ -620,10 +636,10 @@ declare namespace tracer {
620
636
  rateLimit?: number,
621
637
 
622
638
  /**
623
- * Sampling rules to apply to priority sampling. Each rule is a JSON,
624
- * consisting of `service` and `name`, which are regexes to match against
625
- * a trace's `service` and `name`, and a corresponding `sampleRate`. If not
626
- * specified, will defer to global sampling rate for all spans.
639
+ * Sampling rules to apply to priority sampling. Each rule matches against a trace's
640
+ * `service`, `name`, `resource`, and `tags`, and applies the rule's `sampleRate`. Use a
641
+ * `sampleRate` of `0` to drop matching traces (for example to filter out unwanted resources).
642
+ * If not specified, will defer to global sampling rate for all spans.
627
643
  * @default []
628
644
  * @env DD_TRACE_SAMPLING_RULES
629
645
  * Programmatic configuration takes precedence over the environment variables listed above.
@@ -660,25 +676,25 @@ declare namespace tracer {
660
676
  */
661
677
  runtimeMetrics?: boolean | {
662
678
 
663
- /**
679
+ /**
664
680
  * @env DD_RUNTIME_METRICS_ENABLED
665
681
  * Programmatic configuration takes precedence over the environment variables listed above.
666
682
  */
667
683
  enabled?: boolean,
668
684
 
669
- /**
685
+ /**
670
686
  * @env DD_RUNTIME_METRICS_GC_ENABLED
671
687
  * Programmatic configuration takes precedence over the environment variables listed above.
672
688
  */
673
689
  gc?: boolean,
674
690
 
675
- /**
691
+ /**
676
692
  * @env DD_RUNTIME_METRICS_EVENT_LOOP_ENABLED
677
693
  * Programmatic configuration takes precedence over the environment variables listed above.
678
694
  */
679
695
  eventLoop?: boolean,
680
696
 
681
- /**
697
+ /**
682
698
  * Whether to use native metrics. When set to false, forces the JS implementation
683
699
  * @default true
684
700
  * @env DD_RUNTIME_METRICS_NATIVE
@@ -2363,6 +2379,14 @@ declare namespace tracer {
2363
2379
  * [aws-sdk](https://github.com/aws/aws-sdk-js) module.
2364
2380
  */
2365
2381
  interface aws_sdk extends Instrumentation {
2382
+ /**
2383
+ * The service name to be used for this plugin. When a function is used it is called with the AWS
2384
+ * request parameters (e.g. `{ TableName }` for DynamoDB, `{ Bucket }` for S3) and its return value
2385
+ * is used as the service name. Returning a nullish value falls back to the default service name, so
2386
+ * individual resources can be mapped without renaming every call to the service.
2387
+ */
2388
+ service?: string | ((params: anyObject) => string | undefined | null);
2389
+
2366
2390
  /**
2367
2391
  * Whether to inject all messages during batch AWS SQS, Kinesis, and SNS send operations. Normal
2368
2392
  * behavior is to inject the first message in batch send operations.
@@ -2581,24 +2605,24 @@ declare namespace tracer {
2581
2605
  * This plugin automatically instruments the
2582
2606
  * [@google-cloud/vertexai](https://github.com/googleapis/nodejs-vertexai) module.
2583
2607
  */
2584
- interface google_cloud_vertexai extends Integration {}
2608
+ interface google_cloud_vertexai extends Integration {}
2585
2609
 
2586
- /**
2610
+ /**
2587
2611
  * This plugin automatically instruments the
2588
2612
  * [@google-genai](https://github.com/googleapis/js-genai) module.
2589
2613
  */
2590
- interface google_genai extends Integration {}
2591
-
2592
- /** @hidden */
2593
- interface ExecutionArgs {
2594
- schema: any,
2595
- document: any,
2596
- rootValue?: any,
2597
- contextValue?: any,
2598
- variableValues?: any,
2599
- operationName?: string,
2600
- fieldResolver?: any,
2601
- typeResolver?: any,
2614
+ interface google_genai extends Integration {}
2615
+
2616
+ /** @hidden */
2617
+ interface ExecutionArgs {
2618
+ schema: any,
2619
+ document: any,
2620
+ rootValue?: any,
2621
+ contextValue?: any,
2622
+ variableValues?: any,
2623
+ operationName?: string,
2624
+ fieldResolver?: any,
2625
+ typeResolver?: any,
2602
2626
  }
2603
2627
 
2604
2628
  /**
@@ -3217,7 +3241,16 @@ declare namespace tracer {
3217
3241
  * This plugin automatically instruments the
3218
3242
  * [router](https://github.com/pillarjs/router) module.
3219
3243
  */
3220
- interface router extends Integration {}
3244
+ interface router extends Integration {
3245
+ /**
3246
+ * Whether to enable instrumentation of router.middleware spans.
3247
+ * When set to `false`, middleware spans are suppressed but route
3248
+ * tracking (resource name, `http.route` tag) is still performed.
3249
+ *
3250
+ * @default true
3251
+ */
3252
+ middleware?: boolean;
3253
+ }
3221
3254
 
3222
3255
  /**
3223
3256
  * This plugin automatically instruments the
@@ -4201,6 +4234,15 @@ declare namespace tracer {
4201
4234
  * Programmatic configuration takes precedence over the environment variables listed above.
4202
4235
  */
4203
4236
  agentlessEnabled?: boolean,
4237
+
4238
+ /**
4239
+ * The proportion of LLM Observability traces to sample, between `0` and `1` (inclusive).
4240
+ * The decision is computed once per trace, propagated across services, and recorded on every
4241
+ * span; spans are always sent and the decision is honored at ingestion time. Defaults to `1`.
4242
+ * @env DD_LLMOBS_SAMPLE_RATE
4243
+ * Programmatic configuration takes precedence over the environment variables listed above.
4244
+ */
4245
+ sampleRate?: number,
4204
4246
  }
4205
4247
  /** @hidden */
4206
4248
  type spanKind = 'agent' | 'workflow' | 'task' | 'tool' | 'retrieval' | 'embedding' | 'llm'
package/loader-hook.mjs CHANGED
@@ -1,3 +1,9 @@
1
+ /* eslint n/no-unsupported-features/node-builtins: ['error', { ignores: ['module.registerHooks'] }] */
2
+
3
+ import * as Module from 'node:module'
4
+ import { pathToFileURL } from 'node:url'
5
+
6
+ import { createHook, supportsSyncHooks } from 'import-in-the-middle/create-hook.mjs'
1
7
  import { initialize as origInitialize, load as origLoad, resolve } from 'import-in-the-middle/hook.mjs'
2
8
  import regexpEscapeModule from './vendor/dist/escape-string-regexp/index.js'
3
9
  import hooks from './packages/datadog-instrumentations/src/helpers/hooks.js'
@@ -8,11 +14,18 @@ import { isRelativeRequire } from './packages/datadog-instrumentations/src/helpe
8
14
  // This file must support Node.js 12.0.0 syntax
9
15
 
10
16
  const regexpEscape = regexpEscapeModule.default
17
+ const require = Module.createRequire(import.meta.url)
18
+ let syncImportInTheMiddleHook
11
19
 
12
20
  // For some reason `getEnvironmentVariable` is not otherwise available to ESM.
13
21
  const env = configHelper.getEnvironmentVariable
14
22
 
15
23
  function initialize (data = {}) {
24
+ prepareImportInTheMiddleOptions(data)
25
+ return origInitialize(data)
26
+ }
27
+
28
+ function prepareImportInTheMiddleOptions (data = {}) {
16
29
  if (data.include == null) data.include = []
17
30
  if (data.exclude == null) data.exclude = []
18
31
 
@@ -20,13 +33,86 @@ function initialize (data = {}) {
20
33
  addSecurityControls(data)
21
34
  addExclusions(data)
22
35
 
23
- return origInitialize(data)
36
+ return data
24
37
  }
25
38
 
26
39
  function load (url, context, nextLoad) {
27
40
  return rewriterLoader.load(url, context, (url, context) => origLoad(url, context, nextLoad))
28
41
  }
29
42
 
43
+ function loadSync (url, context, nextLoad) {
44
+ if (isCommonJSLoad(context)) {
45
+ return getSyncImportInTheMiddleHook().loadSync(url, context, nextLoad)
46
+ }
47
+
48
+ return rewriterLoader.loadSync(url, context, (url, context) => {
49
+ return getSyncImportInTheMiddleHook().loadSync(url, context, nextLoad)
50
+ })
51
+ }
52
+
53
+ function isCommonJSLoad (context) {
54
+ if (context.format) return context.format === 'commonjs'
55
+
56
+ // Sync hooks report CommonJS require() dependency loads with a `require`
57
+ // condition but no format. If a format is present, trust it instead: ESM
58
+ // loaded through require() reports `format: 'module'` and still needs rewrite.
59
+ const conditions = context.conditions
60
+ if (!conditions) return false
61
+
62
+ for (let i = 0; i < conditions.length; i++) {
63
+ if (conditions[i] === 'require') return true
64
+ }
65
+
66
+ return false
67
+ }
68
+
69
+ function getSyncImportInTheMiddleHook () {
70
+ if (syncImportInTheMiddleHook) {
71
+ return syncImportInTheMiddleHook
72
+ }
73
+
74
+ const importInTheMiddleRegisterHooksUrl = pathToFileURL(
75
+ require.resolve('import-in-the-middle/register-hooks.mjs')
76
+ ).href
77
+ syncImportInTheMiddleHook = createHook({ url: importInTheMiddleRegisterHooksUrl })
78
+ return syncImportInTheMiddleHook
79
+ }
80
+
81
+ function registerSyncLoaderHooks (data = {}) {
82
+ // The synchronous loader strips the source of a require() pulled into the iitm
83
+ // ESM graph so Node loads it natively, but module.registerHooks rejected that
84
+ // nullish CommonJS source until nodejs/node#59929 (released in 22.22.3, 24.11.1,
85
+ // 25.1.0 and 26.0.0). On versions that ship registerHooks but predate the fix,
86
+ // fall back to the asynchronous loader instead of crashing mid-graph.
87
+ if (!supportsSyncHooks()) {
88
+ return false
89
+ }
90
+
91
+ const syncHook = getSyncImportInTheMiddleHook()
92
+
93
+ if (
94
+ typeof Module.registerHooks !== 'function' ||
95
+ typeof syncHook.applyOptions !== 'function' ||
96
+ typeof syncHook.loadSync !== 'function' ||
97
+ typeof syncHook.resolveSync !== 'function'
98
+ ) {
99
+ return false
100
+ }
101
+
102
+ // Node built-ins are instrumented under the synchronous loader as well: iitm
103
+ // reads a built-in's exports through process.getBuiltinModule(), which
104
+ // bypasses the registered hooks and therefore cannot re-enter them. The
105
+ // synchronous and asynchronous loaders share the same option preparation so
106
+ // that `import http from 'node:http'` is wrapped on both paths.
107
+ syncHook.applyOptions(prepareImportInTheMiddleOptions(data))
108
+ Module.registerHooks({
109
+ resolve: syncHook.resolveSync,
110
+ load: loadSync,
111
+ })
112
+
113
+ return true
114
+ }
115
+
30
116
  function addInstrumentations (data) {
31
117
  const instrumentations = Object.keys(hooks)
32
118
 
@@ -74,4 +160,4 @@ export const iitmExclusions = [
74
160
  /@anthropic-ai\/sdk\/_shims/,
75
161
  ]
76
162
 
77
- export { initialize, load, resolve }
163
+ export { initialize, load, registerSyncLoaderHooks, resolve }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.109.0",
3
+ "version": "5.110.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -23,6 +23,7 @@
23
23
  "lint:inspect": "npx @eslint/config-inspector@latest",
24
24
  "lint:codeowners": "codeowners-audit",
25
25
  "lint:codeowners:ci": "codeowners-audit --glob='**/*.spec.js' --glob='benchmark/sirun/**' --glob='.agents/**' --glob='.claude/**'",
26
+ "lint:editorconfig": "editorconfig-checker",
26
27
  "release:proposal": "node scripts/release/proposal",
27
28
  "services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
28
29
  "test": "echo '\nError: The root \"npm test\" command is intentionally disabled.\n\nInstead, run specific test suites:\n - npm run test:trace:core\n - npm run test:appsec\n - etc.\n\nOr run individual test files:\n npx mocha path/to/test.spec.js\n\nSee CONTRIBUTING.md (Testing section) for more details.\n' && exit 1",
@@ -103,6 +104,7 @@
103
104
  "test:integration:plugins:coverage": "yarn services && node ./integration-tests/coverage/run-suite.js \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/${SPEC:-*}*.spec.js\"",
104
105
  "test:unit:plugins": "mocha \"packages/datadog-instrumentations/test/@(${PLUGINS}).spec.js\" \"packages/datadog-plugin-@(${PLUGINS})/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@(${PLUGINS})/test/integration-test/**/*.spec.js\"",
105
106
  "test:release": "mocha \"scripts/release/**/*.spec.js\"",
107
+ "test:scripts": "mocha \"scripts/helpers/**/*.spec.js\"",
106
108
  "test:shimmer": "mocha \"packages/datadog-shimmer/test/**/*.spec.js\"",
107
109
  "test:shimmer:ci": "nyc --silent node init && nyc -- npm run test:shimmer",
108
110
  "verify:workflow-job-names": "node scripts/verify-workflow-job-names.js",
@@ -129,8 +131,9 @@
129
131
  },
130
132
  "homepage": "https://github.com/DataDog/dd-trace-js#readme",
131
133
  "engines": {
132
- "node": ">=18 <27"
134
+ "node": ">=18"
133
135
  },
136
+ "nodeMaxMajor": 27,
134
137
  "files": [
135
138
  "/package.json",
136
139
  "ci/**/*",
@@ -161,7 +164,7 @@
161
164
  ],
162
165
  "dependencies": {
163
166
  "dc-polyfill": "^0.1.11",
164
- "import-in-the-middle": "^3.0.1",
167
+ "import-in-the-middle": "^3.1.0",
165
168
  "opentracing": ">=0.14.7"
166
169
  },
167
170
  "optionalDependencies": {
@@ -170,7 +173,7 @@
170
173
  "@datadog/native-iast-taint-tracking": "4.2.0",
171
174
  "@datadog/native-metrics": "3.1.2",
172
175
  "@datadog/openfeature-node-server": "2.0.0",
173
- "@datadog/pprof": "5.15.0",
176
+ "@datadog/pprof": "5.15.1",
174
177
  "@datadog/wasm-js-rewriter": "5.0.1",
175
178
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
176
179
  "@opentelemetry/api-logs": "<1.0.0",
@@ -179,29 +182,30 @@
179
182
  "devDependencies": {
180
183
  "@actions/core": "^3.0.1",
181
184
  "@actions/github": "^9.1.1",
182
- "@babel/helpers": "^7.29.7",
185
+ "@babel/helpers": "^8.0.0",
183
186
  "@eslint/eslintrc": "^3.3.5",
184
187
  "@eslint/js": "^9.39.2",
185
188
  "@msgpack/msgpack": "^3.1.3",
186
- "@openfeature/core": "^1.10.0",
187
- "@openfeature/server-sdk": "~1.21.0",
189
+ "@openfeature/core": "^1.11.0",
190
+ "@openfeature/server-sdk": "~1.22.0",
188
191
  "@stylistic/eslint-plugin": "^5.10.0",
189
192
  "@types/mocha": "^10.0.10",
190
193
  "@types/node": "^18.19.106",
191
194
  "@types/sinon": "^21.0.1",
192
- "axios": "^1.17.0",
195
+ "axios": "^1.18.0",
193
196
  "benchmark": "^2.1.4",
194
- "body-parser": "^2.2.2",
197
+ "body-parser": "^2.3.0",
195
198
  "bun": "1.3.14",
196
199
  "codeowners-audit": "^2.9.0",
200
+ "editorconfig-checker": "^6.1.1",
197
201
  "eslint": "^9.39.2",
198
202
  "eslint-plugin-cypress": "^6.4.1",
199
203
  "eslint-plugin-import": "^2.32.0",
200
- "eslint-plugin-jsdoc": "^63.0.1",
204
+ "eslint-plugin-jsdoc": "^63.0.6",
201
205
  "eslint-plugin-mocha": "^11.3.0",
202
- "eslint-plugin-n": "^18.0.1",
206
+ "eslint-plugin-n": "^18.1.0",
203
207
  "eslint-plugin-promise": "^7.3.0",
204
- "eslint-plugin-sonarjs": "^4.0.3",
208
+ "eslint-plugin-sonarjs": "^4.1.0",
205
209
  "eslint-plugin-unicorn": "^64.0.0",
206
210
  "express": "^5.1.0",
207
211
  "glob": "^10.4.5",
@@ -214,7 +218,7 @@
214
218
  "mocha": "^11.7.6",
215
219
  "mocha-junit-reporter": "^2.2.1",
216
220
  "mocha-multi-reporters": "^1.5.1",
217
- "multer": "^2.1.1",
221
+ "multer": "^2.2.0",
218
222
  "nock": "^14.0.15",
219
223
  "node-preload": "^0.2.1",
220
224
  "nyc": "^18.0.0",
@@ -223,7 +227,7 @@
223
227
  "proxyquire": "^2.1.3",
224
228
  "retry": "^0.13.1",
225
229
  "semifies": "^1.0.0",
226
- "semver": "^7.8.2",
230
+ "semver": "^7.8.4",
227
231
  "sinon": "^22.0.0",
228
232
  "tiktoken": "^1.0.21",
229
233
  "typescript": "^6.0.3",
@@ -8,6 +8,11 @@ const { pathToFileURL, fileURLToPath } = require('node:url')
8
8
  const instrumentations = require('../datadog-instrumentations/src/helpers/instrumentations')
9
9
  const extractPackageAndModulePath = require('../datadog-instrumentations/src/helpers/extract-package-and-module-path')
10
10
  const hooks = require('../datadog-instrumentations/src/helpers/hooks')
11
+ const {
12
+ OPTIONAL_PEER_FILTER,
13
+ matchesOptionalPeerFile,
14
+ rewriteOptionalPeerLoads,
15
+ } = require('../datadog-instrumentations/src/helpers/optional-peer-bundler')
11
16
  const { processModule, isESMFile } = require('./src/utils')
12
17
  const log = require('./src/log')
13
18
 
@@ -128,6 +133,11 @@ module.exports.setup = function (build) {
128
133
  ${build.initialOptions.banner.js}`
129
134
  }
130
135
 
136
+ // Keep the build from failing on the optional `@openfeature/core` peer of
137
+ // `@openfeature/server-sdk` when it is not installed (#8635). esbuild follows the chain
138
+ // whenever `@openfeature/server-sdk` is reachable -- the app importing it directly, or the
139
+ // bundled provider when the optional peer is present -- so mark `@openfeature/core` external
140
+ // when it is absent rather than erroring at bundle time.
131
141
  try {
132
142
  // eslint-disable-next-line n/no-unpublished-require
133
143
  require.resolve('@openfeature/core')
@@ -172,6 +182,22 @@ ${build.initialOptions.banner.js}`
172
182
  log.warn('No git metadata available - skipping injection')
173
183
  }
174
184
 
185
+ // Rewrite optional-peer loads so installed peers get bundled and survive the bundle being
186
+ // relocated without them on disk (#8980). Registered before the generic onLoad so it wins for
187
+ // these files. Absent peers stay opaque, so a build that does not opt into the feature does not
188
+ // follow their dependency chain (#8635).
189
+ build.onLoad({ filter: OPTIONAL_PEER_FILTER }, args => {
190
+ const normalizedPath = args.path.replaceAll('\\', '/')
191
+ if (!matchesOptionalPeerFile(normalizedPath)) return
192
+
193
+ log.debug('INLINE: optional-peer loader applied to %s', normalizedPath)
194
+ return {
195
+ contents: rewriteOptionalPeerLoads(fs.readFileSync(args.path, 'utf8'), path.dirname(args.path)),
196
+ loader: 'js',
197
+ resolveDir: path.dirname(args.path),
198
+ }
199
+ })
200
+
175
201
  // first time is intercepted, proxy should be created, next time the original should be loaded
176
202
  const interceptedESMModules = new Set()
177
203
 
@@ -6,6 +6,9 @@ const fs = require('node:fs')
6
6
  const path = require('node:path')
7
7
  const { NODE_MAJOR, NODE_MINOR } = require('../../../version.js')
8
8
 
9
+ const LOAD_OPERATION = 0
10
+ const RESOLVE_OPERATION = 1
11
+
9
12
  const getExportsImporting = (url) => import(url).then(Object.keys)
10
13
  let getExportsModulePromise
11
14
 
@@ -19,7 +22,11 @@ const loadGetExportsModule = () => {
19
22
  const getExports = NODE_MAJOR >= 20 || (NODE_MAJOR === 18 && NODE_MINOR >= 19)
20
23
  ? async (srcUrl, context, getSource) => {
21
24
  const mod = await loadGetExportsModule()
22
- return mod.getExports(srcUrl, context, getSource)
25
+ const exportNames = mod.getExports(srcUrl, context, getSource)
26
+ if (exportNames?.next) {
27
+ return driveGetExportsGenerator(exportNames, getSource)
28
+ }
29
+ return exportNames
23
30
  }
24
31
  : getExportsImporting
25
32
 
@@ -78,14 +85,50 @@ function getSource (url, { format }) {
78
85
  }
79
86
  }
80
87
 
88
+ /**
89
+ * Drives the generator returned by import-in-the-middle >=3.1.0 export discovery.
90
+ *
91
+ * @param {Generator<Array, Set<string>>} exportsGenerator Generator returned by getExports
92
+ * @param {(url: URL, context: object) => { source: string, format: string }} getSource
93
+ * Function that loads module source
94
+ * @returns {Set<string>}
95
+ */
96
+ function driveGetExportsGenerator (exportsGenerator, getSource) {
97
+ let next = exportsGenerator.next()
98
+ while (next.done === false) {
99
+ let result
100
+ let error
101
+ let threw = false
102
+
103
+ try {
104
+ const operation = next.value
105
+ const operationType = operation[0]
106
+
107
+ if (operationType === LOAD_OPERATION) {
108
+ result = getSource(operation[1], operation[2])
109
+ } else if (operationType === RESOLVE_OPERATION) {
110
+ result = resolve(operation[1], operation[2])
111
+ } else {
112
+ throw new Error(`Unsupported import-in-the-middle getExports operation: ${operationType}`)
113
+ }
114
+ } catch (err) {
115
+ threw = true
116
+ error = err
117
+ }
118
+
119
+ next = threw ? exportsGenerator.throw(error) : exportsGenerator.next(result)
120
+ }
121
+ return next.value
122
+ }
123
+
81
124
  /**
82
125
  * Generates the pieces of code for the proxy module before the path
83
126
  *
84
127
  * @param {object} moduleData
85
128
  * @param {string} moduleData.path
86
- * @param {boolean} [moduleData.internal]
129
+ * @param {boolean} [moduleData.internal]
87
130
  * @param {object} moduleData.context
88
- * @param {boolean} [moduleData.excludeDefault]
131
+ * @param {boolean} [moduleData.excludeDefault]
89
132
  * @returns {Promise<Map>}
90
133
  */
91
134
  async function processModule ({ path, internal = false, context, excludeDefault = false }) {
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { addHook, channel } = require('./helpers/instrument')
4
+ const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
5
5
 
6
6
  const enterChannel = channel('apm:connect:middleware:enter')
7
7
  const exitChannel = channel('apm:connect:middleware:exit')
@@ -9,6 +9,7 @@ const errorChannel = channel('apm:connect:middleware:error')
9
9
  const nextChannel = channel('apm:connect:middleware:next')
10
10
  const finishChannel = channel('apm:connect:middleware:finish')
11
11
  const handleChannel = channel('apm:connect:request:handle')
12
+ const publishError = createErrorPublisher(errorChannel)
12
13
 
13
14
  function wrapConnect (connect) {
14
15
  if (typeof connect !== 'function') return connect
@@ -78,7 +79,7 @@ function wrapLayerHandle (layer) {
78
79
  try {
79
80
  return original.apply(this, args)
80
81
  } catch (error) {
81
- errorChannel.publish({ req, error })
82
+ publishError({ req, error })
82
83
  nextChannel.publish({ req })
83
84
  finishChannel.publish({ req })
84
85
 
@@ -93,7 +94,7 @@ function wrapNext (req, next) {
93
94
  // Mirror next's name/arity so wrapCallback skips its per-call identity rewrite.
94
95
  return shimmer.wrapCallback(next, original => function next (error) {
95
96
  if (error) {
96
- errorChannel.publish({ req, error })
97
+ publishError({ req, error })
97
98
  }
98
99
 
99
100
  nextChannel.publish({ req })
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { addHook, channel } = require('./helpers/instrument')
4
+ const { addHook, channel, createErrorPublisher } = require('./helpers/instrument')
5
5
 
6
6
  const errorChannel = channel('apm:fastify:middleware:error')
7
+ const publishErrorChannel = createErrorPublisher(errorChannel)
7
8
  const handleChannel = channel('apm:fastify:request:handle')
8
9
  const routeAddedChannel = channel('apm:fastify:route:added')
9
10
  const bodyParserReadCh = channel('datadog:fastify:body-parser:finish')
@@ -303,18 +304,9 @@ function getRouteConfig (request) {
303
304
  return request?.routeOptions?.config
304
305
  }
305
306
 
306
- let publishingError = false
307
-
308
307
  function publishError (ctx) {
309
- // `errorChannel` is public: a subscriber that re-enters the hook pipeline while
310
- // handling the error republishes here and recurses until the stack overflows.
311
- if (ctx.error && !publishingError) {
312
- publishingError = true
313
- try {
314
- errorChannel.publish(ctx)
315
- } finally {
316
- publishingError = false
317
- }
308
+ if (ctx.error) {
309
+ publishErrorChannel(ctx)
318
310
  }
319
311
 
320
312
  return ctx.error
@@ -455,15 +455,26 @@ function finishResolvers ({ fields }) {
455
455
  }
456
456
  }
457
457
 
458
- addHook({ name: '@graphql-tools/executor', versions: ['>=0.0.14'] }, executor => {
459
- // graphql-yoga uses the normalizedExecutor function, so we need to wrap both. There is no risk in wrapping both
460
- // since the functions are closely related, and our wrappedExecute function prevents double calls with the
461
- // contexts.has(contextValue) check.
462
- shimmer.wrap(executor, 'execute', wrapExecute(executor))
463
- shimmer.wrap(executor, 'normalizedExecutor', wrapExecute(executor))
458
+ // The CJS package root re-exports `execute` and `normalizedExecutor` as
459
+ // non-configurable tslib `__exportStar` getters that shimmer cannot rewrite in
460
+ // place, so the inner execute.js hook below carries CJS instead: graphql-yoga
461
+ // (`normalizedExecutor`) and direct callers both funnel through that `execute`.
462
+ // Under iitm (ESM) the namespace is settable and the inner-file hook never
463
+ // matches the `esm/` path, so the root wrap is the only option there.
464
+ addHook({ name: '@graphql-tools/executor', versions: ['>=0.0.14'] }, (executor, _version, isIitm) => {
465
+ /* istanbul ignore if: covered only by the graphql-yoga ESM integration test (subprocess) */
466
+ if (isIitm) {
467
+ shimmer.wrap(executor, 'execute', wrapExecute(executor))
468
+ shimmer.wrap(executor, 'normalizedExecutor', wrapExecute(executor))
469
+ }
464
470
  return executor
465
471
  })
466
472
 
473
+ addHook({ name: '@graphql-tools/executor', file: 'cjs/execution/execute.js', versions: ['>=0.0.14'] }, execute => {
474
+ shimmer.wrap(execute, 'execute', wrapExecute(execute))
475
+ return execute
476
+ })
477
+
467
478
  // TODO(BridgeAR): graphql >=17.0.0-alpha.9 routes execute() through
468
479
  // experimentalExecuteIncrementally(), bypassing this hook. The same
469
480
  // function returns { initialResult, subsequentResults } for @defer /