dd-trace 5.132.0 → 5.134.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 (231) hide show
  1. package/ci/diagnose.js +9 -14
  2. package/ci/init.js +1 -2
  3. package/ci/test-optimization-validation/ci-discovery.js +9 -4
  4. package/ci/test-optimization-validation/executable.js +1 -5
  5. package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
  6. package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
  7. package/ci/test-optimization-validation/generated-files.js +6 -4
  8. package/ci/test-optimization-validation/generated-test-contract.js +7 -3
  9. package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
  10. package/ci/test-optimization-validation/manifest-schema.js +6 -7
  11. package/ci/test-optimization-validation/report-writer.js +6 -2
  12. package/ci/test-optimization-validation/runner-command.js +2 -4
  13. package/ci/test-optimization-validation/runner-contract.js +1 -2
  14. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
  15. package/ci/vitest-no-worker-init-runner-setup.mjs +27 -0
  16. package/ci/vitest-no-worker-init-setup.mjs +10 -7
  17. package/index.d.ts +41 -19
  18. package/initialize.mjs +1 -3
  19. package/openfeature/rules-based.d.ts +11 -0
  20. package/openfeature/rules-based.js +7 -0
  21. package/package.json +25 -22
  22. package/packages/datadog-core/src/storage.js +16 -0
  23. package/packages/datadog-esbuild/index.js +18 -12
  24. package/packages/datadog-instrumentations/index.js +2 -1
  25. package/packages/datadog-instrumentations/src/aerospike.js +3 -3
  26. package/packages/datadog-instrumentations/src/ai.js +3 -3
  27. package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
  28. package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
  29. package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
  30. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
  31. package/packages/datadog-instrumentations/src/cucumber.js +65 -41
  32. package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
  33. package/packages/datadog-instrumentations/src/electron.js +6 -6
  34. package/packages/datadog-instrumentations/src/fastify.js +13 -4
  35. package/packages/datadog-instrumentations/src/fs.js +2 -2
  36. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
  37. package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
  38. package/packages/datadog-instrumentations/src/helpers/bundler-register.js +28 -9
  39. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
  40. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  41. package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
  42. package/packages/datadog-instrumentations/src/helpers/register.js +45 -1
  43. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
  44. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +89 -6
  45. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
  46. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/jest.js +12 -0
  47. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
  48. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/vitest.js +8 -0
  49. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
  50. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +5 -0
  51. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
  52. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
  53. package/packages/datadog-instrumentations/src/ioredis.js +7 -5
  54. package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
  55. package/packages/datadog-instrumentations/src/jest.js +72 -67
  56. package/packages/datadog-instrumentations/src/knex.js +9 -5
  57. package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
  58. package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
  59. package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
  60. package/packages/datadog-instrumentations/src/mocha/utils.js +38 -4
  61. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
  62. package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
  63. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  64. package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
  65. package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
  66. package/packages/datadog-instrumentations/src/nova-sonic/session.js +492 -0
  67. package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
  68. package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
  69. package/packages/datadog-instrumentations/src/openai.js +7 -0
  70. package/packages/datadog-instrumentations/src/pino.js +5 -6
  71. package/packages/datadog-instrumentations/src/playwright.js +2 -0
  72. package/packages/datadog-instrumentations/src/redis.js +27 -13
  73. package/packages/datadog-instrumentations/src/router.js +30 -0
  74. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
  75. package/packages/datadog-instrumentations/src/stripe.js +23 -12
  76. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +8 -5
  77. package/packages/datadog-instrumentations/src/vitest-main.js +16 -18
  78. package/packages/datadog-instrumentations/src/vitest-util.js +7 -4
  79. package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
  80. package/packages/datadog-instrumentations/src/vitest.js +9 -1
  81. package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
  82. package/packages/datadog-instrumentations/src/ws.js +1 -4
  83. package/packages/datadog-plugin-amqplib/src/client.js +1 -2
  84. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
  85. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
  86. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
  87. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
  88. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
  89. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
  90. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
  91. package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
  92. package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
  93. package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
  94. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +4 -5
  95. package/packages/datadog-plugin-cypress/src/index.js +6 -6
  96. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
  97. package/packages/datadog-plugin-cypress/src/support.js +3 -4
  98. package/packages/datadog-plugin-express/src/code_origin.js +1 -2
  99. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
  100. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
  101. package/packages/datadog-plugin-graphql/src/execute.js +5 -5
  102. package/packages/datadog-plugin-grpc/src/util.js +1 -2
  103. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
  104. package/packages/datadog-plugin-mocha/src/index.js +7 -10
  105. package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
  106. package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
  107. package/packages/datadog-plugin-next/src/index.js +1 -2
  108. package/packages/datadog-plugin-openai/src/tracing.js +5 -2
  109. package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
  110. package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
  111. package/packages/datadog-plugin-postgres/src/index.js +24 -0
  112. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
  113. package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
  114. package/packages/datadog-plugin-vitest/src/index.js +5 -2
  115. package/packages/datadog-shimmer/src/shimmer.js +5 -4
  116. package/packages/datadog-webpack/index.js +55 -43
  117. package/packages/datadog-webpack/src/loader.js +56 -8
  118. package/packages/dd-trace/src/aiguard/evaluation.js +143 -1
  119. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
  120. package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
  121. package/packages/dd-trace/src/aiguard/redaction.js +8 -2
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
  123. package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
  124. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
  127. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
  128. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
  129. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
  130. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
  131. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
  132. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
  133. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
  134. package/packages/dd-trace/src/appsec/lambda.js +1 -3
  135. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
  136. package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
  137. package/packages/dd-trace/src/appsec/reporter.js +21 -1
  138. package/packages/dd-trace/src/appsec/stack_trace.js +4 -4
  139. package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
  140. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +3 -2
  141. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
  142. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
  143. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
  144. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
  145. package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
  146. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
  147. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
  148. package/packages/dd-trace/src/config/config-types.d.ts +2 -2
  149. package/packages/dd-trace/src/config/defaults.js +5 -5
  150. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  151. package/packages/dd-trace/src/config/helper.js +34 -3
  152. package/packages/dd-trace/src/config/index.js +55 -9
  153. package/packages/dd-trace/src/config/normalize-service.js +1 -1
  154. package/packages/dd-trace/src/config/parsers.js +2 -2
  155. package/packages/dd-trace/src/config/supported-configurations.json +44 -0
  156. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  157. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
  158. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +6 -5
  159. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -5
  160. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
  161. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +4 -6
  162. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
  163. package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
  164. package/packages/dd-trace/src/debugger/unsupported-runtime.js +16 -0
  165. package/packages/dd-trace/src/dogstatsd.js +1 -1
  166. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  167. package/packages/dd-trace/src/exporters/common/request.js +1 -5
  168. package/packages/dd-trace/src/id.js +14 -1
  169. package/packages/dd-trace/src/lambda/handler.js +179 -19
  170. package/packages/dd-trace/src/lambda/index.js +15 -1
  171. package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
  172. package/packages/dd-trace/src/llmobs/agent-manifest.js +19 -2
  173. package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
  174. package/packages/dd-trace/src/llmobs/constants/tags.js +15 -0
  175. package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
  176. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
  177. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
  178. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  180. package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
  181. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +5 -0
  182. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +64 -2
  183. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
  184. package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
  185. package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
  186. package/packages/dd-trace/src/llmobs/sdk.js +15 -4
  187. package/packages/dd-trace/src/llmobs/span_processor.js +13 -2
  188. package/packages/dd-trace/src/llmobs/tagger.js +20 -10
  189. package/packages/dd-trace/src/llmobs/util.js +11 -6
  190. package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
  191. package/packages/dd-trace/src/log/log.js +1 -5
  192. package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
  193. package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
  194. package/packages/dd-trace/src/openfeature/index.js +6 -0
  195. package/packages/dd-trace/src/openfeature/writers/util.js +4 -4
  196. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
  199. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
  200. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +24 -24
  201. package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
  202. package/packages/dd-trace/src/otel-sampling.js +4 -5
  203. package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
  204. package/packages/dd-trace/src/plugin_manager.js +1 -2
  205. package/packages/dd-trace/src/plugins/database.js +7 -16
  206. package/packages/dd-trace/src/plugins/tracing.js +0 -1
  207. package/packages/dd-trace/src/plugins/util/git.js +99 -59
  208. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
  209. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -1
  210. package/packages/dd-trace/src/plugins/util/test.js +50 -4
  211. package/packages/dd-trace/src/plugins/util/web.js +90 -20
  212. package/packages/dd-trace/src/priority_sampler.js +1 -2
  213. package/packages/dd-trace/src/profiling/profiler.js +4 -6
  214. package/packages/dd-trace/src/proxy.js +1 -2
  215. package/packages/dd-trace/src/ritm.js +4 -2
  216. package/packages/dd-trace/src/serverless.js +5 -0
  217. package/packages/dd-trace/src/span_processor.js +13 -17
  218. package/packages/dd-trace/src/span_stats.js +4 -1
  219. package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
  220. package/packages/dd-trace/src/telemetry/logs/log-collector.js +1 -1
  221. package/packages/dd-trace/src/util.js +37 -4
  222. package/packages/dd-trace/src/web-tags-cache.js +6 -2
  223. package/vendor/dist/@apm-js-collab/code-transformer/index.js +8 -8
  224. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  225. package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
  226. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
  227. package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
  228. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  229. package/vendor/dist/protobufjs/index.js +1 -1
  230. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  231. package/vendor/dist/shell-quote/index.js +1 -1
