dd-trace 5.109.0 → 5.111.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 (208) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +25 -5
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +79 -24
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +154 -47
  8. package/package.json +20 -16
  9. package/packages/datadog-esbuild/index.js +26 -0
  10. package/packages/datadog-esbuild/src/utils.js +46 -3
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  13. package/packages/datadog-instrumentations/src/fs.js +8 -6
  14. package/packages/datadog-instrumentations/src/graphql.js +26 -484
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  17. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  19. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  23. package/packages/datadog-instrumentations/src/koa.js +3 -2
  24. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  25. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  26. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  27. package/packages/datadog-instrumentations/src/next.js +10 -10
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  29. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  30. package/packages/datadog-instrumentations/src/restify.js +4 -3
  31. package/packages/datadog-instrumentations/src/router.js +18 -5
  32. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  33. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  34. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  35. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  36. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  37. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  38. package/packages/datadog-instrumentations/src/vitest.js +11 -1598
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  45. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  46. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
  48. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  49. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  50. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  52. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  53. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  54. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  55. package/packages/datadog-plugin-http/src/client.js +8 -3
  56. package/packages/datadog-plugin-http/src/index.js +6 -8
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-jest/src/index.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  60. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  61. package/packages/datadog-plugin-openai/src/services.js +2 -2
  62. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  63. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  64. package/packages/datadog-plugin-undici/src/index.js +5 -2
  65. package/packages/datadog-plugin-vitest/src/index.js +76 -64
  66. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  67. package/packages/datadog-webpack/index.js +17 -1
  68. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  69. package/packages/dd-trace/src/aiguard/sdk.js +15 -3
  70. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  71. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  72. package/packages/dd-trace/src/appsec/channels.js +3 -1
  73. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  74. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  75. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  76. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  77. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  78. package/packages/dd-trace/src/appsec/index.js +20 -7
  79. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  80. package/packages/dd-trace/src/appsec/reporter.js +50 -10
  81. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  82. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
  83. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  84. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  85. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  86. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  90. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  91. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  92. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  94. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  95. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  96. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  97. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  98. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  99. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  100. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  101. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  102. package/packages/dd-trace/src/config/defaults.js +25 -5
  103. package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
  104. package/packages/dd-trace/src/config/helper.js +134 -33
  105. package/packages/dd-trace/src/config/index.js +39 -23
  106. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  107. package/packages/dd-trace/src/config/parsers.js +19 -1
  108. package/packages/dd-trace/src/config/remote_config.js +1 -1
  109. package/packages/dd-trace/src/config/supported-configurations.json +109 -57
  110. package/packages/dd-trace/src/constants.js +7 -0
  111. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  112. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  114. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  116. package/packages/dd-trace/src/debugger/index.js +5 -0
  117. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  118. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  119. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  120. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  122. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  123. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  124. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  125. package/packages/dd-trace/src/guardrails/index.js +8 -5
  126. package/packages/dd-trace/src/index.js +6 -4
  127. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  128. package/packages/dd-trace/src/llmobs/index.js +17 -6
  129. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  130. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  131. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/sdk.js +8 -6
  134. package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
  135. package/packages/dd-trace/src/llmobs/tagger.js +58 -3
  136. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  137. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  138. package/packages/dd-trace/src/log/index.js +18 -20
  139. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  140. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  141. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  142. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  143. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  145. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  146. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  147. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  148. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  149. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  150. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  151. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  152. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
  153. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  154. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  155. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  156. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  157. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  158. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  159. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  161. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  162. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  163. package/packages/dd-trace/src/plugin_manager.js +14 -8
  164. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
  165. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  166. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +91 -9
  168. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  169. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  170. package/packages/dd-trace/src/priority_sampler.js +1 -13
  171. package/packages/dd-trace/src/profiling/config.js +129 -198
  172. package/packages/dd-trace/src/profiling/constants.js +4 -7
  173. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  174. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  175. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  176. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  177. package/packages/dd-trace/src/profiling/oom.js +71 -0
  178. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  179. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  180. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  182. package/packages/dd-trace/src/proxy.js +8 -8
  183. package/packages/dd-trace/src/ritm.js +4 -2
  184. package/packages/dd-trace/src/serverless.js +1 -2
  185. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  186. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  187. package/packages/dd-trace/src/span_format.js +2 -1
  188. package/packages/dd-trace/src/span_processor.js +11 -6
  189. package/packages/dd-trace/src/span_stats.js +96 -78
  190. package/packages/dd-trace/src/startup-log.js +2 -1
  191. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  192. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  193. package/packages/dd-trace/src/telemetry/index.js +2 -2
  194. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  195. package/packages/dd-trace/src/telemetry/send-data.js +20 -18
  196. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  197. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  198. package/packages/dd-trace/src/tracer.js +15 -1
  199. package/packages/dd-trace/src/util.js +14 -0
  200. package/register.js +58 -1
  201. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  202. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  203. package/vendor/dist/pprof-format/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
  208. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -0,0 +1,1567 @@
