dd-trace 5.97.0 → 5.99.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 (175) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ext/tags.js +1 -0
  3. package/index.d.ts +35 -3
  4. package/package.json +48 -46
  5. package/packages/datadog-instrumentations/src/crypto.js +45 -0
  6. package/packages/datadog-instrumentations/src/cucumber.js +65 -3
  7. package/packages/datadog-instrumentations/src/cypress-config.js +153 -53
  8. package/packages/datadog-instrumentations/src/dns.js +24 -56
  9. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +74 -0
  11. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +4 -1
  12. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +10 -3
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +59 -0
  16. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +11 -2
  17. package/packages/datadog-instrumentations/src/jest.js +104 -12
  18. package/packages/datadog-instrumentations/src/mocha/utils.js +8 -0
  19. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +7 -0
  20. package/packages/datadog-instrumentations/src/pino.js +4 -28
  21. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +27 -0
  22. package/packages/datadog-instrumentations/src/playwright.js +5 -17
  23. package/packages/datadog-instrumentations/src/redis.js +12 -6
  24. package/packages/datadog-instrumentations/src/stripe.js +38 -24
  25. package/packages/datadog-instrumentations/src/vitest.js +32 -4
  26. package/packages/datadog-instrumentations/src/zlib.js +29 -0
  27. package/packages/datadog-plugin-aws-sdk/src/base.js +2 -3
  28. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  29. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
  30. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
  32. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  33. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  34. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +8 -15
  35. package/packages/datadog-plugin-azure-service-bus/src/producer.js +4 -9
  36. package/packages/datadog-plugin-cucumber/src/index.js +8 -2
  37. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +114 -6
  38. package/packages/datadog-plugin-cypress/src/index.js +59 -2
  39. package/packages/datadog-plugin-cypress/src/source-map-utils.js +48 -1
  40. package/packages/datadog-plugin-fs/src/index.js +1 -1
  41. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
  42. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
  43. package/packages/datadog-plugin-http/src/client.js +1 -1
  44. package/packages/datadog-plugin-http/src/server.js +21 -13
  45. package/packages/datadog-plugin-http2/src/client.js +1 -1
  46. package/packages/datadog-plugin-http2/src/server.js +10 -2
  47. package/packages/datadog-plugin-jest/src/index.js +2 -2
  48. package/packages/datadog-plugin-mocha/src/index.js +1 -2
  49. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/index.js +24 -0
  50. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +55 -0
  51. package/packages/datadog-plugin-mongodb-core/src/index.js +4 -9
  52. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  53. package/packages/datadog-plugin-next/src/index.js +8 -2
  54. package/packages/datadog-plugin-pg/src/index.js +1 -1
  55. package/packages/datadog-plugin-playwright/src/index.js +2 -3
  56. package/packages/datadog-plugin-tedious/src/index.js +1 -1
  57. package/packages/datadog-plugin-vitest/src/index.js +14 -6
  58. package/packages/datadog-plugin-ws/src/close.js +3 -1
  59. package/packages/datadog-plugin-ws/src/producer.js +2 -0
  60. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  61. package/packages/dd-trace/src/aiguard/channels.js +8 -0
  62. package/packages/dd-trace/src/aiguard/index.js +7 -3
  63. package/packages/dd-trace/src/aiguard/sdk.js +66 -22
  64. package/packages/dd-trace/src/aiguard/tags.js +1 -0
  65. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
  66. package/packages/dd-trace/src/appsec/blocking.js +62 -34
  67. package/packages/dd-trace/src/appsec/graphql.js +6 -6
  68. package/packages/dd-trace/src/appsec/index.js +9 -11
  69. package/packages/dd-trace/src/appsec/rasp/command_injection.js +4 -5
  70. package/packages/dd-trace/src/appsec/rasp/lfi.js +8 -4
  71. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +5 -10
  72. package/packages/dd-trace/src/appsec/rasp/ssrf.js +5 -6
  73. package/packages/dd-trace/src/appsec/recommended.json +2438 -13
  74. package/packages/dd-trace/src/appsec/reporter.js +6 -5
  75. package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
  76. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
  77. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +6 -10
  78. package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
  79. package/packages/dd-trace/src/appsec/store.js +50 -0
  80. package/packages/dd-trace/src/appsec/waf/index.js +3 -5
  81. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
  82. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
  83. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
  84. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  85. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -4
  86. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -5
  88. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +3 -4
  89. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +6 -6
  90. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -2
  91. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
  92. package/packages/dd-trace/src/config/config-types.d.ts +0 -4
  93. package/packages/dd-trace/src/config/defaults.js +10 -11
  94. package/packages/dd-trace/src/config/generated-config-types.d.ts +14 -8
  95. package/packages/dd-trace/src/config/index.js +49 -32
  96. package/packages/dd-trace/src/config/parsers.js +26 -9
  97. package/packages/dd-trace/src/config/supported-configurations.json +86 -33
  98. package/packages/dd-trace/src/constants.js +1 -0
  99. package/packages/dd-trace/src/debugger/config.js +2 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/send.js +25 -5
  101. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
  102. package/packages/dd-trace/src/encode/0.4.js +11 -11
  103. package/packages/dd-trace/src/encode/span-stats.js +4 -1
  104. package/packages/dd-trace/src/exporters/agent/index.js +0 -1
  105. package/packages/dd-trace/src/exporters/agent/writer.js +1 -2
  106. package/packages/dd-trace/src/exporters/agentless/writer.js +3 -3
  107. package/packages/dd-trace/src/exporters/common/util.js +2 -2
  108. package/packages/dd-trace/src/id.js +2 -0
  109. package/packages/dd-trace/src/index.js +2 -5
  110. package/packages/dd-trace/src/lambda/handler.js +1 -3
  111. package/packages/dd-trace/src/llmobs/plugins/{anthropic.js → anthropic/index.js} +5 -63
  112. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +106 -0
  113. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +3 -2
  114. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +3 -2
  115. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +2 -1
  116. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +0 -49
  117. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +2 -1
  118. package/packages/dd-trace/src/llmobs/plugins/langchain/messages.js +76 -0
  119. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -26
  120. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/index.js +68 -0
  121. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +57 -0
  122. package/packages/dd-trace/src/llmobs/sdk.js +2 -2
  123. package/packages/dd-trace/src/log/index.js +0 -10
  124. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +103 -0
  125. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -0
  126. package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
  127. package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
  128. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  129. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +3 -2
  130. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  131. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +3 -2
  132. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +19 -51
  133. package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
  134. package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
  135. package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
  136. package/packages/dd-trace/src/opentelemetry/trace/index.js +70 -0
  137. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +74 -0
  138. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +342 -0
  139. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -11
  140. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -10
  141. package/packages/dd-trace/src/opentracing/span.js +1 -1
  142. package/packages/dd-trace/src/opentracing/tracer.js +17 -5
  143. package/packages/dd-trace/src/plugins/index.js +1 -0
  144. package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
  145. package/packages/dd-trace/src/plugins/plugin.js +6 -11
  146. package/packages/dd-trace/src/plugins/storage.js +2 -2
  147. package/packages/dd-trace/src/plugins/tracing.js +22 -5
  148. package/packages/dd-trace/src/plugins/util/test.js +128 -5
  149. package/packages/dd-trace/src/plugins/util/url.js +2 -1
  150. package/packages/dd-trace/src/plugins/util/web.js +6 -88
  151. package/packages/dd-trace/src/profiling/profiler.js +34 -77
  152. package/packages/dd-trace/src/profiling/profilers/event_plugins/crypto.js +32 -0
  153. package/packages/dd-trace/src/profiling/profilers/event_plugins/zlib.js +19 -0
  154. package/packages/dd-trace/src/profiling/profilers/events.js +35 -0
  155. package/packages/dd-trace/src/proxy.js +3 -4
  156. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -13
  157. package/packages/dd-trace/src/service-naming/index.js +1 -1
  158. package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
  159. package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
  160. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
  161. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
  162. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +21 -1
  163. package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
  164. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
  165. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +15 -1
  166. package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
  167. package/packages/dd-trace/src/span_processor.js +1 -2
  168. package/packages/dd-trace/src/span_stats.js +5 -1
  169. package/packages/dd-trace/src/tagger.js +2 -2
  170. package/packages/dd-trace/src/telemetry/send-data.js +5 -7
  171. package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
  172. package/vendor/dist/protobufjs/index.js +1 -1
  173. package/packages/dd-trace/src/log/utils.js +0 -16
  174. package/vendor/dist/ignore/LICENSE +0 -21
  175. package/vendor/dist/ignore/index.js +0 -1
