dd-trace 5.97.0 → 5.99.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 (175) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ext/tags.js +1 -0
  3. package/index.d.ts +35 -3
  4. package/package.json +48 -46
  5. package/packages/datadog-instrumentations/src/crypto.js +45 -0
  6. package/packages/datadog-instrumentations/src/cucumber.js +65 -3
  7. package/packages/datadog-instrumentations/src/cypress-config.js +153 -53
  8. package/packages/datadog-instrumentations/src/dns.js +24 -56
  9. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +74 -0
  11. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +4 -1
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +10 -3
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +59 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +11 -2
  17. package/packages/datadog-instrumentations/src/jest.js +104 -12
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +8 -0
  19. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +7 -0
  20. package/packages/datadog-instrumentations/src/pino.js +4 -28
  21. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +27 -0
  22. package/packages/datadog-instrumentations/src/playwright.js +5 -17
  23. package/packages/datadog-instrumentations/src/redis.js +12 -6
  24. package/packages/datadog-instrumentations/src/stripe.js +38 -24
  25. package/packages/datadog-instrumentations/src/vitest.js +32 -4
  26. package/packages/datadog-instrumentations/src/zlib.js +29 -0
  27. package/packages/datadog-plugin-aws-sdk/src/base.js +2 -3
  28. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  29. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  30. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
  32. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  33. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  34. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +8 -15
  35. package/packages/datadog-plugin-azure-service-bus/src/producer.js +4 -9
  36. package/packages/datadog-plugin-cucumber/src/index.js +8 -2
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +114 -6
  38. package/packages/datadog-plugin-cypress/src/index.js +59 -2
  39. package/packages/datadog-plugin-cypress/src/source-map-utils.js +48 -1
  40. package/packages/datadog-plugin-fs/src/index.js +1 -1
  41. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
  42. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
  43. package/packages/datadog-plugin-http/src/client.js +1 -1
  44. package/packages/datadog-plugin-http/src/server.js +21 -13
  45. package/packages/datadog-plugin-http2/src/client.js +1 -1
  46. package/packages/datadog-plugin-http2/src/server.js +10 -2
  47. package/packages/datadog-plugin-jest/src/index.js +2 -2
  48. package/packages/datadog-plugin-mocha/src/index.js +1 -2
  49. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/index.js +24 -0
  50. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +55 -0
  51. package/packages/datadog-plugin-mongodb-core/src/index.js +4 -9
  52. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  53. package/packages/datadog-plugin-next/src/index.js +8 -2
  54. package/packages/datadog-plugin-pg/src/index.js +1 -1
  55. package/packages/datadog-plugin-playwright/src/index.js +2 -3
  56. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  57. package/packages/datadog-plugin-vitest/src/index.js +14 -6
  58. package/packages/datadog-plugin-ws/src/close.js +3 -1
  59. package/packages/datadog-plugin-ws/src/producer.js +2 -0
  60. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  61. package/packages/dd-trace/src/aiguard/channels.js +8 -0
  62. package/packages/dd-trace/src/aiguard/index.js +7 -3
  63. package/packages/dd-trace/src/aiguard/sdk.js +66 -22
  64. package/packages/dd-trace/src/aiguard/tags.js +1 -0
  65. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
  66. package/packages/dd-trace/src/appsec/blocking.js +62 -34
  67. package/packages/dd-trace/src/appsec/graphql.js +6 -6
  68. package/packages/dd-trace/src/appsec/index.js +9 -11
  69. package/packages/dd-trace/src/appsec/rasp/command_injection.js +4 -5
  70. package/packages/dd-trace/src/appsec/rasp/lfi.js +8 -4
  71. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +5 -10
  72. package/packages/dd-trace/src/appsec/rasp/ssrf.js +5 -6
  73. package/packages/dd-trace/src/appsec/recommended.json +2438 -13
  74. package/packages/dd-trace/src/appsec/reporter.js +6 -5
  75. package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
  76. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
  77. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +6 -10
  78. package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
  79. package/packages/dd-trace/src/appsec/store.js +50 -0
  80. package/packages/dd-trace/src/appsec/waf/index.js +3 -5
  81. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
  82. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
  83. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
  84. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  85. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -4
  86. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -5
  88. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +3 -4
  89. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +6 -6
  90. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -2
  91. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
  92. package/packages/dd-trace/src/config/config-types.d.ts +0 -4
  93. package/packages/dd-trace/src/config/defaults.js +10 -11
  94. package/packages/dd-trace/src/config/generated-config-types.d.ts +14 -8
  95. package/packages/dd-trace/src/config/index.js +49 -32
  96. package/packages/dd-trace/src/config/parsers.js +26 -9
  97. package/packages/dd-trace/src/config/supported-configurations.json +86 -33
  98. package/packages/dd-trace/src/constants.js +1 -0
  99. package/packages/dd-trace/src/debugger/config.js +2 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/send.js +25 -5
  101. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
  102. package/packages/dd-trace/src/encode/0.4.js +11 -11
  103. package/packages/dd-trace/src/encode/span-stats.js +4 -1
  104. package/packages/dd-trace/src/exporters/agent/index.js +0 -1
  105. package/packages/dd-trace/src/exporters/agent/writer.js +1 -2
  106. package/packages/dd-trace/src/exporters/agentless/writer.js +3 -3
  107. package/packages/dd-trace/src/exporters/common/util.js +2 -2
  108. package/packages/dd-trace/src/id.js +2 -0
  109. package/packages/dd-trace/src/index.js +2 -5
  110. package/packages/dd-trace/src/lambda/handler.js +1 -3
  111. package/packages/dd-trace/src/llmobs/plugins/{anthropic.js → anthropic/index.js} +5 -63
  112. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +106 -0
  113. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +3 -2
  114. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +3 -2
  115. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +2 -1
  116. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +0 -49
  117. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +2 -1
  118. package/packages/dd-trace/src/llmobs/plugins/langchain/messages.js +76 -0
  119. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -26
  120. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/index.js +68 -0
  121. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +57 -0
  122. package/packages/dd-trace/src/llmobs/sdk.js +2 -2
  123. package/packages/dd-trace/src/log/index.js +0 -10
  124. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +103 -0
  125. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -0
  126. package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
  127. package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
  128. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  129. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +3 -2
  130. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  131. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +3 -2
  132. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +19 -51
  133. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
  134. package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
  135. package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
  136. package/packages/dd-trace/src/opentelemetry/trace/index.js +70 -0
  137. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +74 -0
  138. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +342 -0
  139. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -11
  140. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -10
  141. package/packages/dd-trace/src/opentracing/span.js +1 -1
  142. package/packages/dd-trace/src/opentracing/tracer.js +17 -5
  143. package/packages/dd-trace/src/plugins/index.js +1 -0
  144. package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
  145. package/packages/dd-trace/src/plugins/plugin.js +6 -11
  146. package/packages/dd-trace/src/plugins/storage.js +2 -2
  147. package/packages/dd-trace/src/plugins/tracing.js +22 -5
  148. package/packages/dd-trace/src/plugins/util/test.js +128 -5
  149. package/packages/dd-trace/src/plugins/util/url.js +2 -1
  150. package/packages/dd-trace/src/plugins/util/web.js +6 -88
  151. package/packages/dd-trace/src/profiling/profiler.js +34 -77
  152. package/packages/dd-trace/src/profiling/profilers/event_plugins/crypto.js +32 -0
  153. package/packages/dd-trace/src/profiling/profilers/event_plugins/zlib.js +19 -0
  154. package/packages/dd-trace/src/profiling/profilers/events.js +35 -0
  155. package/packages/dd-trace/src/proxy.js +3 -4
  156. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -13
  157. package/packages/dd-trace/src/service-naming/index.js +1 -1
  158. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
  159. package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
  160. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
  161. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
  162. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +21 -1
  163. package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
  164. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
  165. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +15 -1
  166. package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
  167. package/packages/dd-trace/src/span_processor.js +1 -2
  168. package/packages/dd-trace/src/span_stats.js +5 -1
  169. package/packages/dd-trace/src/tagger.js +2 -2
  170. package/packages/dd-trace/src/telemetry/send-data.js +5 -7
  171. package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
  172. package/vendor/dist/protobufjs/index.js +1 -1
  173. package/packages/dd-trace/src/log/utils.js +0 -16
  174. package/vendor/dist/ignore/LICENSE +0 -21
  175. package/vendor/dist/ignore/index.js +0 -1
