dd-trace 6.10.0 → 6.12.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 (173) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/README.md +0 -17
  3. package/index.d.ts +128 -6
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +27 -23
  7. package/packages/datadog-esbuild/index.js +0 -34
  8. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  10. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  11. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  12. package/packages/datadog-instrumentations/src/cucumber.js +30 -15
  13. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  14. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  15. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  16. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  17. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  18. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  19. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
  20. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  21. package/packages/datadog-instrumentations/src/jest.js +25 -17
  22. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  23. package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +135 -19
  25. package/packages/datadog-instrumentations/src/playwright-reporter.js +247 -0
  26. package/packages/datadog-instrumentations/src/playwright.js +182 -35
  27. package/packages/datadog-instrumentations/src/router.js +2 -0
  28. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +6 -8
  29. package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  32. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
  33. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  34. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  35. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  36. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  37. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  38. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  39. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
  40. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  41. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  42. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  43. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  44. package/packages/datadog-plugin-electron/src/index.js +2 -0
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-jest/src/index.js +6 -0
  47. package/packages/datadog-plugin-mocha/src/index.js +56 -1
  48. package/packages/datadog-plugin-nats/src/index.js +1 -1
  49. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  50. package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
  51. package/packages/datadog-plugin-playwright/src/index.js +46 -13
  52. package/packages/datadog-plugin-vitest/src/index.js +6 -0
  53. package/packages/datadog-webpack/index.js +0 -11
  54. package/packages/dd-trace/index.js +0 -1
  55. package/packages/dd-trace/src/agent/info.js +6 -5
  56. package/packages/dd-trace/src/aiguard/client.js +100 -1
  57. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  58. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  59. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  60. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  61. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  62. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  63. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  64. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  65. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  66. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  67. package/packages/dd-trace/src/appsec/reporter.js +2 -0
  68. package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
  69. package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
  70. package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
  71. package/packages/dd-trace/src/carrier.js +107 -48
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  73. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
  74. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +29 -3
  75. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +27 -9
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  77. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  78. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +26 -8
  79. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  80. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
  81. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  82. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  83. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  84. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -4
  85. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  86. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  87. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  88. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
  89. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +10 -4
  90. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
  91. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  92. package/packages/dd-trace/src/config/index.js +13 -2
  93. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  94. package/packages/dd-trace/src/config/supported-configurations.json +14 -0
  95. package/packages/dd-trace/src/datastreams/index.js +2 -0
  96. package/packages/dd-trace/src/datastreams/manager.js +9 -2
  97. package/packages/dd-trace/src/datastreams/processor.js +15 -13
  98. package/packages/dd-trace/src/datastreams/size.js +5 -0
  99. package/packages/dd-trace/src/debugger/constants.js +1 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  101. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -11
  102. package/packages/dd-trace/src/debugger/index.js +4 -2
  103. package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
  104. package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
  105. package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
  106. package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
  107. package/packages/dd-trace/src/evp_proxy/path.js +22 -0
  108. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  109. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  110. package/packages/dd-trace/src/exporters/common/request.js +5 -2
  111. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  112. package/packages/dd-trace/src/llmobs/experiments/client.js +167 -5
  113. package/packages/dd-trace/src/llmobs/experiments/dataset.js +484 -92
  114. package/packages/dd-trace/src/llmobs/experiments/experiment.js +302 -29
  115. package/packages/dd-trace/src/llmobs/experiments/index.js +75 -64
  116. package/packages/dd-trace/src/llmobs/experiments/noop.js +135 -7
  117. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  118. package/packages/dd-trace/src/llmobs/experiments/util.js +101 -9
  119. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  120. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  121. package/packages/dd-trace/src/noop/proxy.js +3 -4
  122. package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
  123. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  124. package/packages/dd-trace/src/openfeature/index.js +7 -4
  125. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  126. package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
  127. package/packages/dd-trace/src/openfeature/writers/exposures.js +62 -14
  128. package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
  129. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  130. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  131. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  132. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  133. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  134. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  135. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  136. package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -13
  137. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  138. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  139. package/packages/dd-trace/src/plugin_manager.js +3 -3
  140. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  141. package/packages/dd-trace/src/plugins/index.js +1 -0
  142. package/packages/dd-trace/src/plugins/util/jest.js +4 -3
  143. package/packages/dd-trace/src/plugins/util/test.js +9 -9
  144. package/packages/dd-trace/src/plugins/util/web.js +3 -0
  145. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  146. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  148. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  149. package/packages/dd-trace/src/proxy.js +38 -29
  150. package/packages/dd-trace/src/ritm.js +9 -2
  151. package/packages/dd-trace/src/serverless.js +37 -0
  152. package/packages/dd-trace/src/span_format.js +1 -0
  153. package/packages/dd-trace/src/span_stats.js +30 -3
  154. package/packages/dd-trace/src/storage-channels.js +86 -0
  155. package/packages/dd-trace/src/telemetry/metrics.js +62 -8
  156. package/packages/dd-trace/src/telemetry/send-data.js +1 -0
  157. package/packages/dd-trace/src/tracer.js +9 -2
  158. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  159. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  160. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  161. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  162. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  163. package/vendor/dist/https-proxy-agent/LICENSE +22 -0
  164. package/vendor/dist/https-proxy-agent/index.js +4 -0
  165. package/vendor/dist/protobufjs/index.js +1 -1
  166. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  167. package/vendor/dist/proxy-from-env/LICENSE +20 -0
  168. package/vendor/dist/proxy-from-env/index.js +1 -0
  169. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  170. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  171. package/packages/dd-trace/src/feature-registry.js +0 -29
  172. package/packages/dd-trace/src/openfeature/register.js +0 -35
  173. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -0,0 +1,201 @@
