dd-trace 4.52.0 → 4.53.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 (144) hide show
  1. package/LICENSE-3rdparty.csv +8 -2
  2. package/ci/init.js +16 -0
  3. package/index.d.ts +31 -13
  4. package/init.js +4 -68
  5. package/loader-hook.mjs +4 -0
  6. package/package.json +16 -11
  7. package/packages/datadog-core/src/storage.js +39 -2
  8. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  9. package/packages/datadog-instrumentations/src/cucumber.js +29 -3
  10. package/packages/datadog-instrumentations/src/express.js +38 -4
  11. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +3 -3
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +0 -1
  13. package/packages/datadog-instrumentations/src/helpers/register.js +3 -4
  14. package/packages/datadog-instrumentations/src/http/client.js +1 -1
  15. package/packages/datadog-instrumentations/src/jest.js +27 -8
  16. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +13 -8
  18. package/packages/datadog-instrumentations/src/next.js +7 -4
  19. package/packages/datadog-instrumentations/src/passport-http.js +2 -14
  20. package/packages/datadog-instrumentations/src/passport-local.js +2 -14
  21. package/packages/datadog-instrumentations/src/passport-utils.js +43 -19
  22. package/packages/datadog-instrumentations/src/pg.js +6 -6
  23. package/packages/datadog-instrumentations/src/playwright.js +17 -4
  24. package/packages/datadog-instrumentations/src/router.js +97 -1
  25. package/packages/datadog-instrumentations/src/sequelize.js +9 -4
  26. package/packages/datadog-instrumentations/src/url.js +4 -0
  27. package/packages/datadog-instrumentations/src/vitest.js +27 -2
  28. package/packages/datadog-plugin-avsc/src/schema_iterator.js +8 -3
  29. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +154 -0
  30. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  31. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  34. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  35. package/packages/datadog-plugin-aws-sdk/src/util.js +92 -0
  36. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
  37. package/packages/datadog-plugin-cucumber/src/index.js +39 -4
  38. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -3
  39. package/packages/datadog-plugin-grpc/src/client.js +2 -2
  40. package/packages/datadog-plugin-grpc/src/util.js +1 -1
  41. package/packages/datadog-plugin-jest/src/index.js +39 -4
  42. package/packages/datadog-plugin-mocha/src/index.js +36 -2
  43. package/packages/datadog-plugin-oracledb/src/index.js +1 -1
  44. package/packages/datadog-plugin-vitest/src/index.js +34 -2
  45. package/packages/datadog-shimmer/src/shimmer.js +8 -4
  46. package/packages/dd-trace/src/appsec/addresses.js +3 -0
  47. package/packages/dd-trace/src/appsec/blocked_templates.js +1 -1
  48. package/packages/dd-trace/src/appsec/channels.js +1 -0
  49. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +4 -0
  50. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
  51. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +1 -1
  52. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +1 -1
  53. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +10 -3
  54. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +4 -0
  55. package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +4 -0
  56. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +6 -19
  57. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
  58. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +64 -3
  59. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
  60. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
  61. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  62. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +32 -37
  63. package/packages/dd-trace/src/appsec/index.js +16 -10
  64. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
  65. package/packages/dd-trace/src/appsec/remote_config/index.js +25 -1
  66. package/packages/dd-trace/src/appsec/reporter.js +3 -1
  67. package/packages/dd-trace/src/appsec/sdk/track_event.js +32 -19
  68. package/packages/dd-trace/src/appsec/telemetry.js +10 -0
  69. package/packages/dd-trace/src/appsec/user_tracking.js +168 -0
  70. package/packages/dd-trace/src/azure_metadata.js +4 -4
  71. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -4
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +39 -3
  73. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  74. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  75. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  77. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -9
  78. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  79. package/packages/dd-trace/src/config.js +24 -32
  80. package/packages/dd-trace/src/constants.js +1 -0
  81. package/packages/dd-trace/src/crashtracking/crashtracker.js +3 -2
  82. package/packages/dd-trace/src/datastreams/processor.js +4 -6
  83. package/packages/dd-trace/src/datastreams/writer.js +6 -5
  84. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +80 -0
  85. package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -1
  86. package/packages/dd-trace/src/debugger/devtools_client/defaults.js +6 -0
  87. package/packages/dd-trace/src/debugger/devtools_client/index.js +63 -8
  88. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +10 -67
  89. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  90. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
  91. package/packages/dd-trace/src/debugger/devtools_client/status.js +4 -4
  92. package/packages/dd-trace/src/debugger/index.js +14 -10
  93. package/packages/dd-trace/src/dogstatsd.js +2 -2
  94. package/packages/dd-trace/src/encode/0.4.js +23 -78
  95. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +0 -32
  96. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +1 -2
  97. package/packages/dd-trace/src/encode/span-stats.js +0 -30
  98. package/packages/dd-trace/src/exporters/agent/writer.js +3 -3
  99. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  100. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -1
  101. package/packages/dd-trace/src/flare/index.js +1 -1
  102. package/packages/dd-trace/src/guardrails/index.js +64 -0
  103. package/packages/dd-trace/src/guardrails/log.js +32 -0
  104. package/packages/dd-trace/src/guardrails/telemetry.js +78 -0
  105. package/packages/dd-trace/src/guardrails/util.js +10 -0
  106. package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
  107. package/packages/dd-trace/src/llmobs/storage.js +2 -3
  108. package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
  109. package/packages/dd-trace/src/{encode → msgpack}/chunk.js +8 -5
  110. package/packages/dd-trace/src/msgpack/encoder.js +309 -0
  111. package/packages/dd-trace/src/msgpack/index.js +6 -0
  112. package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -2
  113. package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -9
  114. package/packages/dd-trace/src/opentracing/span.js +1 -1
  115. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  116. package/packages/dd-trace/src/plugin_manager.js +4 -2
  117. package/packages/dd-trace/src/plugins/ci_plugin.js +47 -4
  118. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  119. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  120. package/packages/dd-trace/src/plugins/util/git.js +7 -7
  121. package/packages/dd-trace/src/plugins/util/test.js +36 -3
  122. package/packages/dd-trace/src/plugins/util/web.js +2 -2
  123. package/packages/dd-trace/src/profiling/config.js +3 -0
  124. package/packages/dd-trace/src/profiling/exporters/agent.js +9 -68
  125. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +76 -0
  126. package/packages/dd-trace/src/profiling/exporters/file.js +8 -4
  127. package/packages/dd-trace/src/profiling/profiler.js +62 -10
  128. package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +22 -12
  129. package/packages/dd-trace/src/profiling/profilers/events.js +47 -8
  130. package/packages/dd-trace/src/profiling/profilers/wall.js +2 -17
  131. package/packages/dd-trace/src/profiling/webspan-utils.js +23 -0
  132. package/packages/dd-trace/src/proxy.js +7 -2
  133. package/packages/dd-trace/src/runtime_metrics.js +107 -4
  134. package/packages/dd-trace/src/serverless.js +1 -1
  135. package/packages/dd-trace/src/span_processor.js +10 -10
  136. package/packages/dd-trace/src/tagger.js +1 -1
  137. package/packages/dd-trace/src/telemetry/index.js +1 -0
  138. package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
  139. package/packages/dd-trace/src/telemetry/logs/log-collector.js +10 -2
  140. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  141. package/packages/dd-trace/src/util.js +5 -16
  142. package/packages/datadog-instrumentations/src/qs.js +0 -24
  143. package/packages/dd-trace/src/appsec/passport.js +0 -110
  144. package/packages/dd-trace/src/telemetry/init-telemetry.js +0 -75