@@ -10,6 +10,11 @@ const RealtimeSession = require('./session')
10
10
  /**
11
11
  * @typedef {import('./session').TurnDescriptor} TurnDescriptor
12
12
  * @typedef {import('./turns').ResponseTurn} ResponseTurn
13
+ * @typedef {{
14
+ * session: RealtimeSession,
15
+ * socket: object | undefined,
16
+ * finalize: (failed?: boolean) => void,
17
+ * }} Connection
13
18
  */
14
19
 
15
20
  // One span per phase of a turn. `user speech` and `agent speech` share a channel: they differ only
@@ -36,7 +41,7 @@ const audioChannel = dc.channel('dd-trace:openai:realtime:audio')
36
41
  const USER_SPEECH = { operation: 'createRealtimeUserSpeech', llmobsName: 'user speech' }
37
42
  const AGENT_SPEECH = { operation: 'createRealtimeAgentSpeech', llmobsName: 'agent speech' }
38
43
 
39
- /** @type {WeakMap<object, { session: RealtimeSession, finalize: () => void }>} */
44
+ /** @type {WeakMap<object, Connection>} */
40
45
  const connections = new WeakMap()
41
46
 
42
47
  /**
@@ -92,7 +97,7 @@ function captureContext (turn) {
92
97
  * Start tracking a connection, reading the model and base path off the URL the SDK already built.
93
98
  *
94
99
  * @param {object} emitter
95
- * @returns {{ session: RealtimeSession, finalize: () => void }}
100
+ * @returns {Connection}
96
101
  */
97
102
  function createConnection (emitter) {
98
103
  let model
@@ -135,7 +140,7 @@ function createConnection (emitter) {
135
140
  session.finishSession(Date.now(), failed)
136
141
  }
137
142
 
138
- const connection = { session, finalize }
143
+ const connection = { session, socket: emitter.socket, finalize }
139
144
  attachSocketClose(emitter, finalize)
140
145
  return connection
141
146
  }