1
+ 'use strict'
2
+
3
+ const { Readable } = require('node:stream')
4
+
5
+ const commonRequest = require('../../exporters/common/request')
6
+ const {
7
+ RATE_LIMIT_MAX_WAIT_MS,
8
+ getMaxAttempts,
9
+ getRetryDelay,
10
+ isRetriableNetworkError,
11
+ } = require('../../exporters/common/retry')
12
+ const { getRateLimitResetDelay } = require('../requests/rate-limit')
13
+
14
+ /**
15
+ * @param {AbortSignal} signal
16
+ * @returns {Error}
17
+ */
18
+ function getAbortError (signal) {
19
+ return signal.reason || Object.assign(new Error('Request aborted'), { code: 'ABORT_ERR' })
20
+ }
21
+
22
+ /**
23
+ * Buffers a stream once so a Test Optimization exporter request can safely retry it.
24
+ *
25
+ * @param {Readable} data
26
+ * @param {AbortSignal} [signal]
27
+ * @param {(error: Error|null, data?: Buffer) => void} callback
28
+ * @returns {void}
29
+ */
30
+ function bufferReadable (data, signal, callback) {
31
+ const chunks = []
32
+ let settled = false
33
+
34
+ const cleanup = (keepErrorListener = false) => {
35
+ data.removeListener('data', onData)
36
+ data.removeListener('end', onEnd)
37
+ if (!keepErrorListener) data.removeListener('error', onError)
38
+ signal?.removeEventListener('abort', onAbort)
39
+ }
40
+ const onData = chunk => chunks.push(chunk)
41
+ const onEnd = () => {
42
+ if (settled) return
43
+ settled = true
44
+ cleanup()
45
+ callback(null, Buffer.concat(chunks))
46
+ }
47
+ const onError = error => {
48
+ if (settled) return
49
+ settled = true
50
+ cleanup()
51
+ callback(error)
52
+ }
53
+ const onAbort = () => {
54
+ if (settled) return
55
+ settled = true
56
+ cleanup(true)
57
+ data.once('close', () => data.removeListener('error', onError))
58
+ data.destroy()
59
+ callback(getAbortError(signal))
60
+ }
61
+
62
+ data.on('data', onData)
63
+ data.once('end', onEnd)
64
+ data.once('error', onError)
65
+ signal?.addEventListener('abort', onAbort, { once: true })
66
+ if (signal?.aborted) onAbort()
67
+ }
68
+
69
+ /**
70
+ * Sends Test Optimization exporter data through the common single-attempt transport while
71
+ * keeping retry and finalization policy scoped to Test Optimization.
72
+ *
73
+ * @param {Buffer|string|Readable|Array<Buffer|string>} data
74
+ * @param {object} options
75
+ * @param {(error: Error|null, result?: string|null, statusCode?: number,
76
+ * headers?: import('node:http').IncomingHttpHeaders) => void} callback
77
+ * @returns {void}
78
+ */
79
+ function request (data, options, callback) {
80
+ const { signal } = options
81
+ if (signal?.aborted) return callback(getAbortError(signal))
82
+
83
+ if (data instanceof Readable) {
84
+ bufferReadable(data, signal, (error, bufferedData) => {
85
+ if (error) return callback(error)
86
+ requestBuffered(bufferedData, options, callback)
87
+ })
88
+ return
89
+ }
90
+
91
+ requestBuffered(data, options, callback)
92
+ }
93
+
94
+ /**
95
+ * Applies the Test Optimization retry policy to replayable request data.
96
+ *
97
+ * @param {Buffer|string|Array<Buffer|string>} data
98
+ * @param {object} options
99
+ * @param {(error: Error|null, result?: string|null, statusCode?: number,
100
+ * headers?: import('node:http').IncomingHttpHeaders) => void} callback
101
+ * @returns {void}
102
+ */
103
+ function requestBuffered (data, options, callback) {
104
+ const { signal } = options
105
+ const timeout = options.timeout || 2000
106
+ let retryTimer
107
+ let settled = false
108
+
109
+ const complete = (error, result, statusCode, headers) => {
110
+ if (settled) return
111
+ settled = true
112
+ clearTimeout(retryTimer)
113
+ signal?.removeEventListener('abort', onAbort)
114
+ callback(error, result, statusCode, headers)
115
+ }
116
+ const onAbort = () => complete(getAbortError(signal))
117
+
118
+ signal?.addEventListener('abort', onAbort, { once: true })
119
+ if (signal?.aborted) return onAbort()
120
+
121
+ /** @param {number} attemptIndex */
122
+ const attempt = attemptIndex => {
123
+ if (settled) return
124
+ if (signal?.aborted) return onAbort()
125
+
126
+ const deadline = options.deadline
127
+ const remaining = deadline === undefined ? Infinity : deadline - Date.now()
128
+ if (remaining <= 0) {
129
+ const error = new Error('Test Optimization request reached its finalization deadline')
130
+ error.code = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
131
+ complete(error)
132
+ return
133
+ }
134
+
135
+ if (deadline !== undefined && !commonRequest.writable) {
136
+ retryTimer = setTimeout(attempt, Math.min(50, remaining), attemptIndex)
137
+ retryTimer.unref?.()
138
+ return
139
+ }
140
+
141
+ const attemptOptions = {
142
+ ...options,
143
+ headers: options.headers ? { ...options.headers } : undefined,
144
+ retry: false,
145
+ }
146
+ if (deadline !== undefined) attemptOptions.timeout = Math.max(1, Math.min(timeout, remaining))
147
+
148
+ commonRequest(data, attemptOptions, (error, result, statusCode, headers) => {
149
+ if (settled) return
150
+ if (!error) {
151
+ complete(null, result, statusCode, headers)
152
+ return
153
+ }
154
+
155
+ const responseStatus = statusCode ?? error.status
156
+ const isRetriableHttpError = options.deadline !== undefined &&
157
+ (responseStatus === 429 || responseStatus >= 500)
158
+ if (options.retry === false || (attemptIndex >= getMaxAttempts(attemptOptions) &&
159
+ (isRetriableNetworkError(error) || isRetriableHttpError))) {
160
+ complete(error, result, statusCode, headers)
161
+ return
162
+ }
163
+
164
+ if (!isRetriableNetworkError(error) && !isRetriableHttpError) {
165
+ complete(error, result, statusCode, headers)
166
+ return
167
+ }
168
+
169
+ let retryDelay
170
+ if (responseStatus === 429) {
171
+ const resetDelay = getRateLimitResetDelay(headers)
172
+ if (Number.isFinite(resetDelay)) {
173
+ const retryRemaining = options.deadline - Date.now()
174
+ if (resetDelay > RATE_LIMIT_MAX_WAIT_MS || resetDelay >= retryRemaining) {
175
+ complete(error, result, statusCode, headers)
176
+ return
177
+ }
178
+ retryDelay = resetDelay
179
+ }
180
+ }
181
+
182
+ let delay = retryDelay ?? getRetryDelay(attemptOptions, attemptIndex)
183
+ if (options.deadline !== undefined && retryDelay === undefined) {
184
+ const retryRemaining = options.deadline - Date.now()
185
+ if (retryRemaining <= 0) {
186
+ complete(error, result, statusCode, headers)
187
+ return
188
+ }
189
+ const retryAttemptTimeout = timeout < retryRemaining ? timeout : Math.ceil(retryRemaining / 2)
190
+ delay = Math.min(delay, Math.max(0, retryRemaining - retryAttemptTimeout))
191
+ }
192
+
193
+ retryTimer = setTimeout(attempt, delay, attemptIndex + 1)
194
+ retryTimer.unref?.()
195
+ })
196
+ }
197
+
198
+ attempt(1)
199
+ }
200
+
201
+ module.exports = request
@@ -127,7 +127,7 @@ class TestWorkerCiVisibilityExporter {
127
127
  }
