dd-trace 5.98.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 (123) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ext/tags.js +1 -0
  3. package/index.d.ts +9 -1
  4. package/package.json +48 -46
  5. package/packages/datadog-instrumentations/src/crypto.js +45 -0
  6. package/packages/datadog-instrumentations/src/cypress-config.js +122 -16
  7. package/packages/datadog-instrumentations/src/dns.js +24 -56
  8. package/packages/datadog-instrumentations/src/graphql.js +1 -1
  9. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +74 -0
  10. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +4 -1
  11. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +10 -3
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +59 -0
  15. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +11 -2
  16. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +7 -0
  17. package/packages/datadog-instrumentations/src/pino.js +4 -28
  18. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +27 -0
  19. package/packages/datadog-instrumentations/src/playwright.js +5 -17
  20. package/packages/datadog-instrumentations/src/stripe.js +38 -24
  21. package/packages/datadog-instrumentations/src/vitest.js +32 -4
  22. package/packages/datadog-instrumentations/src/zlib.js +29 -0
  23. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -2
  24. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +8 -15
  25. package/packages/datadog-plugin-azure-service-bus/src/producer.js +4 -9
  26. package/packages/datadog-plugin-cucumber/src/index.js +2 -2
  27. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -5
  28. package/packages/datadog-plugin-cypress/src/source-map-utils.js +48 -1
  29. package/packages/datadog-plugin-http/src/server.js +11 -11
  30. package/packages/datadog-plugin-jest/src/index.js +2 -2
  31. package/packages/datadog-plugin-mocha/src/index.js +1 -2
  32. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/index.js +24 -0
  33. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +55 -0
  34. package/packages/datadog-plugin-mongodb-core/src/index.js +1 -6
  35. package/packages/datadog-plugin-playwright/src/index.js +2 -3
  36. package/packages/datadog-plugin-vitest/src/index.js +14 -6
  37. package/packages/datadog-plugin-ws/src/close.js +2 -0
  38. package/packages/datadog-plugin-ws/src/producer.js +2 -0
  39. package/packages/datadog-plugin-ws/src/receiver.js +1 -0
  40. package/packages/dd-trace/src/aiguard/channels.js +8 -0
  41. package/packages/dd-trace/src/aiguard/index.js +7 -3
  42. package/packages/dd-trace/src/aiguard/sdk.js +44 -0
  43. package/packages/dd-trace/src/aiguard/tags.js +1 -0
  44. package/packages/dd-trace/src/appsec/graphql.js +6 -6
  45. package/packages/dd-trace/src/appsec/index.js +9 -11
  46. package/packages/dd-trace/src/appsec/rasp/command_injection.js +4 -5
  47. package/packages/dd-trace/src/appsec/rasp/lfi.js +8 -4
  48. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +5 -10
  49. package/packages/dd-trace/src/appsec/rasp/ssrf.js +5 -6
  50. package/packages/dd-trace/src/appsec/recommended.json +2438 -13
  51. package/packages/dd-trace/src/appsec/reporter.js +6 -5
  52. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +4 -8
  53. package/packages/dd-trace/src/appsec/store.js +50 -0
  54. package/packages/dd-trace/src/appsec/waf/index.js +3 -5
  55. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
  56. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
  57. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
  58. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
  59. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -4
  60. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
  61. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -5
  62. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +3 -4
  63. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +6 -6
  64. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -2
  65. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
  66. package/packages/dd-trace/src/config/config-types.d.ts +0 -4
  67. package/packages/dd-trace/src/config/defaults.js +10 -10
  68. package/packages/dd-trace/src/config/generated-config-types.d.ts +13 -12
  69. package/packages/dd-trace/src/config/index.js +25 -35
  70. package/packages/dd-trace/src/config/parsers.js +26 -9
  71. package/packages/dd-trace/src/config/supported-configurations.json +32 -36
  72. package/packages/dd-trace/src/debugger/config.js +2 -0
  73. package/packages/dd-trace/src/debugger/devtools_client/send.js +25 -5
  74. package/packages/dd-trace/src/encode/0.4.js +4 -5
  75. package/packages/dd-trace/src/exporters/agent/index.js +0 -1
  76. package/packages/dd-trace/src/exporters/agent/writer.js +1 -2
  77. package/packages/dd-trace/src/exporters/agentless/writer.js +3 -3
  78. package/packages/dd-trace/src/exporters/common/util.js +2 -2
  79. package/packages/dd-trace/src/id.js +2 -0
  80. package/packages/dd-trace/src/index.js +2 -5
  81. package/packages/dd-trace/src/lambda/handler.js +1 -3
  82. package/packages/dd-trace/src/llmobs/plugins/{anthropic.js → anthropic/index.js} +5 -63
  83. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +106 -0
  84. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +3 -2
  85. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +3 -2
  86. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +2 -1
  87. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +0 -49
  88. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +2 -1
  89. package/packages/dd-trace/src/llmobs/plugins/langchain/messages.js +76 -0
  90. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -26
  91. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/index.js +68 -0
  92. package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +57 -0
  93. package/packages/dd-trace/src/llmobs/sdk.js +2 -2
  94. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +103 -0
  95. package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -0
  96. package/packages/dd-trace/src/opentelemetry/logs/index.js +1 -1
  97. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +3 -2
  98. package/packages/dd-trace/src/opentelemetry/metrics/index.js +1 -1
  99. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +3 -2
  100. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +19 -66
  101. package/packages/dd-trace/src/opentelemetry/trace/index.js +11 -16
  102. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +11 -3
  103. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +51 -41
  104. package/packages/dd-trace/src/opentelemetry/tracer.js +9 -11
  105. package/packages/dd-trace/src/opentracing/propagation/text_map.js +17 -10
  106. package/packages/dd-trace/src/opentracing/span.js +1 -1
  107. package/packages/dd-trace/src/opentracing/tracer.js +12 -5
  108. package/packages/dd-trace/src/plugins/index.js +1 -0
  109. package/packages/dd-trace/src/plugins/util/test.js +126 -5
  110. package/packages/dd-trace/src/plugins/util/url.js +2 -1
  111. package/packages/dd-trace/src/profiling/profilers/event_plugins/crypto.js +32 -0
  112. package/packages/dd-trace/src/profiling/profilers/event_plugins/zlib.js +19 -0
  113. package/packages/dd-trace/src/profiling/profilers/events.js +35 -0
  114. package/packages/dd-trace/src/proxy.js +2 -8
  115. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
  116. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  117. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  118. package/packages/dd-trace/src/span_processor.js +1 -2
  119. package/packages/dd-trace/src/tagger.js +2 -2
  120. package/packages/dd-trace/src/telemetry/send-data.js +5 -7
  121. package/packages/dd-trace/src/tracer.js +2 -2
  122. package/vendor/dist/ignore/LICENSE +0 -21
  123. package/vendor/dist/ignore/index.js +0 -1
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../../datadog-shimmer')
4
+ const { channel } = require('./instrument')
5
+
6
+ /**
7
+ * Create a shimmer-compatible instrumentor for callback-style APIs whose work is offloaded to the
8
+ * libuv worker thread pool (e.g. zlib.gzip, crypto.pbkdf2, dns.lookup). Builds a set of three
9
+ * diagnostic channels at the given prefix (`<prefix>:start`, `:finish`, `:error`) and returns a
10
+ * factory that produces shimmer wrappers driven by a caller-supplied `buildContext` function.
11
+ *
12
+ * The returned wrapper:
13
+ * - calls through unmodified when there are no subscribers or the last argument is not a callback;
14
+ * - invokes `buildContext(thisArg, args)` to construct the context object; a return of `undefined`
15
+ * also causes a bypass, letting callers enforce additional guards (e.g. minimum argument count);
16
+ * - publishes `:start` via `runStores`, wraps the callback to publish `:error` (on truthy error),
17
+ * optionally set `ctx.result` to the callback's first non-error argument, and publish `:finish`
18
+ * via `runStores`; publishes `:error` if the original call throws synchronously.
19
+ *
20
+ * @param {string} prefix
21
+ * @param {object} [options]
22
+ * @param {boolean} [options.captureResult=false] set `ctx.result` to the callback's first
23
+ * non-error argument before publishing `:finish`. Plugins that tag spans from the call's
24
+ * return value (e.g. the DNS lookup plugin) rely on this.
25
+ * @returns {(buildContext: (thisArg: unknown, args: IArguments) => object | undefined) =>
26
+ * (fn: Function) => Function}
27
+ */
28
+ function createCallbackInstrumentor (prefix, { captureResult = false } = {}) {
29
+ const startCh = channel(prefix + ':start')
30
+ const finishCh = channel(prefix + ':finish')
31
+ const errorCh = channel(prefix + ':error')
32
+
33
+ return function instrument (buildContext) {
34
+ return function wrap (fn) {
35
+ return function () {
36
+ const lastIndex = arguments.length - 1
37
+ const cb = arguments[lastIndex]
38
+ if (!startCh.hasSubscribers || typeof cb !== 'function') {
39
+ return fn.apply(this, arguments)
40
+ }
41
+
42
+ const ctx = buildContext(this, arguments)
43
+ if (ctx === undefined) {
44
+ return fn.apply(this, arguments)
45
+ }
46
+
47
+ return startCh.runStores(ctx, () => {
48
+ arguments[lastIndex] = shimmer.wrapFunction(cb, cb => function (error, ...rest) {
49
+ if (error) {
50
+ ctx.error = error
51
+ errorCh.publish(ctx)
52
+ }
53
+ if (captureResult) {
54
+ ctx.result = rest[0]
55
+ }
56
+ return finishCh.runStores(ctx, cb, this, error, ...rest)
57
+ })
58
+
59
+ try {
60
+ return fn.apply(this, arguments)
61
+ } catch (error) {
62
+ void error.stack // trigger getting the stack at the original throwing point
63
+ ctx.error = error
64
+ errorCh.publish(ctx)
65
+
66
+ throw error
67
+ }
68
+ })
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ module.exports = { createCallbackInstrumentor }
@@ -97,7 +97,10 @@ module.exports.checkForPotentialConflicts = function () {
97
97
  }
98
98
 
99
99
  module.exports.flushStartupLogs = function (log) {
100
+ // Some callers pass `./log/writer` (simple pass-through) while others pass the main `./log`
101
+ // module (which supports lazy delegate functions). Invoke closures here so both work.
100
102
  while (warnings.length) {
101
- log.warn(warnings.shift())
103
+ const entry = warnings.shift()
104
+ log.warn(typeof entry === 'function' ? entry() : entry)
102
105
  }
103
106
  }
@@ -12,11 +12,13 @@ module.exports = {
12
12
  net: () => require('../net'),
13
13
  url: () => require('../url'),
14
14
  vm: () => require('../vm'),
15
+ zlib: () => require('../zlib'),
15
16
  // Non Node.js modules
16
17
  '@anthropic-ai/sdk': { esmFirst: true, fn: () => require('../anthropic') },
17
18
  '@apollo/server': () => require('../apollo-server'),
18
19
  '@apollo/gateway': () => require('../apollo'),
19
20
  '@langchain/langgraph': { esmFirst: true, fn: () => require('../langgraph') },
21
+ '@modelcontextprotocol/sdk': () => require('../modelcontextprotocol-sdk'),
20
22
  'apollo-server-core': () => require('../apollo-server-core'),
21
23
  '@aws-sdk/smithy-client': () => require('../aws-sdk'),
22
24
  '@azure/event-hubs': () => require('../azure-event-hubs'),
@@ -1,5 +1,13 @@
1
1
  'use strict'
2
2
 
3
+ /**
4
+ * This file is meant to be only thin wrappers over core
5
+ * parsing/traversing/generating functionality with the goal to eventually move
6
+ * them out of the project. No other code should be added to this file such as
7
+ * helpers etc, and the API should be kept exactly as an external API would be
8
+ * expected to be.
9
+ */
10
+
3
11
  const log = require('../../../../dd-trace/src/log')
4
12
 
5
13
  // eslint-disable-next-line camelcase, no-undef
@@ -24,14 +32,13 @@ const compiler = {
24
32
  } catch (e) {
25
33
  log.error(e)
26
34
 
27
- // Fallback for when OXC is not available.
28
35
  const meriyah = require('../../../../../vendor/dist/meriyah')
29
36
 
30
- compiler.parse = (sourceText, { range, sourceType } = {}) => {
37
+ compiler.parse = (sourceText, { range, isModule } = {}) => {
31
38
  return meriyah.parse(sourceText.toString(), {
32
39
  loc: range,
33
40
  ranges: range,
34
- module: sourceType === 'module',
41
+ module: isModule,
35
42
  })
36
43
  }
37
44
  }
@@ -5,4 +5,5 @@ module.exports = [
5
5
  ...require('./bullmq'),
6
6
  ...require('./langchain'),
7
7
  ...require('./langgraph'),
8
+ ...require('./modelcontextprotocol-sdk'),
8
9
  ]
@@ -0,0 +1,59 @@
1
+ 'use strict'
2
+
3
+ // NOTE: Protocol.request (dist/esm|cjs/shared/protocol.js) is intentionally not instrumented here.
4
+ // It will be used for distributed tracing header injection when server-side coverage is added.
5
+
6
+ module.exports = [
7
+ {
8
+ module: {
9
+ name: '@modelcontextprotocol/sdk',
10
+ versionRange: '>=1.27.1',
11
+ filePath: 'dist/esm/client/index.js',
12
+ },
13
+ functionQuery: {
14
+ methodName: 'callTool',
15
+ className: 'Client',
16
+ kind: 'Async',
17
+ },
18
+ channelName: 'Client_callTool',
19
+ },
20
+ {
21
+ module: {
22
+ name: '@modelcontextprotocol/sdk',
23
+ versionRange: '>=1.27.1',
24
+ filePath: 'dist/cjs/client/index.js',
25
+ },
26
+ functionQuery: {
27
+ methodName: 'callTool',
28
+ className: 'Client',
29
+ kind: 'Async',
30
+ },
31
+ channelName: 'Client_callTool',
32
+ },
33
+ {
34
+ module: {
35
+ name: '@modelcontextprotocol/sdk',
36
+ versionRange: '>=1.27.1',
37
+ filePath: 'dist/esm/client/index.js',
38
+ },
39
+ functionQuery: {
40
+ methodName: 'listTools',
41
+ className: 'Client',
42
+ kind: 'Async',
43
+ },
44
+ channelName: 'Client_listTools',
45
+ },
46
+ {
47
+ module: {
48
+ name: '@modelcontextprotocol/sdk',
49
+ versionRange: '>=1.27.1',
50
+ filePath: 'dist/cjs/client/index.js',
51
+ },
52
+ functionQuery: {
53
+ methodName: 'listTools',
54
+ className: 'Client',
55
+ kind: 'Async',
56
+ },
57
+ channelName: 'Client_listTools',
58
+ },
59
+ ]
@@ -14,11 +14,13 @@ const transforms = module.exports = {
14
14
  if (node.body.some(tracingChannelPredicate)) return
15
15
 
16
16
  const index = node.body.findIndex(child => child.directive === 'use strict')
17
- const code = sourceType === 'module'
17
+ const code = isModuleSourceType(sourceType)
18
18
  ? `import { tracingChannel as tr_ch_apm_tracingChannel } from "${dcModule}"`
19
19
  : `const {tracingChannel: tr_ch_apm_tracingChannel} = require("${dcModule}")`
20
20
 
21
- node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
21
+ node.body.splice(index + 1, 0, parse(code, {
22
+ isModule: isModuleSourceType(sourceType),
23
+ }).body[0])
22
24
  },
23
25
 
24
26
  tracingChannelDeclaration (state, node) {
@@ -51,6 +53,13 @@ function traceAny (state, node, _parent, ancestry) {
51
53
  }
52
54
  }
53
55
 
56
+ /**
57
+ * @param {string} sourceType
58
+ */
59
+ function isModuleSourceType (sourceType) {
60
+ return sourceType === 'module' || sourceType === 'esm'
61
+ }
62
+
54
63
  function traceFunction (state, node, program) {
55
64
  transforms.tracingChannelDeclaration(state, program)
56
65
 
@@ -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
@@ -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
+ })
@@ -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) {
@@ -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