dd-trace 6.12.0 → 6.14.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 (251) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  8. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  9. package/ci/test-optimization-validation/package-check.js +1 -1
  10. package/ci/test-optimization-validation/report-writer.js +7 -5
  11. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  12. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  13. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  14. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  15. package/index.d.ts +127 -26
  16. package/package.json +22 -19
  17. package/packages/datadog-esbuild/index.js +3 -1
  18. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  19. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  20. package/packages/datadog-instrumentations/src/fs.js +24 -20
  21. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  22. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  23. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  24. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  25. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  26. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  34. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  35. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
  36. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
  37. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  38. package/packages/datadog-instrumentations/src/jest.js +165 -38
  39. package/packages/datadog-instrumentations/src/knex.js +76 -0
  40. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  41. package/packages/datadog-instrumentations/src/mariadb.js +13 -0
  42. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  43. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  44. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  45. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  46. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  47. package/packages/datadog-instrumentations/src/net.js +14 -17
  48. package/packages/datadog-instrumentations/src/next.js +212 -62
  49. package/packages/datadog-instrumentations/src/openai.js +42 -40
  50. package/packages/datadog-instrumentations/src/pg.js +15 -0
  51. package/packages/datadog-instrumentations/src/pino.js +15 -4
  52. package/packages/datadog-instrumentations/src/playwright.js +137 -27
  53. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  54. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  55. package/packages/datadog-instrumentations/src/undici.js +53 -4
  56. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
  57. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  58. package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
  59. package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
  60. package/packages/datadog-instrumentations/src/winston.js +6 -3
  61. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  62. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  63. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  64. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  65. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  66. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  67. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  68. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  69. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  70. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  71. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  72. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  73. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  74. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  75. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  76. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  77. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  78. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  79. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  80. package/packages/datadog-plugin-http/src/client.js +2 -15
  81. package/packages/datadog-plugin-http2/src/client.js +2 -15
  82. package/packages/datadog-plugin-jest/src/index.js +0 -6
  83. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  84. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  85. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  86. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  87. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  88. package/packages/datadog-plugin-next/src/index.js +63 -29
  89. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  90. package/packages/datadog-plugin-playwright/src/index.js +176 -28
  91. package/packages/datadog-plugin-undici/src/index.js +29 -56
  92. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  93. package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
  94. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  95. package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
  96. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  97. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  98. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  99. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  100. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  101. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  102. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  103. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  104. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  107. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  108. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  109. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  110. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  111. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  112. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  113. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  114. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  115. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  116. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  117. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
  118. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  119. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
  122. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  124. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  125. package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
  126. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  127. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  128. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  129. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  130. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  131. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  132. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  133. package/packages/dd-trace/src/config/index.js +75 -14
  134. package/packages/dd-trace/src/config/parsers.js +11 -1
  135. package/packages/dd-trace/src/config/stable.js +3 -3
  136. package/packages/dd-trace/src/config/supported-configurations.json +47 -2
  137. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  138. package/packages/dd-trace/src/debugger/config.js +12 -1
  139. package/packages/dd-trace/src/debugger/constants.js +1 -0
  140. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  141. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  142. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
  143. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  144. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  145. package/packages/dd-trace/src/debugger/index.js +23 -3
  146. package/packages/dd-trace/src/dogstatsd.js +532 -71
  147. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  148. package/packages/dd-trace/src/encode/0.4.js +5 -5
  149. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  150. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  151. package/packages/dd-trace/src/exporters/agent/index.js +24 -3
  152. package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
  153. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  154. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  155. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  156. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  157. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  158. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  159. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  160. package/packages/dd-trace/src/exporters/common/writer.js +31 -7
  161. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  162. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  163. package/packages/dd-trace/src/flush.js +105 -0
  164. package/packages/dd-trace/src/id.js +14 -0
  165. package/packages/dd-trace/src/index.js +5 -2
  166. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
  167. package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
  168. package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
  169. package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
  170. package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
  171. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  172. package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
  173. package/packages/dd-trace/src/llmobs/index.js +111 -48
  174. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  175. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  176. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  177. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  178. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  180. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  181. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  182. package/packages/dd-trace/src/llmobs/util.js +0 -33
  183. package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
  184. package/packages/dd-trace/src/log/index.js +7 -1
  185. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
  186. package/packages/dd-trace/src/noop/span.js +5 -0
  187. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  188. package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
  189. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  190. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  191. package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
  192. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  193. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  194. package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
  195. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  196. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  199. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  200. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  201. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  202. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  203. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  204. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  205. package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
  206. package/packages/dd-trace/src/opentracing/span.js +28 -0
  207. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  208. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  209. package/packages/dd-trace/src/plugin_manager.js +2 -0
  210. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  211. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  212. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  213. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  214. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  215. package/packages/dd-trace/src/plugins/util/test.js +157 -41
  216. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  217. package/packages/dd-trace/src/plugins/util/web.js +6 -74
  218. package/packages/dd-trace/src/profiling/config.js +1 -0
  219. package/packages/dd-trace/src/profiling/oom.js +11 -5
  220. package/packages/dd-trace/src/profiling/profiler.js +30 -13
  221. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  222. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  223. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  224. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  225. package/packages/dd-trace/src/proxy.js +108 -10
  226. package/packages/dd-trace/src/remote_config/index.js +319 -101
  227. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  228. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  229. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  230. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
  231. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
  232. package/packages/dd-trace/src/sampling_rule.js +36 -9
  233. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
  234. package/packages/dd-trace/src/serverless/vercel.js +155 -0
  235. package/packages/dd-trace/src/serverless.js +45 -21
  236. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  237. package/packages/dd-trace/src/span_stats.js +64 -31
  238. package/packages/dd-trace/src/startup-log.js +3 -2
  239. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  240. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  241. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  242. package/packages/dd-trace/src/tracer.js +26 -5
  243. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  244. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  245. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  246. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  247. package/vendor/dist/protobufjs/index.js +1 -1
  248. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  249. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  250. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
  251. package/packages/dd-trace/src/encode/agentless-json.js +0 -209
