dd-trace 5.82.0 → 5.84.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 (150) hide show
  1. package/LICENSE-3rdparty.csv +77 -79
  2. package/ci/init.js +6 -6
  3. package/index.d.ts +213 -4
  4. package/loader-hook.mjs +1 -1
  5. package/package.json +59 -56
  6. package/packages/datadog-core/src/storage.js +7 -7
  7. package/packages/datadog-esbuild/index.js +6 -0
  8. package/packages/datadog-instrumentations/src/ai.js +7 -3
  9. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  10. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  11. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  12. package/packages/datadog-instrumentations/src/helpers/instrumentations.js +4 -3
  13. package/packages/datadog-instrumentations/src/helpers/register.js +3 -7
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +6 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -1
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +73 -16
  17. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  18. package/packages/datadog-instrumentations/src/jest.js +124 -64
  19. package/packages/datadog-instrumentations/src/koa.js +2 -1
  20. package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
  21. package/packages/datadog-instrumentations/src/mocha/main.js +2 -2
  22. package/packages/datadog-instrumentations/src/mocha/worker.js +1 -1
  23. package/packages/datadog-instrumentations/src/mocha.js +1 -1
  24. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  25. package/packages/datadog-instrumentations/src/mysql2.js +2 -2
  26. package/packages/datadog-instrumentations/src/net.js +13 -5
  27. package/packages/datadog-instrumentations/src/nyc.js +1 -1
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +4 -4
  29. package/packages/datadog-instrumentations/src/pg.js +4 -2
  30. package/packages/datadog-instrumentations/src/playwright.js +15 -11
  31. package/packages/datadog-instrumentations/src/selenium.js +2 -2
  32. package/packages/datadog-instrumentations/src/undici.js +12 -1
  33. package/packages/datadog-plugin-aws-sdk/src/base.js +4 -4
  34. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +2 -2
  35. package/packages/datadog-plugin-azure-service-bus/src/producer.js +2 -2
  36. package/packages/datadog-plugin-cucumber/src/index.js +35 -34
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
  38. package/packages/datadog-plugin-dd-trace-api/src/index.js +2 -2
  39. package/packages/datadog-plugin-express/src/code_origin.js +21 -15
  40. package/packages/datadog-plugin-fastify/src/code_origin.js +17 -4
  41. package/packages/datadog-plugin-jest/src/index.js +2 -2
  42. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  43. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -2
  44. package/packages/datadog-plugin-playwright/src/index.js +26 -26
  45. package/packages/datadog-plugin-undici/src/index.js +305 -2
  46. package/packages/datadog-plugin-vitest/src/index.js +5 -5
  47. package/packages/datadog-shimmer/src/shimmer.js +2 -5
  48. package/packages/dd-trace/index.js +19 -0
  49. package/packages/dd-trace/src/agent/info.js +57 -0
  50. package/packages/dd-trace/src/agent/url.js +28 -0
  51. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
  52. package/packages/dd-trace/src/appsec/index.js +47 -7
  53. package/packages/dd-trace/src/appsec/rasp/index.js +2 -4
  54. package/packages/dd-trace/src/azure_metadata.js +8 -3
  55. package/packages/dd-trace/src/baggage.js +36 -11
  56. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -1
  57. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
  58. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +3 -4
  59. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
  60. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +5 -5
  61. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  62. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
  63. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -3
  64. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -4
  65. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +1 -1
  66. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
  67. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -4
  68. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -4
  69. package/packages/dd-trace/src/ci-visibility/telemetry.js +6 -2
  70. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
  71. package/packages/dd-trace/src/{config_defaults.js → config/defaults.js} +3 -3
  72. package/packages/dd-trace/src/{config-helper.js → config/helper.js} +88 -15
  73. package/packages/dd-trace/src/{config.js → config/index.js} +107 -46
  74. package/packages/dd-trace/src/config/remote_config.js +188 -19
  75. package/packages/dd-trace/src/{config_stable.js → config/stable.js} +20 -32
  76. package/packages/dd-trace/src/{supported-configurations.json → config/supported-configurations.json} +3 -1
  77. package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -5
  78. package/packages/dd-trace/src/datastreams/processor.js +1 -1
  79. package/packages/dd-trace/src/datastreams/writer.js +2 -8
  80. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  81. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -7
  82. package/packages/dd-trace/src/debugger/devtools_client/json-buffer.js +10 -11
  83. package/packages/dd-trace/src/debugger/devtools_client/send.js +3 -3
  84. package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +1 -1
  85. package/packages/dd-trace/src/debugger/index.js +83 -15
  86. package/packages/dd-trace/src/dogstatsd.js +5 -11
  87. package/packages/dd-trace/src/encode/0.4.js +2 -2
  88. package/packages/dd-trace/src/exporter.js +1 -1
  89. package/packages/dd-trace/src/exporters/agent/index.js +5 -11
  90. package/packages/dd-trace/src/exporters/agent/writer.js +12 -16
  91. package/packages/dd-trace/src/exporters/common/{agent-info-exporter.js → buffering-exporter.js} +10 -37
  92. package/packages/dd-trace/src/exporters/common/docker.js +2 -2
  93. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  94. package/packages/dd-trace/src/exporters/common/util.js +2 -2
  95. package/packages/dd-trace/src/exporters/span-stats/index.js +3 -10
  96. package/packages/dd-trace/src/flare/index.js +1 -1
  97. package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
  98. package/packages/dd-trace/src/index.js +4 -4
  99. package/packages/dd-trace/src/lambda/handler.js +2 -2
  100. package/packages/dd-trace/src/lambda/index.js +2 -2
  101. package/packages/dd-trace/src/lambda/runtime/patch.js +2 -2
  102. package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
  103. package/packages/dd-trace/src/llmobs/constants/tags.js +8 -1
  104. package/packages/dd-trace/src/llmobs/index.js +2 -2
  105. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  106. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +3 -4
  107. package/packages/dd-trace/src/llmobs/sdk.js +33 -6
  108. package/packages/dd-trace/src/llmobs/span_processor.js +17 -7
  109. package/packages/dd-trace/src/llmobs/tagger.js +175 -1
  110. package/packages/dd-trace/src/llmobs/writers/base.js +118 -45
  111. package/packages/dd-trace/src/llmobs/writers/spans.js +4 -3
  112. package/packages/dd-trace/src/llmobs/writers/util.js +3 -9
  113. package/packages/dd-trace/src/log/index.js +50 -35
  114. package/packages/dd-trace/src/log/writer.js +13 -78
  115. package/packages/dd-trace/src/noop/proxy.js +3 -3
  116. package/packages/dd-trace/src/openfeature/writers/base.js +9 -16
  117. package/packages/dd-trace/src/openfeature/writers/util.js +3 -8
  118. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +2 -2
  119. package/packages/dd-trace/src/opentelemetry/tracer.js +48 -6
  120. package/packages/dd-trace/src/opentracing/propagation/text_map.js +45 -21
  121. package/packages/dd-trace/src/opentracing/span.js +4 -4
  122. package/packages/dd-trace/src/plugin_manager.js +8 -6
  123. package/packages/dd-trace/src/plugins/util/ci.js +5 -8
  124. package/packages/dd-trace/src/plugins/util/git-cache.js +3 -3
  125. package/packages/dd-trace/src/plugins/util/test.js +1 -1
  126. package/packages/dd-trace/src/plugins/util/user-provided-git.js +41 -43
  127. package/packages/dd-trace/src/profiler.js +4 -39
  128. package/packages/dd-trace/src/profiling/config.js +74 -34
  129. package/packages/dd-trace/src/profiling/exporter_cli.js +5 -5
  130. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  131. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +9 -2
  132. package/packages/dd-trace/src/profiling/index.js +1 -1
  133. package/packages/dd-trace/src/profiling/libuv-size.js +1 -1
  134. package/packages/dd-trace/src/profiling/profiler.js +57 -2
  135. package/packages/dd-trace/src/proxy.js +34 -5
  136. package/packages/dd-trace/src/remote_config/capabilities.js +4 -0
  137. package/packages/dd-trace/src/remote_config/index.js +2 -7
  138. package/packages/dd-trace/src/ritm.js +8 -4
  139. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
  140. package/packages/dd-trace/src/serverless.js +2 -2
  141. package/packages/dd-trace/src/span_processor.js +2 -2
  142. package/packages/dd-trace/src/startup-log.js +7 -16
  143. package/packages/dd-trace/src/telemetry/endpoints.js +67 -5
  144. package/packages/dd-trace/src/telemetry/send-data.js +103 -4
  145. package/packages/dd-trace/src/telemetry/telemetry.js +229 -110
  146. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  147. package/vendor/dist/esquery/index.js +1 -1
  148. package/vendor/dist/meriyah/index.js +1 -1
  149. package/vendor/dist/protobufjs/index.js +1 -1
  150. /package/packages/dd-trace/src/{git_properties.js → config/git_properties.js} +0 -0
