dd-trace 6.4.0 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/README.electron.md +7 -0
  2. package/README.md +17 -0
  3. package/ci/diagnose.js +2100 -0
  4. package/ci/init.js +23 -2
  5. package/ci/runbook.md +198 -0
  6. package/ci/test-optimization-validation/approval-artifacts.js +143 -0
  7. package/ci/test-optimization-validation/approval.js +299 -0
  8. package/ci/test-optimization-validation/artifact-id.js +20 -0
  9. package/ci/test-optimization-validation/bounded-json.js +121 -0
  10. package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
  11. package/ci/test-optimization-validation/ci-discovery.js +181 -0
  12. package/ci/test-optimization-validation/ci-remediation.js +210 -0
  13. package/ci/test-optimization-validation/cli.js +903 -0
  14. package/ci/test-optimization-validation/command-blocker.js +81 -0
  15. package/ci/test-optimization-validation/command-output-policy.js +206 -0
  16. package/ci/test-optimization-validation/command-runner.js +707 -0
  17. package/ci/test-optimization-validation/command-suitability.js +471 -0
  18. package/ci/test-optimization-validation/executable-approval.js +48 -0
  19. package/ci/test-optimization-validation/executable.js +480 -0
  20. package/ci/test-optimization-validation/generated-file-policy.js +63 -0
  21. package/ci/test-optimization-validation/generated-files.js +351 -0
  22. package/ci/test-optimization-validation/generated-verifier.js +196 -0
  23. package/ci/test-optimization-validation/init-probe-preload.js +163 -0
  24. package/ci/test-optimization-validation/init-probe.js +246 -0
  25. package/ci/test-optimization-validation/late-initialization.js +63 -0
  26. package/ci/test-optimization-validation/local-command.js +163 -0
  27. package/ci/test-optimization-validation/manifest-loader.js +133 -0
  28. package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
  29. package/ci/test-optimization-validation/manifest-schema.js +862 -0
  30. package/ci/test-optimization-validation/offline-fixtures.js +327 -0
  31. package/ci/test-optimization-validation/offline-output.js +406 -0
  32. package/ci/test-optimization-validation/payload-normalizer.js +72 -0
  33. package/ci/test-optimization-validation/plan-writer.js +1120 -0
  34. package/ci/test-optimization-validation/preflight-runner.js +114 -0
  35. package/ci/test-optimization-validation/redaction.js +331 -0
  36. package/ci/test-optimization-validation/report-writer.js +1508 -0
  37. package/ci/test-optimization-validation/safe-files.js +203 -0
  38. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
  39. package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
  40. package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
  41. package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
  42. package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
  43. package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
  44. package/ci/test-optimization-validation/setup-runner.js +97 -0
  45. package/ci/test-optimization-validation/static-diagnosis.js +159 -0
  46. package/ci/test-optimization-validation/test-output.js +129 -0
  47. package/ci/test-optimization-validation-manifest.schema.json +1 -0
  48. package/ci/validate-test-optimization.js +3 -0
  49. package/ext/exporters.js +1 -0
  50. package/index.d.ts +121 -8
  51. package/init.js +18 -0
  52. package/initialize.mjs +1 -1
  53. package/loader-hook.mjs +28 -18
  54. package/openfeature.d.ts +1 -0
  55. package/openfeature.js +4 -0
  56. package/package.json +11 -8
  57. package/packages/datadog-instrumentations/src/ai.js +8 -2
  58. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
  60. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
  61. package/packages/datadog-instrumentations/src/cucumber.js +31 -19
  62. package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
  63. package/packages/datadog-instrumentations/src/fastify.js +7 -11
  64. package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
  65. package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
  66. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
  68. package/packages/datadog-instrumentations/src/jest.js +44 -17
  69. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  70. package/packages/datadog-instrumentations/src/mocha/main.js +79 -32
  71. package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
  72. package/packages/datadog-instrumentations/src/mongodb.js +3 -3
  73. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  74. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  75. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  76. package/packages/datadog-instrumentations/src/next.js +24 -0
  77. package/packages/datadog-instrumentations/src/nyc.js +0 -2
  78. package/packages/datadog-instrumentations/src/oracledb.js +2 -2
  79. package/packages/datadog-instrumentations/src/pg.js +2 -2
  80. package/packages/datadog-instrumentations/src/playwright.js +162 -46
  81. package/packages/datadog-instrumentations/src/process.js +3 -0
  82. package/packages/datadog-instrumentations/src/router.js +18 -8
  83. package/packages/datadog-instrumentations/src/selenium.js +52 -26
  84. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +3 -3
  85. package/packages/datadog-instrumentations/src/vitest-main.js +14 -28
  86. package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
  87. package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
  88. package/packages/datadog-instrumentations/src/ws.js +2 -1
  89. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
  90. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
  91. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
  92. package/packages/datadog-plugin-cucumber/src/index.js +3 -2
  93. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -57
  94. package/packages/datadog-plugin-cypress/src/index.js +6 -1
  95. package/packages/datadog-plugin-cypress/src/support.js +92 -26
  96. package/packages/datadog-plugin-electron/src/ipc.js +1 -1
  97. package/packages/datadog-plugin-graphql/src/execute.js +87 -45
  98. package/packages/datadog-plugin-graphql/src/validate.js +2 -2
  99. package/packages/datadog-plugin-http2/src/client.js +1 -1
  100. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
  101. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
  102. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  103. package/packages/datadog-plugin-mocha/src/index.js +2 -1
  104. package/packages/datadog-plugin-playwright/src/index.js +181 -65
  105. package/packages/datadog-plugin-router/src/index.js +11 -2
  106. package/packages/datadog-plugin-selenium/src/index.js +5 -36
  107. package/packages/datadog-plugin-vitest/src/index.js +5 -5
  108. package/packages/datadog-plugin-ws/src/close.js +2 -1
  109. package/packages/datadog-plugin-ws/src/producer.js +2 -1
  110. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  111. package/packages/datadog-plugin-ws/src/server.js +2 -1
  112. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  113. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  114. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
  115. package/packages/dd-trace/src/azure_metadata.js +2 -1
  116. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
  117. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  118. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
  119. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
  122. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +41 -3
  124. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -2
  125. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -1
  126. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
  127. package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
  128. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  129. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  130. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
  131. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +168 -8
  132. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
  133. package/packages/dd-trace/src/config/generated-config-types.d.ts +16 -0
  134. package/packages/dd-trace/src/config/helper.js +1 -0
  135. package/packages/dd-trace/src/config/index.js +10 -0
  136. package/packages/dd-trace/src/config/supported-configurations.json +66 -0
  137. package/packages/dd-trace/src/exporter.js +2 -0
  138. package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
  139. package/packages/dd-trace/src/exporters/common/request.js +59 -30
  140. package/packages/dd-trace/src/exporters/common/url.js +15 -1
  141. package/packages/dd-trace/src/feature-registry.js +10 -3
  142. package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
  143. package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
  144. package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
  145. package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
  146. package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
  147. package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
  148. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  149. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
  150. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
  151. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  152. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
  153. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
  154. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
  155. package/packages/dd-trace/src/llmobs/sdk.js +17 -0
  156. package/packages/dd-trace/src/llmobs/tagger.js +68 -0
  157. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  158. package/packages/dd-trace/src/llmobs/util.js +32 -0
  159. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
  160. package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
  161. package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
  162. package/packages/dd-trace/src/openfeature/index.js +0 -2
  163. package/packages/dd-trace/src/openfeature/noop.js +1 -28
  164. package/packages/dd-trace/src/openfeature/register.js +16 -27
  165. package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
  166. package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
  167. package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
  168. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  169. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
  170. package/packages/dd-trace/src/plugins/util/git.js +3 -2
  171. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
  172. package/packages/dd-trace/src/plugins/util/test.js +56 -4
  173. package/packages/dd-trace/src/profiling/profilers/space.js +1 -1
  174. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  175. package/packages/dd-trace/src/proxy.js +84 -7
  176. package/packages/dd-trace/src/span_processor.js +5 -0
  177. package/packages/dd-trace/src/standalone/index.js +0 -14
  178. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  179. package/packages/dd-trace/src/util.js +26 -0
  180. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  181. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  182. package/vendor/dist/protobufjs/index.js +1 -1
  183. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  184. package/vendor/dist/shell-quote/index.js +1 -1
  185. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +0 -17
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @template {unknown[]} T
5
+ * @param {(...args: T) => void} onDone
6
+ * @returns {(...args: T) => void}
7
+ */
8
+ function getCompletion (onDone) {
9
+ let hasCompleted = false
10
+ return (...args) => {
11
+ if (hasCompleted) return
12
+
13
+ hasCompleted = true
14
+ onDone(...args)
15
+ }
16
+ }
17
+
18
+ /**
19
+ * @template {unknown[]} T
20
+ * @param {import('node:diagnostics_channel').Channel} channel
21
+ * @param {Record<string, unknown>} payload
22
+ * @param {(...args: T) => void} onDone
23
+ * @returns {void}
24
+ */
25
+ function publishWithCompletion (channel, payload, onDone) {
26
+ const complete = getCompletion(onDone)
27
+ channel.publish({ ...payload, onDone: complete })
28
+ if (!channel.hasSubscribers) complete()
29
+ }
30
+
31
+ /**
32
+ * @template {unknown[]} T
33
+ * @param {import('node:diagnostics_channel').Channel} channel
34
+ * @param {Record<string, unknown> & { onDone?: (...args: T) => void }} payload
35
+ * @param {(...args: T) => void} onDone
36
+ * @returns {void}
37
+ */
38
+ function runStoresWithCompletion (channel, payload, onDone) {
39
+ const complete = getCompletion(onDone)
40
+ payload.onDone = complete
41
+ channel.runStores(payload, () => {})
42
+ if (!channel.hasSubscribers) complete()
43
+ }
44
+
45
+ /**
46
+ * @template T
47
+ * @param {import('node:diagnostics_channel').Channel} channel
48
+ * @param {Record<string, unknown>} [payload]
49
+ * @returns {Promise<T>}
50
+ */
51
+ function getChannelPromise (channel, payload = {}) {
52
+ return new Promise(resolve => {
53
+ publishWithCompletion(channel, payload, resolve)
54
+ })
55
+ }
56
+
57
+ /**
58
+ * @template T
59
+ * @param {import('node:diagnostics_channel').Channel} channel
60
+ * @param {Record<string, unknown>} [payload]
61
+ * @returns {Promise<T>}
62
+ */
63
+ function getRunStoresPromise (channel, payload = {}) {
64
+ return new Promise(resolve => {
65
+ runStoresWithCompletion(channel, { ...payload }, resolve)
66
+ })
67
+ }
68
+
69
+ module.exports = {
70
+ getChannelPromise,
71
+ getRunStoresPromise,
72
+ publishWithCompletion,
73
+ runStoresWithCompletion,
74
+ }
@@ -49,10 +49,19 @@ function Hook (modules, hookOptions, onrequire) {
49
49
  this._patched = Object.create(null)
50
50
  const patched = new WeakMap()
51
51
 
52
+ /**
53
+ * @param {object|Function|undefined} moduleExports
54
+ * @param {string} moduleName
55
+ * @param {string|undefined} moduleBaseDir
56
+ * @param {string|undefined} moduleVersion
57
+ * @param {boolean|undefined} isIitm
58
+ */
52
59
  const safeHook = (moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm) => {
53
60
  const parts = [moduleBaseDir, moduleName].filter(Boolean)
54
61
  const filename = path.join(...parts)
55
62
 
63
+ const defaultExport = isIitm && moduleExports.default
64
+ let defaultExportAliases
56
65
  let defaultWrapResult
57
66
 
58
67
  const wrappedOnrequire = (moduleExports, ...args) => {
@@ -77,17 +86,29 @@ function Hook (modules, hookOptions, onrequire) {
77
86
  }
78
87
 
79
88
  if (
80
- isIitm &&
81
- moduleExports.default &&
82
- (typeof moduleExports.default === 'object' ||
83
- typeof moduleExports.default === 'function')
89
+ defaultExport &&
90
+ (typeof defaultExport === 'object' ||
91
+ typeof defaultExport === 'function')
84
92
  ) {
85
- defaultWrapResult = wrappedOnrequire(moduleExports.default, moduleName, moduleBaseDir, moduleVersion, isIitm)
93
+ defaultWrapResult = wrappedOnrequire(defaultExport, moduleName, moduleBaseDir, moduleVersion, isIitm)
94
+ if (defaultWrapResult && defaultWrapResult !== defaultExport) {
95
+ defaultExportAliases = []
96
+ for (const exportName of Object.keys(moduleExports)) {
97
+ if (exportName !== 'default' && moduleExports[exportName] === defaultExport) {
98
+ defaultExportAliases.push(exportName)
99
+ }
100
+ }
101
+ }
86
102
  }
87
103
 
88
104
  const newExports = wrappedOnrequire(moduleExports, moduleName, moduleBaseDir, moduleVersion, isIitm)
89
105
 
90
- if (defaultWrapResult) newExports.default = defaultWrapResult
106
+ if (defaultWrapResult && defaultExportAliases) {
107
+ newExports.default = defaultWrapResult
108
+ for (const exportName of defaultExportAliases) {
109
+ moduleExports[exportName] = defaultWrapResult
110
+ }
111
+ }
91
112
 
92
113
  this._patched[filename] = true
93
114
 
@@ -4,19 +4,19 @@ const path = require('node:path')
4
4
 
5
5
  // Build-time half of the optional-peer mechanism shared by the webpack and esbuild plugins.
6
6
  //
7
- // Runtime files load an optional peer through `requireOptionalPeer('name')` (see
8
- // `require-optional-peer.js`), which bundlers cannot follow, so a build that does not opt into
9
- // the feature never pulls in the peer's (possibly optional) dependency chain (#8635). When the
10
- // peer is installed at build time the user has opted in, so the plugins rewrite the call into a
11
- // literal `require('name')` and let the bundler inline the peer, which keeps it working after
12
- // the bundle is relocated to a tree without the peer on disk (#8980). Peers that are absent at
13
- // build time stay opaque, so the rewrite is a no-op and the #8635 guarantee holds.
7
+ // Runtime files load an optional peer through a local `requireOptionalPeer('name')` wrapper.
8
+ // File tracers recognize its bound-require shape, while bundlers cannot follow the dynamic
9
+ // argument, so a build that does not opt into the feature never pulls in the peer's dependency
10
+ // chain (#8635). When the peer is installed at build time the user has opted in, so the plugins
11
+ // rewrite the call into a literal `require('name')` and let the bundler inline the peer, which
12
+ // keeps it working after the bundle is relocated without the peer on disk (#8980). Peers that
13
+ // are absent at build time stay opaque, so the rewrite is a no-op and the #8635 guarantee holds.
14
14
 
15
15
  // Files that load an optional peer this way, as suffixes of the resolved module path. The same
16
16
  // suffix matches the repo layout and `node_modules/dd-trace`. Add a file here to extend the
17
17
  // mechanism to a new optional peer; no plugin change is needed.
18
18
  const OPTIONAL_PEER_FILES = [
19
- 'packages/dd-trace/src/openfeature/flagging_provider.js',
19
+ 'packages/dd-trace/src/openfeature/require-provider.js',
20
20
  ]
21
21
 
22
22
  // Captures the peer name from `requireOptionalPeer('name')` / `requireOptionalPeer("name")`.
@@ -1,8 +1,34 @@
1
1
  'use strict'
2
2
 
3
- // Playwright 1.60 bundles several former hook targets into local classes/functions.
4
- // Keep these rewrites limited to private bundled internals that addHook cannot wrap.
3
+ // Playwright keeps several hook targets in private local classes/functions.
4
+ // Keep these rewrites limited to bundled internals that addHook cannot wrap.
5
5
  module.exports = [
6
+ {
7
+ module: {
8
+ name: 'playwright',
9
+ versionRange: '>=1.38.0 <1.51.0',
10
+ filePath: 'lib/index.js',
11
+ },
12
+ functionQuery: {
13
+ className: 'ArtifactsRecorder',
14
+ methodName: '_createScreenshotAttachmentPath',
15
+ kind: 'Sync',
16
+ },
17
+ channelName: 'ArtifactsRecorder_createScreenshotAttachmentPath',
18
+ },
19
+ {
20
+ module: {
21
+ name: 'playwright',
22
+ versionRange: '>=1.51.0',
23
+ filePath: 'lib/index.js',
24
+ },
25
+ functionQuery: {
26
+ className: 'SnapshotRecorder',
27
+ methodName: '_createAttachmentPath',
28
+ kind: 'Sync',
29
+ },
30
+ channelName: 'SnapshotRecorder_createAttachmentPath',
31
+ },
6
32
  {
7
33
  module: {
8
34
  name: 'playwright',
@@ -46,6 +46,10 @@ const {
46
46
  addCoverageBackfillUntestedFiles,
47
47
  getCoverageBackfillFiles,
48
48
  } = require('./jest/coverage-backfill')
49
+ const {
50
+ getChannelPromise,
51
+ publishWithCompletion,
52
+ } = require('./helpers/channel')
49
53
  const { addHook, channel } = require('./helpers/instrument')
50
54
 
51
55
  const testSessionStartCh = channel('ci:jest:session:start')
@@ -1858,6 +1862,26 @@ function resetSuiteSkippingRunState () {
1858
1862
  coverageBackfillFiles = undefined
1859
1863
  }
1860
1864
 
1865
+ function resetLibraryConfiguration () {
1866
+ knownTests = {}
1867
+ isCodeCoverageEnabled = false
1868
+ isCoverageReportUploadEnabled = false
1869
+ isItrEnabled = false
1870
+ isSuitesSkippingEnabled = false
1871
+ isEarlyFlakeDetectionEnabled = false
1872
+ earlyFlakeDetectionNumRetries = 0
1873
+ earlyFlakeDetectionSlowTestRetries = {}
1874
+ earlyFlakeDetectionFaultyThreshold = 30
1875
+ isEarlyFlakeDetectionFaulty = false
1876
+ isKnownTestsEnabled = false
1877
+ isTestManagementTestsEnabled = false
1878
+ testManagementTests = {}
1879
+ testManagementAttemptToFixRetries = 0
1880
+ isImpactedTestsEnabled = false
1881
+ modifiedFiles = {}
1882
+ repositoryRoot = undefined
1883
+ }
1884
+
1861
1885
  function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
1862
1886
  if (!isItrEnabled || !isSuitesSkippingEnabled) return originalTests
1863
1887
 
@@ -1951,6 +1975,9 @@ function shouldFinishBailTestSession (globalConfig, results) {
1951
1975
  return !!globalConfig?.bail && getNumBailFailures(results) >= globalConfig.bail
1952
1976
  }
1953
1977
 
1978
+ /**
1979
+ * @param {Record<string, unknown> & { onDone?: () => void }} payload
1980
+ */
1954
1981
  async function waitForTestSessionFinish (payload) {
1955
1982
  if (!testSessionFinishCh.hasSubscribers || hasFinishedTestSession) return
1956
1983
 
@@ -1958,8 +1985,9 @@ async function waitForTestSessionFinish (payload) {
1958
1985
 
1959
1986
  let timeoutId
1960
1987
 
1988
+ let onDone
1961
1989
  const flushPromise = new Promise((resolve) => {
1962
- payload.onDone = () => {
1990
+ onDone = () => {
1963
1991
  clearTimeout(timeoutId)
1964
1992
  resolve()
1965
1993
  }
@@ -1972,7 +2000,7 @@ async function waitForTestSessionFinish (payload) {
1972
2000
  timeoutId.unref?.()
1973
2001
  })
1974
2002
 
1975
- testSessionFinishCh.publish(payload)
2003
+ publishWithCompletion(testSessionFinishCh, payload, onDone)
1976
2004
 
1977
2005
  const waitingResult = await Promise.race([flushPromise, timeoutPromise])
1978
2006
 
@@ -2137,12 +2165,6 @@ function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
2137
2165
  }
2138
2166
  }
2139
2167
 
2140
- function getChannelPromise (channelToPublishTo, payload = {}) {
2141
- return new Promise(resolve => {
2142
- channelToPublishTo.publish({ ...payload, onDone: resolve })
2143
- })
2144
- }
2145
-
2146
2168
  function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
2147
2169
  const SearchSource = searchSourcePackage.default ?? searchSourcePackage
2148
2170
 
@@ -2211,11 +2233,13 @@ function getCliWrapper (isNewJestVersion) {
2211
2233
  resetSuiteSkippingRunState()
2212
2234
  hasFinishedTestSession = false
2213
2235
 
2236
+ let shouldResetLibraryConfiguration = true
2214
2237
  try {
2215
2238
  const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, {
2216
2239
  frameworkVersion: jestVersion,
2217
2240
  })
2218
2241
  if (!err) {
2242
+ shouldResetLibraryConfiguration = false
2219
2243
  isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
2220
2244
  isCoverageReportUploadEnabled = libraryConfig.isCoverageReportUploadEnabled
2221
2245
  isItrEnabled = libraryConfig.isItrEnabled
@@ -2231,6 +2255,10 @@ function getCliWrapper (isNewJestVersion) {
2231
2255
  }
2232
2256
  } catch (err) {
2233
2257
  log.error('Jest library configuration error', err)
2258
+ } finally {
2259
+ if (shouldResetLibraryConfiguration) {
2260
+ resetLibraryConfiguration()
2261
+ }
2234
2262
  }
2235
2263
 
2236
2264
  const {
@@ -2491,9 +2519,7 @@ function getCliWrapper (isNewJestVersion) {
2491
2519
 
2492
2520
  if (codeCoverageReportCh.hasSubscribers) {
2493
2521
  const rootDir = result.globalConfig?.rootDir || process.cwd()
2494
- await new Promise((resolve) => {
2495
- codeCoverageReportCh.publish({ rootDir, onDone: resolve })
2496
- })
2522
+ await getChannelPromise(codeCoverageReportCh, { rootDir })
2497
2523
  }
2498
2524
 
2499
2525
  logSessionSummary(ignoredFailuresSummary, getAttemptToFixExecutionsFromJestResults(result))
@@ -2511,6 +2537,10 @@ function shouldWaitForTestSuiteFinish (environment) {
2511
2537
  return isJestWorker && environment.globalConfig?.workerIdleMemoryLimit !== undefined
2512
2538
  }
2513
2539
 
2540
+ /**
2541
+ * @param {Record<string, unknown>} payload
2542
+ * @param {boolean} waitForFinish
2543
+ */
2514
2544
  function publishTestSuiteFinish (payload, waitForFinish) {
2515
2545
  if (!testSuiteFinishCh.hasSubscribers) return
2516
2546
 
@@ -2519,12 +2549,9 @@ function publishTestSuiteFinish (payload, waitForFinish) {
2519
2549
  return
2520
2550
  }
2521
2551
 
2522
- return new Promise(resolve => {
2523
- testSuiteFinishCh.publish({
2524
- ...payload,
2525
- waitForFinish,
2526
- onDone: resolve,
2527
- })
2552
+ return getChannelPromise(testSuiteFinishCh, {
2553
+ ...payload,
2554
+ waitForFinish,
2528
2555
  })
2529
2556
  }
2530
2557
 
@@ -135,7 +135,7 @@ function wrapPoolMethod (createConnection) {
135
135
 
136
136
  function wrapPoolGetConnectionMethod (getConnection) {
137
137
  return function wrappedGetConnection (...args) {
138
- const cb = args[args.length - 1]
138
+ const cb = args.at(-1)
139
139
  if (typeof cb !== 'function') return getConnection.apply(this, args)
140
140
 
141
141
  const ctx = {}
@@ -3,6 +3,11 @@
3
3
  const { createCoverageMap } = require('../../../../vendor/dist/istanbul-lib-coverage')
4
4
  const satisfies = require('../../../../vendor/dist/semifies')
5
5
  const { DD_MAJOR } = require('../../../../version')
6
+ const {
7
+ getRunStoresPromise,
8
+ publishWithCompletion,
9
+ runStoresWithCompletion,
10
+ } = require('../helpers/channel')
6
11
  const { addHook, channel } = require('../helpers/instrument')
7
12
  const shimmer = require('../../../datadog-shimmer')
8
13
  const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
@@ -240,6 +245,41 @@ function resetSuiteSkippingRunState () {
240
245
  writeCoverageBackfillToCache({})
241
246
  }
242
247
 
248
+ /**
249
+ * @param {((failures: number) => void) | undefined} callback
250
+ * @returns {{ onRunDone: (failures: number) => void, onFlushDone: () => void }}
251
+ */
252
+ function getRunCompletionCallbacks (callback) {
253
+ let failures
254
+ let hasRunFinished = false
255
+ let hasFlushFinished = false
256
+ let hasCompleted = false
257
+ const onDone = callback || (() => {})
258
+
259
+ const completeIfReady = () => {
260
+ if (hasCompleted || !hasRunFinished || !hasFlushFinished) return
261
+
262
+ hasCompleted = true
263
+ onDone(failures)
264
+ }
265
+
266
+ return {
267
+ onRunDone: (runFailures) => {
268
+ if (hasRunFinished) return
269
+
270
+ failures = runFailures
271
+ hasRunFinished = true
272
+ completeIfReady()
273
+ },
274
+ onFlushDone: () => {
275
+ if (hasFlushFinished) return
276
+
277
+ hasFlushFinished = true
278
+ completeIfReady()
279
+ },
280
+ }
281
+ }
282
+
243
283
  function getOnStartHandler (frameworkVersion) {
244
284
  return function () {
245
285
  const processArgv = process.argv.slice(2).join(' ')
@@ -251,7 +291,12 @@ function getOnStartHandler (frameworkVersion) {
251
291
  }
252
292
  }
253
293
 
254
- function getOnEndHandler (isParallel) {
294
+ /**
295
+ * @param {boolean} isParallel
296
+ * @param {() => void} onDone
297
+ * @returns {() => void}
298
+ */
299
+ function getOnEndHandler (isParallel, onDone) {
255
300
  return function () {
256
301
  let status = 'pass'
257
302
  let error
@@ -337,7 +382,7 @@ function getOnEndHandler (isParallel) {
337
382
  global.__coverage__ = fromCoverageMapToCoverage(originalCoverageMap)
338
383
  }
339
384
 
340
- testSessionFinishCh.publish({
385
+ publishWithCompletion(testSessionFinishCh, {
341
386
  status,
342
387
  isSuitesSkipped,
343
388
  testCodeCoverageLinesTotal,
@@ -350,19 +395,13 @@ function getOnEndHandler (isParallel) {
350
395
  isEarlyFlakeDetectionFaulty: config.isEarlyFlakeDetectionFaulty,
351
396
  isTestManagementEnabled: config.isTestManagementTestsEnabled,
352
397
  isParallel,
353
- })
398
+ }, onDone)
354
399
 
355
400
  logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
356
401
  loggedAttemptToFixTests.clear()
357
402
  }
358
403
  }
359
404
 
360
- function getRunStoresPromise (channelToPublishTo, ctx) {
361
- return new Promise(resolve => {
362
- channelToPublishTo.runStores({ ...ctx, onDone: resolve }, () => {})
363
- })
364
- }
365
-
366
405
  function applyKnownTestsResponse ({ err, knownTests }) {
367
406
  if (err) {
368
407
  config.knownTests = []
@@ -395,13 +434,14 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
395
434
  let skippableSuitesResponse
396
435
  resetSuiteSkippingRunState()
397
436
 
398
- const onReceivedSkippableSuites = ({
399
- err,
400
- skippableSuites,
401
- itrCorrelationId: responseItrCorrelationId,
402
- skippableSuitesCoverage: responseSkippableSuitesCoverage,
403
- }) => {
404
- if (err) {
437
+ const onReceivedSkippableSuites = (response) => {
438
+ const {
439
+ err,
440
+ skippableSuites,
441
+ itrCorrelationId: responseItrCorrelationId,
442
+ skippableSuitesCoverage: responseSkippableSuitesCoverage,
443
+ } = response || {}
444
+ if (!response || err) {
405
445
  suitesToSkip = []
406
446
  skippableSuitesCoverage = {}
407
447
  } else {
@@ -443,12 +483,12 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
443
483
  return
444
484
  }
445
485
 
446
- ctx.onDone = onReceivedSkippableSuites
447
- skippableSuitesCh.runStores(ctx, () => {})
486
+ runStoresWithCompletion(skippableSuitesCh, ctx, onReceivedSkippableSuites)
448
487
  }
449
488
 
450
- const onReceivedImpactedTests = ({ err, modifiedFiles: receivedModifiedFiles }) => {
451
- if (err) {
489
+ const onReceivedImpactedTests = (response) => {
490
+ const { err, modifiedFiles: receivedModifiedFiles } = response || {}
491
+ if (!response || err) {
452
492
  config.modifiedFiles = []
453
493
  config.isImpactedTestsEnabled = false
454
494
  } else {
@@ -465,8 +505,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
465
505
 
466
506
  const continueAfterTestRequests = () => {
467
507
  if (config.isImpactedTestsEnabled) {
468
- ctx.onDone = onReceivedImpactedTests
469
- modifiedFilesCh.runStores(ctx, () => {})
508
+ runStoresWithCompletion(modifiedFilesCh, ctx, onReceivedImpactedTests)
470
509
  } else if (config.isSuitesSkippingEnabled) {
471
510
  requestSkippableSuites()
472
511
  } else {
@@ -476,8 +515,14 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
476
515
  }
477
516
  }
478
517
 
479
- const onReceivedConfiguration = ({ err, isTestDynamicInstrumentationEnabled, libraryConfig, repositoryRoot }) => {
480
- if (err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
518
+ const onReceivedConfiguration = (response) => {
519
+ const {
520
+ err,
521
+ isTestDynamicInstrumentationEnabled,
522
+ libraryConfig,
523
+ repositoryRoot,
524
+ } = response || {}
525
+ if (!response || err || !skippableSuitesCh.hasSubscribers || !knownTestsCh.hasSubscribers) {
481
526
  return mochaGlobalRunCh.runStores(ctx, () => {
482
527
  onFinishRequest()
483
528
  })
@@ -526,9 +571,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
526
571
  })
527
572
  }
528
573
 
529
- ctx.onDone = onReceivedConfiguration
530
-
531
- libraryConfigurationCh.runStores(ctx, () => {})
574
+ runStoresWithCompletion(libraryConfigurationCh, ctx, onReceivedConfiguration)
532
575
  }
533
576
 
534
577
  // In this hook we delay the execution with options.delay to grab library configuration,
@@ -553,7 +596,6 @@ addHook({
553
596
 
554
597
  const runner = run.apply(this, args)
555
598
 
556
- // eslint-disable-next-line unicorn/no-array-for-each
557
599
  this.files.forEach((path) => {
558
600
  const isUnskippable = isMarkedAsUnskippable({ path })
559
601
  if (isUnskippable) {
@@ -642,6 +684,9 @@ addHook({
642
684
  return run.apply(this, args)
643
685
  }
644
686
 
687
+ const { onRunDone, onFlushDone } = getRunCompletionCallbacks(args[0])
688
+ args[0] = onRunDone
689
+
645
690
  const { suitesByTestFile, numSuitesByTestFile } = getSuitesByTestFile(this.suite)
646
691
  // Root-level tests (direct children of root, no describe wrapper) keyed by file.
647
692
  // Populated during the root 'suite' event so the normal finish path can include them
@@ -753,7 +798,7 @@ addHook({
753
798
  finishRootSuiteForFile(test.file)
754
799
  }
755
800
 
756
- const onEnd = getOnEndHandler(false)
801
+ const onEnd = getOnEndHandler(false, onFlushDone)
757
802
 
758
803
  this.once('start', getOnStartHandler(frameworkVersion))
759
804
 
@@ -915,7 +960,6 @@ addHook({
915
960
  status = 'skip'
916
961
  } else {
917
962
  // has to check every test in the test file
918
- // eslint-disable-next-line unicorn/no-array-for-each
919
963
  suitesInTestFile.forEach(suite => {
920
964
  suite.eachTest(test => {
921
965
  if (test.state === 'failed' || test.timedOut) {
@@ -1043,8 +1087,11 @@ addHook({
1043
1087
  return run.apply(this, arguments)
1044
1088
  }
1045
1089
 
1090
+ const { onRunDone, onFlushDone } = getRunCompletionCallbacks(cb)
1091
+ arguments[0] = onRunDone
1092
+
1046
1093
  this.once('start', getOnStartHandler(frameworkVersion))
1047
- this.once('end', getOnEndHandler(true))
1094
+ this.once('end', getOnEndHandler(true, onFlushDone))
1048
1095
 
1049
1096
  // Populate unskippable suites before config is fetched (matches serial mode at Mocha.prototype.run)
1050
1097
  for (const filePath of files) {
@@ -1084,7 +1131,7 @@ addHook({
1084
1131
  skippedSuites = skippedFiles
1085
1132
  skippedSuitesCoverage = getSkippedSuitesCoverageForRun()
1086
1133
  writeCoverageBackfillToCache(skippedSuitesCoverage, getCoverageRootDir())
1087
- run.apply(this, [cb, { files: filteredFiles }])
1134
+ run.apply(this, [onRunDone, { files: filteredFiles }])
1088
1135
  } else {
1089
1136
  run.apply(this, arguments)
1090
1137
  }
@@ -229,7 +229,6 @@ function getSuitesByTestFile (root) {
229
229
  suitesByTestFile[suite.file] = [suite]
230
230
  }
231
231
  }
232
- // eslint-disable-next-line unicorn/no-array-for-each
233
232
  suite.suites.forEach(suite => {
234
233
  getSuites(suite)
235
234
  })
@@ -920,7 +919,6 @@ function getOnPendingHandler () {
920
919
  function getRunTestsWrapper (runTests, config) {
921
920
  return function (suite) {
922
921
  if (config.isTestManagementTestsEnabled) {
923
- // eslint-disable-next-line unicorn/no-array-for-each
924
922
  suite.tests.forEach((test) => {
925
923
  const { isAttemptToFix, isDisabled, isQuarantined } = getTestProperties(test, config.testManagementTests)
926
924
  if (isAttemptToFix && !test.isPending()) {
@@ -944,7 +942,6 @@ function getRunTestsWrapper (runTests, config) {
944
942
  }
945
943
 
946
944
  if (config.isImpactedTestsEnabled) {
947
- // eslint-disable-next-line unicorn/no-array-for-each
948
945
  suite.tests.forEach((test) => {
949
946
  isModifiedCh.publish({
950
947
  modifiedFiles: config.modifiedFiles,
@@ -968,7 +965,6 @@ function getRunTestsWrapper (runTests, config) {
968
965
 
969
966
  if (config.isKnownTestsEnabled) {
970
967
  // by the time we reach `this.on('test')`, it is too late. We need to add retries here
971
- // eslint-disable-next-line unicorn/no-array-for-each
972
968
  suite.tests.forEach((test) => {
973
969
  if (!test.isPending() && isNewTest(test, config.knownTests)) {
974
970
  test._ddIsNew = true
@@ -20,11 +20,11 @@ const collectionMethodsWithFilter = [
20
20
  'replaceOne',
21
21
  ] // findOne is ignored because it calls to find
22
22
 
23
- const collectionMethodsWithTwoFilters = [
23
+ const collectionMethodsWithTwoFilters = new Set([
24
24
  'findOneAndUpdate',
25
25
  'updateMany',
26
26
  'updateOne',
27
- ]
27
+ ])
28
28
 
29
29
  const startCh = channel('datadog:mongodb:collection:filter:start')
30
30
 
@@ -36,7 +36,7 @@ addHook({ name: 'mongodb', versions: ['>=3.3 <5', '5', '>=6'] }, mongodb => {
36
36
  for (const methodName of [...collectionMethodsWithFilter, ...collectionMethodsWithTwoFilters]) {
37
37
  if (!(methodName in mongodb.Collection.prototype)) continue
38
38
 
39
- const useTwoArguments = collectionMethodsWithTwoFilters.includes(methodName)
39
+ const useTwoArguments = collectionMethodsWithTwoFilters.has(methodName)
40
40
 
41
41
  shimmer.wrap(mongodb.Collection.prototype, methodName, method => {
42
42
  return function (...args) {
@@ -55,11 +55,11 @@ const collectionMethodsWithFilter = [
55
55
  'remove',
56
56
  ]
57
57
 
58
- const collectionMethodsWithTwoFilters = [
58
+ const collectionMethodsWithTwoFilters = new Set([
59
59
  'findOneAndUpdate',
60
60
  'updateMany',
61
61
  'updateOne',
62
- ]
62
+ ])
63
63
 
64
64
  addHook({
65
65
  name: 'mongoose',
@@ -67,7 +67,7 @@ addHook({
67
67
  file: 'lib/model.js',
68
68
  }, Model => {
69
69
  for (const methodName of [...collectionMethodsWithFilter, ...collectionMethodsWithTwoFilters]) {
70
- const useTwoArguments = collectionMethodsWithTwoFilters.includes(methodName)
70
+ const useTwoArguments = collectionMethodsWithTwoFilters.has(methodName)
71
71
  if (!(methodName in Model)) continue
72
72
 
73
73
  shimmer.wrap(Model, methodName, method => {
@@ -20,14 +20,14 @@ const methods = [
20
20
  'where',
21
21
  ]
22
22
 
23
- const methodsOptionalArgs = ['findOneAndUpdate']
23
+ const methodsOptionalArgs = new Set(['findOneAndUpdate'])
24
24
 
25
25
  function getFilters (args, methodName) {
26
26
  const [arg0, arg1] = args
27
27
 
28
28
  const filters = arg0 !== null && typeof arg0 === 'object' ? [arg0] : []
29
29
 
30
- if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
30
+ if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.has(methodName)) {
31
31
  filters.push(arg1)
32
32
  }
33
33
 
@@ -84,7 +84,7 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
84
84
  const finish = () => finishPoolQueryCh.publish(ctx)
85
85
 
86
86
  return startPoolQueryCh.runStores(ctx, () => {
87
- const cb = args[args.length - 1]
87
+ const cb = args.at(-1)
88
88
  if (typeof cb === 'function') {
89
89
  args[args.length - 1] = shimmer.wrapCallback(cb, cb => function (...args) {
90
90
  return finishPoolQueryCh.runStores(ctx, cb, this, ...args)