dd-trace 6.2.0 → 6.4.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 (76) hide show
  1. package/README.md +1 -1
  2. package/index.electron.js +3 -0
  3. package/package.json +8 -5
  4. package/packages/datadog-instrumentations/src/ai.js +29 -24
  5. package/packages/datadog-instrumentations/src/cookie.js +7 -1
  6. package/packages/datadog-instrumentations/src/cucumber.js +4 -3
  7. package/packages/datadog-instrumentations/src/express.js +20 -2
  8. package/packages/datadog-instrumentations/src/grpc/server.js +18 -0
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -1
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +30 -54
  11. package/packages/datadog-instrumentations/src/helpers/router-helper.js +8 -8
  12. package/packages/datadog-instrumentations/src/http/server.js +27 -0
  13. package/packages/datadog-instrumentations/src/http2/server.js +143 -17
  14. package/packages/datadog-instrumentations/src/jest.js +10 -4
  15. package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +248 -1
  16. package/packages/datadog-instrumentations/src/playwright.js +40 -3
  17. package/packages/datadog-instrumentations/src/router.js +180 -105
  18. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +41 -16
  19. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +21 -21
  20. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +14 -8
  21. package/packages/datadog-plugin-aws-sdk/src/util.js +0 -22
  22. package/packages/datadog-plugin-azure-functions/src/index.js +2 -1
  23. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -5
  24. package/packages/datadog-plugin-graphql/src/execute.js +90 -14
  25. package/packages/datadog-plugin-graphql/src/parse.js +22 -1
  26. package/packages/datadog-plugin-graphql/src/request.js +33 -2
  27. package/packages/datadog-plugin-graphql/src/utils.js +42 -0
  28. package/packages/datadog-plugin-graphql/src/validate.js +13 -1
  29. package/packages/datadog-plugin-http2/src/server.js +38 -6
  30. package/packages/datadog-plugin-jest/src/util.js +21 -6
  31. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +144 -26
  32. package/packages/datadog-plugin-modelcontextprotocol-sdk/src/utils.js +30 -0
  33. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  34. package/packages/datadog-plugin-vitest/src/index.js +4 -2
  35. package/packages/datadog-shimmer/src/shimmer.js +9 -2
  36. package/packages/dd-trace/index.electron.js +3 -0
  37. package/packages/dd-trace/index.js +2 -37
  38. package/packages/dd-trace/src/appsec/channels.js +1 -0
  39. package/packages/dd-trace/src/appsec/index.js +6 -3
  40. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  41. package/packages/dd-trace/src/bootstrap.js +39 -0
  42. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +23 -16
  43. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +14 -2
  44. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +22 -6
  45. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +5 -0
  46. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +55 -5
  47. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +8 -1
  48. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  49. package/packages/dd-trace/src/config/config-types.d.ts +8 -0
  50. package/packages/dd-trace/src/config/defaults.js +20 -15
  51. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  52. package/packages/dd-trace/src/config/index.js +13 -3
  53. package/packages/dd-trace/src/config/parsers.js +79 -0
  54. package/packages/dd-trace/src/config/supported-configurations.json +17 -0
  55. package/packages/dd-trace/src/constants.js +7 -0
  56. package/packages/dd-trace/src/datastreams/pathway.js +6 -4
  57. package/packages/dd-trace/src/feature-registry.js +22 -0
  58. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  59. package/packages/dd-trace/src/llmobs/index.js +10 -2
  60. package/packages/dd-trace/src/llmobs/tagger.js +19 -1
  61. package/packages/dd-trace/src/noop/proxy.js +5 -4
  62. package/packages/dd-trace/src/openfeature/register.js +46 -0
  63. package/packages/dd-trace/src/opentelemetry/span_processor.js +7 -5
  64. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +32 -16
  65. package/packages/dd-trace/src/opentracing/propagation/log.js +11 -2
  66. package/packages/dd-trace/src/opentracing/propagation/text_map.js +80 -14
  67. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +10 -1
  68. package/packages/dd-trace/src/opentracing/tracer.js +7 -1
  69. package/packages/dd-trace/src/plugins/util/test.js +9 -4
  70. package/packages/dd-trace/src/plugins/util/web.js +12 -0
  71. package/packages/dd-trace/src/proxy.js +21 -9
  72. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +20 -11
  73. package/packages/dd-trace/src/standalone/index.js +0 -22
  74. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  75. package/vendor/dist/protobufjs/index.js +1 -1
  76. package/vendor/dist/protobufjs/minimal/index.js +1 -1
