dd-trace 5.114.0 → 5.116.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 (207) hide show
  1. package/README.electron.md +7 -0
  2. package/README.md +17 -0
  3. package/ci/diagnose.js +2100 -0
  4. package/ci/init.js +23 -2
  5. package/ci/runbook.md +198 -0
  6. package/ci/test-optimization-validation/approval-artifacts.js +143 -0
  7. package/ci/test-optimization-validation/approval.js +299 -0
  8. package/ci/test-optimization-validation/artifact-id.js +20 -0
  9. package/ci/test-optimization-validation/bounded-json.js +121 -0
  10. package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
  11. package/ci/test-optimization-validation/ci-discovery.js +181 -0
  12. package/ci/test-optimization-validation/ci-remediation.js +210 -0
  13. package/ci/test-optimization-validation/cli.js +903 -0
  14. package/ci/test-optimization-validation/command-blocker.js +81 -0
  15. package/ci/test-optimization-validation/command-output-policy.js +206 -0
  16. package/ci/test-optimization-validation/command-runner.js +707 -0
  17. package/ci/test-optimization-validation/command-suitability.js +471 -0
  18. package/ci/test-optimization-validation/executable-approval.js +48 -0
  19. package/ci/test-optimization-validation/executable.js +480 -0
  20. package/ci/test-optimization-validation/generated-file-policy.js +63 -0
  21. package/ci/test-optimization-validation/generated-files.js +351 -0
  22. package/ci/test-optimization-validation/generated-verifier.js +196 -0
  23. package/ci/test-optimization-validation/init-probe-preload.js +163 -0
  24. package/ci/test-optimization-validation/init-probe.js +246 -0
  25. package/ci/test-optimization-validation/late-initialization.js +63 -0
  26. package/ci/test-optimization-validation/local-command.js +163 -0
  27. package/ci/test-optimization-validation/manifest-loader.js +133 -0
  28. package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
  29. package/ci/test-optimization-validation/manifest-schema.js +862 -0
  30. package/ci/test-optimization-validation/offline-fixtures.js +327 -0
  31. package/ci/test-optimization-validation/offline-output.js +406 -0
  32. package/ci/test-optimization-validation/payload-normalizer.js +72 -0
  33. package/ci/test-optimization-validation/plan-writer.js +1120 -0
  34. package/ci/test-optimization-validation/preflight-runner.js +114 -0
  35. package/ci/test-optimization-validation/redaction.js +331 -0
  36. package/ci/test-optimization-validation/report-writer.js +1508 -0
  37. package/ci/test-optimization-validation/safe-files.js +203 -0
  38. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
  39. package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
  40. package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
  41. package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
  42. package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
  43. package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
  44. package/ci/test-optimization-validation/setup-runner.js +97 -0
  45. package/ci/test-optimization-validation/static-diagnosis.js +159 -0
  46. package/ci/test-optimization-validation/test-output.js +129 -0
  47. package/ci/test-optimization-validation-manifest.schema.json +1 -0
  48. package/ci/validate-test-optimization.js +3 -0
  49. package/ext/exporters.js +1 -0
  50. package/index.d.ts +121 -8
  51. package/index.electron.js +3 -0
  52. package/init.js +18 -0
  53. package/initialize.mjs +1 -1
  54. package/loader-hook.mjs +28 -18
  55. package/openfeature.d.ts +1 -0
  56. package/openfeature.js +4 -0
  57. package/package.json +15 -8
  58. package/packages/datadog-instrumentations/src/ai.js +37 -26
  59. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  60. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
  61. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
  62. package/packages/datadog-instrumentations/src/cucumber.js +24 -10
  63. package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
  64. package/packages/datadog-instrumentations/src/express.js +20 -2
  65. package/packages/datadog-instrumentations/src/fastify.js +7 -11
  66. package/packages/datadog-instrumentations/src/grpc/server.js +18 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
  68. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
  69. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
  70. package/packages/datadog-instrumentations/src/helpers/router-helper.js +8 -8
  71. package/packages/datadog-instrumentations/src/http2/server.js +143 -17
  72. package/packages/datadog-instrumentations/src/jest.js +36 -4
  73. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  74. package/packages/datadog-instrumentations/src/mocha/main.js +0 -2
  75. package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
  76. package/packages/datadog-instrumentations/src/mongodb.js +3 -3
  77. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  78. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  79. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  80. package/packages/datadog-instrumentations/src/next.js +24 -0
  81. package/packages/datadog-instrumentations/src/nyc.js +0 -2
  82. package/packages/datadog-instrumentations/src/oracledb.js +2 -2
  83. package/packages/datadog-instrumentations/src/pg.js +2 -2
  84. package/packages/datadog-instrumentations/src/playwright.js +159 -22
  85. package/packages/datadog-instrumentations/src/process.js +3 -0
  86. package/packages/datadog-instrumentations/src/router.js +191 -106
  87. package/packages/datadog-instrumentations/src/selenium.js +52 -26
  88. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +2 -2
  89. package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
  90. package/packages/datadog-instrumentations/src/ws.js +2 -1
  91. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
  92. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
  93. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +41 -16
  94. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +21 -21
  95. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +14 -8
  96. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -24
  97. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  98. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -62
  99. package/packages/datadog-plugin-cypress/src/index.js +6 -1
  100. package/packages/datadog-plugin-cypress/src/support.js +92 -26
  101. package/packages/datadog-plugin-electron/src/ipc.js +1 -1
  102. package/packages/datadog-plugin-graphql/src/execute.js +177 -59
  103. package/packages/datadog-plugin-graphql/src/parse.js +22 -1
  104. package/packages/datadog-plugin-graphql/src/request.js +32 -1
  105. package/packages/datadog-plugin-graphql/src/utils.js +42 -0
  106. package/packages/datadog-plugin-graphql/src/validate.js +15 -3
  107. package/packages/datadog-plugin-http2/src/client.js +1 -1
  108. package/packages/datadog-plugin-http2/src/server.js +38 -6
  109. package/packages/datadog-plugin-jest/src/util.js +21 -6
  110. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
  111. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
  112. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  113. package/packages/datadog-plugin-playwright/src/index.js +177 -64
  114. package/packages/datadog-plugin-router/src/index.js +11 -2
  115. package/packages/datadog-plugin-selenium/src/index.js +5 -36
  116. package/packages/datadog-plugin-vitest/src/index.js +4 -2
  117. package/packages/datadog-plugin-ws/src/close.js +2 -1
  118. package/packages/datadog-plugin-ws/src/producer.js +2 -1
  119. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  120. package/packages/datadog-plugin-ws/src/server.js +2 -1
  121. package/packages/dd-trace/index.electron.js +3 -0
  122. package/packages/dd-trace/index.js +2 -37
  123. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  124. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  125. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
  126. package/packages/dd-trace/src/azure_metadata.js +2 -1
  127. package/packages/dd-trace/src/bootstrap.js +39 -0
  128. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
  129. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  130. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
  131. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
  132. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
  133. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
  134. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
  135. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -5
  136. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +22 -6
  137. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +5 -0
  138. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +62 -7
  139. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -2
  140. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
  141. package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
  142. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  143. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  144. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
  145. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +173 -9
  146. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
  147. package/packages/dd-trace/src/config/generated-config-types.d.ts +20 -0
  148. package/packages/dd-trace/src/config/helper.js +1 -0
  149. package/packages/dd-trace/src/config/index.js +10 -0
  150. package/packages/dd-trace/src/config/supported-configurations.json +83 -0
  151. package/packages/dd-trace/src/constants.js +7 -0
  152. package/packages/dd-trace/src/datastreams/pathway.js +6 -4
  153. package/packages/dd-trace/src/exporter.js +2 -0
  154. package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
  155. package/packages/dd-trace/src/exporters/common/request.js +59 -30
  156. package/packages/dd-trace/src/exporters/common/url.js +15 -1
  157. package/packages/dd-trace/src/feature-registry.js +29 -0
  158. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  159. package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
  160. package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
  161. package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
  162. package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
  163. package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
  164. package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
  165. package/packages/dd-trace/src/llmobs/index.js +10 -2
  166. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  167. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
  168. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
  169. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  170. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
  171. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
  172. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
  173. package/packages/dd-trace/src/llmobs/sdk.js +17 -0
  174. package/packages/dd-trace/src/llmobs/tagger.js +79 -7
  175. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  176. package/packages/dd-trace/src/llmobs/util.js +32 -0
  177. package/packages/dd-trace/src/noop/proxy.js +5 -4
  178. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
  179. package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
  180. package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
  181. package/packages/dd-trace/src/openfeature/index.js +0 -2
  182. package/packages/dd-trace/src/openfeature/noop.js +1 -28
  183. package/packages/dd-trace/src/openfeature/register.js +35 -0
  184. package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
  185. package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
  186. package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
  187. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  188. package/packages/dd-trace/src/opentracing/propagation/log.js +11 -2
  189. package/packages/dd-trace/src/opentracing/propagation/text_map.js +80 -14
  190. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +10 -1
  191. package/packages/dd-trace/src/opentracing/tracer.js +7 -1
  192. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
  193. package/packages/dd-trace/src/plugins/util/git.js +3 -2
  194. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
  195. package/packages/dd-trace/src/plugins/util/test.js +65 -8
  196. package/packages/dd-trace/src/plugins/util/web.js +12 -0
  197. package/packages/dd-trace/src/proxy.js +99 -10
  198. package/packages/dd-trace/src/span_processor.js +5 -0
  199. package/packages/dd-trace/src/standalone/index.js +0 -36
  200. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  201. package/packages/dd-trace/src/util.js +26 -0
  202. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  203. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +0 -17
