dd-trace 4.18.0 → 5.6.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 (209) hide show
  1. package/CONTRIBUTING.md +98 -0
  2. package/LICENSE-3rdparty.csv +4 -5
  3. package/MIGRATING.md +15 -0
  4. package/README.md +20 -140
  5. package/ci/cypress/after-run.js +1 -0
  6. package/ci/cypress/after-spec.js +1 -0
  7. package/ci/init.js +1 -4
  8. package/ext/kinds.d.ts +1 -0
  9. package/ext/kinds.js +2 -1
  10. package/ext/tags.d.ts +2 -1
  11. package/ext/tags.js +6 -1
  12. package/index.d.ts +1523 -1460
  13. package/package.json +19 -19
  14. package/packages/datadog-core/src/storage/async_resource.js +1 -1
  15. package/packages/datadog-core/src/utils/src/get.js +11 -0
  16. package/packages/datadog-core/src/utils/src/has.js +14 -0
  17. package/packages/datadog-core/src/utils/src/kebabcase.js +16 -0
  18. package/packages/datadog-core/src/utils/src/pick.js +11 -0
  19. package/packages/datadog-core/src/utils/src/set.js +16 -0
  20. package/packages/datadog-core/src/utils/src/uniq.js +5 -0
  21. package/packages/datadog-esbuild/index.js +1 -20
  22. package/packages/datadog-instrumentations/src/aerospike.js +47 -0
  23. package/packages/datadog-instrumentations/src/amqplib.js +2 -2
  24. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  25. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  26. package/packages/datadog-instrumentations/src/child_process.js +150 -0
  27. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  28. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  29. package/packages/datadog-instrumentations/src/cucumber.js +163 -46
  30. package/packages/datadog-instrumentations/src/dns.js +2 -1
  31. package/packages/datadog-instrumentations/src/express.js +20 -0
  32. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  33. package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
  34. package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
  35. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
  36. package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -3
  37. package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
  38. package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
  39. package/packages/datadog-instrumentations/src/http/client.js +12 -2
  40. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  41. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  42. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  43. package/packages/datadog-instrumentations/src/jest.js +239 -52
  44. package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
  45. package/packages/datadog-instrumentations/src/mocha.js +154 -18
  46. package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
  47. package/packages/datadog-instrumentations/src/mongoose.js +23 -10
  48. package/packages/datadog-instrumentations/src/mquery.js +65 -0
  49. package/packages/datadog-instrumentations/src/net.js +10 -2
  50. package/packages/datadog-instrumentations/src/next.js +35 -9
  51. package/packages/datadog-instrumentations/src/playwright.js +110 -16
  52. package/packages/datadog-instrumentations/src/restify.js +14 -1
  53. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  54. package/packages/datadog-plugin-aerospike/src/index.js +113 -0
  55. package/packages/datadog-plugin-amqplib/src/consumer.js +14 -1
  56. package/packages/datadog-plugin-amqplib/src/producer.js +13 -1
  57. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
  58. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +163 -27
  59. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +46 -8
  60. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +129 -22
  61. package/packages/datadog-plugin-child_process/src/index.js +91 -0
  62. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +70 -13
  64. package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
  65. package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
  66. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
  67. package/packages/datadog-plugin-cypress/src/plugin.js +6 -454
  68. package/packages/datadog-plugin-cypress/src/support.js +50 -3
  69. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/index.js +1 -6
  71. package/packages/datadog-plugin-graphql/src/resolve.js +28 -18
  72. package/packages/datadog-plugin-grpc/src/client.js +16 -2
  73. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  74. package/packages/datadog-plugin-http/src/client.js +19 -2
  75. package/packages/datadog-plugin-jest/src/index.js +118 -12
  76. package/packages/datadog-plugin-jest/src/util.js +38 -16
  77. package/packages/datadog-plugin-kafkajs/src/consumer.js +76 -6
  78. package/packages/datadog-plugin-kafkajs/src/producer.js +64 -8
  79. package/packages/datadog-plugin-mocha/src/index.js +87 -17
  80. package/packages/datadog-plugin-next/src/index.js +40 -14
  81. package/packages/datadog-plugin-playwright/src/index.js +71 -8
  82. package/packages/datadog-plugin-rhea/src/consumer.js +16 -1
  83. package/packages/datadog-plugin-rhea/src/producer.js +10 -0
  84. package/packages/dd-trace/src/appsec/activation.js +29 -0
  85. package/packages/dd-trace/src/appsec/addresses.js +5 -1
  86. package/packages/dd-trace/src/appsec/api_security_sampler.js +61 -0
  87. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  88. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  89. package/packages/dd-trace/src/appsec/channels.js +7 -3
  90. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  91. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  92. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
  93. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
  94. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
  95. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
  96. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
  97. package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
  98. package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
  99. package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
  100. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  101. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +13 -2
  102. package/packages/dd-trace/src/appsec/iast/index.js +15 -5
  103. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
  104. package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -0
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
  107. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
  108. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
  109. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
  110. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
  111. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +19 -6
  112. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +41 -3
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
  115. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
  116. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
  117. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
  118. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
  119. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
  120. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
  121. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
  122. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  123. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
  124. package/packages/dd-trace/src/appsec/index.js +49 -33
  125. package/packages/dd-trace/src/appsec/recommended.json +1763 -106
  126. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -1
  127. package/packages/dd-trace/src/appsec/remote_config/index.js +42 -16
  128. package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
  129. package/packages/dd-trace/src/appsec/reporter.js +51 -34
  130. package/packages/dd-trace/src/appsec/rule_manager.js +11 -8
  131. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  132. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
  133. package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
  134. package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → early-flake-detection/get-known-tests.js} +17 -22
  135. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  139. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +95 -37
  140. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +134 -61
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +37 -4
  142. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +131 -0
  143. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  144. package/packages/dd-trace/src/config.js +561 -470
  145. package/packages/dd-trace/src/data_streams_context.js +1 -1
  146. package/packages/dd-trace/src/datastreams/pathway.js +58 -1
  147. package/packages/dd-trace/src/datastreams/processor.js +196 -27
  148. package/packages/dd-trace/src/datastreams/writer.js +11 -5
  149. package/packages/dd-trace/src/dogstatsd.js +3 -5
  150. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -6
  151. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  152. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  153. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  154. package/packages/dd-trace/src/exporters/common/request.js +21 -3
  155. package/packages/dd-trace/src/format.js +30 -2
  156. package/packages/dd-trace/src/id.js +12 -0
  157. package/packages/dd-trace/src/iitm.js +1 -1
  158. package/packages/dd-trace/src/log/channels.js +1 -1
  159. package/packages/dd-trace/src/noop/proxy.js +4 -0
  160. package/packages/dd-trace/src/noop/span.js +1 -0
  161. package/packages/dd-trace/src/opentelemetry/span.js +104 -4
  162. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
  163. package/packages/dd-trace/src/opentracing/propagation/text_map.js +16 -7
  164. package/packages/dd-trace/src/opentracing/span.js +48 -4
  165. package/packages/dd-trace/src/opentracing/span_context.js +15 -6
  166. package/packages/dd-trace/src/opentracing/tracer.js +4 -3
  167. package/packages/dd-trace/src/plugin_manager.js +1 -1
  168. package/packages/dd-trace/src/plugins/ci_plugin.js +78 -19
  169. package/packages/dd-trace/src/plugins/database.js +1 -1
  170. package/packages/dd-trace/src/plugins/index.js +7 -0
  171. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  172. package/packages/dd-trace/src/plugins/util/ci.js +6 -19
  173. package/packages/dd-trace/src/plugins/util/git.js +104 -22
  174. package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
  175. package/packages/dd-trace/src/plugins/util/test.js +60 -10
  176. package/packages/dd-trace/src/plugins/util/url.js +26 -0
  177. package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
  178. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  179. package/packages/dd-trace/src/priority_sampler.js +30 -38
  180. package/packages/dd-trace/src/profiler.js +5 -3
  181. package/packages/dd-trace/src/profiling/config.js +77 -24
  182. package/packages/dd-trace/src/profiling/exporters/agent.js +77 -31
  183. package/packages/dd-trace/src/profiling/exporters/file.js +2 -1
  184. package/packages/dd-trace/src/profiling/profiler.js +33 -22
  185. package/packages/dd-trace/src/profiling/profilers/events.js +270 -0
  186. package/packages/dd-trace/src/profiling/profilers/shared.js +45 -0
  187. package/packages/dd-trace/src/profiling/profilers/space.js +18 -2
  188. package/packages/dd-trace/src/profiling/profilers/wall.js +146 -70
  189. package/packages/dd-trace/src/proxy.js +56 -24
  190. package/packages/dd-trace/src/ritm.js +1 -1
  191. package/packages/dd-trace/src/sampling_rule.js +130 -0
  192. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
  193. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
  194. package/packages/dd-trace/src/span_processor.js +9 -1
  195. package/packages/dd-trace/src/span_sampler.js +6 -64
  196. package/packages/dd-trace/src/spanleak.js +98 -0
  197. package/packages/dd-trace/src/startup-log.js +7 -1
  198. package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
  199. package/packages/dd-trace/src/telemetry/index.js +182 -53
  200. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  201. package/packages/dd-trace/src/telemetry/send-data.js +65 -7
  202. package/packages/dd-trace/src/tracer.js +12 -5
  203. package/register.js +4 -0
  204. package/scripts/install_plugin_modules.js +11 -3
  205. package/scripts/st.js +105 -0
  206. package/packages/datadog-instrumentations/src/child-process.js +0 -30
  207. package/packages/dd-trace/src/plugins/util/exec.js +0 -13
  208. package/packages/diagnostics_channel/index.js +0 -3
  209. package/packages/diagnostics_channel/src/index.js +0 -121
