dd-trace 5.31.0 → 5.33.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 (85) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +17 -14
  3. package/index.d.ts +11 -1
  4. package/package.json +6 -5
  5. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
  6. package/packages/datadog-instrumentations/src/cucumber.js +31 -14
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  8. package/packages/datadog-instrumentations/src/jest.js +105 -56
  9. package/packages/datadog-instrumentations/src/mocha/main.js +9 -4
  10. package/packages/datadog-instrumentations/src/mocha/utils.js +27 -9
  11. package/packages/datadog-instrumentations/src/mocha/worker.js +4 -2
  12. package/packages/datadog-instrumentations/src/node-serialize.js +22 -0
  13. package/packages/datadog-instrumentations/src/openai.js +2 -0
  14. package/packages/datadog-instrumentations/src/playwright.js +8 -3
  15. package/packages/datadog-instrumentations/src/vitest.js +134 -62
  16. package/packages/datadog-instrumentations/src/vm.js +49 -0
  17. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +16 -0
  18. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +63 -0
  19. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +287 -0
  20. package/packages/datadog-plugin-aws-sdk/src/services/index.js +1 -0
  21. package/packages/datadog-plugin-cucumber/src/index.js +31 -31
  22. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -8
  23. package/packages/datadog-plugin-cypress/src/support.js +6 -2
  24. package/packages/datadog-plugin-fetch/src/index.js +3 -3
  25. package/packages/datadog-plugin-http/src/client.js +5 -33
  26. package/packages/datadog-plugin-jest/src/index.js +37 -37
  27. package/packages/datadog-plugin-langchain/src/index.js +12 -80
  28. package/packages/datadog-plugin-langchain/src/tracing.js +89 -0
  29. package/packages/datadog-plugin-mocha/src/index.js +19 -35
  30. package/packages/datadog-plugin-playwright/src/index.js +3 -1
  31. package/packages/datadog-plugin-vitest/src/index.js +33 -35
  32. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  33. package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +2 -0
  34. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +3 -3
  35. package/packages/dd-trace/src/appsec/iast/analyzers/untrusted-deserialization-analyzer.js +16 -0
  36. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +41 -24
  37. package/packages/dd-trace/src/appsec/iast/iast-context.js +12 -0
  38. package/packages/dd-trace/src/appsec/iast/path-line.js +19 -23
  39. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +9 -8
  40. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -0
  41. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  42. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +75 -24
  43. package/packages/dd-trace/src/appsec/rasp/utils.js +10 -5
  44. package/packages/dd-trace/src/appsec/stack_trace.js +38 -28
  45. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +37 -0
  46. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +65 -28
  47. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +57 -17
  48. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -4
  49. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +18 -3
  50. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +5 -3
  51. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +20 -3
  52. package/packages/dd-trace/src/config.js +43 -3
  53. package/packages/dd-trace/src/crashtracking/crashtracker.js +9 -0
  54. package/packages/dd-trace/src/crashtracking/noop.js +3 -0
  55. package/packages/dd-trace/src/datastreams/fnv.js +1 -1
  56. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -2
  57. package/packages/dd-trace/src/debugger/devtools_client/config.js +1 -0
  58. package/packages/dd-trace/src/debugger/devtools_client/defaults.js +1 -0
  59. package/packages/dd-trace/src/debugger/devtools_client/index.js +30 -13
  60. package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -8
  61. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +35 -1
  62. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +112 -0
  63. package/packages/dd-trace/src/debugger/devtools_client/status.js +12 -10
  64. package/packages/dd-trace/src/debugger/index.js +2 -13
  65. package/packages/dd-trace/src/llmobs/plugins/base.js +40 -11
  66. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +59 -0
  67. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +24 -0
  68. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +111 -0
  69. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +42 -0
  70. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +102 -0
  71. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/llm.js +32 -0
  72. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +131 -0
  73. package/packages/dd-trace/src/llmobs/plugins/openai.js +1 -1
  74. package/packages/dd-trace/src/llmobs/tagger.js +11 -3
  75. package/packages/dd-trace/src/llmobs/util.js +7 -1
  76. package/packages/dd-trace/src/llmobs/writers/spans/agentProxy.js +3 -3
  77. package/packages/dd-trace/src/opentelemetry/context_manager.js +43 -3
  78. package/packages/dd-trace/src/plugins/ci_plugin.js +58 -27
  79. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +0 -2
  80. package/packages/dd-trace/src/plugins/util/test.js +44 -12
  81. package/packages/dd-trace/src/priority_sampler.js +4 -1
  82. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +21 -0
  83. package/packages/dd-trace/src/profiling/profiler.js +11 -8
  84. package/packages/dd-trace/src/profiling/profilers/events.js +17 -1
  85. package/packages/dd-trace/src/proxy.js +6 -3