@@ -91,8 +91,11 @@ const wrappedWorkers = new WeakSet()
91
91
  const testSuiteMockedFiles = new Map()
92
92
  const testsToBeRetried = new Set()
93
93
  const testSuiteAbsolutePathsWithFastCheck = new Set()
94
+ const testSuiteJestObjects = new Map()
94
95
 
95
96
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
97
+ const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
98
+ const atrSuppressedErrors = new Map()
96
99
 
97
100
  // based on https://github.com/facebook/jest/blob/main/packages/jest-circus/src/formatNodeAssertErrors.ts#L41
98
101
  function formatJestError (errors) {
@@ -235,6 +238,28 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
235
238
  }
236
239
  }
237
240
 
241
+ /**
242
+ * Jest mock state issue during test retries
243
+ *
244
+ * Problem:
245
+ * - Jest tracks mock function calls using internal state (call count, call arguments, etc.)
246
+ * - When a test is retried, the mock state is not automatically reset
247
+ * - This causes assertions like `toHaveBeenCalledTimes(1)` to fail because the call count
248
+ * accumulates across retries
249
+ *
250
+ * The solution is to clear all mocks before each retry attempt.
251
+ */
252
+ resetMockState () {
253
+ try {
254
+ const jestObject = testSuiteJestObjects.get(this.testSuiteAbsolutePath)
255
+ if (jestObject?.clearAllMocks) {
256
+ jestObject.clearAllMocks()
257
+ }
258
+ } catch (e) {
259
+ log.warn('Error resetting mock state', e)
260
+ }
261
+ }
262
+
238
263
  // This function returns an array if the known tests are valid and null otherwise.