@@ -8,8 +8,13 @@ class LogPropagator {
8
8
  this._config = config
9
9
  }
10
10
 
11
+ /**
12
+ * @param {DatadogSpanContext | null | undefined} spanContext
13
+ * @param {Record<string, unknown>} [carrier]
14
+ * @returns {Record<string, unknown> | undefined}
15
+ */
11
16
  inject (spanContext, carrier) {
12
- if (!carrier) return
17
+ if (carrier === null) return
13
18
 
14
19
  const dd = {}
15
20
  let hasField = false
@@ -35,7 +40,11 @@ class LogPropagator {
35
40
  hasField = true
36
41
  }
37
42
 
38
- if (hasField) carrier.dd = dd
43
+ if (!hasField) return
44
+
45
+ carrier ??= {}
46
+ carrier.dd = dd
47
+ return carrier
39
48
  }
40
49
 
41
50
  extract (carrier) {
@@ -8,6 +8,7 @@ const log = require('../../log')
8
8
  const tags = require('../../../../../ext/tags')
9
9
  const { getConfiguredEnvName } = require('../../config/helper')
10
10
  const { setAllBaggageItems, getAllBaggageItems, removeAllBaggageItems } = require('../../baggage')
11
+ const { hasTraceSourcePropagationTag } = require('../../standalone/tracesource')
11
12
  const telemetryMetrics = require('../../telemetry/metrics')
12
13
  const { DD_MAJOR } = require('../../../../../version')
13
14
 
@@ -119,22 +120,35 @@ class TextMapPropagator {
119
120
  return this.#baggageTagKeysSet
120
121
  }
121
122
 
123
+ /**
124
+ * @param {DatadogSpanContext | null | undefined} spanContext
125
+ * @param {Record<string, string>} [carrier]
126
+ * @returns {Record<string, string> | undefined}
127
+ */
122
128
  inject (spanContext, carrier) {
123
- if (!carrier) return
124
- this._injectBaggageItems(spanContext, carrier)
125
- if (!spanContext) return
129
+ if (carrier === null) return
130
+ let injectedCarrier = this._injectBaggageItems(spanContext, carrier)
131
+ if (!spanContext) return injectedCarrier
126
132
 
127
- this._injectDatadog(spanContext, carrier)
128
- this._injectB3MultipleHeaders(spanContext, carrier)
129
- this._injectB3SingleHeader(spanContext, carrier)
130
- this._injectTraceparent(spanContext, carrier)
131
-
132
- if (injectCh.hasSubscribers) {
133
- injectCh.publish({ spanContext, carrier })
133
+ const injectTraceContext = this._config.apmTracingEnabled !== false ||
134
+ hasTraceSourcePropagationTag(spanContext._trace.tags)
135
+ if (injectTraceContext) {
136
+ injectedCarrier = this._injectDatadog(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
137
+ injectedCarrier = this._injectB3MultipleHeaders(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
138
+ injectedCarrier = this._injectB3SingleHeader(spanContext, injectedCarrier ?? carrier) ?? injectedCarrier
134
139
  }
140
+ injectedCarrier = this
141
+ ._injectTraceparent(spanContext, injectedCarrier ?? carrier, injectTraceContext) ?? injectedCarrier
142
+
143
+ if (injectedCarrier === undefined) return
144
+
145
+ carrier = injectedCarrier
146
+ if (injectCh.hasSubscribers) injectCh.publish({ spanContext, carrier })
135
147
 
136
148
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
137
149
  log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
150
+
151
+ return carrier
138
152
  }
139
153
 
140
154
  extract (carrier) {
@@ -156,15 +170,23 @@ class TextMapPropagator {
156
170
  return spanContext
157
171
  }
158
172
 
173
+ /**
174
+ * @param {DatadogSpanContext} spanContext
175
+ * @param {Record<string, string>} [carrier]
176
+ * @returns {Record<string, string> | undefined}
177
+ */
159
178
  _injectDatadog (spanContext, carrier) {
160
179
  if (!this._hasPropagationStyle('inject', 'datadog')) return
161
180
 
181
+ carrier ??= {}
162
182
  carrier[traceKey] = spanContext.toTraceId()
163
183
  carrier[spanKey] = spanContext.toSpanId()
164
184
 
165
185
  this._injectOrigin(spanContext, carrier)
166
186
  this._injectSamplingPriority(spanContext, carrier)
167
187
  this._injectTags(spanContext, carrier)
188
+
189
+ return carrier
168
190
  }
169
191
 
170
192
  _injectOrigin (spanContext, carrier) {
@@ -183,7 +205,13 @@ class TextMapPropagator {
183
205
  }
184
206
  }
185
207
 
208
+ /**
209
+ * @param {DatadogSpanContext | null | undefined} spanContext
210
+ * @param {Record<string, string>} [carrier]
211
+ * @returns {Record<string, string> | undefined}
212
+ */
186
213
  _injectBaggageItems (spanContext, carrier) {
214
+ let injectedCarrier
187
215
  if (this._config.legacyBaggageEnabled) {
188
216
  const baggageItems = spanContext?._baggageItems
189
217
  if (baggageItems) {
@@ -202,7 +230,8 @@ class TextMapPropagator {
202
230
  if (invalidHeaderValueCharExpr.test(headerValue)) {
203
231
  headerValue = encodeURIComponent(headerValue)
204
232
  }
205
- carrier[headerName] = headerValue
233
+ injectedCarrier ??= carrier ?? {}
234
+ injectedCarrier[headerName] = headerValue
206
235
  }
207
236
  }
208
237
  }
@@ -213,7 +242,6 @@ class TextMapPropagator {
213
242
  let byteCounter = 0
214
243
 
215
244
  const baggageItems = getAllBaggageItems()
216
- if (!baggageItems) return
217
245
  for (const key of Object.keys(baggageItems)) {
218
246
  const baggageKey = key.trim()
219
247
  if (!baggageTokenExpr.test(baggageKey)) continue
@@ -242,10 +270,13 @@ class TextMapPropagator {
242
270
 
243
271
  baggage = baggage.slice(0, -1)
244
272
  if (baggage) {
245
- carrier.baggage = baggage
273
+ injectedCarrier ??= carrier ?? {}
274
+ injectedCarrier.baggage = baggage
246
275
  tracerMetrics.count('context_header_style.injected', ['header_style:baggage']).inc()
247
276
  }
248
277
  }
278
+
279
+ return injectedCarrier
249
280
  }
250
281
 
251
282
  _injectTags (spanContext, carrier) {
@@ -278,12 +309,18 @@ class TextMapPropagator {
278
309
  }
279
310
  }
280
311
 
312
+ /**
313
+ * @param {DatadogSpanContext} spanContext
314
+ * @param {Record<string, string>} [carrier]
315
+ * @returns {Record<string, string> | undefined}
316
+ */
281
317
  _injectB3MultipleHeaders (spanContext, carrier) {
282
318
  // v5 also accepts the legacy `'b3'` spelling for multi; v6 routes `'b3'` to single-header.
283
319
  const hasB3multi = this._hasPropagationStyle('inject', 'b3multi') ||
284
320
  (DD_MAJOR < 6 && this._hasPropagationStyle('inject', 'b3'))
285
321
  if (!hasB3multi) return
286
322
 
323
+ carrier ??= {}
287
324
  carrier[b3TraceKey] = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
288
325
  carrier[b3SpanKey] = spanContext._spanId.toString(16)
289
326
  carrier[b3SampledKey] = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
@@ -295,14 +332,22 @@ class TextMapPropagator {
295
332
  if (spanContext._parentId) {
296
333
  carrier[b3ParentKey] = spanContext._parentId.toString(16)
297
334
  }
335
+
336
+ return carrier
298
337
  }
299
338
 
339
+ /**
340
+ * @param {DatadogSpanContext} spanContext
341
+ * @param {Record<string, string>} [carrier]
342
+ * @returns {Record<string, string> | undefined}
343
+ */
300
344
  _injectB3SingleHeader (spanContext, carrier) {
301
345
  // v6 keeps `'b3 single header'` as a back-compat alias for callers that bypass parser normalisation.
302
346
  const hasB3SingleHeader = this._hasPropagationStyle('inject', 'b3 single header') ||
303
347
  (DD_MAJOR >= 6 && this._hasPropagationStyle('inject', 'b3'))
304
348
  if (!hasB3SingleHeader) return
305
349
 
350
+ carrier ??= {}
306
351
  const traceId = spanContext._traceId.toTraceIdHex(spanContext._trace.tags['_dd.p.tid'])
307
352
  const spanId = spanContext._spanId.toString(16)
308
353
  const sampled = spanContext._sampling.priority >= AUTO_KEEP ? '1' : '0'
@@ -311,11 +356,30 @@ class TextMapPropagator {
311
356
  if (spanContext._parentId) {
312
357
  carrier[b3HeaderKey] += '-' + spanContext._parentId.toString(16)
313
358
  }
359
+
360
+ return carrier
314
361
  }
315
362
 
316
- _injectTraceparent (spanContext, carrier) {
363
+ /**
364
+ * @param {DatadogSpanContext} spanContext
365
+ * @param {Record<string, string> | undefined} carrier
366
+ * @param {boolean} injectTraceContext
367
+ * @returns {Record<string, string> | undefined}
368
+ */
369
+ _injectTraceparent (spanContext, carrier, injectTraceContext) {
317
370
  if (!this._hasPropagationStyle('inject', 'tracecontext')) return
318
371
 
372
+ if (!injectTraceContext) {
373
+ const tracestate = TraceState.fromString(spanContext._tracestate?.toString())
374
+ tracestate.delete('dd')
375
+ const header = tracestate.toString()
376
+ if (!header) return
377
+ carrier ??= {}
378
+ carrier.tracestate = header
379
+ return carrier
380
+ }
381
+
382
+ carrier ??= {}
319
383
  const {
320
384
  _sampling: { priority, mechanism },
321
385
  _tracestate: ts = new TraceState(),
@@ -363,6 +427,8 @@ class TextMapPropagator {
363
427
  })
364
428
 
365
429
  carrier.tracestate = ts.toString()
430
+
431
+ return carrier
366
432
  }
367
433
 
368
434
  _hasPropagationStyle (mode, name) {
@@ -13,13 +13,22 @@ class DSMTextMapPropagator {
13
13
  this.config = config
14
14
  }
15
15
 
16
+ /**
17
+ * @param {object} ctx DSM pathway context.
18
+ * @param {Record<string, string>} [carrier]
19
+ * @returns {Record<string, string> | undefined}
20
+ */
16
21
  inject (ctx, carrier) {
17
22
  if (!this.config.dsmEnabled) return
18
23
 
19
- DsmPathwayCodec.encode(ctx, carrier)
24
+ const injectedCarrier = DsmPathwayCodec.encode(ctx, carrier)
25
+ if (injectedCarrier === undefined) return
20
26
 
27
+ carrier = injectedCarrier
21
28
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
22
29
  log.debug(() => `Inject into carrier (DSM): ${JSON.stringify(pick(carrier, logKeys))}.`)
30
+
31
+ return carrier
23
32
  }
24
33
 
25
34
  extract (carrier) {
@@ -101,6 +101,12 @@ class DatadogTracer {
101
101
  return span
102
102
  }
103
103
 
104
+ /**
105
+ * @param {Span|SpanContext} context
106
+ * @param {string} format
107
+ * @param {object} [carrier]
108
+ * @returns {object | undefined}
109
+ */
104
110
  inject (context, format, carrier) {
105
111
  if (context instanceof Span) {
106
112
  context = context.context()
@@ -110,7 +116,7 @@ class DatadogTracer {
110
116
  if (format !== 'text_map_dsm' && format !== formats.LOG) {
111
117
  this._prioritySampler.sample(context)
112
118
  }
113
- this._propagators[format].inject(context, carrier)
119
+ return this._propagators[format].inject(context, carrier)
114
120
  } catch (e) {
115
121
  log.error('Error injecting trace', e)
116
122
  runtimeMetrics.increment('datadog.tracer.node.inject.errors', true)
@@ -181,6 +181,9 @@ module.exports = class CiPlugin extends Plugin {
181
181
  }
182
182
  this.tracer._exporter.getLibraryConfiguration(this.testConfiguration, (err, libraryConfig) => {
183
183
  if (err) {
184
+ this.libraryConfig = undefined
185
+ this.itrCorrelationId = undefined
186
+ this.skippableSuitesCoverage = undefined
184
187
  log.error('Library configuration could not be fetched. %s', err.message)
185
188
  this._addRequestErrorTag(DD_CI_LIBRARY_CONFIGURATION_ERROR_SETTINGS, err)
186
189
  } else {
@@ -955,17 +958,28 @@ module.exports = class CiPlugin extends Plugin {
955
958
  }
956
959
  log.debug('Removing all Dynamic Instrumentation probes')
957
960
  const promises = []
958
- for (const fileLine of this.fileLineToProbeId.keys()) {
959
- const [file, line] = fileLine.split(':')
960
- promises.push(this.removeDiProbe({ file, line }))
961
+ for (const [activeProbeKey, probeId] of this.fileLineToProbeId) {
962
+ promises.push(this.#removeDiProbe(activeProbeKey, probeId))
961
963
  }
962
964
  return Promise.all(promises)
963
965
  }
964
966
 
967
+ /**
968
+ * @param {{ file: string, line: number }} location
969
+ */
965
970
  removeDiProbe ({ file, line }) {
966
- const probeId = this.fileLineToProbeId.get(`${file}:${line}`)
967
- log.warn('Removing probe from %s:%s, with id: %s', file, line, probeId)
968
- this.fileLineToProbeId.delete(probeId)
971
+ const activeProbeKey = `${file}:${line}`
972
+ const probeId = this.fileLineToProbeId.get(activeProbeKey)
973
+ return this.#removeDiProbe(activeProbeKey, probeId)
974
+ }
975
+
976
+ /**
977
+ * @param {string} activeProbeKey
978
+ * @param {string|undefined} probeId
979
+ */
980
+ #removeDiProbe (activeProbeKey, probeId) {
981
+ log.warn('Removing probe from %s, with id: %s', activeProbeKey, probeId)
982
+ this.fileLineToProbeId.delete(activeProbeKey)
969
983
  return this.di.removeProbe(probeId)
970
984
  }
971
985
 
@@ -13,6 +13,7 @@ const {
13
13
  TELEMETRY_GIT_COMMAND_ERRORS,
14
14
  } = require('../../ci-visibility/telemetry')
15
15
  const { storage } = require('../../../../datadog-core')
16
+ const { getSegment } = require('../../util')
16
17
  const {
17
18
  GIT_COMMIT_SHA,
18
19
  GIT_BRANCH,
@@ -251,7 +252,7 @@ function getGitRemoteName () {
251
252
  )
252
253
 
253
254
  if (upstreamRemote) {
254
- return upstreamRemote.split('/')[0]
255
+ return getSegment(upstreamRemote, '/', 0)
255
256
  }
256
257
 
257
258
  const remotes = sanitizedExec(
@@ -263,7 +264,7 @@ function getGitRemoteName () {
263
264
  false
264
265
  )
265
266
 
266
- return remotes.split('\n')[0] || 'origin'
267
+ return getSegment(remotes, '\n', 0) || 'origin'
267
268
  }
268
269
 
269
270
  function getSourceBranch () {
@@ -26,14 +26,27 @@ const supportedProxies = {
26
26
  'aws-apigateway': {
27
27
  spanName: 'aws.apigateway',
28
28
  component: 'aws-apigateway',
29
+ providesTimestamp: true,
29
30
  },
30
31
  'aws-httpapi': {
31
32
  spanName: 'aws.httpapi',
32
33
  component: 'aws-httpapi',
34
+ providesTimestamp: true,
33
35
  },
34
36
  'azure-apim': {
35
37
  spanName: 'azure.apim',
36
38
  component: 'azure-apim',
39
+ providesTimestamp: true,
40
+ },
41
+ 'azure-gw': {
42
+ spanName: 'azure.app-gateway',
43
+ component: 'azure-gw',
44
+ providesTimestamp: false,
45
+ },
46
+ 'azure-fd': {
47
+ spanName: 'azure.frontdoor',
48
+ component: 'azure-fd',
49
+ providesTimestamp: false,
37
50
  },
38
51
  }
39
52
 
@@ -109,21 +122,32 @@ function setInferredProxySpanTags (span, proxyContext) {
109
122
  }
110
123
 
111
124
  function extractInferredProxyContext (headers) {
112
- if (!(PROXY_HEADER_START_TIME_MS in headers)) {
125
+ if (!(PROXY_HEADER_SYSTEM in headers)) {
126
+ return null
127
+ }
128
+ if (!Object.hasOwn(supportedProxies, headers[PROXY_HEADER_SYSTEM])) {
129
+ log.debug('Received headers to create inferred proxy span but headers include an unsupported proxy type: %s',
130
+ headers[PROXY_HEADER_SYSTEM])
131
+
113
132
  return null
114
133
  }
115
134
 
116
- if (!(PROXY_HEADER_SYSTEM in headers && headers[PROXY_HEADER_SYSTEM] in supportedProxies)) {
117
- log.debug('Received headers to create inferred proxy span but headers include an unsupported proxy type', headers)
135
+ const detectedProxy = supportedProxies[headers[PROXY_HEADER_SYSTEM]]
136
+
137
+ if (detectedProxy.providesTimestamp && !headers[PROXY_HEADER_START_TIME_MS]) {
118
138
  return null
119
139
  }
120
140
 
121
141
  return {
122
142
  requestTime: headers[PROXY_HEADER_START_TIME_MS]
123
143
  ? Number.parseInt(headers[PROXY_HEADER_START_TIME_MS], 10)
124
- : null,
144
+ : Date.now(),
125
145
  method: headers[PROXY_HEADER_HTTPMETHOD],
126
- path: headers[PROXY_HEADER_PATH],
146
+ path: headers[PROXY_HEADER_PATH]
147
+ ? (headers[PROXY_HEADER_PATH].startsWith('/')
148
+ ? headers[PROXY_HEADER_PATH]
149
+ : '/' + headers[PROXY_HEADER_PATH])
150
+ : headers[PROXY_HEADER_PATH],
127
151
  stage: headers[PROXY_HEADER_STAGE],
128
152
  domainName: headers[PROXY_HEADER_DOMAIN],
129
153
  proxySystemName: headers[PROXY_HEADER_SYSTEM],
@@ -6,6 +6,7 @@ const { URL } = require('url')
6
6
  const { getLageTestSessionName } = require('../../ci-visibility/lage')
7
7
  const log = require('../../log')
8
8
  const { getEnvironmentVariable } = require('../../config/helper')
9
+ const { getSegment } = require('../../util')
9
10
  const satisfies = require('../../../../../vendor/dist/semifies')
10
11
 
11
12
  const istanbul = require('../../../../../vendor/dist/istanbul-lib-coverage')
@@ -77,6 +78,11 @@ const {
77
78
  *
78
79
  * @typedef {{ service?: string, isServiceUserProvided?: boolean }} TestEnvironmentConfig
79
80
  * @typedef {Record<string, string|number|undefined>} TestEnvironmentMetadata
81
+ * @typedef {{
82
+ * isRumActive?: boolean,
83
+ * browserVersion?: string,
84
+ * testExecutionId?: string
85
+ * }} RumTestCorrelationContext
80
86
  */
81
87
 
82
88
  // session tags
@@ -130,7 +136,7 @@ const CI_APP_ORIGIN = 'ciapp-test'
130
136
  // - Math.random() float values (10+ decimal digits after 0.)
131
137
  const DYNAMIC_NAME_RE = new RegExp(
132
138
  String.raw`\b1[6-9]\d{8,11}\b|` +
133
- String.raw`[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|` +
139
+ '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|' +
134
140
  String.raw`\b\d{4}-\d{2}-\d{2}|` +
135
141
  String.raw`(?:localhost|127\.0\.0\.1|0\.0\.0\.0):\d{4,5}\b|` +
136
142
  String.raw`\b0\.\d{10,}`,
@@ -434,6 +440,8 @@ module.exports = {
434
440
  TEST_PARAMETERS,
435
441
  TEST_SKIP_REASON,
436
442
  TEST_IS_RUM_ACTIVE,
443
+ setRumTestCorrelation,
444
+ setRumTestTags,
437
445
  TEST_SOURCE_FILE,
438
446
  TEST_FAILURE_SCREENSHOT_UPLOADED,
439
447
  TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR,
@@ -567,7 +575,7 @@ module.exports = {
567
575
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
568
576
  function getPkgManager () {
569
577
  try {
570
- return getEnvironmentVariable('npm_config_user_agent').split(' ')[0].replace('/', '-')
578
+ return getSegment(getEnvironmentVariable('npm_config_user_agent'), ' ', 0).replace('/', '-')
571
579
  } catch {
572
580
  return ''
573
581
  }
@@ -798,6 +806,9 @@ function getTestTypeFromFramework (testFramework) {
798
806
  return 'test'
799
807
  }
800
808
 
809
+ /**
810
+ * @param {import('../../opentracing/span')} span
811
+ */
801
812
  function finishAllTraceSpans (span) {
802
813
  for (const traceSpan of span.context()._trace.started) {
803
814
  if (traceSpan !== span) {
@@ -806,6 +817,49 @@ function finishAllTraceSpans (span) {
806
817
  }
807
818
  }
808
819
 
820
+ /**
821
+ * @param {import('../../opentracing/span')} testSpan
822
+ * @param {boolean|undefined} isRumActive
823
+ * @param {string} [browserVersion]
824
+ * @returns {void}
825
+ */
826
+ function setRumTestTags (testSpan, isRumActive, browserVersion) {
827
+ if (isRumActive) {
828
+ testSpan.setTag(TEST_IS_RUM_ACTIVE, 'true')
829
+ }
830
+ if (browserVersion) {
831
+ testSpan.setTag(TEST_BROWSER_VERSION, browserVersion)
832
+ }
833
+ }
834
+
835
+ /**
836
+ * @param {RumTestCorrelationContext} context
837
+ * @param {import('../../opentracing/span')|undefined} activeSpan
838
+ * @returns {import('../../opentracing/span')|undefined}
839
+ */
840
+ function setRumTestCorrelation (context, activeSpan) {
841
+ if (!activeSpan) return
842
+
843
+ const activeContext = activeSpan.context()
844
+ let testSpan
845
+ if (activeContext.getTag(SPAN_TYPE) === 'test') {
846
+ testSpan = activeSpan
847
+ } else {
848
+ for (const traceSpan of activeContext._trace.started) {
849
+ if (traceSpan.context().getTag(SPAN_TYPE) === 'test') {
850
+ testSpan = traceSpan
851
+ break
852
+ }
853
+ }
854
+ }
855
+
856
+ if (!testSpan) return
857
+
858
+ context.testExecutionId = testSpan.context().toTraceId()
859
+ setRumTestTags(testSpan, context.isRumActive, context.browserVersion)
860
+ return testSpan
861
+ }
862
+
809
863
  function getTestParentSpan (tracer) {
810
864
  return tracer.extract('text_map', {
811
865
  'x-datadog-trace-id': id().toString(10),
@@ -1360,7 +1414,6 @@ function resetCoverage (coverage) {
1360
1414
 
1361
1415
  return coverageMap
1362
1416
  .files()
1363
- // eslint-disable-next-line unicorn/no-array-for-each
1364
1417
  .forEach(filename => {
1365
1418
  const fileCoverage = coverageMap.fileCoverageFor(filename)
1366
1419
  fileCoverage.resetHits()
@@ -1371,7 +1424,6 @@ function mergeCoverage (coverage, targetCoverage) {
1371
1424
  const coverageMap = istanbul.createCoverageMap(coverage)
1372
1425
  return coverageMap
1373
1426
  .files()
1374
- // eslint-disable-next-line unicorn/no-array-for-each
1375
1427
  .forEach(filename => {
1376
1428
  const fileCoverage = coverageMap.fileCoverageFor(filename)
1377
1429
 
@@ -1454,7 +1506,7 @@ function parseAnnotations (annotations) {
1454
1506
  * Returns 0 when the test is too slow to retry (≥ 5 min).
1455
1507
  *
1456
1508
  * @param {number} durationMs
1457
- * @param {Record<string, number>} slowTestRetries e.g. { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
1509
+ * @param {Record<string, number>} slowTestRetries e.g. { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
1458
1510
  * @returns {number}
1459
1511
  */
1460
1512
  function getEfdRetryCount (durationMs, slowTestRetries) {
@@ -1469,12 +1521,17 @@ function getEfdRetryCount (durationMs, slowTestRetries) {
1469
1521
  /**
1470
1522
  * Returns the maximum retry count configured by the backend for EFD.
1471
1523
  *
1472
- * @param {Record<string, number>} slowTestRetries e.g. { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
1473
- * @returns {number}
1524
+ * @param {Record<string, number> | undefined} slowTestRetries
1525
+ * @returns {number | undefined}
1474
1526
  */
1475
1527
  function getMaxEfdRetryCount (slowTestRetries) {
1528
+ if (slowTestRetries === undefined) return
1529
+
1530
+ const retryCounts = Object.values(slowTestRetries)
1531
+ if (retryCounts.length === 0) return
1532
+
1476
1533
  let maxRetries = 0
1477
- for (const retryCount of Object.values(slowTestRetries || {})) {
1534
+ for (const retryCount of retryCounts) {
1478
1535
  if (retryCount > maxRetries) {
1479
1536
  maxRetries = retryCount
1480
1537
  }
@@ -185,6 +185,18 @@ const web = {
185
185
  return context
186
186
  },
187
187
 
188
+ // Key an existing context on the underlying `Http2Stream` so a second request
189
+ // backed by the same stream resolves to it via the `req.stream` lookup in
190
+ // `patch`. Only a mixed HTTP/2 server (raw-stream + 'request' listeners) needs
191
+ // this: it creates the span from a throwaway stream adapter, then the
192
+ // compatibility layer synthesizes the real `Http2ServerRequest` off the same
193
+ // stream and must reuse the adapter's span rather than open an orphan context.
194
+ // Confined to that path so the common single-listener request pays no extra
195
+ // per-request map write.
196
+ linkContextToStream (stream, context) {
197
+ contexts.set(stream, context)
198
+ },
199
+
188
200
  // Return the request root span.
189
201
  root (req) {
190
202
  const context = contexts.get(req)