@@ -4,9 +4,21 @@ const fs = require('fs')
4
4
  const os = require('os')
5
5
  const path = require('path')
6
6
  const { pathToFileURL } = require('url')
7
+ const { channel } = require('./helpers/instrument')
7
8
 
8
9
  const DD_CONFIG_WRAPPED = Symbol('dd-trace.cypress.config.wrapped')
9
10
 
11
+ const setupNodeEventsCh = channel('ci:cypress:setup-node-events')
12
+
13
+ // Ensure the cypress plugin is loaded so it can subscribe to our channel.
14
+ // Normally, plugins are loaded when their npm module is required (via addHook),
15
+ // but plain-object configs don't require('cypress'), so the plugin would never
16
+ // be instantiated in the Cypress Config Manager child process.
17
+ const loadCh = channel('dd-trace:instrumentation:load')
18
+ if (loadCh.hasSubscribers) {
19
+ loadCh.publish({ name: 'cypress' })
20
+ }
21
+
10
22
  const noopTask = {
11
23
  'dd:testSuiteStart': () => null,
12
24
  'dd:beforeEach': () => ({}),
@@ -93,8 +105,9 @@ function injectSupportFile (config) {
93
105
 
94
106
  /**
95
107
  * Registers dd-trace's Cypress hooks (before:run, after:spec, after:run, tasks)
96
- * and injects the support file. Handles chaining with user-registered handlers
97
- * for after:spec/after:run so both the user's code and dd-trace's run in sequence.
108
+ * and injects the support file. Communicates with the plugin layer via
109
+ * the `ci:cypress:setup-node-events` diagnostic channel, avoiding direct
110
+ * tracer references in the instrumentation layer.
98
111
  *
99
112
  * @param {Function} on Cypress event registration function
100
113
  * @param {object} config Cypress resolved config object
@@ -111,8 +124,6 @@ function registerDdTraceHooks (on, config, userAfterSpecHandlers, userAfterRunHa
111
124
  }
112
125
  }
113
126
 
114
- const tracer = global._ddtrace
115
-
116
127
  const registerAfterRunWithCleanup = () => {
117
128
  on('after:run', (results) => {
118
129
  const chain = userAfterRunHandlers.reduce(
@@ -129,49 +140,32 @@ function registerDdTraceHooks (on, config, userAfterSpecHandlers, userAfterRunHa
129
140
  on('task', noopTask)
130
141
  }
131
142
 
132
- if (!tracer || !tracer._initialized) {
143
+ if (!setupNodeEventsCh.hasSubscribers) {
133
144
  registerNoopHandlers()
134
145
  return config
135
146
  }
136
147
 
137
- const NoopTracer = require('../../../packages/dd-trace/src/noop/tracer')
138
-
139
- if (tracer._tracer instanceof NoopTracer) {
140
- registerNoopHandlers()
141
- return config
148
+ // Publish to the plugin layer via diagnostic channel.
149
+ // The subscriber sets `payload.registered = true` and optionally
150
+ // `payload.configPromise` when it handles the event.
151
+ const payload = {
152
+ on,
153
+ config,
154
+ userAfterSpecHandlers,
155
+ userAfterRunHandlers,
156
+ cleanupWrapper,
157
+ registered: false,
158
+ configPromise: undefined,
142
159
  }
143
160
 
144
- const cypressPlugin = require('../../../packages/datadog-plugin-cypress/src/cypress-plugin')
161
+ setupNodeEventsCh.publish(payload)
145
162
 
146
- if (cypressPlugin._isInit) {
147
- for (const h of userAfterSpecHandlers) on('after:spec', h)
148
- registerAfterRunWithCleanup()
163
+ if (!payload.registered) {
164
+ registerNoopHandlers()
149
165
  return config
150
166
  }
151
167
 
152
- on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin))
153
-
154
- on('after:spec', (spec, results) => {
155
- const chain = userAfterSpecHandlers.reduce(
156
- (p, h) => p.then(() => h(spec, results)),
157
- Promise.resolve()
158
- )
159
- return chain.then(() => cypressPlugin.afterSpec(spec, results))
160
- })
161
-
162
- on('after:run', (results) => {
163
- const chain = userAfterRunHandlers.reduce(
164
- (p, h) => p.then(() => h(results)),
165
- Promise.resolve()
166
- )
167
- return chain
168
- .then(() => cypressPlugin.afterRun(results))
169
- .finally(cleanupWrapper)
170
- })
171
-
172
- on('task', cypressPlugin.getTasks())
173
-
174
- return Promise.resolve(cypressPlugin.init(tracer, config)).then(() => config)
168
+ return payload.configPromise || config
175
169
  }
176
170
 
177
171
  /**
@@ -235,32 +229,139 @@ function wrapConfig (config) {
235
229
  return config
236
230
  }
237
231
 
232
+ /**
233
+ * Returns `true` if the nearest package.json walking up from `filePath`
234
+ * sets `"type": "module"`. Used to decide whether ambiguous extensions
235
+ * (`.js`, `.ts`) are loaded as ESM or CJS.
236
+ *
237
+ * @param {string} filePath absolute path to a file under the project
238
+ * @returns {boolean}
239
+ */
240
+ function isUnderEsmPackage (filePath) {
241
+ let dir = path.dirname(filePath)
242
+ while (true) {
243
+ const candidate = path.join(dir, 'package.json')
244
+ try {
245
+ const pkg = JSON.parse(fs.readFileSync(candidate, 'utf8'))
246
+ return pkg && pkg.type === 'module'
247
+ } catch { /* no package.json at this level */ }
248
+ const parent = path.dirname(dir)
249
+ if (parent === dir) return false
250
+ dir = parent
251
+ }
252
+ }
253
+
238
254
  /**
239
255
  * @param {string} originalConfigFile absolute path to the original config file
240
256
  * @returns {string} path to the generated wrapper file
241
257
  */
242
258
  function createConfigWrapper (originalConfigFile) {
259
+ // Decide the wrapper's module mode (ESM vs CJS). It must match how
260
+ // Cypress would interpret the user's original config so that (1) Cypress
261
+ // keeps the loader it would have used (notably the ts-node registration
262
+ // for `.ts` configs), and (2) the wrapper body parses in that mode.
263
+ const originalExt = path.extname(originalConfigFile)
264
+ const isEsm = originalExt === '.mjs' || originalExt === '.mts' ||
265
+ (originalExt !== '.cjs' && originalExt !== '.cts' && isUnderEsmPackage(originalConfigFile))
266
+
267
+ // Preserve `.ts`/`.cts`/`.mts` so Cypress keeps ts-node registered for
268
+ // the wrapper. For plain JS originals, pick the extension that encodes
269
+ // the chosen module mode directly.
270
+ let wrapperExt
271
+ if (originalExt === '.ts' || originalExt === '.cts' || originalExt === '.mts') {
272
+ wrapperExt = originalExt
273
+ } else {
274
+ wrapperExt = isEsm ? '.mjs' : '.cjs'
275
+ }
276
+
243
277
  const wrapperFile = path.join(
244
278
  path.dirname(originalConfigFile),
245
- `.dd-cypress-config-${process.pid}.mjs`
279
+ `.dd-cypress-config-${process.pid}${wrapperExt}`
246
280
  )
247
281
 
248
282
  const cypressConfigPath = require.resolve('./cypress-config')
249
283
 
250
- // Always use ESM: it can import both CJS and ESM configs, so it works
251
- // regardless of the original file's extension or "type": "module" in package.json.
252
- // Import cypress-config.js directly (CJS default = module.exports object).
253
- fs.writeFileSync(wrapperFile, [
254
- `import originalConfig from ${JSON.stringify(pathToFileURL(originalConfigFile).href)}`,
255
- `import cypressConfig from ${JSON.stringify(pathToFileURL(cypressConfigPath).href)}`,
256
- '',
257
- 'export default cypressConfig.wrapConfig(originalConfig)',
258
- '',
259
- ].join('\n'))
260
-
284
+ // ESM body: `import` default-interops a CJS module (cypress-config.js)
285
+ // by exposing its `module.exports` as the default binding, and handles
286
+ // both CJS and ESM user configs transparently.
287
+ // CJS body: avoids top-level `import` — older Cypress transpiles `.ts`
288
+ // configs through CJS ts-node, where `require('file://...')` is not
289
+ // supported. Guards against ES-module-default shape so TS-authored
290
+ // configs using `export default` still work.
291
+ const body = isEsm
292
+ ? [
293
+ `import originalConfig from ${JSON.stringify(pathToFileURL(originalConfigFile).href)}`,
294
+ `import cypressConfig from ${JSON.stringify(pathToFileURL(cypressConfigPath).href)}`,
295
+ '',
296
+ 'export default cypressConfig.wrapConfig(originalConfig)',
297
+ '',
298
+ ].join('\n')
299
+ : [
300
+ `const cypressConfig = require(${JSON.stringify(cypressConfigPath)})`,
301
+ `const originalExports = require(${JSON.stringify(originalConfigFile)})`,
302
+ 'const originalConfig = originalExports && originalExports.__esModule',
303
+ ' ? originalExports.default',
304
+ ' : originalExports',
305
+ 'module.exports = cypressConfig.wrapConfig(originalConfig)',
306
+ '',
307
+ ].join('\n')
308
+
309
+ fs.writeFileSync(wrapperFile, body)
261
310
  return wrapperFile
262
311
  }
263
312
 
313
+ /**
314
+ * @param {string} projectRoot
315
+ * @returns {boolean}
316
+ */
317
+ function isTypeScript6OrNewer (projectRoot) {
318
+ try {
319
+ // eslint-disable-next-line n/no-unpublished-require
320
+ const packageJsonPath = require.resolve('typescript/package.json', { paths: [projectRoot] })
321
+ const { version } = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
322
+ const major = Number(String(version).split('.')[0])
323
+ return major >= 6
324
+ } catch {
325
+ return false
326
+ }
327
+ }
328
+
329
+ /**
330
+ * @param {string} projectRoot
331
+ * @param {string} configFilePath
332
+ * @returns {() => void}
333
+ */
334
+ function configureTsNodeForTypeScript6 (projectRoot, configFilePath) {
335
+ const configExt = path.extname(configFilePath)
336
+ if (configExt !== '.ts' && configExt !== '.cts' && configExt !== '.mts') return () => {}
337
+ if (!isTypeScript6OrNewer(projectRoot)) return () => {}
338
+
339
+ /* eslint-disable eslint-rules/eslint-process-env */
340
+ const previousCompilerOptions = process.env.TS_NODE_COMPILER_OPTIONS
341
+ let compilerOptions = {}
342
+ if (previousCompilerOptions) {
343
+ try {
344
+ compilerOptions = JSON.parse(previousCompilerOptions)
345
+ } catch {
346
+ compilerOptions = {}
347
+ }
348
+ }
349
+
350
+ process.env.TS_NODE_COMPILER_OPTIONS = JSON.stringify({
351
+ ...compilerOptions,
352
+ ignoreDeprecations: '6.0',
353
+ })
354
+
355
+ return () => {
356
+ if (previousCompilerOptions === undefined) {
357
+ delete process.env.TS_NODE_COMPILER_OPTIONS
358
+ } else {
359
+ process.env.TS_NODE_COMPILER_OPTIONS = previousCompilerOptions
360
+ }
361
+ }
362
+ /* eslint-enable eslint-rules/eslint-process-env */
363
+ }
364
+
264
365
  /**
265
366
  * Wraps the Cypress config file for a CLI start() call. When an explicit
266
367
  * configFile is provided, creates a temp wrapper that imports the original
@@ -297,17 +398,16 @@ function wrapCliConfigFileOptions (options) {
297
398
  }
298
399
  }
299
400
 
300
- // Skip .ts files — Cypress transpiles them internally via its own loader.
301
- // The ESM wrapper can't import .ts directly. The defineConfig shimmer
302
- // handles .ts configs since they're transpiled to CJS by Cypress.
303
- if (!configFilePath || !fs.existsSync(configFilePath) || path.extname(configFilePath) === '.ts') return noop
401
+ if (!configFilePath || !fs.existsSync(configFilePath)) return noop
304
402
 
305
403
  try {
306
404
  const wrapperFile = createConfigWrapper(configFilePath)
405
+ const restoreTsNodeCompilerOptions = configureTsNodeForTypeScript6(projectRoot, configFilePath)
307
406
 
308
407
  return {
309
408
  options: { ...options, configFile: wrapperFile },
310
409
  cleanup: () => {
410
+ restoreTsNodeCompilerOptions()
311
411
  try { fs.unlinkSync(wrapperFile) } catch { /* best effort */ }
312
412
  },