@@ -16,7 +16,8 @@ const testSuiteStartCh = channel('ci:cucumber:test-suite:start')
16
16
  const testSuiteFinishCh = channel('ci:cucumber:test-suite:finish')
17
17
  const testSuiteCodeCoverageCh = channel('ci:cucumber:test-suite:code-coverage')
18
18
 
19
- const itrConfigurationCh = channel('ci:cucumber:itr-configuration')
19
+ const libraryConfigurationCh = channel('ci:cucumber:library-configuration')
20
+ const knownTestsCh = channel('ci:cucumber:known-tests')
20
21
  const skippableSuitesCh = channel('ci:cucumber:test-suite:skippable')
21
22
  const sessionStartCh = channel('ci:cucumber:session:start')
22
23
  const sessionFinishCh = channel('ci:cucumber:session:finish')
@@ -41,11 +42,18 @@ const originalCoverageMap = createCoverageMap()
41
42
  // TODO: remove in a later major version
42
43
  const patched = new WeakSet()
43
44
 
45
+ const lastStatusByPickleId = new Map()
46
+ const numRetriesByPickleId = new Map()
47
+
44
48
  let pickleByFile = {}
45
49
  const pickleResultByFile = {}
46
50
  let skippableSuites = []
51
+ let itrCorrelationId = ''
47
52
  let isForcedToRun = false
