dd-trace 6.12.0 → 6.14.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 (251) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  8. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  9. package/ci/test-optimization-validation/package-check.js +1 -1
  10. package/ci/test-optimization-validation/report-writer.js +7 -5
  11. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  12. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  13. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  14. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  15. package/index.d.ts +127 -26
  16. package/package.json +22 -19
  17. package/packages/datadog-esbuild/index.js +3 -1
  18. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  19. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  20. package/packages/datadog-instrumentations/src/fs.js +24 -20
  21. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  22. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  23. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  24. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  25. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  26. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  34. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  35. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
  36. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
  37. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  38. package/packages/datadog-instrumentations/src/jest.js +165 -38
  39. package/packages/datadog-instrumentations/src/knex.js +76 -0
  40. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  41. package/packages/datadog-instrumentations/src/mariadb.js +13 -0
  42. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  43. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  44. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  45. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  46. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  47. package/packages/datadog-instrumentations/src/net.js +14 -17
  48. package/packages/datadog-instrumentations/src/next.js +212 -62
  49. package/packages/datadog-instrumentations/src/openai.js +42 -40
  50. package/packages/datadog-instrumentations/src/pg.js +15 -0
  51. package/packages/datadog-instrumentations/src/pino.js +15 -4
  52. package/packages/datadog-instrumentations/src/playwright.js +137 -27
  53. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  54. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  55. package/packages/datadog-instrumentations/src/undici.js +53 -4
  56. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
  57. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  58. package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
  59. package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
  60. package/packages/datadog-instrumentations/src/winston.js +6 -3
  61. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  62. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  63. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  64. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  65. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  66. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  67. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  68. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  69. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  70. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  71. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  72. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  73. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  74. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  75. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  76. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  77. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  78. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  79. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  80. package/packages/datadog-plugin-http/src/client.js +2 -15
  81. package/packages/datadog-plugin-http2/src/client.js +2 -15
  82. package/packages/datadog-plugin-jest/src/index.js +0 -6
  83. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  84. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  85. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  86. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  87. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  88. package/packages/datadog-plugin-next/src/index.js +63 -29
  89. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  90. package/packages/datadog-plugin-playwright/src/index.js +176 -28
  91. package/packages/datadog-plugin-undici/src/index.js +29 -56
  92. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  93. package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
  94. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  95. package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
  96. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  97. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  98. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  99. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  100. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  101. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  102. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  103. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  104. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  107. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  108. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  109. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  110. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  111. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  112. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  113. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  114. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  115. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  116. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  117. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
  118. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  119. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
  122. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  124. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  125. package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
  126. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  127. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  128. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  129. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  130. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  131. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  132. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  133. package/packages/dd-trace/src/config/index.js +75 -14
  134. package/packages/dd-trace/src/config/parsers.js +11 -1
  135. package/packages/dd-trace/src/config/stable.js +3 -3
  136. package/packages/dd-trace/src/config/supported-configurations.json +47 -2
  137. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  138. package/packages/dd-trace/src/debugger/config.js +12 -1
  139. package/packages/dd-trace/src/debugger/constants.js +1 -0
  140. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  141. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  142. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
  143. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  144. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  145. package/packages/dd-trace/src/debugger/index.js +23 -3
  146. package/packages/dd-trace/src/dogstatsd.js +532 -71
  147. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  148. package/packages/dd-trace/src/encode/0.4.js +5 -5
  149. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  150. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  151. package/packages/dd-trace/src/exporters/agent/index.js +24 -3
  152. package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
  153. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  154. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  155. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  156. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  157. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  158. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  159. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  160. package/packages/dd-trace/src/exporters/common/writer.js +31 -7
  161. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  162. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  163. package/packages/dd-trace/src/flush.js +105 -0
  164. package/packages/dd-trace/src/id.js +14 -0
  165. package/packages/dd-trace/src/index.js +5 -2
  166. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
  167. package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
  168. package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
  169. package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
  170. package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
  171. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  172. package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
  173. package/packages/dd-trace/src/llmobs/index.js +111 -48
  174. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  175. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  176. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  177. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  178. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  180. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  181. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  182. package/packages/dd-trace/src/llmobs/util.js +0 -33
  183. package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
  184. package/packages/dd-trace/src/log/index.js +7 -1
  185. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
  186. package/packages/dd-trace/src/noop/span.js +5 -0
  187. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  188. package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
  189. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  190. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  191. package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
  192. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  193. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  194. package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
  195. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  196. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  199. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  200. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  201. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  202. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  203. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  204. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  205. package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
  206. package/packages/dd-trace/src/opentracing/span.js +28 -0
  207. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  208. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  209. package/packages/dd-trace/src/plugin_manager.js +2 -0
  210. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  211. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  212. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  213. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  214. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  215. package/packages/dd-trace/src/plugins/util/test.js +157 -41
  216. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  217. package/packages/dd-trace/src/plugins/util/web.js +6 -74
  218. package/packages/dd-trace/src/profiling/config.js +1 -0
  219. package/packages/dd-trace/src/profiling/oom.js +11 -5
  220. package/packages/dd-trace/src/profiling/profiler.js +30 -13
  221. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  222. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  223. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  224. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  225. package/packages/dd-trace/src/proxy.js +108 -10
  226. package/packages/dd-trace/src/remote_config/index.js +319 -101
  227. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  228. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  229. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  230. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
  231. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
  232. package/packages/dd-trace/src/sampling_rule.js +36 -9
  233. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
  234. package/packages/dd-trace/src/serverless/vercel.js +155 -0
  235. package/packages/dd-trace/src/serverless.js +45 -21
  236. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  237. package/packages/dd-trace/src/span_stats.js +64 -31
  238. package/packages/dd-trace/src/startup-log.js +3 -2
  239. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  240. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  241. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  242. package/packages/dd-trace/src/tracer.js +26 -5
  243. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  244. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  245. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  246. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  247. package/vendor/dist/protobufjs/index.js +1 -1
  248. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  249. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  250. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
  251. package/packages/dd-trace/src/encode/agentless-json.js +0 -209