@@ -11,6 +11,7 @@ const {
11
11
  JEST_WORKER_TRACE_PAYLOAD_CODE,
12
12
  JEST_WORKER_COVERAGE_PAYLOAD_CODE,
13
13
  JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
14
+ JEST_WORKER_QUARANTINE_PAYLOAD_CODE,
14
15
  getTestLineStart,
15
16
  getTestSuitePath,
16
17
  getTestParametersString,
@@ -123,6 +124,38 @@ const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
123
124
  const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
124
125
  const atrSuppressedErrors = new Map()
125
126
 
127
+ // Track quarantined tests whose errors were suppressed, keyed by "suite › testName"
128
+ const quarantinedFailingTests = new Set()
129
+
130
+ /**
131
+ * Sends suppressed quarantine test names from a worker process to the main process.
132
+ * Supports both child_process (process.send) and worker_threads (parentPort.postMessage).
133
+ * Returns true if the data was sent (worker mode), false if in main process (runInBand).
134
+ *
135
+ * @param {string[]} testNames
136
+ * @returns {boolean}
137
+ */
138
+ function sendQuarantineInfoToMainProcess (testNames) {
139
+ const payload = [JEST_WORKER_QUARANTINE_PAYLOAD_CODE, JSON.stringify(testNames)]
140
+
141
+ if (process.send) {
142
+ process.send(payload)
143
+ return true
144
+ }
145
+
146
+ try {
147
+ const { isMainThread, parentPort } = require('node:worker_threads')
148
+ if (!isMainThread && parentPort) {
149
+ parentPort.postMessage(payload)
150
+ return true
151
+ }
152
+ } catch {
153
+ // Not in a worker context
154
+ }
155
+
156
+ return false
157
+ }
158
+
126
159
  // based on https://github.com/facebook/jest/blob/main/packages/jest-circus/src/formatNodeAssertErrors.ts#L41
127
160
  function formatJestError (errors) {
128
161
  let error
@@ -756,6 +789,18 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
756
789
  const willBeRetriedByFailedTestReplay = numRetries > 0 && numTestExecutions - 1 < numRetries
757
790
  const mightHitBreakpoint = this.isDiEnabled && numTestExecutions >= 2
758
791
 
792
+ // For quarantined tests, suppress errors so Jest doesn't count them as failures.
793
+ // This prevents --bail from stopping the test run on quarantined test failures.
794
+ // The actual status ('fail') is already captured above for dd-trace reporting.
795
+ // Only suppress on the final execution — not when ATR/EFD/ATF will retry the test.
796
+ if (!event.test?.[ATR_RETRY_SUPPRESSION_FLAG] && !willBeRetriedByFailedTestReplay) {
797
+ const quarantineCtx = testContexts.get(event.test)
798
+ if (quarantineCtx?.isQuarantined && event.test.errors?.length) {
799
+ quarantinedFailingTests.add(`${quarantineCtx.suite} › ${quarantineCtx.name}`)
800
+ event.test.errors = []
801
+ }
802
+ }
803
+
759
804
  const ctx = testContexts.get(event.test)
760
805
  if (!ctx) {
761
806
  log.warn('"ci:jest:test_done": no context found for test "%s"', testName)
@@ -813,9 +858,25 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
813
858
  }
814
859
  if (event.name === 'run_finish') {
815
860
  for (const [test, errors] of atrSuppressedErrors) {
816
- test.errors = errors
861
+ // Do not restore errors for quarantined tests — they should stay suppressed
862
+ // so Jest doesn't see the failure (prevents --bail from stopping the run).
863
+ const ctx = testContexts.get(test)
864
+ if (ctx?.isQuarantined) {
865
+ const testName = getJestTestName(test, this.getShouldStripSeedFromTestName())
866
+ quarantinedFailingTests.add(`${ctx.suite} › ${testName}`)
867
+ } else {
868
+ test.errors = errors
869
+ }
817
870
  }
818
871
  atrSuppressedErrors.clear()
872
+
873
+ // In parallel mode, send suppressed quarantine info to the main process
874
+ // so it can include them in the session summary.
875
+ // In runInBand mode, keep the set — it will be consumed by the session-level code directly.
876
+ if (quarantinedFailingTests.size > 0 && sendQuarantineInfoToMainProcess([...quarantinedFailingTests])) {
877
+ quarantinedFailingTests.clear()
878
+ }
879
+
819
880
  efdDeterminedRetries.clear()
820
881
  efdSlowAbortedTests.clear()
821
882
  efdNewTestCandidates.clear()
@@ -1255,6 +1316,7 @@ function getCliWrapper (isNewJestVersion) {
1255
1316
 
1256
1317
  let numFailedQuarantinedTests = 0
1257
1318
  let numFailedQuarantinedOrDisabledAttemptedToFixTests = 0
1319
+ let numSuppressedQuarantinedTests = 0
1258
1320
  if (isTestManagementTestsEnabled) {
1259
1321
  const failedTests = result
1260
1322
  .results
@@ -1291,45 +1353,69 @@ function getCliWrapper (isNewJestVersion) {
1291
1353
  }
1292
1354
  }
1293
1355
 
1356
+ // Include quarantined tests whose errors were suppressed at test_done time.
1357
+ // These tests don't appear as failed in Jest's results because their errors were cleared
1358
+ // to prevent --bail from stopping the run, but they should still be counted for the summary.
1359
+ for (const name of quarantinedFailingTests) {
1360
+ if (!quarantineIgnoredNames.includes(name)) {
1361
+ numSuppressedQuarantinedTests++
1362
+ quarantineIgnoredNames.push(name)
1363
+ }
1364
+ }
1365
+ quarantinedFailingTests.clear()
1366
+
1294
1367
  // If every test that failed was quarantined, we'll consider the suite passed
1295
1368
  // Note that if a test is attempted to fix,
1296
1369
  // it's considered quarantined both if it's disabled and if it's quarantined
1297
1370
  // (it'll run but its status is ignored)
1298
1371
  // Skip if EFD block already flipped (to avoid logging twice)
1372
+ // Only use visible failures (from Jest results) for the flip check.
1373
+ // Suppressed quarantine failures are not in numFailedTests.
1374
+ const visibleQuarantineFailures = numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
1299
1375
  if (
1300
1376
  !result.results.success &&
1301
1377
  !mustNotFlipSuccess &&
1302
- (numFailedQuarantinedOrDisabledAttemptedToFixTests !== 0 || numFailedQuarantinedTests !== 0) &&
1303
- result.results.numFailedTests ===
1304
- numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
1378
+ visibleQuarantineFailures !== 0 &&
1379
+ result.results.numFailedTests === visibleQuarantineFailures
1305
1380
  ) {
1306
1381
  result.results.success = true
1307
- ignoredFailuresSummary = {
1308
- efdNames: [],
1309
- quarantineNames: quarantineIgnoredNames,
1310
- totalCount: numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests,
1382
+ }
1383
+
1384
+ const totalQuarantineFailures = visibleQuarantineFailures + numSuppressedQuarantinedTests
1385
+ if (totalQuarantineFailures > 0) {
1386
+ if (ignoredFailuresSummary) {
1387
+ ignoredFailuresSummary.quarantineNames = quarantineIgnoredNames
1388
+ ignoredFailuresSummary.totalCount += totalQuarantineFailures
1389
+ } else {
1390
+ ignoredFailuresSummary = {
1391
+ efdNames: [],
1392
+ quarantineNames: quarantineIgnoredNames,
1393
+ totalCount: totalQuarantineFailures,
1394
+ }
1311
1395
  }
1312
1396
  }
1313
1397
  }
1314
1398
 
1315
1399
  // Combined check: if all failed tests are accounted for by EFD (flaky retries) and/or quarantine,
1316
1400
  // we should consider the suite passed even when neither check alone covers all failures.
1401
+ // Only visible failures (in Jest results) are compared — suppressed quarantine failures
1402
+ // are already removed from numFailedTests at test_done time.
1317
1403
  if (
1318
1404
  !result.results.success &&
1319
1405
  !mustNotFlipSuccess &&
1320
1406
  (isEarlyFlakeDetectionEnabled || isTestManagementTestsEnabled)
1321
1407
  ) {
1322
- const totalIgnoredFailures =
1408
+ const visibleIgnoredFailures =
1323
1409
  numEfdFailedTestsToIgnore + numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
1324
1410
  if (
1325
- totalIgnoredFailures !== 0 &&
1326
- result.results.numFailedTests === totalIgnoredFailures
1411
+ visibleIgnoredFailures !== 0 &&
1412
+ result.results.numFailedTests === visibleIgnoredFailures
1327
1413
  ) {
1328
1414
  result.results.success = true
1329
1415
  ignoredFailuresSummary = {
1330
1416
  efdNames: efdIgnoredNames,
1331
1417
  quarantineNames: quarantineIgnoredNames,
1332
- totalCount: totalIgnoredFailures,
1418
+ totalCount: visibleIgnoredFailures + numSuppressedQuarantinedTests,
1333
1419
  }
1334
1420
  }
1335
1421
  }
@@ -1865,6 +1951,12 @@ function onMessageWrapper (onMessage) {
1865
1951
  workerReportTelemetryCh.publish(data)
1866
1952
  return
1867
1953
  }
1954
+ if (code === JEST_WORKER_QUARANTINE_PAYLOAD_CODE) { // quarantined test failures suppressed in worker
1955
+ for (const name of JSON.parse(data)) {
1956
+ quarantinedFailingTests.add(name)
1957
+ }
1958
+ return
1959
+ }
1868
1960
  return onMessage.apply(this, arguments)
1869
1961
  }
1870
1962
  }
@@ -264,6 +264,14 @@ function getFinalStatus ({
264
264
  }) {
265
265
  // Note that intermediate executions DO NOT report a final status tag
266
266
 
267
+ // Intermediate EFD and ATF executions must not carry a final status, regardless of quarantine/disabled state
268
+ const isIntermediateExecution =
269
+ (isEfdRetry && !isLastEfdRetry) ||
270
+ (isAttemptToFix && !isLastAttemptToFix)
271
+ if (isIntermediateExecution) {
272
+ return
273
+ }
274
+
267
275
  // If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
268
276
  // the final status of its last execution should be reported as 'skip'.
269
277
  if (isQuarantined || isDisabled) {
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const { addHook, getHooks } = require('./helpers/instrument')
4
+
5
+ for (const hook of getHooks('@modelcontextprotocol/sdk')) {
6
+ addHook(hook, exports => exports)
7
+ }
@@ -34,22 +34,6 @@ function wrapAsJson (asJson) {
34
34
  }
35
35
  }
36
36
 
37
- function wrapMixin (mixin) {
38
- const ch = channel('apm:pino:log')
39
- return function mixinWithTrace () {
40
- let obj = {}
41
-
42
- if (mixin) {
43
- obj = mixin.apply(this, arguments)
44
- }
45
-
46
- const payload = { message: obj }
47
- ch.publish(payload)
48
-
49
- return payload.message
50
- }
51
- }
52
-
53
37
  function wrapPrettifyObject (prettifyObject) {
54
38
  const ch = channel('apm:pino:log')
55
39
  return function prettifyObjectWithTrace (input) {
@@ -81,26 +65,18 @@ addHook({ name: 'pino', versions: ['2 - 3', '4'], patchDefault: true }, (pino) =
81
65
  return wrapped
82
66
  })
83
67
 
84
- addHook({ name: 'pino', versions: ['>=5 <5.14.0'], patchDefault: true }, (pino) => {
68
+ addHook({ name: 'pino', versions: ['>=5 <6.8.0'], patchDefault: true }, (pino) => {
85
69
  const asJsonSym = ((pino.default || pino)?.symbols.asJsonSym) || 'asJson'
86
70
 
87
- const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, wrapAsJson, pino))
88
-
89
- return wrapped
90
- })
91
-
92
- addHook({ name: 'pino', versions: ['>=5.14.0 <6.8.0'] }, (pino) => {
93
- const mixinSym = (pino.default || pino).symbols.mixinSym
94
-
95
- const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(mixinSym, wrapMixin, pino.default || pino))
71
+ const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, wrapAsJson, pino.default || pino))
96
72
 
97
73
  return wrapped
98
74
  })
99
75
 
100
76
  addHook({ name: 'pino', versions: ['>=6.8.0'], patchDefault: false }, (pino) => {
101
- const mixinSym = pino.symbols.mixinSym
77
+ const asJsonSym = pino.symbols.asJsonSym
102
78
 
103
- const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(mixinSym, wrapMixin, pino))
79
+ const wrapped = shimmer.wrapFunction(pino, pino => wrapPino(asJsonSym, wrapAsJson, pino))
104
80
  wrapped.pino = wrapped
105
81
  wrapped.default = wrapped
106
82
 
@@ -0,0 +1,27 @@
1
+ 'use strict'
2
+
3
+ // Serialized into chromium via Playwright's `page.evaluate`. Excluded from coverage by filename.
4
+ // Rename only if you update that glob too.
5
+
6
+ /** @returns {{ isRumInstrumented: boolean, isRumActive: boolean, rumSamplingRate: number | null }} */
7
+ function detectRum () {
8
+ const isRumInstrumented = !!window.DD_RUM
9
+ const isRumActive = window.DD_RUM && window.DD_RUM.getInternalContext
10
+ ? !!window.DD_RUM.getInternalContext()
11
+ : false
12
+ const rumSamplingRate = window.DD_RUM && window.DD_RUM.getInitConfiguration
13
+ ? window.DD_RUM.getInitConfiguration().sessionSampleRate
14
+ : null
15
+ return { isRumInstrumented, isRumActive, rumSamplingRate }
16
+ }
17
+
18
+ /** @returns {boolean} */
19
+ function stopRumSession () {
20
+ if (window.DD_RUM && window.DD_RUM.stopSession) {
21
+ window.DD_RUM.stopSession()
22
+ return true
23
+ }
24
+ return false
25
+ }
26
+
27
+ module.exports = { detectRum, stopRumSession }
@@ -52,6 +52,9 @@ let applyRepeatEachIndex = null
52
52
 
53
53
  let startedSuites = []
54
54
 
55
+ // Browser-side callbacks live in a coverage-excluded file so coverage counters can't reach chromium.
56
+ const { detectRum, stopRumSession } = require('./playwright-browser-scripts')
57
+
55
58
  const STATUS_TO_TEST_STATUS = {
56
59
  passed: 'pass',
57
60
  failed: 'fail',
@@ -1117,16 +1120,7 @@ addHook({
1117
1120
 
1118
1121
  try {
1119
1122
  if (page) {
1120
- const { isRumInstrumented, isRumActive, rumSamplingRate } = await page.evaluate(() => {
1121
- const isRumInstrumented = !!window.DD_RUM
1122
- const isRumActive = window.DD_RUM && window.DD_RUM.getInternalContext
1123
- ? !!window.DD_RUM.getInternalContext()
1124
- : false
1125
- const rumSamplingRate = window.DD_RUM && window.DD_RUM.getInitConfiguration
1126
- ? window.DD_RUM.getInitConfiguration().sessionSampleRate
1127
- : null
1128
- return { isRumInstrumented, isRumActive, rumSamplingRate }
1129
- })
1123
+ const { isRumInstrumented, isRumActive, rumSamplingRate } = await page.evaluate(detectRum)
1130
1124
  if (isRumInstrumented && rumSamplingRate < 100 && !isRumActive) {
1131
1125
  log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
1132
1126
  }
@@ -1209,13 +1203,7 @@ addHook({
1209
1203
  fn: async function ({ page }) {
1210
1204
  try {
1211
1205
  if (page) {
1212
- const isRumActive = await page.evaluate(() => {
1213
- if (window.DD_RUM && window.DD_RUM.stopSession) {
1214
- window.DD_RUM.stopSession()
1215
- return true
1216
- }
1217
- return false
1218
- })
1206
+ const isRumActive = await page.evaluate(stopRumSession)
1219
1207
 
1220
1208
  if (isRumActive) {
1221
1209
  // Give some time RUM to flush data, similar to what we do in selenium
@@ -11,6 +11,8 @@ const finishCh = channel('apm:redis:command:finish')
11
11
  const errorCh = channel('apm:redis:command:error')
12
12
 
13
13
  let createClientUrl
14
+ let createClientName
15
+ const instanceInfo = new WeakMap()
14
16
 
15
17
  function wrapAddCommand (addCommand) {
16
18
  return function (command) {
@@ -21,7 +23,7 @@ function wrapAddCommand (addCommand) {
21
23
  const name = command[0]
22
24
  const args = command.slice(1)
23
25
 
24
- const ctx = getStartCtx(this, name, args, this._url)
26
+ const ctx = getStartCtx(this, name, args)
25
27
  return startCh.runStores(ctx, () => {
26
28
  const res = addCommand.apply(this, arguments)
27
29
 
@@ -36,17 +38,16 @@ function wrapCommandQueueClass (cls) {
36
38
  const ret = class RedisCommandQueue extends cls {
37
39
  constructor (...args) {
38
40
  super(...args)
41
+ let url = { host: 'localhost', port: 6379 }
39
42
  if (createClientUrl) {
40
43
  try {
41
44
  const parsed = new URL(createClientUrl)
42
- if (parsed) {
43
- this._url = { host: parsed.hostname, port: Number(parsed.port) || 6379 }
44
- }
45
+ url = { host: parsed.hostname, port: Number(parsed.port) || 6379 }
45
46
  } catch {
46
47
  // ignore
47
48
  }
48
49
  }
49
- this._url = this._url || { host: 'localhost', port: 6379 }
50
+ instanceInfo.set(this, { connectionName: createClientName, url })
50
51
  }
51
52
  }
52
53
  return ret
@@ -55,8 +56,10 @@ function wrapCommandQueueClass (cls) {
55
56
  function wrapCreateClient (request) {
56
57
  return function (opts) {
57
58
  createClientUrl = opts && opts.url
59
+ createClientName = opts && opts.name
58
60
  const ret = request.apply(this, arguments)
59
61
  createClientUrl = undefined
62
+ createClientName = undefined
60
63
  return ret
61
64
  }
62
65
  }
@@ -134,12 +137,15 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
134
137
  return redis
135
138
  })
136
139
 
137
- function getStartCtx (client, command, args, url = {}) {
140
+ function getStartCtx (client, command, args) {
141
+ const { url, connectionName } = instanceInfo.get(client) || {}
142
+
138
143
  return {
139
144
  db: client.selected_db,
140
145
  command,
141
146
  args,
142
147
  connectionOptions: client.connection_options || client.connection_option || client.connectionOption || url,
148
+ connectionName,
143
149
  }
144
150
  }
145
151
 
@@ -58,35 +58,49 @@ function wrapConstructEventAsync (constructEventAsync) {
58
58
  }
59
59
  }
60
60
 
61
- function wrapStripe (Stripe) {
61
+ 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
+ }
74
+ }
75
+
76
+ // stripe <22: the constructor mutates this (when invoked with 'new') and
77
+ // returns nothing; without 'new' it delegates to 'new Stripe(...)' and returns
78
+ // that result. We need to instrument whichever object actually got populated
79
+ function wrapLegacyStripe (Stripe) {
62
80
  return function wrappedStripe () {
63
- let stripe = Stripe.apply(this, arguments)
64
-
65
- // to support both with and without "new" operator syntax
66
- if (this instanceof Stripe) {
67
- stripe = this
68
- }
69
-
70
- if (typeof stripe.checkout?.sessions?.create === 'function') {
71
- shimmer.wrap(stripe.checkout.sessions, 'create', wrapSessionCreate)
72
- }
73
- if (typeof stripe.paymentIntents?.create === 'function') {
74
- shimmer.wrap(stripe.paymentIntents, 'create', wrapPaymentIntentCreate)
75
- }
76
- if (typeof stripe.webhooks?.constructEvent === 'function') {
77
- shimmer.wrap(stripe.webhooks, 'constructEvent', wrapConstructEvent)
78
- }
79
- if (typeof stripe.webhooks?.constructEventAsync === 'function') {
80
- shimmer.wrap(stripe.webhooks, 'constructEventAsync', wrapConstructEventAsync)
81
- }
81
+ const result = Stripe.apply(this, arguments)
82
+ const stripe = this instanceof Stripe ? this : result
83
+ instrumentStripeInstance(stripe)
84
+ return stripe
85
+ }
86
+ }
82
87
 
88
+ // stripe >=22: the constructor is a factory that always returns a fresh Stripe
89
+ // instance regardless of 'new', so we just instrument and forward the result
90
+ function wrapStripe (Stripe) {
91
+ return function wrappedStripe () {
92
+ const stripe = Stripe.apply(this, arguments)
93
+ instrumentStripeInstance(stripe)
83
94
  return stripe
84
95
  }
85
96
  }
86
97
 
87
98
  addHook({
88
99
  name: 'stripe',
89
- versions: ['9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '>=20.0.0'],
90
- }, Stripe => {
91
- return shimmer.wrapFunction(Stripe, wrapStripe)
92
- })
100
+ versions: ['9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '>=20.0.0 <22'],
101
+ }, Stripe => shimmer.wrapFunction(Stripe, wrapLegacyStripe))
102
+
103
+ addHook({
104
+ name: 'stripe',
105
+ versions: ['>=22'],
106
+ }, Stripe => shimmer.wrapFunction(Stripe, wrapStripe))
@@ -866,6 +866,9 @@ function wrapVitestTestRunner (VitestTestRunner) {
866
866
  } else {
867
867
  testPassCh.publish({ task, ...ctx.currentStore })
868
868
  }
869
+ if (shouldFlipStatus) {
870
+ task.result.state = 'pass'
871
+ }
869
872
  }
870
873
 
871
874
  const isRetryReasonAtr = numAttempt > 0 &&
@@ -1174,7 +1177,12 @@ addHook({
1174
1177
  })
1175
1178
  } else if (state === 'pass' && !isSwitchedStatus) {
1176
1179
  if (testCtx) {
1177
- testPassCh.publish({ task, ...testCtx.currentStore })
1180
+ testPassCh.publish({
1181
+ task,
1182
+ finalStatus:
1183
+ disabledTasks.has(task) || quarantinedTasks.has(task) ? 'skip' : 'pass',
1184
+ ...testCtx.currentStore,
1185
+ })
1178
1186
  }
1179
1187
  } else if (state === 'fail' || isSwitchedStatus) {
1180
1188
  let testError
@@ -1197,7 +1205,9 @@ addHook({
1197
1205
 
1198
1206
  // Check if all EFD retries failed
1199
1207
  const providedContext = getProvidedContext()
1200
- if (providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))) {
1208
+ const isEfdRetry =
1209
+ providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))
1210
+ if (isEfdRetry) {
1201
1211
  const statuses = taskToStatuses.get(task)
1202
1212
  // statuses only includes repetitions (not the initial run), so we check against numRepeats (not +1)
1203
1213
  if (statuses && statuses.length === providedContext.numRepeats &&
@@ -1207,8 +1217,9 @@ addHook({
1207
1217
  }
1208
1218
 
1209
1219
  // ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
1210
- if (providedContext.isFlakyTestRetriesEnabled && !attemptToFixTasks.has(task) &&
1211
- !newTasks.has(task) && !modifiedTasks.has(task)) {
1220
+ const isAtrRetry = providedContext.isFlakyTestRetriesEnabled && !attemptToFixTasks.has(task) &&
1221
+ !newTasks.has(task) && !modifiedTasks.has(task)
1222
+ if (isAtrRetry) {
1212
1223
  const maxRetries = providedContext.flakyTestRetriesCount ?? 0
1213
1224
  if (maxRetries > 0 && task.result?.retryCount === maxRetries) {
1214
1225
  hasFailedAllRetries = true
@@ -1218,11 +1229,28 @@ addHook({
1218
1229
  if (testCtx) {
1219
1230
  const isRetry = task.result?.retryCount > 0
1220
1231
  // `duration` is the duration of all the retries, so it can't be used if there are retries
1232
+
1233
+ let finalStatus
1234
+ if (isSwitchedStatus) {
1235
+ if (disabledTasks.has(task) || quarantinedTasks.has(task)) {
1236
+ finalStatus = 'skip'
1237
+ } else if (isAtrRetry || isEfdRetry) {
1238
+ finalStatus = hasFailedAllRetries ? 'fail' : 'pass'
1239
+ } else if (attemptToFixTasks.has(task)) {
1240
+ finalStatus = attemptToFixFailed ? 'fail' : 'pass'
1241
+ } else {
1242
+ finalStatus = undefined
1243
+ }
1244
+ } else {
1245
+ finalStatus = 'fail'
1246
+ }
1247
+
1221
1248
  testErrorCh.publish({
1222
1249
  duration: isRetry ? undefined : duration,
1223
1250
  error: testError,
1224
1251
  hasFailedAllRetries,
1225
1252
  attemptToFixFailed,
1253
+ finalStatus,
1226
1254
  ...testCtx.currentStore,
1227
1255
  })
1228
1256
  }
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { addHook } = require('./helpers/instrument')
5
+ const { createCallbackInstrumentor } = require('./helpers/callback-instrumentor')
6
+
7
+ const asyncMethods = [
8
+ 'brotliCompress',
9
+ 'brotliDecompress',
10
+ 'deflate',
11
+ 'deflateRaw',
12
+ 'gunzip',
13
+ 'gzip',
14
+ 'inflate',
15
+ 'inflateRaw',
16
+ 'unzip',
17
+ 'zstdCompress', // Node 22.15+ / 23.8+ / 24+
18
+ 'zstdDecompress', // Node 22.15+ / 23.8+ / 24+
19
+ ]
20
+
21
+ addHook({ name: 'zlib' }, zlib => {
22
+ const instrument = createCallbackInstrumentor('apm:zlib:operation')
23
+ for (const method of asyncMethods) {
24
+ if (typeof zlib[method] === 'function') {
25
+ shimmer.wrap(zlib, method, instrument(() => ({ operation: method })))
26
+ }
27
+ }
28
+ return zlib
29
+ })
@@ -56,7 +56,6 @@ class BaseAwsSdkPlugin extends ClientPlugin {
56
56
 
57
57
  const meta = {
58
58
  'span.kind': 'client',
59
- 'service.name': this.serviceName(),
60
59
  'aws.operation': operation,
61
60
  'aws.region': awsRegion,
62
61
  region: awsRegion,
@@ -70,6 +69,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
70
69
  const span = this.startSpan(this.operationFromRequest(request), {
71
70
  childOf,
72
71
  meta,
72
+ service: this.serviceName(),
73
73
  integrationName: 'aws-sdk',
74
74
  }, ctx)
75
75
 
@@ -197,8 +197,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
197
197
 
198
198
  isEnabled (request) {
199
199
  const serviceId = this.serviceIdentifier.toUpperCase()
200
- const envVarValue = getValueFromEnvSources(`DD_TRACE_AWS_SDK_${serviceId}_ENABLED`)
201
- return envVarValue ? isTrue(envVarValue) : true
200
+ return this._tracerConfig[`DD_TRACE_AWS_SDK_${serviceId}_ENABLED`] ?? true
202
201
  }
203
202
 
204
203
  addResponseTags (span, response) {
@@ -40,6 +40,7 @@ class DynamoDb extends BaseAwsSdkPlugin {
40
40
 
41
41
  // Also add span type to match serverless convention
42
42
  tags['span.type'] = 'dynamodb'
43
+ tags['db.system'] = 'aws.dynamodb'
43
44
 
44
45
  return tags
45
46
  }
@@ -12,6 +12,7 @@ class EventBridge extends BaseAwsSdkPlugin {
12
12
  return {
13
13
  'resource.name': operation ? `${operation} ${params.source}` : params.source,
14
14
  'aws.eventbridge.source': `${params.source}`,
15
+ 'messaging.system': 'aws_eventbridge',
15
16
  rulename: `${rulename}`,
16
17
  }
17
18
  }
@@ -68,6 +68,7 @@ class Kinesis extends BaseAwsSdkPlugin {
68
68
  return {
69
69
  'resource.name': `${operation} ${params.StreamName}`,
70
70
  'aws.kinesis.stream_name': params.StreamName,
71
+ 'messaging.system': 'aws_kinesis',
71
72
  streamname: params.StreamName,
72
73
  }
73
74
  }
@@ -11,6 +11,7 @@ class Redshift extends BaseAwsSdkPlugin {
11
11
  return {
12
12
  'resource.name': `${operation} ${params.ClusterIdentifier}`,
13
13
  'aws.redshift.cluster_identifier': params.ClusterIdentifier,
14
+ 'db.system': 'aws.redshift',
14
15
  clusteridentifier: params.ClusterIdentifier,
15
16
  }
16
17
  }
@@ -24,6 +24,7 @@ class Sns extends BaseAwsSdkPlugin {
24
24
  return {
25
25
  'resource.name': `${operation} ${params.TopicArn || response.data.TopicArn}`,
26
26
  'aws.sns.topic_arn': TopicArn,
27
+ 'messaging.system': 'aws.sns',
27
28
  topicname: topicName,
28
29
  }
29
30
 
@@ -100,6 +100,7 @@ class Sqs extends BaseAwsSdkPlugin {
100
100
  const tags = {
101
101
  'resource.name': `${operation} ${params.QueueName || params.QueueUrl}`,
102
102
  'aws.sqs.queue_name': params.QueueName || params.QueueUrl,
103
+ 'messaging.system': 'aws_sqs',
103
104
  queuename: queueName,
104
105
  }
105
106