239
264
  getKnownTestsForSuite (suiteKnownTests) {
240
265
  // `suiteKnownTests` is `this.testEnvironmentOptions._ddKnownTests`,
@@ -337,8 +362,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
337
362
  if (event.name === 'test_start') {
338
363
  const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
339
364
  if (testsToBeRetried.has(testName)) {
340
- // This is needed because we're trying tests with the same name
365
+ // This is needed because we're retrying tests with the same name
341
366
  this.resetSnapshotState()
367
+ this.resetMockState()
342
368
  }
343
369
 
344
370
  let isNewTest = false
@@ -378,7 +404,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
378
404
  isNewTest = retriedTestsToNumAttempts.has(testName)
379
405
  }
380
406
 
381
- if (this.isEarlyFlakeDetectionEnabled && (isNewTest || isModified)) {
407
+ const willRunEfd = this.isEarlyFlakeDetectionEnabled && (isNewTest || isModified)
408
+ event.test[ATR_RETRY_SUPPRESSION_FLAG] = Boolean(isAttemptToFix || willRunEfd)
409
+
410
+ if (!isAttemptToFix && willRunEfd) {
382
411
  numEfdRetry = retriedTestsToNumAttempts.get(testName)
383
412
  retriedTestsToNumAttempts.set(testName, numEfdRetry + 1)
384
413
  }
@@ -434,19 +463,22 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
434
463
 
435
464
  const testFullName = this.getTestNameFromAddTestEvent(event, state)
436
465
  const isSkipped = event.mode === 'todo' || event.mode === 'skip'
437
- if (this.isTestManagementTestsEnabled) {
438
- const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testFullName)
439
- if (isAttemptToFix && !isSkipped && !retriedTestsToNumAttempts.has(testFullName)) {
440
- retriedTestsToNumAttempts.set(testFullName, 0)
441
- testsToBeRetried.add(testFullName)
442
- this.retryTest({
443
- jestEvent: event,
444
- retryCount: testManagementAttemptToFixRetries,
445
- retryType: 'Test Management (Attempt to Fix)'
446
- })
447
- }
466
+ const isAttemptToFix = this.isTestManagementTestsEnabled &&
467
+ this.testManagementTestsForThisSuite?.attemptToFix?.includes(testFullName)
468
+ if (
469
+ isAttemptToFix &&
470
+ !isSkipped &&
471
+ !retriedTestsToNumAttempts.has(testFullName)
472
+ ) {
473
+ retriedTestsToNumAttempts.set(testFullName, 0)
474
+ testsToBeRetried.add(testFullName)
475
+ this.retryTest({
476
+ jestEvent: event,
477
+ retryCount: testManagementAttemptToFixRetries,
478
+ retryType: 'Test Management (Attempt to Fix)'
479
+ })
448
480
  }
449
- if (this.isImpactedTestsEnabled) {
481
+ if (!isAttemptToFix && this.isImpactedTestsEnabled) {
450
482
  const testStartLine = getTestLineStart(event.asyncError, this.testSuite)
451
483
  const testEndLine = getTestEndLine(event.fn, testStartLine)
452
484
  const isModified = isModifiedTest(
@@ -466,7 +498,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
466
498
  })
467
499
  }
468
500
  }
469
- if (this.isKnownTestsEnabled) {
501
+ if (!isAttemptToFix && this.isKnownTestsEnabled) {
470
502
  const isNew = !this.knownTestsForThisSuite.includes(testFullName)
471
503
  if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(testFullName)) {
472
504
  retriedTestsToNumAttempts.set(testFullName, 0)
@@ -488,6 +520,13 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
488
520
  }
489
521
  // restore in case it is retried
490
522
  event.test.fn = originalTestFns.get(event.test)
523
+ // If ATR retry is being suppressed for this test (due to EFD or Attempt to Fix taking precedence)
524
+ // and the test has errors for this attempt, store the errors temporarily and clear them
525
+ // so Jest won't treat this attempt as failed (the real status will be reported after retries).
526
+ if (event.test?.[ATR_RETRY_SUPPRESSION_FLAG] && event.test.errors?.length) {
527
+ atrSuppressedErrors.set(event.test, event.test.errors)
528
+ event.test.errors = []
529
+ }
491
530
 
492
531
  let attemptToFixPassed = false
493
532
  let attemptToFixFailed = false
@@ -581,6 +620,12 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
581
620
  await promises.isProbeReady
582
621
  }
583
622
  }
