dd-trace 5.100.0 → 5.101.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 (64) hide show
  1. package/index.d.ts +14 -0
  2. package/package.json +5 -5
  3. package/packages/datadog-instrumentations/src/cypress.js +5 -3
  4. package/packages/datadog-instrumentations/src/http/client.js +20 -3
  5. package/packages/datadog-instrumentations/src/jest.js +62 -32
  6. package/packages/datadog-instrumentations/src/mocha/common.js +4 -1
  7. package/packages/datadog-instrumentations/src/mocha/main.js +25 -4
  8. package/packages/datadog-instrumentations/src/mocha/worker.js +5 -2
  9. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
  10. package/packages/datadog-plugin-bullmq/src/consumer.js +2 -2
  11. package/packages/datadog-plugin-bullmq/src/producer.js +14 -20
  12. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +17 -0
  13. package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
  14. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
  15. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
  16. package/packages/dd-trace/src/appsec/reporter.js +4 -1
  17. package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
  18. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
  19. package/packages/dd-trace/src/config/config-types.d.ts +0 -2
  20. package/packages/dd-trace/src/config/index.js +1 -55
  21. package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
  22. package/packages/dd-trace/src/datastreams/encoding.js +39 -28
  23. package/packages/dd-trace/src/datastreams/pathway.js +29 -26
  24. package/packages/dd-trace/src/datastreams/processor.js +17 -15
  25. package/packages/dd-trace/src/datastreams/size.js +6 -2
  26. package/packages/dd-trace/src/debugger/config.js +5 -2
  27. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
  28. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  29. package/packages/dd-trace/src/dogstatsd.js +10 -7
  30. package/packages/dd-trace/src/encode/0.4.js +2 -2
  31. package/packages/dd-trace/src/encode/0.5.js +2 -2
  32. package/packages/dd-trace/src/encode/agentless-json.js +2 -2
  33. package/packages/dd-trace/src/encode/tags-processors.js +2 -27
  34. package/packages/dd-trace/src/exporters/common/request.js +22 -11
  35. package/packages/dd-trace/src/exporters/common/retry.js +104 -0
  36. package/packages/dd-trace/src/git_metadata.js +66 -0
  37. package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
  38. package/packages/dd-trace/src/id.js +15 -26
  39. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  40. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
  41. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
  42. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +30 -13
  43. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
  44. package/packages/dd-trace/src/llmobs/sdk.js +5 -1
  45. package/packages/dd-trace/src/llmobs/span_processor.js +28 -2
  46. package/packages/dd-trace/src/llmobs/tagger.js +42 -0
  47. package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
  48. package/packages/dd-trace/src/llmobs/util.js +80 -5
  49. package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
  50. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
  51. package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -2
  52. package/packages/dd-trace/src/opentelemetry/span-helpers.js +188 -50
  53. package/packages/dd-trace/src/opentelemetry/span.js +42 -80
  54. package/packages/dd-trace/src/opentracing/propagation/text_map.js +65 -27
  55. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +58 -22
  56. package/packages/dd-trace/src/opentracing/span.js +56 -48
  57. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  58. package/packages/dd-trace/src/priority_sampler.js +6 -4
  59. package/packages/dd-trace/src/profiling/config.js +5 -4
  60. package/packages/dd-trace/src/remote_config/index.js +5 -3
  61. package/packages/dd-trace/src/span_format.js +52 -5
  62. package/packages/dd-trace/src/span_processor.js +0 -4
  63. package/packages/dd-trace/src/spanleak.js +0 -1
  64. package/packages/dd-trace/src/util.js +17 -0