@@ -133,6 +133,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
133
133
  this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled
134
134
  this.isFlakyTestRetriesEnabled = this.testEnvironmentOptions._ddIsFlakyTestRetriesEnabled
135
135
  this.flakyTestRetriesCount = this.testEnvironmentOptions._ddFlakyTestRetriesCount
136
+ this.isDiEnabled = this.testEnvironmentOptions._ddIsDiEnabled
136
137
 
137
138
  if (this.isEarlyFlakeDetectionEnabled) {
138
139
  const hasKnownTests = !!knownTests.jest
@@ -237,7 +238,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
237
238
  name: removeEfdStringFromTestName(testName),
238
239
  suite: this.testSuite,
239
240
  testSourceFile: this.testSourceFile,
240
- runner: 'jest-circus',
241
241
  displayName: this.displayName,
242
242
  testParameters,
243
243
  frameworkVersion: jestVersion,
@@ -274,13 +274,23 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
274
274
  }
275
275
  }
276
276
  if (event.name === 'test_done') {
277
+ const probe = {}
277
278
  const asyncResource = asyncResources.get(event.test)
278
279
  asyncResource.runInAsyncScope(() => {
279
280
  let status = 'pass'
280
281
  if (event.test.errors && event.test.errors.length) {
281
282
  status = 'fail'
282
- const formattedError = formatJestError(event.test.errors[0])
283
- testErrCh.publish(formattedError)
283
+ const numRetries = this.global[RETRY_TIMES]
284
+ const numTestExecutions = event.test?.invocations
285
+ const willBeRetried = numRetries > 0 && numTestExecutions - 1 < numRetries
286
+
287
+ const error = formatJestError(event.test.errors[0])
288
+ testErrCh.publish({
289
+ error,
290
+ willBeRetried,
291
+ probe,
292
+ isDiEnabled: this.isDiEnabled
293
+ })
284
294
  }
285
295
  testRunFinishCh.publish({
286
296
  status,
@@ -302,6 +312,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
302
312
  }
303
313
  }
304
314
  })
315
+ if (probe.setProbePromise) {
316
+ await probe.setProbePromise
317
+ }
305
318
  }