@@ -11,9 +11,11 @@ const {
11
11
  runOutsidePoolQueryAcquire,
12
12
  runPoolAcquireError,
13
13
  runWithPoolWait,
14
+ setPromisePoolConnectionConfig,
14
15
  takePoolWaitTime,
15
16
  wrapPoolQueryMethod,
16
17
  } = require('./helpers/pool-acquire')
18
+ const { wrapCallbackBundle, wrapPromiseBundle } = require('./mariadb-bundle')
17
19
 
18
20
  const commandAddCh = channel('apm:mariadb:command:add')
19
21
  const connectionStartCh = channel('apm:mariadb:connection:start')
@@ -96,6 +98,8 @@ function createWrapQuery (options) {
96
98
  if (!startCh.hasSubscribers) return query.apply(this, arguments)
97
99
 
98
100
  const ctx = { sql, conf: options }
101
+ const poolWaitTime = takePoolWaitTime(this)
102
+ if (poolWaitTime !== undefined) ctx.poolWaitTime = poolWaitTime
99
103
 
100
104
  return startCh.runStores(ctx, query, this, ...arguments)
101
105
  .then(result => {
@@ -145,8 +149,11 @@ function createWrapQueryCallback (options) {
145
149
  function wrapConnection (promiseMethod, Connection) {
146
150
  return function (options) {
147
151
  Connection.apply(this, arguments)
152
+ setPromisePoolConnectionConfig(this, options)
148
153
 
154
+ const query = this[promiseMethod]
149
155
  shimmer.wrap(this, promiseMethod, createWrapQuery(options))
156
+ if (this.query === query) this.query = this[promiseMethod]
150
157
  shimmer.wrap(this, '_queryCallback', createWrapQueryCallback(options))
151
158
  }
152
159
  }
@@ -443,3 +450,9 @@ addHook({ name, file: 'lib/connection.js', versions: ['>=2.0.4 <=2.5.1'] }, (Con
443
450
  addHook({ name, file: 'lib/pool-base.js', versions: ['>=2.0.4 <3'] }, (PoolBase) => {
444
451
  return shimmer.wrapFunction(PoolBase, wrapPoolBase)
445
452
  })
453
+
454
+ // MariaDB 3.5.3 added single-file CommonJS bundles that do not load the original source modules at runtime.
455
+ // Matching their generated, minified internals would couple instrumentation to unstable bundle output, so wrap
456
+ // the runtime objects returned by the public factories instead.
457
+ addHook({ name, versions: ['>=3.5.3'] }, wrapPromiseBundle)
458
+ addHook({ name, file: 'dist/callback.cjs', versions: ['>=3.5.3'] }, wrapCallbackBundle)
@@ -11,6 +11,60 @@ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
11
11
  const parameterizedTestCh = channel('ci:mocha:test:parameterize')
12
12
  const patched = new WeakSet()
13
13
 
14
+ /**
15
+ * Registers every Mocha package view that can expose the constructor.
16
+ *
17
+ * @param {string[]} versions
18
+ * @param {(Mocha: Function, frameworkVersion: string) => Function} wrapMochaRun
19
+ * @returns {void}
20
+ */
21
+ function addMochaRunHooks (versions, wrapMochaRun) {
22
+ const patchedMochaConstructors = new WeakSet()
23
+
24
+ /**
25
+ * @param {Function} Mocha
26
+ * @param {string} frameworkVersion
27
+ * @returns {Function}
28
+ */
29
+ function wrapMochaOnce (Mocha, frameworkVersion) {
30
+ if (patchedMochaConstructors.has(Mocha)) return Mocha
31
+ patchedMochaConstructors.add(Mocha)
32
+ return wrapMochaRun(Mocha, frameworkVersion)
33
+ }
34
+
35
+ /**
36
+ * @param {object|Function} MochaPackage
37
+ * @param {string} frameworkVersion
38
+ * @returns {object|Function}
39
+ */
40
+ function wrapMochaPackage (MochaPackage, frameworkVersion) {
41
+ wrapMochaOnce(MochaPackage.default ?? MochaPackage, frameworkVersion)
42
+ return MochaPackage
43
+ }
44
+
45
+ addHook({
46
+ name: 'mocha',
47
+ versions,
48
+ filePattern: String.raw`lib/mocha\.(?:c?js)$`,
49
+ }, wrapMochaOnce)
50
+
51
+ // Mocha 12's ESM package root can load lib/mocha.cjs before the CJS hook observes it. ESM imports report the
52
+ // package root while synchronous require reports index.js, so hook both views. Keeping the namespace intact makes
53
+ // the ESM hook skip its separate default-export callback, and wrapMochaOnce deduplicates loaders exposing both.
54
+ addHook({
55
+ name: 'mocha',
56
+ versions: ['>=12.0.0'],
57
+ patchDefault: false,
58
+ }, wrapMochaPackage)
59
+
60
+ addHook({
61
+ name: 'mocha',
62
+ versions: ['>=12.0.0'],
63
+ file: 'index.js',
64
+ patchDefault: false,
65
+ }, wrapMochaPackage)
66
+ }
67
+
14
68
  // mocha-each support
15
69
  addHook({
16
70
  name: 'mocha-each',
@@ -38,7 +92,8 @@ addHook({
38
92
  name: 'mocha',
39
93
  versions: [MINIMUM_MOCHA_VERSION],
40
94
  file: 'lib/suite.js',
41
- }, (Suite) => {
95
+ }, (SuitePackage) => {
96
+ const Suite = SuitePackage.Suite ?? SuitePackage
42
97
  shimmer.wrap(Suite.prototype, 'addTest', addTest => function (test) {
43
98
  const callSites = getCallSites()
44
99
  const testCallSite = callSites.find(site => site.getFileName() === test.file)
@@ -47,5 +102,7 @@ addHook({
47
102
  }
48
103
  return addTest.apply(this, arguments)
49
104
  })
50
- return Suite
105
+ return SuitePackage
51
106
  })
107
+
108
+ module.exports = { addMochaRunHooks }
@@ -35,6 +35,7 @@ const {
35
35
  isMarkedAsUnskippable,
36
36
  } = require('../../../dd-trace/src/plugins/util/test')
37
37
 
38
+ const { addMochaRunHooks } = require('./common')
38
39
  const {
39
40
  isNewTest,
40
41
  getTestProperties,
@@ -60,8 +61,6 @@ const {
60
61
  adjustRunnerFailuresForTestOptimization,
61
62
  } = require('./utils')
62
63
 
63
- require('./common')
64
-
65
64
  const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
66
65
 
67
66
  /**
@@ -536,7 +535,10 @@ function stopCurrentHook (runner, hook) {
536
535
  if (test) {
537
536
  if (hook.parent?._afterEach?.includes(hook) || hook.parent?._afterAll?.includes(hook)) {
538
537
  markTestTerminal(runner, test)
539
- if (!test._ddTestFinishStarted) runnerTestEndHandlers.get(runner)?.(test)
538
+ if (!test._ddTestFinishStarted) {
539
+ const onTestEnd = runnerTestEndHandlers.get(runner)
540
+ onTestEnd?.(test)
541
+ }
540
542
  } else {
541
543
  markTestPending(runner, test)
542
544
  }
@@ -763,14 +765,18 @@ function wrapRunnerEmit (Runner) {
763
765
  const test = hook.ctx?.currentTest
764
766
  if (test && hook.parent?._afterEach?.includes(hook)) {
765
767
  markTestTerminal(this, test)
766
- if (!test._ddTestFinishStarted) runnerTestEndHandlers.get(this)?.(test)
768
+ if (!test._ddTestFinishStarted) {
769
+ const onTestEnd = runnerTestEndHandlers.get(this)
770
+ onTestEnd?.(test)
771
+ }
767
772
  }
768
773
  } else if (event === 'pending' || event === 'pass' || event === 'fail' || event === 'retry' ||
769
774
  event === 'test end') {
770
775
  const test = arguments[1]
771
776
  stopAfterEachHooks(this, test)
772
777
  if (event === 'test end' && !test._ddTestFinishStarted) {
773
- runnerTestEndHandlers.get(this)?.(test)
778
+ const onTestEnd = runnerTestEndHandlers.get(this)
779
+ onTestEnd?.(test)
774
780
  }
775
781
  }
776
782
  }
@@ -1063,13 +1069,15 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
1063
1069
  // In this hook we delay the execution with options.delay to grab library configuration,
1064
1070
  // skippable and known tests.
1065
1071
  // It is called but skipped in parallel mode.
1066
- addHook({
1067
- name: 'mocha',
1068
- versions: [MINIMUM_MOCHA_VERSION],
1069
- file: 'lib/mocha.js',
1070
- }, (Mocha, frameworkVersion) => {
1072
+ /**
1073
+ * @param {Function} Mocha
1074
+ * @param {string} frameworkVersion
1075
+ * @returns {Function}
1076
+ */
1077
+ function wrapMochaRun (Mocha, frameworkVersion) {
1071
1078
  warnDeprecatedMochaVersion(frameworkVersion)
1072
1079
 
1080
+ // Shimmer is required because run must return its Runner while execution is paused and resumed after configuration.
1073
1081
  shimmer.wrap(Mocha.prototype, 'run', run => function (...args) {
1074
1082
  // Workers do not need to request any data, just run the tests
1075
1083
  if (!testFinishCh.hasSubscribers || getEnvironmentVariable('MOCHA_WORKER_ID') || this.options.parallel) {
@@ -1121,12 +1129,14 @@ addHook({
1121
1129
  return runner
1122
1130
  })
1123
1131
  return Mocha
1124
- })
1132
+ }
1133
+
1134
+ addMochaRunHooks([MINIMUM_MOCHA_VERSION], wrapMochaRun)
1125
1135
 
1126
1136
  addHook({
1127
1137
  name: 'mocha',
1128
1138
  versions: [MINIMUM_MOCHA_VERSION],
1129
- file: 'lib/cli/run-helpers.js',
1139
+ filePattern: String.raw`lib/cli/run-helpers\.(?:c?js)$`,
1130
1140
  }, (run) => {
1131
1141
  // `runMocha` is an async function
1132
1142
  shimmer.wrap(run, 'runMocha', runMocha => function (...args) {
@@ -1157,7 +1167,7 @@ addHook({
1157
1167
  addHook({
1158
1168
  name: 'mocha',
1159
1169
  versions: [MINIMUM_MOCHA_VERSION],
1160
- file: 'lib/runner.js',
1170
+ filePattern: String.raw`lib/runner\.(?:c?js)$`,
1161
1171
  }, function (Runner, frameworkVersion) {
1162
1172
  if (patched.has(Runner)) return Runner
1163
1173
 
@@ -1588,7 +1598,7 @@ addHook({
1588
1598
  addHook({
1589
1599
  name: 'mocha',
1590
1600
  versions: ['>=8.0.0'],
1591
- file: 'lib/nodejs/parallel-buffered-runner.js',
1601
+ filePattern: String.raw`lib/nodejs/parallel-buffered-runner\.(?:c?js)$`,
1592
1602
  }, (ParallelBufferedRunner, frameworkVersion) => {
1593
1603
  shimmer.wrap(ParallelBufferedRunner.prototype, 'run', run => function (cb, { files, options = {} }) {
1594
1604
  if (!testFinishCh.hasSubscribers) {
@@ -1667,7 +1677,7 @@ addHook({
1667
1677
  addHook({
1668
1678
  name: 'mocha',
1669
1679
  versions: ['>=8.0.0'],
1670
- file: 'lib/nodejs/buffered-worker-pool.js',
1680
+ filePattern: String.raw`lib/nodejs/buffered-worker-pool\.(?:c?js)$`,
1671
1681
  }, (BufferedWorkerPoolPackage, frameworkVersion) => {
1672
1682
  const { BufferedWorkerPool } = BufferedWorkerPoolPackage
1673
1683
 
@@ -507,7 +507,8 @@ function inheritDatadogPropertiesFromRetriedTest (test) {
507
507
  }
508
508
 
509
509
  function runnableWrapper (RunnablePackage, libraryConfig) {
510
- shimmer.wrap(RunnablePackage.prototype, 'run', run => function (...args) {
510
+ const Runnable = RunnablePackage.Runnable ?? RunnablePackage
511
+ shimmer.wrap(Runnable.prototype, 'run', run => function (...args) {
511
512
  if (!testFinishCh.hasSubscribers) {
512
513
  return run.apply(this, args)
513
514
  }
@@ -6,6 +6,11 @@ const {
6
6
  WEBDRIVERIO_WORKER_EVENT,
7
7
  WEBDRIVERIO_WORKER_ORIGIN,
8
8
  } = require('../../../dd-trace/src/ci-visibility/exporters/test-worker/webdriverio')
9
+ const { FINAL_FLUSH_TIMEOUT } = require('../../../dd-trace/src/ci-visibility/final-flush')
10
+
11
+ const SCREENSHOT_UPLOAD = 'dd:test-optimization:webdriverio:screenshot:upload'
12
+ const SCREENSHOT_UPLOAD_RESPONSE = 'dd:test-optimization:webdriverio:screenshot:upload:response'
13
+ const SCREENSHOT_UPLOAD_TIMEOUT_MS = FINAL_FLUSH_TIMEOUT + 5000
9
14
 
10
15
  /**
11
16
  * Sends a message over WebdriverIO's worker IPC envelope.
@@ -30,10 +35,103 @@ function sendWebdriverioWorkerMessage (message, onError, onDone) {
30
35
  })
31
36
  }
32
37
 
38
+ let screenshotUploadRequestId = 0
39
+ const screenshotUploadRequests = new Map()
40
+
41
+ /**
42
+ * Removes shared screenshot response listeners when there are no pending requests.
43
+ *
44
+ * @returns {void}
45
+ */
46
+ function removeScreenshotUploadListeners () {
47
+ if (screenshotUploadRequests.size !== 0) return
48
+
49
+ process.off('message', onScreenshotUploadResponse)
50
+ process.off('disconnect', onScreenshotUploadDisconnect)
51
+ }
52
+
53
+ /**
54
+ * Completes one pending screenshot upload request.
55
+ *
56
+ * @param {string} requestId
57
+ * @param {Error} [error]
58
+ * @returns {void}
59
+ */
60
+ function finishScreenshotUploadRequest (requestId, error) {
61
+ const request = screenshotUploadRequests.get(requestId)
62
+ if (!request) return
63
+
64
+ screenshotUploadRequests.delete(requestId)
65
+ clearTimeout(request.timeout)
66
+ removeScreenshotUploadListeners()
67
+ request.onDone(error)
68
+ }
69
+
70
+ /**
71
+ * Dispatches one coordinator screenshot response to its pending request.
72
+ *
73
+ * @param {object} message
74
+ * @returns {void}
75
+ */
76
+ function onScreenshotUploadResponse (message) {
77
+ if (message?.name !== SCREENSHOT_UPLOAD_RESPONSE) return
78
+
79
+ const { error: errorMessage, requestId } = message.content || {}
80
+ if (!requestId) return
81
+
82
+ finishScreenshotUploadRequest(requestId, errorMessage ? new Error(errorMessage) : undefined)
83
+ }
84
+
85
+ /**
86
+ * Fails every pending screenshot upload after coordinator disconnect.
87
+ *
88
+ * @returns {void}
89
+ */
90
+ function onScreenshotUploadDisconnect () {
91
+ for (const requestId of screenshotUploadRequests.keys()) {
92
+ finishScreenshotUploadRequest(
93
+ requestId,
94
+ new Error('WebdriverIO coordinator disconnected during screenshot upload')
95
+ )
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Requests one screenshot upload from the WebdriverIO coordinator.
101
+ *
102
+ * @param {object} content - Screenshot upload metadata
103
+ * @param {(error?: Error) => void} onDone - Upload completion callback
104
+ * @returns {void}
105
+ */
106
+ function requestWebdriverioScreenshotUpload (content, onDone) {
107
+ const requestId = `${process.pid}-${++screenshotUploadRequestId}`
108
+ const timeout = setTimeout(() => {
109
+ finishScreenshotUploadRequest(requestId, new Error('WebdriverIO screenshot upload timed out'))
110
+ }, SCREENSHOT_UPLOAD_TIMEOUT_MS)
111
+ timeout.unref?.()
112
+ if (screenshotUploadRequests.size === 0) {
113
+ process.on('message', onScreenshotUploadResponse)
114
+ process.once('disconnect', onScreenshotUploadDisconnect)
115
+ }
116
+ screenshotUploadRequests.set(requestId, { onDone, timeout })
117
+ sendWebdriverioWorkerMessage({
118
+ origin: 'datadog',
119
+ name: SCREENSHOT_UPLOAD,
120
+ content: { ...content, requestId },
121
+ }, error => finishScreenshotUploadRequest(
122
+ requestId,
123
+ error || new Error('WebdriverIO screenshot upload IPC failed')
124
+ ))
125
+ }
126
+
33
127
  module.exports = {
34
128
  CONFIGURATION_REQUEST: 'dd:test-optimization:webdriverio:configuration:request',
35
129
  CONFIGURATION_RESPONSE: 'dd:test-optimization:webdriverio:configuration:response',
36
130
  createWebdriverioWorkerMessage,
131
+ requestWebdriverioScreenshotUpload,
132
+ SCREENSHOT_UPLOAD,
133
+ SCREENSHOT_UPLOAD_RESPONSE,
134
+ SCREENSHOT_UPLOAD_TIMEOUT_MS,
37
135
  sendWebdriverioWorkerMessage,
38
136
  SUITE_FINISH: 'dd:test-optimization:webdriverio:test-suite:finish',
39
137
  WORKER_READY: 'dd:test-optimization:webdriverio:worker:ready',
@@ -7,6 +7,7 @@ const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper'
7
7
  const log = require('../../../dd-trace/src/log')
8
8
  const { DD_MAJOR } = require('../../../../version')
9
9
 
10
+ const { addMochaRunHooks } = require('./common')
10
11
  const {
11
12
  runnableWrapper,
12
13
  getOnTestHandler,
@@ -29,7 +30,6 @@ const {
29
30
  WEBDRIVERIO_WORKER_ENV,
30
31
  WORKER_READY,
31
32
  } = require('./webdriverio-protocol')
32
- require('./common')
33
33
 
34
34
  const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
35
35
 
@@ -360,13 +360,15 @@ function isFailedTestReplayEnabled () {
360
360
  return config.isTestDynamicInstrumentationEnabled && config.isDiEnabled
361
361
  }
362
362
 
363
- addHook({
364
- name: 'mocha',
365
- versions: ['>=8.0.0'],
366
- file: 'lib/mocha.js',
367
- }, (Mocha, frameworkVersion) => {
363
+ /**
364
+ * @param {Function} Mocha
365
+ * @param {string} frameworkVersion
366
+ * @returns {Function}
367
+ */
368
+ function wrapMochaRun (Mocha, frameworkVersion) {
368
369
  reportWebdriverioWorkerReady(frameworkVersion)
369
370
 
371
+ // Shimmer is required because run must return its Runner while execution is paused and resumed after configuration.
370
372
  shimmer.wrap(Mocha.prototype, 'run', run => function (...args) {
371
373
  applyMochaOptions(this.options)
372
374
  if (!isWebdriverioWorker || !workerFinishCh.hasSubscribers) {
@@ -438,13 +440,15 @@ addHook({
438
440
  })
439
441
 
440
442
  return Mocha
441
- })
443
+ }
444
+
445
+ addMochaRunHooks(['>=8.0.0'], wrapMochaRun)
442
446
 
443
447
  // Runner is also hooked in mocha/main.js, but in here we only generate test events.
444
448
  addHook({
445
449
  name: 'mocha',
446
450
  versions: [MINIMUM_MOCHA_VERSION],
447
- file: 'lib/runner.js',
451
+ filePattern: String.raw`lib/runner\.(?:c?js)$`,
448
452
  }, function (Runner) {
449
453
  shimmer.wrap(Runner.prototype, 'runTests', runTests => getRunTestsWrapper(runTests, config))
450
454
 
@@ -27,8 +27,6 @@ addHook({ name: 'net' }, (net) => {
27
27
  }
28
28
 
29
29
  const options = getOptions(args)
30
- const lastIndex = args.length - 1
31
- const callback = args[lastIndex]
32
30
 
33
31
  if (!options) return connect.apply(this, args)
34
32
 
@@ -38,42 +36,39 @@ addHook({ name: 'net' }, (net) => {
38
36
  const errorCh = protocol === 'ipc' ? errorICPCh : errorTCPCh
39
37
  const ctx = { options }
40
38
 
41
- if (typeof callback === 'function') {
42
- args[lastIndex] = function (...args) {
43
- return finishCh.runStores(ctx, callback, this, ...args)
44
- }
45
- }
46
-
47
39
  return startCh.runStores(ctx, () => {
48
- setupListeners(this, protocol, ctx, finishCh, errorCh)
40
+ const cleanup = setupListeners(this, protocol, ctx, finishCh, errorCh)
49
41
 
50
- const emit = this.emit
42
+ const originalEmit = this.emit
51
43
  let pendingReadyEvents = 2
52
44
  // Named `emit`/arity-1 mirrors the socket method so the per-socket wrap
53
45
  // skips its name/length rewrite.
54
- this.emit = shimmer.wrapFunction(emit, originalEmit => function emit (eventName) {
46
+ this.emit = shimmer.wrapFunction(originalEmit, wrappedEmit => function emit (eventName) {
55
47
  switch (eventName) {
56
48
  case 'ready':
57
49
  case 'connect':
58
50
  if (--pendingReadyEvents === 0) this.emit = originalEmit
59
51
  return readyCh.runStores(ctx, () => {
60
- return Reflect.apply(originalEmit, this, arguments)
52
+ return Reflect.apply(wrappedEmit, this, arguments)
61
53
  })
62
54
  case 'error':
63
55
  case 'close':
64
56
  this.emit = originalEmit
65
- return Reflect.apply(originalEmit, this, arguments)
57
+ return Reflect.apply(wrappedEmit, this, arguments)
66
58
  default:
67
- return Reflect.apply(originalEmit, this, arguments)
59
+ return Reflect.apply(wrappedEmit, this, arguments)
68
60
  }
69
61
  })
70
62
 
71
63
  try {
72
64
  return connect.apply(this, args)
73
- } catch (err) {
74
- errorCh.publish(err)
65
+ } catch (error) {
66
+ cleanup()
67
+ this.emit = originalEmit
68
+ errorCh.publish(error)
69
+ finishCh.runStores(ctx, () => {})
75
70
 
76
- throw err
71
+ throw error
77
72
  }
78
73
  })
79
74
  })
@@ -142,4 +137,6 @@ function setupListeners (socket, protocol, ctx, finishCh, errorCh) {
142
137
  for (const event of events) {
143
138
  socket.once(event, wrapListener)
144
139
  }
140
+
141
+ return cleanupOtherListeners
145
142
  }