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
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
4
3
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
5
4
 
6
5
  const spanContexts = new WeakMap()
@@ -11,8 +10,9 @@ class AzureEventHubsProducerPlugin extends ProducerPlugin {
11
10
  static get prefix () { return 'tracing:apm:azure-event-hubs:send' }
12
11
 
13
12
  bindStart (ctx) {
13
+ const batchLinksEnabled = this._tracerConfig.DD_TRACE_AZURE_EVENTHUBS_BATCH_LINKS_ENABLED
14
14
  // we do not want to make these spans when batch linking is disabled.
15
- if (!batchLinksAreEnabled() && ctx.functionName === 'tryAdd') {
15
+ if (!batchLinksEnabled && ctx.functionName === 'tryAdd') {
16
16
  return ctx.currentStore
17
17
  }
18
18
 
@@ -37,7 +37,7 @@ class AzureEventHubsProducerPlugin extends ProducerPlugin {
37
37
  span.setTag('message.id', ctx.eventData.messageID)
38
38
  }
39
39
 
40
- if (batchLinksAreEnabled()) {
40
+ if (batchLinksEnabled) {
41
41
  const spanContext = spanContexts.get(ctx.batch)
42
42
  if (spanContext) {
43
43
  spanContext.push(span.context())
@@ -58,13 +58,11 @@ class AzureEventHubsProducerPlugin extends ProducerPlugin {
58
58
  for (const event of eventData) {
59
59
  injectTraceContext(this.tracer, span, event)
60
60
  }
61
- } else {
62
- if (batchLinksAreEnabled()) {
63
- const contexts = spanContexts.get(eventData)
64
- if (contexts) {
65
- for (const spanContext of contexts) {
66
- span.addLink(spanContext)
67
- }
61
+ } else if (batchLinksEnabled) {
62
+ const contexts = spanContexts.get(eventData)
63
+ if (contexts) {
64
+ for (const spanContext of contexts) {
65
+ span.addLink(spanContext)
68
66
  }
69
67
  }
70
68
  }
@@ -88,9 +86,4 @@ function injectTraceContext (tracer, span, event) {
88
86
  tracer.inject(span, 'text_map', event.properties)
89
87
  }
90
88
 
91
- function batchLinksAreEnabled () {
92
- const eh = getValueFromEnvSources('DD_TRACE_AZURE_EVENTHUBS_BATCH_LINKS_ENABLED')
93
- return eh !== 'false'
94
- }
95
-
96
89
  module.exports = AzureEventHubsProducerPlugin
@@ -1,6 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
4
3
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
5
4
  const spanContexts = new WeakMap()
6
5
 
@@ -10,8 +9,9 @@ class AzureServiceBusProducerPlugin extends ProducerPlugin {
10
9
  static get prefix () { return 'tracing:apm:azure-service-bus:send' }
11
10
 
12
11
  bindStart (ctx) {
12
+ const batchLinksEnabled = this._tracerConfig.DD_TRACE_AZURE_SERVICEBUS_BATCH_LINKS_ENABLED
13
13
  // we do not want to make these spans when batch linking is disabled.
14
- if (!batchLinksAreEnabled() && ctx.functionName === 'tryAddMessage') {
14
+ if (!batchLinksEnabled && ctx.functionName === 'tryAddMessage') {
15
15
  return ctx.currentStore
16
16
  }
17
17
 
@@ -36,7 +36,7 @@ class AzureServiceBusProducerPlugin extends ProducerPlugin {
36
36
  span.setTag('message.id', ctx.msg)
37
37
  }
38
38
 
39
- if (batchLinksAreEnabled()) {
39
+ if (batchLinksEnabled) {
40
40
  const spanContext = spanContexts.get(ctx.batch)
41
41
  if (spanContext) {
42
42
  spanContext.push(span.context())
@@ -52,7 +52,7 @@ class AzureServiceBusProducerPlugin extends ProducerPlugin {
52
52
  const isBatch = messages.constructor?.name === 'ServiceBusMessageBatchImpl'
53
53
  if (isBatch) {
54
54
  span.setTag('messaging.batch.message_count', messages.count)
55
- if (batchLinksAreEnabled()) {
55
+ if (batchLinksEnabled) {
56
56
  const contexts = spanContexts.get(messages)
57
57
  if (contexts) {
58
58
  for (const spanContext of contexts) {
@@ -89,9 +89,4 @@ function injectTraceContext (tracer, span, msg) {
89
89
  tracer.inject(span, 'text_map', msg.applicationProperties)
90
90
  }
91
91
 
92
- function batchLinksAreEnabled () {
93
- const sb = getValueFromEnvSources('DD_TRACE_AZURE_SERVICEBUS_BATCH_LINKS_ENABLED')
94
- return sb !== 'false'
95
- }
96
-
97
92
  module.exports = AzureServiceBusProducerPlugin
@@ -6,7 +6,7 @@ const realSetTimeout = setTimeout
6
6
 
7
7
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
8
8
  const { storage } = require('../../datadog-core')
9
- const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
9
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
10
10
 
11
11
  const {
12
12
  addIntelligentTestRunnerSpanTags,
@@ -37,6 +37,7 @@ const {
37
37
  TEST_SOURCE_FILE,
38
38
  TEST_SOURCE_START,
39
39
  TEST_STATUS,
40
+ TEST_FINAL_STATUS,
40
41
  } = require('../../dd-trace/src/plugins/util/test')
41
42
  const { RESOURCE_NAME } = require('../../../ext/tags')
42
43
  const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
@@ -116,7 +117,7 @@ class CucumberPlugin extends CiPlugin {
116
117
  finishAllTraceSpans(this.testSessionSpan)
117
118
  this.telemetry.count(TELEMETRY_TEST_SESSION, {
118
119
  provider: this.ciProviderName,
119
- autoInjected: !!getValueFromEnvSources('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER'),
120
+ autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
120
121
  })
121
122
 
122
123
  this.libraryConfig = null
@@ -307,11 +308,16 @@ class CucumberPlugin extends CiPlugin {
307
308
  isDisabled,
308
309
  isQuarantined,
309
310
  isModified,
311
+ finalStatus,
310
312
  }) => {
311
313
  const statusTag = isStep ? 'step.status' : TEST_STATUS
312
314
 
313
315
  span.setTag(statusTag, status)
314
316
 
317
+ if (finalStatus) {
318
+ span.setTag(TEST_FINAL_STATUS, finalStatus)
319
+ }
320
+
315
321
  if (isNew) {
316
322
  span.setTag(TEST_IS_NEW, 'true')
317
323
  if (isEfdRetry) {
@@ -1,5 +1,9 @@
1
1
  'use strict'
2
2
 
3
+ // Capture real timers at module load, before any test can install fake timers.
4
+ const { performance } = require('perf_hooks')
5
+ const dateNow = Date.now
6
+
3
7
  const {
4
8
  TEST_STATUS,
5
9
  TEST_IS_RUM_ACTIVE,
@@ -53,6 +57,7 @@ const {
53
57
  DYNAMIC_NAME_RE,
54
58
  logDynamicNamesWarning,
55
59
  getPullRequestBaseBranch,
60
+ TEST_FINAL_STATUS,
56
61
  } = require('../../dd-trace/src/plugins/util/test')
57
62
  const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
58
63
  const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
@@ -95,7 +100,7 @@ const {
95
100
  } = require('../../dd-trace/src/plugins/util/env')
96
101
  const { DD_MAJOR } = require('../../../version')
97
102
  const {
98
- resolveOriginalSourcePosition,
103
+ resolveOriginalSourceFile,
99
104
  resolveSourceLineForTest,
100
105
  shouldTrustInvocationDetailsLine,
101
106
  } = require('./source-map-utils')
@@ -240,6 +245,58 @@ function getSuiteStatus (suiteStats) {
240
245
  return 'pass'
241
246
  }
242
247
 
248
+ const FINAL_STATUS_RETRY_KIND = {
249
+ none: 'none',
250
+ atr: 'atr',
251
+ efd: 'efd',
252
+ atf: 'atf',
253
+ }
254
+
255
+ function getFinalStatusRetryKind ({ finishedTest, finishedTestAttempts, flakyTestRetriesCount }) {
256
+ // Infer retry kind from the executions we actually saw so ATR enabled with
257
+ // a retry count of 0 is still treated as a single final execution.
258
+ if (finishedTest.isAttemptToFix) {
259
+ return FINAL_STATUS_RETRY_KIND.atf
260
+ }
261
+
262
+ if (finishedTestAttempts.some(testAttempt => testAttempt.isEfdRetry)) {
263
+ return FINAL_STATUS_RETRY_KIND.efd
264
+ }
265
+
266
+ if (finishedTestAttempts.length > 1 && flakyTestRetriesCount > 0) {
267
+ return FINAL_STATUS_RETRY_KIND.atr
268
+ }
269
+
270
+ return FINAL_STATUS_RETRY_KIND.none
271
+ }
272
+
273
+ function getFinalStatus ({
274
+ status,
275
+ retryKind,
276
+ hasFailedAllRetries,
277
+ hasPassedAllAtfRetries,
278
+ isQuarantined,
279
+ isDisabled,
280
+ }) {
281
+ // If the test is quarantined or disabled, regardless of its actual execution result or active retry features,
282
+ // the final status of its last execution should be reported as 'skip'.
283
+ if (isQuarantined || isDisabled || status === 'skip') {
284
+ return 'skip'
285
+ }
286
+
287
+ switch (retryKind) {
288
+ case FINAL_STATUS_RETRY_KIND.atr:
289
+ case FINAL_STATUS_RETRY_KIND.efd:
290
+ // These modes report the aggregate result across attempts.
291
+ return hasFailedAllRetries ? 'fail' : 'pass'
292
+ case FINAL_STATUS_RETRY_KIND.atf:
293
+ // Attempt-to-fix only passes if every execution passed.
294
+ return hasPassedAllAtfRetries ? 'pass' : 'fail'
295
+ default:
296
+ return status
297
+ }
298
+ }
299
+
243
300
  class CypressPlugin {
244
301
  _isInit = false
245
302
  testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
@@ -349,6 +406,19 @@ class CypressPlugin {
349
406
  this.rumFlushWaitMillis = undefined
350
407
  this._pendingRequestErrorTags = []
351
408
  this.libraryConfigurationPromise = undefined
409
+ this._timeOrigin = 0
410
+ this._perfOrigin = 0
411
+ }
412
+
413
+ /**
414
+ * Returns the current time in the same coordinate system used by span
415
+ * start/finish. Captured at session span creation so it shares the same
416
+ * epoch as the trace without reaching into span internals.
417
+ *
418
+ * @returns {number}
419
+ */
420
+ _now () {
421
+ return this._timeOrigin + performance.now() - this._perfOrigin
352
422
  }
353
423
 
354
424
  // Init function returns a promise that resolves with the Cypress configuration
@@ -448,8 +518,7 @@ class CypressPlugin {
448
518
  this.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
449
519
 
450
520
  if (testSuiteAbsolutePath) {
451
- const resolvedSuitePosition = resolveOriginalSourcePosition(testSuiteAbsolutePath, 1)
452
- const resolvedSuiteAbsolutePath = resolvedSuitePosition ? resolvedSuitePosition.sourceFile : testSuiteAbsolutePath
521
+ const resolvedSuiteAbsolutePath = resolveOriginalSourceFile(testSuiteAbsolutePath) || testSuiteAbsolutePath
453
522
  const testSourceFile = getTestSuitePath(resolvedSuiteAbsolutePath, this.repositoryRoot)
454
523
  testSuiteSpanMetadata[TEST_SOURCE_FILE] = testSourceFile
455
524
  testSuiteSpanMetadata[TEST_SOURCE_START] = 1
@@ -665,6 +734,13 @@ class CypressPlugin {
665
734
  this.tracer._tracer._exporter.addMetadataTags(metadataTags)
666
735
  }
667
736
 
737
+ // Capture time references that match what startSpan records internally
738
+ // (trace.startTime = Date.now(), trace.ticks = performance.now()).
739
+ // This lets _now() produce values in the same coordinate system as
740
+ // span._startTime without accessing span internals.
741
+ this._timeOrigin = dateNow()
742
+ this._perfOrigin = performance.now()
743
+
668
744
  this.testSessionSpan = this.tracer.startSpan(`${TEST_FRAMEWORK_NAME}.test_session`, {
669
745
  childOf,
670
746
  tags: {
@@ -797,6 +873,7 @@ class CypressPlugin {
797
873
  : spec.relative
798
874
 
799
875
  const skippedTestSpan = this.getTestSpan({ testName: cypressTestName, testSuite: spec.relative, testSourceFile })
876
+ skippedTestSpan.setTag(TEST_FINAL_STATUS, 'skip')
800
877
 
801
878
  skippedTestSpan.setTag(TEST_STATUS, 'skip')
802
879
  if (isSkippedByItr) {
@@ -872,8 +949,9 @@ class CypressPlugin {
872
949
  if (this.itrCorrelationId) {
873
950
  finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
874
951
  }
875
- const resolvedSpecPosition = spec.absolute ? resolveOriginalSourcePosition(spec.absolute, 1) : null
876
- const resolvedSpecAbsolutePath = resolvedSpecPosition ? resolvedSpecPosition.sourceFile : spec.absolute
952
+ const resolvedSpecAbsolutePath = spec.absolute
953
+ ? resolveOriginalSourceFile(spec.absolute) || spec.absolute
954
+ : spec.absolute
877
955
  const testSourceFile = resolvedSpecAbsolutePath && this.repositoryRoot
878
956
  ? getTestSuitePath(resolvedSpecAbsolutePath, this.repositoryRoot)
879
957
  : spec.relative
@@ -886,6 +964,36 @@ class CypressPlugin {
886
964
  finishedTest.testSpan.setTag(TEST_CODE_OWNERS, codeOwners)
887
965
  }
888
966
 
967
+ // We can check if this is the last attempt regardless of the retry mechanism
968
+ const isLastAttempt = attemptIndex === finishedTestAttempts.length - 1
969
+ if (isLastAttempt) {
970
+ const testSpanTags = finishedTest.testSpan.context()._tags
971
+ const retryKind = getFinalStatusRetryKind({
972
+ finishedTest,
973
+ finishedTestAttempts,
974
+ flakyTestRetriesCount: this.flakyTestRetriesCount,
975
+ })
976
+
977
+ const hasFailedAllRetries = testSpanTags[TEST_HAS_FAILED_ALL_RETRIES] === 'true'
978
+ const hasPassedAllAtfRetries =
979
+ testSpanTags[TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED] === 'true'
980
+ const isQuarantined = testSpanTags[TEST_MANAGEMENT_IS_QUARANTINED] === 'true'
981
+ const isDisabled = testSpanTags[TEST_MANAGEMENT_IS_DISABLED] === 'true'
982
+
983
+ const finalStatus = getFinalStatus({
984
+ status: cypressTestStatus,
985
+ retryKind,
986
+ hasFailedAllRetries,
987
+ hasPassedAllAtfRetries,
988
+ isQuarantined,
989
+ isDisabled,
990
+ })
991
+
992
+ if (finalStatus) {
993
+ finishedTest.testSpan.setTag(TEST_FINAL_STATUS, finalStatus)
994
+ }
995
+ }
996
+
889
997
  finishedTest.testSpan.finish(finishedTest.finishTime)
890
998
  }
891
999
  }
@@ -1103,7 +1211,7 @@ class CypressPlugin {
1103
1211
  const finishedTest = {
1104
1212
  testName,
1105
1213
  testStatus,
1106
- finishTime: this.activeTestSpan._getTime(), // we store the finish time here
1214
+ finishTime: this._now(),
1107
1215
  testSpan: this.activeTestSpan,
1108
1216
  isEfdRetry,
1109
1217
  isAttemptToFix,
@@ -1,11 +1,68 @@
1
1
  'use strict'
2
2
 
3
+ const NoopTracer = require('../../dd-trace/src/noop/tracer')
3
4
  const Plugin = require('../../dd-trace/src/plugins/plugin')
4
5
 
5
- // Cypress plugin does not patch any library. This is just a placeholder to
6
- // follow the structure of the plugins
6
+ /**
7
+ * Cypress plugin handles setup-node-events from the instrumentation layer
8
+ * via a diagnostic channel, keeping the instrumentation free of tracer references.
9
+ */
7
10
  class CypressPlugin extends Plugin {
8
11
  static id = 'cypress'
12
+
13
+ constructor (...args) {
14
+ super(...args)
15
+
16
+ this.addSub('ci:cypress:setup-node-events', (payload) => {
17
+ // Bail out if the tracer failed to init (e.g. invalid DD_SITE).
18
+ // Mirrors the guard in the manual plugin entrypoint (plugin.js).
19
+ if (this._tracer._tracer instanceof NoopTracer) return
20
+
21
+ const { on, config, userAfterSpecHandlers, userAfterRunHandlers, cleanupWrapper } = payload
22
+
23
+ const registerAfterRunWithCleanup = (afterRunHandler) => {
24
+ on('after:run', (results) => {
25
+ const chain = userAfterRunHandlers.reduce(
26
+ (p, h) => p.then(() => h(results)),
27
+ Promise.resolve()
28
+ )
29
+ if (afterRunHandler) {
30
+ return chain.then(() => afterRunHandler(results)).finally(cleanupWrapper)
31
+ }
32
+ return chain.finally(cleanupWrapper)
33
+ })
34
+ }
35
+
36
+ const cypressPlugin = require('./cypress-plugin')
37
+
38
+ if (cypressPlugin._isInit) {
39
+ // Already initialized by manual plugin call — just chain user handlers
40
+ for (const h of userAfterSpecHandlers) on('after:spec', h)
41
+ registerAfterRunWithCleanup()
42
+ payload.registered = true
43
+ return
44
+ }
45
+
46
+ on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin))
47
+
48
+ on('after:spec', (spec, results) => {
49
+ const chain = userAfterSpecHandlers.reduce(
50
+ (p, h) => p.then(() => h(spec, results)),
51
+ Promise.resolve()
52
+ )
53
+ return chain.then(() => cypressPlugin.afterSpec(spec, results))
54
+ })
55
+
56
+ registerAfterRunWithCleanup((results) => cypressPlugin.afterRun(results))
57
+
58
+ on('task', cypressPlugin.getTasks())
59
+
60
+ payload.registered = true
61
+ // cypressPlugin.init expects the proxy tracer (with ._tracer._exporter),
62
+ // not the unwrapped internal tracer that this.tracer returns.
63
+ payload.configPromise = Promise.resolve(cypressPlugin.init(this._tracer, config)).then(() => config)
64
+ })
65
+ }
9
66
  }
10
67
 
11
68
  module.exports = CypressPlugin
@@ -165,6 +165,48 @@ function resolveOriginalSourcePosition (absoluteFilePath, generatedLine) {
165
165
  return null
166
166
  }
167
167
 
168
+ /**
169
+ * Given a generated file's absolute path, returns the first original source file
170
+ * referenced by its source map. This is useful for file-level metadata when the
171
+ * first generated line has no source mapping, such as TypeScript-emitted prologue
172
+ * statements.
173
+ * @param {string} absoluteFilePath - Absolute path to the generated file
174
+ * @returns {string | null}
175
+ */
176
+ function resolveOriginalSourceFile (absoluteFilePath) {
177
+ const sourceMap = getCachedSourceMap(absoluteFilePath)
178
+ if (!sourceMap) return null
179
+ const { mappings, sources, sourceRoot } = sourceMap
180
+ if (!mappings || !sources?.length) return null
181
+
182
+ const mapDir = path.dirname(absoluteFilePath)
183
+ const cursor = { pos: 0 }
184
+ let srcFile = 0
185
+
186
+ const lines = mappings.split(';')
187
+ for (const line of lines) {
188
+ if (!line) continue
189
+ cursor.pos = 0
190
+ while (cursor.pos < line.length) {
191
+ decodeVLQ(line, cursor) // genCol - not needed
192
+ if (cursor.pos < line.length && line[cursor.pos] !== ',') {
193
+ srcFile += decodeVLQ(line, cursor)
194
+ decodeVLQ(line, cursor) // srcLine - not needed
195
+ decodeVLQ(line, cursor) // srcCol - not needed
196
+ if (cursor.pos < line.length && line[cursor.pos] !== ',') {
197
+ decodeVLQ(line, cursor) // namesIndex - not needed
198
+ }
199
+
200
+ const sourcePath = sources[srcFile]
201
+ if (!sourcePath) return null
202
+ return resolveSourcePath(mapDir, sourceRoot, sourcePath)
203
+ }
204
+ if (cursor.pos < line.length && line[cursor.pos] === ',') cursor.pos++
205
+ }
206
+ }
207
+ return null
208
+ }
209
+
168
210
  /**
169
211
  * Convert a template literal body (the text between backticks, with `${…}` interpolations)
170
212
  * into a regex that matches the runtime-evaluated string. Each `${…}` expression is replaced
@@ -294,4 +336,9 @@ function resolveSourceLineForTest (absoluteFilePath, testName, invocationStack)
294
336
  return null
295
337
  }
296
338
 
297
- module.exports = { resolveOriginalSourcePosition, resolveSourceLineForTest, shouldTrustInvocationDetailsLine }
339
+ module.exports = {
340
+ resolveOriginalSourceFile,
341
+ resolveOriginalSourcePosition,
342
+ resolveSourceLineForTest,
343
+ shouldTrustInvocationDetailsLine,
344
+ }
@@ -11,7 +11,7 @@ class FsPlugin extends TracingPlugin {
11
11
  }
12
12
 
13
13
  bindStart (ctx) {
14
- if (!this.activeSpan) return this.skip()
14
+ if (!this.activeSpan) return { noop: true }
15
15
 
16
16
  const { operation, ...params } = ctx
17
17
  const lowerOp = operation.toLowerCase()
@@ -123,7 +123,8 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
123
123
  const topicName = topic?.slice(topic.lastIndexOf('/') + 1) ??
124
124
  subscription.name.slice(subscription.name.lastIndexOf('/') + 1)
125
125
  const baseService = this.tracer._service || 'unknown'
126
- const serviceName = this.config.service || `${baseService}-pubsub`
126
+ const serviceName = this.config.service || { name: `${baseService}-pubsub`, source: baseService }
127
+
127
128
  const meta = {
128
129
  'gcloud.project_id': subscription.pubsub.projectId,
129
130
  'pubsub.topic': topic,
@@ -145,17 +145,12 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
145
145
  const subscriptionName = subscription?.slice(subscription.lastIndexOf('/') + 1) ?? subscription
146
146
  const publishStartTime = attrs['x-dd-publish-start-time']
147
147
  const startTime = publishStartTime ? Number.parseInt(publishStartTime, 10) : undefined
148
-
149
- // Get the base service name and construct the pubsub service override
150
- const baseService = this.tracer._service
151
- const serviceOverride = this.config.service ?? `${baseService}-pubsub`
152
-
153
148
  // Use this.startSpan() which automatically activates the span
154
149
  const span = this.startSpan('pubsub.push.receive', {
155
150
  childOf: parentContext,
156
151
  startTime,
157
152
  kind: 'consumer',
158
- service: serviceOverride,
153
+ service: this.config.service || { name: `${this.tracer._service}-pubsub`, source: this.tracer._service },
159
154
  meta: {
160
155
  component: 'google-cloud-pubsub',
161
156
  'pubsub.method': 'receive',
@@ -163,7 +158,7 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
163
158
  'pubsub.message_id': message.messageId,
164
159
  'pubsub.subscription_type': 'push',
165
160
  'pubsub.topic': topicName,
166
- '_dd.base_service': baseService,
161
+ '_dd.base_service': this.tracer._service,
167
162
  '_dd.serviceoverride.type': 'integration',
168
163
  'resource.name': `Push Subscription ${subscriptionName}`,
169
164
  },
@@ -40,10 +40,10 @@ class HttpClientPlugin extends ClientPlugin {
40
40
  const span = this.startSpan(this.operationName(), {
41
41
  childOf,
42
42
  integrationName: this.constructor.id,
43
+ service: this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
43
44
  meta: {
44
45
  [COMPONENT]: this.constructor.id,
45
46
  'span.kind': 'client',
46
- 'service.name': this.serviceName({ pluginConfig: this.config, sessionDetails: extractSessionDetails(options) }),
47
47
  'resource.name': method,
48
48
  'span.type': 'http',
49
49
  'http.method': method,
@@ -2,9 +2,12 @@
2
2
 
3
3
  const ServerPlugin = require('../../dd-trace/src/plugins/server')
4
4
  const { storage } = require('../../datadog-core')
5
+ const { withRequest } = require('../../dd-trace/src/appsec/store')
5
6
  const web = require('../../dd-trace/src/plugins/util/web')
6
7
  const { incomingHttpRequestStart, incomingHttpRequestEnd } = require('../../dd-trace/src/appsec/channels')
7
- const { COMPONENT } = require('../../dd-trace/src/constants')
8
+ const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
9
+
10
+ const legacyStorage = storage('legacy')
8
11
 
9
12
  class HttpServerPlugin extends ServerPlugin {
10
13
  static id = 'http'
@@ -17,31 +20,36 @@ class HttpServerPlugin extends ServerPlugin {
17
20
  }
18
21
 
19
22
  start ({ req, res, abortController }) {
20
- let store = storage('legacy').getStore()
23
+ let store = legacyStorage.getStore()
24
+ const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
25
+ const service = this.config.service || schemaServiceName
26
+ const serviceSource = (this.config.service && service !== this.tracer._service)
27
+ ? 'opt.plugin'
28
+ : (service === this.tracer._service ? undefined : schemaServiceSource)
21
29
  const span = web.startSpan(
22
30
  this.tracer,
23
31
  {
24
32
  ...this.config,
25
- service: this.config.service || this.serviceName(),
33
+ service,
26
34
  },
27
35
  req,
28
36
  res,
29
37
  this.operationName()
30
38
  )
39
+ if (serviceSource !== undefined) {
40
+ span.setTag(SVC_SRC_KEY, serviceSource)
41
+ }
31
42
  span.setTag(COMPONENT, this.constructor.id)
32
43
  span._integrationName = this.constructor.id
33
44
 
34
45
  const context = web.getContext(req)
46
+ context.parentStore = store
35
47
 
36
- if (context) {
37
- context.parentStore = store
38
- }
39
-
40
- // Only AppSec needs the request scope to be active for any async work that
41
- // may be scheduled after the synchronous `request` event returns (e.g.
42
- // Fastify).
43
- if (incomingHttpRequestStart.hasSubscribers) {
44
- store = { ...store, req, res }
48
+ const appsecActive = incomingHttpRequestStart.hasSubscribers
49
+ if (appsecActive) {
50
+ // AppSec, IAST, and AI Guard need req on the store so downstream
51
+ // subscribers can access them from the async context.
52
+ store = withRequest(store, req)
45
53
  }
46
54
 
47
55
  this.enter(span, store)
@@ -51,7 +59,7 @@ class HttpServerPlugin extends ServerPlugin {
51
59
  context.instrumented = true
52
60
  }
53
61
 
54
- if (incomingHttpRequestStart.hasSubscribers) {
62
+ if (appsecActive) {
55
63
  incomingHttpRequestStart.publish({ req, res, abortController }) // TODO: no need to make a new object here
56
64
  }
57
65
  }
@@ -41,10 +41,10 @@ class Http2ClientPlugin extends ClientPlugin {
41
41
  const span = this.startSpan(this.operationName(), {
42
42
  childOf,
43
43
  integrationName: this.constructor.id,
44
+ service: this.serviceName({ pluginConfig: this.config, sessionDetails }),
44
45
  meta: {
45
46
  [COMPONENT]: this.constructor.id,
46
47
  [SPAN_KIND]: CLIENT,
47
- 'service.name': this.serviceName({ pluginConfig: this.config, sessionDetails }),
48
48
  'resource.name': method,
49
49
  'span.type': 'http',
50
50
  'http.method': method,
@@ -4,7 +4,7 @@
4
4
 
5
5
  const ServerPlugin = require('../../dd-trace/src/plugins/server')
6
6
  const web = require('../../dd-trace/src/plugins/util/web')
7
- const { COMPONENT } = require('../../dd-trace/src/constants')
7
+ const { COMPONENT, SVC_SRC_KEY } = require('../../dd-trace/src/constants')
8
8
 
9
9
  class Http2ServerPlugin extends ServerPlugin {
10
10
  constructor (tracer, config) {
@@ -19,17 +19,25 @@ class Http2ServerPlugin extends ServerPlugin {
19
19
  bindStart (ctx) {
20
20
  const { req, res } = ctx
21
21
 
22
+ const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
23
+ const service = this.config.service || schemaServiceName
24
+ const serviceSource = (this.config.service && service !== this.tracer._service)
25
+ ? 'opt.plugin'
26
+ : (service === this.tracer._service ? undefined : schemaServiceSource)
22
27
  const span = web.startSpan(
23
28
  this.tracer,
24
29
  {
25
30
  ...this.config,
26
- service: this.config.service || this.serviceName(),
31
+ service,
27
32
  },
28
33
  req,
29
34
  res,
30
35
  this.operationName(),
31
36
  ctx
32
37
  )
38
+ if (serviceSource !== undefined) {
39
+ span.setTag(SVC_SRC_KEY, serviceSource)
40
+ }
33
41
 
34
42
  span.setTag(COMPONENT, this.constructor.id)
35
43
  span._integrationName = this.constructor.id
@@ -5,7 +5,7 @@ const realSetTimeout = setTimeout
5
5
 
6
6
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
7
7
  const { storage } = require('../../datadog-core')
8
- const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
8
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
9
9
  const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
10
10
 
11
11
  const {
@@ -166,7 +166,7 @@ class JestPlugin extends CiPlugin {
166
166
 
167
167
  this.telemetry.count(TELEMETRY_TEST_SESSION, {
168
168
  provider: this.ciProviderName,
169
- autoInjected: !!getValueFromEnvSources('DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER'),
169
+ autoInjected: !!this._tracerConfig.DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER,
170
170
  })
171
171
 
172
172
  appClosingTelemetry()