623
+ if (event.name === 'run_finish') {
624
+ for (const [test, errors] of atrSuppressedErrors) {
625
+ test.errors = errors
626
+ }
627
+ atrSuppressedErrors.clear()
628
+ }
584
629
  if (event.name === 'test_skip' || event.name === 'test_todo') {
585
630
  const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
586
631
  testSkippedCh.publish({
@@ -843,7 +888,6 @@ function getCliWrapper (isNewJestVersion) {
843
888
 
844
889
  const {
845
890
  results: {
846
- success,
847
891
  coverageMap,
848
892
  numFailedTestSuites,
849
893
  numFailedTests,
@@ -862,53 +906,6 @@ function getCliWrapper (isNewJestVersion) {
862
906
  // ignore errors
863
907
  }
864
908
  }
865
- let status, error
866
-
867
- if (success) {
868
- status = numTotalTests === 0 && numTotalTestSuites === 0 ? 'skip' : 'pass'
869
- } else {
870
- status = 'fail'
871
- error = new Error(`Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`)
872
- }
873
- let timeoutId
874
-
875
- // Pass the resolve callback to defer it to DC listener
876
- const flushPromise = new Promise((resolve) => {
877
- onDone = () => {
878
- clearTimeout(timeoutId)
879
- resolve()
880
- }
881
- })
882
-
883
- const timeoutPromise = new Promise((resolve) => {
884
- timeoutId = setTimeout(() => {
885
- resolve('timeout')
886
- }, FLUSH_TIMEOUT).unref()
887
- })
888
-
889
- testSessionFinishCh.publish({
890
- status,
891
- isSuitesSkipped,
892
- isSuitesSkippingEnabled,
893
- isCodeCoverageEnabled,
894
- testCodeCoverageLinesTotal,
895
- numSkippedSuites,
896
- hasUnskippableSuites,
897
- hasForcedToRunSuites,
898
- error,
899
- isEarlyFlakeDetectionEnabled,
900
- isEarlyFlakeDetectionFaulty,
901
- isTestManagementTestsEnabled,
902
- onDone
903
- })
904
-
905
- const waitingResult = await Promise.race([flushPromise, timeoutPromise])
906
-
907
- if (waitingResult === 'timeout') {
908
- log.error('Timeout waiting for the tracer to flush')
909
- }
910
-
911
- numSkippedSuites = 0
912
909
 
913
910
  /**
914
911
  * If Early Flake Detection (EFD) is enabled the logic is as follows:
@@ -917,7 +914,6 @@ function getCliWrapper (isNewJestVersion) {
917
914
  * The rationale behind is the following: you may still be able to block your CI pipeline by gating
918
915
  * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
919
916
  */
920
-
921
917
  if (isEarlyFlakeDetectionEnabled) {
922
918
  let numFailedTestsToIgnore = 0
923
919
  for (const testStatuses of newTestsTestStatuses.values()) {
@@ -975,6 +971,55 @@ function getCliWrapper (isNewJestVersion) {
975
971
  }
976
972
  }
977
973
 
974
+ // Determine session status after EFD and quarantine checks have potentially modified success
975
+ let status, error
976
+ if (result.results.success) {
977
+ status = numTotalTests === 0 && numTotalTestSuites === 0 ? 'skip' : 'pass'
978
+ } else {
979
+ status = 'fail'
980
+ error = new Error(`Failed test suites: ${numFailedTestSuites}. Failed tests: ${numFailedTests}`)
981
+ }
982
+
983
+ let timeoutId
984
+
985
+ // Pass the resolve callback to defer it to DC listener
986
+ const flushPromise = new Promise((resolve) => {
987
+ onDone = () => {
988
+ clearTimeout(timeoutId)
989
+ resolve()
990
+ }
991
+ })
992
+
993
+ const timeoutPromise = new Promise((resolve) => {
994
+ timeoutId = setTimeout(() => {
995
+ resolve('timeout')
996
+ }, FLUSH_TIMEOUT).unref()
997
+ })
998
+
999
+ testSessionFinishCh.publish({
1000
+ status,
1001
+ isSuitesSkipped,
1002
+ isSuitesSkippingEnabled,
1003
+ isCodeCoverageEnabled,
1004
+ testCodeCoverageLinesTotal,
1005
+ numSkippedSuites,
1006
+ hasUnskippableSuites,
1007
+ hasForcedToRunSuites,
1008
+ error,
1009
+ isEarlyFlakeDetectionEnabled,
1010
+ isEarlyFlakeDetectionFaulty,
1011
+ isTestManagementTestsEnabled,
1012
+ onDone
1013
+ })
1014
+
1015
+ const waitingResult = await Promise.race([flushPromise, timeoutPromise])
1016
+
1017
+ if (waitingResult === 'timeout') {
1018
+ log.error('Timeout waiting for the tracer to flush')
1019
+ }
1020
+
1021
+ numSkippedSuites = 0
1022
+
978
1023
  return result
979
1024
  }, {
980
1025
  replaceGetter: true
@@ -1127,9 +1172,19 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
1127
1172
  })
1128
1173
  }
1129
1174
  testSuiteFinishCh.publish({ status, errorMessage, testSuiteAbsolutePath: environment.testSuiteAbsolutePath })
1175
+
1176
+ // Cleanup per-suite state to avoid memory leaks
1177
+ testSuiteMockedFiles.delete(environment.testSuiteAbsolutePath)
1178
+ testSuiteJestObjects.delete(environment.testSuiteAbsolutePath)
1179
+
1130
1180
  return suiteResults
1131
1181
  }).catch(error => {
1132
1182
  testSuiteFinishCh.publish({ status: 'fail', error, testSuiteAbsolutePath: environment.testSuiteAbsolutePath })
1183
+
1184
+ // Cleanup per-suite state to avoid memory leaks
1185
+ testSuiteMockedFiles.delete(environment.testSuiteAbsolutePath)
1186
+ testSuiteJestObjects.delete(environment.testSuiteAbsolutePath)
1187
+
1133
1188
  throw error
1134
1189
  })
1135
1190
  })