@@ -1,55 +1,173 @@
1
1
  'use strict'
2
2
 
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
3
4
  const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
5
 
5
- class McpToolCallPlugin extends TracingPlugin {
6
- static id = 'modelcontextprotocol_client'
7
- static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_callTool'
6
+ const {
7
+ DISTRIBUTED_TRACE_META_KEY,
8
+ tagErrorResult,
9
+ } = require('./utils')
8
10
 
9
- bindStart (ctx) {
10
- const params = ctx.arguments?.[0]
11
- const toolName = params?.name
11
+ const MCP_REQUEST_SPAN_NAME = 'mcp.request'
12
+ const CLIENT_TOOL_CALL_RESOURCE = 'client_tool_call'
13
+ const CLIENT_LIST_TOOLS_RESOURCE = 'ClientSession.list_tools'
14
+ const SERVER_TOOL_CALL_RESOURCE = 'server_tool_call'
12
15
 
13
- this.startSpan('mcp.client.tool.call', {
14
- resource: toolName,
16
+ function getServerRequestResource (request) {
17
+ if (request?.method === 'tools/call') return SERVER_TOOL_CALL_RESOURCE
18
+ return request?.method
19
+ }
20
+
21
+ class McpPlugin extends TracingPlugin {
22
+ bindStart (ctx) {
23
+ const spanOptions = {
24
+ resource: this.getResource(ctx),
15
25
  type: 'mcp',
16
- kind: 'client',
17
- }, ctx)
26
+ kind: this.constructor.kind,
27
+ }
28
+ const childOf = this.getChildOf(ctx)
29
+ if (childOf) spanOptions.childOf = childOf
18
30
 
31
+ this.startSpan(this.constructor.spanName, spanOptions, ctx)
32
+ const span = ctx.currentStore?.span
33
+ if (span) this.onStart(span, ctx)
19
34
  return ctx.currentStore
20
35
  }
21
36
 
22
37
  asyncEnd (ctx) {
23
- const result = ctx.result
24
- if (result?.isError) {
25
- const span = ctx.currentStore?.span
26
- const errorText = result.content?.find?.(c => c.type === 'text')?.text || 'Tool call returned isError: true'
27
- span?.setTag('error', new Error(errorText))
28
- }
38
+ const span = ctx.currentStore?.span
39
+ if (span) this.onEnd(span, ctx)
29
40
  super.finish(ctx)
30
41
  }
42
+
43
+ getResource () {}
44
+ getChildOf () {}
45
+ onStart () {}
46
+ onEnd () {}
31
47
  }
32
48
 
33
- class McpListToolsPlugin extends TracingPlugin {
49
+ class McpPropagationPlugin extends Plugin {
50
+ static id = 'modelcontextprotocol_propagation'
51
+
52
+ constructor (...args) {
53
+ super(...args)
54
+ this.addSub('apm:mcp:client:request:inject', this.injectTraceContext.bind(this))
55
+ }
56
+
57
+ injectTraceContext (ctx) {
58
+ const span = this.tracer.scope().active()
59
+ if (!span) return
60
+
61
+ const traceContext = {}
62
+ this.tracer.inject(span, 'text_map', traceContext)
63
+ ctx.traceContext = traceContext
64
+ }
65
+ }
66
+
67
+ class McpToolCallPlugin extends McpPlugin {
68
+ static id = 'modelcontextprotocol_client'
69
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_callTool'
70
+ static spanName = MCP_REQUEST_SPAN_NAME
71
+ static kind = 'client'
72
+ getResource () { return CLIENT_TOOL_CALL_RESOURCE }
73
+ onEnd (span, ctx) { tagErrorResult(span, ctx.result) }
74
+ }
75
+
76
+ class McpListToolsPlugin extends McpPlugin {
34
77
  static id = 'modelcontextprotocol_list_tools'
35
78
  static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_listTools'
79
+ static spanName = MCP_REQUEST_SPAN_NAME
80
+ static kind = 'client'
81
+ getResource () { return CLIENT_LIST_TOOLS_RESOURCE }
82
+ }
36
83
 
37
- bindStart (ctx) {
38
- this.startSpan('mcp.tools.list', {
39
- resource: 'tools/list',
40
- type: 'mcp',
41
- kind: 'client',
42
- }, ctx)
84
+ class McpListResourcesPlugin extends McpPlugin {
85
+ static id = 'modelcontextprotocol_list_resources'
86
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_listResources'
87
+ static spanName = 'mcp.resources.list'
88
+ static kind = 'client'
89
+ getResource () { return 'resources/list' }
90
+ }
43
91
 
44
- return ctx.currentStore
92
+ class McpReadResourcePlugin extends McpPlugin {
93
+ static id = 'modelcontextprotocol_read_resource'
94
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_readResource'
95
+ static spanName = 'mcp.resource.read'
96
+ static kind = 'client'
97
+ getResource () { return 'resources/read' }
98
+ }
99
+
100
+ class McpListPromptsPlugin extends McpPlugin {
101
+ static id = 'modelcontextprotocol_list_prompts'
102
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_listPrompts'
103
+ static spanName = 'mcp.prompts.list'
104
+ static kind = 'client'
105
+ getResource () { return 'prompts/list' }
106
+ }
107
+
108
+ class McpGetPromptPlugin extends McpPlugin {
109
+ static id = 'modelcontextprotocol_get_prompt'
110
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:Client_getPrompt'
111
+ static spanName = 'mcp.prompt.get'
112
+ static kind = 'client'
113
+ getResource () { return 'prompts/get' }
114
+ }
115
+
116
+ class McpServerRequestPlugin extends McpPlugin {
117
+ static id = 'modelcontextprotocol_server'
118
+ static prefix = 'tracing:apm:mcp:server:request'
119
+ static spanName = MCP_REQUEST_SPAN_NAME
120
+ static kind = 'server'
121
+ getResource (ctx) { return getServerRequestResource(ctx.request) }
122
+ getChildOf (ctx) {
123
+ const traceContext = ctx.request?.params?._meta?.[DISTRIBUTED_TRACE_META_KEY]
124
+ if (!traceContext || typeof traceContext !== 'object') return
125
+
126
+ const childOf = this.tracer.extract('text_map', traceContext)
127
+ const activeSpan = this.activeSpan
128
+ if (!childOf || activeSpan?.context().toTraceId() === childOf.toTraceId()) return
129
+
130
+ return childOf
45
131
  }
46
132
 
47
- asyncEnd (ctx) {
48
- super.finish(ctx)
133
+ onEnd (span, ctx) {
134
+ if (ctx.error) {
135
+ span.setTag('error', ctx.error)
136
+ } else {
137
+ tagErrorResult(span, ctx.result)
138
+ }
49
139
  }
50
140
  }
51
141
 
142
+ class McpServerToolCallPlugin extends McpPlugin {
143
+ static id = 'modelcontextprotocol_server_tool'
144
+ static prefix = 'tracing:orchestrion:@modelcontextprotocol/sdk:McpServer_executeToolHandler'
145
+ static spanName = 'mcp.server.tool.call'
146
+ static kind = 'internal'
147
+
148
+ /**
149
+ * @param {...unknown} args Plugin constructor arguments.
150
+ */
151
+ constructor (...args) {
152
+ super(...args)
153
+ this.toolNames = new WeakMap()
154
+ this.addSub('apm:mcp:server:tool:registered', ({ tool, name }) => {
155
+ this.toolNames.set(tool, name)
156
+ })
157
+ }
158
+
159
+ getResource (ctx) { return this.toolNames.get(ctx.arguments?.[0]) }
160
+ onEnd (span, ctx) { tagErrorResult(span, ctx.result) }
161
+ }
162
+
52
163
  module.exports = [
164
+ McpPropagationPlugin,
53
165
  McpToolCallPlugin,
54
166
  McpListToolsPlugin,
167
+ McpListResourcesPlugin,
168
+ McpReadResourcePlugin,
169
+ McpListPromptsPlugin,
170
+ McpGetPromptPlugin,
171
+ McpServerRequestPlugin,
172
+ McpServerToolCallPlugin,
55
173
  ]
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ const { ERROR_MESSAGE, ERROR_TYPE } = require('../../dd-trace/src/constants')
4
+
5
+ const DISTRIBUTED_TRACE_META_KEY = '_dd_trace_context'
6
+
7
+ function getFirstTextContent (content) {
8
+ if (!Array.isArray(content)) return
9
+
10
+ for (const item of content) {
11
+ if (item.type === 'text' && item.text) return item.text
12
+ }
13
+ }
14
+
15
+ function setErrorTags (span, message) {
16
+ span.setTag('error', 1)
17
+ span.setTag(ERROR_TYPE, 'Error')
18
+ span.setTag(ERROR_MESSAGE, message)
19
+ }
20
+
21
+ function tagErrorResult (span, result) {
22
+ if (result?.isError) {
23
+ setErrorTags(span, getFirstTextContent(result.content) || 'Tool call returned isError: true')
24
+ }
25
+ }
26
+
27
+ module.exports = {
28
+ DISTRIBUTED_TRACE_META_KEY,
29
+ tagErrorResult,
30
+ }
@@ -68,7 +68,7 @@ class PlaywrightPlugin extends CiPlugin {
68
68
  }) => {
69
69
  const testSuite = getTestSuitePath(filePath, this.repositoryRoot)
70
70
  const isModified = isModifiedTest(testSuite, 0, 0, modifiedFiles, this.constructor.id)
71
- onDone({ isModified })
71
+ onDone(isModified)
72
72
  })
73
73
 
74
74
  this.addSub('ci:playwright:session:finish', ({
@@ -264,8 +264,10 @@ class VitestPlugin extends CiPlugin {
264
264
  span.setTag(TEST_EARLY_FLAKE_ABORT_REASON, earlyFlakeAbortReason)
265
265
  }
266
266
  const finish = () => {
267
- if (duration) {
268
- span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds
267
+ if (Number.isFinite(duration) && duration >= 0) {
268
+ span.finish(
269
+ span._startTime + Math.max(duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION, 0)
270
+ ) // milliseconds
269
271
  } else {
270
272
  span.finish() // `duration` is empty for retries, so we'll use clock time
271
273
  }
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { isModuleNamespaceObject } = require('node:util').types
4
+
3
5
  /**
4
6
  * @type {Set<string | symbol>}
5
7
  */
@@ -168,14 +170,19 @@ function wrap (target, name, wrapper, options) {
168
170
 
169
171
  copyProperties(original, wrapped)
170
172
 
171
- if (descriptor.writable) {
173
+ const immutableModuleNamespace = descriptor.configurable === false &&
174
+ descriptor.writable && isModuleNamespaceObject(target)
175
+
176
+ if (descriptor.writable && !immutableModuleNamespace) {
172
177
  if (descriptor.configurable && descriptor.enumerable) {
173
178
  target[name] = wrapped
174
179
  return target
175
180
  }
176
181
  descriptor.value = wrapped
177
182
  } else {
178
- if (descriptor.set && options?.replaceGetter) {
183
+ if (immutableModuleNamespace) {
184
+ descriptor.value = wrapped
185
+ } else if (descriptor.set && options?.replaceGetter) {
179
186
  // A lazy accessor pair (e.g. Node 20's `fs.opendir`). `original` already
180
187
  // resolved the value through the getter. Keep the property an accessor pair
181
188
  // so the shape stays observationally identical — a downstream consumer may
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ module.exports = require('./src/bootstrap')
@@ -1,39 +1,4 @@
1
1
  'use strict'
2
2
 
3
- if (!global._ddtrace) {
4
- const ddTraceSymbol = Symbol.for('dd-trace')
5
-
6
- // Set up beforeExitHandlers before loading the tracer so that modules loaded
7
- // during require('./src') can register handlers.
8
- Object.defineProperty(globalThis, ddTraceSymbol, {
9
- value: {
10
- beforeExitHandlers: new Set(),
11
- },
12
- enumerable: false,
13
- configurable: true,
14
- writable: false,
15
- })
16
-
17
- process.once('beforeExit', function mainBeforeExit () {
18
- if (globalThis[ddTraceSymbol]?.beforeExitHandlers) {
19
- for (const handler of globalThis[ddTraceSymbol].beforeExitHandlers) {
20
- handler()
21
- }
22
- }
23
- })
24
-
25
- const TracerProxy = require('./src')
26
-
27
- Object.defineProperty(global, '_ddtrace', {
28
- value: new TracerProxy(),
29
- enumerable: false,
30
- configurable: true,
31
- writable: true,
32
- })
33
- }
34
-
35
- module.exports = global._ddtrace
36
- // Static aliases so cjs-module-lexer surfaces them as ESM named exports
37
- // (`import { tracer } from 'dd-trace'`).
38
- module.exports.tracer = global._ddtrace
39
- module.exports.default = global._ddtrace
3
+ require('./src/openfeature/register')
4
+ module.exports = require('./src/bootstrap')
@@ -25,6 +25,7 @@ module.exports = {
25
25
  httpClientRequestStart: dc.channel('apm:http:client:request:start'),
26
26
  httpClientResponseStart: dc.channel('apm:http:client:response:start'),
27
27
  httpClientResponseFinish: dc.channel('apm:http:client:response:finish'),
28
+ informationalResponse: dc.channel('datadog:http:server:informational-response:start'),
28
29
  incomingHttpRequestEnd: dc.channel('dd-trace:incomingHttpRequestEnd'),
29
30
  incomingHttpRequestStart: dc.channel('dd-trace:incomingHttpRequestStart'),
30
31
  lambdaStartInvocation: dc.channel('datadog:lambda:start-invocation'),
@@ -29,6 +29,7 @@ const {
29
29
  responseBody,
30
30
  responseWriteHead,
31
31
  responseSetHeader,
32
+ informationalResponse,
32
33
  routerParam,
33
34
  fastifyResponseChannel,
34
35
  fastifyPathParams,
@@ -98,7 +99,8 @@ function enable (_config) {
98
99
  responseBody.subscribe(onResponseBody)
99
100
  fastifyResponseChannel.subscribe(onResponseBody)
100
101
  responseWriteHead.subscribe(onResponseWriteHead)
101
- responseSetHeader.subscribe(onResponseSetHeader)
102
+ responseSetHeader.subscribe(onResponseOperation)
103
+ informationalResponse.subscribe(onResponseOperation)
102
104
  stripeCheckoutSessionCreate.subscribe(onStripeCheckoutSessionCreate)
103
105
  stripePaymentIntentCreate.subscribe(onStripePaymentIntentCreate)
104
106
  stripeConstructEvent.subscribe(onStripeConstructEvent)
@@ -403,7 +405,7 @@ function onResponseWriteHead ({ req, res, abortController, statusCode, responseH
403
405
  handleResults(results?.actions, req, res, rootSpan, abortController)
404
406
  }
405
407
 
406
- function onResponseSetHeader ({ res, abortController }) {
408
+ function onResponseOperation ({ res, abortController }) {
407
409
  if (isBlocked(res)) {
408
410
  abortController?.abort()
409
411
  }
@@ -547,7 +549,8 @@ function disable () {
547
549
  if (responseBody.hasSubscribers) responseBody.unsubscribe(onResponseBody)
548
550
  if (fastifyResponseChannel.hasSubscribers) fastifyResponseChannel.unsubscribe(onResponseBody)
549
551
  if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
550
- if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
552
+ if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseOperation)
553
+ if (informationalResponse.hasSubscribers) informationalResponse.unsubscribe(onResponseOperation)
551
554
  if (stripeCheckoutSessionCreate.hasSubscribers) stripeCheckoutSessionCreate.unsubscribe(onStripeCheckoutSessionCreate)
552
555
  if (stripePaymentIntentCreate.hasSubscribers) stripePaymentIntentCreate.unsubscribe(onStripePaymentIntentCreate)
553
556
  if (stripeConstructEvent.hasSubscribers) stripeConstructEvent.unsubscribe(onStripeConstructEvent)
@@ -42,7 +42,7 @@ class WAFManager {
42
42
  this.ddwafVersion = this.ddwafVersion || 'unknown'
43
43
  Reporter.reportWafInit(this.ddwafVersion, 'unknown')
44
44
 
45
- log.error('[ASM] AppSec could not load native package. In-app WAF features will not be available.')
45
+ log.error('[ASM] AppSec could not load native package. In-app WAF features will not be available.', err)
46
46
 
47
47
  throw err
48
48
  }
@@ -0,0 +1,39 @@
1
+ 'use strict'
2
+
3
+ if (!global._ddtrace) {
4
+ const ddTraceSymbol = Symbol.for('dd-trace')
5
+
6
+ // Set up beforeExitHandlers before loading the tracer so that modules loaded
7
+ // during require('./src') can register handlers.
8
+ Object.defineProperty(globalThis, ddTraceSymbol, {
9
+ value: {
10
+ beforeExitHandlers: new Set(),
11
+ },
12
+ enumerable: false,
13
+ configurable: true,
14
+ writable: false,
15
+ })
16
+
17
+ process.once('beforeExit', function mainBeforeExit () {
18
+ if (globalThis[ddTraceSymbol]?.beforeExitHandlers) {
19
+ for (const handler of globalThis[ddTraceSymbol].beforeExitHandlers) {
20
+ handler()
21
+ }
22
+ }
23
+ })
24
+
25
+ const TracerProxy = require('.')
26
+
27
+ Object.defineProperty(global, '_ddtrace', {
28
+ value: new TracerProxy(),
29
+ enumerable: false,
30
+ configurable: true,
31
+ writable: true,
32
+ })
33
+ }
34
+
35
+ module.exports = global._ddtrace
36
+ // Static aliases so cjs-module-lexer surfaces them as ESM named exports
37
+ // (`import { tracer } from 'dd-trace'`).
38
+ module.exports.tracer = global._ddtrace
39
+ module.exports.default = global._ddtrace
@@ -51,12 +51,20 @@ const limits = {
51
51
  }
52
52
 
53
53
  /**
54
- * Remove empty collection arrays before sending snapshots through the Test Optimization logs path.
54
+ * @param {object} value - Snapshot object or sub-object.
55
+ * @returns {boolean}
56
+ */
57
+ function isCapturedValue (value) {
58
+ return typeof value.type === 'string'
59
+ }
60
+
61
+ /**
62
+ * Remove empty capture containers before sending snapshots through the Test Optimization logs path.
55
63
  *
56
64
  * @param {object} value - Snapshot object or sub-object.
57
65
  * @returns {void}
58
66
  */
59
- function removeEmptyCollectionProperties (value) {
67
+ function removeEmptyCaptureProperties (value) {
60
68
  const stack = [value]
61
69
 
62
70
  while (stack.length > 0) {
@@ -72,26 +80,25 @@ function removeEmptyCollectionProperties (value) {
72
80
  continue
73
81
  }
74
82
 
75
- if (Array.isArray(current.elements)) {
76
- if (current.elements.length === 0) {
83
+ if (isCapturedValue(current)) {
84
+ if (current.elements === null || (Array.isArray(current.elements) && current.elements.length === 0)) {
77
85
  delete current.elements
78
- } else {
79
- stack.push(current.elements)
80
86
  }
81
- }
82
87
 
83
- if (Array.isArray(current.entries)) {
84
- if (current.entries.length === 0) {
88
+ if (current.entries === null || (Array.isArray(current.entries) && current.entries.length === 0)) {
85
89
  delete current.entries
86
- } else {
87
- stack.push(current.entries)
88
90
  }
89
- }
90
91
 
91
- for (const key of Object.keys(current)) {
92
- if (key === 'elements' || key === 'entries') continue
92
+ if (current.fields === null || (
93
+ current.fields &&
94
+ typeof current.fields === 'object' &&
95
+ Object.keys(current.fields).length === 0
96
+ )) {
97
+ delete current.fields
98
+ }
99
+ }
93
100
 
94
- const child = current[key]
101
+ for (const child of Object.values(current)) {
95
102
  if (child && typeof child === 'object') {
96
103
  stack.push(child)
97
104
  }
@@ -129,7 +136,7 @@ session.on('Debugger.paused', async ({ params: { hitBreakpoints: [hitBreakpoint]
129
136
  language: 'javascript',
130
137
  }
131
138
 
132
- removeEmptyCollectionProperties(snapshot.captures)
139
+ removeEmptyCaptureProperties(snapshot.captures)
133
140
 
134
141
  if (processTags) {
135
142
  snapshot[processTags.DYNAMIC_INSTRUMENTATION_FIELD_NAME] = processTags.tagsObject
@@ -293,6 +293,9 @@ class CiVisibilityExporter extends BufferingExporter {
293
293
  isCoverageReportUploadEnabled,
294
294
  } = remoteConfiguration
295
295
  const { testOptimization } = this._config
296
+ const earlyFlakeDetectionRetryCount =
297
+ testOptimization.DD_TEST_EARLY_FLAKE_DETECTION_RETRY_COUNT
298
+ const hasEarlyFlakeDetectionRetryCount = earlyFlakeDetectionRetryCount !== undefined
296
299
  return {
297
300
  isCodeCoverageEnabled,
298
301
  isSuitesSkippingEnabled,
@@ -300,8 +303,16 @@ class CiVisibilityExporter extends BufferingExporter {
300
303
  requireGit,
301
304
  isEarlyFlakeDetectionEnabled:
302
305
  isEarlyFlakeDetectionEnabled && testOptimization.DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED,
303
- earlyFlakeDetectionNumRetries,
304
- earlyFlakeDetectionSlowTestRetries,
306
+ earlyFlakeDetectionNumRetries:
307
+ hasEarlyFlakeDetectionRetryCount ? earlyFlakeDetectionRetryCount : earlyFlakeDetectionNumRetries,
308
+ earlyFlakeDetectionSlowTestRetries: hasEarlyFlakeDetectionRetryCount
309
+ ? {
310
+ '5s': earlyFlakeDetectionRetryCount,
311
+ '10s': earlyFlakeDetectionRetryCount,
312
+ '30s': earlyFlakeDetectionRetryCount,
313
+ '5m': earlyFlakeDetectionRetryCount,
314
+ }
315
+ : earlyFlakeDetectionSlowTestRetries,
305
316
  earlyFlakeDetectionFaultyThreshold,
306
317
  isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && testOptimization.DD_CIVISIBILITY_FLAKY_RETRY_ENABLED,
307
318
  flakyTestRetriesCount: testOptimization.DD_CIVISIBILITY_FLAKY_RETRY_COUNT,
@@ -318,6 +329,7 @@ class CiVisibilityExporter extends BufferingExporter {
318
329
 
319
330
  sendGitMetadata (repositoryUrl) {
320
331
  if (!this._config.testOptimization.DD_CIVISIBILITY_GIT_UPLOAD_ENABLED) {
332
+ this._resolveGit()
321
333
  return
322
334
  }
323
335
  this._canUseCiVisProtocolPromise.then((canUseCiVisProtocol) => {
@@ -102,14 +102,30 @@ class TestWorkerCiVisibilityExporter {
102
102
  this._logsWriter.append({ testEnvironmentMetadata, logMessage })
103
103
  }
104
104
 
105
- // TODO: add to other writers
105
+ /**
106
+ * @param {() => void} [onDone]
107
+ */
106
108
  flush (onDone) {
107
- this._writer.flush(onDone)
108
- this._coverageWriter.flush()
109
- this._logsWriter.flush()
110
- if (this._telemetryWriter) {
111
- this._telemetryWriter.flush()
109
+ if (!onDone) {
110
+ this._writer.flush()
111
+ this._coverageWriter.flush()
112
+ this._logsWriter.flush()
113
+ if (this._telemetryWriter) {
114
+ this._telemetryWriter.flush()
115
+ }
116
+ return
117
+ }
118
+
119
+ let pendingWriters = this._telemetryWriter ? 4 : 3
120
+ const onWriterFlushed = () => {
121
+ pendingWriters--
122
+ if (pendingWriters === 0) onDone()
112
123
  }
124
+
125
+ this._writer.flush(onWriterFlushed)
126
+ this._coverageWriter.flush(onWriterFlushed)
127
+ this._logsWriter.flush(onWriterFlushed)
128
+ this._telemetryWriter?.flush(onWriterFlushed)
113
129
  }
114
130
  }
115
131
 
@@ -15,6 +15,9 @@ class Writer {
15
15
  this._interprocessCode = interprocessCode
16
16
  }
17
17
 
18
+ /**
19
+ * @param {() => void} [onDone]
20
+ */
18
21
  flush (onDone) {
19
22
  const count = this._encoder.count()
20
23
 
@@ -22,6 +25,8 @@ class Writer {
22
25
  const payload = this._encoder.makePayload()
23
26
 
24
27
  this._sendPayload(payload, onDone)
28
+ } else {
29
+ onDone?.()
25
30
  }
26
31
  }
27
32