128
128
 
129
129
  /**
130
- * @param {() => void} [onDone]
130
+ * @param {(error?: Error) => void} [onDone]
131
131
  */
132
132
  flush (onDone) {
133
133
  if (!onDone) {
@@ -141,9 +141,11 @@ class TestWorkerCiVisibilityExporter {
141
141
  }
142
142
 
143
143
  let pendingWriters = this._telemetryWriter ? 4 : 3
144
- const onWriterFlushed = () => {
144
+ let flushError
145
+ const onWriterFlushed = (error) => {
146
+ flushError ||= error
145
147
  pendingWriters--
146
- if (pendingWriters === 0) onDone()
148
+ if (pendingWriters === 0) onDone(flushError)
147
149
  }
148
150
 
149
151
  this._writer.flush(onWriterFlushed)
@@ -21,7 +21,7 @@ class Writer {
21
21
  }
22
22
 
23
23
  /**
24
- * @param {() => void} [onDone]
24
+ * @param {(error?: Error) => void} [onDone]
25
25
  */
26
26
  flush (onDone) {
27
27
  const count = this._encoder.count()
@@ -69,8 +69,9 @@ class Writer {
69
69
 
70
70
  // child_process workers (jest default, cucumber)
71
71
  if (process.send) {
72
- process.send(payload, () => {
73
- onDone()
72
+ process.send(payload, (error) => {
73
+ if (error) log.error('Error sending message to parent process', error)
74
+ onDone(error)
74
75
  })
75
76
  return
76
77
  }
@@ -82,9 +83,12 @@ class Writer {
82
83
  parentPort.postMessage(payload)
83
84
  } catch (error) {
84
85
  log.error('Error posting message to parent port', error)
85
- } finally {
86
- onDone()
86
+ onDone(error)
87
+ return
87
88
  }
89
+ // postMessage has no acknowledgement callback. Completion means the
90
+ // message was accepted by the local port, not processed by the parent.
91
+ onDone()
88
92
  return
89
93
  }
90
94
 
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const getConfig = require('../../config')
4
- const request = require('../requests/request')
4
+ const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../evp_proxy/constants')
5
+ const { joinEVPProxyPath } = require('../../evp_proxy/path')
5
6
  const log = require('../../log')
6
7
  const {
7
8
  incrementCountMetric,
@@ -13,6 +14,7 @@ const {
13
14
  TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS,
14
15
  TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES,
15
16
  } = require('../../ci-visibility/telemetry')
17
+ const request = require('../requests/request')
16
18
  const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
17
19
  const { validateSkippableTestsResponse } = require('../test-optimization-http-cache-schema')
18
20
 
@@ -34,10 +36,11 @@ function parseSkippableSuitesResponse (
34
36
  if (validateRequiredFields) {
35
37
  validateSkippableTestsResponse(parsedResponse, { validationMode })
36
38
  }
37
- const coverage = {}
39
+ let coverage
38
40
  const coverageByFilename = parsedResponse.meta?.coverage
39
41
  if (coverageByFilename) {
40
42
  for (const [filename, bitmap] of Object.entries(coverageByFilename)) {
43
+ coverage ??= {}
41
44
  coverage[filename.replaceAll('\\', '/')] = bitmap
42
45
  }
43
46
  }
@@ -226,8 +229,8 @@ function fetchFromApi ({
226
229
  }
227
230
 
228
231
  if (isEvpProxy) {
229
- options.path = `${evpProxyPrefix}/api/v2/ci/tests/skippable`
230
- options.headers['X-Datadog-EVP-Subdomain'] = 'api'
232
+ options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/ci/tests/skippable')
233
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
231
234
  } else {
232
235
  const { DD_API_KEY } = getConfig()
233
236
  if (!DD_API_KEY) {
@@ -1,6 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const getConfig = require('../../config')
4
+ const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../evp_proxy/constants')
5
+ const { joinEVPProxyPath } = require('../../evp_proxy/path')
4
6
  const id = require('../../id')
5
7
  const log = require('../../log')
6
8
  const { EARLY_FLAKE_DETECTION_RETRY_BUCKETS, createEfdRetryPolicy } = require('../efd-retry-policy')
@@ -238,8 +240,8 @@ function getLibraryConfiguration ({
238
240
  }
239
241
 
240
242
  if (isEvpProxy) {
241
- options.path = `${evpProxyPrefix}/api/v2/libraries/tests/services/setting`
242
- options.headers['X-Datadog-EVP-Subdomain'] = 'api'
243
+ options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/libraries/tests/services/setting')
244
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
243
245
  } else {
244
246
  if (!config.DD_API_KEY) {
245
247
  return done(new Error('Request to settings endpoint was not done because Datadog API key is not defined.'))
@@ -0,0 +1,39 @@
1
+ 'use strict'
2
+
3
+ const EPOCH_SECONDS_THRESHOLD = 1_000_000_000
4
+
5
+ /**
6
+ * Returns the delay requested by a Test Optimization intake rate-limit response.
7
+ *
8
+ * @param {import('node:http').IncomingHttpHeaders} [headers]
9
+ * @returns {number}
10
+ */
11
+ function getRateLimitResetDelay (headers) {
12
+ let retryAfter = headers?.['retry-after']
13
+ if (Array.isArray(retryAfter)) retryAfter = retryAfter[0]
14
+ if (typeof retryAfter === 'string' && retryAfter.trim() !== '') {
15
+ const delaySeconds = Number(retryAfter)
16
+ if (Number.isFinite(delaySeconds)) {
17
+ if (delaySeconds >= 0) return delaySeconds * 1000
18
+ } else {
19
+ const resetTimestamp = Date.parse(retryAfter)
20
+ if (Number.isFinite(resetTimestamp)) return Math.max(0, resetTimestamp - Date.now())
21
+ }
22
+ }
23
+
24
+ let reset = headers?.['x-ratelimit-reset']
25
+ if (Array.isArray(reset)) reset = reset[0]
26
+ if (typeof reset !== 'string' || reset.trim() === '') return NaN
27
+
28
+ const resetSeconds = Number(reset)
29
+ if (!Number.isFinite(resetSeconds) || resetSeconds < 0) return NaN
30
+
31
+ // Datadog defines this header as delay seconds. Preserve compatibility with
32
+ // realistic Unix timestamps without confusing ordinary durations with epochs.
33
+ if (resetSeconds >= EPOCH_SECONDS_THRESHOLD) {
34
+ return Math.max(0, resetSeconds * 1000 - Date.now())
35
+ }
36
+ return resetSeconds * 1000
37
+ }
38
+
39
+ module.exports = { getRateLimitResetDelay }
@@ -13,12 +13,13 @@ const {
13
13
  singleJitteredDelay,
14
14
  } = require('../../exporters/common/retry')
15
15
  const { parseUrl } = require('../../exporters/common/url')
16
+ const { getRateLimitResetDelay } = require('./rate-limit')
16
17
 
17
18
  const legacyStorage = storage('legacy')
18
19
 
19
20
  /**
20
21
  * Simplified HTTP request for test optimization (library config). Uses common HTTP agents.
21
- * Retries: 429 (with X-ratelimit-reset, max 30s wait),
22
+ * Retries: 429 (with Retry-After or X-RateLimit-Reset, max 30s wait),
22
23
  * >=500 and transient network errors (5–7.5s delay with jitter). Max one retry.
23
24
  * Destroys connections on errors to prevent reuse of bad connections. Preserves
24
25
  * original status code across retries for telemetry.
@@ -98,11 +99,7 @@ function request (data, options, callback) {
98
99
  }
99
100
 
100
101
  if (res.statusCode === 429 && !hasRetried) {
101
- const resetHeader = res.headers['x-ratelimit-reset']
102
- const resetTs = (resetHeader === null || resetHeader === undefined)
103
- ? NaN
104
- : Number.parseInt(resetHeader, 10)
105
- const waitMs = Number.isFinite(resetTs) ? Math.max(0, resetTs * 1000 - Date.now()) : NaN
102
+ const waitMs = getRateLimitResetDelay(res.headers)
106
103
 
107
104
  if (Number.isFinite(waitMs) && waitMs <= RATE_LIMIT_MAX_WAIT_MS) {
108
105
  hasRetried = true
@@ -4,6 +4,8 @@ const { closeSync, constants, fstatSync, openSync, readFileSync } = require('nod
4
4
  const { gzipSync } = require('node:zlib')
5
5
 
6
6
  const getConfig = require('../../config')
7
+ const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../evp_proxy/constants')
8
+ const { joinEVPProxyPath } = require('../../evp_proxy/path')
7
9
  const FormData = require('../../exporters/common/form-data')
8
10
  const request = require('../../exporters/common/request')
9
11
  const log = require('../../log')
@@ -98,8 +100,8 @@ function uploadCoverageReport (
98
100
  }
99
101
 
100
102
  if (isEvpProxy) {
101
- options.path = `${evpProxyPrefix}/api/v2/cicovreprt`
102
- options.headers['X-Datadog-EVP-Subdomain'] = 'ci-intake'
103
+ options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/cicovreprt')
104
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'ci-intake'
103
105
  } else {
104
106
  options.path = '/api/v2/cicovreprt'
105
107
  options.headers['dd-api-key'] = DD_API_KEY
@@ -4,8 +4,10 @@ const { readFileSync } = require('node:fs')
4
4
  const { extname } = require('node:path')
5
5
 
6
6
  const getConfig = require('../../config')
7
- const request = require('../../exporters/common/request')
7
+ const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../evp_proxy/constants')
8
+ const { joinEVPProxyPath } = require('../../evp_proxy/path')
8
9
  const log = require('../../log')
10
+ const request = require('../exporters/request')
9
11
 
10
12
  const UPLOAD_TIMEOUT_MS = 30_000
11
13
  const TEST_SCREENSHOT_ENDPOINT_PREFIX = '/api/v2/ci/test-runs/'
@@ -74,10 +76,12 @@ function toIdempotencyQueryValue (idempotencyKey) {
74
76
  * @param {URL} options.url - The base URL for the screenshot upload
75
77
  * @param {boolean} [options.isEvpProxy] - Whether to upload through the Agent's evp_proxy
76
78
  * @param {string} [options.evpProxyPrefix] - The evp_proxy path prefix (e.g. '/evp_proxy/v4')
79
+ * @param {number} [options.deadline] - Absolute finalization deadline in epoch milliseconds
80
+ * @param {AbortSignal} [options.signal] - Signal used to cancel the upload
77
81
  * @param {Function} callback - Callback function (err)
78
82
  */
79
83
  function uploadTestScreenshot (
80
- { filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix },
84
+ { filePath, traceId, idempotencyKey, capturedAtMs, url, isEvpProxy, evpProxyPrefix, deadline, signal },
81
85
  callback
82
86
  ) {
83
87
  const { DD_API_KEY } = getConfig()
@@ -125,13 +129,15 @@ function uploadTestScreenshot (
125
129
  path: `${basePath}?${query}`,
126
130
  timeout: UPLOAD_TIMEOUT_MS,
127
131
  url,
132
+ deadline,
133
+ signal,
128
134
  }
129
135
 
130
136
  if (isEvpProxy) {
131
137
  // Agent mode: prefix the evp_proxy path, tell the proxy which subdomain to forward to, and
132
138
  // drop the API key — the Agent injects it. The query params survive the proxy.
133
- options.path = `${evpProxyPrefix}${basePath}?${query}`
134
- options.headers['X-Datadog-EVP-Subdomain'] = 'api'
139
+ options.path = `${joinEVPProxyPath(evpProxyPrefix, basePath)}?${query}`
140
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
135
141
  } else {
136
142
  options.headers['DD-API-KEY'] = DD_API_KEY
137
143
  }
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
3
  const getConfig = require('../../config')
4
- const request = require('../requests/request')
4
+ const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../evp_proxy/constants')
5
+ const { joinEVPProxyPath } = require('../../evp_proxy/path')
5
6
  const id = require('../../id')
6
7
  const log = require('../../log')
7
-
8
8
  const {
9
9
  incrementCountMetric,
10
10
  distributionMetric,
@@ -14,7 +14,7 @@ const {
14
14
  TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_TESTS,
15
15
  TELEMETRY_TEST_MANAGEMENT_TESTS_RESPONSE_BYTES,
16
16
  } = require('../telemetry')
17
-
17
+ const request = require('../requests/request')
18
18
  const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
19
19
  const { validateTestManagementTestsResponse } = require('../test-optimization-http-cache-schema')
20
20
 
@@ -132,8 +132,8 @@ function fetchFromApi ({
132
132
  }
133
133
 
134
134
  if (isEvpProxy) {
135
- options.path = `${evpProxyPrefix}/api/v2/test/libraries/test-management/tests`
136
- options.headers['X-Datadog-EVP-Subdomain'] = 'api'
135
+ options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/test/libraries/test-management/tests')
136
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
137
137
  } else {
138
138
  const { DD_API_KEY } = getConfig()
139
139
  if (!DD_API_KEY) {
@@ -209,6 +209,7 @@ export interface GeneratedConfig {
209
209
  DD_TRACE_BEAUTIFUL_LOGS: boolean;
210
210
  DD_TRACE_BLUEBIRD_ENABLED: boolean;
211
211
  DD_TRACE_BODY_PARSER_ENABLED: boolean;
212
+ DD_TRACE_BROWSER_BUNYAN_ENABLED: boolean;
212
213
  DD_TRACE_BSON_ENABLED: boolean;
213
214
  DD_TRACE_BULLMQ_ENABLED: boolean;
214
215
  DD_TRACE_BUNYAN_ENABLED: boolean;
@@ -340,6 +341,7 @@ export interface GeneratedConfig {
340
341
  DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
341
342
  DD_TRACE_OPENTELEMETRY_SDK_TRACE_NODE_ENABLED: boolean;
342
343
  DD_TRACE_ORACLEDB_ENABLED: boolean;
344
+ DD_TRACE_OTEL_CTX_ENABLED: boolean;
343
345
  DD_TRACE_OTEL_ENABLED: boolean;
344
346
  DD_TRACE_OTEL_SEMANTICS_ENABLED: boolean;
345
347
  DD_TRACE_PASSPORT_ENABLED: boolean;
@@ -894,6 +896,7 @@ export interface GeneratedEnvVarConfig {
894
896
  DD_TRACE_BEAUTIFUL_LOGS: boolean;
895
897
  DD_TRACE_BLUEBIRD_ENABLED: boolean;
896
898
  DD_TRACE_BODY_PARSER_ENABLED: boolean;
899
+ DD_TRACE_BROWSER_BUNYAN_ENABLED: boolean;
897
900
  DD_TRACE_BSON_ENABLED: boolean;
898
901
  DD_TRACE_BULLMQ_ENABLED: boolean;
899
902
  DD_TRACE_BUNYAN_ENABLED: boolean;
@@ -1040,6 +1043,7 @@ export interface GeneratedEnvVarConfig {
1040
1043
  DD_TRACE_OPENSEARCH_PROJECT_OPENSEARCH_ENABLED: boolean;
1041
1044
  DD_TRACE_OPENTELEMETRY_SDK_TRACE_NODE_ENABLED: boolean;
1042
1045
  DD_TRACE_ORACLEDB_ENABLED: boolean;
1046
+ DD_TRACE_OTEL_CTX_ENABLED: boolean;
1043
1047
  DD_TRACE_OTEL_ENABLED: boolean;
1044
1048
  DD_TRACE_OTEL_SEMANTICS_ENABLED: boolean;
1045
1049
  DD_TRACE_PARTIAL_FLUSH_MIN_SPANS: number;
@@ -15,6 +15,7 @@ const { isTrue } = require('../util')
15
15
  const telemetry = require('../telemetry')
16
16
  const telemetryMetrics = require('../telemetry/metrics')
17
17
  const {
18
+ getServerlessPlatformTags,
18
19
  IS_SERVERLESS,
19
20
  getIsGCPFunction,
20
21
  getIsAzureFunction,
@@ -477,9 +478,13 @@ class Config extends ConfigBase {
477
478
 
478
479
  // Apply the OTel sampler when the user opted into OTel traces or explicitly set the sampler.
479
480
  // OTEL_TRACES_SAMPLER has `default: parentbased_always_on` (per OTel spec), so opt-in users
480
- // that don't set the sampler still get parent-based sampling.
481
+ // that don't set the sampler still get parent-based sampling. Electron exporter spans go over
482
+ // the Electron SDK's IPC bridge rather than OTLP (see opentracing/tracer.js), so an
483
+ // OTEL_TRACES_EXPORTER=otlp set for an unrelated telemetry pipeline shouldn't also override
484
+ // dd-trace's own sampling policy in that case.
481
485
  if (!trackedConfigOrigins.has('sampleRate') &&
482
- (trackedConfigOrigins.has('OTEL_TRACES_SAMPLER') || this.OTEL_TRACES_EXPORTER === 'otlp')) {
486
+ (trackedConfigOrigins.has('OTEL_TRACES_SAMPLER') ||
487
+ (this.OTEL_TRACES_EXPORTER === 'otlp' && this.experimental.exporter !== 'electron'))) {
483
488
  setAndTrack(this, 'sampleRate',
484
489
  getFromOtelSamplerMap(this.OTEL_TRACES_SAMPLER, this.OTEL_TRACES_SAMPLER_ARG))
485
490
  }
@@ -597,6 +602,12 @@ class Config extends ConfigBase {
597
602
  this.tags.version = this.version
598
603
  }
599
604
  this.tags['runtime-id'] = RUNTIME_ID
605
+ const platformTags = getServerlessPlatformTags()
606
+ if (platformTags) {
607
+ for (let i = 0; i < platformTags.length; i += 2) {
608
+ this.tags[platformTags[i]] ??= platformTags[i + 1]
609
+ }
610
+ }
600
611
 
601
612
  if (IS_SERVERLESS) {
602
613
  setAndTrack(this, 'telemetry.DD_INSTRUMENTATION_TELEMETRY_ENABLED', false)
@@ -58,6 +58,13 @@ function applyMajorOverrides (supportedConfigurations, majorVersion) {
58
58
  delete supportedConfigurations.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED
59
59
  // eslint-disable-next-line eslint-rules/eslint-env-aliases
60
60
  dropAlias(supportedConfigurations.DD_RUNTIME_METRICS_RUNTIME_ID_ENABLED, 'DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED')
61
+
62
+ if (majorVersion >= 7) {
63
+ // The Electron plugin moved to the Electron SDK and is opt-in (disabled by default) from v7 on,
64
+ // while remaining enabled by default on earlier majors (see ElectronPlugin.optIn).
65
+ const electronEntry = supportedConfigurations.DD_TRACE_ELECTRON_ENABLED?.[0]
66
+ if (electronEntry) electronEntry.default = 'false'
67
+ }
61
68
  }
62
69
 
63
70
  /**
@@ -2452,6 +2452,13 @@
2452
2452
  "default": "true"
2453
2453
  }
2454
2454
  ],
2455
+ "DD_TRACE_BROWSER_BUNYAN_ENABLED": [
2456
+ {
2457
+ "implementation": "A",
2458
+ "type": "boolean",
2459
+ "default": "true"
2460
+ }
2461
+ ],
2455
2462
  "DD_TRACE_BSON_ENABLED": [
2456
2463
  {
2457
2464
  "implementation": "A",
@@ -3518,6 +3525,13 @@
3518
3525
  "default": "false"
3519
3526
  }
3520
3527
  ],
3528
+ "DD_TRACE_OTEL_CTX_ENABLED": [
3529
+ {
3530
+ "implementation": "A",
3531
+ "type": "boolean",
3532
+ "default": "false"
3533
+ }
3534
+ ],
3521
3535
  "DD_TRACE_PARTIAL_FLUSH_MIN_SPANS": [
3522
3536
  {
3523
3537
  "implementation": "B",
@@ -5,6 +5,7 @@ const {
5
5
  getHeadersSize,
6
6
  getMessageSize,
7
7
  getSizeOrZero,
8
+ PATHWAY_FIELD_BYTES,
8
9
  } = require('./size')
9
10
 
10
11
  // This is only needed because DSM code is spread across existing tracing
@@ -121,6 +122,7 @@ module.exports = {
121
122
  DataStreamsManager,
122
123
  DataStreamsProcessor,
123
124
  SchemaBuilder,
125
+ PATHWAY_FIELD_BYTES,
124
126
 
125
127
  // These are small functions so they are exposed directly and not lazy loaded.
126
128
  getAmqpMessageSize,
@@ -8,9 +8,16 @@ class DataStreamsManager {
8
8
  this._dataStreamsProcessor = processor
9
9
  }
10
10
 
11
- setCheckpoint (edgeTags, span, payloadSize = 0) {
11
+ /**
12
+ * @param {string[]} edgeTags
13
+ * @param {import('../opentracing/span')|null} span
14
+ * @param {number} [payloadSize]
15
+ * @param {number} [pathwayContextSize] See `DataStreamsProcessor#setCheckpoint`.
16
+ * @returns {object|undefined}
17
+ */
18
+ setCheckpoint (edgeTags, span, payloadSize = 0, pathwayContextSize) {
12
19
  const ctx = this._dataStreamsProcessor.setCheckpoint(
13
- edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize
20
+ edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize, pathwayContextSize
14
21
  )
15
22
  DataStreamsContext.setDataStreamsContext(ctx)
16
23
  return ctx