@@ -30,6 +30,7 @@ require,rfdc,MIT,Copyright 2019 David Mark Clements
30
30
  require,semver,ISC,Copyright Isaac Z. Schlueter and Contributors
31
31
  require,shell-quote,mit,Copyright (c) 2013 James Halliday
32
32
  require,source-map,BSD-3-Clause,Copyright (c) 2009-2011, Mozilla Foundation and contributors
33
+ require,ttl-set,MIT,Copyright (c) 2024 Thomas Watson
33
34
  dev,@apollo/server,MIT,Copyright (c) 2016-2020 Apollo Graph, Inc. (Formerly Meteor Development Group, Inc.)
34
35
  dev,@types/node,MIT,Copyright Authors
35
36
  dev,@eslint/eslintrc,MIT,Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
package/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # `dd-trace`: Node.js APM Tracer Library
2
2
 
3
3
  [![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=blue&label=dd-trace%40v5&logo=npm)](https://www.npmjs.com/package/dd-trace)
4
- [![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=blue&label=dd-trace%40v4&logo=npm)](https://www.npmjs.com/package/dd-trace/v/latest-node16)
5
4
  [![codecov](https://codecov.io/gh/DataDog/dd-trace-js/branch/master/graph/badge.svg)](https://codecov.io/gh/DataDog/dd-trace-js)
6
5
 
7
6
  <img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
@@ -23,16 +22,18 @@ Most of the documentation for `dd-trace` is available on these webpages:
23
22
 
24
23
  ## Version Release Lines and Maintenance
25
24
 
26
- | Release Line | Latest Version | Node.js | Status |Initial Release | End of Life |
27
- | :---: | :---: | :---: | :---: | :---: | :---: |
28
- | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
29
- | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | **End of Life** | 2022-01-28 | 2023-08-15 |
30
- | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | **End of Life** | 2022-08-15 | 2024-05-15 |
31
- | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=white&label=%20&style=flat-square) | `>= v16` | **Maintenance** | 2023-05-12 | 2025-01-11 |
32
- | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v18` | **Current** | 2024-01-11 | Unknown |
25
+ | Release Line | Latest Version | Node.js | [SSI](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/single-step-apm/?tab=linuxhostorvm) | [K8s Injection](https://docs.datadoghq.com/tracing/trace_collection/library_injection_local/?tab=kubernetes) |Status |Initial Release | End of Life |
26
+ | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
27
+ | [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) | ![npm v1](https://img.shields.io/npm/v/dd-trace/legacy-v1?color=white&label=%20&style=flat-square) | `>= v12` | NO | NO | **EOL** | 2021-07-13 | 2022-02-25 |
28
+ | [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) | ![npm v2](https://img.shields.io/npm/v/dd-trace/latest-node12?color=white&label=%20&style=flat-square) | `>= v12` | NO | NO | **EOL** | 2022-01-28 | 2023-08-15 |
29
+ | [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) | ![npm v3](https://img.shields.io/npm/v/dd-trace/latest-node14?color=white&label=%20&style=flat-square) | `>= v14` | NO | YES | **EOL** | 2022-08-15 | 2024-05-15 |
30
+ | [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) | ![npm v4](https://img.shields.io/npm/v/dd-trace/latest-node16?color=white&label=%20&style=flat-square) | `>= v16` | YES | YES | **EOL** | 2023-05-12 | 2025-01-11 |
31
+ | [`v5`](https://github.com/DataDog/dd-trace-js/tree/v5.x) | ![npm v5](https://img.shields.io/npm/v/dd-trace/latest?color=white&label=%20&style=flat-square) | `>= v18` | YES | YES | **Current** | 2024-01-11 | Unknown |
33
32
 
34
- We currently maintain two release lines, namely `v5`, and `v4`.
35
- Features and bug fixes that are merged are released to the `v5` line and, if appropriate, also `v4`.
33
+ * EOL = End-of-life
34
+ * SSI = Single-Step Install
35
+
36
+ We currently maintain one release line, namely `v5`.
36
37
 
37
38
  For any new projects it is recommended to use the `v5` release line:
38
39
 
@@ -41,20 +42,22 @@ $ npm install dd-trace
41
42
  $ yarn add dd-trace
42
43
  ```
43
44
 
44
- However, existing projects that already use the `v4` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
45
+ Existing projects that need to use EOL versions of Node.js may continue to use these older release lines.
45
46
  This is done by specifying the version when installing the package.
46
47
 
47
48
  ```sh
48
- $ npm install dd-trace@4
49
- $ yarn add dd-trace@4
49
+ $ npm install dd-trace@4 # or whatever version you need
50
+ $ yarn add dd-trace@4 # or whatever version you need
50
51
  ```
51
52
 
53
+ Note, however, that the end-of-life release lines are no longer maintained and will not receive updates.
54
+
52
55
  Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
53
56
  Such releases are kept to a minimum to reduce the pain of upgrading the library.
54
57
 
55
58
  When a new release line is introduced the previous release line then enters maintenance mode where it will receive updates for the next year.
56
59
  Once that year is up the release line enters End of Life and will not receive new updates.
57
- The library also follows the Node.js LTS lifecycle wherein new release lines drop compatibility with Node.js versions that reach end of life (with the maintenance release line still receiving updates for a year).
60
+ The library also follows the Node.js LTS lifecycle wherein new release lines drop compatibility with Node.js versions that reach end-of-life (with the maintenance release line still receiving updates for a year).
58
61
 
59
62
  For more information about library versioning and compatibility, see the [NodeJS Compatibility Requirements](https://docs.datadoghq.com/tracing/trace_collection/compatibility/nodejs/#releases) page.
60
63
 
package/index.d.ts CHANGED
@@ -2233,7 +2233,17 @@ declare namespace tracer {
2233
2233
  /**
2234
2234
  * Specifies the verbosity of the sent telemetry. Default 'INFORMATION'
2235
2235
  */
2236
- telemetryVerbosity?: string
2236
+ telemetryVerbosity?: string,
2237
+
2238
+ /**
2239
+ * Configuration for stack trace reporting
2240
+ */
2241
+ stackTrace?: {
2242
+ /** Whether to enable stack trace reporting.
2243
+ * @default true
2244
+ */
2245
+ enabled?: boolean,
2246
+ }
2237
2247
  }
2238
2248
 
2239
2249
  export namespace llmobs {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.31.0",
3
+ "version": "5.33.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -33,7 +33,7 @@
33
33
  "test:lambda:ci": "nyc --no-clean --include \"packages/dd-trace/src/lambda/**/*.js\" -- npm run test:lambda",
34
34
  "test:llmobs:sdk": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" --exclude \"packages/dd-trace/test/llmobs/plugins/**/*.spec.js\" \"packages/dd-trace/test/llmobs/**/*.spec.js\" ",
35
35
  "test:llmobs:sdk:ci": "nyc --no-clean --include \"packages/dd-trace/src/llmobs/**/*.js\" -- npm run test:llmobs:sdk",
36
- "test:llmobs:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/llmobs/plugins/**/*.spec.js\"",
36
+ "test:llmobs:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/llmobs/plugins/@($(echo $PLUGINS))/*.spec.js\"",
37
37
  "test:llmobs:plugins:ci": "yarn services && nyc --no-clean --include \"packages/dd-trace/src/llmobs/**/*.js\" -- npm run test:llmobs:plugins",
38
38
  "test:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\"",
39
39
  "test:plugins:ci": "yarn services && nyc --no-clean --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS)).js\" --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS))/**/*.js\" --include \"packages/datadog-plugin-@($(echo $PLUGINS))/src/**/*.js\" -- npm run test:plugins",
@@ -81,12 +81,12 @@
81
81
  "node": ">=18"
82
82
  },
83
83
  "dependencies": {
84
- "@datadog/libdatadog": "^0.3.0",
84
+ "@datadog/libdatadog": "^0.4.0",
85
85
  "@datadog/native-appsec": "8.4.0",
86
86
  "@datadog/native-iast-rewriter": "2.6.1",
87
87
  "@datadog/native-iast-taint-tracking": "3.2.0",
88
88
  "@datadog/native-metrics": "^3.1.0",
89
- "@datadog/pprof": "5.4.1",
89
+ "@datadog/pprof": "5.5.0",
90
90
  "@datadog/sketches-js": "^2.1.0",
91
91
  "@isaacs/ttlcache": "^1.4.1",
92
92
  "@opentelemetry/api": ">=1.0.0 <1.9.0",
@@ -111,7 +111,8 @@
111
111
  "semver": "^7.5.4",
112
112
  "shell-quote": "^1.8.1",
113
113
  "source-map": "^0.7.4",
114
- "tlhunter-sorted-set": "^0.1.0"
114
+ "tlhunter-sorted-set": "^0.1.0",
115
+ "ttl-set": "^1.0.0"
115
116
  },
116
117
  "devDependencies": {
117
118
  "@apollo/server": "^4.11.0",
@@ -155,6 +155,8 @@ function getMessage (request, error, result) {
155
155
  }
156
156
 
157
157
  function getChannelSuffix (name) {
158
+ // some resource identifiers have spaces between ex: bedrock runtime
159
+ name = name.replaceAll(' ', '')
158
160
  return [
159
161
  'cloudwatchlogs',
160
162
  'dynamodb',
@@ -167,7 +169,8 @@ function getChannelSuffix (name) {
167
169
  'sns',
168
170
  'sqs',
169
171
  'states',
170
- 'stepfunctions'
172
+ 'stepfunctions',
173
+ 'bedrockruntime'
171
174
  ].includes(name)
172
175
  ? name
173
176
  : 'default'
@@ -70,6 +70,7 @@ let earlyFlakeDetectionNumRetries = 0
70
70
  let earlyFlakeDetectionFaultyThreshold = 0
71
71
  let isEarlyFlakeDetectionFaulty = false
72
72
  let isFlakyTestRetriesEnabled = false
73
+ let isKnownTestsEnabled = false
73
74
  let numTestRetries = 0
74
75
  let knownTests = []
75
76
  let skippedSuites = []
@@ -238,8 +239,9 @@ function wrapRun (pl, isLatestVersion) {
238
239
  asyncResource.runInAsyncScope(() => {
239
240
  testStartCh.publish(testStartPayload)
240
241
  })
242
+ const promises = {}
241
243
  try {
242
- this.eventBroadcaster.on('envelope', shimmer.wrapFunction(null, () => (testCase) => {
244
+ this.eventBroadcaster.on('envelope', shimmer.wrapFunction(null, () => async (testCase) => {
243
245
  // Only supported from >=8.0.0
244
246
  if (testCase?.testCaseFinished) {
245
247
  const { testCaseFinished: { willBeRetried } } = testCase
@@ -253,17 +255,22 @@ function wrapRun (pl, isLatestVersion) {
253
255
  }
254
256
 
255
257
  const failedAttemptAsyncResource = numAttemptToAsyncResource.get(numAttempt)
256
- const isRetry = numAttempt++ > 0
258
+ const isFirstAttempt = numAttempt++ === 0
259
+
260
+ if (promises.hitBreakpointPromise) {
261
+ await promises.hitBreakpointPromise
262
+ }
263
+
257
264
  failedAttemptAsyncResource.runInAsyncScope(() => {
258
265
  // the current span will be finished and a new one will be created
259
- testRetryCh.publish({ isRetry, error })
266
+ testRetryCh.publish({ isFirstAttempt, error })
260
267
  })
261
268
 
262
269
  const newAsyncResource = new AsyncResource('bound-anonymous-fn')
263
270
  numAttemptToAsyncResource.set(numAttempt, newAsyncResource)
264
271
 
265
272
  newAsyncResource.runInAsyncScope(() => {
266
- testStartCh.publish(testStartPayload) // a new span will be created
273
+ testStartCh.publish({ ...testStartPayload, promises }) // a new span will be created
267
274
  })
268
275
  }
269
276
  }
@@ -273,7 +280,7 @@ function wrapRun (pl, isLatestVersion) {
273
280
  asyncResource.runInAsyncScope(() => {
274
281
  promise = run.apply(this, arguments)
275
282
  })
276
- promise.finally(() => {
283
+ promise.finally(async () => {
277
284
  const result = this.getWorstStepResult()
278
285
  const { status, skipReason } = isLatestVersion
279
286
  ? getStatusFromResultLatest(result)
@@ -286,7 +293,7 @@ function wrapRun (pl, isLatestVersion) {
286
293
  }
287
294
  let isNew = false
288
295
  let isEfdRetry = false
289
- if (isEarlyFlakeDetectionEnabled && status !== 'skip') {
296
+ if (isKnownTestsEnabled && status !== 'skip') {
290
297
  const numRetries = numRetriesByPickleId.get(this.pickle.id)
291
298
 
292
299
  isNew = numRetries !== undefined
@@ -296,6 +303,9 @@ function wrapRun (pl, isLatestVersion) {
296
303
 
297
304
  const error = getErrorFromCucumberResult(result)
298
305
 
306
+ if (promises.hitBreakpointPromise) {
307
+ await promises.hitBreakpointPromise
308
+ }
299
309
  attemptAsyncResource.runInAsyncScope(() => {
300
310
  testFinishCh.publish({ status, skipReason, error, isNew, isEfdRetry, isFlakyRetry: numAttempt > 0 })
301
311
  })
@@ -385,13 +395,15 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
385
395
  isSuitesSkippingEnabled = configurationResponse.libraryConfig?.isSuitesSkippingEnabled
386
396
  isFlakyTestRetriesEnabled = configurationResponse.libraryConfig?.isFlakyTestRetriesEnabled
387
397
  numTestRetries = configurationResponse.libraryConfig?.flakyTestRetriesCount
398
+ isKnownTestsEnabled = configurationResponse.libraryConfig?.isKnownTestsEnabled
388
399
 
389
- if (isEarlyFlakeDetectionEnabled) {
400
+ if (isKnownTestsEnabled) {
390
401
  const knownTestsResponse = await getChannelPromise(knownTestsCh)
391
402
  if (!knownTestsResponse.err) {
392
403
  knownTests = knownTestsResponse.knownTests
393
404
  } else {
394
405
  isEarlyFlakeDetectionEnabled = false
406
+ isKnownTestsEnabled = false
395
407
  }
396
408
  }
397
409
 
@@ -428,7 +440,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
428
440
 
429
441
  pickleByFile = isCoordinator ? getPickleByFileNew(this) : getPickleByFile(this)
430
442
 
431
- if (isEarlyFlakeDetectionEnabled) {
443
+ if (isKnownTestsEnabled) {
432
444
  const isFaulty = getIsFaultyEarlyFlakeDetection(
433
445
  Object.keys(pickleByFile),
434
446
  knownTests.cucumber || {},
@@ -436,6 +448,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
436
448
  )
437
449
  if (isFaulty) {
438
450
  isEarlyFlakeDetectionEnabled = false
451
+ isKnownTestsEnabled = false
439
452
  isEarlyFlakeDetectionFaulty = true
440
453
  }
441
454
  }
@@ -524,7 +537,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
524
537
 
525
538
  let isNew = false
526
539
 
527
- if (isEarlyFlakeDetectionEnabled) {
540
+ if (isKnownTestsEnabled) {
528
541
  isNew = isNewTest(testSuitePath, pickle.name)
529
542
  if (isNew) {
530
543
  numRetriesByPickleId.set(pickle.id, 0)
@@ -669,14 +682,14 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
669
682
  const { status } = getStatusFromResultLatest(worstTestStepResult)
670
683
  let isNew = false
671
684
 
672
- if (isEarlyFlakeDetectionEnabled) {
685
+ if (isKnownTestsEnabled) {
673
686
  isNew = isNewTest(pickle.uri, pickle.name)
674
687
  }
675
688
 
676
689
  const testFileAbsolutePath = pickle.uri
677
690
  const finished = pickleResultByFile[testFileAbsolutePath]
678
691
 
679
- if (isNew) {
692
+ if (isEarlyFlakeDetectionEnabled && isNew) {
680
693
  const testFullname = `${pickle.uri}:${pickle.name}`
681
694
  let testStatuses = newTestsByTestFullname.get(testFullname)
682
695
  if (!testStatuses) {
@@ -830,7 +843,8 @@ addHook({
830
843
  )
831
844
  // EFD in parallel mode only supported in >=11.0.0
832
845
  shimmer.wrap(adapterPackage.ChildProcessAdapter.prototype, 'startWorker', startWorker => function () {
833
- if (isEarlyFlakeDetectionEnabled) {
846
+ if (isKnownTestsEnabled) {
847
+ this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
834
848
  this.options.worldParameters._ddKnownTests = knownTests
835
849
  this.options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
836
850
  }
@@ -853,9 +867,12 @@ addHook({
853
867
  'initialize',
854
868
  initialize => async function () {
855
869
  await initialize.apply(this, arguments)
856
- isEarlyFlakeDetectionEnabled = !!this.options.worldParameters._ddKnownTests
857
- if (isEarlyFlakeDetectionEnabled) {
870
+ isKnownTestsEnabled = !!this.options.worldParameters._ddKnownTests
871
+ if (isKnownTestsEnabled) {
858
872
  knownTests = this.options.worldParameters._ddKnownTests
873
+ }
874
+ isEarlyFlakeDetectionEnabled = !!this.options.worldParameters._ddIsEarlyFlakeDetectionEnabled
875
+ if (isEarlyFlakeDetectionEnabled) {
859
876
  earlyFlakeDetectionNumRetries = this.options.worldParameters._ddEarlyFlakeDetectionNumRetries
860
877
  }
861
878
  }
@@ -88,6 +88,7 @@ module.exports = {
88
88
  mysql2: () => require('../mysql2'),
89
89
  net: () => require('../net'),
90
90
  next: () => require('../next'),
91
+ 'node-serialize': () => require('../node-serialize'),
91
92
  'node:child_process': () => require('../child_process'),
92
93
  'node:crypto': () => require('../crypto'),
93
94
  'node:dns': () => require('../dns'),
@@ -96,6 +97,7 @@ module.exports = {
96
97
  'node:https': () => require('../http'),
97
98
  'node:net': () => require('../net'),
98
99
  'node:url': () => require('../url'),
100
+ 'node:vm': () => require('../vm'),
99
101
  nyc: () => require('../nyc'),
100
102
  oracledb: () => require('../oracledb'),
101
103
  openai: () => require('../openai'),
@@ -122,6 +124,7 @@ module.exports = {
122
124
  undici: () => require('../undici'),
123
125
  url: () => require('../url'),
124
126
  vitest: { esmFirst: true, fn: () => require('../vitest') },
127
+ vm: () => require('../vm'),
125
128
  when: () => require('../when'),
126
129
  winston: () => require('../winston'),
127
130
  workerpool: () => require('../mocha')
@@ -12,7 +12,8 @@ const {
12
12
  getTestParametersString,
13
13
  addEfdStringToTestName,
14
14
  removeEfdStringFromTestName,
15
- getIsFaultyEarlyFlakeDetection
15
+ getIsFaultyEarlyFlakeDetection,
16
+ JEST_WORKER_LOGS_PAYLOAD_CODE
16
17
  } = require('../../dd-trace/src/plugins/util/test')
17
18
  const {
18
19
  getFormattedJestTestParameters,
@@ -30,12 +31,13 @@ const testSuiteFinishCh = channel('ci:jest:test-suite:finish')
30
31
 
31
32
  const workerReportTraceCh = channel('ci:jest:worker-report:trace')
32
33
  const workerReportCoverageCh = channel('ci:jest:worker-report:coverage')
34
+ const workerReportLogsCh = channel('ci:jest:worker-report:logs')
33
35
 
34
36
  const testSuiteCodeCoverageCh = channel('ci:jest:test-suite:code-coverage')
35
37
 
36
38
  const testStartCh = channel('ci:jest:test:start')
37
39
  const testSkippedCh = channel('ci:jest:test:skip')
38
- const testRunFinishCh = channel('ci:jest:test:finish')
40
+ const testFinishCh = channel('ci:jest:test:finish')
39
41
  const testErrCh = channel('ci:jest:test:err')
40
42
 
41
43
  const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
@@ -67,6 +69,7 @@ let earlyFlakeDetectionNumRetries = 0
67
69
  let earlyFlakeDetectionFaultyThreshold = 30
68
70
  let isEarlyFlakeDetectionFaulty = false
69
71
  let hasFilteredSkippableSuites = false
72
+ let isKnownTestsEnabled = false
70
73
 
71
74
  const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
72
75
 
@@ -75,6 +78,8 @@ const originalTestFns = new WeakMap()
75
78
  const retriedTestsToNumAttempts = new Map()
76
79
  const newTestsTestStatuses = new Map()
77
80
 
81
+ const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
82
+
78
83
  // based on https://github.com/facebook/jest/blob/main/packages/jest-circus/src/formatNodeAssertErrors.ts#L41
79
84
  function formatJestError (errors) {
80
85
  let error
@@ -134,17 +139,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
134
139
  this.isFlakyTestRetriesEnabled = this.testEnvironmentOptions._ddIsFlakyTestRetriesEnabled
135
140
  this.flakyTestRetriesCount = this.testEnvironmentOptions._ddFlakyTestRetriesCount
136
141
  this.isDiEnabled = this.testEnvironmentOptions._ddIsDiEnabled
142
+ this.isKnownTestsEnabled = this.testEnvironmentOptions._ddIsKnownTestsEnabled
137
143
 
138
- if (this.isEarlyFlakeDetectionEnabled) {
139
- const hasKnownTests = !!knownTests.jest
140
- earlyFlakeDetectionNumRetries = this.testEnvironmentOptions._ddEarlyFlakeDetectionNumRetries
144
+ if (this.isKnownTestsEnabled) {
141
145
  try {
146
+ const hasKnownTests = !!knownTests.jest
147
+ earlyFlakeDetectionNumRetries = this.testEnvironmentOptions._ddEarlyFlakeDetectionNumRetries
142
148
  this.knownTestsForThisSuite = hasKnownTests
143
149
  ? (knownTests.jest[this.testSuite] || [])
144
150
  : this.getKnownTestsForSuite(this.testEnvironmentOptions._ddKnownTests)
145
151
  } catch (e) {
146
152
  // If there has been an error parsing the tests, we'll disable Early Flake Deteciton
147
153
  this.isEarlyFlakeDetectionEnabled = false
154
+ this.isKnownTestsEnabled = false
148
155
  }
149
156
  }
150
157
 
@@ -224,7 +231,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
224
231
  asyncResources.set(event.test, asyncResource)
225
232
  const testName = getJestTestName(event.test)
226
233
 
227
- if (this.isEarlyFlakeDetectionEnabled) {
234
+ if (this.isKnownTestsEnabled) {
228
235
  const originalTestName = removeEfdStringFromTestName(testName)
229
236
  isNewTest = retriedTestsToNumAttempts.has(originalTestName)
230
237
  if (isNewTest) {
@@ -250,70 +257,96 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
250
257
  })
251
258
  }
252
259
  if (event.name === 'add_test') {
253
- if (this.isEarlyFlakeDetectionEnabled) {
260
+ if (this.isKnownTestsEnabled) {
254
261
  const testName = this.getTestNameFromAddTestEvent(event, state)
255
262
  const isNew = !this.knownTestsForThisSuite?.includes(testName)
256
263
  const isSkipped = event.mode === 'todo' || event.mode === 'skip'
257
264
  if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(testName)) {
258
265
  retriedTestsToNumAttempts.set(testName, 0)
259
- // Retrying snapshots has proven to be problematic, so we'll skip them for now
260
- // We'll still detect new tests, but we won't retry them.
261
- // TODO: do not bail out of EFD with the whole test suite
262
- if (this.getHasSnapshotTests()) {
263
- log.warn('Early flake detection is disabled for suites with snapshots')
264
- return
265
- }
266
- for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
267
- if (this.global.test) {
268
- this.global.test(addEfdStringToTestName(event.testName, retryIndex), event.fn, event.timeout)
269
- } else {
270
- log.error('Early flake detection could not retry test because global.test is undefined')
266
+ if (this.isEarlyFlakeDetectionEnabled) {
267
+ // Retrying snapshots has proven to be problematic, so we'll skip them for now
268
+ // We'll still detect new tests, but we won't retry them.
269
+ // TODO: do not bail out of EFD with the whole test suite
270
+ if (this.getHasSnapshotTests()) {
271
+ log.warn('Early flake detection is disabled for suites with snapshots')
272
+ return
273
+ }
274
+ for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
275
+ if (this.global.test) {
276
+ this.global.test(addEfdStringToTestName(event.testName, retryIndex), event.fn, event.timeout)
277
+ } else {
278
+ log.error('Early flake detection could not retry test because global.test is undefined')
279
+ }
271
280
  }
272
281
  }
273
282
  }
274
283
  }
275
284
  }
276
285
  if (event.name === 'test_done') {
277
- const probe = {}
286
+ let status = 'pass'
287
+ if (event.test.errors && event.test.errors.length) {
288
+ status = 'fail'
289
+ }
290
+ // restore in case it is retried
291
+ event.test.fn = originalTestFns.get(event.test)
292
+
293
+ // We'll store the test statuses of the retries
294
+ if (this.isKnownTestsEnabled) {
295
+ const testName = getJestTestName(event.test)
296
+ const originalTestName = removeEfdStringFromTestName(testName)
297
+ const isNewTest = retriedTestsToNumAttempts.has(originalTestName)
298
+ if (isNewTest) {
299
+ if (newTestsTestStatuses.has(originalTestName)) {
300
+ newTestsTestStatuses.get(originalTestName).push(status)
301
+ } else {
302
+ newTestsTestStatuses.set(originalTestName, [status])
303
+ }
304
+ }
305
+ }
306
+
307
+ const promises = {}
308
+ const numRetries = this.global[RETRY_TIMES]
309
+ const numTestExecutions = event.test?.invocations
310
+ const willBeRetried = numRetries > 0 && numTestExecutions - 1 < numRetries
311
+ const mightHitBreakpoint = this.isDiEnabled && numTestExecutions >= 2
312
+
278
313
  const asyncResource = asyncResources.get(event.test)
279
- asyncResource.runInAsyncScope(() => {
280
- let status = 'pass'
281
- if (event.test.errors && event.test.errors.length) {
282
- status = 'fail'
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])
314
+
315
+ if (status === 'fail') {
316
+ asyncResource.runInAsyncScope(() => {
288
317
  testErrCh.publish({
289
- error,
290
- willBeRetried,
291
- probe,
292
- isDiEnabled: this.isDiEnabled
318
+ error: formatJestError(event.test.errors[0]),
319
+ shouldSetProbe: this.isDiEnabled && willBeRetried && numTestExecutions === 1,
320
+ promises
293
321
  })
294
- }
295
- testRunFinishCh.publish({
322
+ })
323
+ }
324
+
325
+ // After finishing it might take a bit for the snapshot to be handled.
326
+ // This means that tests retried with DI are BREAKPOINT_HIT_GRACE_PERIOD_MS slower at least.
327
+ if (status === 'fail' && mightHitBreakpoint) {
328
+ await new Promise(resolve => {
329
+ setTimeout(() => {
330
+ resolve()
331
+ }, BREAKPOINT_HIT_GRACE_PERIOD_MS)
332
+ })
333
+ }
334
+
335
+ asyncResource.runInAsyncScope(() => {
336
+ testFinishCh.publish({
296
337
  status,
297
- testStartLine: getTestLineStart(event.test.asyncError, this.testSuite)
338
+ testStartLine: getTestLineStart(event.test.asyncError, this.testSuite),
339
+ promises,
340
+ shouldRemoveProbe: this.isDiEnabled && !willBeRetried
298
341
  })
299
- // restore in case it is retried
300
- event.test.fn = originalTestFns.get(event.test)
301
- // We'll store the test statuses of the retries
302
- if (this.isEarlyFlakeDetectionEnabled) {
303
- const testName = getJestTestName(event.test)
304
- const originalTestName = removeEfdStringFromTestName(testName)
305
- const isNewTest = retriedTestsToNumAttempts.has(originalTestName)
306
- if (isNewTest) {
307
- if (newTestsTestStatuses.has(originalTestName)) {
308
- newTestsTestStatuses.get(originalTestName).push(status)
309
- } else {
310
- newTestsTestStatuses.set(originalTestName, [status])
311
- }
312
- }
313
- }
314
342
  })
315
- if (probe.setProbePromise) {
316
- await probe.setProbePromise
343
+
344
+ if (promises.isProbeReady) {
345
+ await promises.isProbeReady
346
+ }
347
+
348
+ if (promises.isProbeRemoved) {
349
+ await promises.isProbeRemoved
317
350
  }
318
351
  }
319
352
  if (event.name === 'test_skip' || event.name === 'test_todo') {
@@ -455,12 +488,13 @@ function cliWrapper (cli, jestVersion) {
455
488
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
456
489
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
457
490
  earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
491
+ isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
458
492
  }
459
493
  } catch (err) {
460
494
  log.error('Jest library configuration error', err)
461
495
  }
462
496
 
463
- if (isEarlyFlakeDetectionEnabled) {
497
+ if (isKnownTestsEnabled) {
464
498
  const knownTestsPromise = new Promise((resolve) => {
465
499
  onDone = resolve
466
500
  })
@@ -476,6 +510,7 @@ function cliWrapper (cli, jestVersion) {
476
510
  } else {
477
511
  // We disable EFD if there has been an error in the known tests request
478
512
  isEarlyFlakeDetectionEnabled = false
513
+ isKnownTestsEnabled = false
479
514
  }
480
515
  } catch (err) {
481
516
  log.error('Jest known tests error', err)
@@ -793,6 +828,7 @@ addHook({
793
828
  _ddIsFlakyTestRetriesEnabled,
794
829
  _ddFlakyTestRetriesCount,
795
830
  _ddIsDiEnabled,
831
+ _ddIsKnownTestsEnabled,
796
832
  ...restOfTestEnvironmentOptions
797
833
  } = testEnvironmentOptions
798
834
 
@@ -820,17 +856,19 @@ addHook({
820
856
  const testPaths = await getTestPaths.apply(this, arguments)
821
857
  const [{ rootDir, shard }] = arguments
822
858
 
823
- if (isEarlyFlakeDetectionEnabled) {
859
+ if (isKnownTestsEnabled) {
824
860
  const projectSuites = testPaths.tests.map(test => getTestSuitePath(test.path, test.context.config.rootDir))
825
861
  const isFaulty =
826
862
  getIsFaultyEarlyFlakeDetection(projectSuites, knownTests.jest || {}, earlyFlakeDetectionFaultyThreshold)
827
863
  if (isFaulty) {
828
864
  log.error('Early flake detection is disabled because the number of new suites is too high.')
829
865
  isEarlyFlakeDetectionEnabled = false
866
+ isKnownTestsEnabled = false
830
867
  const testEnvironmentOptions = testPaths.tests[0]?.context?.config?.testEnvironmentOptions
831
868
  // Project config is shared among all tests, so we can modify it here
832
869
  if (testEnvironmentOptions) {
833
870
  testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled = false
871
+ testEnvironmentOptions._ddIsKnownTestsEnabled = false
834
872
  }
835
873
  isEarlyFlakeDetectionFaulty = true
836
874
  }
@@ -901,6 +939,11 @@ addHook({
901
939
  return runtimePackage
902
940
  })
903
941
 
942
+ /*
943
+ * This hook does two things:
944
+ * - Pass known tests to the workers.
945
+ * - Receive trace, coverage and logs payloads from the workers.
946
+ */
904
947
  addHook({
905
948
  name: 'jest-worker',
906
949
  versions: ['>=24.9.0'],
@@ -908,7 +951,7 @@ addHook({
908
951
  }, (childProcessWorker) => {
909
952
  const ChildProcessWorker = childProcessWorker.default
910
953
  shimmer.wrap(ChildProcessWorker.prototype, 'send', send => function (request) {
911
- if (!isEarlyFlakeDetectionEnabled) {
954
+ if (!isKnownTestsEnabled) {
912
955
  return send.apply(this, arguments)
913
956
  }
914
957
  const [type] = request
@@ -953,6 +996,12 @@ addHook({
953
996
  })
954
997
  return
955
998
  }
999
+ if (code === JEST_WORKER_LOGS_PAYLOAD_CODE) { // datadog logs payload
1000
+ sessionAsyncResource.runInAsyncScope(() => {
1001
+ workerReportLogsCh.publish(data)
1002
+ })
1003
+ return
1004
+ }
956
1005
  return _onMessage.apply(this, arguments)
957
1006
  })
958
1007
  return childProcessWorker