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