dd-trace 5.66.0 → 5.68.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 (107) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/README.md +0 -2
  3. package/ci/init.js +52 -54
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +85 -2
  7. package/initialize.mjs +1 -1
  8. package/package.json +8 -11
  9. package/packages/datadog-esbuild/index.js +56 -0
  10. package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
  11. package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
  12. package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
  13. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  14. package/packages/datadog-instrumentations/src/connect.js +6 -2
  15. package/packages/datadog-instrumentations/src/cucumber.js +31 -6
  16. package/packages/datadog-instrumentations/src/express.js +5 -6
  17. package/packages/datadog-instrumentations/src/fastify.js +3 -3
  18. package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +11 -2
  21. package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
  22. package/packages/datadog-instrumentations/src/http2/client.js +1 -0
  23. package/packages/datadog-instrumentations/src/http2/server.js +0 -1
  24. package/packages/datadog-instrumentations/src/ioredis.js +12 -1
  25. package/packages/datadog-instrumentations/src/jest.js +48 -36
  26. package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
  27. package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
  28. package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
  29. package/packages/datadog-instrumentations/src/mongoose.js +2 -1
  30. package/packages/datadog-instrumentations/src/oracledb.js +19 -13
  31. package/packages/datadog-instrumentations/src/pg.js +9 -5
  32. package/packages/datadog-instrumentations/src/pino.js +18 -6
  33. package/packages/datadog-instrumentations/src/playwright.js +15 -1
  34. package/packages/datadog-instrumentations/src/sequelize.js +1 -1
  35. package/packages/datadog-instrumentations/src/vitest.js +155 -62
  36. package/packages/datadog-plugin-ai/src/tracing.js +3 -3
  37. package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
  38. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
  39. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
  40. package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
  41. package/packages/datadog-plugin-cucumber/src/index.js +4 -56
  42. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -2
  43. package/packages/datadog-plugin-cypress/src/support.js +4 -0
  44. package/packages/datadog-plugin-express/src/code_origin.js +2 -2
  45. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
  46. package/packages/datadog-plugin-jest/src/index.js +0 -21
  47. package/packages/datadog-plugin-mocha/src/index.js +3 -57
  48. package/packages/datadog-plugin-mongodb-core/src/index.js +20 -7
  49. package/packages/datadog-plugin-playwright/src/index.js +11 -5
  50. package/packages/datadog-plugin-vitest/src/index.js +5 -1
  51. package/packages/datadog-plugin-ws/src/close.js +1 -1
  52. package/packages/datadog-plugin-ws/src/producer.js +6 -1
  53. package/packages/datadog-plugin-ws/src/receiver.js +6 -1
  54. package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
  55. package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
  56. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
  57. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
  58. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
  59. package/packages/dd-trace/src/config.js +69 -304
  60. package/packages/dd-trace/src/config_defaults.js +186 -0
  61. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
  62. package/packages/dd-trace/src/datastreams/fnv.js +2 -2
  63. package/packages/dd-trace/src/datastreams/writer.js +3 -2
  64. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
  65. package/packages/dd-trace/src/dogstatsd.js +4 -3
  66. package/packages/dd-trace/src/encode/0.4.js +1 -5
  67. package/packages/dd-trace/src/exporter.js +1 -0
  68. package/packages/dd-trace/src/exporters/agent/index.js +3 -2
  69. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  70. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
  71. package/packages/dd-trace/src/exporters/common/request.js +2 -1
  72. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
  73. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  74. package/packages/dd-trace/src/llmobs/index.js +7 -0
  75. package/packages/dd-trace/src/llmobs/plugins/ai/index.js +4 -3
  76. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -1
  77. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
  78. package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
  79. package/packages/dd-trace/src/llmobs/sdk.js +28 -0
  80. package/packages/dd-trace/src/llmobs/span_processor.js +124 -28
  81. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  82. package/packages/dd-trace/src/llmobs/telemetry.js +9 -2
  83. package/packages/dd-trace/src/log/index.js +28 -17
  84. package/packages/dd-trace/src/log/log.js +29 -5
  85. package/packages/dd-trace/src/log/writer.js +5 -5
  86. package/packages/dd-trace/src/noop/span.js +1 -0
  87. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  88. package/packages/dd-trace/src/opentracing/span.js +18 -4
  89. package/packages/dd-trace/src/plugin_manager.js +20 -2
  90. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
  91. package/packages/dd-trace/src/plugins/index.js +2 -0
  92. package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
  93. package/packages/dd-trace/src/plugins/util/git.js +40 -26
  94. package/packages/dd-trace/src/plugins/util/test.js +37 -27
  95. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  96. package/packages/dd-trace/src/profiler.js +4 -1
  97. package/packages/dd-trace/src/profiling/config.js +73 -42
  98. package/packages/dd-trace/src/profiling/profiler.js +3 -1
  99. package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
  100. package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
  101. package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
  102. package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
  103. package/packages/dd-trace/src/remote_config/manager.js +3 -2
  104. package/packages/dd-trace/src/startup-log.js +2 -1
  105. package/packages/dd-trace/src/supported-configurations.json +3 -0
  106. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  107. package/register.js +1 -1
