dd-trace 5.103.0 → 5.105.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 (213) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +107 -6
  3. package/package.json +18 -17
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +15 -2
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
  11. package/packages/datadog-instrumentations/src/cucumber.js +181 -35
  12. package/packages/datadog-instrumentations/src/dns.js +54 -18
  13. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  14. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  15. package/packages/datadog-instrumentations/src/graphql.js +188 -67
  16. package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
  17. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  18. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  21. package/packages/datadog-instrumentations/src/helpers/kafka.js +17 -0
  22. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  23. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  24. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -6
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +31 -229
  32. package/packages/datadog-instrumentations/src/hono.js +54 -3
  33. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  34. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  35. package/packages/datadog-instrumentations/src/ioredis.js +3 -3
  36. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  37. package/packages/datadog-instrumentations/src/jest.js +390 -183
  38. package/packages/datadog-instrumentations/src/kafkajs.js +140 -17
  39. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  40. package/packages/datadog-instrumentations/src/memcached.js +2 -1
  41. package/packages/datadog-instrumentations/src/mocha/main.js +399 -107
  42. package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
  43. package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
  44. package/packages/datadog-instrumentations/src/mongoose.js +10 -12
  45. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  46. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  47. package/packages/datadog-instrumentations/src/nats.js +182 -0
  48. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  49. package/packages/datadog-instrumentations/src/openai.js +33 -18
  50. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  51. package/packages/datadog-instrumentations/src/pg.js +1 -1
  52. package/packages/datadog-instrumentations/src/pino.js +17 -5
  53. package/packages/datadog-instrumentations/src/playwright.js +537 -297
  54. package/packages/datadog-instrumentations/src/router.js +80 -34
  55. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  56. package/packages/datadog-instrumentations/src/vitest.js +246 -149
  57. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  58. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  59. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  60. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  61. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  62. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  64. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +223 -45
  65. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  66. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  67. package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
  68. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  69. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  71. package/packages/datadog-plugin-graphql/src/utils.js +4 -1
  72. package/packages/datadog-plugin-http/src/server.js +40 -15
  73. package/packages/datadog-plugin-jest/src/index.js +11 -3
  74. package/packages/datadog-plugin-jest/src/util.js +15 -8
  75. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  76. package/packages/datadog-plugin-kafkajs/src/producer.js +35 -0
  77. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  78. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  79. package/packages/datadog-plugin-mongodb-core/src/index.js +311 -35
  80. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  81. package/packages/datadog-plugin-nats/src/index.js +20 -0
  82. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  83. package/packages/datadog-plugin-nats/src/util.js +33 -0
  84. package/packages/datadog-plugin-next/src/index.js +5 -3
  85. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  86. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  87. package/packages/datadog-plugin-pino/src/index.js +42 -0
  88. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  89. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  90. package/packages/datadog-plugin-redis/src/index.js +37 -2
  91. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  92. package/packages/datadog-plugin-router/src/index.js +33 -44
  93. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  94. package/packages/datadog-plugin-undici/src/index.js +19 -0
  95. package/packages/datadog-plugin-vitest/src/index.js +24 -20
  96. package/packages/datadog-plugin-winston/src/index.js +30 -0
  97. package/packages/datadog-shimmer/src/shimmer.js +49 -21
  98. package/packages/dd-trace/src/aiguard/index.js +1 -1
  99. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  100. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  101. package/packages/dd-trace/src/appsec/blocking.js +2 -2
  102. package/packages/dd-trace/src/appsec/index.js +11 -4
  103. package/packages/dd-trace/src/appsec/reporter.js +24 -11
  104. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  105. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  106. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  107. package/packages/dd-trace/src/baggage.js +7 -1
  108. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  109. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  110. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
  111. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
  112. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  113. package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -2
  114. package/packages/dd-trace/src/config/supported-configurations.json +36 -8
  115. package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
  116. package/packages/dd-trace/src/datastreams/context.js +4 -2
  117. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  118. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  119. package/packages/dd-trace/src/encode/0.4.js +124 -108
  120. package/packages/dd-trace/src/encode/0.5.js +114 -26
  121. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +57 -42
  122. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  123. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  124. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  125. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  126. package/packages/dd-trace/src/exporters/common/agents.js +3 -1
  127. package/packages/dd-trace/src/exporters/common/request.js +3 -1
  128. package/packages/dd-trace/src/id.js +17 -4
  129. package/packages/dd-trace/src/lambda/handler.js +2 -4
  130. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  131. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  134. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  135. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  136. package/packages/dd-trace/src/llmobs/sdk.js +10 -16
  137. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  138. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  139. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  140. package/packages/dd-trace/src/llmobs/util.js +66 -3
  141. package/packages/dd-trace/src/log/index.js +1 -1
  142. package/packages/dd-trace/src/log/writer.js +3 -1
  143. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  144. package/packages/dd-trace/src/msgpack/index.js +96 -2
  145. package/packages/dd-trace/src/noop/span.js +3 -1
  146. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  147. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  148. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  149. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  150. package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
  151. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  152. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  153. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  154. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  155. package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
  156. package/packages/dd-trace/src/opentracing/span.js +59 -19
  157. package/packages/dd-trace/src/opentracing/span_context.js +49 -0
  158. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  159. package/packages/dd-trace/src/plugins/ci_plugin.js +23 -33
  160. package/packages/dd-trace/src/plugins/database.js +7 -6
  161. package/packages/dd-trace/src/plugins/index.js +4 -0
  162. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  163. package/packages/dd-trace/src/plugins/log_plugin.js +3 -46
  164. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  165. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  166. package/packages/dd-trace/src/plugins/tracing.js +48 -8
  167. package/packages/dd-trace/src/plugins/util/git.js +3 -1
  168. package/packages/dd-trace/src/plugins/util/test.js +318 -13
  169. package/packages/dd-trace/src/plugins/util/web.js +89 -64
  170. package/packages/dd-trace/src/priority_sampler.js +2 -2
  171. package/packages/dd-trace/src/profiling/profiler.js +2 -2
  172. package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
  173. package/packages/dd-trace/src/sampling_rule.js +7 -7
  174. package/packages/dd-trace/src/scope.js +7 -5
  175. package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
  176. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  177. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  178. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  179. package/packages/dd-trace/src/span_format.js +190 -58
  180. package/packages/dd-trace/src/spanleak.js +1 -1
  181. package/packages/dd-trace/src/standalone/index.js +3 -3
  182. package/packages/dd-trace/src/tagger.js +0 -2
  183. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  184. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  185. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  186. package/vendor/dist/protobufjs/index.js +1 -1
  187. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  188. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  189. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
  190. package/vendor/dist/opentracing/LICENSE +0 -201
  191. package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
  192. package/vendor/dist/opentracing/constants.d.ts +0 -61
  193. package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
  194. package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
  195. package/vendor/dist/opentracing/functions.d.ts +0 -20
  196. package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
  197. package/vendor/dist/opentracing/index.d.ts +0 -12
  198. package/vendor/dist/opentracing/index.js +0 -1
  199. package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
  200. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
  201. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
  202. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
  203. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
  204. package/vendor/dist/opentracing/noop.d.ts +0 -8
  205. package/vendor/dist/opentracing/reference.d.ts +0 -33
  206. package/vendor/dist/opentracing/span.d.ts +0 -147
  207. package/vendor/dist/opentracing/span_context.d.ts +0 -26
  208. package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
  209. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
  210. package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
  211. package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
  212. package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
  213. package/vendor/dist/opentracing/tracer.d.ts +0 -127