306
319
  if (event.name === 'test_skip' || event.name === 'test_todo') {
307
320
  const asyncResource = new AsyncResource('bound-anonymous-fn')
@@ -310,7 +323,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
310
323
  name: getJestTestName(event.test),
311
324
  suite: this.testSuite,
312
325
  testSourceFile: this.testSourceFile,
313
- runner: 'jest-circus',
314
326
  displayName: this.displayName,
315
327
  frameworkVersion: jestVersion,
316
328
  testStartLine: getTestLineStart(event.test.asyncError, this.testSuite)
@@ -445,7 +457,7 @@ function cliWrapper (cli, jestVersion) {
445
457
  earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
446
458
  }
447
459
  } catch (err) {
448
- log.error(err)
460
+ log.error('Jest library configuration error', err)
449
461
  }
450
462
 
451
463
  if (isEarlyFlakeDetectionEnabled) {
@@ -466,7 +478,7 @@ function cliWrapper (cli, jestVersion) {
466
478
  isEarlyFlakeDetectionEnabled = false
467
479
  }
468
480
  } catch (err) {
469
- log.error(err)
481
+ log.error('Jest known tests error', err)
470
482
  }
471
483
  }
472
484
 
@@ -485,7 +497,7 @@ function cliWrapper (cli, jestVersion) {
485
497
  skippableSuites = receivedSkippableSuites
486
498
  }
487
499
  } catch (err) {
488
- log.error(err)
500
+ log.error('Jest test-suite skippable error', err)
489
501
  }
490
502
  }
491
503
 
@@ -780,6 +792,7 @@ addHook({
780
792
  _ddRepositoryRoot,
781
793
  _ddIsFlakyTestRetriesEnabled,
782
794
  _ddFlakyTestRetriesCount,
795
+ _ddIsDiEnabled,
783
796
  ...restOfTestEnvironmentOptions
784
797
  } = testEnvironmentOptions
785
798
 
@@ -855,12 +868,18 @@ addHook({
855
868
 
856
869
  const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = [
857
870
  'selenium-webdriver',
871
+ 'selenium-webdriver/chrome',
872
+ 'selenium-webdriver/edge',
873
+ 'selenium-webdriver/safari',
874
+ 'selenium-webdriver/firefox',
875
+ 'selenium-webdriver/ie',
876
+ 'selenium-webdriver/chromium',
858
877
  'winston'
859
878
  ]
860
879
 
861
880
  function shouldBypassJestRequireEngine (moduleName) {
862
881
  return (
863
- LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.some(library => moduleName.includes(library))
882
+ LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.includes(moduleName)
864
883
  )
865
884
  }
866
885
 
@@ -284,8 +284,9 @@ function getOnTestRetryHandler () {
284
284
  const asyncResource = getTestAsyncResource(test)
285
285
  if (asyncResource) {
286
286
  const isFirstAttempt = test._currentRetry === 0
287
+ const willBeRetried = test._currentRetry < test._retries
287
288
  asyncResource.runInAsyncScope(() => {
288
- testRetryCh.publish({ isFirstAttempt, err })
289
+ testRetryCh.publish({ isFirstAttempt, err, willBeRetried })
289
290
  })
290
291
  }
291
292
  const key = getTestToArKey(test)
@@ -8,7 +8,7 @@ const {
8
8
  const shimmer = require('../../datadog-shimmer')
9
9
  const semver = require('semver')
10
10
 
11
- addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, (Connection, version) => {
11
+ function wrapConnection (Connection, version) {
12
12
  const startCh = channel('apm:mysql2:query:start')
13
13
  const finishCh = channel('apm:mysql2:query:finish')
14
14
  const errorCh = channel('apm:mysql2:query:error')
@@ -151,9 +151,8 @@ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, (Conne
151
151
  }
152
152
  }, cmd))
153
153
  }
154
- })
155
-
156
- addHook({ name: 'mysql2', file: 'lib/pool.js', versions: ['>=1'] }, (Pool, version) => {
154
+ }
155
+ function wrapPool (Pool, version) {
157
156
  const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
158
157
  const shouldEmitEndAfterQueryAbort = semver.intersects(version, '>=1.3.3')
159
158
 
@@ -221,10 +220,9 @@ addHook({ name: 'mysql2', file: 'lib/pool.js', versions: ['>=1'] }, (Pool, versi
221
220
  })
222
221
 
223
222
  return Pool
224
- })
223
+ }
225
224
 