1
+ 'use strict'
2
+
3
+ const path = require('node:path')
4
+ const { fileURLToPath } = require('node:url')
5
+ const { MessagePort } = require('node:worker_threads')
6
+
7
+ const shimmer = require('../../datadog-shimmer')
8
+ const log = require('../../dd-trace/src/log')
9
+ const {
10
+ VITEST_WORKER_TRACE_PAYLOAD_CODE,
11
+ VITEST_WORKER_LOGS_PAYLOAD_CODE,
12
+ getMaxEfdRetryCount,
13
+ collectTestOptimizationSummariesFromTraces,
14
+ logTestOptimizationSummary,
15
+ getTestOptimizationRequestResults,
16
+ getTestSuitePath,
17
+ isModifiedTest,
18
+ } = require('../../dd-trace/src/plugins/util/test')
19
+ const { addHook } = require('./helpers/instrument')
20
+ const noWorkerInit = require('./vitest-main-no-worker-init')
21
+ const {
22
+ testStartCh,
23
+ testPassCh,
24
+ testErrorCh,
25
+ testSkipCh,
26
+ testSuiteStartCh,
27
+ testSuiteFinishCh,
28
+ testSuiteErrorCh,
29
+ testSessionStartCh,
30
+ testSessionFinishCh,
31
+ testSessionConfigurationCh,
32
+ libraryConfigurationCh,
33
+ knownTestsCh,
34
+ isEarlyFlakeDetectionFaultyCh,
35
+ testManagementTestsCh,
36
+ modifiedFilesCh,
37
+ workerReportTraceCh,
38
+ workerReportLogsCh,
39
+ codeCoverageReportCh,
40
+ findExportByName,
41
+ getChannelPromise,
42
+ getTypeTasks,
43
+ getWorkspaceProject,
44
+ setProvidedContext,
45
+ getVitestTestProperties,
46
+ } = require('./vitest-util')
47
+
48
+ const newTestsWithDynamicNames = new Set()
49
+ const attemptToFixExecutions = new Map()
50
+ const workerProcesses = new WeakSet()
51
+ const mainProcessSetupStates = new WeakMap()
52
+ const coverageWrappedProviders = new WeakSet()
53
+ const finishWrappedContexts = new WeakSet()
54
+ let isFlakyTestRetriesEnabled = false
55
+ let flakyTestRetriesCount = 0
56
+ let isEarlyFlakeDetectionEnabled = false
57
+ let earlyFlakeDetectionNumRetries = 0
58
+ let earlyFlakeDetectionSlowTestRetries = {}
59
+ let isEarlyFlakeDetectionFaulty = false
60
+ let isKnownTestsEnabled = false
61
+ let isTestManagementTestsEnabled = false
62
+ let isImpactedTestsEnabled = false
63
+ let testManagementAttemptToFixRetries = 0
64
+ let isDiEnabled = false
65
+ let testCodeCoverageLinesTotal
66
+ let coverageRootDir
67
+ let isSessionStarted = false
68
+ let isVitestNoWorkerInitActive = false
69
+ let vitestPool = null
70
+ let isMessagePortWrapped = false
71
+ const tinyPoolClassWrappers = new WeakMap()
72
+
73
+ function getConfiguredEfdRetryCount (slowTestRetries, fallbackRetryCount) {
74
+ if (!slowTestRetries || !Object.keys(slowTestRetries).length) {
75
+ return fallbackRetryCount
76
+ }
77
+ return getMaxEfdRetryCount(slowTestRetries)
78
+ }
79
+
80
+ function getTestCommand () {
81
+ return `vitest ${process.argv.slice(2).join(' ')}`
82
+ }
83
+
84
+ function isValidKnownTests (receivedKnownTests) {
85
+ return !!receivedKnownTests.vitest
86
+ }
87
+
88
+ function isReporterPackage (vitestPackage) {
89
+ return vitestPackage.B?.name === 'BaseSequencer'
90
+ }
91
+
92
+ function isReporterPackageNew (vitestPackage) {
93
+ return vitestPackage.e?.name === 'BaseSequencer'
94
+ }
95
+
96
+ function isReporterPackageNewest (vitestPackage) {
97
+ return vitestPackage.h?.name === 'BaseSequencer'
98
+ }
99
+
100
+ function getBaseSequencerExport (vitestPackage) {
101
+ return findExportByName(vitestPackage, 'BaseSequencer')
102
+ }
103
+
104
+ function isCliApiPackage (vitestPackage) {
105
+ return !!findExportByName(vitestPackage, 'startVitest')
106
+ }
107
+
108
+ function getVitestExport (vitestPackage) {
109
+ return findExportByName(vitestPackage, 'Vitest')
110
+ }
111
+
112
+ function getTypecheckerExport (vitestPackage) {
113
+ return findExportByName(vitestPackage, 'Typechecker')
114
+ }
115
+
116
+ function getForksPoolWorkerExport (vitestPackage) {
117
+ return findExportByName(vitestPackage, 'ForksPoolWorker')
118
+ }
119
+
120
+ function getThreadsPoolWorkerExport (vitestPackage) {
121
+ return findExportByName(vitestPackage, 'ThreadsPoolWorker')
122
+ }
123
+
124
+ function getSessionStatus (state) {
125
+ if (state.getCountOfFailedTests() > 0) {
126
+ return 'fail'
127
+ }
128
+ if (state.pathsSet.size === 0) {
129
+ return 'skip'
130
+ }
131
+ return 'pass'
132
+ }
133
+
134
+ function getTestFilepathsFromSpecifications (testSpecifications) {
135
+ if (!Array.isArray(testSpecifications) || !testSpecifications.length) {
136
+ return
137
+ }
138
+
139
+ return testSpecifications.map(testSpecification => {
140
+ const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
141
+ return testFile?.moduleId || testFile?.filepath || testFile
142
+ })
143
+ }
144
+
145
+ function getTestSpecificationsKey (testSpecifications) {
146
+ if (!Array.isArray(testSpecifications) || !testSpecifications.length) return
147
+
148
+ const keyParts = []
149
+ for (const testSpecification of testSpecifications) {
150
+ const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
151
+ const testFilepath = testFile?.moduleId || testFile?.filepath || testFile
152
+ if (!testFilepath) continue
153
+
154
+ const projectName = getProjectName(getTestSpecificationProject(testSpecification)) || ''
155
+ const pool = getTestSpecificationPool(testSpecification) || ''
156
+ keyParts.push(`${projectName}\0${pool}\0${testFilepath}`)
157
+ }
158
+
159
+ if (!keyParts.length) return
160
+
161
+ keyParts.sort()
162
+ return keyParts.join('\0')
163
+ }
164
+
165
+ function getTestFilepaths (ctx, testSpecifications) {
166
+ const testFilepaths = getTestFilepathsFromSpecifications(testSpecifications)
167
+ if (testFilepaths) {
168
+ return testFilepaths
169
+ }
170
+
171
+ const getFilePaths = ctx.getTestFilepaths || ctx._globTestFilepaths
172
+ return getFilePaths.call(ctx)
173
+ }
174
+
175
+ /**
176
+ * @typedef {{
177
+ * isAttemptToFix?: boolean,
178
+ * isDisabled?: boolean,
179
+ * isQuarantined?: boolean
180
+ * }} VitestTestManagementProperties
181
+ *
182
+ * @typedef {{
183
+ * testSuite?: string,
184
+ * knownTests?: string[],
185
+ * testManagementTests?: Record<string, VitestTestManagementProperties>,
186
+ * isModified?: boolean
187
+ * }} VitestTestProperties
188
+ */
189
+
190
+ /**
191
+ * Normalize a Vitest test file path to the test suite path used by Test Optimization APIs.
192
+ *
193
+ * @param {string} testFilepath
194
+ * @param {string} repositoryRoot
195
+ * @returns {string}
196
+ */
197
+ function getNormalizedTestSuitePath (testFilepath, repositoryRoot) {
198
+ const testSuiteAbsolutePath = path.isAbsolute(testFilepath) ? testFilepath : path.join(repositoryRoot, testFilepath)
199
+ return getTestSuitePath(testSuiteAbsolutePath, repositoryRoot)
200
+ }
201
+
202
+ /**
203
+ * Build simplified Test Management metadata grouped by normalized test suite path.
204
+ *
205
+ * @param {{ vitest?: { suites?: Record<string, { tests?: Record<string, { properties?: {
206
+ * attempt_to_fix?: boolean,
207
+ * disabled?: boolean,
208
+ * quarantined?: boolean
209
+ * } }> }> } }} testManagementTests
210
+ * @returns {Record<string, Record<string, VitestTestManagementProperties>>}
211
+ */
212
+ function getTestManagementTestsBySuite (testManagementTests) {
213
+ const testManagementTestsBySuite = {}
214
+ const suites = testManagementTests?.vitest?.suites
215
+ if (!suites) return testManagementTestsBySuite
216
+
217
+ for (const [testSuite, suite] of Object.entries(suites)) {
218
+ const tests = suite?.tests
219
+ if (!tests) continue
220
+
221
+ const testsByName = {}
222
+ let hasTests = false
223
+ for (const [testName, test] of Object.entries(tests)) {
224
+ const properties = test?.properties
225
+ const testProperties = {
226
+ isAttemptToFix: properties?.attempt_to_fix,
227
+ isDisabled: properties?.disabled,
228
+ isQuarantined: properties?.quarantined,
229
+ }
230
+ testsByName[testName] = testProperties
231
+ hasTests = true
232
+ }
233
+ if (hasTests) {
234
+ testManagementTestsBySuite[testSuite] = testsByName
235
+ }
236
+ }
237
+
238
+ return testManagementTestsBySuite
239
+ }
240
+
241
+ /**
242
+ * Build a set-like object for test suites modified in the current pull request diff.
243
+ *
244
+ * @param {Record<string, number[]>|undefined} modifiedFiles
245
+ * @returns {Record<string, boolean>}
246
+ */
247
+ function getImpactedTestSuites (modifiedFiles) {
248
+ const impactedTestSuites = {}
249
+ if (!modifiedFiles) return impactedTestSuites
250
+
251
+ for (const testSuite of Object.keys(modifiedFiles)) {
252
+ if (isModifiedTest(testSuite, 0, 0, modifiedFiles, 'vitest')) {
253
+ impactedTestSuites[testSuite] = true
254
+ }
255
+ }
256
+
257
+ return impactedTestSuites
258
+ }
259
+
260
+ /**
261
+ * Build the worker-ready test metadata map keyed by Vitest's absolute filepath.
262
+ *
263
+ * @param {string[]} testFilepaths
264
+ * @param {string} repositoryRoot
265
+ * @param {Record<string, string[]>|undefined} knownTestsBySuite
266
+ * @param {Record<string, Record<string, VitestTestManagementProperties>>|undefined} testManagementTestsBySuite
267
+ * @param {Record<string, boolean>|undefined} impactedTestSuites
268
+ * @returns {Record<string, VitestTestProperties>}
269
+ */
270
+ function getTestPropertiesByFilepath (
271
+ testFilepaths,
272
+ repositoryRoot,
273
+ knownTestsBySuite,
274
+ testManagementTestsBySuite,
275
+ impactedTestSuites
276
+ ) {
277
+ const testPropertiesByFilepath = {}
278
+ if (!Array.isArray(testFilepaths)) return testPropertiesByFilepath
279
+
280
+ for (const testFilepath of testFilepaths) {
281
+ if (typeof testFilepath !== 'string') continue
282
+
283
+ const testSuite = getNormalizedTestSuitePath(testFilepath, repositoryRoot)
284
+ const testProperties = { testSuite }
285
+ const hasProperties = knownTestsBySuite !== undefined ||
286
+ testManagementTestsBySuite !== undefined ||
287
+ impactedTestSuites !== undefined
288
+
289
+ if (knownTestsBySuite) {
290
+ testProperties.knownTests = knownTestsBySuite[testSuite] || []
291
+ }
292
+ if (testManagementTestsBySuite) {
293
+ testProperties.testManagementTests = testManagementTestsBySuite[testSuite] || {}
294
+ }
295
+ if (impactedTestSuites?.[testSuite]) {
296
+ testProperties.isModified = true
297
+ }
298
+
299
+ if (hasProperties) {
300
+ testPropertiesByFilepath[testFilepath] = testProperties
301
+ }
302
+ }
303
+
304
+ return testPropertiesByFilepath
305
+ }
306
+
307
+ function wrapCoverageProvider (ctx) {
308
+ const { coverageProvider } = ctx
309
+ if (!coverageProvider?.generateCoverage || coverageWrappedProviders.has(coverageProvider)) {
310
+ return
311
+ }
312
+ coverageWrappedProviders.add(coverageProvider)
313
+
314
+ // Capture coverage root directory from config (default is 'coverage' in cwd)
315
+ try {
316
+ const coverageConfig = ctx.config?.coverage
317
+ const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
318
+ const rootDir = ctx.config?.root || process.cwd()
319
+ coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
320
+ } catch {
321
+ // Fallback to cwd if we can't get config
322
+ coverageRootDir = process.cwd()
323
+ }
324
+
325
+ shimmer.wrap(coverageProvider, 'generateCoverage', generateCoverage => async function () {
326
+ const totalCodeCoverage = await generateCoverage.apply(this, arguments)
327
+
328
+ try {
329
+ testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
330
+ } catch {
331
+ // ignore errors
332
+ }
333
+ return totalCodeCoverage
334
+ })
335
+ }
336
+
337
+ function wrapSessionFinish (ctx) {
338
+ if (finishWrappedContexts.has(ctx)) {
339
+ return
340
+ }
341
+ finishWrappedContexts.add(ctx)
342
+
343
+ shimmer.wrap(ctx, 'exit', getFinishWrapper)
344
+ shimmer.wrap(ctx, 'close', getFinishWrapper)
345
+ }
346
+
347
+ function resetLibraryConfig () {
348
+ isFlakyTestRetriesEnabled = false
349
+ flakyTestRetriesCount = 0
350
+ isEarlyFlakeDetectionEnabled = false
351
+ earlyFlakeDetectionNumRetries = 0
352
+ earlyFlakeDetectionSlowTestRetries = {}
353
+ isEarlyFlakeDetectionFaulty = false
354
+ isDiEnabled = false
355
+ isKnownTestsEnabled = false
356
+ isTestManagementTestsEnabled = false
357
+ isImpactedTestsEnabled = false
358
+ testManagementAttemptToFixRetries = 0
359
+ }
360
+
361
+ function applyLibraryConfig (libraryConfig) {
362
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
363
+ flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
364
+ isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
365
+ earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
366
+ earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
367
+ isEarlyFlakeDetectionFaulty = false
368
+ isDiEnabled = libraryConfig.isDiEnabled
369
+ isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
370
+ isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
371
+ testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
372
+ isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
373
+ }
374
+
375
+ function resetMainProcessProvidedContext (ctx) {
376
+ setProvidedContext(ctx, {
377
+ _ddIsDiEnabled: false,
378
+ _ddIsEarlyFlakeDetectionEnabled: false,
379
+ _ddEarlyFlakeDetectionNumRetries: 0,
380
+ _ddEarlyFlakeDetectionSlowTestRetries: {},
381
+ _ddIsFlakyTestRetriesEnabled: false,
382
+ _ddFlakyTestRetriesCount: 0,
383
+ _ddFlakyTestRetriesIncludesUnnamedProject: false,
384
+ _ddFlakyTestRetriesProjectNames: undefined,
385
+ _ddIsImpactedTestsEnabled: false,
386
+ _ddIsKnownTestsEnabled: false,
387
+ _ddIsTestManagementTestsEnabled: false,
388
+ _ddTestManagementAttemptToFixRetries: 0,
389
+ _ddTestPropertiesByFilepath: {},
390
+ }, 'Could not reset Test Optimization context for workers.')
391
+ }
392
+
393
+ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications, shouldInstallNoWorkerInit) {
394
+ if (!testSessionFinishCh.hasSubscribers) {
395
+ return
396
+ }
397
+
398
+ let repositoryRoot = process.cwd()
399
+ let testSessionConfiguration
400
+ let testFilepaths
401
+ let shouldSendTestProperties = false
402
+ let testPropertiesByFilepath
403
+ let knownTests
404
+ let knownTestsBySuite
405
+ let testManagementTests
406
+ let testManagementTestsBySuite
407
+ let modifiedFiles
408
+ let impactedTestSuites
409
+ const getCurrentTestFilepaths = async () => {
410
+ if (testFilepaths === undefined) {
411
+ testFilepaths = await getTestFilepaths(ctx, testSpecifications)
412
+ }
413
+ return testFilepaths
414
+ }
415
+
416
+ try {
417
+ const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion, {
418
+ isVitestNoWorkerInitActive: shouldInstallNoWorkerInit,
419
+ })
420
+ if (err) {
421
+ resetLibraryConfig()
422
+ } else {
423
+ applyLibraryConfig(libraryConfig)
424
+ }
425
+ } catch {
426
+ resetLibraryConfig()
427
+ }
428
+
429
+ resetMainProcessProvidedContext(ctx)
430
+
431
+ if (testSessionConfigurationCh.hasSubscribers) {
432
+ testSessionConfiguration = await getChannelPromise(testSessionConfigurationCh, frameworkVersion)
433
+ const {
434
+ testSessionId,
435
+ testModuleId,
436
+ testCommand,
437
+ repositoryRoot: receivedRepositoryRoot,
438
+ codeOwnersEntries,
439
+ } = testSessionConfiguration
440
+ repositoryRoot = receivedRepositoryRoot || repositoryRoot
441
+ if (!shouldInstallNoWorkerInit) {
442
+ setProvidedContext(ctx, {
443
+ _ddTestSessionId: testSessionId,
444
+ _ddTestModuleId: testModuleId,
445
+ _ddTestCommand: testCommand,
446
+ _ddRepositoryRoot: repositoryRoot,
447
+ _ddCodeOwnersEntries: codeOwnersEntries,
448
+ }, 'Could not send test session configuration to workers.')
449
+ }
450
+ }
451
+
452
+ const {
453
+ knownTestsResponse,
454
+ testManagementTestsResponse,
455
+ } = await getTestOptimizationRequestResults({
456
+ isKnownTestsEnabled,
457
+ isTestManagementTestsEnabled,
458
+ getKnownTests: () => getChannelPromise(knownTestsCh),
459
+ getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
460
+ })
461
+
462
+ const flakyTestRetriesConfiguration = configureFlakyTestRetries(ctx, testSpecifications)
463
+ if (flakyTestRetriesConfiguration) {
464
+ setProvidedContext(ctx, {
465
+ _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
466
+ _ddFlakyTestRetriesCount: flakyTestRetriesCount,
467
+ _ddFlakyTestRetriesIncludesUnnamedProject: flakyTestRetriesConfiguration.includesUnnamedProject,
468
+ _ddFlakyTestRetriesProjectNames: flakyTestRetriesConfiguration.projectNames,
469
+ }, 'Could not send library configuration to workers.')
470
+ }
471
+
472
+ if (isKnownTestsEnabled) {
473
+ const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
474
+ if (currentKnownTestsResponse.err) {
475
+ isEarlyFlakeDetectionEnabled = false
476
+ } else {
477
+ knownTests = currentKnownTestsResponse.knownTests
478
+ const currentTestFilepaths = await getCurrentTestFilepaths()
479
+
480
+ if (isValidKnownTests(knownTests)) {
481
+ isEarlyFlakeDetectionFaultyCh.publish({
482
+ knownTests: knownTests.vitest,
483
+ testFilepaths: currentTestFilepaths,
484
+ onDone: (isFaulty) => {
485
+ isEarlyFlakeDetectionFaulty = isFaulty
486
+ },
487
+ })
488
+ if (isEarlyFlakeDetectionFaulty) {
489
+ isEarlyFlakeDetectionEnabled = false
490
+ log.warn('New test detection is disabled because the number of new tests is too high.')
491
+ } else {
492
+ knownTestsBySuite = knownTests.vitest
493
+ shouldSendTestProperties = true
494
+ if (!shouldInstallNoWorkerInit) {
495
+ setProvidedContext(ctx, {
496
+ _ddIsKnownTestsEnabled: isKnownTestsEnabled,
497
+ _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
498
+ _ddEarlyFlakeDetectionNumRetries:
499
+ getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries),
500
+ _ddEarlyFlakeDetectionSlowTestRetries: earlyFlakeDetectionSlowTestRetries,
501
+ }, 'Could not send known tests to workers so Early Flake Detection will not work.')
502
+ }
503
+ }
504
+ } else {
505
+ isEarlyFlakeDetectionFaulty = true
506
+ isEarlyFlakeDetectionEnabled = false
507
+ }
508
+ }
509
+ }
510
+
511
+ if (!shouldInstallNoWorkerInit && isDiEnabled) {
512
+ setProvidedContext(ctx, {
513
+ _ddIsDiEnabled: isDiEnabled,
514
+ }, 'Could not send Dynamic Instrumentation configuration to workers.')
515
+ }
516
+
517
+ if (isTestManagementTestsEnabled) {
518
+ const { err, testManagementTests: receivedTestManagementTests } =
519
+ testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
520
+ if (err) {
521
+ isTestManagementTestsEnabled = false
522
+ log.error('Could not get test management tests.')
523
+ } else {
524
+ testManagementTests = receivedTestManagementTests
525
+ testManagementTestsBySuite = getTestManagementTestsBySuite(receivedTestManagementTests)
526
+ shouldSendTestProperties = true
527
+ if (!shouldInstallNoWorkerInit) {
528
+ setProvidedContext(ctx, {
529
+ _ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
530
+ _ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
531
+ }, 'Could not send test management tests to workers so Test Management will not work.')
532
+ }
533
+ }
534
+ }
535
+
536
+ if (isImpactedTestsEnabled) {
537
+ const modifiedFilesResponse = await getChannelPromise(modifiedFilesCh)
538
+ const { err } = modifiedFilesResponse
539
+ if (err) {
540
+ log.error('Could not get modified tests.')
541
+ } else {
542
+ modifiedFiles = modifiedFilesResponse.modifiedFiles
543
+ impactedTestSuites = getImpactedTestSuites(modifiedFiles)
544
+ shouldSendTestProperties = true
545
+ if (!shouldInstallNoWorkerInit) {
546
+ setProvidedContext(ctx, {
547
+ _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
548
+ }, 'Could not send modified tests to workers so Impacted Tests will not work.')
549
+ }
550
+ }
551
+ }
552
+
553
+ if (shouldSendTestProperties) {
554
+ testPropertiesByFilepath = getTestPropertiesByFilepath(
555
+ await getCurrentTestFilepaths(),
556
+ repositoryRoot,
557
+ knownTestsBySuite,
558
+ testManagementTestsBySuite,
559
+ impactedTestSuites
560
+ )
561
+ if (!shouldInstallNoWorkerInit) {
562
+ setProvidedContext(ctx, {
563
+ _ddTestPropertiesByFilepath: testPropertiesByFilepath,
564
+ }, 'Could not send test properties to workers so some Test Optimization features will not work.')
565
+ }
566
+ }
567
+
568
+ if (shouldInstallNoWorkerInit) {
569
+ noWorkerInit.configure(ctx, frameworkVersion, testSpecifications, {
570
+ knownTests,
571
+ knownTestsBySuite,
572
+ modifiedFiles,
573
+ repositoryRoot,
574
+ flakyTestRetriesConfiguration,
575
+ testManagementTests,
576
+ testManagementTestsBySuite,
577
+ testPropertiesByFilepath: testPropertiesByFilepath || {},
578
+ testSessionConfiguration,
579
+ }, {
580
+ getConfiguredEfdRetryCount,
581
+ state: getNoWorkerInitState(),
582
+ })
583
+ }
584
+
585
+ wrapCoverageProvider(ctx)
586
+ wrapSessionFinish(ctx)
587
+ return shouldInstallNoWorkerInit
588
+ }
589
+
590
+ function getNoWorkerInitState () {
591
+ return {
592
+ attemptToFixExecutions,
593
+ earlyFlakeDetectionNumRetries,
594
+ earlyFlakeDetectionSlowTestRetries,
595
+ isEarlyFlakeDetectionEnabled,
596
+ isEarlyFlakeDetectionFaulty,
597
+ isFlakyTestRetriesEnabled,
598
+ isKnownTestsEnabled,
599
+ newTestsWithDynamicNames,
600
+ testManagementAttemptToFixRetries,
601
+ }
602
+ }
603
+
604
+ function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications, shouldDeactivateOnFallback = false) {
605
+ const shouldInstallNoWorkerInit = shouldUseNoWorkerInit(ctx, frameworkVersion, testSpecifications)
606
+ const specificationsKey = getTestSpecificationsKey(testSpecifications)
607
+ let setupState = mainProcessSetupStates.get(ctx)
608
+ if (shouldDeactivateOnFallback && setupState?.shouldInstallNoWorkerInit && !shouldInstallNoWorkerInit) {
609
+ noWorkerInit.deactivate(ctx)
610
+ }
611
+ if (
612
+ !setupState ||
613
+ setupState.specificationsKey !== specificationsKey ||
614
+ setupState.shouldInstallNoWorkerInit !== shouldInstallNoWorkerInit
615
+ ) {
616
+ setupState = {
617
+ setupPromise: runMainProcessSetup(ctx, frameworkVersion, testSpecifications, shouldInstallNoWorkerInit),
618
+ shouldInstallNoWorkerInit,
619
+ specificationsKey,
620
+ }
621
+ mainProcessSetupStates.set(ctx, setupState)
622
+ }
623
+ return setupState.setupPromise
624
+ }
625
+
626
+ function shouldUseNoWorkerInit (ctx, frameworkVersion, testSpecifications) {
627
+ return noWorkerInit.shouldUse(ctx, frameworkVersion, testSpecifications, {
628
+ hasVitestWorkerPoolTestSpecification,
629
+ isVitestWorkerPool,
630
+ })
631
+ }
632
+
633
+ function configureFlakyTestRetries (ctx, testSpecifications) {
634
+ if (!isFlakyTestRetriesEnabled || flakyTestRetriesCount <= 0) return
635
+
636
+ let configured = false
637
+ let includesUnnamedProject = false
638
+ const projectNames = []
639
+ for (const { config, projectName } of getVitestProjectConfigs(ctx, testSpecifications)) {
640
+ if (!config.retry) {
641
+ config.retry = flakyTestRetriesCount
642
+ configured = true
643
+ if (projectName) {
644
+ projectNames.push(projectName)
645
+ } else {
646
+ includesUnnamedProject = true
647
+ }
648
+ }
649
+ }
650
+
651
+ if (!configured) return
652
+
653
+ return {
654
+ includesUnnamedProject,
655
+ projectNames,
656
+ }
657
+ }
658
+
659
+ function getVitestProjectConfigs (ctx, testSpecifications) {
660
+ const entries = []
661
+
662
+ addTestSpecificationConfigs(entries, testSpecifications)
663
+ if (entries.length > 0) {
664
+ return entries
665
+ }
666
+
667
+ const selectedProjectNames = getSelectedProjectNames()
668
+ addSelectedInlineProjectConfigs(entries, safeConfig(ctx), selectedProjectNames)
669
+ addSelectedRuntimeProjectConfigs(entries, ctx?.projects, selectedProjectNames)
670
+ if (entries.length > 0) {
671
+ return entries
672
+ }
673
+
674
+ if (Array.isArray(ctx?.projects)) {
675
+ for (const project of ctx.projects) {
676
+ addConfig(entries, safeConfig(project), getProjectName(project))
677
+ }
678
+ if (entries.length > 0) {
679
+ return entries
680
+ }
681
+ }
682
+
683
+ addConfig(entries, safeConfig(ctx))
684
+ addConfig(entries, safeConfig(safeWorkspaceProject(ctx)))
685
+
686
+ return entries
687
+ }
688
+
689
+ function addTestSpecificationConfigs (entries, testSpecifications) {
690
+ if (!Array.isArray(testSpecifications)) return
691
+
692
+ for (const testSpecification of testSpecifications) {
693
+ const project = getTestSpecificationProject(testSpecification)
694
+ addConfig(entries, safeConfig(project), getProjectName(project))
695
+ }
696
+ }
697
+
698
+ function addSelectedInlineProjectConfigs (entries, rootConfig, selectedProjectNames) {
699
+ if (selectedProjectNames.length === 0 || !Array.isArray(rootConfig?.projects)) return
700
+
701
+ for (const project of rootConfig.projects) {
702
+ const config = getInlineProjectConfig(project)
703
+ const projectName = getProjectName(project)
704
+ if (selectedProjectNames.includes(projectName)) {
705
+ addConfig(entries, config, projectName)
706
+ }
707
+ }
708
+ }
709
+
710
+ function addSelectedRuntimeProjectConfigs (entries, projects, selectedProjectNames) {
711
+ if (selectedProjectNames.length === 0 || !Array.isArray(projects)) return
712
+
713
+ for (const project of projects) {
714
+ const projectName = getProjectName(project)
715
+ if (selectedProjectNames.includes(projectName)) {
716
+ addConfig(entries, safeConfig(project), projectName)
717
+ }
718
+ }
719
+ }
720
+
721
+ function getSelectedProjectNames () {
722
+ const names = []
723
+ for (let index = 0; index < process.argv.length; index++) {
724
+ const argument = process.argv[index]
725
+ if (argument === '--project' && process.argv[index + 1]) {
726
+ names.push(process.argv[index + 1])
727
+ index++
728
+ } else if (argument.startsWith('--project=')) {
729
+ names.push(argument.slice('--project='.length))
730
+ }
731
+ }
732
+ return names
733
+ }
734
+
735
+ function getInlineProjectConfig (project) {
736
+ return project?.test || project
737
+ }
738
+
739
+ function getProjectName (project) {
740
+ return normalizeProjectName(project?.name || project?.config?.name || project?.test?.name)
741
+ }
742
+
743
+ function normalizeProjectName (name) {
744
+ if (typeof name === 'string') return name
745
+
746
+ const label = name?.label
747
+ return typeof label === 'string' ? label : undefined
748
+ }
749
+
750
+ function addConfig (entries, config, projectName) {
751
+ if (config && !entries.some(entry => entry.config === config || (projectName && entry.projectName === projectName))) {
752
+ entries.push({ config, projectName })
753
+ }
754
+ }
755
+
756
+ function safeConfig (project) {
757
+ let config
758
+ try {
759
+ config = project?.config
760
+ } catch {}
761
+ return config
762
+ }
763
+
764
+ function safeWorkspaceProject (ctx) {
765
+ let project
766
+ try {
767
+ project = getWorkspaceProject(ctx)
768
+ } catch {}
769
+ return project
770
+ }
771
+
772
+ function getSortWrapper (sort, frameworkVersion) {
773
+ return async function () {
774
+ await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
775
+ return sort.apply(this, arguments)
776
+ }
777
+ }
778
+
779
+ function getFinishWrapper (exitOrClose) {
780
+ let isClosed = false
781
+ return async function () {
782
+ if (isClosed) { // needed because exit calls close
783
+ return exitOrClose.apply(this, arguments)
784
+ }
785
+ isClosed = true
786
+
787
+ if (!testSessionFinishCh.hasSubscribers) {
788
+ return exitOrClose.apply(this, arguments)
789
+ }
790
+
791
+ let onFinish
792
+
793
+ const flushPromise = new Promise(resolve => {
794
+ onFinish = resolve
795
+ })
796
+ const failedSuites = this.state.getFailedFilepaths()
797
+ let error
798
+ if (failedSuites.length) {
799
+ error = new Error(`Test suites failed: ${failedSuites.length}.`)
800
+ }
801
+
802
+ testSessionFinishCh.publish({
803
+ status: getSessionStatus(this.state),
804
+ testCodeCoverageLinesTotal,
805
+ error,
806
+ isEarlyFlakeDetectionEnabled,
807
+ isEarlyFlakeDetectionFaulty,
808
+ isTestManagementTestsEnabled,
809
+ vitestPool,
810
+ isVitestNoWorkerInitActive,
811
+ onFinish,
812
+ })
813
+
814
+ logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
815
+
816
+ await flushPromise
817
+
818
+ // If coverage was generated, publish coverage report channel for upload
819
+ if (coverageRootDir && codeCoverageReportCh.hasSubscribers) {
820
+ await new Promise((resolve) => {
821
+ codeCoverageReportCh.publish({ rootDir: coverageRootDir, onDone: resolve })
822
+ })
823
+ }
824
+
825
+ return exitOrClose.apply(this, arguments)
826
+ }
827
+ }
828
+
829
+ function getCliOrStartVitestWrapper (frameworkVersion) {
830
+ return function (oldCliOrStartVitest) {
831
+ return function (...args) {
832
+ if (!testSessionStartCh.hasSubscribers || isSessionStarted) {
833
+ return oldCliOrStartVitest.apply(this, args)
834
+ }
835
+ isSessionStarted = true
836
+ testSessionStartCh.publish({ command: getTestCommand(), frameworkVersion })
837
+ return oldCliOrStartVitest.apply(this, args)
838
+ }
839
+ }
840
+ }
841
+
842
+ function isForkPool (pool) {
843
+ return pool === 'forks' || pool === 'vmForks'
844
+ }
845
+
846
+ function isThreadPool (pool) {
847
+ return pool === 'threads' || pool === 'vmThreads'
848
+ }
849
+
850
+ function isVitestWorkerPool (pool) {
851
+ return isForkPool(pool) || isThreadPool(pool)
852
+ }
853
+
854
+ function getTestSpecificationProject (testSpecification) {
855
+ if (Array.isArray(testSpecification)) {
856
+ return testSpecification[0]
857
+ }
858
+ return testSpecification?.project
859
+ }
860
+
861
+ function getTestSpecificationOptions (testSpecification) {
862
+ if (Array.isArray(testSpecification)) {
863
+ return testSpecification[2]
864
+ }
865
+ return testSpecification
866
+ }
867
+
868
+ function getTestSpecificationPool (testSpecification) {
869
+ const options = getTestSpecificationOptions(testSpecification)
870
+ const project = getTestSpecificationProject(testSpecification)
871
+ return options?.pool || project?.config?.pool || project?.serializedConfig?.pool || project?.pool ||
872
+ testSpecification?.pool
873
+ }
874
+
875
+ function hasVitestWorkerPoolTestSpecification (testSpecifications) {
876
+ if (!Array.isArray(testSpecifications)) {
877
+ return false
878
+ }
879
+
880
+ for (const testSpecification of testSpecifications) {
881
+ if (isVitestWorkerPool(getTestSpecificationPool(testSpecification))) {
882
+ return true
883
+ }
884
+ }
885
+
886
+ return false
887
+ }
888
+
889
+ function shouldMarkVitestWorkerEnv (pool, testSpecifications, shouldSkipWorkerInit) {
890
+ if (!shouldSkipWorkerInit) {
891
+ return isVitestWorkerPool(pool) || hasVitestWorkerPoolTestSpecification(testSpecifications) ||
892
+ (!testSpecifications && pool === undefined)
893
+ }
894
+
895
+ return isVitestWorkerPool(pool) || pool === undefined || hasVitestWorkerPoolTestSpecification(testSpecifications)
896
+ }
897
+
898
+ function markVitestWorkerEnv (ctx, testSpecifications, shouldSkipWorkerInit = false) {
899
+ const config = ctx?.config
900
+ isVitestNoWorkerInitActive = shouldSkipWorkerInit
901
+ if (!config || !shouldMarkVitestWorkerEnv(config.pool, testSpecifications, shouldSkipWorkerInit)) {
902
+ return
903
+ }
904
+ config.env = getVitestWorkerEnv(config.env, shouldSkipWorkerInit)
905
+ }
906
+
907
+ function wrapVitestRunFiles (Vitest, frameworkVersion) {
908
+ if (!Vitest?.prototype?.runFiles) {
909
+ return
910
+ }
911
+
912
+ shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
913
+ const shouldSkipWorkerInit = await ensureMainProcessSetup(this, frameworkVersion, testSpecifications, true)
914
+ markVitestWorkerEnv(this, testSpecifications, shouldSkipWorkerInit)
915
+ return runFiles.apply(this, arguments)
916
+ })
917
+
918
+ if (Vitest.prototype.collectTests) {
919
+ shimmer.wrap(Vitest.prototype, 'collectTests', collectTests => function (testSpecifications) {
920
+ const shouldSkipWorkerInit = shouldUseNoWorkerInit(this, frameworkVersion, testSpecifications)
921
+ markVitestWorkerEnv(this, testSpecifications, shouldSkipWorkerInit)
922
+ return collectTests.apply(this, arguments)
923
+ })
924
+ }
925
+ }
926
+
927
+ function getTypecheckTaskStatus (task) {
928
+ const state = task.result?.state
929
+ if (state === 'fail') return 'fail'
930
+ if (state === 'skip' || task.mode === 'skip' || task.mode === 'todo') return 'skip'
931
+ return 'pass'
932
+ }
933
+
934
+ /**
935
+ * Return whether a typecheck suite name represents the synthetic file-level suite.
936
+ *
937
+ * @param {string|undefined} suiteName
938
+ * @param {string} testSuiteAbsolutePath
939
+ * @returns {boolean}
940
+ */
941
+ function isTypecheckFileSuiteName (suiteName, testSuiteAbsolutePath) {
942
+ if (!suiteName || !testSuiteAbsolutePath) return false
943
+
944
+ const normalizedSuiteName = path.normalize(suiteName).replaceAll('\\', '/')
945
+ const normalizedSuitePath = path.normalize(testSuiteAbsolutePath).replaceAll('\\', '/')
946
+
947
+ return normalizedSuitePath === normalizedSuiteName || normalizedSuitePath.endsWith(`/${normalizedSuiteName}`)
948
+ }
949
+
950
+ /**
951
+ * Return a typecheck test name with describe/suite prefixes and without the file-level suite prefix.
952
+ *
953
+ * @param {object} task
954
+ * @param {string} testSuiteAbsolutePath
955
+ * @returns {string}
956
+ */
957
+ function getTypecheckTestName (task, testSuiteAbsolutePath) {
958
+ let testName = task.name || task.fullTestName
959
+ let currentTask = task.suite
960
+
961
+ while (currentTask) {
962
+ if (currentTask.name && !isTypecheckFileSuiteName(currentTask.name, testSuiteAbsolutePath)) {
963
+ testName = `${currentTask.name} ${testName}`
964
+ }
965
+ currentTask = currentTask.suite
966
+ }
967
+
968
+ return testName
969
+ }
970
+
971
+ /**
972
+ * Return Test Optimization metadata prepared in Vitest's main process setup.
973
+ *
974
+ * @param {object|undefined} ctx
975
+ * @returns {{ testPropertiesByFilepath: object }}
976
+ */
977
+ function getMainProcessProvidedContext (ctx) {
978
+ try {
979
+ const workspaceProject = getWorkspaceProject(ctx)
980
+ const providedContext = workspaceProject.getProvidedContext?.() || workspaceProject._provided || {}
981
+
982
+ return {
983
+ testPropertiesByFilepath: providedContext._ddTestPropertiesByFilepath || {},
984
+ }
985
+ } catch {
986
+ return {
987
+ testPropertiesByFilepath: {},
988
+ }
989
+ }
990
+ }
991
+
992
+ /**
993
+ * Return the Vitest context that owns a Typechecker instance.
994
+ *
995
+ * @param {object} typechecker
996
+ * @returns {object|undefined}
997
+ */
998
+ function getTypecheckerVitestContext (typechecker) {
999
+ return typechecker.ctx || typechecker.project?.vitest
1000
+ }
1001
+
1002
+ /**
1003
+ * Apply Test Management result semantics to Vitest's returned typecheck task result.
1004
+ *
1005
+ * @param {object} task
1006
+ * @param {string} status
1007
+ * @param {{
1008
+ * isAttemptToFix: boolean,
1009
+ * isDisabled: boolean,
1010
+ * isQuarantined: boolean
1011
+ * }} testManagement
1012
+ */
1013
+ function updateTypecheckTaskResultForTestManagement (task, status, testManagement) {
1014
+ const { isAttemptToFix, isDisabled, isQuarantined } = testManagement
1015
+ if (isAttemptToFix || !task.result) return
1016
+
1017
+ if (isDisabled) {
1018
+ task.mode = 'skip'
1019
+ task.result.state = 'skip'
1020
+ task.result.errors = []
1021
+ return
1022
+ }
1023
+
1024
+ if (isQuarantined && status === 'fail') {
1025
+ task.result.state = 'pass'
1026
+ task.result.errors = []
1027
+ }
1028
+ }
1029
+
1030
+ /**
1031
+ * Recompute suite/file typecheck results after Test Management rewrites child test results.
1032
+ *
1033
+ * @param {object} task
1034
+ * @returns {string}
1035
+ */
1036
+ function updateTypecheckTaskTreeResult (task) {
1037
+ if (!Array.isArray(task.tasks)) return getTypecheckTaskStatus(task)
1038
+
1039
+ let hasPassedTest = false
1040
+ let hasSkippedTest = false
1041
+ for (const childTask of task.tasks) {
1042
+ const status = updateTypecheckTaskTreeResult(childTask)
1043
+ if (status === 'fail') {
1044
+ task.result = {
1045
+ ...task.result,
1046
+ state: 'fail',
1047
+ }
1048
+ return 'fail'
1049
+ }
1050
+ if (status === 'skip') {
1051
+ hasSkippedTest = true
1052
+ } else {
1053
+ hasPassedTest = true
1054
+ }
1055
+ }
1056
+
1057
+ if (task.result?.errors?.length) return 'fail'
1058
+ if (!hasPassedTest && !hasSkippedTest) return getTypecheckTaskStatus(task)
1059
+
1060
+ task.result = {
1061
+ ...task.result,
1062
+ state: hasPassedTest ? 'pass' : 'skip',
1063
+ errors: [],
1064
+ }
1065
+ return task.result.state
1066
+ }
1067
+
1068
+ /**
1069
+ * Recompute the aggregate typecheck result after Test Management rewrites file results.
1070
+ *
1071
+ * @param {{
1072
+ * files?: object[],
1073
+ * errors?: object[],
1074
+ * diagnostics?: object[],
1075
+ * sourceErrors?: object[],
1076
+ * state?: string
1077
+ * }} result
1078
+ * @returns {boolean}
1079
+ */
1080
+ function updateTypecheckResult (result) {
1081
+ if (result.sourceErrors?.length) return false
1082
+
1083
+ let hasPassedFile = false
1084
+ let hasSkippedFile = false
1085
+ for (const file of result.files) {
1086
+ const status = getTypecheckTaskStatus(file)
1087
+ if (status === 'fail') return false
1088
+ if (status === 'skip') {
1089
+ hasSkippedFile = true
1090
+ } else {
1091
+ hasPassedFile = true
1092
+ }
1093
+ }
1094
+
1095
+ if (!hasPassedFile && !hasSkippedFile) return false
1096
+
1097
+ result.state = hasPassedFile ? 'pass' : 'skip'
1098
+ result.errors = []
1099
+ result.diagnostics = []
1100
+ result.sourceErrors = []
1101
+ return true
1102
+ }
1103
+
1104
+ /**
1105
+ * Clear Vitest's typechecker exit code after all typecheck failures were handled by Test Management.
1106
+ *
1107
+ * @param {{ process?: { exitCode?: number|null } }} typechecker
1108
+ */
1109
+ function clearTypecheckerExitCode (typechecker) {
1110
+ if (typechecker?.process?.exitCode == null) return
1111
+
1112
+ typechecker.process.exitCode = 0
1113
+ }
1114
+
1115
+ function reportTypecheckTest (task, testSuiteAbsolutePath, providedContext) {
1116
+ const testName = getTypecheckTestName(task, testSuiteAbsolutePath)
1117
+ const testProperties = getVitestTestProperties(providedContext, testSuiteAbsolutePath, testName)
1118
+ const isAttemptToFix = testProperties.isAttemptToFix === true
1119
+ const isDisabled = testProperties.isDisabled === true
1120
+ const isQuarantined = testProperties.isQuarantined === true
1121
+ const isModified = testProperties.isModified === true
1122
+ const isSkippedByTestManagement = !isAttemptToFix && isDisabled
1123
+ const status = getTypecheckTaskStatus(task)
1124
+
1125
+ if (status === 'skip' || isSkippedByTestManagement) {
1126
+ testSkipCh.publish({
1127
+ testName,
1128
+ testSuiteAbsolutePath,
1129
+ isNew: testProperties.isNew,
1130
+ isDisabled,
1131
+ })
1132
+ updateTypecheckTaskResultForTestManagement(task, status, { isAttemptToFix, isDisabled, isQuarantined })
1133
+ return
1134
+ }
1135
+
1136
+ const ctx = {
1137
+ testName,
1138
+ testSuiteAbsolutePath,
1139
+ isRetry: false,
1140
+ isNew: testProperties.isNew,
1141
+ hasDynamicName: false,
1142
+ mightHitProbe: false,
1143
+ isAttemptToFix,
1144
+ isDisabled,
1145
+ isQuarantined,
1146
+ isModified,
1147
+ }
1148
+ testStartCh.runStores(ctx, () => {})
1149
+
1150
+ const finalStatus = !isAttemptToFix && isQuarantined ? 'skip' : undefined
1151
+ if (status === 'fail') {
1152
+ testErrorCh.publish({
1153
+ error: task.result?.errors?.[0],
1154
+ finalStatus,
1155
+ ...ctx.currentStore,
1156
+ })
1157
+ } else {
1158
+ testPassCh.publish({
1159
+ task,
1160
+ finalStatus,
1161
+ ...ctx.currentStore,
1162
+ })
1163
+ }
1164
+ updateTypecheckTaskResultForTestManagement(task, status, { isAttemptToFix, isDisabled, isQuarantined })
1165
+ }
1166
+
1167
+ async function reportTypecheckFile (file, sessionConfiguration, frameworkVersion, providedContext) {
1168
+ const testSuiteAbsolutePath = file.filepath
1169
+ const testSuiteCtx = {
1170
+ testSuiteAbsolutePath,
1171
+ frameworkVersion,
1172
+ testSessionId: sessionConfiguration.testSessionId,
1173
+ testModuleId: sessionConfiguration.testModuleId,
1174
+ testCommand: sessionConfiguration.testCommand,
1175
+ repositoryRoot: sessionConfiguration.repositoryRoot,
1176
+ codeOwnersEntries: sessionConfiguration.codeOwnersEntries,
1177
+ }
1178
+ testSuiteStartCh.runStores(testSuiteCtx, () => {})
1179
+
1180
+ for (const task of getTypeTasks(file.tasks)) {
1181
+ reportTypecheckTest(task, testSuiteAbsolutePath, providedContext)
1182
+ }
1183
+ updateTypecheckTaskTreeResult(file)
1184
+
1185
+ const testSuiteError = file.result?.errors?.[0]
1186
+ if (testSuiteError) {
1187
+ testSuiteCtx.error = testSuiteError
1188
+ testSuiteErrorCh.runStores(testSuiteCtx, () => {})
1189
+ }
1190
+
1191
+ let onFinish
1192
+ const onFinishPromise = new Promise(resolve => {
1193
+ onFinish = resolve
1194
+ })
1195
+ testSuiteFinishCh.publish({
1196
+ status: getTypecheckTaskStatus(file),
1197
+ onFinish,
1198
+ ...testSuiteCtx.currentStore,
1199
+ })
1200
+ await onFinishPromise
1201
+ }
1202
+
1203
+ async function reportTypecheckResults (result, frameworkVersion, ctx, typechecker) {
1204
+ if (!testSuiteFinishCh.hasSubscribers) return
1205
+ if (!Array.isArray(result?.files)) return
1206
+
1207
+ if (ctx) {
1208
+ await ensureMainProcessSetup(ctx, frameworkVersion, result.files)
1209
+ }
1210
+ const providedContext = getMainProcessProvidedContext(ctx)
1211
+ const sessionConfiguration = testSessionConfigurationCh.hasSubscribers
1212
+ ? await getChannelPromise(testSessionConfigurationCh, frameworkVersion)
1213
+ : {}
1214
+
1215
+ await Promise.all(result.files.map(file => reportTypecheckFile(
1216
+ file,
1217
+ sessionConfiguration,
1218
+ frameworkVersion,
1219
+ providedContext
1220
+ )))
1221
+ if (updateTypecheckResult(result)) {
1222
+ clearTypecheckerExitCode(typechecker)
1223
+ }
1224
+ }
1225
+
1226
+ function wrapTypechecker (Typechecker, frameworkVersion) {
1227
+ if (!Typechecker?.prototype?.prepareResults) return
1228
+
1229
+ shimmer.wrap(Typechecker.prototype, 'prepareResults', prepareResults => async function () {
1230
+ const result = await prepareResults.apply(this, arguments)
1231
+ await reportTypecheckResults(result, frameworkVersion, getTypecheckerVitestContext(this), this)
1232
+ return result
1233
+ })
1234
+ }
1235
+
1236
+ function getTypecheckerWrapper (vitestPackage, frameworkVersion) {
1237
+ const typechecker = getTypecheckerExport(vitestPackage)
1238
+ if (typechecker) {
1239
+ wrapTypechecker(typechecker.value, frameworkVersion)
1240
+ }
1241
+ return vitestPackage
1242
+ }
1243
+
1244
+ function getCreateCliWrapper (vitestPackage, frameworkVersion) {
1245
+ const createCliExport = findExportByName(vitestPackage, 'createCLI')
1246
+ if (!createCliExport) {
1247
+ return vitestPackage
1248
+ }
1249
+ shimmer.wrap(vitestPackage, createCliExport.key, getCliOrStartVitestWrapper(frameworkVersion))
1250
+
1251
+ return vitestPackage
1252
+ }
1253
+
1254
+ function threadHandler (thread) {
1255
+ const { runtime } = thread
1256
+ let workerProcess
1257
+ if (runtime === 'child_process') {
1258
+ vitestPool = 'child_process'
1259
+ workerProcess = thread.process
1260
+ } else if (runtime === 'worker_threads') {
1261
+ vitestPool = 'worker_threads'
1262
+ workerProcess = thread.thread
1263
+ } else {
1264
+ vitestPool = 'unknown'
1265
+ }
1266
+ if (!workerProcess) {
1267
+ log.error('Vitest error: could not get process or thread from TinyPool#run')
1268
+ return
1269
+ }
1270
+
1271
+ if (workerProcesses.has(workerProcess)) {
1272
+ return
1273
+ }
1274
+ workerProcesses.add(workerProcess)
1275
+ workerProcess.on('message', (message) => {
1276
+ if (message.__tinypool_worker_message__ && message.data) {
1277
+ handleWorkerReport(message.interprocessCode, message.data)
1278
+ }
1279
+ })
1280
+ }
1281
+
1282
+ function isVitestTinypoolOptions (options) {
1283
+ if (options?.env?.VITEST !== 'true' || typeof options.filename !== 'string') return false
1284
+
1285
+ let filename = options.filename
1286
+ if (filename.startsWith('file:')) {
1287
+ try {
1288
+ filename = fileURLToPath(filename)
1289
+ } catch {
1290
+ return false
1291
+ }
1292
+ }
1293
+
1294
+ const workerPath = path.normalize(filename)
1295
+ const workerDir = path.dirname(workerPath)
1296
+ const packageDir = path.dirname(workerDir)
1297
+
1298
+ return path.basename(workerPath) === 'worker.js' &&
1299
+ path.basename(workerDir) === 'dist' &&
1300
+ path.basename(packageDir) === 'vitest'
1301
+ }
1302
+
1303
+ function markVitestTinypoolOptions (options) {
1304
+ if (!isVitestTinypoolOptions(options)) return
1305
+
1306
+ options.env = getVitestWorkerEnv(options.env, isVitestNoWorkerInitActive)
1307
+ }
1308
+
1309
+ function getVitestWorkerEnv (env = {}, shouldSkipWorkerInit = false) {
1310
+ return noWorkerInit.configureWorkerEnv({
1311
+ ...env,
1312
+ DD_VITEST_WORKER: '1',
1313
+ }, shouldSkipWorkerInit)
1314
+ }
1315
+
1316
+ function wrapTinyPoolRun (TinyPool) {
1317
+ if (!TinyPool?.prototype?.run) return
1318
+
1319
+ shimmer.wrap(TinyPool.prototype, 'run', run => async function () {
1320
+ // We have to do this before and after because the threads list gets recycled, that is, the processes are re-created
1321
+ // eslint-disable-next-line unicorn/no-array-for-each
1322
+ this.threads.forEach(threadHandler)
1323
+ const runResult = await run.apply(this, arguments)
1324
+ // eslint-disable-next-line unicorn/no-array-for-each
1325
+ this.threads.forEach(threadHandler)
1326
+ return runResult
1327
+ })
1328
+ }
1329
+
1330
+ function wrapTinyPoolClass (TinyPool) {
1331
+ if (typeof TinyPool !== 'function') return TinyPool
1332
+
1333
+ const wrappedTinyPool = tinyPoolClassWrappers.get(TinyPool)
1334
+ if (wrappedTinyPool) return wrappedTinyPool
1335
+
1336
+ class DatadogTinyPool extends TinyPool {
1337
+ constructor (options) {
1338
+ markVitestTinypoolOptions(options)
1339
+ super(options)
1340
+ }
1341
+ }
1342
+
1343
+ tinyPoolClassWrappers.set(TinyPool, DatadogTinyPool)
1344
+ wrapTinyPoolRun(DatadogTinyPool)
1345
+
1346
+ return DatadogTinyPool
1347
+ }
1348
+
1349
+ function wrapTinyPool (TinyPool) {
1350
+ if (typeof TinyPool === 'function') {
1351
+ return wrapTinyPoolClass(TinyPool)
1352
+ }
1353
+
1354
+ const defaultTinyPool = wrapTinyPoolClass(TinyPool?.default)
1355
+ if (defaultTinyPool) {
1356
+ TinyPool.default = defaultTinyPool
1357
+ }
1358
+
1359
+ const namedTinyPool = TinyPool?.Tinypool === TinyPool?.default
1360
+ ? defaultTinyPool
1361
+ : wrapTinyPoolClass(TinyPool?.Tinypool)
1362
+ if (namedTinyPool) {
1363
+ TinyPool.Tinypool = namedTinyPool
1364
+ }
1365
+
1366
+ return TinyPool
1367
+ }
1368
+
1369
+ function getWrappedOn (on) {
1370
+ return function (event, callback) {
1371
+ if (event !== 'message') {
1372
+ return on.apply(this, arguments)
1373
+ }
1374
+ // `arguments[1]` is the callback function, which
1375
+ // we modify to intercept our messages to not interfere
1376
+ // with vitest's own messages
1377
+ arguments[1] = shimmer.wrapFunction(callback, callback => function (message) {
1378
+ if (message.type !== 'Buffer' && Array.isArray(message)) {
1379
+ const [interprocessCode, data] = message
1380
+ if (handleWorkerReport(interprocessCode, data)) {
1381
+ // If we execute the callback vitest crashes, as the message is not supported
1382
+ return
1383
+ }
1384
+ }
1385
+ return callback.apply(this, arguments)
1386
+ })
1387
+ return on.apply(this, arguments)
1388
+ }
1389
+ }
1390
+
1391
+ function handleWorkerReport (interprocessCode, data) {
1392
+ if (interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
1393
+ collectTestOptimizationSummariesFromTraces(data, {
1394
+ newTestsWithDynamicNames,
1395
+ attemptToFixExecutions,
1396
+ })
1397
+ workerReportTraceCh.publish(data)
1398
+ return true
1399
+ }
1400
+
1401
+ if (interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
1402
+ workerReportLogsCh.publish(data)
1403
+ return true
1404
+ }
1405
+
1406
+ return false
1407
+ }
1408
+
1409
+ function wrapMessagePortOn () {
1410
+ if (isMessagePortWrapped) return
1411
+
1412
+ isMessagePortWrapped = true
1413
+ shimmer.wrap(MessagePort.prototype, 'on', getWrappedOn)
1414
+ shimmer.wrap(MessagePort.prototype, 'addListener', getWrappedOn)
1415
+ }
1416
+
1417
+ function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
1418
+ if (!isCliApiPackage(cliApiPackage)) {
1419
+ return cliApiPackage
1420
+ }
1421
+ const startVitestExport = findExportByName(cliApiPackage, 'startVitest')
1422
+ shimmer.wrap(cliApiPackage, startVitestExport.key, getCliOrStartVitestWrapper(frameworkVersion))
1423
+ wrapMessagePortOn()
1424
+
1425
+ const vitest = getVitestExport(cliApiPackage)
1426
+ if (vitest) {
1427
+ wrapVitestRunFiles(vitest.value, frameworkVersion)
1428
+ }
1429
+
1430
+ const forksPoolWorker = getForksPoolWorkerExport(cliApiPackage)
1431
+ if (forksPoolWorker) {
1432
+ // function is async
1433
+ shimmer.wrap(forksPoolWorker.value.prototype, 'start', start => function (...args) {
1434
+ vitestPool = 'child_process'
1435
+ this.env = getVitestWorkerEnv(this.env, isVitestNoWorkerInitActive)
1436
+
1437
+ return start.apply(this, args)
1438
+ })
1439
+ shimmer.wrap(forksPoolWorker.value.prototype, 'on', getWrappedOn)
1440
+ }
1441
+
1442
+ const threadsPoolWorker = getThreadsPoolWorkerExport(cliApiPackage)
1443
+ if (threadsPoolWorker) {
1444
+ // function is async
1445
+ shimmer.wrap(threadsPoolWorker.value.prototype, 'start', start => function (...args) {
1446
+ vitestPool = 'worker_threads'
1447
+ this.env = getVitestWorkerEnv(this.env, isVitestNoWorkerInitActive)
1448
+ return start.apply(this, args)
1449
+ })
1450
+ shimmer.wrap(threadsPoolWorker.value.prototype, 'on', getWrappedOn)
1451
+ }
1452
+ return cliApiPackage
1453
+ }
1454
+
1455
+ addHook({
1456
+ name: 'tinypool',
1457
+ // version from tinypool@0.8 was used in vitest@1.6.0
1458
+ versions: ['>=0.8.0'],
1459
+ }, (TinyPool) => {
1460
+ return wrapTinyPool(TinyPool)
1461
+ })
1462
+
1463
+ // There are multiple index* files across different versions of vitest,
1464
+ // so we check for the existence of BaseSequencer to determine if we are in the right file
1465
+ addHook({
1466
+ name: 'vitest',
1467
+ versions: ['>=1.6.0 <2.0.0'],
1468
+ filePattern: 'dist/vendor/index.*',
1469
+ }, (vitestPackage) => {
1470
+ if (isReporterPackage(vitestPackage)) {
1471
+ shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
1472
+ }
1473
+
1474
+ return vitestPackage
1475
+ })
1476
+
1477
+ addHook({
1478
+ name: 'vitest',
1479
+ versions: ['>=2.0.0 <2.0.5'],
1480
+ filePattern: 'dist/vendor/index.*',
1481
+ }, (vitestPackage) => {
1482
+ if (isReporterPackageNew(vitestPackage)) {
1483
+ shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
1484
+ }
1485
+
1486
+ return vitestPackage
1487
+ })
1488
+
1489
+ addHook({
1490
+ name: 'vitest',
1491
+ versions: ['>=2.0.5 <2.1.0'],
1492
+ filePattern: 'dist/chunks/index.*',
1493
+ }, (vitestPackage) => {
1494
+ if (isReporterPackageNewest(vitestPackage)) {
1495
+ shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
1496
+ }
1497
+
1498
+ return vitestPackage
1499
+ })
1500
+
1501
+ addHook({
1502
+ name: 'vitest',
1503
+ versions: ['>=3.2.0 <4.0.0'],
1504
+ filePattern: 'dist/chunks/typechecker.*',
1505
+ }, getTypecheckerWrapper)
1506
+
1507
+ addHook({
1508
+ name: 'vitest',
1509
+ versions: ['>=4.0.0'],
1510
+ filePattern: 'dist/chunks/index.*',
1511
+ }, getTypecheckerWrapper)
1512
+
1513
+ addHook({
1514
+ name: 'vitest',
1515
+ versions: ['>=2.1.0 <3.0.0'],
1516
+ filePattern: 'dist/chunks/RandomSequencer.*',
1517
+ }, (randomSequencerPackage) => {
1518
+ shimmer.wrap(randomSequencerPackage.B.prototype, 'sort', getSortWrapper)
1519
+ return randomSequencerPackage
1520
+ })
1521
+
1522
+ addHook({
1523
+ name: 'vitest',
1524
+ versions: ['>=3.0.9'],
1525
+ filePattern: 'dist/chunks/coverage.*',
1526
+ }, (coveragePackage) => {
1527
+ const baseSequencer = getBaseSequencerExport(coveragePackage)
1528
+ if (baseSequencer) {
1529
+ shimmer.wrap(baseSequencer.value.prototype, 'sort', getSortWrapper)
1530
+ }
1531
+ return coveragePackage
1532
+ })
1533
+
1534
+ addHook({
1535
+ name: 'vitest',
1536
+ versions: ['>=3.0.0 <3.0.9'],
1537
+ filePattern: 'dist/chunks/resolveConfig.*',
1538
+ }, (resolveConfigPackage) => {
1539
+ shimmer.wrap(resolveConfigPackage.B.prototype, 'sort', getSortWrapper)
1540
+ return resolveConfigPackage
1541
+ })
1542
+
1543
+ // Can't specify file because compiled vitest includes hashes in their files
1544
+ // Following 3 wrappers are for test session start
1545
+ addHook({
1546
+ name: 'vitest',
1547
+ versions: ['>=1.6.0 <2.0.5'],
1548
+ filePattern: 'dist/vendor/cac.*',
1549
+ }, getCreateCliWrapper)
1550
+
1551
+ addHook({
1552
+ name: 'vitest',
1553
+ versions: ['>=2.0.5'],
1554
+ filePattern: 'dist/chunks/cac.*',
1555
+ }, getCreateCliWrapper)
1556
+
1557
+ addHook({
1558
+ name: 'vitest',
1559
+ versions: ['>=1.6.0 <2.0.5'],
1560
+ filePattern: 'dist/vendor/cli-api.*',
1561
+ }, getStartVitestWrapper)
1562
+
1563
+ addHook({
1564
+ name: 'vitest',
1565
+ versions: ['>=2.0.5'],
1566
+ filePattern: 'dist/chunks/cli-api.*',
1567
+ }, getStartVitestWrapper)