@@ -1291,6 +1346,11 @@ addHook({
1291
1346
  const result = _createJestObjectFor.apply(this, arguments)
1292
1347
  const suiteFilePath = this._testPath || from
1293
1348
 
1349
+ // Store the jest object so we can access it later for resetting mock state
1350
+ if (suiteFilePath) {
1351
+ testSuiteJestObjects.set(suiteFilePath, result)
1352
+ }
1353
+
1294
1354
  shimmer.wrap(result, 'mock', mock => function (moduleName) {
1295
1355
  // If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine
1296
1356
  if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
@@ -90,7 +90,8 @@ function wrapMiddleware (fn, layer) {
90
90
  const req = ctx.req
91
91
 
92
92
  const path = layer && layer.path
93
- const route = typeof path === 'string' && !path.endsWith('(.*)') && !path.endsWith('([^/]*)') && path
93
+ const route = typeof path === 'string' && !path.endsWith('(.*)') && !path.endsWith('([^/]*)') &&
94
+ !path.includes('(?:') && path
94
95
 
95
96
  enterChannel.publish({ req, name, route })
96
97
 
@@ -62,8 +62,8 @@ function wrapDispatchFunc (dispatchFunc) {
62
62
 
63
63
  // light-my-request Response emits 'finish' when done
64
64
  if (res.on && typeof res.on === 'function') {
65
- res.on('finish', onFinish)
66
- res.on('close', onFinish)
65
+ res.once('finish', onFinish)
66
+ res.once('close', onFinish)
67
67
  }
68
68
 
69
69
  // Also wrap end() as fallback
@@ -5,7 +5,7 @@ const { addHook, channel } = require('../helpers/instrument')
5
5
  const shimmer = require('../../../datadog-shimmer')
6
6
  const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
7
7
  const log = require('../../../dd-trace/src/log')
8
- const { getEnvironmentVariable } = require('../../../dd-trace/src/config-helper')
8
+ const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper')
9
9
  const {
10
10
  getTestSuitePath,
11
11
  MOCHA_WORKER_TRACE_PAYLOAD_CODE,
@@ -282,7 +282,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
282
282
  if (config.isTestManagementTestsEnabled) {
283
283
  ctx.onDone = onReceivedTestManagementTests
284
284
  testManagementTestsCh.runStores(ctx, () => {})
285
- } if (config.isImpactedTestsEnabled) {
285
+ } else if (config.isImpactedTestsEnabled) {
286
286
  ctx.onDone = onReceivedImpactedTests
287
287
  modifiedFilesCh.runStores(ctx, () => {})
288
288
  } else if (config.isSuitesSkippingEnabled) {
@@ -67,7 +67,7 @@ addHook({
67
67
  return run.apply(this, arguments)
68
68
  }
69
69
  // We flush when the worker ends with its test file (a mocha instance in a worker runs a single test file)
70
- this.on('end', () => {
70
+ this.once('end', () => {
71
71
  workerFinishCh.publish()
72
72
  })
73
73
  this.on('test', getOnTestHandler(false))
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
3
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
4
4
 
5
5
  if (getEnvironmentVariable('MOCHA_WORKER_ID')) {
6
6
  require('./mocha/worker')
@@ -39,7 +39,7 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
39
39
  return finishCh.runStores(ctx, cb, this, error, result)
40
40
  })
41
41
  } else {
42
- res.on('end', () => finishCh.publish(ctx))
42
+ res.once('end', () => finishCh.publish(ctx))
43
43
  }
44
44
 
45
45
  return res
@@ -141,11 +141,11 @@ function wrapConnection (Connection, version) {
141
141
  finishCh.runStores(ctx, onResult, this, ...arguments)
142
142
  })
143
143
  } else {
144
- this.on(errorMonitor, error => {
144
+ this.once(errorMonitor, error => {
145
145
  ctx.error = error
146
146
  errorCh.publish(ctx)
147
147
  })
148
- this.on('end', () => finishCh.publish(ctx))
148
+ this.once('end', () => finishCh.publish(ctx))
149
149
  }
150
150
 
151
151
  this.execute = execute
@@ -101,7 +101,7 @@ function getOptions (args) {
101
101
  }
102
102
 
103
103
  function setupListeners (socket, protocol, ctx, finishCh, errorCh) {
104
- const events = ['connect', errorMonitor, 'close', 'timeout']
104
+ const events = [errorMonitor, 'close', 'timeout']
105
105
 
106
106
  const wrapListener = function (error) {
107
107
  if (error) {
@@ -109,27 +109,35 @@ function setupListeners (socket, protocol, ctx, finishCh, errorCh) {
109
109
  errorCh.publish(ctx)
110
110
  }
111
111
  finishCh.runStores(ctx, () => {})
112
+ cleanupOtherListeners()
112
113
  }
113
114
 
114
- const localListener = function () {
115
+ const localListener = function (error) {
115
116
  ctx.socket = socket
116
117
  connectionCh.publish(ctx)
118
+ if (error) {
119
+ ctx.error = error
120
+ errorCh.publish(ctx)
121
+ }
122
+ finishCh.runStores(ctx, () => {})
123
+ cleanupOtherListeners()
117
124
  }
118
125
 
119
- const cleanupListener = function () {
126
+ const cleanupOtherListeners = function () {
120
127
  socket.removeListener('connect', localListener)
121
128
  events.forEach(event => {
122
129
  socket.removeListener(event, wrapListener)
123
- socket.removeListener(event, cleanupListener)
124
130
  })
125
131
  }
126
132
 
133
+ // TODO: Identify why the connect listener should remove the other listeners.
127
134
  if (protocol === 'tcp') {
128
135
  socket.once('connect', localListener)
136
+ } else {
137
+ events.push('connect')
129
138
  }
130
139
 
131
140
  events.forEach(event => {
132
141
  socket.once(event, wrapListener)
133
- socket.once(event, cleanupListener)
134
142
  })
135
143
  }
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
4
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
5
5
  const { addHook, channel } = require('./helpers/instrument')
6
6
 
7
7
  const codeCoverageWrapCh = channel('ci:nyc:wrap')
@@ -2,12 +2,12 @@
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
4
  const tracer = require('../../dd-trace')
5
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
5
+ const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
6
6
  const { addHook } = require('./helpers/instrument')
7
7
 
8
- const otelSdkEnabled = getEnvironmentVariable('DD_TRACE_OTEL_ENABLED') ||
9
- getEnvironmentVariable('OTEL_SDK_DISABLED')
10
- ? !getEnvironmentVariable('OTEL_SDK_DISABLED')
8
+ const otelSdkEnabled = getValueFromEnvSources('DD_TRACE_OTEL_ENABLED') ||
9
+ getValueFromEnvSources('OTEL_SDK_DISABLED')
10
+ ? !getValueFromEnvSources('OTEL_SDK_DISABLED')
11
11
  : undefined
12
12
 
13
13
  if (otelSdkEnabled) {
@@ -112,8 +112,10 @@ function wrapQuery (query) {
112
112
  arguments[0] = pgQuery
113
113
 
114
114
  const retval = query.apply(this, arguments)
115
- const queryQueue = this.queryQueue || this._queryQueue
116
- const activeQuery = this.activeQuery || this._activeQuery
115
+
116
+ const deperecated = Object.hasOwn(this, '_activeQuery')
117
+ const queryQueue = deperecated ? this._queryQueue : this.queryQueue
118
+ const activeQuery = deperecated ? this._activeQuery : this.activeQuery
117
119
 
118
120
  const newQuery = queryQueue.at(-1) || activeQuery
119
121
 
@@ -11,8 +11,8 @@ const {
11
11
  } = require('../../dd-trace/src/plugins/util/test')
12
12
  const log = require('../../dd-trace/src/log')
13
13
  const {
14
- getEnvironmentVariable
15
- } = require('../../dd-trace/src/config-helper')
14
+ getValueFromEnvSources
15
+ } = require('../../dd-trace/src/config/helper')
16
16
  const { DD_MAJOR } = require('../../../version')
17
17
  const { addHook, channel } = require('./helpers/instrument')
18
18
 
@@ -41,7 +41,7 @@ const testSuiteToTestStatuses = new Map()
41
41
  const testSuiteToErrors = new Map()
42
42
  const testsToTestStatuses = new Map()
43
43
 
44
- const RUM_FLUSH_WAIT_TIME = Number(getEnvironmentVariable('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 1000
44
+ const RUM_FLUSH_WAIT_TIME = Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 1000
45
45
 
46
46
  let applyRepeatEachIndex = null
47
47
 
@@ -484,10 +484,11 @@ function dispatcherRunWrapper (run) {
484
484
 
485
485
  function dispatcherRunWrapperNew (run) {
486
486
  return function (testGroups) {
487
- // Filter out disabled tests from testGroups before they get scheduled
487
+ // Filter out disabled tests from testGroups before they get scheduled,
488
+ // unless they have attemptToFix (in which case they should still run and be retried)
488
489
  if (isTestManagementTestsEnabled) {
489
490
  testGroups.forEach(group => {
490
- group.tests = group.tests.filter(test => !test._ddIsDisabled)
491
+ group.tests = group.tests.filter(test => !test._ddIsDisabled || test._ddIsAttemptToFix)
491
492
  })
492
493
  // Remove empty groups
493
494
  testGroups = testGroups.filter(group => group.tests.length > 0)
@@ -911,14 +912,16 @@ addHook({
911
912
  const fileSuitesWithManagedTestsToProjects = new Map()
912
913
  for (const test of allTests) {
913
914
  const testProperties = getTestProperties(test)
914
- // Disabled tests are skipped and not retried
915
+ // Disabled tests are skipped unless they have attemptToFix
915
916
  if (testProperties.disabled) {
916
917
  test._ddIsDisabled = true
917
- test.expectedStatus = 'skipped'
918
- // setting test.expectedStatus to 'skipped' does not work for every case,
919
- // so we need to filter out disabled tests in dispatcherRunWrapperNew,
920
- // so they don't get to the workers
921
- continue
918
+ if (!testProperties.attemptToFix) {
919
+ test.expectedStatus = 'skipped'
920
+ // setting test.expectedStatus to 'skipped' does not work for every case,
921
+ // so we need to filter out disabled tests in dispatcherRunWrapperNew,
922
+ // so they don't get to the workers
923
+ continue
924
+ }
922
925
  }
923
926
  if (testProperties.quarantined) {
924
927
  test._ddIsQuarantined = true
@@ -947,6 +950,7 @@ addHook({
947
950
  (test) => test._ddIsAttemptToFix,
948
951
  [
949
952
  (test) => test._ddIsQuarantined && '_ddIsQuarantined',
953
+ (test) => test._ddIsDisabled && '_ddIsDisabled',
950
954
  '_ddIsAttemptToFix',
951
955
  '_ddIsAttemptToFixRetry'
952
956
  ],
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
4
+ const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
5
5
  const { addHook, channel } = require('./helpers/instrument')
6
6
 
7
7
  const ciSeleniumDriverGetStartCh = channel('ci:selenium:driver:get')
@@ -17,7 +17,7 @@ if (window.DD_RUM && window.DD_RUM.stopSession) {
17
17
  const IS_RUM_ACTIVE_SCRIPT = 'return !!window.DD_RUM'
18
18
 
19
19
  const DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS =
20
- Number(getEnvironmentVariable('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 500
20
+ Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 500
21
21
  const DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME = 'datadog-ci-visibility-test-execution-id'
22
22
 
23
23
  // TODO: can we increase the supported version range?
@@ -3,6 +3,7 @@
3
3
  const tracingChannel = require('dc-polyfill').tracingChannel
4
4
 
5
5
  const shimmer = require('../../datadog-shimmer')
6
+ const satisfies = require('../../../vendor/dist/semifies')
6
7
  const {
7
8
  addHook
8
9
  } = require('./helpers/instrument')
@@ -10,9 +11,19 @@ const { createWrapFetch } = require('./helpers/fetch')
10
11
 
11
12
  const ch = tracingChannel('apm:undici:fetch')
12
13
 
14
+ // Undici 5.0.x has a bug where fetch doesn't preserve AggregateError in the error cause chain
15
+ // Use native DC only for versions where error handling works correctly
16
+ const NATIVE_DC_VERSION = '>=4.7.0 <5.0.0 || >=5.1.0'
17
+
13
18
  addHook({
14
19
  name: 'undici',
15
20
  versions: ['^4.4.1', '5', '>=6.0.0']
16
- }, undici => {
21
+ }, (undici, version) => {
22
+ // For versions with working native DC, let the plugin subscribe directly
23
+ if (satisfies(version, NATIVE_DC_VERSION)) {
24
+ return undici
25
+ }
26
+
27
+ // For older versions or those with buggy error handling, wrap fetch
17
28
  return shimmer.wrap(undici, 'fetch', createWrapFetch(undici.Request, ch))
18
29
  })
@@ -5,7 +5,7 @@ const ClientPlugin = require('../../dd-trace/src/plugins/client')
5
5
  const { storage } = require('../../datadog-core')
6
6
  const { isTrue } = require('../../dd-trace/src/util')
7
7
  const { tagsFromRequest, tagsFromResponse } = require('../../dd-trace/src/payload-tagging')
8
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
8
+ const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
9
9
 
10
10
  class BaseAwsSdkPlugin extends ClientPlugin {
11
11
  static id = 'aws'
@@ -190,7 +190,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
190
190
 
191
191
  isEnabled (request) {
192
192
  const serviceId = this.serviceIdentifier.toUpperCase()
193
- const envVarValue = getEnvironmentVariable(`DD_TRACE_AWS_SDK_${serviceId}_ENABLED`)
193
+ const envVarValue = getValueFromEnvSources(`DD_TRACE_AWS_SDK_${serviceId}_ENABLED`)
194
194
  return envVarValue ? isTrue(envVarValue) : true
195
195
  }
196
196
 
@@ -273,9 +273,9 @@ function normalizeConfig (config, serviceIdentifier) {
273
273
  const serviceId = serviceIdentifier.toUpperCase()
274
274
  const batchPropagationEnabled = isTrue(
275
275
  specificConfig.batchPropagationEnabled ??
276
- getEnvironmentVariable(`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`) ??
276
+ getValueFromEnvSources(`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`) ??
277
277
  config.batchPropagationEnabled ??
278
- getEnvironmentVariable('DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED')
278
+ getValueFromEnvSources('DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED')
279
279
  )
280
280
 
281
281
  // Merge the specific config back into the main config
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
3
+ const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
4
4
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
5
5
 
6
6
  const spanContexts = new WeakMap()
@@ -89,7 +89,7 @@ function injectTraceContext (tracer, span, event) {
89
89
  }
90
90
 
91
91
  function batchLinksAreEnabled () {
92
- const eh = getEnvironmentVariable('DD_TRACE_AZURE_EVENTHUBS_BATCH_LINKS_ENABLED')
92
+ const eh = getValueFromEnvSources('DD_TRACE_AZURE_EVENTHUBS_BATCH_LINKS_ENABLED')
93
93
  return eh !== 'false'
94
94
  }
95
95
 
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
3
+ const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
4
4
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
5
5
  const spanContexts = new WeakMap()
6
6
 
@@ -90,7 +90,7 @@ function injectTraceContext (tracer, span, msg) {
90
90
  }
91
91
 
92
92
  function batchLinksAreEnabled () {
93
- const sb = getEnvironmentVariable('DD_TRACE_AZURE_SERVICEBUS_BATCH_LINKS_ENABLED')
93
+ const sb = getValueFromEnvSources('DD_TRACE_AZURE_SERVICEBUS_BATCH_LINKS_ENABLED')
94
94
  return sb !== 'false'
95
95
  }
96
96