226
- // PoolNamespace.prototype.query does not exist in mysql2<2.3.0
227
- addHook({ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['>=2.3.0'] }, PoolCluster => {
225
+ function wrapPoolCluster (PoolCluster) {
228
226
  const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
229
227
  const wrappedPoolNamespaces = new WeakSet()
230
228
 
@@ -297,4 +295,11 @@ addHook({ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['>=2.3.0'] },
297
295
  })
298
296
 
299
297
  return PoolCluster
300
- })
298
+ }
299
+
300
+ addHook({ name: 'mysql2', file: 'lib/base/connection.js', versions: ['>=3.11.5'] }, wrapConnection)
301
+ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['1 - 3.11.4'] }, wrapConnection)
302
+ addHook({ name: 'mysql2', file: 'lib/pool.js', versions: ['1 - 3.11.4'] }, wrapPool)
303
+
304
+ // PoolNamespace.prototype.query does not exist in mysql2<2.3.0
305
+ addHook({ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['2.3.0 - 3.11.4'] }, wrapPoolCluster)
@@ -2,7 +2,6 @@
2
2
 
3
3
  const { channel, addHook } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { DD_MAJOR } = require('../../../version')
6
5
 
7
6
  const startChannel = channel('apm:next:request:start')
8
7
  const finishChannel = channel('apm:next:request:finish')
@@ -221,7 +220,7 @@ addHook({
221
220
 
222
221
  addHook({
223
222
  name: 'next',
224
- versions: DD_MAJOR >= 4 ? ['>=10.2 <11.1'] : ['>=9.5 <11.1'],
223
+ versions: ['>=10.2 <11.1'],
225
224
  file: 'dist/next-server/server/serve-static.js'
226
225
  }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
227
226
 
@@ -248,7 +247,11 @@ addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js'
248
247
  return nextServer
249
248
  })
250
249
 
251
- addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-server.js' }, nextServer => {
250
+ addHook({
251
+ name: 'next',
252
+ versions: ['>=11.1 <13.2'],
253
+ file: 'dist/server/next-server.js'
254
+ }, nextServer => {
252
255
  const Server = nextServer.default
253
256
  shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequest)
254
257
  return nextServer
@@ -256,7 +259,7 @@ addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-serv
256
259
 
257
260
  addHook({
258
261
  name: 'next',
259
- versions: DD_MAJOR >= 4 ? ['>=10.2 <11.1'] : ['>=9.5 <11.1'],
262
+ versions: ['>=10.2 <11.1'],
260
263
  file: 'dist/next-server/server/next-server.js'
261
264
  }, nextServer => {
262
265
  const Server = nextServer.default
@@ -1,22 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const shimmer = require('../../datadog-shimmer')
4
3
  const { addHook } = require('./helpers/instrument')
5
- const { wrapVerify } = require('./passport-utils')
4
+ const { strategyHook } = require('./passport-utils')
6
5
 
7
6
  addHook({
8
7
  name: 'passport-http',
9
8
  file: 'lib/passport-http/strategies/basic.js',
10
9
  versions: ['>=0.3.0']
11
- }, BasicStrategy => {
12
- return shimmer.wrapFunction(BasicStrategy, BasicStrategy => function () {
13
- const type = 'http'
14
-
15
- if (typeof arguments[0] === 'function') {
16
- arguments[0] = wrapVerify(arguments[0], false, type)
17
- } else {
18
- arguments[1] = wrapVerify(arguments[1], (arguments[0] && arguments[0].passReqToCallback), type)
19
- }
20
- return BasicStrategy.apply(this, arguments)
21
- })
22
- })
10
+ }, strategyHook)
@@ -1,22 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const shimmer = require('../../datadog-shimmer')
4
3
  const { addHook } = require('./helpers/instrument')
5
- const { wrapVerify } = require('./passport-utils')
4
+ const { strategyHook } = require('./passport-utils')
6
5
 
7
6
  addHook({
8
7
  name: 'passport-local',
9
8
  file: 'lib/strategy.js',
10
9
  versions: ['>=1.0.0']
11
- }, Strategy => {
12
- return shimmer.wrapFunction(Strategy, Strategy => function () {
13
- const type = 'local'
14
-
15
- if (typeof arguments[0] === 'function') {
16
- arguments[0] = wrapVerify(arguments[0], false, type)
17
- } else {
18
- arguments[1] = wrapVerify(arguments[1], (arguments[0] && arguments[0].passReqToCallback), type)
19
- }
20
- return Strategy.apply(this, arguments)
21
- })
22
- })
10
+ }, strategyHook)
@@ -5,33 +5,57 @@ const { channel } = require('./helpers/instrument')
5
5
 