@@ -82,10 +82,14 @@ let testManagementAttemptToFixRetries = 0
82
82
  let testManagementTests = {}
83
83
  let modifiedTests = {}
84
84
  let numTestRetries = 0
85
- let knownTests = []
85
+ let knownTests = {}
86
86
  let skippedSuites = []
87
87
  let isSuitesSkipped = false
88
88
 
89
+ function isValidKnownTests (receivedKnownTests) {
90
+ return !!receivedKnownTests.cucumber
91
+ }
92
+
89
93
  function getSuiteStatusFromTestStatuses (testStatuses) {
90
94
  if (testStatuses.includes('fail')) {
91
95
  return 'fail'
@@ -123,7 +127,10 @@ function getStatusFromResultLatest (result) {
123
127
  }
124
128
 
125
129
  function isNewTest (testSuite, testName) {
126
- const testsForSuite = knownTests.cucumber?.[testSuite] || []
130
+ if (!isValidKnownTests(knownTests)) {
131
+ return false
132
+ }
133
+ const testsForSuite = knownTests.cucumber[testSuite] || []
127
134
  return !testsForSuite.includes(testName)
128
135
  }
129
136
 
@@ -508,9 +515,9 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
508
515
  pickleByFile = isCoordinator ? getPickleByFileNew(this) : getPickleByFile(this)
509
516
 
510
517
  if (isKnownTestsEnabled) {
511
- const isFaulty = getIsFaultyEarlyFlakeDetection(
518
+ const isFaulty = !isValidKnownTests(knownTests) || getIsFaultyEarlyFlakeDetection(
512
519
  Object.keys(pickleByFile),
513
- knownTests.cucumber || {},
520
+ knownTests.cucumber,
514
521
  earlyFlakeDetectionFaultyThreshold
515
522
  )
516
523
  if (isFaulty) {
@@ -592,6 +599,9 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
592
599
  // Handles EFD in both the main process and the worker process.
593
600
  function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = false, isWorker = false) {
594
601
  return async function () {
602
+ if (!testSuiteFinishCh.hasSubscribers) {
603
+ return runTestCaseFunction.apply(this, arguments)
604
+ }
595
605
  const pickle = isNewerCucumberVersion
596
606
  ? arguments[0].pickle
597
607
  : this.eventDataCollector.getPickle(arguments[0])
@@ -747,6 +757,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
747
757
 
748
758
  function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
749
759
  return function (worker, message) {
760
+ if (!testSuiteFinishCh.hasSubscribers) {
761
+ return parseWorkerMessageFunction.apply(this, arguments)
762
+ }
750
763
  // If the message is an array, it's a dd-trace message, so we need to stop cucumber processing,
751
764
  // or cucumber will throw an error
752
765
  // TODO: identify the message better
@@ -972,10 +985,17 @@ addHook({
972
985
  )
973
986
  // EFD in parallel mode only supported in >=11.0.0
974
987
  shimmer.wrap(adapterPackage.ChildProcessAdapter.prototype, 'startWorker', startWorker => function () {
975
- if (isKnownTestsEnabled) {
988
+ if (isKnownTestsEnabled && isValidKnownTests(knownTests)) {
989
+ this.options.worldParameters._ddIsKnownTestsEnabled = true
976
990
  this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
977
991
  this.options.worldParameters._ddKnownTests = knownTests
978
992
  this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
993
+ } else {
994
+ isEarlyFlakeDetectionEnabled = false
995
+ isKnownTestsEnabled = false
996
+ this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
997
+ this.options.worldParameters._ddIsKnownTestsEnabled = false
998
+ this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = 0
979
999
  }
980
1000
 
981
1001
  if (isImpactedTestsEnabled) {
@@ -1001,9 +1021,14 @@ addHook({
1001
1021
  'initialize',
1002
1022
  initialize => async function () {
1003
1023
  await initialize.apply(this, arguments)
1004
- isKnownTestsEnabled = !!this.options.worldParameters._ddKnownTests
1024
+ isKnownTestsEnabled = !!this.options.worldParameters._ddIsKnownTestsEnabled
1005
1025
  if (isKnownTestsEnabled) {
1006
1026
  knownTests = this.options.worldParameters._ddKnownTests
1027
+ // if for whatever reason the worker does not receive valid known tests, we disable EFD and known tests
1028
+ if (!isValidKnownTests(knownTests)) {
1029
+ isKnownTestsEnabled = false
1030
+ knownTests = {}
1031
+ }
1007
1032
  }
1008
1033
  isEarlyFlakeDetectionEnabled = !!this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled
1009
1034
  if (isEarlyFlakeDetectionEnabled) {
@@ -2,8 +2,7 @@
2
2
 
3
3
  const { createWrapRouterMethod } = require('./router')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { addHook, channel } = require('./helpers/instrument')
6
- const tracingChannel = require('dc-polyfill').tracingChannel
5
+ const { addHook, channel, tracingChannel } = require('./helpers/instrument')
7
6
 
8
7
  const handleChannel = channel('apm:express:request:handle')
9
8
 
@@ -57,7 +56,7 @@ function wrapResponseRender (render) {
57
56
  }
58
57
  }
59
58
 
60
- addHook({ name: 'express', versions: ['>=4'] }, express => {
59
+ addHook({ name: 'express', versions: ['>=4'], file: ['lib/express.js'] }, express => {
61
60
  shimmer.wrap(express.application, 'handle', wrapHandle)
62
61
 
63
62
  shimmer.wrap(express.response, 'json', wrapResponseJson)
@@ -70,7 +69,7 @@ addHook({ name: 'express', versions: ['>=4'] }, express => {
70
69
  // Express 5 does not rely on router in the same way as v4 and should not be instrumented anymore.
71
70
  // It would otherwise produce spans for router and express, and so duplicating them.
72
71
  // We now fall back to router instrumentation
73
- addHook({ name: 'express', versions: ['4'] }, express => {
72
+ addHook({ name: 'express', versions: ['4'], file: 'lib/express.js' }, express => {
74
73
  shimmer.wrap(express.Router, 'use', wrapRouterMethod)
75
74
  shimmer.wrap(express.Router, 'route', wrapRouterMethod)
76
75
 
@@ -132,12 +131,12 @@ function wrapProcessParamsMethod (requestPositionInArguments) {
132
131
  }
133
132
  }
134
133
 
135
- addHook({ name: 'express', versions: ['>=4.0.0 <4.3.0'] }, express => {
134
+ addHook({ name: 'express', versions: ['>=4.0.0 <4.3.0'], file: ['lib/express.js'] }, express => {
136
135
  shimmer.wrap(express.Router, 'process_params', wrapProcessParamsMethod(1))
137
136
  return express
138
137
  })
139
138
 
140
- addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'] }, express => {
139
+ addHook({ name: 'express', versions: ['>=4.3.0 <5.0.0'], file: ['lib/express.js'] }, express => {
141
140
  shimmer.wrap(express.Router, 'process_params', wrapProcessParamsMethod(2))
142
141
  return express
143
142
  })
@@ -265,7 +265,7 @@ function canPublishResponsePayload (payload) {
265
265
  !ArrayBuffer.isView(payload) // TypedArray
266
266
  }
267
267
 
268
- addHook({ name: 'fastify', versions: ['>=3'] }, fastify => {
268
+ addHook({ name: 'fastify', versions: ['>=3'] }, (fastify) => {
269
269
  const wrapped = shimmer.wrapFunction(fastify, fastify => wrapFastify(fastify, true))
270
270
 
271
271
  wrapped.fastify = wrapped
@@ -274,11 +274,11 @@ addHook({ name: 'fastify', versions: ['>=3'] }, fastify => {
274
274
  return wrapped
275
275
  })
276
276
 
277
- addHook({ name: 'fastify', versions: ['2'] }, fastify => {
277
+ addHook({ name: 'fastify', versions: ['2'] }, (fastify) => {
278
278
  return shimmer.wrapFunction(fastify, fastify => wrapFastify(fastify, true))
279
279
  })
280
280
 
281
- addHook({ name: 'fastify', versions: ['1'] }, fastify => {
281
+ addHook({ name: 'fastify', versions: ['1'] }, (fastify) => {
282
282
  return shimmer.wrapFunction(fastify, fastify => wrapFastify(fastify, false))
283
283
  })
284
284
 
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
-
3
- const path = require('path')
4
2
  const iitm = require('../../../dd-trace/src/iitm')
3
+ const path = require('path')
5
4
  const ritm = require('../../../dd-trace/src/ritm')
6
5
 
7
6
  /**
@@ -20,29 +19,43 @@ function Hook (modules, hookOptions, onrequire) {
20
19
  }
21
20
 
22
21
  this._patched = Object.create(null)
22
+ const patched = new WeakMap()
23
23
 
24
- const safeHook = (moduleExports, moduleName, moduleBaseDir, moduleVersion) => {
24
+ const safeHook = (moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm) => {
25
25
  const parts = [moduleBaseDir, moduleName].filter(Boolean)
26
26
  const filename = path.join(...parts)
27
27
 
28
- if (this._patched[filename]) return moduleExports
28
+ if (this._patched[filename] && patched.has(moduleExports)) {
29
+ return patched.get(moduleExports)
30
+ }
29
31
 
30
- this._patched[filename] = true
32
+ let defaultWrapResult
33
+
34
+ if (
35
+ isIitm &&
36
+ moduleExports.default &&
37
+ (typeof moduleExports.default === 'object' ||
38
+ typeof moduleExports.default === 'function')
39
+ ) {
40
+ defaultWrapResult = onrequire(moduleExports.default, moduleName, moduleBaseDir, moduleVersion, isIitm)
41
+ }
42
+
43
+ const newExports = onrequire(moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm)
44
+
45
+ if (defaultWrapResult) newExports.default = defaultWrapResult
31
46
 
32
- return onrequire(moduleExports, moduleName, moduleBaseDir, moduleVersion)
47
+ this._patched[filename] = true
48
+ if (newExports &&
49
+ (typeof newExports === 'object' ||
50
+ typeof newExports === 'function')) {
51
+ patched.set(moduleExports, newExports)
52
+ }
53
+ return newExports
33
54
  }
34
55
 
35
56
  this._ritmHook = ritm(modules, {}, safeHook)
36
57
  this._iitmHook = iitm(modules, hookOptions, (moduleExports, moduleName, moduleBaseDir) => {
37
- // TODO: Move this logic to import-in-the-middle and only do it for CommonJS
38
- // modules and not ESM. In the meantime, all the modules we instrument are
39
- // CommonJS modules for which the default export is always moved to
40
- // `default` anyway.
41
- if (moduleExports && moduleExports.default) {
42
- moduleExports.default = safeHook(moduleExports.default, moduleName, moduleBaseDir)
43
- return moduleExports
44
- }
45
- return safeHook(moduleExports, moduleName, moduleBaseDir)
58
+ return safeHook(moduleExports, moduleName, moduleBaseDir, null, true)
46
59
  })
47
60
  }
48
61
 
@@ -16,6 +16,7 @@ module.exports = {
16
16
  '@graphql-tools/executor': () => require('../graphql'),
17
17
  '@grpc/grpc-js': () => require('../grpc'),
18
18
  '@hapi/hapi': () => require('../hapi'),
19
+ '@happy-dom/jest-environment': () => require('../jest'),
19
20
  '@jest/core': () => require('../jest'),
20
21
  '@jest/reporters': () => require('../jest'),
21
22
  '@jest/test-sequencer': () => require('../jest'),
@@ -130,6 +131,7 @@ module.exports = {
130
131
  sequelize: () => require('../sequelize'),
131
132
  sharedb: () => require('../sharedb'),
132
133
  tedious: () => require('../tedious'),
134
+ tinypool: { esmFirst: true, fn: () => require('../vitest') },
133
135
  undici: () => require('../undici'),
134
136
  url: () => require('../url'),
135
137
  vitest: { esmFirst: true, fn: () => require('../vitest') },
@@ -13,6 +13,15 @@ exports.channel = function (name) {
13
13
  return ch
14
14
  }
15
15
 
16
+ const tracingChannelMap = {}
17
+ exports.tracingChannel = function (name) {
18
+ const maybe = tracingChannelMap[name]
19
+ if (maybe) return maybe
20
+ const tc = dc.tracingChannel(name)
21
+ tracingChannelMap[name] = tc
22
+ return tc
23
+ }
24
+
16
25
  /**
17
26
  * @param {string} args.name module name
18
27
  * @param {string[]} args.versions array of semver range strings
@@ -20,7 +29,7 @@ exports.channel = function (name) {
20
29
  * @param {string} args.filePattern pattern to match files within package to instrument
21
30
  * @param Function hook
22
31
  */
23
- exports.addHook = function addHook ({ name, versions, file, filePattern }, hook) {
32
+ exports.addHook = function addHook ({ name, versions, file, filePattern, patchDefault }, hook) {
24
33
  if (typeof name === 'string') {
25
34
  name = [name]
26
35
  }
@@ -29,7 +38,7 @@ exports.addHook = function addHook ({ name, versions, file, filePattern }, hook)
29
38
  if (!instrumentations[val]) {
30
39
  instrumentations[val] = []
31
40
  }
32
- instrumentations[val].push({ name: val, versions, file, filePattern, hook })
41
+ instrumentations[val].push({ name: val, versions, file, filePattern, hook, patchDefault })
33
42
  }
34
43
  }
35
44
 
@@ -66,7 +66,7 @@ for (const packageName of names) {
66
66
  // get the instrumentation file name to save all hooked versions
67
67
  const instrumentationFileName = parseHookInstrumentationFileName(packageName)
68
68
 
69
- Hook([packageName], hookOptions, (moduleExports, moduleName, moduleBaseDir, moduleVersion) => {
69
+ Hook([packageName], hookOptions, (moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm) => {
70
70
  moduleName = moduleName.replace(pathSepExpr, '/')
71
71
 
72
72
  // This executes the integration file thus adding its entries to `instrumentations`
@@ -77,7 +77,13 @@ for (const packageName of names) {
77
77
  }
78
78
 
79
79
  const namesAndSuccesses = {}
80
- for (const { name, file, versions, hook, filePattern } of instrumentations[packageName]) {
80
+ for (const { name, file, versions, hook, filePattern, patchDefault } of instrumentations[packageName]) {
81
+ if (patchDefault === false && !moduleExports.default && isIitm) {
82
+ return moduleExports
83
+ } else if (patchDefault === true && moduleExports.default && isIitm) {
84
+ moduleExports = moduleExports.default
85
+ }
86
+
81
87
  let fullFilePattern = filePattern
82
88
  const fullFilename = filename(name, file)
83
89
  if (fullFilePattern) {
@@ -137,7 +143,8 @@ for (const packageName of names) {
137
143
  // picked up due to the unification. Check what modules actually use the name.
138
144
  // TODO(BridgeAR): Only replace moduleExports if the hook returns a new value.
139
145
  // This allows to reduce the instrumentation code (no return needed).
140
- moduleExports = hook(moduleExports, version, name) ?? moduleExports
146
+
147
+ moduleExports = hook(moduleExports, version, name, isIitm) ?? moduleExports
141
148
  // Set the moduleExports in the hooks WeakSet
142
149
  hook[HOOK_SYMBOL].add(moduleExports)
143
150
  } catch (e) {
@@ -70,6 +70,7 @@ function wrapConnect (connect) {
70
70
 
71
71
  addHook({ name: names }, http2 => {
72
72
  shimmer.wrap(http2, 'connect', wrapConnect)
73
+ if (http2.default) http2.default.connect = http2.connect
73
74
 
74
75
  return http2
75
76
  })
@@ -18,7 +18,6 @@ const names = ['http2', 'node:http2']
18
18
  addHook({ name: names }, http2 => {
19
19
  shimmer.wrap(http2, 'createSecureServer', wrapCreateServer)
20
20
  shimmer.wrap(http2, 'createServer', wrapCreateServer)
21
- return http2
22
21
  })
23
22
 
24
23
  function wrapCreateServer (createServer) {
@@ -10,7 +10,7 @@ const startCh = channel('apm:ioredis:command:start')
10
10
  const finishCh = channel('apm:ioredis:command:finish')
11
11
  const errorCh = channel('apm:ioredis:command:error')
12
12
 
13
- addHook({ name: 'ioredis', versions: ['>=2'] }, Redis => {
13
+ function wrapRedis (Redis) {
14
14
  shimmer.wrap(Redis.prototype, 'sendCommand', sendCommand => function (command, stream) {
15
15
  if (!startCh.hasSubscribers) return sendCommand.apply(this, arguments)
16
16
 
@@ -35,8 +35,19 @@ addHook({ name: 'ioredis', versions: ['>=2'] }, Redis => {
35
35
  })
36
36
  })
37
37
  return Redis
38
+ }
39
+
40
+ addHook({ name: 'ioredis', versions: ['>=2 <4'], file: 'lib/redis.js' }, wrapRedis)
41
+
42
+ addHook({ name: 'ioredis', versions: ['>=4 <4.11.0'], file: 'built/redis.js' }, wrapRedis)
43
+
44
+ addHook({ name: 'ioredis', versions: ['>=4.11.0 <5'], file: 'built/redis/index.js' }, (exports) => {
45
+ wrapRedis(exports.default)
46
+ return exports
38
47
  })
39
48
 
49
+ addHook({ name: 'ioredis', versions: ['>=5'] }, wrapRedis)
50
+
40
51
  function finish (finishCh, errorCh, ctx, error) {
41
52
  if (error) {
42
53
  ctx.error = error
@@ -160,15 +160,15 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
160
160
  this.isImpactedTestsEnabled = this.testEnvironmentOptions._ddIsImpactedTestsEnabled
161
161
 
162
162
  if (this.isKnownTestsEnabled) {
163
+ earlyFlakeDetectionNumRetries = this.testEnvironmentOptions._ddEarlyFlakeDetectionNumRetries
163
164
  try {
164
- const hasKnownTests = !!knownTests?.jest
165
- earlyFlakeDetectionNumRetries = this.testEnvironmentOptions._ddEarlyFlakeDetectionNumRetries
166
- this.knownTestsForThisSuite = hasKnownTests
167
- ? (knownTests?.jest?.[this.testSuite] || [])
168
- : this.getKnownTestsForSuite(this.testEnvironmentOptions._ddKnownTests)
169
- log.debug(`this.knownTestsForThisSuite is an array: ${Array.isArray(this.knownTestsForThisSuite)}`)
170
- log.debug(`this.knownTestsForThisSuite is null: ${this.knownTestsForThisSuite === null}`)
171
- log.debug(`this.knownTestsForThisSuite is undefined: ${this.knownTestsForThisSuite === undefined}`)
165
+ this.knownTestsForThisSuite = this.getKnownTestsForSuite(this.testEnvironmentOptions._ddKnownTests)
166
+
167
+ if (!Array.isArray(this.knownTestsForThisSuite)) {
168
+ log.warn('this.knownTestsForThisSuite is not an array so new test and Early Flake detection is disabled.')
169
+ this.isEarlyFlakeDetectionEnabled = false
170
+ this.isKnownTestsEnabled = false
171
+ }
172
172
  } catch {
173
173
  // If there has been an error parsing the tests, we'll disable Early Flake Deteciton
174
174
  this.isEarlyFlakeDetectionEnabled = false
@@ -224,19 +224,20 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
224
224
  return hasSnapshotTests
225
225
  }
226
226
 
227
- // Function that receives a list of known tests for a test service and
228
- // returns the ones that belong to the current suite
229
- getKnownTestsForSuite (knownTests) {
230
- if (this.knownTestsForThisSuite) {
231
- return this.knownTestsForThisSuite
227
+ // This function returns an array if the known tests are valid and null otherwise.
228
+ getKnownTestsForSuite (suiteKnownTests) {
229
+ // `suiteKnownTests` is `this.testEnvironmentOptions._ddKnownTests`,
230
+ // which is only set if jest is configured to run in parallel.
231
+ if (suiteKnownTests) {
232
+ return suiteKnownTests
232
233
  }
233
- let knownTestsForSuite = knownTests
234
- // If jest is using workers, known tests are serialized to json.
235
- // If jest runs in band, they are not.
236
- if (typeof knownTestsForSuite === 'string') {
237
- knownTestsForSuite = JSON.parse(knownTestsForSuite)
234
+ // Global variable `knownTests` is set only in the main process.
235
+ // If jest is configured to run serially, the tests run in the same process, so `knownTests` is set.
236
+ // The assumption is that if the key `jest` is defined in the dictionary, the response is valid.
237
+ if (knownTests?.jest) {
238
+ return knownTests.jest[this.testSuite] || []
238
239
  }
239
- return knownTestsForSuite
240
+ return null
240
241
  }
241
242
 
242
243
  getTestManagementTestsForSuite (testManagementTests) {
@@ -472,10 +473,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
472
473
  }
473
474
  }
474
475
  if (this.isKnownTestsEnabled) {
475
- // Check if knownTestsForThisSuite is an array, since the worker may not have provided this information.
476
- // If it's null or undefined, we can't determine if the test is new.
477
- const isNew = Array.isArray(this.knownTestsForThisSuite) &&
478
- !this.knownTestsForThisSuite.includes(originalTestName)
476
+ const isNew = !this.knownTestsForThisSuite.includes(originalTestName)
479
477
  if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(originalTestName)) {
480
478
  retriedTestsToNumAttempts.set(originalTestName, 0)
481
479
  if (this.isEarlyFlakeDetectionEnabled) {
@@ -623,9 +621,17 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
623
621
  function getTestEnvironment (pkg, jestVersion) {
624
622
  if (pkg.default) {
625
623
  const wrappedTestEnvironment = getWrappedEnvironment(pkg.default, jestVersion)
626
- pkg.default = wrappedTestEnvironment
627
- pkg.TestEnvironment = wrappedTestEnvironment
628
- return pkg
624
+ return new Proxy(pkg, {
625
+ get (target, prop) {
626
+ if (prop === 'default') {
627
+ return wrappedTestEnvironment
628
+ }
629
+ if (prop === 'TestEnvironment') {
630
+ return wrappedTestEnvironment
631
+ }
632
+ return target[prop]
633
+ }
634
+ })
629
635
  }
630
636
  return getWrappedEnvironment(pkg, jestVersion)
631
637
  }
@@ -657,6 +663,11 @@ addHook({
657
663
  versions: ['>=24.8.0']
658
664
  }, getTestEnvironment)
659
665
 
666
+ addHook({
667
+ name: '@happy-dom/jest-environment',
668
+ versions: ['>=10.0.0']
669
+ }, getTestEnvironment)
670
+
660
671
  function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
661
672
  // `scheduleTests` is an async function
662
673
  return function (tests) {
@@ -681,8 +692,11 @@ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
681
692
 
682
693
  if (isKnownTestsEnabled) {
683
694
  const projectSuites = testPaths.tests.map(test => getTestSuitePath(test.path, test.context.config.rootDir))
684
- const isFaulty =
685
- getIsFaultyEarlyFlakeDetection(projectSuites, knownTests?.jest || {}, earlyFlakeDetectionFaultyThreshold)
695
+
696
+ // If the `jest` key does not exist in the known tests response, we consider the Early Flake detection faulty.
697
+ const isFaulty = !knownTests?.jest ||
698
+ getIsFaultyEarlyFlakeDetection(projectSuites, knownTests.jest, earlyFlakeDetectionFaultyThreshold)
699
+
686
700
  if (isFaulty) {
687
701
  log.error('Early flake detection is disabled because the number of new suites is too high.')
688
702
  isEarlyFlakeDetectionEnabled = false
@@ -1266,12 +1280,6 @@ const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = new Set([
1266
1280
  'winston'
1267
1281
  ])
1268
1282
 
1269
- function shouldBypassJestRequireEngine (moduleName) {
1270
- return (
1271
- LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)
1272
- )
1273
- }
1274
-
1275
1283
  addHook({
1276
1284
  name: 'jest-runtime',
1277
1285
  versions: ['>=24.8.0']
@@ -1283,6 +1291,10 @@ addHook({
1283
1291
  const suiteFilePath = this._testPath
1284
1292
 
1285
1293
  shimmer.wrap(result, 'mock', mock => function (moduleName) {
1294
+ // If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine
1295
+ if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
1296
+ LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.delete(moduleName)
1297
+ }
1286
1298
  if (suiteFilePath) {
1287
1299
  const existingMockedFiles = testSuiteMockedFiles.get(suiteFilePath) || []
1288
1300
  const suiteDir = path.dirname(suiteFilePath)
@@ -1297,7 +1309,7 @@ addHook({
1297
1309
 
1298
1310
  shimmer.wrap(Runtime.prototype, 'requireModuleOrMock', requireModuleOrMock => function (from, moduleName) {
1299
1311
  // TODO: do this for every library that we instrument
1300
- if (shouldBypassJestRequireEngine(moduleName)) {
1312
+ if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
1301
1313
  // To bypass jest's own require engine
1302
1314
  return this._requireCoreModule(moduleName)
1303
1315
  }
@@ -1336,7 +1348,7 @@ function sendWrapper (send) {
1336
1348
  // https://github.com/jestjs/jest/blob/1d682f21c7a35da4d3ab3a1436a357b980ebd0fa/packages/jest-worker/src/workers/ChildProcessWorker.ts#L424
1337
1349
  if (type === CHILD_MESSAGE_CALL) {
1338
1350
  // This is the message that the main process sends to the worker to run a test suite (=test file).
1339
- // In here we modify the config.testEnvironmentOptions to include the known tests for the suite.
1351
+ // In here we modify the `config.testEnvironmentOptions` to include the known tests for the suite.
1340
1352
  // This way the suite only knows about the tests that are part of it.
1341
1353
  const args = request.at(-1)
1342
1354
  if (args.length > 1) {
@@ -13,7 +13,8 @@ function wrapRequest (original) {
13
13
 
14
14
  addHook({
15
15
  name: 'limitd-client',
16
- versions: ['>=2.8']
16
+ versions: ['>=2.8'],
17
+ file: ['client.js']
17
18
  }, LimitdClient => {
18
19
  shimmer.wrap(LimitdClient.prototype, '_directRequest', wrapRequest)
19
20
  shimmer.wrap(LimitdClient.prototype, '_retriedRequest', wrapRequest)
@@ -663,14 +663,22 @@ addHook({
663
663
  const newWorkerArgs = { ...workerArgs }
664
664
 
665
665
  if (config.isKnownTestsEnabled) {
666
- const testSuiteKnownTests = config.knownTests.mocha?.[testPath] || []
667
- newWorkerArgs._ddEfdNumRetries = config.earlyFlakeDetectionNumRetries
668
- newWorkerArgs._ddIsEfdEnabled = config.isEarlyFlakeDetectionEnabled
669
- newWorkerArgs._ddIsKnownTestsEnabled = true
670
- newWorkerArgs._ddKnownTests = {
671
- mocha: {
672
- [testPath]: testSuiteKnownTests
666
+ if (config.knownTests?.mocha) {
667
+ const testSuiteKnownTests = config.knownTests.mocha[testPath] || []
668
+ newWorkerArgs._ddEfdNumRetries = config.earlyFlakeDetectionNumRetries
669
+ newWorkerArgs._ddIsEfdEnabled = config.isEarlyFlakeDetectionEnabled
670
+ newWorkerArgs._ddIsKnownTestsEnabled = true
671
+ newWorkerArgs._ddKnownTests = {
672
+ mocha: {
673
+ [testPath]: testSuiteKnownTests
674
+ }
673
675
  }
676
+ } else {
677
+ config.isEarlyFlakeDetectionEnabled = false
678
+ config.isKnownTestsEnabled = false
679
+ newWorkerArgs._ddIsKnownTestsEnabled = false
680
+ newWorkerArgs._ddIsEfdEnabled = false
681
+ newWorkerArgs._ddKnownTests = {}
674
682
  }
675
683
  }
676
684
  if (config.isTestManagementTestsEnabled) {
@@ -56,6 +56,9 @@ function getTestProperties (test, testManagementTests) {
56
56
  }
57
57
 
58
58
  function isNewTest (test, knownTests) {
59
+ if (!knownTests?.mocha) { // invalid response, so we won't consider it as new
60
+ return false
61
+ }
59
62
  const testSuite = getTestSuitePath(test.file, process.cwd())
60
63
  const testName = removeEfdStringFromTestName(test.fullTitle())
61
64
  const testsForSuite = knownTests.mocha?.[testSuite] || []
@@ -24,7 +24,8 @@ function wrapAddQueue (addQueue) {
24
24
 
25
25
  addHook({
26
26
  name: 'mongoose',
27
- versions: ['>=4.6.4 <5', '5', '6', '>=7']
27
+ versions: ['>=4.6.4 <5', '5', '6', '>=7'],
28
+ file: 'lib/index.js'
28
29
  }, mongoose => {
29
30
  // As of Mongoose 7, custom promise libraries are no longer supported and mongoose.Promise may be undefined
30
31
  if (mongoose.Promise && mongoose.Promise !== global.Promise) {
@@ -20,7 +20,7 @@ function finish (ctx) {
20
20
  finishChannel.publish(ctx)
21
21
  }
22
22
 
23
- addHook({ name: 'oracledb', versions: ['>=5'] }, oracledb => {
23
+ addHook({ name: 'oracledb', versions: ['>=5'], file: 'lib/oracledb.js' }, oracledb => {
24
24
  shimmer.wrap(oracledb.Connection.prototype, 'execute', execute => {
25
25
  return function wrappedExecute (dbQuery, ...args) {
26
26
  if (!startChannel.hasSubscribers) {
@@ -40,22 +40,28 @@ addHook({ name: 'oracledb', versions: ['>=5'] }, oracledb => {
40
40
  })
41
41
  }
42
42
 
43
- // The connAttrs are used to pass through the argument to the potential
44
- // serviceName method a user might have passed through as well as parsing
45
- // the connection string in v5.
46
- const connAttrs = connectionAttributes.get(this)
47
-
48
- const details = typeof this.hostName === 'string' ? this : this._impl
49
-
50
43
  let hostname
51
44
  let port
52
45
  let dbInstance
53
46
 
54
- if (details) {
55
- dbInstance = details.serviceName
56
- hostname = details.hostName ?? details.nscon?.ntAdapter?.hostName
57
- port = String(details.port ?? details.nscon?.ntAdapter?.port ?? '')
58
- }
47
+ try {
48
+ if (this.thin) {
49
+ const details = this._impl ?? this
50
+ // Prefer public getters when available (v6), fallback to nscon in v5.
51
+ dbInstance = this.serviceName ?? details.serviceName
52
+ hostname = this.hostName ?? details.nscon?.ntAdapter?.hostName
53
+ const p = this.port ?? details.nscon?.ntAdapter?.port
54
+ if (p != null) port = String(p)
55
+ } else {
56
+ // Avoid host/port getters in thick mode, as they may throw.
57
+ dbInstance = this.serviceName
58
+ }
59
+ } catch {}
60
+
61
+ // The connAttrs are used to pass through the argument to the potential
62
+ // serviceName method a user might have passed through as well as parsing
63
+ // the connection string in v5 as well as in thick mode.
64
+ const connAttrs = connectionAttributes.get(this)
59
65
 
60
66
  const ctx = {
61
67
  dbInstance,
@@ -15,17 +15,21 @@ const finishPoolQueryCh = channel('datadog:pg:pool:query:finish')
15
15
 
16
16
  const { errorMonitor } = require('node:events')
17
17
 
18
- addHook({ name: 'pg', versions: ['>=8.0.3'] }, pg => {
19
- shimmer.wrap(pg.Client.prototype, 'query', query => wrapQuery(query))
20
- shimmer.wrap(pg.Pool.prototype, 'query', query => wrapPoolQuery(query))
21
- return pg
18
+ addHook({ name: 'pg', versions: ['>=8.0.3'], file: 'lib/native/client.js' }, Client => {
19
+ shimmer.wrap(Client.prototype, 'query', query => wrapQuery(query))
20
+ return Client
22
21
  })
23
22
 
24
- addHook({ name: 'pg', file: 'lib/native/index.js', versions: ['>=8.0.3'] }, Client => {
23
+ addHook({ name: 'pg', versions: ['>=8.0.3 <8.15.0', '>=8.15.0 <9'], file: 'lib/client.js' }, Client => {
25
24
  shimmer.wrap(Client.prototype, 'query', query => wrapQuery(query))
26
25
  return Client
27
26
  })
28
27
 
28
+ addHook({ name: 'pg', versions: ['>=8.0.3'] }, pg => {
29
+ shimmer.wrap(pg.Pool.prototype, 'query', query => wrapPoolQuery(query))
30
+ return pg
31
+ })
32
+
29
33
  function wrapQuery (query) {
30
34
  return function () {
31
35
  if (!startCh.hasSubscribers) {