@@ -19,6 +19,7 @@ const {
19
19
  collectTestOptimizationSummariesFromTraces,
20
20
  logAttemptToFixTestExecution,
21
21
  logTestOptimizationSummary,
22
+ getTestOptimizationRequestResults,
22
23
  } = require('../../dd-trace/src/plugins/util/test')
23
24
  const { addHook, channel } = require('./helpers/instrument')
24
25
 
@@ -43,6 +44,7 @@ const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
43
44
  // test session hooks
44
45
  const testSessionStartCh = channel('ci:vitest:session:start')
45
46
  const testSessionFinishCh = channel('ci:vitest:session:finish')
47
+ const testSessionConfigurationCh = channel('ci:vitest:session:configuration')
46
48
  const libraryConfigurationCh = channel('ci:vitest:library-configuration')
47
49
  const knownTestsCh = channel('ci:vitest:known-tests')
48
50
  const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detection-faulty')
@@ -74,6 +76,9 @@ let isRetryReasonEfd = false
74
76
  let isRetryReasonAttemptToFix = false
75
77
  const switchedStatuses = new WeakSet()
76
78
  const workerProcesses = new WeakSet()
79
+ const mainProcessSetupPromises = new WeakMap()
80
+ const coverageWrappedProviders = new WeakSet()
81
+ const finishWrappedContexts = new WeakSet()
77
82
  let isFlakyTestRetriesEnabled = false
78
83
  let flakyTestRetriesCount = 0
79
84
  let isEarlyFlakeDetectionEnabled = false