@@ -181,14 +186,21 @@ function attachSocketClose (emitter, finalize) {
181
186
 
182
187
  /**
183
188
  * @param {object} emitter
184
- * @returns {{ session: RealtimeSession, finalize: () => void } | undefined}
189
+ * @returns {Connection | undefined}
185
190
  */
186
191
  function getConnection (emitter) {
187
192
  const connection = connections.get(emitter)
188
- if (connection !== undefined) return connection
193
+ if (connection !== undefined) {
194
+ if (connection.socket === emitter.socket) return connection
195
+
196
+ // Opt-in SDK recovery keeps the emitter but replaces its socket with a new server session.
197
+ // Retire the old state before attaching close and exit cleanup to the replacement.
198
+ connection.finalize(true)
199
+ connections.delete(emitter)
200
+ }
189
201
 
190
202
  // Nothing is listening, so never allocate. Checked only on the miss path, so an established
191
- // connection pays one WeakMap lookup per event.
203
+ // connection reuses its state while the socket is unchanged.
192
204
  if (!turnChannel.start.hasSubscribers) return
193
205
 
194
206
  const created = createConnection(emitter)
@@ -829,7 +829,7 @@ class RealtimeSession {
829
829
  input.audio.totalDecodedBytes, inputFormat.mimeType, inputFormat.sampleRate
830
830
  )
831
831
  const userEnd = input.speechEndTime ??
832
- (userStart !== undefined && inputDurationMs !== undefined ? userStart + inputDurationMs : undefined)
832
+ (userStart === undefined || inputDurationMs === undefined ? undefined : userStart + inputDurationMs)
833
833
 
834
834
  const agentStart = turn.audio.startTime
835
835
  const playbackMs = segmentDurationMs(
@@ -58,6 +58,13 @@ const V4_PACKAGE_SHIMS = [
58
58
  versions: ['>=4.87.0'],
59
59
  interceptChannel: responsesInterceptChannel,
60
60
  },
61
+ {
62
+ file: 'resources/decisions',
63
+ targetClass: 'Decisions',
64
+ baseResource: 'decisions',
65
+ methods: ['create'],
66
+ versions: ['>=7.30.0'],
67
+ },
61
68
  {
62
69
  file: 'resources/embeddings',
63
70
  targetClass: 'Embeddings',
@@ -10,10 +10,9 @@ const logSubmissionCh = channel('ci:log-submission:log')
10
10
 
11
11
  /**
12
12
  * @param {string} symbol
13
- * @param {(original: Function) => Function} wrapper
14
13
  * @param {Function} pino
15
14
  */
16
- function wrapPino (symbol, wrapper, pino) {
15
+ function wrapPino (symbol, pino) {
17
16
  /**
18
17
  * @param {unknown[]} args
19
18
  * @returns {unknown}
@@ -25,7 +24,7 @@ function wrapPino (symbol, wrapper, pino) {
25
24
  configurable: true,
26
25
  enumerable: true,
27
26
  writable: true,
28
- value: wrapper(instance[symbol]),
27
+ value: wrapAsJson(instance[symbol]),
29
28
  })
30
29
 
31
30
  return instance
@@ -83,19 +82,19 @@ function wrapPrettyFactory (prettyFactory) {
83
82
  addHook({ name: 'pino', versions: ['2 - 3', '4'], patchDefault: true }, (pino) => {
84
83
  const asJsonSym = (pino.symbols && pino.symbols.asJsonSym) || 'asJson'
85
84
 
86
- return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, wrapAsJson, pino))
85
+ return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, pino))
87
86
  })
88
87
 
89
88
  addHook({ name: 'pino', versions: ['>=5 <6.8.0'], patchDefault: true }, (pino) => {
90
89
  const asJsonSym = ((pino.default || pino)?.symbols.asJsonSym) || 'asJson'
91
90
 
92
- return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, wrapAsJson, pino.default || pino))
91
+ return shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, pino.default || pino))
93
92
  })
94
93
 
95
94
  addHook({ name: 'pino', versions: ['>=6.8.0'], patchDefault: false }, (pino) => {
96
95
  const asJsonSym = pino.symbols.asJsonSym
97
96
 
98
- const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, wrapAsJson, pino))
97
+ const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, pino))
99
98
  wrapped.pino = wrapped
100
99
  wrapped.default = wrapped
101
100
 
@@ -1077,6 +1077,7 @@ function testEndHandler ({
1077
1077
  : undefined
1078
1078
  const isAtrRetry = testResult?.retry > 0 &&
1079
1079
  isFlakyTestRetriesEnabled &&
1080
+ hasAutomaticRetries(test, projects) &&
1080
1081
  !test._ddIsAttemptToFix &&
1081
1082
  !test._ddIsEfdRetry
1082
1083
 
@@ -1340,6 +1341,7 @@ function onDispatcherCreateWorker (dispatcher, worker) {
1340
1341
  }
1341
1342
  const isAtrRetry = testResult?.retry > 0 &&
1342
1343
  isFlakyTestRetriesEnabled &&
1344
+ hasAutomaticRetries(test, projects) &&
1343
1345
  !test._ddIsAttemptToFix &&
1344
1346
  !test._ddIsEfdRetry
1345
1347
 
@@ -25,7 +25,7 @@ function wrapAddCommand (addCommand) {
25
25
  return startCh.runStores(ctx, () => {
26
26
  const res = addCommand.apply(this, arguments)
27
27
 
28
- res.then(() => finish(finishCh, errorCh, ctx), err => finish(finishCh, errorCh, ctx, err))
28
+ res.then(() => finish(ctx), err => finish(ctx, err))
29
29
 
30
30
  return res
31
31
  })
@@ -85,13 +85,11 @@ addHook({ name: '@redis/client', file: 'dist/lib/client/commands-queue.js', vers
85
85
 
86
86
  addHook({ name: 'redis', versions: ['>=2.6 <4'] }, redis => {
87
87
  shimmer.wrap(redis.RedisClient.prototype, 'internal_send_command', internalSendCommand => function (options) {
88
- if (!startCh.hasSubscribers) return internalSendCommand.apply(this, arguments)
89
-
90
- if (!options.callback) return internalSendCommand.apply(this, arguments)
88
+ if (!startCh.hasSubscribers || !options.callback) return internalSendCommand.apply(this, arguments)
91
89
 
92
90
  const ctx = getStartCtx(this, options.command, options.args)
93
91
  return startCh.runStores(ctx, () => {
94
- options.callback = wrapCallback(finishCh, errorCh, ctx, options.callback)
92
+ options.callback = wrapCallback(ctx, options.callback)
95
93
 
96
94
  try {
97
95
  return internalSendCommand.apply(this, arguments)
@@ -114,11 +112,11 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
114
112
  const ctx = getStartCtx(this, command, args)
115
113
  return startCh.runStores(ctx, () => {
116
114
  if (typeof callback === 'function') {
117
- arguments[2] = wrapCallback(finishCh, errorCh, ctx, callback)
115
+ arguments[2] = wrapCallback(ctx, callback)
118
116
  } else if (Array.isArray(args) && typeof args.at(-1) === 'function') {
119
- args[args.length - 1] = wrapCallback(finishCh, errorCh, ctx, args.at(-1))
117
+ args[args.length - 1] = wrapCallback(ctx, args.at(-1))
120
118
  } else {
121
- arguments[2] = wrapCallback(finishCh, errorCh, ctx)
119
+ arguments[2] = wrapCallback(ctx)
122
120
  }
123
121
 
124
122
  try {
@@ -155,20 +153,36 @@ function getStartCtx (client, command, args, argsStartIndex) {
155
153
  }
156
154
  }
157
155
 
158
- function wrapCallback (finishCh, errorCh, ctx, callback) {
156
+ /**
157
+ * @param {Record<string, unknown> & { error?: Error }} ctx
158
+ * @param {Function} [callback]
159
+ */
160
+ function wrapCallback (ctx, callback) {
159
161
  if (typeof callback !== 'function') return callback
160
162
  return function (err) {
161
- return finish(finishCh, errorCh, ctx, err, callback, this, arguments)
163
+ return finish(ctx, err, callback, this, arguments)
162
164
  }
163
165
  }
164
166
 
165
- function finish (finishCh, errorCh, ctx, error, callback, thisArg, args) {
167
+ /**
168
+ * @param {Record<string, unknown> & { error?: Error }} ctx
169
+ * @param {Error} [error]
170
+ * @param {Function} [callback]
171
+ * @param {object} [thisArg]
172
+ * @param {Iterable<unknown>} [args]
173
+ */
174
+ function finish (ctx, error, callback, thisArg, args) {
166
175
  if (error) {
167
176
  ctx.error = error
168
177
  errorCh.publish(ctx)
169
178
  }
170
179
  if (callback) {
171
- return finishCh.runStores(ctx, callback, thisArg, ...args)
180
+ return finishCh.runStores(
181
+ ctx,
182
+ /** @type {Parameters<typeof finishCh.runStores>[1]} */ (callback),
183
+ thisArg,
184
+ ...(/** @type {Iterable<unknown>} */ (args))
185
+ )
172
186
  }
173
- finishCh.publish(ctx)
187
+ return finishCh.publish(ctx)
174
188
  }
@@ -91,6 +91,28 @@ function resolveLayerRouteOrForwardError (meta, layer, next) {
91
91
  }
92
92
  }
93
93
 
94
+ // Wrapped `next` -> the host continuation it calls.
95
+ const hostNexts = new WeakMap()
96
+
97
+ /**
98
+ * Routers store their continuation on `req.next` once per `handle()`, and host
99
+ * APIs call it directly (express `res.format`, `res.sendFile` and `res.render`
100
+ * errors). Point it at the wrapper the current handler receives when that
101
+ * wrapper wraps the same continuation: `req.next` is the host continuation
102
+ * itself, or an earlier layer's wrapper of it. Route handlers receive a
103
+ * route-local continuation from `Route.dispatch`, so `req.next` keeps the
104
+ * router-level one there. A nested router restores its parent's `req.next`
105
+ * when it finishes.
106
+ *
107
+ * @param {import('node:http').IncomingMessage | undefined} req
108
+ * @param {Function} hostNext
109
+ * @param {Function} wrappedNext
110
+ */
111
+ function adoptRequestNext (req, hostNext, wrappedNext) {
112
+ hostNexts.set(wrappedNext, hostNext)
113
+ if (req && (req.next === hostNext || hostNexts.get(req.next) === hostNext)) req.next = wrappedNext
114
+ }
115
+
94
116
  /**
95
117
  * Build the request/error dispatch wrappers for one host (`express` / `router`).
96
118
  * They wrap the layer's prototype dispatch and read the side-table metadata, so
@@ -171,6 +193,7 @@ function createLayerDispatchWrappers (name) {
171
193
  }
172
194
 
173
195
  const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
196
+ if (wrappedNext !== next) adoptRequestNext(req, next, wrappedNext)
174
197
  enterChannel.publish({ name: meta.name, req, route, layer: this })
175
198
 
176
199
  try {
@@ -199,6 +222,7 @@ function createLayerDispatchWrappers (name) {
199
222
  }
200
223
 
201
224
  const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
225
+ if (wrappedNext !== next) adoptRequestNext(req, next, wrappedNext)
202
226
  enterChannel.publish({ name: meta.name, req, route, layer: this })
203
227
 
204
228
  try {
@@ -223,6 +247,7 @@ function createLayerDispatchWrappers (name) {
223
247
 
224
248
  let calls = 0
225
249
  if (typeof next === 'function') {
250
+ const hostNext = next
226
251
  next = shimmer.wrapCallback(next, originalNext => function next (error) {
227
252
  calls++
228
253
  if (calls === 1) {
@@ -238,6 +263,7 @@ function createLayerDispatchWrappers (name) {
238
263
 
239
264
  originalNext.apply(this, arguments)
240
265
  })
266
+ adoptRequestNext(req, hostNext, next)
241
267
  }
242
268
 
243
269
  const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
@@ -273,6 +299,7 @@ function createLayerDispatchWrappers (name) {
273
299
 
274
300
  let calls = 0
275
301
  if (typeof next === 'function') {
302
+ const hostNext = next
276
303
  next = shimmer.wrapCallback(next, originalNext => function next (nextError) {
277
304
  calls++
278
305
  if (calls === 1) {
@@ -288,6 +315,7 @@ function createLayerDispatchWrappers (name) {
288
315
 
289
316
  originalNext.apply(this, arguments)
290
317
  })
318
+ adoptRequestNext(req, hostNext, next)
291
319
  }
292
320
 
293
321
  const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
@@ -336,6 +364,7 @@ function createLayerDispatchWrappers (name) {
336
364
  const nextIndex = isErrorHandler ? 3 : 2
337
365
  let calls = 0
338
366
  if (typeof args[nextIndex] === 'function') {
367
+ const hostNext = args[nextIndex]
339
368
  args[nextIndex] = shimmer.wrapCallback(args[nextIndex], originalNext => function next (error) {
340
369
  calls++
341
370
  if (calls === 1) {
@@ -351,6 +380,7 @@ function createLayerDispatchWrappers (name) {
351
380
 
352
381
  originalNext.apply(this, arguments)
353
382
  })
383
+ adoptRequestNext(req, hostNext, args[nextIndex])
354
384
  }
355
385
 
356
386
  const route = needMultiMatch ? resolveLayerRoute(meta, layer) : captureRoute
@@ -5,9 +5,7 @@
5
5
  /** @returns {{ isRumInstrumented: boolean, isRumActive: boolean, rumSamplingRate: number | null }} */
6
6
  function detectRum () {
7
7
  const isRumInstrumented = !!window.DD_RUM
8
- const isRumActive = window.DD_RUM && window.DD_RUM.getInternalContext
9
- ? !!window.DD_RUM.getInternalContext()
10
- : false
8
+ const isRumActive = !!(window.DD_RUM && window.DD_RUM.getInternalContext && window.DD_RUM.getInternalContext())
11
9
  const rumInitConfiguration = window.DD_RUM && window.DD_RUM.getInitConfiguration
12
10
  ? window.DD_RUM.getInitConfiguration()
13
11
  : undefined
@@ -26,9 +24,7 @@ function stopRumSession () {
26
24
 
27
25
  function stopRumSessionAndReportActivity () {
28
26
  if (window.DD_RUM && window.DD_RUM.stopSession) {
29
- const isRumActive = window.DD_RUM.getInternalContext
30
- ? !!window.DD_RUM.getInternalContext()
31
- : false
27
+ const isRumActive = !!(window.DD_RUM.getInternalContext && window.DD_RUM.getInternalContext())
32
28
  window.DD_RUM.stopSession()
33
29
  return isRumActive
34
30
  }
@@ -58,19 +58,30 @@ function wrapConstructEventAsync (constructEventAsync) {
58
58
  }
59
59
  }
60
60
 
61
+ const wrappedMethods = new WeakSet()
62
+
63
+ // Stripe >=9 <11.10 and >=18 share `webhooks` across clients. Stripe >=11.10 <18
64
+ // creates it per client. checkout.sessions and paymentIntents are separate for each client.
65
+ // Rewrapping shared webhook methods adds one layer per client, causing duplicate
66
+ // publications and eventually a stack overflow. Track installed functions so replacements
67
+ // still get wrapped and restored wrappers are reused.
68
+ /**
69
+ * @param {object|undefined} target
70
+ * @param {string} name
71
+ * @param {Function} wrapper
72
+ */
73
+ function wrapOnce (target, name, wrapper) {
74
+ if (typeof target?.[name] !== 'function' || wrappedMethods.has(target[name])) return
75
+
76
+ shimmer.wrap(target, name, wrapper)
77
+ wrappedMethods.add(target[name])
78
+ }
79
+
61
80
  function instrumentStripeInstance (stripe) {
62
- if (typeof stripe.checkout?.sessions?.create === 'function') {
63
- shimmer.wrap(stripe.checkout.sessions, 'create', wrapSessionCreate)
64
- }
65
- if (typeof stripe.paymentIntents?.create === 'function') {
66
- shimmer.wrap(stripe.paymentIntents, 'create', wrapPaymentIntentCreate)
67
- }
68
- if (typeof stripe.webhooks?.constructEvent === 'function') {
69
- shimmer.wrap(stripe.webhooks, 'constructEvent', wrapConstructEvent)
70
- }
71
- if (typeof stripe.webhooks?.constructEventAsync === 'function') {
72
- shimmer.wrap(stripe.webhooks, 'constructEventAsync', wrapConstructEventAsync)
73
- }
81
+ wrapOnce(stripe.checkout?.sessions, 'create', wrapSessionCreate)
82
+ wrapOnce(stripe.paymentIntents, 'create', wrapPaymentIntentCreate)
83
+ wrapOnce(stripe.webhooks, 'constructEvent', wrapConstructEvent)
84
+ wrapOnce(stripe.webhooks, 'constructEventAsync', wrapConstructEventAsync)
74
85
  }
75
86
 
76
87
  // stripe <22: the constructor mutates this (when invoked with 'new') and
@@ -659,8 +659,7 @@ function createMainProcessReporter (reporterState) {
659
659
  },
660
660
 
661
661
  onTaskUpdate (packs, events) {
662
- if (!isReporterActive()) return
663
- if (!events) return
662
+ if (!isReporterActive() || !events) return
664
663
 
665
664
  for (const event of events) {
666
665
  if (event[1] === 'test-retried') {
@@ -685,8 +684,7 @@ function createMainProcessReporter (reporterState) {
685
684
  },
686
685
 
687
686
  onFinished (files) {
688
- if (!isReporterActive()) return
689
- if (!files) return
687
+ if (!isReporterActive() || !files) return
690
688
 
691
689
  for (const file of files) {
692
690
  const testModule = createTestModuleFromFile(file)
@@ -936,6 +934,7 @@ function createMainProcessReporter (reporterState) {
936
934
  isDynamicAtrEnabled: state.isDynamicAtrEnabled,
937
935
  flakyTestRetriesIncludesUnnamedProject: flakyTestRetriesConfiguration.includesUnnamedProject,
938
936
  flakyTestRetriesProjectNames: flakyTestRetriesConfiguration.projectNames,
937
+ flakyTestRetriesUseRetryMarker: flakyTestRetriesConfiguration.usesRetryMarker,
939
938
  }, task)
940
939
 
941
940
  return {
@@ -1276,10 +1275,12 @@ function reportFinalTestAttempt (testReport) {
1276
1275
  isQuarantined: testProperties.isQuarantined,
1277
1276
  })
1278
1277
  }
1278
+ const attemptIndex = task.meta?.__ddTestOptCurrentAttemptIndex ?? 0
1279
+ const attemptStartTime = task.meta?.__ddTestOptAttemptStartTimes?.[attemptIndex]
1279
1280
  const {
1280
1281
  isRumActive,
1281
1282
  testExecutionId,
1282
- } = getRumCorrelation(task)
1283
+ } = getRumCorrelation(task, attemptIndex)
1283
1284
  testSkipCh.publish({
1284
1285
  ...browserEnvironment,
1285
1286
  testName,
@@ -1291,6 +1292,8 @@ function reportFinalTestAttempt (testReport) {
1291
1292
  isRumActive,
1292
1293
  isTestFrameworkWorker: true,
1293
1294
  requestErrorTags: state.requestErrorTags,
1295
+ startTime: Number.isFinite(attemptStartTime) ? attemptStartTime : undefined,
1296
+ duration: task.meta?.__ddTestOptAttemptDurations?.[attemptIndex],
1294
1297
  testStartLine: task.location?.line,
1295
1298
  testExecutionId,
1296
1299
  ...testSuiteStore,
@@ -300,8 +300,7 @@ function configureEfdSuiteTracker (testFilepaths, repositoryRoot) {
300
300
  * @param {boolean} hasNewTest
301
301
  */
302
302
  function reserveEarlyFlakeDetectionSuite (testSuite, hasNewTest) {
303
- if (!isEfdSuiteAdmissionEnabled || typeof testSuite !== 'string') return false
304
- if (isEarlyFlakeDetectionFaulty) return false
303
+ if (!isEfdSuiteAdmissionEnabled || typeof testSuite !== 'string' || isEarlyFlakeDetectionFaulty) return false
305
304
 
306
305
  if (hasNewTest) {
307
306
  suitesWithNewTests.add(testSuite)
@@ -681,6 +680,7 @@ function resetMainProcessProvidedContext (ctx) {
681
680
  _ddIsEfdSuiteAdmissionEnabled: false,
682
681
  _ddIsFlakyTestRetriesEnabled: false,
683
682
  _ddFlakyTestRetriesCount: 0,
683
+ _ddFlakyTestRetriesUseRetryMarker: false,
684
684
  _ddIsDynamicAtrEnabled: false,
685
685
  _ddDynamicAtrBuckets: undefined,
686
686
  _ddFlakyTestRetriesIncludesUnnamedProject: false,
@@ -824,7 +824,7 @@ async function runMainProcessSetup (
824
824
  }
825
825
  }
826
826
 
827
- const flakyTestRetriesConfiguration = configureFlakyTestRetries(ctx, testSpecifications)
827
+ const flakyTestRetriesConfiguration = configureFlakyTestRetries(ctx, testSpecifications, frameworkVersion)
828
828
  if (flakyTestRetriesConfiguration) {
829
829
  setProvidedContext(ctx, {
830
830
  _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
@@ -833,6 +833,7 @@ async function runMainProcessSetup (
833
833
  _ddDynamicAtrBuckets: dynamicAtrBuckets,
834
834
  _ddFlakyTestRetriesIncludesUnnamedProject: flakyTestRetriesConfiguration.includesUnnamedProject,
835
835
  _ddFlakyTestRetriesProjectNames: flakyTestRetriesConfiguration.projectNames,
836
+ _ddFlakyTestRetriesUseRetryMarker: flakyTestRetriesConfiguration.usesRetryMarker,
836
837
  }, 'Could not send library configuration to workers.')
837
838
  }
838
839
 
@@ -1039,7 +1040,7 @@ function shouldUseBrowserReporter (frameworkVersion, testSpecifications) {
1039
1040
  testSpecifications.some(isBrowserTestSpecification)
1040
1041
  }
1041
1042
 
1042
- function configureFlakyTestRetries (ctx, testSpecifications) {
1043
+ function configureFlakyTestRetries (ctx, testSpecifications, frameworkVersion) {
1043
1044
  if (!isFlakyTestRetriesEnabled || (!isDynamicAtrEnabled && flakyTestRetriesCount <= 0)) return
1044
1045
 
1045
1046
  const maximumDynamicAtrRetries = dynamicAtrBuckets
@@ -1048,13 +1049,14 @@ function configureFlakyTestRetries (ctx, testSpecifications) {
1048
1049
  const retryCount = isDynamicAtrEnabled
1049
1050
  ? Math.max(1, maximumDynamicAtrRetries)
1050
1051
  : flakyTestRetriesCount
1052
+ const usesRetryMarker = isDynamicAtrEnabled || satisfies(frameworkVersion, '>=4.1.0')
1051
1053
  let configured = false
1052
1054
  let includesUnnamedProject = false
1053
1055
  const projectNames = []
1054
1056
  for (const { config, projectName } of getVitestProjectConfigs(ctx, testSpecifications)) {
1055
1057
  if (!config.retry || config.retry.__ddTestOptAtr) {
1056
1058
  // The serializable marker survives task inheritance and setup refreshes, unlike numeric retry counts.
1057
- config.retry = isDynamicAtrEnabled ? { count: retryCount, __ddTestOptAtr: true } : retryCount
1059
+ config.retry = usesRetryMarker ? { count: retryCount, __ddTestOptAtr: true } : retryCount
1058
1060
  configured = true
1059
1061
  if (projectName) {
1060
1062
  projectNames.push(projectName)
@@ -1069,6 +1071,7 @@ function configureFlakyTestRetries (ctx, testSpecifications) {
1069
1071
  return {
1070
1072
  includesUnnamedProject,
1071
1073
  projectNames,
1074
+ usesRetryMarker,
1072
1075
  }
1073
1076
  }
1074
1077
 
@@ -1201,11 +1204,7 @@ function getSortWrapper (sort, frameworkVersion) {
1201
1204
 
1202
1205
  function getFinishWrapper (exitOrClose) {
1203
1206
  return async function () {
1204
- if (finishedSessionContexts.has(this)) {
1205
- return exitOrClose.apply(this, arguments)
1206
- }
1207
-
1208
- if (!testSessionFinishCh.hasSubscribers) {
1207
+ if (finishedSessionContexts.has(this) || !testSessionFinishCh.hasSubscribers) {
1209
1208
  return exitOrClose.apply(this, arguments)
1210
1209
  }
1211
1210
  finishedSessionContexts.add(this)
@@ -1230,15 +1229,15 @@ function getFinishWrapper (exitOrClose) {
1230
1229
  const tests = getTypeTasks(this.state.getFiles())
1231
1230
  const hasExecutedTests = tests.some(test =>
1232
1231
  test.mode !== 'skip' && test.mode !== 'todo' &&
1233
- test.result?.state !== 'skip' && test.result?.state !== 'todo'
1232
+ (test.result?.state === 'pass' || test.result?.state === 'fail')
1234
1233
  )
1235
1234
  const hasErrors = hasRunError || error ||
1236
1235
  this.state.getCountOfFailedTests() > 0 || this.state.getUnhandledErrors().length > 0
1237
- const testSessionEmptyReason = !hasErrors && !hasExecutedTests
1238
- ? (isEmptyShard
1239
- ? 'zero_test_shard'
1240
- : tests.length > 0 || skippedSuites.length > 0 ? 'all_tests_skipped' : 'zero_tests')
1241
- : undefined
1236
+ const testSessionEmptyReason = hasErrors || hasExecutedTests
1237
+ ? undefined
1238
+ : isEmptyShard
1239
+ ? 'zero_test_shard'
1240
+ : tests.length > 0 || skippedSuites.length > 0 ? 'all_tests_skipped' : 'zero_tests'
1242
1241
  const status = hasErrors ? 'fail' : (testSessionEmptyReason ? 'skip' : 'pass')
1243
1242
  const flushPromise = getChannelPromise(testSessionFinishCh, {
1244
1243
  status,
@@ -1758,8 +1757,7 @@ async function reportTypecheckFile (file, sessionConfiguration, frameworkVersion
1758
1757
  }
1759
1758
 
1760
1759
  async function reportTypecheckResults (result, frameworkVersion, ctx, typechecker, files = result?.files) {
1761
- if (!testSuiteFinishCh.hasSubscribers) return
1762
- if (!Array.isArray(result?.files) || !Array.isArray(files)) return
1760
+ if (!testSuiteFinishCh.hasSubscribers || !Array.isArray(result?.files) || !Array.isArray(files)) return
1763
1761
 
1764
1762
  const setupState = ctx && mainProcessSetupStates.get(ctx)
1765
1763
  if (
@@ -33,7 +33,7 @@ const testManagementTestsCh = channel('ci:vitest:test-management-tests')
33
33
  const modifiedFilesCh = channel('ci:vitest:modified-files')
34
34
 
35
35
  const CLOSING_SCRIPT_TAG_RE = /<\/script/i
36
- const SERIALIZED_CONTEXT_PREFIX = '\u0000dd-vitest-context:'
36
+ const SERIALIZED_CONTEXT_PREFIX = '\0dd-vitest-context:'
37
37
 
38
38
  const workerReportTraceCh = channel('ci:vitest:worker-report:trace')
39
39
  const workerReportCoverageCh = channel('ci:vitest:worker-report:coverage')
@@ -182,6 +182,7 @@ function getProvidedContext () {
182
182
  _ddDynamicAtrBuckets: dynamicAtrBuckets,
183
183
  _ddFlakyTestRetriesIncludesUnnamedProject: flakyTestRetriesIncludesUnnamedProject,
184
184
  _ddFlakyTestRetriesProjectNames: flakyTestRetriesProjectNames,
185
+ _ddFlakyTestRetriesUseRetryMarker: flakyTestRetriesUseRetryMarker,
185
186
  _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
186
187
  _ddTestSessionId: testSessionId,
187
188
  _ddTestModuleId: testModuleId,
@@ -211,6 +212,7 @@ function getProvidedContext () {
211
212
  dynamicAtrBuckets,
212
213
  flakyTestRetriesIncludesUnnamedProject,
213
214
  flakyTestRetriesProjectNames,
215
+ flakyTestRetriesUseRetryMarker,
214
216
  isImpactedTestsEnabled,
215
217
  testSessionId,
216
218
  testModuleId,
@@ -254,8 +256,9 @@ function getProvidedContext () {
254
256
  }
255
257
 
256
258
  function isFlakyTestRetriesEnabledForTask (providedContext, task) {
257
- if (!providedContext.isFlakyTestRetriesEnabled) return false
258
- if (providedContext.isDynamicAtrEnabled && !task.retry?.__ddTestOptAtr) return false
259
+ if (!providedContext.isFlakyTestRetriesEnabled || task.meta?.__ddTestOptNativeRetry) return false
260
+ if ((providedContext.isDynamicAtrEnabled || providedContext.flakyTestRetriesUseRetryMarker) &&
261
+ !task.retry?.__ddTestOptAtr) return false
259
262
 
260
263
  const { flakyTestRetriesProjectNames } = providedContext
261
264
  if (!Array.isArray(flakyTestRetriesProjectNames)) return true
@@ -299,7 +302,7 @@ function getVitestTestProperties (providedContext, testSuiteAbsolutePath, testNa
299
302
 
300
303
  return {
301
304
  testSuite: testProperties?.testSuite,
302
- isNew: Array.isArray(knownTests) ? !knownTests.includes(testName) : false,
305
+ isNew: Array.isArray(knownTests) && !knownTests.includes(testName),
303
306
  isModified: testProperties?.isModified === true,
304
307
  isAttemptToFix: testManagementProperties.isAttemptToFix,
305
308
  isDisabled: testManagementProperties.isDisabled,
@@ -354,8 +354,7 @@ function hasConcurrentTask (tasks) {
354
354
  if (!tasks) return false
355
355
 
356
356
  for (const task of tasks) {
357
- if (task.concurrent === true) return true
358
- if (hasConcurrentTask(task.tasks)) return true
357
+ if (task.concurrent === true || hasConcurrentTask(task.tasks)) return true
359
358
  }
360
359
 
361
360
  return false
@@ -2,10 +2,18 @@
2
2
 
3
3
  const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
4
4
  const { isTrue } = require('../../dd-trace/src/util')
5
+ const { channel } = require('./helpers/instrument')
5
6
 
6
- const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
7
+ channel('tracing:orchestrion:@vitest/runner:createTestTask:end').subscribe(({ arguments: args, result: task }) => {
8
+ // Suite options have already been merged here, including overrides equal to ATR's count.
9
+ if (args[1]?.retry !== undefined && task?.meta) {
10
+ task.meta = { ...task.meta, __ddTestOptNativeRetry: true }
11
+ }
12
+ })
7
13
 
8
14
  if (getEnvironmentVariable('DD_VITEST_WORKER') === '1') {
15
+ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
16
+
9
17
  // In no-worker-init mode the main process reports Vitest events from reporter hooks.
10
18
  // Loading worker instrumentation here would reintroduce the worker tracer path.
11
19
  // eslint-disable-next-line eslint-rules/eslint-process-env
@@ -297,8 +297,7 @@ async function preloadRumNavigation () {
297
297
  try {
298
298
  const browser = this
299
299
  const testExecutionId = getRumTestExecutionId(browser)
300
- if (!canCorrelateRumBrowser(browser)) return
301
- if (!rumRunnerBrowsers.has(browser)) return
300
+ if (!canCorrelateRumBrowser(browser) || !rumRunnerBrowsers.has(browser)) return
302
301
 
303
302
  retainRumBrowser(browser)
304
303
  if (!testExecutionId) return
@@ -371,9 +370,7 @@ async function handleRumNavigation (context) {
371
370
  log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
372
371
  }
373
372
  const testExecutionId = getRumTestExecutionId(browser, isRumActive)
374
- if (sampledOut) return
375
-
376
- if (!testExecutionId) {
373
+ if (sampledOut || !testExecutionId) {
377
374
  return
378
375
  }
379
376
  if (!isRumActive && browser.isBidi) return
@@ -1736,9 +1733,7 @@ function handleWorkerMessage (state, workerRecord, message) {
1736
1733
  requestId,
1737
1734
  },
1738
1735
  })
1739
- if (!requestId || !uploadCh.hasSubscribers) {
1740
- respond(new Error('WebdriverIO media upload is not available'))
1741
- } else {
1736
+ if (requestId && uploadCh.hasSubscribers) {
1742
1737
  try {
1743
1738
  let signal
1744
1739
  if (isVideo) {
@@ -1754,6 +1749,8 @@ function handleWorkerMessage (state, workerRecord, message) {
1754
1749
  log.error('WebdriverIO media upload error: %s', error?.message || String(error))
1755
1750
  respond(error)
1756
1751
  }
1752
+ } else {
1753
+ respond(new Error('WebdriverIO media upload is not available'))
1757
1754
  }
1758
1755
  return
1759
1756
  }