dd-trace 5.8.0 → 5.10.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 (109) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ci/init.js +3 -3
  3. package/package.json +5 -6
  4. package/packages/datadog-esbuild/index.js +2 -2
  5. package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
  6. package/packages/datadog-instrumentations/src/apollo.js +5 -3
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
  8. package/packages/datadog-instrumentations/src/cassandra-driver.js +1 -1
  9. package/packages/datadog-instrumentations/src/cucumber.js +6 -2
  10. package/packages/datadog-instrumentations/src/fs.js +0 -1
  11. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +1 -1
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +56 -56
  13. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  14. package/packages/datadog-instrumentations/src/http/client.js +1 -0
  15. package/packages/datadog-instrumentations/src/jest.js +94 -15
  16. package/packages/datadog-instrumentations/src/kafkajs.js +6 -8
  17. package/packages/datadog-instrumentations/src/ldapjs.js +2 -1
  18. package/packages/datadog-instrumentations/src/mocha.js +1 -1
  19. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  20. package/packages/datadog-instrumentations/src/net.js +1 -1
  21. package/packages/datadog-instrumentations/src/oracledb.js +1 -1
  22. package/packages/datadog-instrumentations/src/passport-utils.js +1 -0
  23. package/packages/datadog-instrumentations/src/playwright.js +8 -2
  24. package/packages/datadog-instrumentations/src/rhea.js +5 -2
  25. package/packages/datadog-instrumentations/src/tedious.js +1 -1
  26. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +1 -1
  27. package/packages/datadog-plugin-apollo/src/gateway/plan.js +0 -1
  28. package/packages/datadog-plugin-apollo/src/gateway/request.js +2 -17
  29. package/packages/datadog-plugin-apollo/src/gateway/validate.js +6 -2
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
  31. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +2 -2
  33. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  34. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  36. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  37. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  38. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -1
  39. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -2
  40. package/packages/datadog-plugin-child_process/src/index.js +1 -1
  41. package/packages/datadog-plugin-couchbase/src/index.js +2 -1
  42. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -0
  43. package/packages/datadog-plugin-fetch/src/index.js +1 -1
  44. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  45. package/packages/datadog-plugin-grpc/src/client.js +2 -2
  46. package/packages/datadog-plugin-grpc/src/server.js +2 -2
  47. package/packages/datadog-plugin-http/src/client.js +2 -2
  48. package/packages/datadog-plugin-http2/src/client.js +4 -3
  49. package/packages/datadog-plugin-jest/src/index.js +1 -0
  50. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -1
  51. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
  52. package/packages/datadog-plugin-next/src/index.js +1 -1
  53. package/packages/datadog-plugin-openai/src/index.js +4 -4
  54. package/packages/datadog-plugin-playwright/src/index.js +7 -2
  55. package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
  56. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  57. package/packages/datadog-plugin-router/src/index.js +1 -1
  58. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  59. package/packages/dd-trace/src/appsec/blocking.js +1 -1
  60. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +17 -17
  61. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -0
  62. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +132 -132
  63. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -0
  64. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +1 -1
  65. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +6 -2
  66. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +2 -1
  67. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
  68. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +4 -4
  69. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -1
  70. package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +27 -18
  71. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +1 -1
  72. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +1 -1
  73. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +7 -4
  74. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +2 -2
  75. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +9 -3
  76. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -1
  77. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +1 -0
  78. package/packages/dd-trace/src/config.js +16 -14
  79. package/packages/dd-trace/src/datastreams/pathway.js +1 -1
  80. package/packages/dd-trace/src/datastreams/processor.js +15 -15
  81. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +2 -2
  82. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +1 -1
  83. package/packages/dd-trace/src/exporters/common/request.js +1 -0
  84. package/packages/dd-trace/src/exporters/span-stats/writer.js +0 -1
  85. package/packages/dd-trace/src/external-logger/src/index.js +5 -5
  86. package/packages/dd-trace/src/opentelemetry/span.js +2 -0
  87. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -1
  88. package/packages/dd-trace/src/opentracing/span.js +1 -1
  89. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  90. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -1
  91. package/packages/dd-trace/src/plugins/composite.js +3 -4
  92. package/packages/dd-trace/src/plugins/database.js +1 -0
  93. package/packages/dd-trace/src/plugins/index.js +43 -43
  94. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  95. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  96. package/packages/dd-trace/src/plugins/util/git.js +33 -11
  97. package/packages/dd-trace/src/plugins/util/test.js +2 -1
  98. package/packages/dd-trace/src/plugins/util/web.js +4 -4
  99. package/packages/dd-trace/src/profiling/config.js +1 -1
  100. package/packages/dd-trace/src/profiling/loggers/console.js +1 -1
  101. package/packages/dd-trace/src/runtime_metrics.js +8 -5
  102. package/packages/dd-trace/src/serverless.js +3 -2
  103. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +0 -1
  104. package/packages/dd-trace/src/span_processor.js +2 -2
  105. package/packages/dd-trace/src/span_stats.js +1 -1
  106. package/packages/dd-trace/src/telemetry/dependencies.js +4 -5
  107. package/packages/dd-trace/src/telemetry/index.js +12 -13
  108. package/packages/dd-trace/src/telemetry/send-data.js +0 -1
  109. package/packages/dd-trace/src/util.js +7 -7