package/index.d.ts CHANGED
@@ -3934,6 +3934,13 @@ declare namespace tracer {
3934
3934
  */
3935
3935
  tags?: { [key: string]: any },
3936
3936
 
3937
+ /**
3938
+ * List of tag keys to propagate to LLM Observability cost and token metrics emitted from this span.
3939
+ * Each key must already be present in `tags` from this call or from a previous annotation on the
3940
+ * same span.
3941
+ */
3942
+ costTags?: string[],
3943
+
3937
3944
  /**
3938
3945
  * A Prompt object that represents the prompt used for an LLM call. Only used on `llm` spans.
3939
3946
  */
@@ -3946,6 +3953,13 @@ declare namespace tracer {
3946
3953
  */
3947
3954
  tags?: { [key: string]: any },
3948
3955
 
3956
+ /**
3957
+ * List of tag keys to propagate to LLM Observability cost and token metrics emitted from each span
3958
+ * in the context.
3959
+ * Each key must already be present in `tags` on the span when it starts.
3960
+ */
3961
+ costTags?: string[],
3962
+
3949
3963
  /**
3950
3964
  * Set to override the span name for any spans annotated within the returned context.
3951
3965
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.100.0",
3
+ "version": "5.101.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -18,8 +18,8 @@
18
18
  "type:check": "tsc --noEmit -p tsconfig.dev.json",
19
19
  "type:doc:build": "cd docs && yarn && yarn build",
20
20
  "type:doc:test": "cd docs && yarn && yarn test",
21
- "lint": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && eslint . --concurrency=auto --max-warnings 0",
22
- "lint:fix": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && eslint . --concurrency=auto --max-warnings 0 --fix",
21
+ "lint": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && node scripts/check-no-mcr-images.js && eslint . --concurrency=auto --max-warnings 0",
22
+ "lint:fix": "node scripts/check_licenses.js && node scripts/check-no-coverage-artifacts.js && node scripts/check-no-mcr-images.js && eslint . --concurrency=auto --max-warnings 0 --fix",
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/**'",
@@ -166,12 +166,12 @@
166
166
  "@datadog/native-appsec": "11.0.1",
167
167
  "@datadog/native-iast-taint-tracking": "4.1.0",
168
168
  "@datadog/native-metrics": "3.1.1",
169
- "@datadog/openfeature-node-server": "^1.1.1",
169
+ "@datadog/openfeature-node-server": "^1.1.2",
170
170
  "@datadog/pprof": "5.14.1",
171
171
  "@datadog/wasm-js-rewriter": "5.0.1",
172
172
  "@opentelemetry/api": ">=1.0.0 <1.10.0",
173
173
  "@opentelemetry/api-logs": "<1.0.0",
174
- "oxc-parser": "^0.127.0"
174
+ "oxc-parser": "^0.128.0"
175
175
  },
176
176
  "devDependencies": {
177
177
  "@actions/core": "^3.0.1",
@@ -8,11 +8,13 @@ const {
8
8
  wrapConfig,
9
9
  } = require('./cypress-config')
10
10
 
11
+ const MINIMUM_CYPRESS_VERSION = DD_MAJOR >= 6 ? '>=12.0.0' : '>=10.2.0'
12
+
11
13
  // Wrap defineConfig() so configs are instrumented when loaded in Cypress's
12
14
  // config child process. This covers both CLI and programmatic usage with CJS configs.
13
15
  addHook({
14
16
  name: 'cypress',
15
- versions: ['>=10.2.0'],
17
+ versions: [MINIMUM_CYPRESS_VERSION],
16
18
  }, (cypress) => {
17
19
  if (typeof cypress.defineConfig === 'function') {
18
20
  shimmer.wrap(cypress, 'defineConfig', (defineConfig) => function (config) {
@@ -61,7 +63,7 @@ function wrapStartOnModule (mod) {
61
63
  for (const file of ['lib/exec/run.js', 'lib/exec/open.js', 'dist/exec/run.js', 'dist/exec/open.js']) {
62
64
  addHook({
63
65
  name: 'cypress',
64
- versions: ['>=10.2.0'],
66
+ versions: [MINIMUM_CYPRESS_VERSION],
65
67
  file,
66
68
  }, wrapStartOnModule)
67
69
  }
@@ -70,7 +72,7 @@ for (const file of ['lib/exec/run.js', 'lib/exec/open.js', 'dist/exec/run.js', '
70
72
  // The chunk exports runModule and openModule, each with a start() method.
71
73
  addHook({
72
74
  name: 'cypress',
73
- versions: ['>=10.2.0'],
75
+ versions: [MINIMUM_CYPRESS_VERSION],
74
76
  filePattern: 'dist/cli.*',
75
77
  }, (cliChunk) => {
76
78
  if (cliChunk.runModule?.start) {
@@ -26,10 +26,27 @@ function hookFn (http) {
26
26
  return http
27
27
  }
28
28
 
29
+ // `inputURL` may be the user's options object (for the `http.request(options)`
30
+ // shape); never write directly into it. The result is later mutated by
31
+ // `normalizeHeaders` and read by `url.format`, so the merged object must be
32
+ // owned by the tracer. `undefined` means "no URL supplied" — Node merges
33
+ // with the options object or its defaults, so build a tracer-owned
34
+ // options-only shape and let tracing proceed. `null`/primitive first args
35
+ // are returned as-is so `normalizeHeaders` throws and the surrounding
36
+ // try/catch in `instrumentRequest` falls through to the native request;
37
+ // spreading a primitive yields `{}`, which would silently turn an invalid
38
+ // `http.request(123)` into a synthesized localhost request.
29
39
  function combineOptions (inputURL, inputOptions) {
30
- return inputOptions !== null && typeof inputOptions === 'object'
31
- ? Object.assign(inputURL || {}, inputOptions)
32
- : inputURL
40
+ if (inputURL === undefined) {
41
+ return inputOptions !== null && typeof inputOptions === 'object' ? { ...inputOptions } : {}
42
+ }
43
+ if (inputURL === null || (typeof inputURL !== 'object' && typeof inputURL !== 'function')) {
44
+ return inputURL
45
+ }
46
+ if (inputOptions !== null && typeof inputOptions === 'object') {
47
+ return { ...inputURL, ...inputOptions }
48
+ }
49
+ return { ...inputURL }
33
50
  }
34
51
  function normalizeHeaders (options) {
35
52
  options.headers ??= {}
@@ -4,6 +4,8 @@
4
4
  const realSetTimeout = setTimeout
5
5
 
6
6
  const path = require('path')
7
+ const satisfies = require('../../../vendor/dist/semifies')
8
+ const { DD_MAJOR } = require('../../../version')
7
9
  const shimmer = require('../../datadog-shimmer')
8
10
  const log = require('../../dd-trace/src/log')
9
11
  const {
@@ -126,7 +128,13 @@ const testSuiteJestObjects = new Map()
126
128
 
127
129
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
128
130
  const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
131
+ const MINIMUM_JEST_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=24.8.0'
132
+ const MINIMUM_JEST_VERSION_BEFORE_30 = DD_MAJOR >= 6 ? '>=28.0.0 <30.0.0' : '>=24.8.0 <30.0.0'
133
+ const MINIMUM_JEST_WORKER_VERSION_BEFORE_30 = DD_MAJOR >= 6 ? '>=28.0.0 <30.0.0' : '>=24.9.0 <30.0.0'
134
+ const MINIMUM_JEST_CONFIG_ASYNC_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=25.1.0'
135
+ const MINIMUM_JEST_TEST_SCHEDULER_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=27.0.0'
129
136
  const atrSuppressedErrors = new Map()
137
+ let hasWarnedDeprecatedJestVersion = false
130
138
 
131
139
  // Track quarantined tests whose errors were suppressed, keyed by "suite › testName"
132
140
  const quarantinedFailingTests = new Set()
@@ -177,6 +185,20 @@ function formatJestError (errors) {
177
185
  return error
178
186
  }
179
187
 
188
+ function warnDeprecatedJestVersion (frameworkVersion) {
189
+ if (DD_MAJOR >= 6 || hasWarnedDeprecatedJestVersion || !frameworkVersion ||
190
+ !satisfies(frameworkVersion, '<28.0.0')) {
191
+ return
192
+ }
193
+
194
+ hasWarnedDeprecatedJestVersion = true
195
+ // eslint-disable-next-line no-console
196
+ console.warn(
197
+ 'dd-trace support for Jest<28.0.0 is deprecated and will be removed in dd-trace v6. ' +
198
+ 'Please upgrade Jest to >=28.0.0.'
199
+ )
200
+ }
201
+
180
202
  function getTestEnvironmentOptions (config) {
181
203
  if (config.projectConfig && config.projectConfig.testEnvironmentOptions) { // newer versions
182
204
  return config.projectConfig.testEnvironmentOptions
@@ -1073,12 +1095,12 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
1073
1095
 
1074
1096
  addHook({
1075
1097
  name: 'jest-environment-node',
1076
- versions: ['>=24.8.0'],
1098
+ versions: [MINIMUM_JEST_VERSION],
1077
1099
  }, getTestEnvironment)
1078
1100
 
1079
1101
  addHook({
1080
1102
  name: 'jest-environment-jsdom',
1081
- versions: ['>=24.8.0'],
1103
+ versions: [MINIMUM_JEST_VERSION],
1082
1104
  }, getTestEnvironment)
1083
1105
 
1084
1106
  addHook({
@@ -1150,6 +1172,8 @@ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
1150
1172
 
1151
1173
  function getCliWrapper (isNewJestVersion) {
1152
1174
  return function cliWrapper (cli, jestVersion) {
1175
+ warnDeprecatedJestVersion(jestVersion)
1176
+
1153
1177
  if (isNewJestVersion) {
1154
1178
  cli = shimmer.wrap(
1155
1179
  cli,
@@ -1536,7 +1560,7 @@ function coverageReporterWrapper (coverageReporter) {
1536
1560
  addHook({
1537
1561
  name: '@jest/core',
1538
1562
  file: 'build/TestScheduler.js',
1539
- versions: ['>=27.0.0'],
1563
+ versions: [MINIMUM_JEST_TEST_SCHEDULER_VERSION],
1540
1564
  }, (testSchedulerPackage, frameworkVersion) => {
1541
1565
  const oldCreateTestScheduler = testSchedulerPackage.createTestScheduler
1542
1566
  const newCreateTestScheduler = async function () {
@@ -1552,17 +1576,19 @@ addHook({
1552
1576
  return testSchedulerPackage
1553
1577
  })
1554
1578
 
1555
- addHook({
1556
- name: '@jest/core',
1557
- file: 'build/TestScheduler.js',
1558
- versions: ['>=24.8.0 <27.0.0'],
1559
- }, (testSchedulerPackage, frameworkVersion) => {
1560
- shimmer.wrap(
1561
- testSchedulerPackage.default.prototype,
1562
- 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
1563
- )
1564
- return testSchedulerPackage
1565
- })
1579
+ if (DD_MAJOR < 6) {
1580
+ addHook({
1581
+ name: '@jest/core',
1582
+ file: 'build/TestScheduler.js',
1583
+ versions: ['>=24.8.0 <27.0.0'],
1584
+ }, (testSchedulerPackage, frameworkVersion) => {
1585
+ shimmer.wrap(
1586
+ testSchedulerPackage.default.prototype,
1587
+ 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
1588
+ )
1589
+ return testSchedulerPackage
1590
+ })
1591
+ }
1566
1592
 
1567
1593
  addHook({
1568
1594
  name: '@jest/test-sequencer',
@@ -1582,16 +1608,18 @@ addHook({
1582
1608
  return sequencerPackage
1583
1609
  })
1584
1610
 
1585
- addHook({
1586
- name: '@jest/reporters',
1587
- file: 'build/coverage_reporter.js',
1588
- versions: ['>=24.8.0 <26.6.2'],
1589
- }, coverageReporterWrapper)
1611
+ if (DD_MAJOR < 6) {
1612
+ addHook({
1613
+ name: '@jest/reporters',
1614
+ file: 'build/coverage_reporter.js',
1615
+ versions: ['>=24.8.0 <26.6.2'],
1616
+ }, coverageReporterWrapper)
1617
+ }
1590
1618
 
1591
1619
  addHook({
1592
1620
  name: '@jest/reporters',
1593
1621
  file: 'build/CoverageReporter.js',
1594
- versions: ['>=26.6.2'],
1622
+ versions: [DD_MAJOR >= 6 ? '>=28.0.0' : '>=26.6.2'],
1595
1623
  }, coverageReporterWrapper)
1596
1624
 
1597
1625
  addHook({
@@ -1604,7 +1632,7 @@ addHook({
1604
1632
  addHook({
1605
1633
  name: '@jest/core',
1606
1634
  file: 'build/cli/index.js',
1607
- versions: ['>=24.8.0 <30.0.0'],
1635
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1608
1636
  }, getCliWrapper(false))
1609
1637
 
1610
1638
  addHook({
@@ -1691,7 +1719,7 @@ addHook({
1691
1719
  addHook({
1692
1720
  name: 'jest-circus',
1693
1721
  file: 'build/legacy-code-todo-rewrite/jestAdapter.js',
1694
- versions: ['>=24.8.0'],
1722
+ versions: [MINIMUM_JEST_VERSION],
1695
1723
  }, jestAdapterWrapper)
1696
1724
 
1697
1725
  function configureTestEnvironment (readConfigsResult) {
@@ -1828,7 +1856,7 @@ function wrapCreateScriptTransformer (createScriptTransformer) {
1828
1856
 
1829
1857
  addHook({
1830
1858
  name: '@jest/transform',
1831
- versions: ['>=24.8.0 <30.0.0'],
1859
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1832
1860
  file: 'build/ScriptTransformer.js',
1833
1861
  }, transformPackage => {
1834
1862
  transformPackage.createScriptTransformer = wrapCreateScriptTransformer(transformPackage.createScriptTransformer)
@@ -1848,20 +1876,22 @@ addHook({
1848
1876
  */
1849
1877
  addHook({
1850
1878
  name: '@jest/core',
1851
- versions: ['>=24.8.0 <30.0.0'],
1879
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1852
1880
  file: 'build/SearchSource.js',
1853
1881
  }, searchSourceWrapper)
1854
1882
 
1855
1883
  // from 25.1.0 on, readConfigs becomes async
1856
1884
  addHook({
1857
1885
  name: 'jest-config',
1858
- versions: ['>=25.1.0'],
1886
+ versions: [MINIMUM_JEST_CONFIG_ASYNC_VERSION],
1859
1887
  }, jestConfigAsyncWrapper)
1860
1888
 
1861
- addHook({
1862
- name: 'jest-config',
1863
- versions: ['24.8.0 - 24.9.0'],
1864
- }, jestConfigSyncWrapper)
1889
+ if (DD_MAJOR < 6) {
1890
+ addHook({
1891
+ name: 'jest-config',
1892
+ versions: ['24.8.0 - 24.9.0'],
1893
+ }, jestConfigSyncWrapper)
1894
+ }
1865
1895
 
1866
1896
  const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = new Set([
1867
1897
  'selenium-webdriver',
@@ -1876,7 +1906,7 @@ const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = new Set([
1876
1906
 
1877
1907
  addHook({
1878
1908
  name: 'jest-runtime',
1879
- versions: ['>=24.8.0'],
1909
+ versions: [MINIMUM_JEST_VERSION],
1880
1910
  }, (runtimePackage) => {
1881
1911
  const Runtime = runtimePackage.default ?? runtimePackage
1882
1912
 
@@ -2077,7 +2107,7 @@ function enqueueWrapper (enqueue) {
2077
2107
  */
2078
2108
  addHook({
2079
2109
  name: 'jest-worker',
2080
- versions: ['>=24.9.0 <30.0.0'],
2110
+ versions: [MINIMUM_JEST_WORKER_VERSION_BEFORE_30],
2081
2111
  file: 'build/workers/ChildProcessWorker.js',
2082
2112
  }, (childProcessWorker) => {
2083
2113
  const ChildProcessWorker = childProcessWorker.default
@@ -2092,7 +2122,7 @@ addHook({
2092
2122
 
2093
2123
  addHook({
2094
2124
  name: 'jest-worker',
2095
- versions: ['>=24.9.0 <30.0.0'],
2125
+ versions: [MINIMUM_JEST_WORKER_VERSION_BEFORE_30],
2096
2126
  file: 'build/workers/NodeThreadsWorker.js',
2097
2127
  }, (nodeThreadsWorker) => {
2098
2128
  const ExperimentalWorker = nodeThreadsWorker.default
@@ -3,8 +3,11 @@
3
3
  const { addHook, channel } = require('../helpers/instrument')
4
4
  const shimmer = require('../../../datadog-shimmer')
5
5
  const { getCallSites } = require('../../../dd-trace/src/plugins/util/stacktrace')
6
+ const { DD_MAJOR } = require('../../../../version')
6
7
  const { testToStartLine } = require('./utils')
7
8
 
9
+ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
10
+
8
11
  const parameterizedTestCh = channel('ci:mocha:test:parameterize')
9
12
  const patched = new WeakSet()
10
13
 
@@ -33,7 +36,7 @@ addHook({
33
36
  // support for start line
34
37
  addHook({
35
38
  name: 'mocha',
36
- versions: ['>=5.2.0'],
39
+ versions: [MINIMUM_MOCHA_VERSION],
37
40
  file: 'lib/suite.js',
38
41
  }, (Suite) => {
39
42
  shimmer.wrap(Suite.prototype, 'addTest', addTest => function (test) {
@@ -1,6 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { createCoverageMap } = require('../../../../vendor/dist/istanbul-lib-coverage')
4
+ const satisfies = require('../../../../vendor/dist/semifies')
5
+ const { DD_MAJOR } = require('../../../../version')
4
6
  const { addHook, channel } = require('../helpers/instrument')
5
7
  const shimmer = require('../../../datadog-shimmer')
6
8
  const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
@@ -41,7 +43,10 @@ const {
41
43
 
42
44
  require('./common')
43
45
 
46
+ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
47
+
44
48
  const patched = new WeakSet()
49
+ let hasWarnedDeprecatedMochaVersion = false
45
50
 
46
51
  const unskippableSuites = []
47
52
  let suitesToSkip = []
@@ -79,6 +84,20 @@ const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
79
84
 
80
85
  const getCodeCoverageCh = channel('ci:nyc:get-coverage')
81
86
 
87
+ function warnDeprecatedMochaVersion (frameworkVersion) {
88
+ if (DD_MAJOR >= 6 || hasWarnedDeprecatedMochaVersion || !frameworkVersion ||
89
+ !satisfies(frameworkVersion, '<8.0.0')) {
90
+ return
91
+ }
92
+
93
+ hasWarnedDeprecatedMochaVersion = true
94
+ // eslint-disable-next-line no-console
95
+ console.warn(
96
+ 'dd-trace support for Mocha<8.0.0 is deprecated and will be removed in dd-trace v6. ' +
97
+ 'Please upgrade Mocha to >=8.0.0.'
98
+ )
99
+ }
100
+
82
101
  // Tests from workers do not come with `isFailed` method
83
102
  function isTestFailed (test) {
84
103
  if (test.isFailed) {
@@ -345,9 +364,11 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
345
364
  // It is called but skipped in parallel mode.
346
365
  addHook({
347
366
  name: 'mocha',
348
- versions: ['>=5.2.0'],
367
+ versions: [MINIMUM_MOCHA_VERSION],
349
368
  file: 'lib/mocha.js',
350
369
  }, (Mocha, frameworkVersion) => {
370
+ warnDeprecatedMochaVersion(frameworkVersion)
371
+
351
372
  shimmer.wrap(Mocha.prototype, 'run', run => function () {
352
373
  // Workers do not need to request any data, just run the tests
353
374
  if (!testFinishCh.hasSubscribers || getEnvironmentVariable('MOCHA_WORKER_ID') || this.options.parallel) {
@@ -401,7 +422,7 @@ addHook({
401
422
 
402
423
  addHook({
403
424
  name: 'mocha',
404
- versions: ['>=5.2.0'],
425
+ versions: [MINIMUM_MOCHA_VERSION],
405
426
  file: 'lib/cli/run-helpers.js',
406
427
  }, (run) => {
407
428
  // `runMocha` is an async function
@@ -432,7 +453,7 @@ addHook({
432
453
  // This hook is used to generate session, module, suite and test events
433
454
  addHook({
434
455
  name: 'mocha',
435
- versions: ['>=5.2.0'],
456
+ versions: [MINIMUM_MOCHA_VERSION],
436
457
  file: 'lib/runner.js',
437
458
  }, function (Runner, frameworkVersion) {
438
459
  if (patched.has(Runner)) return Runner
@@ -541,7 +562,7 @@ addHook({
541
562
  // Used to set the correct async resource to the test.
542
563
  addHook({
543
564
  name: 'mocha',
544
- versions: ['>=5.2.0'],
565
+ versions: [MINIMUM_MOCHA_VERSION],
545
566
  file: 'lib/runnable.js',
546
567
  }, (runnablePackage) => runnableWrapper(runnablePackage, config))
547
568
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { addHook, channel } = require('../helpers/instrument')
4
4
  const shimmer = require('../../../datadog-shimmer')
5
+ const { DD_MAJOR } = require('../../../../version')
5
6
 
6
7
  const {
7
8
  runnableWrapper,
@@ -15,6 +16,8 @@ const {
15
16
  } = require('./utils')
16
17
  require('./common')
17
18
 
19
+ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
20
+
18
21
  const workerFinishCh = channel('ci:mocha:worker:finish')
19
22
 
20
23
  const config = {}
@@ -64,7 +67,7 @@ addHook({
64
67
  // Runner is also hooked in mocha/main.js, but in here we only generate test events.
65
68
  addHook({
66
69
  name: 'mocha',
67
- versions: ['>=5.2.0'],
70
+ versions: [MINIMUM_MOCHA_VERSION],
68
71
  file: 'lib/runner.js',
69
72
  }, function (Runner) {
70
73
  shimmer.wrap(Runner.prototype, 'runTests', runTests => getRunTestsWrapper(runTests, config))
@@ -99,6 +102,6 @@ addHook({
99
102
  // Used to set the correct async resource to the test.
100
103
  addHook({
101
104
  name: 'mocha',
102
- versions: ['>=5.2.0'],
105
+ versions: [MINIMUM_MOCHA_VERSION],
103
106
  file: 'lib/runnable.js',
104
107
  }, (runnablePackage) => runnableWrapper(runnablePackage, config))
@@ -3,14 +3,10 @@
3
3
  const shimmer = require('../../datadog-shimmer')
4
4
  const tracer = require('../../dd-trace')
5
5
  const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
6
+ const { isFalse, isTrue } = require('../../dd-trace/src/util')
6
7
  const { addHook } = require('./helpers/instrument')
7
8
 
8
- const otelSdkEnabled = getValueFromEnvSources('DD_TRACE_OTEL_ENABLED') ||
9
- getValueFromEnvSources('OTEL_SDK_DISABLED')
10
- ? !getValueFromEnvSources('OTEL_SDK_DISABLED')
11
- : undefined
12
-
13
- if (otelSdkEnabled) {
9
+ if (isOtelSdkEnabled()) {
14
10
  addHook({
15
11
  name: '@opentelemetry/sdk-trace-node',
16
12
  file: 'build/src/NodeTracerProvider.js',
@@ -22,3 +18,12 @@ if (otelSdkEnabled) {
22
18
  return mod
23
19
  })
24
20
  }
21
+
22
+ function isOtelSdkEnabled () {
23
+ // Datadog explicit opt-out wins over every OTel signal; check it first.
24
+ const ddTraceOtelEnabled = getValueFromEnvSources('DD_TRACE_OTEL_ENABLED')
25
+ if (isFalse(ddTraceOtelEnabled)) return false
26
+ const otelSdkDisabled = getValueFromEnvSources('OTEL_SDK_DISABLED')
27
+ if (isTrue(otelSdkDisabled)) return false
28
+ return isTrue(ddTraceOtelEnabled) || isFalse(otelSdkDisabled)
29
+ }
@@ -69,8 +69,8 @@ class BullmqConsumerPlugin extends ConsumerPlugin {
69
69
  const ddCarrier = metadata._datadog
70
70
  if (!ddCarrier) return
71
71
 
72
- // Clean up only our _datadog key, preserve other metadata
73
- delete metadata._datadog
72
+ // Avoid `delete`'s hidden-class transition; JSON.stringify also omits undefined values.
73
+ metadata._datadog = undefined
74
74
  job.opts.telemetry.metadata = JSON.stringify(metadata)
75
75
 
76
76
  return ddCarrier
@@ -56,12 +56,14 @@ class BaseBullmqProducerPlugin extends ProducerPlugin {
56
56
  throw new Error('injectTraceContext must be implemented by subclass')
57
57
  }
58
58
 
59
+ // Returned so setProducerCheckpoint can mutate it without a second parse.
59
60
  _injectIntoOpts (span, opts) {
60
61
  const carrier = {}
61
62
  this.tracer.inject(span, 'text_map', carrier)
62
63
  const metadata = parseTelemetryMetadata(opts.telemetry?.metadata)
63
64
  metadata._datadog = carrier
64
65
  opts.telemetry = { metadata: JSON.stringify(metadata), omitContext: true }
66
+ return metadata
65
67
  }
66
68
 
67
69
  setProducerCheckpoint (span, ctx) {
@@ -70,7 +72,7 @@ class BaseBullmqProducerPlugin extends ProducerPlugin {
70
72
  const dataStreamsContext = this.tracer.setCheckpoint(edgeTags, span, payloadSize)
71
73
 
72
74
  if (optsTarget && typeof optsTarget === 'object') {
73
- const metadata = parseTelemetryMetadata(optsTarget.telemetry?.metadata)
75
+ const metadata = ctx._ddMetadata ?? parseTelemetryMetadata(optsTarget.telemetry?.metadata)
74
76
  DsmPathwayCodec.encode(dataStreamsContext, metadata._datadog || metadata)
75
77
  if (!metadata._datadog) metadata._datadog = {}
76
78
  optsTarget.telemetry = { metadata: JSON.stringify(metadata), omitContext: true }
@@ -110,7 +112,7 @@ class QueueAddPlugin extends BaseBullmqProducerPlugin {
110
112
 
111
113
  injectTraceContext (span, ctx) {
112
114
  const opts = this.#ensureOpts(ctx)
113
- this._injectIntoOpts(span, opts)
115
+ ctx._ddMetadata = this._injectIntoOpts(span, opts)
114
116
  }
115
117
 
116
118
  getDsmData (ctx) {
@@ -146,36 +148,28 @@ class QueueAddBulkPlugin extends BaseBullmqProducerPlugin {
146
148
  const jobs = ctx.arguments?.[0]
147
149
  if (!Array.isArray(jobs)) return
148
150
 
149
- for (const job of jobs) {
151
+ const cache = []
152
+ for (let i = 0; i < jobs.length; i++) {
153
+ const job = jobs[i]
150
154
  if (!job) continue
151
155
  job.opts = job.opts || {}
152
- this._injectIntoOpts(span, job.opts)
153
- }
154
- }
155
-
156
- getDsmData (ctx) {
157
- const jobs = ctx.arguments?.[0] || []
158
- const payloadSize = jobs.reduce((total, job) => {
159
- return total + (job?.data ? getMessageSize(job.data) : 0)
160
- }, 0)
161
- return {
162
- queueName: ctx.self?.name || 'bullmq',
163
- payloadSize,
164
- optsTarget: jobs[0]?.opts,
156
+ cache[i] = this._injectIntoOpts(span, job.opts)
165
157
  }
158
+ ctx._ddMetadata = cache
166
159
  }
167
160
 
168
161
  setProducerCheckpoint (span, ctx) {
169
162
  const jobs = ctx.arguments?.[0] || []
170
163
  const queueName = ctx.self?.name || 'bullmq'
171
164
  const edgeTags = ['direction:out', `topic:${queueName}`, 'type:bullmq']
165
+ const cache = ctx._ddMetadata
172
166
 
173
- for (const job of jobs) {
167
+ for (let i = 0; i < jobs.length; i++) {
168
+ const job = jobs[i]
174
169
  if (!job?.data) continue
175
170
  const payloadSize = getMessageSize(job.data)
176
171
  const dataStreamsContext = this.tracer.setCheckpoint(edgeTags, span, payloadSize)
177
- job.opts = job.opts || {}
178
- const metadata = parseTelemetryMetadata(job.opts.telemetry?.metadata)
172
+ const metadata = cache?.[i] ?? parseTelemetryMetadata(job.opts.telemetry?.metadata)
179
173
  DsmPathwayCodec.encode(dataStreamsContext, metadata._datadog || metadata)
180
174
  if (!metadata._datadog) metadata._datadog = {}
181
175
  job.opts.telemetry = { metadata: JSON.stringify(metadata), omitContext: true }
@@ -201,7 +195,7 @@ class FlowProducerAddPlugin extends BaseBullmqProducerPlugin {
201
195
  const flow = ctx.arguments?.[0]
202
196
  if (!flow) return
203
197
  flow.opts = flow.opts || {}
204
- this._injectIntoOpts(span, flow.opts)
198
+ ctx._ddMetadata = this._injectIntoOpts(span, flow.opts)
205
199
  }
206
200
 
207
201
  getDsmData (ctx) {
@@ -4,6 +4,7 @@
4
4
  const { performance } = require('perf_hooks')
5
5
  const dateNow = Date.now
6
6
 
7
+ const satisfies = require('../../../vendor/dist/semifies')
7
8
  const {
8
9
  TEST_STATUS,
9
10
  TEST_IS_RUM_ACTIVE,
@@ -108,6 +109,7 @@ const {
108
109
  } = require('./source-map-utils')
109
110
 
110
111
  const TEST_FRAMEWORK_NAME = 'cypress'
112
+ let hasWarnedDeprecatedCypressVersion = false
111
113
 
112
114
  const CYPRESS_STATUS_TO_TEST_STATUS = {
113
115
  passed: 'pass',
@@ -136,6 +138,20 @@ function getCypressVersion (details) {
136
138
  return ''
137
139
  }
138
140
 
141
+ function warnDeprecatedCypressVersion (version) {
142
+ if (DD_MAJOR >= 6 || hasWarnedDeprecatedCypressVersion || !version || !satisfies(version, '<12.0.0')) {
143
+ return
144
+ }
145
+
146
+ hasWarnedDeprecatedCypressVersion = true
147
+ // console.warn does not seem to work reliably in Cypress, so use console.log instead.
148
+ // eslint-disable-next-line no-console
149
+ console.log(
150
+ 'WARNING: dd-trace support for Cypress<12.0.0 is deprecated' +
151
+ ' and will not be supported in dd-trace v6. Please upgrade Cypress to >=12.0.0.'
152
+ )
153
+ }
154
+
139
155
  function getRootDir (details) {
140
156
  if (details?.config) {
141
157
  return details.config.projectRoot || details.config.repoRoot || process.cwd()
@@ -434,6 +450,7 @@ class CypressPlugin {
434
450
  this._isInit = true
435
451
  this.tracer = tracer
436
452
  this.cypressConfig = cypressConfig
453
+ warnDeprecatedCypressVersion(cypressConfig.version)
437
454
 
438
455
  this.isTestIsolationEnabled = getIsTestIsolationEnabled(cypressConfig)
439
456