48
53
  let isUnskippable = false
54
+ let isEarlyFlakeDetectionEnabled = false
55
+ let earlyFlakeDetectionNumRetries = 0
56
+ let knownTests = []
49
57
 
50
58
  function getSuiteStatusFromTestStatuses (testStatuses) {
51
59
  if (testStatuses.some(status => status === 'fail')) {
@@ -83,6 +91,21 @@ function getStatusFromResultLatest (result) {
83
91
  return { status: 'fail', errorMessage: result.message }
84
92
  }
85
93
 
94
+ function isNewTest (testSuite, testName) {
95
+ const testsForSuite = knownTests.cucumber?.[testSuite] || []
96
+ return !testsForSuite.includes(testName)
97
+ }
98
+
99
+ function getTestStatusFromRetries (testStatuses) {
100
+ if (testStatuses.every(status => status === 'fail')) {
101
+ return 'fail'
102
+ }
103
+ if (testStatuses.some(status => status === 'pass')) {
104
+ return 'pass'
105
+ }
106
+ return 'pass'
107
+ }
108
+
86
109
  function wrapRun (pl, isLatestVersion) {
87
110
  if (patched.has(pl)) return
88
111
 
@@ -95,24 +118,13 @@ function wrapRun (pl, isLatestVersion) {
95
118
 
96
119
  const asyncResource = new AsyncResource('bound-anonymous-fn')
97
120
  return asyncResource.runInAsyncScope(() => {
98
- const testSuiteFullPath = this.pickle.uri
121
+ const testFileAbsolutePath = this.pickle.uri
99
122
 
100
- if (!pickleResultByFile[testSuiteFullPath]) { // first test in suite
101
- isUnskippable = isMarkedAsUnskippable(this.pickle)
102
- const testSuitePath = getTestSuitePath(testSuiteFullPath, process.cwd())
103
- isForcedToRun = isUnskippable && skippableSuites.includes(testSuitePath)
104
-
105
- testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun })
106
- }
107
-
108
- const testSourceLine = this.gherkinDocument &&
109
- this.gherkinDocument.feature &&
110
- this.gherkinDocument.feature.location &&
111
- this.gherkinDocument.feature.location.line
123
+ const testSourceLine = this.gherkinDocument?.feature?.location?.line
112
124
 
113
125
  testStartCh.publish({
114
126
  testName: this.pickle.name,
115
- fullTestSuite: testSuiteFullPath,
127
+ testFileAbsolutePath,
116
128
  testSourceLine
117
129
  })
118
130
  try {
@@ -122,30 +134,20 @@ function wrapRun (pl, isLatestVersion) {
122
134
  const { status, skipReason, errorMessage } = isLatestVersion
123
135
  ? getStatusFromResultLatest(result) : getStatusFromResult(result)
124
136
 
125
- if (!pickleResultByFile[testSuiteFullPath]) {
126
- pickleResultByFile[testSuiteFullPath] = [status]
137
+ if (lastStatusByPickleId.has(this.pickle.id)) {
138
+ lastStatusByPickleId.get(this.pickle.id).push(status)
127
139
  } else {
128
- pickleResultByFile[testSuiteFullPath].push(status)
140
+ lastStatusByPickleId.set(this.pickle.id, [status])
129
141
  }
130
- testFinishCh.publish({ status, skipReason, errorMessage })
131
- // last test in suite
132
- if (pickleResultByFile[testSuiteFullPath].length === pickleByFile[testSuiteFullPath].length) {
133
- const testSuiteStatus = getSuiteStatusFromTestStatuses(pickleResultByFile[testSuiteFullPath])
134
- if (global.__coverage__) {
135
- const coverageFiles = getCoveredFilenamesFromCoverage(global.__coverage__)
136
-
137
- testSuiteCodeCoverageCh.publish({
138
- coverageFiles,
139
- suiteFile: testSuiteFullPath
140
- })
141
- // We need to reset coverage to get a code coverage per suite
142
- // Before that, we preserve the original coverage
143
- mergeCoverage(global.__coverage__, originalCoverageMap)
144
- resetCoverage(global.__coverage__)
145
- }
146
-
147
- testSuiteFinishCh.publish(testSuiteStatus)
142
+ let isNew = false
143
+ let isEfdRetry = false
144
+ if (isEarlyFlakeDetectionEnabled && status !== 'skip') {
145
+ const numRetries = numRetriesByPickleId.get(this.pickle.id)
146
+
147
+ isNew = numRetries !== undefined
148
+ isEfdRetry = numRetries > 0
148
149
  }
150
+ testFinishCh.publish({ status, skipReason, errorMessage, isNew, isEfdRetry })
149
151
  })
150
152
  return promise
151
153
  } catch (err) {
@@ -257,12 +259,11 @@ function getPickleByFile (runtime) {
257
259
  }, {})
258
260
  }
259
261
 
260
- addHook({
261
- name: '@cucumber/cucumber',
262
- versions: ['>=7.0.0'],
263
- file: 'lib/runtime/index.js'
264
- }, (runtimePackage, frameworkVersion) => {
265
- shimmer.wrap(runtimePackage.default.prototype, 'start', start => async function () {
262
+ function getWrappedStart (start, frameworkVersion) {
263
+ return async function () {
264
+ if (!libraryConfigurationCh.hasSubscribers) {
265
+ return start.apply(this, arguments)
266
+ }
266
267
  const asyncResource = new AsyncResource('bound-anonymous-fn')
267
268
  let onDone
268
269
 
@@ -271,10 +272,26 @@ addHook({
271
272
  })
272
273
 
273
274
  asyncResource.runInAsyncScope(() => {
274
- itrConfigurationCh.publish({ onDone })
275
+ libraryConfigurationCh.publish({ onDone })
275
276
  })
276
277
 
277
- await configPromise
278
+ const configurationResponse = await configPromise
279
+
280
+ isEarlyFlakeDetectionEnabled = configurationResponse.libraryConfig?.isEarlyFlakeDetectionEnabled
281
+ earlyFlakeDetectionNumRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionNumRetries
282
+
283
+ if (isEarlyFlakeDetectionEnabled) {
284
+ const knownTestsPromise = new Promise(resolve => {
285
+ onDone = resolve
286
+ })
287
+ asyncResource.runInAsyncScope(() => {
288
+ knownTestsCh.publish({ onDone })
289
+ })
290
+ const knownTestsResponse = await knownTestsPromise
291
+ if (!knownTestsResponse.err) {
292
+ knownTests = knownTestsResponse.knownTests
293
+ }
294
+ }
278
295
 
279
296
  const skippableSuitesPromise = new Promise(resolve => {
280
297
  onDone = resolve
@@ -304,6 +321,7 @@ addHook({
304
321
  this.pickleIds = picklesToRun
305
322
 
306
323
  skippedSuites = Array.from(filteredPickles.skippedSuites)
324
+ itrCorrelationId = skippableResponse.itrCorrelationId
307
325
  }
308
326
 
309
327
  pickleByFile = getPickleByFile(this)
@@ -340,11 +358,110 @@ addHook({
340
358
  testCodeCoverageLinesTotal,
341
359
  numSkippedSuites: skippedSuites.length,
342
360
  hasUnskippableSuites: isUnskippable,
343
- hasForcedToRunSuites: isForcedToRun
361
+ hasForcedToRunSuites: isForcedToRun,
362
+ isEarlyFlakeDetectionEnabled
344
363
  })
345
364
  })
346
365
  return success
347
- })
366
+ }
367
+ }
368
+
369
+ function getWrappedRunTest (runTestFunction) {
370
+ return async function (pickleId) {
371
+ const test = this.eventDataCollector.getPickle(pickleId)
372
+
373
+ const testFileAbsolutePath = test.uri
374
+ const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
375
+
376
+ if (!pickleResultByFile[testFileAbsolutePath]) { // first test in suite
377
+ isUnskippable = isMarkedAsUnskippable(test)
378
+ isForcedToRun = isUnskippable && skippableSuites.includes(testSuitePath)
379
+
380
+ testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId })
381
+ }
382
+
383
+ let isNew = false
384
+
385
+ if (isEarlyFlakeDetectionEnabled) {
386
+ isNew = isNewTest(testSuitePath, test.name)
387
+ if (isNew) {
388
+ numRetriesByPickleId.set(pickleId, 0)
389
+ }
390
+ }
391
+ const runTestCaseResult = await runTestFunction.apply(this, arguments)
392
+
393
+ const testStatuses = lastStatusByPickleId.get(pickleId)
394
+ const lastTestStatus = testStatuses[testStatuses.length - 1]
395
+ // If it's a new test and it hasn't been skipped, we run it again
396
+ if (isEarlyFlakeDetectionEnabled && lastTestStatus !== 'skip' && isNew) {
397
+ for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
398
+ numRetriesByPickleId.set(pickleId, retryIndex + 1)
399
+ await runTestFunction.apply(this, arguments)
400
+ }
401
+ }
402
+ let testStatus = lastTestStatus
403
+ if (isEarlyFlakeDetectionEnabled) {
404
+ /**
405
+ * If Early Flake Detection (EFD) is enabled the logic is as follows:
406
+ * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
407
+ * - If just a single attempt passes, we will prevent the test process from failing.
408
+ * The rationale behind is the following: you may still be able to block your CI pipeline by gating
409
+ * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
410
+ */
411
+ testStatus = getTestStatusFromRetries(testStatuses)
412
+ if (testStatus === 'pass') {
413
+ this.success = true
414
+ }
415
+ }
416
+
417
+ if (!pickleResultByFile[testFileAbsolutePath]) {
418
+ pickleResultByFile[testFileAbsolutePath] = [testStatus]
419
+ } else {
420
+ pickleResultByFile[testFileAbsolutePath].push(testStatus)
421
+ }
422
+
423
+ // last test in suite
424
+ if (pickleResultByFile[testFileAbsolutePath].length === pickleByFile[testFileAbsolutePath].length) {
425
+ const testSuiteStatus = getSuiteStatusFromTestStatuses(pickleResultByFile[testFileAbsolutePath])
426
+ if (global.__coverage__) {
427
+ const coverageFiles = getCoveredFilenamesFromCoverage(global.__coverage__)
428
+
429
+ testSuiteCodeCoverageCh.publish({
430
+ coverageFiles,
431
+ suiteFile: testFileAbsolutePath
432
+ })
433
+ // We need to reset coverage to get a code coverage per suite
434
+ // Before that, we preserve the original coverage
435
+ mergeCoverage(global.__coverage__, originalCoverageMap)
436
+ resetCoverage(global.__coverage__)
437
+ }
438
+
439
+ testSuiteFinishCh.publish(testSuiteStatus)
440
+ }
441
+
442
+ return runTestCaseResult
443
+ }
444
+ }
445
+
446
+ // From 7.3.0 onwards, runPickle becomes runTestCase
447
+ addHook({
448
+ name: '@cucumber/cucumber',
449
+ versions: ['>=7.3.0'],
450
+ file: 'lib/runtime/index.js'
451
+ }, (runtimePackage, frameworkVersion) => {
452
+ shimmer.wrap(runtimePackage.default.prototype, 'runTestCase', runTestCase => getWrappedRunTest(runTestCase))
453
+ shimmer.wrap(runtimePackage.default.prototype, 'start', start => getWrappedStart(start, frameworkVersion))
454
+
455
+ return runtimePackage
456
+ })
457
+
458
+ addHook({
459
+ name: '@cucumber/cucumber',
460
+ versions: ['>=7.0.0 <7.3.0'],
461
+ file: 'lib/runtime/index.js'
462
+ }, (runtimePackage, frameworkVersion) => {
463
+ shimmer.wrap(runtimePackage.default.prototype, 'runPickle', runPickle => getWrappedRunTest(runPickle))
464
+ shimmer.wrap(runtimePackage.default.prototype, 'start', start => getWrappedStart(start, frameworkVersion))
348
465
 
349
466
  return runtimePackage
350
467
  })
@@ -18,8 +18,9 @@ const rrtypes = {
18
18
  }
19
19
 
20
20
  const rrtypeMap = new WeakMap()
21
+ const names = ['dns', 'node:dns']
21
22
 
22
- addHook({ name: 'dns' }, dns => {
23
+ addHook({ name: names }, dns => {
23
24
  dns.lookup = wrap('apm:dns:lookup', dns.lookup, 2)
24
25
  dns.lookupService = wrap('apm:dns:lookup_service', dns.lookupService, 3)
25
26
  dns.resolve = wrap('apm:dns:resolve', dns.resolve, 2)
@@ -19,11 +19,31 @@ function wrapHandle (handle) {
19
19
 
20
20
  const wrapRouterMethod = createWrapRouterMethod('express')
21
21
 
22
+ const responseJsonChannel = channel('datadog:express:response:json:start')
23
+
24
+ function wrapResponseJson (json) {
25
+ return function wrappedJson (obj) {
26
+ if (responseJsonChannel.hasSubscribers) {
27
+ // backward compat as express 4.x supports deprecated 3.x signature
28
+ if (arguments.length === 2 && typeof arguments[1] !== 'number') {
29
+ obj = arguments[1]
30
+ }
31
+
32
+ responseJsonChannel.publish({ req: this.req, body: obj })
33
+ }
34
+
35
+ return json.apply(this, arguments)
36
+ }
37
+ }
38
+
22
39
  addHook({ name: 'express', versions: ['>=4'] }, express => {
23
40
  shimmer.wrap(express.application, 'handle', wrapHandle)
24
41
  shimmer.wrap(express.Router, 'use', wrapRouterMethod)
25
42
  shimmer.wrap(express.Router, 'route', wrapRouterMethod)
26
43
 
44
+ shimmer.wrap(express.response, 'json', wrapResponseJson)
45
+ shimmer.wrap(express.response, 'jsonp', wrapResponseJson)
46
+
27
47
  return express
28
48
  })
29
49
 
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { AbortController } = require('node-abort-controller')
4
+
3
5
  const {
4
6
  addHook,
5
7
  channel,
@@ -37,6 +39,13 @@ const validateStartCh = channel('apm:graphql:validate:start')
37
39
  const validateFinishCh = channel('apm:graphql:validate:finish')
38
40
  const validateErrorCh = channel('apm:graphql:validate:error')
39
41
 
42
+ class AbortError extends Error {
43
+ constructor (message) {
44
+ super(message)
45
+ this.name = 'AbortError'
46
+ }
47
+ }
48
+
40
49
  function getOperation (document, operationName) {
41
50
  if (!document || !Array.isArray(document.definitions)) {
42
51
  return
@@ -175,11 +184,11 @@ function wrapExecute (execute) {
175
184
  docSource: documentSources.get(document)
176
185
  })
177
186
 
178
- const context = { source, asyncResource, fields: {} }
187
+ const context = { source, asyncResource, fields: {}, abortController: new AbortController() }
179
188
 
180
189
  contexts.set(contextValue, context)
181
190
 
182
- return callInAsyncScope(exe, asyncResource, this, arguments, (err, res) => {
191
+ return callInAsyncScope(exe, asyncResource, this, arguments, context.abortController, (err, res) => {
183
192
  if (finishResolveCh.hasSubscribers) finishResolvers(context)
184
193
 
185
194
  const error = err || (res && res.errors && res.errors[0])
@@ -207,7 +216,7 @@ function wrapResolve (resolve) {
207
216
 
208
217
  const field = assertField(context, info, args)
209
218
 
210
- return callInAsyncScope(resolve, field.asyncResource, this, arguments, (err) => {
219
+ return callInAsyncScope(resolve, field.asyncResource, this, arguments, context.abortController, (err) => {
211
220
  updateFieldCh.publish({ field, info, err })
212
221
  })
213
222
  }
@@ -217,10 +226,15 @@ function wrapResolve (resolve) {
217
226
  return resolveAsync
218
227
  }
219
228
 
220
- function callInAsyncScope (fn, aR, thisArg, args, cb) {
229
+ function callInAsyncScope (fn, aR, thisArg, args, abortController, cb) {
221
230
  cb = cb || (() => {})
222
231
 
223
232
  return aR.runInAsyncScope(() => {
233
+ if (abortController?.signal.aborted) {
234
+ cb(null, null)
235
+ throw new AbortError('Aborted')
236
+ }
237
+
224
238
  try {
225
239
  const result = fn.apply(thisArg, args)
226
240
  if (result && typeof result.then === 'function') {
@@ -15,54 +15,52 @@ const errorChannel = channel('apm:grpc:client:request:error')
15
15
  const finishChannel = channel('apm:grpc:client:request:finish')
16
16
  const emitChannel = channel('apm:grpc:client:request:emit')
17
17
 
18
- function createWrapMakeRequest (type) {
18
+ function createWrapMakeRequest (type, hasPeer = false) {
19
19
  return function wrapMakeRequest (makeRequest) {
20
20
  return function (path) {
21
21
  const args = ensureMetadata(this, arguments, 4)
22
22
 
23
- return callMethod(this, makeRequest, args, path, args[4], type)
23
+ return callMethod(this, makeRequest, args, path, args[4], type, hasPeer)
24
24
  }
25
25
  }
26
26
  }
27
27
 
28
- function createWrapLoadPackageDefinition () {
28
+ function createWrapLoadPackageDefinition (hasPeer = false) {
29
29
  return function wrapLoadPackageDefinition (loadPackageDefinition) {
30
30
  return function (packageDef) {
31
31
  const result = loadPackageDefinition.apply(this, arguments)
32
32
 
33
33
  if (!result) return result
34
34
 
35
- wrapPackageDefinition(result)
35
+ wrapPackageDefinition(result, hasPeer)
36
36
 
37
37
  return result
38
38
  }
39
39
  }
40
40
  }
41
41
 
42
- function createWrapMakeClientConstructor () {
42
+ function createWrapMakeClientConstructor (hasPeer = false) {
43
43
  return function wrapMakeClientConstructor (makeClientConstructor) {
44
44
  return function (methods) {
45
45
  const ServiceClient = makeClientConstructor.apply(this, arguments)
46
-
47
- wrapClientConstructor(ServiceClient, methods)
48
-
46
+ wrapClientConstructor(ServiceClient, methods, hasPeer)
49
47
  return ServiceClient
50
48
  }
51
49
  }
52
50
  }
53
51
 
54
- function wrapPackageDefinition (def) {
52
+ function wrapPackageDefinition (def, hasPeer = false) {
55
53
  for (const name in def) {
56
54
  if (def[name].format) continue
57
55
  if (def[name].service && def[name].prototype) {
58
- wrapClientConstructor(def[name], def[name].service)
56
+ wrapClientConstructor(def[name], def[name].service, hasPeer)
59
57
  } else {
60
- wrapPackageDefinition(def[name])
58
+ wrapPackageDefinition(def[name], hasPeer)
61
59
  }
62
60
  }
63
61
  }
64
62
 
65
- function wrapClientConstructor (ServiceClient, methods) {
63
+ function wrapClientConstructor (ServiceClient, methods, hasPeer = false) {
66
64
  const proto = ServiceClient.prototype
67
65
 
68
66
  if (typeof methods !== 'object' || 'format' in methods) return
@@ -76,24 +74,23 @@ function wrapClientConstructor (ServiceClient, methods) {
76
74
  const type = getType(methods[name])
77
75
 
78
76
  if (methods[name]) {
79
- proto[name] = wrapMethod(proto[name], path, type)
77
+ proto[name] = wrapMethod(proto[name], path, type, hasPeer)
80
78
  }
81
79
 
82
80
  if (originalName) {
83
- proto[originalName] = wrapMethod(proto[originalName], path, type)
81
+ proto[originalName] = wrapMethod(proto[originalName], path, type, hasPeer)
84
82
  }
85
83
  })
86
84
  }
87
85
 
88
- function wrapMethod (method, path, type) {
86
+ function wrapMethod (method, path, type, hasPeer) {
89
87
  if (typeof method !== 'function' || patched.has(method)) {
90
88
  return method
91
89
  }
92
90
 
93
91
  const wrapped = function () {
94
92
  const args = ensureMetadata(this, arguments, 1)
95
-
96
- return callMethod(this, method, args, path, args[1], type)
93
+ return callMethod(this, method, args, path, args[1], type, hasPeer)
97
94
  }
98
95
 
99
96
  Object.assign(wrapped, method)
@@ -117,7 +114,20 @@ function wrapCallback (ctx, callback = () => { }) {
117
114
  }
118
115
  }
119
116
 
120
- function createWrapEmit (ctx) {
117
+ function createWrapEmit (ctx, hasPeer = false) {
118
+ const onStatusWithPeer = function (ctx, arg1, thisArg) {
119
+ ctx.result = arg1
120
+ ctx.peer = thisArg.getPeer()
121
+ finishChannel.publish(ctx)
122
+ }
123
+
124
+ const onStatusWithoutPeer = function (ctx, arg1, thisArg) {
125
+ ctx.result = arg1
126
+ finishChannel.publish(ctx)
127
+ }
128
+
129
+ const onStatus = hasPeer ? onStatusWithPeer : onStatusWithoutPeer
130
+
121
131
  return function wrapEmit (emit) {
122
132
  return function (event, arg1) {
123
133
  switch (event) {
@@ -126,8 +136,7 @@ function createWrapEmit (ctx) {
126
136
  errorChannel.publish(ctx)
127
137
  break
128
138
  case 'status':
129
- ctx.result = arg1
130
- finishChannel.publish(ctx)
139
+ onStatus(ctx, arg1, this)
131
140
  break
132
141
  }
133
142
 
@@ -138,7 +147,7 @@ function createWrapEmit (ctx) {
138
147
  }
139
148
  }
140
149
 
141
- function callMethod (client, method, args, path, metadata, type) {
150
+ function callMethod (client, method, args, path, metadata, type, hasPeer = false) {
142
151
  if (!startChannel.hasSubscribers) return method.apply(client, args)
143
152
 
144
153
  const length = args.length
@@ -159,7 +168,7 @@ function callMethod (client, method, args, path, metadata, type) {
159
168
  const call = method.apply(client, args)
160
169
 
161
170
  if (call && typeof call.emit === 'function') {
162
- shimmer.wrap(call, 'emit', createWrapEmit(ctx))
171
+ shimmer.wrap(call, 'emit', createWrapEmit(ctx, hasPeer))
163
172
  }
164
173
 
165
174
  return call
@@ -223,34 +232,45 @@ function getGrpc (client) {
223
232
  } while ((proto = Object.getPrototypeOf(proto)))
224
233
  }
225
234
 
226
- function patch (grpc) {
227
- const proto = grpc.Client.prototype
235
+ function patch (hasPeer = false) {
236
+ return function patch (grpc) {
237
+ const proto = grpc.Client.prototype
228
238
 
229
- instances.set(proto, grpc)
239
+ instances.set(proto, grpc)
230
240
 
231
- shimmer.wrap(proto, 'makeBidiStreamRequest', createWrapMakeRequest(types.bidi))
232
- shimmer.wrap(proto, 'makeClientStreamRequest', createWrapMakeRequest(types.clientStream))
233
- shimmer.wrap(proto, 'makeServerStreamRequest', createWrapMakeRequest(types.serverStream))
234
- shimmer.wrap(proto, 'makeUnaryRequest', createWrapMakeRequest(types.unary))
241
+ shimmer.wrap(proto, 'makeBidiStreamRequest', createWrapMakeRequest(types.bidi, hasPeer))
242
+ shimmer.wrap(proto, 'makeClientStreamRequest', createWrapMakeRequest(types.clientStream, hasPeer))
243
+ shimmer.wrap(proto, 'makeServerStreamRequest', createWrapMakeRequest(types.serverStream, hasPeer))
244
+ shimmer.wrap(proto, 'makeUnaryRequest', createWrapMakeRequest(types.unary, hasPeer))
235
245
 
236
- return grpc
246
+ return grpc
247
+ }
237
248
  }
238
249
 
239
250
  if (nodeMajor <= 14) {
240
- addHook({ name: 'grpc', versions: ['>=1.24.3'] }, patch)
251
+ addHook({ name: 'grpc', versions: ['>=1.24.3'] }, patch(true))
241
252
 
242
253
  addHook({ name: 'grpc', versions: ['>=1.24.3'], file: 'src/client.js' }, client => {
243
- shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor())
254
+ shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor(true))
244
255
 
245
256
  return client
246
257
  })
247
258
  }
248
259
 
249
- addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'] }, patch)
260
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3 <1.1.4'] }, patch(false))
261
+
262
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3 <1.1.4'], file: 'build/src/make-client.js' }, client => {
263
+ shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor(false))
264
+ shimmer.wrap(client, 'loadPackageDefinition', createWrapLoadPackageDefinition(false))
265
+
266
+ return client
267
+ })
268
+
269
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.1.4'] }, patch(true))
250
270
 
251
- addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'], file: 'build/src/make-client.js' }, client => {
252
- shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor())
253
- shimmer.wrap(client, 'loadPackageDefinition', createWrapLoadPackageDefinition())
271
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.1.4'], file: 'build/src/make-client.js' }, client => {
272
+ shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor(true))
273
+ shimmer.wrap(client, 'loadPackageDefinition', createWrapLoadPackageDefinition(true))
254
274
 
255
275
  return client
256
276
  })
@@ -92,7 +92,9 @@ function createWrapEmit (call, ctx, onCancel) {
92
92
  finishChannel.publish(ctx)
93
93
  call.removeListener('cancelled', onCancel)
94
94
  break
95
- case 'finish':
95
+ // Streams are always cancelled before `finish` since 1.10.0 so we have
96
+ // to use `prefinish` instead to avoid cancellation false positives.
97
+ case 'prefinish':
96
98
  if (call.status) {
97
99
  updateChannel.publish(call.status)
98
100
  }
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
- // eslint-disable-next-line n/no-restricted-require
4
- const dc = require('diagnostics_channel')
3
+ const dc = require('dc-polyfill')
5
4
 
6
5
  const {
7
6
  filename,
@@ -1,6 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  module.exports = {
4
+ '@apollo/server': () => require('../apollo-server'),
5
+ 'apollo-server-core': () => require('../apollo-server-core'),
4
6
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
5
7
  '@cucumber/cucumber': () => require('../cucumber'),
6
8
  '@playwright/test': () => require('../playwright'),
@@ -20,6 +22,7 @@ module.exports = {
20
22
  '@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
21
23
  '@redis/client': () => require('../redis'),
22
24
  '@smithy/smithy-client': () => require('../aws-sdk'),
25
+ 'aerospike': () => require('../aerospike'),
23
26
  'amqp10': () => require('../amqp10'),
24
27
  'amqplib': () => require('../amqplib'),
25
28
  'aws-sdk': () => require('../aws-sdk'),
@@ -27,8 +30,7 @@ module.exports = {
27
30
  'body-parser': () => require('../body-parser'),
28
31
  'bunyan': () => require('../bunyan'),
29
32
  'cassandra-driver': () => require('../cassandra-driver'),
30
- 'child_process': () => require('../child-process'),
31
- 'node:child_process': () => require('../child-process'),
33
+ 'child_process': () => require('../child_process'),
32
34
  'connect': () => require('../connect'),
33
35
  'cookie': () => require('../cookie'),
34
36
  'cookie-parser': () => require('../cookie-parser'),
@@ -42,7 +44,6 @@ module.exports = {
42
44
  'fastify': () => require('../fastify'),
43
45
  'find-my-way': () => require('../find-my-way'),
44
46
  'fs': () => require('../fs'),
45
- 'node:fs': () => require('../fs'),
46
47
  'generic-pool': () => require('../generic-pool'),
47
48
  'graphql': () => require('../graphql'),
48
49
  'grpc': () => require('../grpc'),
@@ -72,10 +73,18 @@ module.exports = {
72
73
  'mongodb': () => require('../mongodb'),
73
74
  'mongodb-core': () => require('../mongodb-core'),
74
75
  'mongoose': () => require('../mongoose'),
76
+ 'mquery': () => require('../mquery'),
75
77
  'mysql': () => require('../mysql'),
76
78
  'mysql2': () => require('../mysql2'),
77
79
  'net': () => require('../net'),
78
80
  'next': () => require('../next'),
81
+ 'node:child_process': () => require('../child_process'),
82
+ 'node:crypto': () => require('../crypto'),
83
+ 'node:dns': () => require('../dns'),
84
+ 'node:http': () => require('../http'),
85
+ 'node:http2': () => require('../http2'),
86
+ 'node:https': () => require('../http'),
87
+ 'node:net': () => require('../net'),
79
88
  'oracledb': () => require('../oracledb'),
80
89
  'openai': () => require('../openai'),
81
90
  'paperplane': () => require('../paperplane'),