@@ -48,7 +48,6 @@ dev,eslint-config-standard,MIT,Copyright Feross Aboukhadijeh
48
48
  dev,eslint-plugin-import,MIT,Copyright 2015 Ben Mosher
49
49
  dev,eslint-plugin-mocha,MIT,Copyright 2014 Mathias Schreck
50
50
  dev,eslint-plugin-n,MIT,Copyright 2015 Toru Nagashima
51
- dev,eslint-plugin-node,MIT,Copyright 2015 Toru Nagashima
52
51
  dev,eslint-plugin-promise,ISC,jden and other contributors
53
52
  dev,eslint-plugin-standard,MIT,Copyright 2015 Jamund Ferguson
54
53
  dev,express,MIT,Copyright 2009-2014 TJ Holowaychuk 2013-2014 Roman Shtylman 2014-2015 Douglas Christopher Wilson
package/ci/init.js CHANGED
@@ -20,9 +20,9 @@ if (isAgentlessEnabled) {
20
20
  exporter: 'datadog'
21
21
  }
22
22
  } else {
23
- console.error(`DD_CIVISIBILITY_AGENTLESS_ENABLED is set, \
24
- but neither DD_API_KEY nor DATADOG_API_KEY are set in your environment, \
25
- so dd-trace will not be initialized.`)
23
+ console.error('DD_CIVISIBILITY_AGENTLESS_ENABLED is set, but neither ' +
24
+ 'DD_API_KEY nor DATADOG_API_KEY are set in your environment, so ' +
25
+ 'dd-trace will not be initialized.')
26
26
  shouldInit = false
27
27
  }
28
28
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.8.0",
3
+ "version": "5.10.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -69,11 +69,11 @@
69
69
  "node": ">=18"
70
70
  },