6
6
  const passportVerifyChannel = channel('datadog:passport:verify:finish')
7
7
 
8
- function wrapVerifiedAndPublish (username, password, verified, type) {
9
- if (!passportVerifyChannel.hasSubscribers) {
10
- return verified
11
- }
8
+ function wrapVerifiedAndPublish (framework, username, verified) {
9
+ return shimmer.wrapFunction(verified, function wrapVerified (verified) {
10
+ return function wrappedVerified (err, user) {
11
+ // if there is an error, it's neither an auth success nor a failure
12
+ if (!err) {
13
+ const abortController = new AbortController()
14
+
15
+ passportVerifyChannel.publish({ framework, login: username, user, success: !!user, abortController })
16
+
17
+ if (abortController.signal.aborted) return
18
+ }
12
19
 
13
- // eslint-disable-next-line n/handle-callback-err
14
- return shimmer.wrapFunction(verified, verified => function (err, user, info) {
15
- const credentials = { type, username }
16
- passportVerifyChannel.publish({ credentials, user })
17
- return verified.apply(this, arguments)
20
+ return verified.apply(this, arguments)
21
+ }
18
22
  })
19
23
  }
20
24
 
21
- function wrapVerify (verify, passReq, type) {
22
- if (passReq) {
23
- return function (req, username, password, verified) {
24
- arguments[3] = wrapVerifiedAndPublish(username, password, verified, type)
25
- return verify.apply(this, arguments)
25
+ function wrapVerify (verify) {
26
+ return function wrappedVerify (req, username, password, verified) {
27
+ if (passportVerifyChannel.hasSubscribers) {
28
+ const framework = `passport-${this.name}`
29
+
30
+ // replace the callback with our own wrapper to get the result
31
+ if (this._passReqToCallback) {
32
+ arguments[3] = wrapVerifiedAndPublish(framework, arguments[1], arguments[3])
33
+ } else {
34
+ arguments[2] = wrapVerifiedAndPublish(framework, arguments[0], arguments[2])
35
+ }
26
36
  }
27
- } else {
28
- return function (username, password, verified) {
29
- arguments[2] = wrapVerifiedAndPublish(username, password, verified, type)
30
- return verify.apply(this, arguments)
37
+
38
+ return verify.apply(this, arguments)
39
+ }
40
+ }
41
+
42
+ function wrapStrategy (Strategy) {
43
+ return function wrappedStrategy () {
44
+ // verify function can be either the first or second argument
45
+ if (typeof arguments[0] === 'function') {
46
+ arguments[0] = wrapVerify(arguments[0])
47
+ } else {
48
+ arguments[1] = wrapVerify(arguments[1])
31
49
  }
50
+
51
+ return Strategy.apply(this, arguments)
32
52
  }
33
53
  }
34
54
 
55
+ function strategyHook (Strategy) {
56
+ return shimmer.wrapFunction(Strategy, wrapStrategy)
57
+ }
58
+
35
59
  module.exports = {
36
- wrapVerify
60
+ strategyHook
37
61
  }
@@ -62,17 +62,17 @@ function wrapQuery (query) {
62
62
  abortController
63
63
  })
64
64
 
65
- const finish = asyncResource.bind(function (error) {
65
+ const finish = asyncResource.bind(function (error, res) {
66
66
  if (error) {
67
67
  errorCh.publish(error)
68
68
  }
69
- finishCh.publish()
69
+ finishCh.publish({ result: res?.rows })
70
70
  })
71
71
 
72
72
  if (abortController.signal.aborted) {
73
73
  const error = abortController.signal.reason || new Error('Aborted')
74
74
 
75
- // eslint-disable-next-line max-len
75
+ // eslint-disable-next-line @stylistic/js/max-len
76
76
  // Based on: https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg/lib/client.js#L510
77
77
  const reusingQuery = typeof pgQuery.submit === 'function'
78
78
  const callback = arguments[arguments.length - 1]
@@ -119,15 +119,15 @@ function wrapQuery (query) {
119
119
  if (newQuery.callback) {
120
120
  const originalCallback = callbackResource.bind(newQuery.callback)
121
121
  newQuery.callback = function (err, res) {
122
- finish(err)
122
+ finish(err, res)
123
123
  return originalCallback.apply(this, arguments)
124
124
  }
125
125
  } else if (newQuery.once) {
126
126
  newQuery
127
127
  .once('error', finish)
128
- .once('end', () => finish())
128
+ .once('end', (res) => finish(null, res))
129
129
  } else {
130
- newQuery.then(() => finish(), finish)
130
+ newQuery.then((res) => finish(null, res), finish)
131
131
  }
132
132
 
133
133
  try {
@@ -47,7 +47,7 @@ function isNewTest (test) {
47
47
  const testSuite = getTestSuitePath(test._requireFile, rootDir)
48
48
  const testsForSuite = knownTests?.playwright?.[testSuite] || []
49
49
 
50
- return !testsForSuite.includes(test.title)
50
+ return !testsForSuite.includes(getTestFullname(test))
51
51
  }
52
52
 
53
53
  function getSuiteType (test, type) {
@@ -224,10 +224,21 @@ function testWillRetry (test, testStatus) {
224
224
  return testStatus === 'fail' && test.results.length <= test.retries
225
225
  }
226
226
 
227
+ function getTestFullname (test) {
228
+ let parent = test.parent
229
+ const names = [test.title]
230
+ while (parent?._type === 'describe' || parent?._isDescribe) {
231
+ if (parent.title) {
232
+ names.unshift(parent.title)
233
+ }
234
+ parent = parent.parent
235
+ }
236
+ return names.join(' ')
237
+ }
238
+
227
239
  function testBeginHandler (test, browserName) {
228
240
  const {
229
241
  _requireFile: testSuiteAbsolutePath,
230
- title: testName,
231
242
  _type,
232
243
  location: {
233
244
  line: testSourceLine
@@ -238,6 +249,8 @@ function testBeginHandler (test, browserName) {
238
249
  return
239
250
  }
240
251
 
252
+ const testName = getTestFullname(test)
253
+
241
254
  const isNewTestSuite = !startedSuites.includes(testSuiteAbsolutePath)
242
255
 
243
256
  if (isNewTestSuite) {
@@ -412,7 +425,7 @@ function runnerHook (runnerExport, playwrightVersion) {
412
425
  }
413
426
  } catch (e) {
414
427
  isEarlyFlakeDetectionEnabled = false
415
- log.error(e)
428
+ log.error('Playwright session start error', e)
416
429
  }
417
430
 
418
431
  if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) {
@@ -425,7 +438,7 @@ function runnerHook (runnerExport, playwrightVersion) {
425
438
  }
426
439
  } catch (err) {
427
440
  isEarlyFlakeDetectionEnabled = false
428
- log.error(err)
441
+ log.error('Playwright known tests error', err)
429
442
  }
430
443
  }
431
444
 
@@ -169,11 +169,107 @@ function createWrapRouterMethod (name) {
169
169
 
170
170
  const wrapRouterMethod = createWrapRouterMethod('router')
171
171
 
172
- addHook({ name: 'router', versions: ['>=1'] }, Router => {
172
+ addHook({ name: 'router', versions: ['>=1 <2'] }, Router => {
173
173
  shimmer.wrap(Router.prototype, 'use', wrapRouterMethod)
174
174
  shimmer.wrap(Router.prototype, 'route', wrapRouterMethod)
175
175
 
176
176
  return Router
177
177
  })
178
178
 
179
+ const queryParserReadCh = channel('datadog:query:read:finish')
180
+
181
+ addHook({ name: 'router', versions: ['>=2'] }, Router => {
182
+ const WrappedRouter = shimmer.wrapFunction(Router, function (originalRouter) {
183
+ return function wrappedMethod () {
184
+ const router = originalRouter.apply(this, arguments)
185
+
186
+ shimmer.wrap(router, 'handle', function wrapHandle (originalHandle) {
187
+ return function wrappedHandle (req, res, next) {
188
+ const abortController = new AbortController()
189
+
190
+ if (queryParserReadCh.hasSubscribers && req) {
191
+ queryParserReadCh.publish({ req, res, query: req.query, abortController })
192
+
193
+ if (abortController.signal.aborted) return
194
+ }
195
+
196
+ return originalHandle.apply(this, arguments)
197
+ }
198
+ })
199
+
200
+ return router
201
+ }
202
+ })
203
+
204
+ shimmer.wrap(WrappedRouter.prototype, 'use', wrapRouterMethod)
205
+ shimmer.wrap(WrappedRouter.prototype, 'route', wrapRouterMethod)
206
+
207
+ return WrappedRouter
208
+ })
209
+
210
+ const routerParamStartCh = channel('datadog:router:param:start')
211
+ const visitedParams = new WeakSet()
212
+
213
+ function wrapHandleRequest (original) {
214
+ return function wrappedHandleRequest (req, res, next) {
215
+ if (routerParamStartCh.hasSubscribers && Object.keys(req.params).length && !visitedParams.has(req.params)) {
216
+ visitedParams.add(req.params)
217
+
218
+ const abortController = new AbortController()
219
+
220
+ routerParamStartCh.publish({
221
+ req,
222
+ res,
223
+ params: req?.params,
224
+ abortController
225
+ })
226
+
227
+ if (abortController.signal.aborted) return
228
+ }
229
+
230
+ return original.apply(this, arguments)
231
+ }
232
+ }
233
+
234
+ addHook({
235
+ name: 'router', file: 'lib/layer.js', versions: ['>=2']
236
+ }, Layer => {
237
+ shimmer.wrap(Layer.prototype, 'handleRequest', wrapHandleRequest)
238
+ return Layer
239
+ })
240
+
241
+ function wrapParam (original) {
242
+ return function wrappedProcessParams () {
243
+ arguments[1] = shimmer.wrapFunction(arguments[1], (originalFn) => {
244
+ return function wrappedFn (req, res) {
245
+ if (routerParamStartCh.hasSubscribers && Object.keys(req.params).length && !visitedParams.has(req.params)) {
246
+ visitedParams.add(req.params)
247
+
248
+ const abortController = new AbortController()
249
+
250
+ routerParamStartCh.publish({
251
+ req,
252
+ res,
253
+ params: req?.params,
254
+ abortController
255
+ })
256
+
257
+ if (abortController.signal.aborted) return
258
+ }
259
+
260
+ return originalFn.apply(this, arguments)
261
+ }
262
+ })
263
+
264
+ return original.apply(this, arguments)
265
+ }
266
+ }
267
+
268
+ addHook({
269
+ name: 'router', versions: ['>=2']
270
+ }, router => {
271
+ shimmer.wrap(router.prototype, 'param', wrapParam)
272
+ return router
273
+ })
274
+
179
275
  module.exports = { createWrapRouterMethod }
@@ -13,7 +13,7 @@ addHook({ name: 'sequelize', versions: ['>=4'] }, Sequelize => {
13
13
  const finishCh = channel('datadog:sequelize:query:finish')
14
14
 
15
15
  shimmer.wrap(Sequelize.prototype, 'query', query => {
16
- return function (sql) {
16
+ return function (sql, options) {
17
17
  if (!startCh.hasSubscribers) {
18
18
  return query.apply(this, arguments)
19
19
  }
@@ -27,9 +27,14 @@ addHook({ name: 'sequelize', versions: ['>=4'] }, Sequelize => {
27
27
  dialect = this.dialect.name
28
28
  }
29
29
 
30
- function onFinish () {
30
+ function onFinish (result) {
31
+ const type = options?.type || 'RAW'
32
+ if (type === 'RAW' && result?.length > 1) {
33
+ result = result[0]
34
+ }
35
+
31
36
  asyncResource.bind(function () {
32
- finishCh.publish()
37
+ finishCh.publish({ result })
33
38
  }, this).apply(this)
34
39
  }
35
40
 
@@ -40,7 +45,7 @@ addHook({ name: 'sequelize', versions: ['>=4'] }, Sequelize => {
40
45
  })
41
46
 
42
47
  const promise = query.apply(this, arguments)
43
- promise.then(onFinish, onFinish)
48
+ promise.then(onFinish, () => { onFinish() })
44
49
 
45
50
  return promise
46
51
  }, this).apply(this, arguments)
@@ -59,6 +59,10 @@ addHook({ name: names }, function (url) {
59
59
  isURL: true
60
60
  })
61
61
  }
62
+
63
+ static [Symbol.hasInstance] (instance) {
64
+ return instance instanceof URL
65
+ }
62
66
  }
63
67
  })
64
68
 
@@ -117,6 +117,7 @@ function getSortWrapper (sort) {
117
117
  let isEarlyFlakeDetectionEnabled = false
118
118
  let earlyFlakeDetectionNumRetries = 0
119
119
  let isEarlyFlakeDetectionFaulty = false
120
+ let isDiEnabled = false
120
121
  let knownTests = {}
121
122
 
122
123
  try {
@@ -126,10 +127,12 @@ function getSortWrapper (sort) {
126
127
  flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
127
128
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
128
129
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
130
+ isDiEnabled = libraryConfig.isDiEnabled
129
131
  }
130
132
  } catch (e) {
131
133
  isFlakyTestRetriesEnabled = false
132
134
  isEarlyFlakeDetectionEnabled = false
135
+ isDiEnabled = false
133
136
  }
134
137
 
135
138
  if (isFlakyTestRetriesEnabled && !this.ctx.config.retry && flakyTestRetriesCount > 0) {
@@ -169,6 +172,15 @@ function getSortWrapper (sort) {
169
172
  }
170
173
  }
171
174
 
175
+ if (isDiEnabled) {
176
+ try {
177
+ const workspaceProject = this.ctx.getCoreWorkspaceProject()
178
+ workspaceProject._provided._ddIsDiEnabled = isDiEnabled
179
+ } catch (e) {
180
+ log.warn('Could not send Dynamic Instrumentation configuration to workers.')
181
+ }
182
+ }
183
+
172
184
  let testCodeCoverageLinesTotal
173
185
 
174
186
  if (this.ctx.coverageProvider?.generateCoverage) {
@@ -298,13 +310,16 @@ addHook({
298
310
  const testName = getTestName(task)
299
311
  let isNew = false
300
312
  let isEarlyFlakeDetectionEnabled = false
313
+ let isDiEnabled = false
301
314
 
302
315
  try {
303
316
  const {
304
- _ddIsEarlyFlakeDetectionEnabled
317
+ _ddIsEarlyFlakeDetectionEnabled,
318
+ _ddIsDiEnabled
305
319
  } = globalThis.__vitest_worker__.providedContext
306
320
 
307
321
  isEarlyFlakeDetectionEnabled = _ddIsEarlyFlakeDetectionEnabled
322
+ isDiEnabled = _ddIsDiEnabled
308
323
 
309
324
  if (isEarlyFlakeDetectionEnabled) {
310
325
  isNew = newTasks.has(task)
@@ -316,12 +331,22 @@ addHook({
316
331
 
317
332
  // We finish the previous test here because we know it has failed already
318
333
  if (numAttempt > 0) {
334
+ const probe = {}
319
335
  const asyncResource = taskToAsync.get(task)
320
336
  const testError = task.result?.errors?.[0]
321
337
  if (asyncResource) {
322
338
  asyncResource.runInAsyncScope(() => {
323
- testErrorCh.publish({ error: testError })
339
+ testErrorCh.publish({
340
+ error: testError,
341
+ willBeRetried: true,
342
+ probe,
343
+ isDiEnabled
344
+ })
324
345
  })
346
+ // We wait for the probe to be set
347
+ if (probe.setProbePromise) {
348
+ await probe.setProbePromise
349
+ }
325
350
  }
326
351
  }
327
352