313
413
  }
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { channel, addHook } = require('./helpers/instrument')
4
+ const { addHook } = require('./helpers/instrument')
5
+ const { createCallbackInstrumentor } = require('./helpers/callback-instrumentor')
5
6
 
6
7
  const rrtypes = {
7
8
  resolveAny: 'ANY',
@@ -20,78 +21,45 @@ const rrtypes = {
20
21
  const rrtypeMap = new WeakMap()
21
22
 
22
23
  addHook({ name: 'dns' }, dns => {
23
- shimmer.wrap(dns, 'lookup', fn => wrap('apm:dns:lookup', fn, 2))
24
- shimmer.wrap(dns, 'lookupService', fn => wrap('apm:dns:lookup_service', fn, 2))
25
- shimmer.wrap(dns, 'resolve', fn => wrap('apm:dns:resolve', fn, 2))
26
- shimmer.wrap(dns, 'reverse', fn => wrap('apm:dns:reverse', fn, 2))
24
+ const lookup = createCallbackInstrumentor('apm:dns:lookup', { captureResult: true })
25
+ const lookupService = createCallbackInstrumentor('apm:dns:lookup_service', { captureResult: true })
26
+ const resolve = createCallbackInstrumentor('apm:dns:resolve', { captureResult: true })
27
+ const reverse = createCallbackInstrumentor('apm:dns:reverse', { captureResult: true })
27
28
 
28
- patchResolveShorthands(dns)
29
+ shimmer.wrap(dns, 'lookup', lookup(buildArgsContext()))
30
+ shimmer.wrap(dns, 'lookupService', lookupService(buildArgsContext()))
31
+ shimmer.wrap(dns, 'resolve', resolve(buildArgsContext()))
32
+ shimmer.wrap(dns, 'reverse', reverse(buildArgsContext()))
33
+
34
+ patchResolveShorthands(dns, resolve)
29
35
 
30
36
  if (dns.Resolver) {
31
- shimmer.wrap(dns.Resolver.prototype, 'resolve', fn => wrap('apm:dns:resolve', fn, 2))
32
- shimmer.wrap(dns.Resolver.prototype, 'reverse', fn => wrap('apm:dns:reverse', fn, 2))
37
+ shimmer.wrap(dns.Resolver.prototype, 'resolve', resolve(buildArgsContext()))
38
+ shimmer.wrap(dns.Resolver.prototype, 'reverse', reverse(buildArgsContext()))
33
39
 
34
- patchResolveShorthands(dns.Resolver.prototype)
40
+ patchResolveShorthands(dns.Resolver.prototype, resolve)
35
41
  }
36
42
 
37
43
  return dns
38
44
  })
39
45
 
40
- function patchResolveShorthands (prototype) {
46
+ function patchResolveShorthands (prototype, resolve) {
41
47
  for (const method of Object.keys(rrtypes)) {
42
48
  if (prototype[method]) {
43
49
  rrtypeMap.set(prototype[method], rrtypes[method])
44
- shimmer.wrap(prototype, method, fn => wrap('apm:dns:resolve', fn, 2, rrtypes[method]))
50
+ shimmer.wrap(prototype, method, resolve(buildArgsContext(rrtypes[method])))
45
51
  }
46
52
  }
47
53
  }
48
54
 
49
- function wrap (prefix, fn, expectedArgs, rrtype) {
50
- const startCh = channel(prefix + ':start')
51
- const finishCh = channel(prefix + ':finish')
52
- const errorCh = channel(prefix + ':error')
53
-
54
- const wrapped = function () {
55
- const cb = arguments[arguments.length - 1]
56
- if (
57
- !startCh.hasSubscribers ||
58
- arguments.length < expectedArgs ||
59
- typeof cb !== 'function'
60
- ) {
61
- return fn.apply(this, arguments)
62
- }
63
-
64
- const args = [...arguments]
65
- args.pop() // gets rid of the callback
55
+ function buildArgsContext (rrtype) {
56
+ return function (_, args) {
57
+ if (args.length < 2) return
58
+ const captured = [...args]
59
+ captured.pop() // remove the callback
66
60
  if (rrtype) {
67
- args.push(rrtype)
61
+ captured.push(rrtype)
68
62
  }
69
-
70
- const ctx = { args }
71
-
72
- return startCh.runStores(ctx, () => {
73
- arguments[arguments.length - 1] = shimmer.wrapFunction(cb, cb => function (error, result, ...args) {
74
- if (error) {
75
- ctx.error = error
76
- errorCh.publish(ctx)
77
- }
78
-
79
- ctx.result = result
80
- finishCh.runStores(ctx, cb, this, error, result, ...args)
81
- })
82
-
83
- try {
84
- return fn.apply(this, arguments)
85
- // TODO deal with promise versions when we support `dns/promises`
86
- } catch (error) {
87
- void error.stack // trigger getting the stack at the original throwing point
88
- ctx.error = error
89
- errorCh.publish(ctx)
90
-
91
- throw error
92
- }
93
- })
63
+ return { args: captured }
94
64
  }
95
-
96
- return wrapped
97
65
  }
@@ -129,7 +129,7 @@ function wrapValidate (validate) {
129
129
  try {
130
130
  errors = validate.apply(this, arguments)
131
131
  if (errors && errors[0]) {
132
- ctx.error = errors && errors[0]
132
+ ctx.error = errors[0]
133
133
  validateErrorCh.publish(ctx)
134
134
  }
135
135
  return errors
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../../datadog-shimmer')
4
+ const { channel } = require('./instrument')
5
+
6
+ /**
7
+ * Create a shimmer-compatible instrumentor for callback-style APIs whose work is offloaded to the
8
+ * libuv worker thread pool (e.g. zlib.gzip, crypto.pbkdf2, dns.lookup). Builds a set of three
9
+ * diagnostic channels at the given prefix (`<prefix>:start`, `:finish`, `:error`) and returns a
10
+ * factory that produces shimmer wrappers driven by a caller-supplied `buildContext` function.
11
+ *
12
+ * The returned wrapper:
13
+ * - calls through unmodified when there are no subscribers or the last argument is not a callback;
14
+ * - invokes `buildContext(thisArg, args)` to construct the context object; a return of `undefined`
15
+ * also causes a bypass, letting callers enforce additional guards (e.g. minimum argument count);
16
+ * - publishes `:start` via `runStores`, wraps the callback to publish `:error` (on truthy error),
17
+ * optionally set `ctx.result` to the callback's first non-error argument, and publish `:finish`
18
+ * via `runStores`; publishes `:error` if the original call throws synchronously.
19
+ *
20
+ * @param {string} prefix
21
+ * @param {object} [options]
22
+ * @param {boolean} [options.captureResult=false] set `ctx.result` to the callback's first
23
+ * non-error argument before publishing `:finish`. Plugins that tag spans from the call's
24
+ * return value (e.g. the DNS lookup plugin) rely on this.
25
+ * @returns {(buildContext: (thisArg: unknown, args: IArguments) => object | undefined) =>
26
+ * (fn: Function) => Function}
27
+ */
28
+ function createCallbackInstrumentor (prefix, { captureResult = false } = {}) {
29
+ const startCh = channel(prefix + ':start')
30
+ const finishCh = channel(prefix + ':finish')
31
+ const errorCh = channel(prefix + ':error')
32
+
33
+ return function instrument (buildContext) {
34
+ return function wrap (fn) {
35
+ return function () {
36
+ const lastIndex = arguments.length - 1
37
+ const cb = arguments[lastIndex]
38
+ if (!startCh.hasSubscribers || typeof cb !== 'function') {
39
+ return fn.apply(this, arguments)
40
+ }
41
+
42
+ const ctx = buildContext(this, arguments)
43
+ if (ctx === undefined) {
44
+ return fn.apply(this, arguments)
45
+ }
46
+
47
+ return startCh.runStores(ctx, () => {
48
+ arguments[lastIndex] = shimmer.wrapFunction(cb, cb => function (error, ...rest) {
49
+ if (error) {
50
+ ctx.error = error
51
+ errorCh.publish(ctx)
52
+ }
53
+ if (captureResult) {
54
+ ctx.result = rest[0]
55
+ }
56
+ return finishCh.runStores(ctx, cb, this, error, ...rest)
57
+ })
58
+
59
+ try {
60
+ return fn.apply(this, arguments)
61
+ } catch (error) {
62
+ void error.stack // trigger getting the stack at the original throwing point
63
+ ctx.error = error
64
+ errorCh.publish(ctx)
65
+
66
+ throw error
67
+ }
68
+ })
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ module.exports = { createCallbackInstrumentor }
@@ -97,7 +97,10 @@ module.exports.checkForPotentialConflicts = function () {
97
97
  }
98
98
 
99
99
  module.exports.flushStartupLogs = function (log) {
100
+ // Some callers pass `./log/writer` (simple pass-through) while others pass the main `./log`
101
+ // module (which supports lazy delegate functions). Invoke closures here so both work.
100
102
  while (warnings.length) {
101
- log.warn(warnings.shift())
103
+ const entry = warnings.shift()
104
+ log.warn(typeof entry === 'function' ? entry() : entry)
102
105
  }
103
106
  }
@@ -12,11 +12,13 @@ module.exports = {
12
12
  net: () => require('../net'),
13
13
  url: () => require('../url'),
14
14
  vm: () => require('../vm'),
15
+ zlib: () => require('../zlib'),
15
16
  // Non Node.js modules
16
17
  '@anthropic-ai/sdk': { esmFirst: true, fn: () => require('../anthropic') },
17
18
  '@apollo/server': () => require('../apollo-server'),
18
19
  '@apollo/gateway': () => require('../apollo'),
19
20
  '@langchain/langgraph': { esmFirst: true, fn: () => require('../langgraph') },
21
+ '@modelcontextprotocol/sdk': () => require('../modelcontextprotocol-sdk'),
20
22
  'apollo-server-core': () => require('../apollo-server-core'),
21
23
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
22
24
  '@azure/event-hubs': () => require('../azure-event-hubs'),
@@ -1,5 +1,13 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * This file is meant to be only thin wrappers over core
5
+ * parsing/traversing/generating functionality with the goal to eventually move
6
+ * them out of the project. No other code should be added to this file such as
7
+ * helpers etc, and the API should be kept exactly as an external API would be
8
+ * expected to be.
9
+ */
10
+
3
11
  const log = require('../../../../dd-trace/src/log')
4
12
 
5
13
  // eslint-disable-next-line camelcase, no-undef
@@ -24,14 +32,13 @@ const compiler = {
24
32
  } catch (e) {
25
33
  log.error(e)
26
34
 
27
- // Fallback for when OXC is not available.
28
35
  const meriyah = require('../../../../../vendor/dist/meriyah')
29
36
 
30
- compiler.parse = (sourceText, { range, sourceType } = {}) => {
37
+ compiler.parse = (sourceText, { range, isModule } = {}) => {
31
38
  return meriyah.parse(sourceText.toString(), {
32
39
  loc: range,
33
40
  ranges: range,
34
- module: sourceType === 'module',
41
+ module: isModule,
35
42
  })
36
43
  }
37
44
  }
@@ -5,4 +5,5 @@ module.exports = [
5
5
  ...require('./bullmq'),
6
6
  ...require('./langchain'),
7
7
  ...require('./langgraph'),
8
+ ...require('./modelcontextprotocol-sdk'),
8
9
  ]
@@ -0,0 +1,59 @@
1
+ 'use strict'
2
+
3
+ // NOTE: Protocol.request (dist/esm|cjs/shared/protocol.js) is intentionally not instrumented here.
4
+ // It will be used for distributed tracing header injection when server-side coverage is added.
5
+
6
+ module.exports = [
7
+ {
8
+ module: {
9
+ name: '@modelcontextprotocol/sdk',
10
+ versionRange: '>=1.27.1',
11
+ filePath: 'dist/esm/client/index.js',
12
+ },
13
+ functionQuery: {
14
+ methodName: 'callTool',
15
+ className: 'Client',
16
+ kind: 'Async',
17
+ },
18
+ channelName: 'Client_callTool',
19
+ },
20
+ {
21
+ module: {
22
+ name: '@modelcontextprotocol/sdk',
23
+ versionRange: '>=1.27.1',
24
+ filePath: 'dist/cjs/client/index.js',
25
+ },
26
+ functionQuery: {
27
+ methodName: 'callTool',
28
+ className: 'Client',
29
+ kind: 'Async',
30
+ },
31
+ channelName: 'Client_callTool',
32
+ },
33
+ {
34
+ module: {
35
+ name: '@modelcontextprotocol/sdk',
36
+ versionRange: '>=1.27.1',
37
+ filePath: 'dist/esm/client/index.js',
38
+ },
39
+ functionQuery: {
40
+ methodName: 'listTools',
41
+ className: 'Client',
42
+ kind: 'Async',
43
+ },
44
+ channelName: 'Client_listTools',
45
+ },
46
+ {
47
+ module: {
48
+ name: '@modelcontextprotocol/sdk',
49
+ versionRange: '>=1.27.1',
50
+ filePath: 'dist/cjs/client/index.js',
51
+ },
52
+ functionQuery: {
53
+ methodName: 'listTools',
54
+ className: 'Client',
55
+ kind: 'Async',
56
+ },
57
+ channelName: 'Client_listTools',
58
+ },
59
+ ]
@@ -14,11 +14,13 @@ const transforms = module.exports = {
14
14
  if (node.body.some(tracingChannelPredicate)) return
15
15
 
16
16
  const index = node.body.findIndex(child => child.directive === 'use strict')
17
- const code = sourceType === 'module'
17
+ const code = isModuleSourceType(sourceType)
18
18
  ? `import { tracingChannel as tr_ch_apm_tracingChannel } from "${dcModule}"`
19
19
  : `const {tracingChannel: tr_ch_apm_tracingChannel} = require("${dcModule}")`
20
20
 
21
- node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
21
+ node.body.splice(index + 1, 0, parse(code, {
22
+ isModule: isModuleSourceType(sourceType),
23
+ }).body[0])
22
24
  },
23
25
 
24
26
  tracingChannelDeclaration (state, node) {
@@ -51,6 +53,13 @@ function traceAny (state, node, _parent, ancestry) {
51
53
  }
52
54
  }
53
55
 
56
+ /**
57
+ * @param {string} sourceType
58
+ */
59
+ function isModuleSourceType (sourceType) {
60
+ return sourceType === 'module' || sourceType === 'esm'
61
+ }
62
+
54
63
  function traceFunction (state, node, program) {
55
64
  transforms.tracingChannelDeclaration(state, program)
56
65