@@ -134,6 +139,9 @@ function getProvidedContext () {
134
139
  _ddFlakyTestRetriesCount: flakyTestRetriesCount,
135
140
  _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
136
141
  _ddModifiedFiles: modifiedFiles,
142
+ _ddTestSessionId: testSessionId,
143
+ _ddTestModuleId: testModuleId,
144
+ _ddTestCommand: testCommand,
137
145
  } = globalThis.__vitest_worker__.providedContext
138
146
 
139
147
  return {
@@ -150,6 +158,9 @@ function getProvidedContext () {
150
158
  flakyTestRetriesCount: flakyTestRetriesCount ?? 0,
151
159
  isImpactedTestsEnabled,
152
160
  modifiedFiles,
161
+ testSessionId,
162
+ testModuleId,
163
+ testCommand,
153
164
  }
154
165
  } catch {
155
166
  log.error('Vitest workers could not parse provided context, so some features will not work.')
@@ -167,6 +178,9 @@ function getProvidedContext () {
167
178
  flakyTestRetriesCount: 0,
168
179
  isImpactedTestsEnabled: false,
169
180
  modifiedFiles: {},
181
+ testSessionId: undefined,
182
+ testModuleId: undefined,
183
+ testCommand: undefined,
170
184
  }
171
185
  }
172
186
  }
@@ -217,6 +231,10 @@ function getTestRunnerExport (testPackage) {
217
231
  return findExportByName(testPackage, 'VitestTestRunner') || findExportByName(testPackage, 'TestRunner')
218
232
  }
219
233
 
234
+ function getVitestExport (vitestPackage) {
235
+ return findExportByName(vitestPackage, 'Vitest')
236
+ }
237
+
220
238
  function getForksPoolWorkerExport (vitestPackage) {
221
239
  return findExportByName(vitestPackage, 'ForksPoolWorker')
222
240
  }
@@ -329,170 +347,223 @@ function wrapBeforeEachCleanupResult (task, result) {
329
347
  return result
330
348
  }
331
349
 