@@ -59,6 +59,7 @@ const testSuiteStartCh = channel('ci:playwright:test-suite:start')
59
59
  const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
60
60
 
61
61
  const workerReportCh = channel('ci:playwright:worker:report')
62
+ const logSubmissionFlushCh = channel('ci:log-submission:flush')
62
63
  const workerReportTelemetryCh = channel('ci:playwright:worker-report:telemetry')
63
64
  const testPageGotoCh = channel('ci:playwright:test:page-goto')
64
65
 
@@ -69,6 +70,7 @@ const createRootSuiteCh = tracingChannel('orchestrion:playwright:createRootSuite
69
70
  const artifactsRecorderScreenshotPathCh =
70
71
  tracingChannel('orchestrion:playwright:ArtifactsRecorder_createScreenshotAttachmentPath')
71
72
  const snapshotRecorderScreenshotPathCh = tracingChannel('orchestrion:playwright:SnapshotRecorder_createAttachmentPath')
73
+ const saveAutomaticVideoCh = tracingChannel('orchestrion:playwright:saveAutomaticVideo')
72
74
  const pageGotoCh = tracingChannel('orchestrion:playwright-core:Page_goto')
73
75
 
74
76
  const testToCtx = new WeakMap()
@@ -82,6 +84,8 @@ const RUM_FLUSH_WAIT_TIME = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WA
82
84
  const DD_PROPERTIES_TIMEOUT = 5000
83
85
  const isFailureScreenshotUploadEnabled =
84
86
  getValueFromEnvSources('DD_TEST_FAILURE_SCREENSHOTS_ENABLED') === true
87
+ const isFailureVideoUploadEnabled =
88
+ getValueFromEnvSources('DD_TEST_FAILURE_VIDEOS_ENABLED') === true
85
89
 
86
90
  let applyRepeatEachIndex = null
87
91
  let reporterError
@@ -90,7 +94,7 @@ let hasReporterError = false
90
94
  let startedSuites = []
91
95
 
92
96
  // Browser-side callbacks live in a coverage-excluded file so coverage counters can't reach chromium.
93
- const { detectRum, stopRumSession } = require('./playwright-browser-scripts')
97
+ const { detectRum, stopRumSession } = require('./rum-browser-scripts')
94
98
 
95
99
  const STATUS_TO_TEST_STATUS = {
96
100
  passed: 'pass',
@@ -133,18 +137,22 @@ let rootDir = ''
133
137
  let sessionProjects = []
134
138
 
135
139
  const MINIMUM_SUPPORTED_VERSION_RANGE_EFD = '>=1.38.0' // TODO: remove this once we drop support for v5
140
+ const MINIMUM_FAILURE_VIDEO_UPLOAD_VERSION_RANGE = '>=1.38.0'
136
141
  const EFD_RETRY_COUNT_REQUEST = 'ddEfdRetryCountRequest'
137
142
  const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
138
143
  const DD_PROPERTIES_REQUEST = 'ddPropertiesRequest'
139
144
  const DD_PROPERTIES_RESPONSE = 'ddProperties'
140
145
  const kDdPlaywrightDisabledTestIds = Symbol('ddPlaywrightDisabledTestIds')
141
146
  const kDdPlaywrightFailureScreenshots = Symbol('ddPlaywrightFailureScreenshots')
147
+ const kDdPlaywrightFailureVideos = Symbol('ddPlaywrightFailureVideos')
142
148
  const kDdPlaywrightReporterConfigured = Symbol('ddPlaywrightReporterConfigured')
143
149
  const kDdPlaywrightWorkerHostInstrumented = Symbol('ddPlaywrightWorkerHostInstrumented')
144
150
  const kDdPlaywrightWorkerInstrumented = Symbol('ddPlaywrightWorkerInstrumented')
145
151
  const instrumentedPlaywrightReporters = new WeakSet()
146
152
  const PLAYWRIGHT_FAILURE_SCREENSHOT_PATH_RE = /(?:^|[\\/])test-failed-\d+\.png$/
153
+ const PLAYWRIGHT_FAILURE_VIDEO_PATH_RE = /(?:^|[\\/])video(?:-\d+)?\.webm$/
147
154
  const automaticFailureScreenshotPaths = new Set()
155
+ const automaticFailureVideoPaths = new Set()
148
156
 
149
157
  /**
150
158
  * Returns whether Playwright's internal screenshot recorder created an attachment.
@@ -156,6 +164,16 @@ function isAutomaticFailureScreenshotAttachment (attachment) {
156
164
  return typeof attachment?.path === 'string' && automaticFailureScreenshotPaths.delete(attachment.path)
157
165
  }
158
166
 
167
+ /**
168
+ * Returns whether Playwright's internal video recorder created an attachment.
169
+ *
170
+ * @param {object} attachment - Playwright attachment payload
171
+ * @returns {boolean}
172
+ */
173
+ function isAutomaticFailureVideoAttachment (attachment) {
174
+ return typeof attachment?.path === 'string' && automaticFailureVideoPaths.delete(attachment.path)
175
+ }
176
+
159
177
  function isValidKnownTests (receivedKnownTests) {
160
178
  return !!receivedKnownTests.playwright
161
179
  }
@@ -552,6 +570,21 @@ function isFailureScreenshotCaptureEnabled (projects) {
552
570
  return false
553
571
  }
554
572
 
573
+ /**
574
+ * Returns whether at least one Playwright project records videos that can be retained for failures.
575
+ *
576
+ * @param {Array<object>} projects - Playwright projects with resolved use options
577
+ * @returns {boolean} Whether video capture is enabled
578
+ */
579
+ function isFailureVideoCaptureEnabled (projects) {
580
+ for (const project of projects) {
581
+ const video = project.use?.video
582
+ const mode = typeof video === 'object' && video !== null ? video.mode : video
583
+ if (mode === 'on' || mode === 'retain-on-failure') return true
584
+ }
585
+ return false
586
+ }
587
+
555
588
  function getProjectsFromDispatcher (dispatcher) {
556
589
  const bundledConfig = dispatcher._testRun?.config?.config?.projects
557
590
  if (bundledConfig) {
@@ -613,7 +646,15 @@ function getTestSuiteError (testSuiteAbsolutePath) {
613
646
  if (errors.length === 1) {
614
647
  return errors[0]
615
648
  }
616
- return new Error(`${errors.length} errors in this test suite:\n${errors.map(e => e.message).join('\n------\n')}`)
649
+ let errorMessages = ''
650
+ let isFirstError = true
651
+ for (const error of errors) {
652
+ if (!isFirstError) errorMessages += '\n------\n'
653
+ errorMessages += error.message || error.value || ''
654
+ isFirstError = false
655
+ }
656
+
657
+ return new Error(`${errors.length} errors in this test suite:\n${errorMessages}`)
617
658
  }
618
659
 
619
660
  function getTestByTestId (dispatcher, testId) {
@@ -1085,7 +1126,8 @@ function onDispatcherCreateWorker (dispatcher, worker) {
1085
1126
 
1086
1127
  const projects = getProjectsFromDispatcher(dispatcher)
1087
1128
  sessionProjects = projects
1088
- const automaticFailureScreenshotPathsByTestId = new Map()
1129
+ const automaticFailureScreenshotsByTestId = new Map()
1130
+ const failureVideosByTestId = new Map()
1089
1131
 
1090
1132
  if (disabledTestIds.size && !worker[kDdPlaywrightWorkerHostInstrumented] &&
1091
1133
  typeof worker.runTestGroup === 'function') {
@@ -1110,19 +1152,29 @@ function onDispatcherCreateWorker (dispatcher, worker) {
1110
1152
  const test = getTestByTestId(dispatcher, testId)
1111
1153
  if (!test) return
1112
1154
 
1155
+ automaticFailureScreenshotsByTestId.clear()
1156
+ failureVideosByTestId.clear()
1113
1157
  const browser = getBrowserNameFromProjects(projects, test)
1114
1158
  const shouldCreateTestSpan = test.expectedStatus === 'skipped'
1115
1159
  testBeginHandler(test, browser, shouldCreateTestSpan)
1116
1160
  })
1117
- worker.on('attach', ({ testId, path, _ddIsAutomaticFailureScreenshot }) => {
1118
- if (!_ddIsAutomaticFailureScreenshot) return
1119
-
1120
- let screenshotPaths = automaticFailureScreenshotPathsByTestId.get(testId)
1121
- if (!screenshotPaths) {
1122
- screenshotPaths = new Set()
1123
- automaticFailureScreenshotPathsByTestId.set(testId, screenshotPaths)
1161
+ worker.on('attach', (attachment) => {
1162
+ const { testId, _ddIsAutomaticFailureScreenshot, _ddIsAutomaticFailureVideo } = attachment
1163
+ if (_ddIsAutomaticFailureScreenshot) {
1164
+ let screenshots = automaticFailureScreenshotsByTestId.get(testId)
1165
+ if (!screenshots) {
1166
+ screenshots = []
1167
+ automaticFailureScreenshotsByTestId.set(testId, screenshots)
1168
+ }
1169
+ screenshots.push(attachment)
1170
+ } else if (isFailureVideoUploadEnabled && _ddIsAutomaticFailureVideo) {
1171
+ let videos = failureVideosByTestId.get(testId)
1172
+ if (!videos) {
1173
+ videos = []
1174
+ failureVideosByTestId.set(testId, videos)
1175
+ }
1176
+ videos.push(attachment)
1124
1177
  }
1125
- screenshotPaths.add(path)
1126
1178
  })
1127
1179
  worker.on('testEnd', ({ testId, status, errors, annotations }) => {
1128
1180
  const test = getTestByTestId(dispatcher, testId)
@@ -1146,19 +1198,28 @@ function onDispatcherCreateWorker (dispatcher, worker) {
1146
1198
  }
1147
1199
  )
1148
1200
  const testResult = test.results.at(-1)
1149
- const automaticFailureScreenshotPaths = automaticFailureScreenshotPathsByTestId.get(testId)
1150
- automaticFailureScreenshotPathsByTestId.delete(testId)
1151
- if (testStatus === 'fail' && automaticFailureScreenshotPaths?.size && testResult?.attachments?.length) {
1152
- const screenshots = []
1153
- for (const attachment of testResult.attachments) {
1154
- if (automaticFailureScreenshotPaths.has(attachment.path)) {
1155
- screenshots.push(attachment)
1201
+ if (isFailureScreenshotUploadEnabled &&
1202
+ !shouldCreateTestSpan &&
1203
+ !test._ddShouldSkipEfdRetry &&
1204
+ !disabledTestIds.has(testId)) {
1205
+ let screenshots
1206
+ if (testStatus === 'fail') {
1207
+ screenshots = automaticFailureScreenshotsByTestId.get(testId)
1208
+ if (!screenshots) {
1209
+ screenshots = []
1210
+ automaticFailureScreenshotsByTestId.set(testId, screenshots)
1156
1211
  }
1157
1212
  }
1158
- if (screenshots.length) {
1159
- worker[kDdPlaywrightFailureScreenshots] ??= []
1160
- worker[kDdPlaywrightFailureScreenshots].push(screenshots)
1161
- }
1213
+ worker[kDdPlaywrightFailureScreenshots] ??= []
1214
+ worker[kDdPlaywrightFailureScreenshots].push(screenshots)
1215
+ }
1216
+ if (isFailureVideoUploadEnabled &&
1217
+ !shouldCreateTestSpan &&
1218
+ !test._ddShouldSkipEfdRetry &&
1219
+ !disabledTestIds.has(testId)) {
1220
+ const videos = testStatus === 'fail' ? failureVideosByTestId.get(testId) : undefined
1221
+ worker[kDdPlaywrightFailureVideos] ??= []
1222
+ worker[kDdPlaywrightFailureVideos].push(videos)
1162
1223
  }
1163
1224
  const isAtrRetry = testResult?.retry > 0 &&
1164
1225
  isFlakyTestRetriesEnabled &&
@@ -1274,6 +1335,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1274
1335
  rootDir = getRootDir(this, config)
1275
1336
  const projects = getProjectsFromRunner(this, config)
1276
1337
  const isFailureScreenshotEnabled = isFailureScreenshotCaptureEnabled(projects)
1338
+ const isFailureVideoEnabled = isFailureVideoCaptureEnabled(projects)
1339
+ const isFailureVideoUploadSupported = satisfies(playwrightVersion, MINIMUM_FAILURE_VIDEO_UPLOAD_VERSION_RANGE)
1277
1340
  const processArgv = process.argv.slice(2).join(' ')
1278
1341
  const command = `playwright ${processArgv}`
1279
1342
  testSessionStartCh.publish({
@@ -1281,6 +1344,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1281
1344
  frameworkVersion: playwrightVersion,
1282
1345
  rootDir,
1283
1346
  isFailureScreenshotEnabled,
1347
+ isFailureVideoEnabled,
1348
+ isFailureVideoUploadSupported,
1284
1349
  })
1285
1350
 
1286
1351
  try {
@@ -1307,7 +1372,11 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1307
1372
  log.error('Playwright session start error', e)
1308
1373
  }
1309
1374
 
1310
- testSessionConfigurationCh.publish({ isFailureScreenshotEnabled })
1375
+ testSessionConfigurationCh.publish({
1376
+ isFailureScreenshotEnabled,
1377
+ isFailureVideoEnabled,
1378
+ isFailureVideoUploadSupported,
1379
+ })
1311
1380
 
1312
1381
  const isTestOptimizationSupported = satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)
1313
1382
  const shouldGetKnownTests = isKnownTestsEnabled && isTestOptimizationSupported
@@ -1693,8 +1762,33 @@ function recordAutomaticFailureScreenshotPath (ctx) {
1693
1762
  }
1694
1763
  }
1695
1764
 
1765
+ /**
1766
+ * Records only the destination used by Playwright's automatic video recorder.
1767
+ *
1768
+ * @param {object} ctx - Orchestrion context
1769
+ * @returns {void}
1770
+ */
1771
+ function recordAutomaticFailureVideoPath (ctx) {
1772
+ const video = ctx.arguments?.[0]
1773
+ if (!isFailureVideoUploadEnabled || typeof video?.saveAs !== 'function') return
1774
+
1775
+ const saveAs = video.saveAs
1776
+ ctx.arguments[0] = new Proxy(video, {
1777
+ get (target, property, receiver) {
1778
+ if (property !== 'saveAs') return Reflect.get(target, property, receiver)
1779
+ return function (filePath) {
1780
+ if (typeof filePath === 'string' && PLAYWRIGHT_FAILURE_VIDEO_PATH_RE.test(filePath)) {
1781
+ automaticFailureVideoPaths.add(filePath)
1782
+ }
1783
+ return Reflect.apply(saveAs, target, arguments)
1784
+ }
1785
+ },
1786
+ })
1787
+ }
1788
+
1696
1789
  artifactsRecorderScreenshotPathCh.subscribe({ end: recordAutomaticFailureScreenshotPath })
1697
1790
  snapshotRecorderScreenshotPathCh.subscribe({ end: recordAutomaticFailureScreenshotPath })
1791
+ saveAutomaticVideoCh.subscribe({ start: recordAutomaticFailureVideoPath })
1698
1792
 
1699
1793
  if (DD_MAJOR < 6) { // <1.38.0 is only supported up to version 5
1700
1794
  addHook({
@@ -2057,6 +2151,7 @@ function finishProcessHostStartRunner (processHost) {
2057
2151
  workerReportCh.publish({
2058
2152
  serializedTraces: message[1],
2059
2153
  screenshots: processHost[kDdPlaywrightFailureScreenshots]?.shift(),
2154
+ videos: processHost[kDdPlaywrightFailureVideos]?.shift(),
2060
2155
  })
2061
2156
  } else if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE) {
2062
2157
  workerReportTelemetryCh.publish(message[1])
@@ -2212,7 +2307,8 @@ addHook({
2212
2307
 
2213
2308
  function instrumentWorkerMainMethods (workerMain) {
2214
2309
  if (!workerMain || workerMain[kDdPlaywrightWorkerInstrumented] ||
2215
- typeof workerMain._runTest !== 'function' || typeof workerMain.dispatchEvent !== 'function') {
2310
+ typeof workerMain._runTest !== 'function' || typeof workerMain.dispatchEvent !== 'function' ||
2311
+ typeof workerMain.gracefullyClose !== 'function') {
2216
2312
  return workerMain
2217
2313
  }
2218
2314
 
@@ -2228,6 +2324,15 @@ function instrumentWorkerMainMethods (workerMain) {
2228
2324
  return runTestGroup.apply(this, arguments)
2229
2325
  })
2230
2326
 
2327
+ // Playwright >=1.60 creates WorkerMain through a runtime factory, which Orchestrion cannot wrap statically.
2328
+ shimmer.wrap(workerMain, 'gracefullyClose', gracefullyClose => async function () {
2329
+ try {
2330
+ return await gracefullyClose.apply(this, arguments)
2331
+ } finally {
2332
+ await getChannelPromise(logSubmissionFlushCh)
2333
+ }
2334
+ })
2335
+
2231
2336
  shimmer.wrap(workerMain, '_runTest', _runTest => async function (test) {
2232
2337
  if (this[kDdPlaywrightDisabledTestIds]?.has(test.id)) {
2233
2338
  test._ddIsDisabled = true
@@ -2393,8 +2498,9 @@ function instrumentWorkerMainMethods (workerMain) {
2393
2498
  // We reproduce what happens in `Dispatcher#_onStepBegin` and `Dispatcher#_onStepEnd`,
2394
2499
  // since `startTime` and `duration` are not available directly in the worker process
2395
2500
  shimmer.wrap(workerMain, 'dispatchEvent', dispatchEvent => function (event, payload) {
2396
- if (event === 'testBegin' || event === 'testEnd') {
2501
+ if (event === 'testBegin') {
2397
2502
  automaticFailureScreenshotPaths.clear()
2503
+ automaticFailureVideoPaths.clear()
2398
2504
  } else if (event === 'stepBegin') {
2399
2505
  stepInfoByStepId[payload.stepId] = {
2400
2506
  startTime: payload.wallTime,
@@ -2411,8 +2517,12 @@ function instrumentWorkerMainMethods (workerMain) {
2411
2517
  duration: payload.wallTime - stepInfo.startTime,
2412
2518
  error: payload.error,
2413
2519
  })
2414
- } else if (event === 'attach' && isAutomaticFailureScreenshotAttachment(payload)) {
2415
- payload._ddIsAutomaticFailureScreenshot = true
2520
+ } else if (event === 'attach') {
2521
+ if (isAutomaticFailureScreenshotAttachment(payload)) {
2522
+ payload._ddIsAutomaticFailureScreenshot = true
2523
+ } else if (isAutomaticFailureVideoAttachment(payload)) {
2524
+ payload._ddIsAutomaticFailureVideo = true
2525
+ }
2416
2526
  }
2417
2527
  return dispatchEvent.apply(this, arguments)
2418
2528
  })
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ // Serialized into browsers by browser-test integrations. Excluded from coverage by filename.
4
+
5
+ /** @returns {{ isRumInstrumented: boolean, isRumActive: boolean, rumSamplingRate: number | null }} */
6
+ function detectRum () {
7
+ const isRumInstrumented = !!window.DD_RUM
8
+ const isRumActive = window.DD_RUM && window.DD_RUM.getInternalContext
9
+ ? !!window.DD_RUM.getInternalContext()
10
+ : false
11
+ const rumInitConfiguration = window.DD_RUM && window.DD_RUM.getInitConfiguration
12
+ ? window.DD_RUM.getInitConfiguration()
13
+ : undefined
14
+ const rumSamplingRate = rumInitConfiguration ? rumInitConfiguration.sessionSampleRate : null
15
+
16
+ return { isRumInstrumented, isRumActive, rumSamplingRate }
17
+ }
18
+
19
+ /** @returns {boolean} */
20
+ function stopRumSession () {
21
+ if (window.DD_RUM && window.DD_RUM.stopSession) {
22
+ window.DD_RUM.stopSession()
23
+ return true
24
+ }
25
+ return false
26
+ }
27
+
28
+ /** @returns {boolean} */
29
+ function stopRumSessionAndReportActivity () {
30
+ if (window.DD_RUM && window.DD_RUM.stopSession) {
31
+ const isRumActive = window.DD_RUM.getInternalContext
32
+ ? !!window.DD_RUM.getInternalContext()
33
+ : false
34
+ window.DD_RUM.stopSession()
35
+ return isRumActive
36
+ }
37
+ return false
38
+ }
39
+
40
+ module.exports = { detectRum, stopRumSession, stopRumSessionAndReportActivity }
@@ -5,15 +5,45 @@ const {
5
5
  channel,
6
6
  addHook,
7
7
  } = require('./helpers/instrument')
8
+ const {
9
+ wrapPoolRelease,
10
+ wrapPromisePoolAcquire,
11
+ wrapPromisePoolQueryMethod,
12
+ } = require('./helpers/pool-acquire')
13
+
14
+ addHook({
15
+ name: 'sequelize',
16
+ versions: ['>=4'],
17
+ file: 'lib/dialects/abstract/connection-manager.js',
18
+ }, ConnectionManager => {
19
+ shimmer.wrap(ConnectionManager.prototype, 'getConnection', getConnection => wrapPromisePoolAcquire(
20
+ getConnection,
21
+ resolveSequelizeDriver,
22
+ resolveSequelizeConfig,
23
+ sequelizeHasIdleConnection
24
+ ))
25
+ shimmer.wrap(ConnectionManager.prototype, 'releaseConnection', wrapPoolRelease)
26
+ if (typeof ConnectionManager.prototype.destroyConnection === 'function') {
27
+ shimmer.wrap(ConnectionManager.prototype, 'destroyConnection', wrapPoolRelease)
28
+ }
29
+
30
+ return ConnectionManager
31
+ })
8
32
 
9
33
  addHook({ name: 'sequelize', versions: ['>=4'], file: 'lib/sequelize.js' }, Sequelize => {
10
34
  const startCh = channel('datadog:sequelize:query:start')
11
35
  const finishCh = channel('datadog:sequelize:query:finish')
12
36
 
13
37
  shimmer.wrap(Sequelize.prototype, 'query', query => {
38
+ const queryWithPool = wrapPromisePoolQueryMethod(
39
+ query,
40
+ (sequelize, args) => args[1]?.transaction ? undefined : sequelize.connectionManager,
41
+ sequelize => resolveSequelizeDriver(sequelize.connectionManager)
42
+ )
43
+
14
44
  return function (sql, options) {
15
45
  if (!startCh.hasSubscribers) {
16
- return query.apply(this, arguments)
46
+ return queryWithPool.apply(this, arguments)
17
47
  }
18
48
 
19
49
  let dialect
@@ -33,7 +63,7 @@ addHook({ name: 'sequelize', versions: ['>=4'], file: 'lib/sequelize.js' }, Sequ
33
63
  }
34
64
 
35
65
  return startCh.runStores({ sql, dialect }, () => {
36
- const promise = query.apply(this, arguments)
66
+ const promise = queryWithPool.apply(this, arguments)
37
67
  promise.then(onFinish, () => { onFinish() })
38
68
 
39
69
  return promise
@@ -43,3 +73,65 @@ addHook({ name: 'sequelize', versions: ['>=4'], file: 'lib/sequelize.js' }, Sequ
43
73
 
44
74
  return Sequelize
45
75
  })
76
+
77
+ addHook({ name: 'sequelize', versions: ['>=4'], file: 'lib/transaction.js' }, Transaction => {
78
+ shimmer.wrap(Transaction.prototype, 'prepareEnvironment', prepareEnvironment => wrapPromisePoolQueryMethod(
79
+ prepareEnvironment,
80
+ transaction => transaction.parent ? undefined : transaction.sequelize.connectionManager,
81
+ transaction => resolveSequelizeDriver(transaction.sequelize.connectionManager)
82
+ ))
83
+
84
+ return Transaction
85
+ })
86
+
87
+ /**
88
+ * @typedef {{
89
+ * available: number,
90
+ * pending?: number,
91
+ * waiting?: number,
92
+ * }} SequelizePool
93
+ */
94
+
95
+ /**
96
+ * @param {{ pool: SequelizePool & { read?: SequelizePool, write?: SequelizePool } }} manager
97
+ * @param {[{ type?: string, useMaster?: boolean }?]} args
98
+ * @returns {boolean}
99
+ */
100
+ function sequelizeHasIdleConnection (manager, args) {
101
+ const pool = manager.pool
102
+ const options = args[0]
103
+ const selected = pool?.read === undefined
104
+ ? pool
105
+ : options?.type === 'SELECT' && !options?.useMaster ? pool.read : pool.write
106
+ const waiting = selected.waiting ?? selected.pending
107
+ return selected.available > waiting
108
+ }
109
+
110
+ /**
111
+ * @param {{ dialectName?: string }|undefined} manager
112
+ * @returns {'mariadb'|'mysql2'|'pg'|undefined}
113
+ */
114
+ function resolveSequelizeDriver (manager) {
115
+ switch (manager?.dialectName) {
116
+ case 'mariadb':
117
+ return 'mariadb'
118
+ case 'mysql':
119
+ return 'mysql2'
120
+ case 'postgres':
121
+ return 'pg'
122
+ }
123
+ }
124
+
125
+ /**
126
+ * @param {{ config: Record<string, unknown> & { username?: string } }} manager
127
+ * @returns {Record<string, unknown>}
128
+ */
129
+ function resolveSequelizeConfig (manager) {
130
+ const config = manager.config
131
+ return {
132
+ database: config.database,
133
+ host: config.host,
134
+ port: config.port,
135
+ user: config.username,
136
+ }
137
+ }
@@ -1,20 +1,25 @@
1
1
  'use strict'
2
2
 
3
- const tracingChannel = require('dc-polyfill').tracingChannel
3
+ const { channel, tracingChannel } = require('dc-polyfill')
4
4
 
5
5
  const shimmer = require('../../datadog-shimmer')
6
6
  const satisfies = require('../../../vendor/dist/semifies')
7
- const {
8
- addHook,
9
- } = require('./helpers/instrument')
7
+ const { addHook } = require('./helpers/instrument')
10
8
  const { createWrapFetch } = require('./helpers/fetch')
11
9
 
12
10
  const ch = tracingChannel('apm:undici:fetch')
11
+ const upgradeCh = channel('apm:undici:request:upgrade')
13
12
 
14
13
  // Undici 5.0.x has a bug where fetch doesn't preserve AggregateError in the error cause chain
15
14
  // Use native DC only for versions where error handling works correctly
16
15
  const NATIVE_DC_VERSION = '>=4.7.0 <5.0.0 || >=5.1.0'
17
16
 
17
+ addHook({
18
+ name: 'undici',
19
+ versions: ['>=4.4.1 <8.0.0', '>=8.0.0'],
20
+ file: 'lib/core/request.js',
21
+ }, wrapRequestUpgrade)
22
+
18
23
  addHook({
19
24
  name: 'undici',
20
25
  versions: ['^4.4.1', '5', '>=6.0.0'],
@@ -27,3 +32,47 @@ addHook({
27
32
  // For older versions or those with buggy error handling, wrap fetch
28
33
  return shimmer.wrap(undici, 'fetch', createWrapFetch(undici.Request, ch))
29
34
  })
35
+
36
+ /**
37
+ * @param {Function & { prototype: Record<string, Function> }} Request
38
+ * @returns {Function}
39
+ */
40
+ function wrapRequestUpgrade (Request) {
41
+ const requestSource = Function.prototype.toString.call(Request)
42
+ const methodName = typeof Request.prototype.onRequestUpgrade === 'function'
43
+ ? 'onRequestUpgrade'
44
+ : 'onUpgrade'
45
+ const method = Request.prototype[methodName]
46
+
47
+ // Orchestrion cannot leave fixed source byte-for-byte untouched after a conditional AST match.
48
+ if (typeof method !== 'function' ||
49
+ !requestSource.includes('channels.trailers.publish') ||
50
+ Function.prototype.toString.call(method).includes('channels.trailers.publish')) {
51
+ return Request
52
+ }
53
+
54
+ shimmer.wrap(Request.prototype, methodName, createWrapUpgrade)
55
+ return Request
56
+ }
57
+
58
+ /**
59
+ * @param {Function} upgrade
60
+ * @returns {Function}
61
+ */
62
+ function createWrapUpgrade (upgrade) {
63
+ return function (statusCode, headers, socket) {
64
+ if (!upgradeCh.hasSubscribers) return upgrade.apply(this, arguments)
65
+
66
+ let result
67
+ try {
68
+ result = upgrade.apply(this, arguments)
69
+ } catch (error) {
70
+ upgradeCh.publish({ error, headers, request: this, statusCode })
71
+ throw error
72
+ }
73
+ if (!this.aborted) {
74
+ upgradeCh.publish({ headers, request: this, statusCode })
75
+ }
76
+ return result
77
+ }
78
+ }
@@ -56,6 +56,22 @@ const VITEST_NO_WORKER_INIT_SETUP_FILE = path.join(
56
56
  'ci',
57
57
  'vitest-no-worker-init-setup.mjs'
58
58
  )
59
+ const VITEST_NO_WORKER_INIT_RUNNER_SETUP_FILE = path.join(
60
+ __dirname,
61
+ '..',
62
+ '..',
63
+ '..',
64
+ 'ci',
65
+ 'vitest-no-worker-init-runner-setup.mjs'
66
+ )
67
+ const VITEST_NO_WORKER_INIT_V5_SETUP_FILE = path.join(
68
+ __dirname,
69
+ '..',
70
+ '..',
71
+ '..',
72
+ 'ci',
73
+ 'vitest-no-worker-init-v5-setup.mjs'
74
+ )
59
75
  const VITEST_BROWSER_SETUP_FILE_PLUGIN = {
60
76
  name: 'datadog:vitest-browser-setup-file',
61
77
  config: configureVitestBrowserSetupFile,
@@ -325,7 +341,10 @@ function isEarlyFlakeDetectionActive (state) {
325
341
  function configure (ctx, frameworkVersion, testSpecifications, setupData, options) {
326
342
  const { shouldReportTestModule, state } = options
327
343
  reserveEarlyFlakeDetectionSuite = options.reserveEarlyFlakeDetectionSuite
328
- addSetupFileToVitestConfigs(ctx, VITEST_NO_WORKER_INIT_SETUP_FILE, testSpecifications)
344
+ const setupFile = satisfies(frameworkVersion, '>=5.0.0')
345
+ ? VITEST_NO_WORKER_INIT_V5_SETUP_FILE
346
+ : VITEST_NO_WORKER_INIT_RUNNER_SETUP_FILE
347
+ addSetupFileToVitestConfigs(ctx, setupFile, testSpecifications)
329
348
  addVitestBrowserSetupFileAccess(testSpecifications)
330
349
 
331
350
  const {
@@ -470,8 +489,15 @@ function allowVitestBrowserSetupFile (viteConfig) {
470
489
  const allow = viteConfig.server?.fs?.allow
471
490
  if (!allow) return
472
491
 
473
- if (!allow.includes(VITEST_NO_WORKER_INIT_SETUP_FILE)) {
474
- allow.push(VITEST_NO_WORKER_INIT_SETUP_FILE)
492
+ const setupFiles = [
493
+ VITEST_NO_WORKER_INIT_SETUP_FILE,
494
+ VITEST_NO_WORKER_INIT_RUNNER_SETUP_FILE,
495
+ VITEST_NO_WORKER_INIT_V5_SETUP_FILE,
496
+ ]
497
+ for (const setupFile of setupFiles) {
498
+ if (!allow.includes(setupFile)) {
499
+ allow.push(setupFile)
500
+ }
475
501
  }
476
502
  }
477
503
 
@@ -493,6 +519,7 @@ function addVitestBrowserSetupFileAccess (testSpecifications) {
493
519
  if (!browserServerProject) continue
494
520
 
495
521
  addVitestBrowserCommand(safeConfig(project))
522
+ addVitestBrowserRuntimeCommand(project)
496
523
  if (configuredProjects.has(browserServerProject)) continue
497
524
 
498
525
  configuredProjects.add(browserServerProject)
@@ -507,6 +534,13 @@ function addVitestBrowserSetupFileAccess (testSpecifications) {
507
534
  }
508
535
  }
509
536
 
537
+ function addVitestBrowserRuntimeCommand (project) {
538
+ const parentBrowser = project?._parentBrowser || project?._parent?._parentBrowser
539
+ if (parentBrowser?.commands) {
540
+ parentBrowser.commands[VITEST_BROWSER_EFD_SUITE_ADMISSION_COMMAND] = handleBrowserEfdSuiteAdmission
541
+ }
542
+ }
543
+
510
544
  /**
511
545
  * Adds the private EFD admission command to a Vitest Browser Mode configuration.
512
546
  *
@@ -1602,15 +1636,18 @@ function splitNodeOptions (nodeOptions) {
1602
1636
  }
1603
1637
 
1604
1638
  function serializeNodeOptions (tokens) {
1605
- const serializedTokens = []
1639
+ let serializedTokens = ''
1640
+ let hasSerializedToken = false
1606
1641
  for (const token of tokens) {
1642
+ if (hasSerializedToken) serializedTokens += ' '
1607
1643
  if (NODE_OPTIONS_QUOTE_RE.test(token)) {
1608
- serializedTokens.push(JSON.stringify(token))
1644
+ serializedTokens += JSON.stringify(token)
1609
1645
  } else {
1610
- serializedTokens.push(token)
1646
+ serializedTokens += token
1611
1647
  }
1648
+ hasSerializedToken = true
1612
1649
  }
1613
- return serializedTokens.join(' ')
1650
+ return serializedTokens
1614
1651
  }
1615
1652
 
1616
1653
  function getTestSpecificationProject (testSpecification) {