71
71
  "dependencies": {
72
- "@datadog/native-appsec": "7.1.0",
72
+ "@datadog/native-appsec": "7.1.1",
73
73
  "@datadog/native-iast-rewriter": "2.3.0",
74
74
  "@datadog/native-iast-taint-tracking": "1.7.0",
75
75
  "@datadog/native-metrics": "^2.0.0",
76
- "@datadog/pprof": "5.1.0",
76
+ "@datadog/pprof": "5.2.0",
77
77
  "@datadog/sketches-js": "^2.1.0",
78
78
  "@opentelemetry/api": "^1.0.0",
79
79
  "@opentelemetry/core": "^1.14.0",
@@ -95,7 +95,7 @@
95
95
  "node-abort-controller": "^3.1.1",
96
96
  "opentracing": ">=0.12.1",
97
97
  "path-to-regexp": "^0.1.2",
98
- "pprof-format": "^2.0.7",
98
+ "pprof-format": "^2.1.0",
99
99
  "protobufjs": "^7.2.5",
100
100
  "retry": "^0.13.1",
101
101
  "semver": "^7.5.4",
@@ -116,11 +116,10 @@
116
116
  "dotenv": "16.3.1",
117
117
  "esbuild": "0.16.12",
118
118
  "eslint": "^8.23.0",
119
- "eslint-config-standard": "^11.0.0-beta.0",
119
+ "eslint-config-standard": "^17.1.0",
120
120
  "eslint-plugin-import": "^2.8.0",
121
121
  "eslint-plugin-mocha": "^10.1.0",
122
122
  "eslint-plugin-n": "^15.7.0",
123
- "eslint-plugin-node": "^5.2.1",
124
123
  "eslint-plugin-promise": "^3.6.0",
125
124
  "eslint-plugin-standard": "^3.0.1",
126
125
  "express": "^4.18.2",
@@ -89,7 +89,7 @@ module.exports.setup = function (build) {
89
89
 
90
90
  let pathToPackageJson
91
91
  try {
92
- pathToPackageJson = require.resolve(`${extracted.pkg}/package.json`, { paths: [ args.resolveDir ] })
92
+ pathToPackageJson = require.resolve(`${extracted.pkg}/package.json`, { paths: [args.resolveDir] })
93
93
  } catch (err) {
94
94
  if (err.code === 'MODULE_NOT_FOUND') {
95
95
  if (!internal) {
@@ -173,7 +173,7 @@ function dotFriendlyResolve (path, directory) {
173
173
  path = '../'
174
174
  }
175
175
 
176
- return require.resolve(path, { paths: [ directory ] })
176
+ return require.resolve(path, { paths: [directory] })
177
177
  }
178
178
 
179
179
  /**
@@ -37,7 +37,7 @@ function wrapExecuteHTTPGraphQLRequest (originalExecuteHTTPGraphQLRequest) {
37
37
  })
38
38
 
39
39
  resolve({
40
- headers: headers,
40
+ headers,
41
41
  status: abortData.statusCode,
42
42
  body: {
43
43
  kind: 'complete',
@@ -14,14 +14,14 @@ const CHANNELS = {
14
14
  'gateway.postprocessing': tracingChannel('apm:apollo:gateway:postprocessing')
15
15
  }
16
16
 
17
- const executorCh = channel('apm:apollo:gateway:request:executor')
18
17
  const generalErrorCh = channel('apm:apollo:gateway:general:error')
19
18
 
20
19
  function wrapExecutor (executor) {
21
20
  return function (...args) {
21
+ const channel = CHANNELS['gateway.request']
22
22
  const ctx = { requestContext: args[0], gateway: this }
23
- executorCh.publish(ctx)
24
- return executor.apply(this, args)
23
+
24
+ return channel.tracePromise(executor, ctx, this, ...args)
25
25
  }
26
26
  }
27
27
 
@@ -67,7 +67,9 @@ function wrapStartActiveSpan (startActiveSpan) {
67
67
  }
68
68
  break
69
69
  }
70
+ // Patch `executor` instead so the requestContext can be captured.
70
71
  case 'gateway.request':
72
+ break
71
73
  case 'gateway.execute':
72
74
  case 'gateway.postprocessing' :
73
75
  case 'gateway.fetch': {
@@ -111,6 +111,7 @@ function wrapSmithySend (send) {
111
111
  }
112
112
 
113
113
  function wrapCb (cb, serviceName, request, ar) {
114
+ // eslint-disable-next-line n/handle-callback-err
114
115
  return function wrappedCb (err, response) {
115
116
  const obj = { request, response }
116
117
  return ar.runInAsyncScope(() => {
@@ -163,7 +164,9 @@ function getChannelSuffix (name) {
163
164
  's3',
164
165
  'sns',
165
166
  'sqs'
166
- ].includes(name) ? name : 'default'
167
+ ].includes(name)
168
+ ? name
169
+ : 'default'
167
170
  }
168
171
 
169
172
  addHook({ name: '@smithy/smithy-client', versions: ['>=1.0.3'] }, smithy => {
@@ -10,7 +10,7 @@ const shimmer = require('../../datadog-shimmer')
10
10
  const startCh = channel('apm:cassandra-driver:query:start')
11
11
  const finishCh = channel('apm:cassandra-driver:query:finish')
12
12
  const errorCh = channel('apm:cassandra-driver:query:error')
13
- const connectCh = channel(`apm:cassandra-driver:query:connect`)
13
+ const connectCh = channel('apm:cassandra-driver:query:connect')
14
14
 
15
15
  addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
16
16
  shimmer.wrap(cassandra.Client.prototype, 'batch', batch => function (queries, options, callback) {
@@ -132,7 +132,8 @@ function wrapRun (pl, isLatestVersion) {
132
132
  promise.finally(() => {
133
133
  const result = this.getWorstStepResult()
134
134
  const { status, skipReason, errorMessage } = isLatestVersion
135
- ? getStatusFromResultLatest(result) : getStatusFromResult(result)
135
+ ? getStatusFromResultLatest(result)
136
+ : getStatusFromResult(result)
136
137
 
137
138
  if (lastStatusByPickleId.has(this.pickle.id)) {
138
139
  lastStatusByPickleId.get(this.pickle.id).push(status)
@@ -177,7 +178,8 @@ function wrapRun (pl, isLatestVersion) {
177
178
 
178
179
  promise.then((result) => {
179
180
  const { status, skipReason, errorMessage } = isLatestVersion
180
- ? getStatusFromResultLatest(result) : getStatusFromResult(result)
181
+ ? getStatusFromResultLatest(result)
182
+ : getStatusFromResult(result)
181
183
 
182
184
  testFinishCh.publish({ isStep: true, status, skipReason, errorMessage })
183
185
  })
@@ -290,6 +292,8 @@ function getWrappedStart (start, frameworkVersion) {
290
292
  const knownTestsResponse = await knownTestsPromise
291
293
  if (!knownTestsResponse.err) {
292
294
  knownTests = knownTestsResponse.knownTests
295
+ } else {
296
+ isEarlyFlakeDetectionEnabled = false
293
297
  }
294
298
  }
295
299
 
@@ -1,4 +1,3 @@
1
-
2
1
  'use strict'
3
2
 
4
3
  const {
@@ -11,7 +11,7 @@ const requestStartCh = channel('apm:google-cloud-pubsub:request:start')
11
11
  const requestFinishCh = channel('apm:google-cloud-pubsub:request:finish')
12
12
  const requestErrorCh = channel('apm:google-cloud-pubsub:request:error')
13
13
 
14
- const receiveStartCh = channel(`apm:google-cloud-pubsub:receive:start`)
14
+ const receiveStartCh = channel('apm:google-cloud-pubsub:receive:start')
15
15
  const receiveFinishCh = channel('apm:google-cloud-pubsub:receive:finish')
16
16
  const receiveErrorCh = channel('apm:google-cloud-pubsub:receive:error')
17
17
 
@@ -23,62 +23,62 @@ module.exports = {
23
23
  '@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
24
24
  '@redis/client': () => require('../redis'),
25
25
  '@smithy/smithy-client': () => require('../aws-sdk'),
26
- 'aerospike': () => require('../aerospike'),
27
- 'amqp10': () => require('../amqp10'),
28
- 'amqplib': () => require('../amqplib'),
26
+ aerospike: () => require('../aerospike'),
27
+ amqp10: () => require('../amqp10'),
28
+ amqplib: () => require('../amqplib'),
29
29
  'aws-sdk': () => require('../aws-sdk'),
30
- 'bluebird': () => require('../bluebird'),
30
+ bluebird: () => require('../bluebird'),
31
31
  'body-parser': () => require('../body-parser'),
32
- 'bunyan': () => require('../bunyan'),
32
+ bunyan: () => require('../bunyan'),
33
33
  'cassandra-driver': () => require('../cassandra-driver'),
34
- 'child_process': () => require('../child_process'),
35
- 'connect': () => require('../connect'),
36
- 'cookie': () => require('../cookie'),
34
+ child_process: () => require('../child_process'),
35
+ connect: () => require('../connect'),
36
+ cookie: () => require('../cookie'),
37
37
  'cookie-parser': () => require('../cookie-parser'),
38
- 'couchbase': () => require('../couchbase'),
39
- 'crypto': () => require('../crypto'),
40
- 'cypress': () => require('../cypress'),
41
- 'dns': () => require('../dns'),
42
- 'elasticsearch': () => require('../elasticsearch'),
43
- 'express': () => require('../express'),
38
+ couchbase: () => require('../couchbase'),
39
+ crypto: () => require('../crypto'),
40
+ cypress: () => require('../cypress'),
41
+ dns: () => require('../dns'),
42
+ elasticsearch: () => require('../elasticsearch'),
43
+ express: () => require('../express'),
44
44
  'express-mongo-sanitize': () => require('../express-mongo-sanitize'),
45
- 'fastify': () => require('../fastify'),
45
+ fastify: () => require('../fastify'),
46
46
  'find-my-way': () => require('../find-my-way'),
47
- 'fs': () => require('../fs'),
47
+ fs: () => require('../fs'),
48
48
  'generic-pool': () => require('../generic-pool'),
49
- 'graphql': () => require('../graphql'),
50
- 'grpc': () => require('../grpc'),
51
- 'hapi': () => require('../hapi'),
52
- 'http': () => require('../http'),
53
- 'http2': () => require('../http2'),
54
- 'https': () => require('../http'),
55
- 'ioredis': () => require('../ioredis'),
49
+ graphql: () => require('../graphql'),
50
+ grpc: () => require('../grpc'),
51
+ hapi: () => require('../hapi'),
52
+ http: () => require('../http'),
53
+ http2: () => require('../http2'),
54
+ https: () => require('../http'),
55
+ ioredis: () => require('../ioredis'),
56
56
  'jest-circus': () => require('../jest'),
57
57
  'jest-config': () => require('../jest'),
58
58
  'jest-environment-node': () => require('../jest'),
59
59
  'jest-environment-jsdom': () => require('../jest'),
60
60
  'jest-jasmine2': () => require('../jest'),
61
61
  'jest-worker': () => require('../jest'),
62
- 'knex': () => require('../knex'),
63
- 'koa': () => require('../koa'),
62
+ knex: () => require('../knex'),
63
+ koa: () => require('../koa'),
64
64
  'koa-router': () => require('../koa'),
65
- 'kafkajs': () => require('../kafkajs'),
66
- 'ldapjs': () => require('../ldapjs'),
65
+ kafkajs: () => require('../kafkajs'),
66
+ ldapjs: () => require('../ldapjs'),
67
67
  'limitd-client': () => require('../limitd-client'),
68
- 'mariadb': () => require('../mariadb'),
69
- 'memcached': () => require('../memcached'),
68
+ mariadb: () => require('../mariadb'),
69
+ memcached: () => require('../memcached'),
70
70
  'microgateway-core': () => require('../microgateway-core'),
71
- 'mocha': () => require('../mocha'),
71
+ mocha: () => require('../mocha'),
72
72
  'mocha-each': () => require('../mocha'),
73
- 'moleculer': () => require('../moleculer'),
74
- 'mongodb': () => require('../mongodb'),
73
+ moleculer: () => require('../moleculer'),
74
+ mongodb: () => require('../mongodb'),
75
75
  'mongodb-core': () => require('../mongodb-core'),
76
- 'mongoose': () => require('../mongoose'),
77
- 'mquery': () => require('../mquery'),
78
- 'mysql': () => require('../mysql'),
79
- 'mysql2': () => require('../mysql2'),
80
- 'net': () => require('../net'),
81
- 'next': () => require('../next'),
76
+ mongoose: () => require('../mongoose'),
77
+ mquery: () => require('../mquery'),
78
+ mysql: () => require('../mysql'),
79
+ mysql2: () => require('../mysql2'),
80
+ net: () => require('../net'),
81
+ next: () => require('../next'),
82
82
  'node:child_process': () => require('../child_process'),
83
83
  'node:crypto': () => require('../crypto'),
84
84
  'node:dns': () => require('../dns'),
@@ -86,26 +86,26 @@ module.exports = {
86
86
  'node:http2': () => require('../http2'),
87
87
  'node:https': () => require('../http'),
88
88
  'node:net': () => require('../net'),
89
- 'oracledb': () => require('../oracledb'),
90
- 'openai': () => require('../openai'),
91
- 'paperplane': () => require('../paperplane'),
89
+ oracledb: () => require('../oracledb'),
90
+ openai: () => require('../openai'),
91
+ paperplane: () => require('../paperplane'),
92
92
  'passport-http': () => require('../passport-http'),
93
93
  'passport-local': () => require('../passport-local'),
94
- 'pg': () => require('../pg'),
95
- 'pino': () => require('../pino'),
94
+ pg: () => require('../pg'),
95
+ pino: () => require('../pino'),
96
96
  'pino-pretty': () => require('../pino'),
97
- 'playwright': () => require('../playwright'),
97
+ playwright: () => require('../playwright'),
98
98
  'promise-js': () => require('../promise-js'),
99
- 'promise': () => require('../promise'),
100
- 'q': () => require('../q'),
101
- 'qs': () => require('../qs'),
102
- 'redis': () => require('../redis'),
103
- 'restify': () => require('../restify'),
104
- 'rhea': () => require('../rhea'),
105
- 'router': () => require('../router'),
106
- 'sharedb': () => require('../sharedb'),
107
- 'sequelize': () => require('../sequelize'),
108
- 'tedious': () => require('../tedious'),
109
- 'when': () => require('../when'),
110
- 'winston': () => require('../winston')
99
+ promise: () => require('../promise'),
100
+ q: () => require('../q'),
101
+ qs: () => require('../qs'),
102
+ redis: () => require('../redis'),
103
+ restify: () => require('../restify'),
104
+ rhea: () => require('../rhea'),
105
+ router: () => require('../router'),
106
+ sharedb: () => require('../sharedb'),
107
+ sequelize: () => require('../sequelize'),
108
+ tedious: () => require('../tedious'),
109
+ when: () => require('../when'),
110
+ winston: () => require('../winston')
111
111
  }
@@ -56,13 +56,13 @@ if (semver.satisfies(process.versions.node, '>=17.8.0')) {
56
56
  bound = this.runInAsyncScope.bind(this, fn, thisArg)
57
57
  }
58
58
  Object.defineProperties(bound, {
59
- 'length': {
59
+ length: {
60
60
  configurable: true,
61
61
  enumerable: false,
62
62
  value: fn.length,
63
63
  writable: false
64
64
  },
65
- 'asyncResource': {
65
+ asyncResource: {
66
66
  configurable: true,
67
67
  enumerable: true,
68
68
  value: this,
@@ -155,6 +155,7 @@ function patch (http, methodName) {
155
155
  try {
156
156
  return urlToOptions(new url.URL(inputURL))
157
157
  } catch (e) {
158
+ // eslint-disable-next-line n/no-deprecated-api
158
159
  return url.parse(inputURL)
159
160
  }
160
161
  } else if (inputURL instanceof url.URL) {
@@ -10,7 +10,7 @@ const {
10
10
  getTestLineStart,
11
11
  getTestSuitePath,
12
12
  getTestParametersString,
13
- EFD_STRING,
13
+ addEfdStringToTestName,
14
14
  removeEfdStringFromTestName
15
15
  } = require('../../dd-trace/src/plugins/util/test')
16
16
  const {
@@ -44,11 +44,14 @@ const knownTestsCh = channel('ci:jest:known-tests')
44
44
 
45
45
  const itrSkippedSuitesCh = channel('ci:jest:itr:skipped-suites')
46
46
 
47
+ // Message sent by jest's main process to workers to run a test suite (=test file)
48
+ // https://github.com/jestjs/jest/blob/1d682f21c7a35da4d3ab3a1436a357b980ebd0fa/packages/jest-worker/src/types.ts#L37
49
+ const CHILD_MESSAGE_CALL = 1
47
50
  // Maximum time we'll wait for the tracer to flush
48
51
  const FLUSH_TIMEOUT = 10000
49
52
 
50
53
  let skippableSuites = []
51
- let knownTests = []
54
+ let knownTests = {}
52
55
  let isCodeCoverageEnabled = false
53
56
  let isSuitesSkippingEnabled = false
54
57
  let isUserCodeCoverageEnabled = false
@@ -63,16 +66,17 @@ let hasFilteredSkippableSuites = false
63
66
  const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
64
67
 
65
68
  const specStatusToTestStatus = {
66
- 'pending': 'skip',
67
- 'disabled': 'skip',
68
- 'todo': 'skip',
69
- 'passed': 'pass',
70
- 'failed': 'fail'
69
+ pending: 'skip',
70
+ disabled: 'skip',
71
+ todo: 'skip',
72
+ passed: 'pass',
73
+ failed: 'fail'
71
74
  }
72
75
 
73
76
  const asyncResources = new WeakMap()
74
77
  const originalTestFns = new WeakMap()
75
78
  const retriedTestsToNumAttempts = new Map()
79
+ const newTestsTestStatuses = new Map()
76
80
 
77
81
  // based on https://github.com/facebook/jest/blob/main/packages/jest-circus/src/formatNodeAssertErrors.ts#L41
78
82
  function formatJestError (errors) {
@@ -101,8 +105,11 @@ function getTestEnvironmentOptions (config) {
101
105
  return {}
102
106
  }
103
107
 
104
- function getEfdTestName (testName, numAttempt) {
105
- return `${EFD_STRING} (#${numAttempt}): ${testName}`
108
+ function getEfdStats (testStatuses) {
109
+ return testStatuses.reduce((acc, testStatus) => {
110
+ acc[testStatus]++
111
+ return acc
112
+ }, { pass: 0, fail: 0 })
106
113
  }
107
114
 
108
115
  function getWrappedEnvironment (BaseEnvironment, jestVersion) {
@@ -127,9 +134,12 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
127
134
  this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled
128
135
 
129
136
  if (this.isEarlyFlakeDetectionEnabled) {
137
+ const hasKnownTests = !!knownTests.jest
130
138
  earlyFlakeDetectionNumRetries = this.testEnvironmentOptions._ddEarlyFlakeDetectionNumRetries
131
139
  try {
132
- this.knownTestsForThisSuite = this.getKnownTestsForSuite(this.testEnvironmentOptions._ddKnownTests)
140
+ this.knownTestsForThisSuite = hasKnownTests
141
+ ? (knownTests.jest[this.testSuite] || [])
142
+ : this.getKnownTestsForSuite(this.testEnvironmentOptions._ddKnownTests)
133
143
  } catch (e) {
134
144
  // If there has been an error parsing the tests, we'll disable Early Flake Deteciton
135
145
  this.isEarlyFlakeDetectionEnabled = false
@@ -149,14 +159,15 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
149
159
  if (typeof knownTestsForSuite === 'string') {
150
160
  knownTestsForSuite = JSON.parse(knownTestsForSuite)
151
161
  }
152
- return knownTestsForSuite.jest?.[this.testSuite] || []
162
+ return knownTestsForSuite
153
163
  }
154
164
 
155
165
  // Add the `add_test` event we don't have the test object yet, so
156
166
  // we use its describe block to get the full name
157
167
  getTestNameFromAddTestEvent (event, state) {
158
168
  const describeSuffix = getJestTestName(state.currentDescribeBlock)
159
- return removeEfdStringFromTestName(`${describeSuffix} ${event.testName}`).trim()
169
+ const fullTestName = describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
170
+ return removeEfdStringFromTestName(fullTestName)
160
171
  }
161
172
 
162
173
  async handleTestEvent (event, state) {
@@ -197,7 +208,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
197
208
  retriedTestsToNumAttempts.set(originalTestName, numEfdRetry + 1)
198
209
  }
199
210
  }
200
-
201
211
  asyncResource.runInAsyncScope(() => {
202
212
  testStartCh.publish({
203
213
  name: removeEfdStringFromTestName(testName),
@@ -223,7 +233,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
223
233
  retriedTestsToNumAttempts.set(testName, 0)
224
234
  for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
225
235
  if (this.global.test) {
226
- this.global.test(getEfdTestName(event.testName, retryIndex), event.fn, event.timeout)
236
+ this.global.test(addEfdStringToTestName(event.testName, retryIndex), event.fn, event.timeout)
227
237
  } else {
228
238
  log.error('Early flake detection could not retry test because global.test is undefined')
229
239
  }
@@ -246,6 +256,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
246
256
  })
247
257
  // restore in case it is retried
248
258
  event.test.fn = originalTestFns.get(event.test)
259
+ // We'll store the test statuses of the retries
260
+ if (this.isEarlyFlakeDetectionEnabled) {
261
+ const testName = getJestTestName(event.test)
262
+ const originalTestName = removeEfdStringFromTestName(testName)
263
+ const isNewTest = retriedTestsToNumAttempts.has(originalTestName)
264
+ if (isNewTest) {
265
+ if (newTestsTestStatuses.has(originalTestName)) {
266
+ newTestsTestStatuses.get(originalTestName).push(status)
267
+ } else {
268
+ newTestsTestStatuses.set(originalTestName, [status])
269
+ }
270
+ }
271
+ }
249
272
  })
250
273
  }
251
274
  if (event.name === 'test_skip' || event.name === 'test_todo') {
@@ -405,6 +428,9 @@ function cliWrapper (cli, jestVersion) {
405
428
  const { err, knownTests: receivedKnownTests } = await knownTestsPromise
406
429
  if (!err) {
407
430
  knownTests = receivedKnownTests
431
+ } else {
432
+ // We disable EFD if there has been an error in the known tests request
433
+ isEarlyFlakeDetectionEnabled = false
408
434
  }
409
435
  } catch (err) {
410
436
  log.error(err)
@@ -509,6 +535,28 @@ function cliWrapper (cli, jestVersion) {
509
535
 
510
536
  numSkippedSuites = 0
511
537
 
538
+ /**
539
+ * If Early Flake Detection (EFD) is enabled the logic is as follows:
540
+ * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
541
+ * - If just a single attempt passes, we will prevent the test process from failing.
542
+ * The rationale behind is the following: you may still be able to block your CI pipeline by gating
543
+ * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
544
+ */
545
+
546
+ if (isEarlyFlakeDetectionEnabled) {
547
+ let numFailedTestsToIgnore = 0
548
+ for (const testStatuses of newTestsTestStatuses.values()) {
549
+ const { pass, fail } = getEfdStats(testStatuses)
550
+ if (pass > 0) { // as long as one passes, we'll consider the test passed
551
+ numFailedTestsToIgnore += fail
552
+ }
553
+ }
554
+ // If every test that failed was an EFD retry, we'll consider the suite passed
555
+ if (numFailedTestsToIgnore !== 0 && result.results.numFailedTests === numFailedTestsToIgnore) {
556
+ result.results.success = true
557
+ }
558
+ }
559
+
512
560
  return result
513
561
  })
514
562
 
@@ -620,7 +668,6 @@ function configureTestEnvironment (readConfigsResult) {
620
668
  // because `jestAdapterWrapper` runs in a different process. We have to go through `testEnvironmentOptions`
621
669
  configs.forEach(config => {
622
670
  config.testEnvironmentOptions._ddTestCodeCoverageEnabled = isCodeCoverageEnabled
623
- config.testEnvironmentOptions._ddKnownTests = knownTests
624
671
  })
625
672
 
626
673
  isUserCodeCoverageEnabled = !!readConfigsResult.globalConfig.collectCoverage
@@ -796,6 +843,38 @@ addHook({
796
843
  file: 'build/workers/ChildProcessWorker.js'
797
844
  }, (childProcessWorker) => {
798
845
  const ChildProcessWorker = childProcessWorker.default
846
+ shimmer.wrap(ChildProcessWorker.prototype, 'send', send => function (request) {
847
+ if (!isEarlyFlakeDetectionEnabled) {
848
+ return send.apply(this, arguments)
849
+ }
850
+ const [type] = request
851
+ // eslint-disable-next-line
852
+ // https://github.com/jestjs/jest/blob/1d682f21c7a35da4d3ab3a1436a357b980ebd0fa/packages/jest-worker/src/workers/ChildProcessWorker.ts#L424
853
+ if (type === CHILD_MESSAGE_CALL) {
854
+ // This is the message that the main process sends to the worker to run a test suite (=test file).
855
+ // In here we modify the config.testEnvironmentOptions to include the known tests for the suite.
856
+ // This way the suite only knows about the tests that are part of it.
857
+ const args = request[request.length - 1]
858
+ if (args.length > 1) {
859
+ return send.apply(this, arguments)
860
+ }
861
+ if (!args[0]?.config) {
862
+ return send.apply(this, arguments)
863
+ }
864
+ const [{ globalConfig, config, path: testSuiteAbsolutePath }] = args
865
+ const testSuite = getTestSuitePath(testSuiteAbsolutePath, globalConfig.rootDir || process.cwd())
866
+ const suiteKnownTests = knownTests.jest?.[testSuite] || []
867
+ args[0].config = {
868
+ ...config,
869
+ testEnvironmentOptions: {
870
+ ...config.testEnvironmentOptions,
871
+ _ddKnownTests: suiteKnownTests
872
+ }
873
+ }
874
+ }
875
+
876
+ return send.apply(this, arguments)
877
+ })
799
878
  shimmer.wrap(ChildProcessWorker.prototype, '_onMessage', _onMessage => function () {
800
879
  const [code, data] = arguments[0]
801
880
  if (code === JEST_WORKER_TRACE_PAYLOAD_CODE) { // datadog trace payload
@@ -38,7 +38,8 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
38
38
  constructor (options) {
39
39
  super(options)
40
40
  this._brokers = (options.brokers && typeof options.brokers !== 'function')
41
- ? options.brokers.join(',') : undefined
41
+ ? options.brokers.join(',')
42
+ : undefined
42
43
  }
43
44
  }
44
45
 
@@ -67,7 +68,10 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
67
68
  const result = send.apply(this, arguments)
68
69
 
69
70
  result.then(
70
- innerAsyncResource.bind(() => producerFinishCh.publish(undefined)),
71
+ innerAsyncResource.bind(res => {
72
+ producerFinishCh.publish(undefined)
73
+ producerCommitCh.publish(res)
74
+ }),
71
75
  innerAsyncResource.bind(err => {
72
76
  if (err) {
73
77
  producerErrorCh.publish(err)
@@ -76,12 +80,6 @@ addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKaf
76
80
  })
77
81
  )
78
82
 
79
- result.then(res => {
80
- if (producerCommitCh.hasSubscribers) {
81
- producerCommitCh.publish(res)
82
- }
83
- })
84
-
85
83
  return result
86
84
  } catch (e) {
87
85
  producerErrorCh.publish(e)
@@ -76,8 +76,9 @@ addHook({ name: 'ldapjs', versions: ['>=2'] }, ldapjs => {
76
76
  const callbackIndex = getCallbackArgIndex(arguments)
77
77
  if (callbackIndex > -1) {
78
78
  const callback = arguments[callbackIndex]
79
+ // eslint-disable-next-line n/handle-callback-err
79
80
  arguments[callbackIndex] = shimmer.wrap(callback, function (err, corkedEmitter) {
80
- if (typeof corkedEmitter === 'object' && typeof corkedEmitter['on'] === 'function') {
81
+ if (typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
81
82
  wrapEmitter(corkedEmitter)
82
83
  }
83
84
  callback.apply(this, arguments)
@@ -483,7 +483,7 @@ addHook({
483
483
  */
484
484
  shimmer.wrap(Mocha.prototype, 'run', run => function () {
485
485
  if (this.options.parallel) {
486
- log.warn(`Unable to initialize CI Visibility because Mocha is running in parallel mode.`)
486
+ log.warn('Unable to initialize CI Visibility because Mocha is running in parallel mode.')
487
487
  return run.apply(this, arguments)
488
488
  }
489
489