332
- function getSortWrapper (sort, frameworkVersion) {
333
- return async function () {
334
- if (!testSessionFinishCh.hasSubscribers) {
335
- return sort.apply(this, arguments)
336
- }
337
- // There isn't any other async function that we seem to be able to hook into
338
- // So we will use the sort from BaseSequencer. This means that a custom sequencer
339
- // will not work. This will be a known limitation.
350
+ function getWorkspaceProject (ctx) {
351
+ return ctx.getCoreWorkspaceProject
352
+ ? ctx.getCoreWorkspaceProject()
353
+ : ctx.getRootProject()
354
+ }
355
+
356
+ function setProvidedContext (ctx, values, warningMessage) {
357
+ try {
358
+ Object.assign(getWorkspaceProject(ctx)._provided, values)
359
+ } catch {
360
+ log.warn(warningMessage)
361
+ }
362
+ }
363
+
364
+ function getTestFilepathsFromSpecifications (testSpecifications) {
365
+ if (!Array.isArray(testSpecifications) || !testSpecifications.length) {
366
+ return
367
+ }
368
+
369
+ return testSpecifications.map(testSpecification => {
370
+ const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
371
+ return testFile?.moduleId || testFile?.filepath || testFile
372
+ })
373
+ }
374
+
375
+ function getTestFilepaths (ctx, testSpecifications) {
376
+ const testFilepaths = getTestFilepathsFromSpecifications(testSpecifications)
377
+ if (testFilepaths) {
378
+ return testFilepaths
379
+ }
380
+
381
+ const getFilePaths = ctx.getTestFilepaths || ctx._globTestFilepaths
382
+ return getFilePaths.call(ctx)
383
+ }
384
+
385
+ function wrapCoverageProvider (ctx) {
386
+ const { coverageProvider } = ctx
387
+ if (!coverageProvider?.generateCoverage || coverageWrappedProviders.has(coverageProvider)) {
388
+ return
389
+ }
390
+ coverageWrappedProviders.add(coverageProvider)
391
+
392
+ // Capture coverage root directory from config (default is 'coverage' in cwd)
393
+ try {
394
+ const coverageConfig = ctx.config?.coverage
395
+ const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
396
+ const rootDir = ctx.config?.root || process.cwd()
397
+ coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
398
+ } catch {
399
+ // Fallback to cwd if we can't get config
400
+ coverageRootDir = process.cwd()
401
+ }
402
+
403
+ shimmer.wrap(coverageProvider, 'generateCoverage', generateCoverage => async function () {
404
+ const totalCodeCoverage = await generateCoverage.apply(this, arguments)
340
405
 
341
406
  try {
342
- const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
343
- if (!err) {
344
- isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
345
- flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
346
- isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
347
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
348
- earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
349
- isDiEnabled = libraryConfig.isDiEnabled
350
- isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
351
- isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
352
- testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
353
- isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
354
- }
407
+ testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
355
408
  } catch {
356
- isFlakyTestRetriesEnabled = false
357
- isEarlyFlakeDetectionEnabled = false
358
- isDiEnabled = false
359
- isKnownTestsEnabled = false
360
- isImpactedTestsEnabled = false
409
+ // ignore errors
361
410
  }
411
+ return totalCodeCoverage
412
+ })
413
+ }
362
414
 
363
- if (isFlakyTestRetriesEnabled && !this.ctx.config.retry && flakyTestRetriesCount > 0) {
364
- this.ctx.config.retry = flakyTestRetriesCount
365
- try {
366
- const workspaceProject = this.ctx.getCoreWorkspaceProject
367
- ? this.ctx.getCoreWorkspaceProject()
368
- : this.ctx.getRootProject()
369
- workspaceProject._provided._ddIsFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
370
- workspaceProject._provided._ddFlakyTestRetriesCount = flakyTestRetriesCount
371
- } catch {
372
- log.warn('Could not send library configuration to workers.')
373
- }
374
- }
415
+ function wrapSessionFinish (ctx) {
416
+ if (finishWrappedContexts.has(ctx)) {
417
+ return
418
+ }
419
+ finishWrappedContexts.add(ctx)
375
420
 
376
- if (isKnownTestsEnabled) {
377
- const knownTestsResponse = await getChannelPromise(knownTestsCh)
378
- if (knownTestsResponse.err) {
379
- isEarlyFlakeDetectionEnabled = false
380
- } else {
381
- const knownTests = knownTestsResponse.knownTests
382
- const getFilePaths = this.ctx.getTestFilepaths || this.ctx._globTestFilepaths
383
-
384
- const testFilepaths = await getFilePaths.call(this.ctx)
385
-
386
- if (isValidKnownTests(knownTests)) {
387
- isEarlyFlakeDetectionFaultyCh.publish({
388
- knownTests: knownTests.vitest,
389
- testFilepaths,
390
- onDone: (isFaulty) => {
391
- isEarlyFlakeDetectionFaulty = isFaulty
392
- },
393
- })
394
- if (isEarlyFlakeDetectionFaulty) {
395
- isEarlyFlakeDetectionEnabled = false
396
- log.warn('New test detection is disabled because the number of new tests is too high.')
397
- } else {
398
- // TODO: use this to pass session and module IDs to the worker, instead of polluting process.env
399
- // Note: setting this.ctx.config.provide directly does not work because it's cached
400
- try {
401
- const workspaceProject = this.ctx.getCoreWorkspaceProject
402
- ? this.ctx.getCoreWorkspaceProject()
403
- : this.ctx.getRootProject()
404
- workspaceProject._provided._ddIsKnownTestsEnabled = isKnownTestsEnabled
405
- workspaceProject._provided._ddKnownTests = knownTests
406
- workspaceProject._provided._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
407
- workspaceProject._provided._ddEarlyFlakeDetectionNumRetries =
408
- getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries)
409
- workspaceProject._provided._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
410
- } catch {
411
- log.warn('Could not send known tests to workers so Early Flake Detection will not work.')
412
- }
413
- }
414
- } else {
415
- isEarlyFlakeDetectionFaulty = true
416
- isEarlyFlakeDetectionEnabled = false
417
- }
418
- }
419
- }
421
+ shimmer.wrap(ctx, 'exit', getFinishWrapper)
422
+ shimmer.wrap(ctx, 'close', getFinishWrapper)
423
+ }
420
424
 
421
- if (isDiEnabled) {
422
- try {
423
- const workspaceProject = this.ctx.getCoreWorkspaceProject
424
- ? this.ctx.getCoreWorkspaceProject()
425
- : this.ctx.getRootProject()
426
- workspaceProject._provided._ddIsDiEnabled = isDiEnabled
427
- } catch {
428
- log.warn('Could not send Dynamic Instrumentation configuration to workers.')
429
- }
430
- }
425
+ async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
426
+ if (!testSessionFinishCh.hasSubscribers) {
427
+ return
428
+ }
431
429
 
432
- if (isTestManagementTestsEnabled) {
433
- const { err, testManagementTests: receivedTestManagementTests } = await getChannelPromise(testManagementTestsCh)
434
- if (err) {
435
- isTestManagementTestsEnabled = false
436
- log.error('Could not get test management tests.')
437
- } else {
438
- const testManagementTests = receivedTestManagementTests
439
- try {
440
- const workspaceProject = this.ctx.getCoreWorkspaceProject
441
- ? this.ctx.getCoreWorkspaceProject()
442
- : this.ctx.getRootProject()
443
- workspaceProject._provided._ddIsTestManagementTestsEnabled = isTestManagementTestsEnabled
444
- workspaceProject._provided._ddTestManagementAttemptToFixRetries = testManagementAttemptToFixRetries
445
- workspaceProject._provided._ddTestManagementTests = testManagementTests
446
- } catch {
447
- log.warn('Could not send test management tests to workers so Test Management will not work.')
448
- }
449
- }
430
+ try {
431
+ const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
432
+ if (!err) {
433
+ isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
434
+ flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
435
+ isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
436
+ earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
437
+ earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
438
+ isDiEnabled = libraryConfig.isDiEnabled
439
+ isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
440
+ isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
441
+ testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
442
+ isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
450
443
  }
444
+ } catch {
445
+ isFlakyTestRetriesEnabled = false
446
+ isEarlyFlakeDetectionEnabled = false
447
+ isDiEnabled = false
448
+ isKnownTestsEnabled = false
449
+ isImpactedTestsEnabled = false
450
+ }
451
451
 
452
- if (isImpactedTestsEnabled) {
453
- const { err, modifiedFiles } = await getChannelPromise(modifiedFilesCh)
454
- if (err) {
455
- log.error('Could not get modified tests.')
456
- } else {
457
- try {
458
- const workspaceProject = this.ctx.getCoreWorkspaceProject
459
- ? this.ctx.getCoreWorkspaceProject()
460
- : this.ctx.getRootProject()
461
- workspaceProject._provided._ddIsImpactedTestsEnabled = isImpactedTestsEnabled
462
- workspaceProject._provided._ddModifiedFiles = modifiedFiles
463
- } catch {
464
- log.warn('Could not send modified tests to workers so Impacted Tests will not work.')
452
+ if (testSessionConfigurationCh.hasSubscribers) {
453
+ const { testSessionId, testModuleId, testCommand } = await getChannelPromise(
454
+ testSessionConfigurationCh,
455
+ frameworkVersion
456
+ )
457
+ setProvidedContext(ctx, {
458
+ _ddTestSessionId: testSessionId,
459
+ _ddTestModuleId: testModuleId,
460
+ _ddTestCommand: testCommand,
461
+ }, 'Could not send test session configuration to workers.')
462
+ }
463
+
464
+ const {
465
+ knownTestsResponse,
466
+ testManagementTestsResponse,
467
+ } = await getTestOptimizationRequestResults({
468
+ isKnownTestsEnabled,
469
+ isTestManagementTestsEnabled,
470
+ getKnownTests: () => getChannelPromise(knownTestsCh),
471
+ getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
472
+ })
473
+
474
+ if (isFlakyTestRetriesEnabled && !ctx.config.retry && flakyTestRetriesCount > 0) {
475
+ ctx.config.retry = flakyTestRetriesCount
476
+ setProvidedContext(ctx, {
477
+ _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
478
+ _ddFlakyTestRetriesCount: flakyTestRetriesCount,
479
+ }, 'Could not send library configuration to workers.')
480
+ }
481
+
482
+ if (isKnownTestsEnabled) {
483
+ const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
484
+ if (currentKnownTestsResponse.err) {
485
+ isEarlyFlakeDetectionEnabled = false
486
+ } else {
487
+ const knownTests = currentKnownTestsResponse.knownTests
488
+ const testFilepaths = await getTestFilepaths(ctx, testSpecifications)
489
+
490
+ if (isValidKnownTests(knownTests)) {
491
+ isEarlyFlakeDetectionFaultyCh.publish({
492
+ knownTests: knownTests.vitest,
493
+ testFilepaths,
494
+ onDone: (isFaulty) => {
495
+ isEarlyFlakeDetectionFaulty = isFaulty
496
+ },
497
+ })
498
+ if (isEarlyFlakeDetectionFaulty) {
499
+ isEarlyFlakeDetectionEnabled = false
500
+ log.warn('New test detection is disabled because the number of new tests is too high.')
501
+ } else {
502
+ setProvidedContext(ctx, {
503
+ _ddIsKnownTestsEnabled: isKnownTestsEnabled,
504
+ _ddKnownTests: knownTests,
505
+ _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
506
+ _ddEarlyFlakeDetectionNumRetries:
507
+ getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries),
508
+ _ddEarlyFlakeDetectionSlowTestRetries: earlyFlakeDetectionSlowTestRetries,
509
+ }, 'Could not send known tests to workers so Early Flake Detection will not work.')
465
510
  }
511
+ } else {
512
+ isEarlyFlakeDetectionFaulty = true
513
+ isEarlyFlakeDetectionEnabled = false
466
514
  }
467
515
  }
516
+ }
468
517
 
469
- if (this.ctx.coverageProvider?.generateCoverage) {
470
- // Capture coverage root directory from config (default is 'coverage' in cwd)
471
- try {
472
- const coverageConfig = this.ctx.config?.coverage
473
- const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
474
- const rootDir = this.ctx.config?.root || process.cwd()
475
- coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
476
- } catch {
477
- // Fallback to cwd if we can't get config
478
- coverageRootDir = process.cwd()
479
- }
518
+ if (isDiEnabled) {
519
+ setProvidedContext(ctx, {
520
+ _ddIsDiEnabled: isDiEnabled,
521
+ }, 'Could not send Dynamic Instrumentation configuration to workers.')
522
+ }
480
523
 
481
- shimmer.wrap(this.ctx.coverageProvider, 'generateCoverage', generateCoverage => async function () {
482
- const totalCodeCoverage = await generateCoverage.apply(this, arguments)
524
+ if (isTestManagementTestsEnabled) {
525
+ const { err, testManagementTests: receivedTestManagementTests } =
526
+ testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
527
+ if (err) {
528
+ isTestManagementTestsEnabled = false
529
+ log.error('Could not get test management tests.')
530
+ } else {
531
+ setProvidedContext(ctx, {
532
+ _ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
533
+ _ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
534
+ _ddTestManagementTests: receivedTestManagementTests,
535
+ }, 'Could not send test management tests to workers so Test Management will not work.')
536
+ }
537
+ }
483
538
 
484
- try {
485
- testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
486
- } catch {
487
- // ignore errors
488
- }
489
- return totalCodeCoverage
490
- })
539
+ if (isImpactedTestsEnabled) {
540
+ const { err, modifiedFiles } = await getChannelPromise(modifiedFilesCh)
541
+ if (err) {
542
+ log.error('Could not get modified tests.')
543
+ } else {
544
+ setProvidedContext(ctx, {
545
+ _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
546
+ _ddModifiedFiles: modifiedFiles,
547
+ }, 'Could not send modified tests to workers so Impacted Tests will not work.')
491
548
  }
549
+ }
492
550
 
493
- shimmer.wrap(this.ctx, 'exit', getFinishWrapper)
494
- shimmer.wrap(this.ctx, 'close', getFinishWrapper)
551
+ wrapCoverageProvider(ctx)
552
+ wrapSessionFinish(ctx)
553
+ }
495
554
 
555
+ function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
556
+ let setupPromise = mainProcessSetupPromises.get(ctx)
557
+ if (!setupPromise) {
558
+ setupPromise = runMainProcessSetup(ctx, frameworkVersion, testSpecifications)
559
+ mainProcessSetupPromises.set(ctx, setupPromise)
560
+ }
561
+ return setupPromise
562
+ }
563
+
564
+ function getSortWrapper (sort, frameworkVersion) {
565
+ return async function () {
566
+ await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
496
567
  return sort.apply(this, arguments)
497
568
  }
498
569
  }
@@ -504,6 +575,11 @@ function getFinishWrapper (exitOrClose) {
504
575
  return exitOrClose.apply(this, arguments)
505
576
  }
506
577
  isClosed = true
578
+
579
+ if (!testSessionFinishCh.hasSubscribers) {
580
+ return exitOrClose.apply(this, arguments)
581
+ }
582
+
507
583
  let onFinish
508
584
 
509
585
  const flushPromise = new Promise(resolve => {
@@ -555,6 +631,17 @@ function getCliOrStartVitestWrapper (frameworkVersion) {
555
631
  }
556
632
  }
557
633
 
634
+ function wrapVitestRunFiles (Vitest, frameworkVersion) {
635
+ if (!Vitest?.prototype?.runFiles) {
636
+ return
637
+ }
638
+
639
+ shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
640
+ await ensureMainProcessSetup(this, frameworkVersion, testSpecifications)
641
+ return runFiles.apply(this, arguments)
642
+ })
643
+ }
644
+
558
645
  function getCreateCliWrapper (vitestPackage, frameworkVersion) {
559
646
  const createCliExport = findExportByName(vitestPackage, 'createCLI')
560
647
  if (!createCliExport) {
@@ -658,6 +745,11 @@ function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
658
745
  const startVitestExport = findExportByName(cliApiPackage, 'startVitest')
659
746
  shimmer.wrap(cliApiPackage, startVitestExport.key, getCliOrStartVitestWrapper(frameworkVersion))
660
747
 
748
+ const vitest = getVitestExport(cliApiPackage)
749
+ if (vitest) {
750
+ wrapVitestRunFiles(vitest.value, frameworkVersion)
751
+ }
752
+
661
753
  const forksPoolWorker = getForksPoolWorkerExport(cliApiPackage)
662
754
  if (forksPoolWorker) {
663
755
  // function is async
@@ -921,7 +1013,6 @@ function wrapVitestTestRunner (VitestTestRunner) {
921
1013
  // Here we finish the earlier iteration,
922
1014
  // as long as it's not the _last_ iteration (which will be finished normally)
923
1015
 
924
- // TODO: check test duration (not to repeat if it's too slow)
925
1016
  const ctx = taskToCtx.get(task)
926
1017
  if (ctx) {
927
1018
  if (lastExecutionStatus === 'fail') {
@@ -1269,8 +1360,15 @@ addHook({
1269
1360
  }
1270
1361
  // From >=3.0.1, the first arguments changes from a string to an object containing the filepath
1271
1362
  const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
1272
-
1273
- const testSuiteCtx = { testSuiteAbsolutePath, frameworkVersion }
1363
+ const providedContext = getProvidedContext()
1364
+
1365
+ const testSuiteCtx = {
1366
+ testSuiteAbsolutePath,
1367
+ frameworkVersion,
1368
+ testSessionId: providedContext.testSessionId,
1369
+ testModuleId: providedContext.testModuleId,
1370
+ testCommand: providedContext.testCommand,
1371
+ }
1274
1372
  testSuiteStartCh.runStores(testSuiteCtx, () => {})
1275
1373
  const startTestsResponse = await startTests.apply(this, arguments)
1276
1374
 
@@ -1288,7 +1386,6 @@ addHook({
1288
1386
  // We have to trick vitest into thinking that the test has passed
1289
1387
  // but we want to report it as failed if it did fail
1290
1388
  const isSwitchedStatus = switchedStatuses.has(task)
1291
- const providedContext = getProvidedContext()
1292
1389
 
1293
1390
  if (result) {
1294
1391
  const { state, duration, errors } = result
@@ -140,7 +140,7 @@ class SchemaExtractor {
140
140
  return
141
141
  }
142
142
 
143
- if (span.context()._tags[SCHEMA_TYPE] && operation === 'serialization') {
143
+ if (span.context().getTag(SCHEMA_TYPE) && operation === 'serialization') {
144
144
  // we have already added a schema to this span, this call is an encode of nested schema types
145
145
  return
146
146
  }
@@ -0,0 +1,144 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../datadog-core')
4
+
5
+ const DatabasePlugin = require('../../dd-trace/src/plugins/database')
6
+
7
+ class AzureCosmosPlugin extends DatabasePlugin {
8
+ static id = 'azure-cosmos'
9
+ // Channel prefix determines how the plugin subscribes to instrumentation events.
10
+ // Three patterns exist — set `static prefix` explicitly based on instrumentation type:
11
+ //
12
+ // Orchestrion: static prefix = 'tracing:orchestrion:<npm-package>:<channelName>'
13
+ // Shimmer + tracingChannel: static prefix = 'tracing:apm:<name>:<operation>'
14
+ // Shimmer + manual channels: omit prefix — defaults to `apm:${id}:${operation}`
15
+ static prefix = 'tracing:orchestrion:@azure/cosmos:executePlugins'
16
+ static peerServicePrecursors = ['db.name']
17
+
18
+ operationName () {
19
+ return 'cosmosdb.query'
20
+ }
21
+
22
+ asyncEnd (ctx) {
23
+ if (!ctx.span) return
24
+ const span = ctx.currentStore?.span
25
+ if (span) {
26
+ const result = ctx.result
27
+ if (result?.code) span.setTag('db.response.status_code', (result.code).toString())
28
+ if (result?.substatus) span.setTag('cosmosdb.response.sub_status_code', result.substatus)
29
+ span.finish()
30
+ }
31
+ }
32
+
33
+ error (ctx) {
34
+ if (!ctx.span) return
35
+ const span = ctx.currentStore?.span
36
+ if (span) {
37
+ const error = ctx.error
38
+ this.addError(error, span)
39
+ if (error?.code) span.setTag('db.response.status_code', (error.code).toString())
40
+ if (error?.substatus) span.setTag('cosmosdb.response.sub_status_code', error.substatus)
41
+ }
42
+ }
43
+
44
+ bindStart (ctx) {
45
+ const requestContext = ctx.arguments[1]
46
+ const resource = this.getResource(requestContext)
47
+ const { dbName, containerName } = this.getDbInfo(requestContext)
48
+ const connectionMode = this.getConnectionMode(requestContext)
49
+ const { outHost, userAgent } = this.getHttpInfo(requestContext)
50
+ const pluginOn = ctx.arguments[3]
51
+
52
+ if (pluginOn != null && requestContext.operationType != null && requestContext.resourceType != null) {
53
+ const operationType = requestContext.operationType
54
+ const resourceType = requestContext.resourceType
55
+ // only trace operations not requests (pluginOn)
56
+ // trace requests only if they are read or query operations not on docs
57
+ // prevents doubled read spans for createIfNotExists calls
58
+ if (pluginOn === 'request' && ((operationType !== 'read' && operationType !== 'query') ||
59
+ (operationType === 'read' && resourceType !== 'docs'))) {
60
+ return storage('legacy').getStore()
61
+ }
62
+
63
+ // separately, skip tracing read requests without a path, these don't
64
+ // represent CRUD operations on a resource we care about
65
+ // not returning current store because we don't want the child http.request spans
66
+ // to be created
67
+ if (operationType === 'read' && requestContext.path === '') {
68
+ return { noop: true }
69
+ }
70
+ }
71
+
72
+ const span = this.startSpan(this.operationName(), {
73
+ resource,
74
+ type: 'cosmosdb',
75
+ kind: 'client',
76
+ meta: {
77
+ component: 'azure_cosmos',
78
+ 'db.system': 'cosmosdb',
79
+ 'db.name': dbName,
80
+ 'cosmosdb.container': containerName,
81
+ 'cosmosdb.connection.mode': connectionMode,
82
+ 'http.useragent': userAgent,
83
+ 'out.host': outHost,
84
+ },
85
+ }, ctx)
86
+
87
+ ctx.span = span
88
+ return ctx.currentStore
89
+ }
90
+
91
+ getResource (requestContext) {
92
+ const path = requestContext.path
93
+ const parts = path.split('/')
94
+ let modified = false
95
+ for (let i = 2; i < parts.length; i += 2) {
96
+ if (parts[i].length > 0 && parts[i - 1] !== 'dbs' && parts[i - 1] !== 'colls') {
97
+ parts[i] = '?'
98
+ modified = true
99
+ }
100
+ }
101
+
102
+ return `${requestContext.operationType} ${modified ? parts.join('/') : path}`
103
+ }
104
+
105
+ getDbInfo (requestContext) {
106
+ let dbName = null
107
+ let containerName = null
108
+
109
+ if (requestContext.operationType === 'create' && requestContext.resourceType === 'dbs' &&
110
+ requestContext.body != null && requestContext.body.id != null) {
111
+ dbName = requestContext.body.id
112
+ }
113
+
114
+ let resourceLink = requestContext.path
115
+ if (resourceLink?.length > 1 && resourceLink.startsWith('/')) {
116
+ resourceLink = resourceLink.slice(1)
117
+ const parts = resourceLink.split('/')
118
+ if (parts.length > 0 && parts[0].toLowerCase() === 'dbs' && parts.length >= 2) {
119
+ dbName = parts[1]
120
+ if (parts.length >= 4 && parts[2].toLowerCase() === 'colls' && parts[3] !== '') {
121
+ containerName = parts[3]
122
+ }
123
+ }
124
+ }
125
+
126
+ return { dbName, containerName }
127
+ }
128
+
129
+ getConnectionMode (requestContext) {
130
+ const mode = requestContext.client?.connectionPolicy?.connectionMode
131
+ if (mode === 0) {
132
+ return 'gateway'
133
+ }
134
+ return 'direct'
135
+ }
136
+
137
+ getHttpInfo (requestContext) {
138
+ const outHost = requestContext.client?.cosmosClientOptions?.endpoint
139
+ const userAgent = requestContext.headers?.['User-Agent']
140
+ return { outHost, userAgent }
141
+ }
142
+ }
143
+
144
+ module.exports = AzureCosmosPlugin
@@ -62,7 +62,7 @@ class AzureEventHubsProducerPlugin extends ProducerPlugin {
62
62
  const contexts = spanContexts.get(eventData)
63
63
  if (contexts) {
64
64
  for (const spanContext of contexts) {
65
- span.addLink(spanContext)
65
+ span.addLink({ context: spanContext })
66
66
  }
67
67
  }
68
68
  }