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
@@ -9,6 +9,23 @@ const FIELD_HAS = 4
9
9
  const FIELD_DELETE = 8
10
10
  const FIELD_NAME_LENGTH = 16
11
11
 
12
+ const b3FlagsHeader = 'x-b3-flags'
13
+ const b3Header = 'b3'
14
+ const b3SampledHeader = 'x-b3-sampled'
15
+ const b3SpanIdHeader = 'x-b3-spanid'
16
+ const b3TraceIdHeader = 'x-b3-traceid'
17
+ const baggageHeader = 'baggage'
18
+ const datadogOriginHeader = 'x-datadog-origin'
19
+ const datadogParentIdHeader = 'x-datadog-parent-id'
20
+ const datadogSamplingPriorityHeader = 'x-datadog-sampling-priority'
21
+ const datadogTagsHeader = 'x-datadog-tags'
22
+ const datadogTraceIdHeader = 'x-datadog-trace-id'
23
+ const dsmBase64Header = 'dd-pathway-ctx-base64'
24
+ const dsmBinaryHeader = 'dd-pathway-ctx'
25
+ const sqsdHeader = 'x-aws-sqsd-attr-_datadog'
26
+ const traceparentHeader = 'traceparent'
27
+ const tracestateHeader = 'tracestate'
28
+
12
29
  /** @type {Record<string, unknown>} */
13
30
  const carrierOperations = {}
14
31
 
@@ -24,7 +41,7 @@ const carrierOperations = {}
24
41
  /**
25
42
  * @template Value
26
43
  * @typedef {object} CarrierField
27
- * @property {(carrier: Carrier) => Value | undefined} read
44
+ * @property {((carrier: Carrier) => Value | undefined) | undefined} read
28
45
  * @property {(carrier: Carrier, value: Value) => void} write
29
46
  * @property {(carrier: Carrier) => boolean} has
30
47
  * @property {(carrier: Carrier) => void} delete
@@ -53,19 +70,13 @@ function defineCapability (name, operations, capability) {
53
70
  * @template Value
54
71
  * @param {string} fieldName
55
72
  * @param {string} headerName
56
- * @param {(value: unknown) => Value | undefined} resolve
73
+ * @param {((carrier: Carrier) => Value | undefined) | undefined} read
57
74
  * @param {number} operations
58
75
  * @returns {CarrierField<Value>}
59
76
  */
60
- function defineField (fieldName, headerName, resolve, operations) {
77
+ function defineField (fieldName, headerName, read, operations) {
61
78
  return defineCapability(fieldName, operations, {
62
- /**
63
- * @param {Carrier} carrier
64
- * @returns {Value | undefined}
65
- */
66
- read (carrier) {
67
- return resolve(carrier[headerName])
68
- },
79
+ read,
69
80
 
70
81
  /**
71
82
  * @param {Carrier} carrier
@@ -183,48 +194,96 @@ function readLastPathway (value) {
183
194
  return result
184
195
  }
185
196
 
197
+ /** @param {Carrier} carrier */
198
+ function readB3Flags (carrier) { return readLastText(carrier[b3FlagsHeader]) }
199
+
200
+ /** @param {Carrier} carrier */
201
+ function readB3 (carrier) { return readLastText(carrier[b3Header]) }
202
+
203
+ /** @param {Carrier} carrier */
204
+ function readB3Sampled (carrier) { return readLastText(carrier[b3SampledHeader]) }
205
+
206
+ /** @param {Carrier} carrier */
207
+ function readB3SpanId (carrier) { return readLastText(carrier[b3SpanIdHeader]) }
208
+
209
+ /** @param {Carrier} carrier */
210
+ function readB3TraceId (carrier) { return readLastText(carrier[b3TraceIdHeader]) }
211
+
212
+ /** @param {Carrier} carrier */
213
+ function readBaggage (carrier) { return readListText(carrier[baggageHeader]) }
214
+
215
+ /** @param {Carrier} carrier */
216
+ function readDatadogOrigin (carrier) { return readLastText(carrier[datadogOriginHeader]) }
217
+
218
+ /** @param {Carrier} carrier */
219
+ function readDatadogParentId (carrier) { return readLastText(carrier[datadogParentIdHeader]) }
220
+
221
+ /** @param {Carrier} carrier */
222
+ function readDatadogSamplingPriority (carrier) { return readLastText(carrier[datadogSamplingPriorityHeader]) }
223
+
224
+ /** @param {Carrier} carrier */
225
+ function readDatadogTags (carrier) { return readListText(carrier[datadogTagsHeader]) }
226
+
227
+ /** @param {Carrier} carrier */
228
+ function readDatadogTraceId (carrier) { return readLastText(carrier[datadogTraceIdHeader]) }
229
+
230
+ /** @param {Carrier} carrier */
231
+ function readDsmBase64 (carrier) { return readLastPathway(carrier[dsmBase64Header]) }
232
+
233
+ /** @param {Carrier} carrier */
234
+ function readDsmBinary (carrier) { return readLastPathway(carrier[dsmBinaryHeader]) }
235
+
236
+ /** @param {Carrier} carrier */
237
+ function readSqsd (carrier) { return readLastText(carrier[sqsdHeader]) }
238
+
239
+ /** @param {Carrier} carrier */
240
+ function readTraceparent (carrier) { return readUniqueText(carrier[traceparentHeader]) }
241
+
242
+ /** @param {Carrier} carrier */
243
+ function readTracestate (carrier) { return readListText(carrier[tracestateHeader]) }
244
+
186
245
  const datadogTraceId = defineField(
187
246
  'datadogTraceId',
188
- 'x-datadog-trace-id',
189
- readLastText,
247
+ datadogTraceIdHeader,
248
+ readDatadogTraceId,
190
249
  FIELD_READ | FIELD_WRITE
191
250
  )
192
251
  const datadogParentId = defineField(
193
252
  'datadogParentId',
194
- 'x-datadog-parent-id',
195
- readLastText,
253
+ datadogParentIdHeader,
254
+ readDatadogParentId,
196
255
  FIELD_READ | FIELD_WRITE | FIELD_DELETE
197
256
  )
198
257
  const datadogOrigin = defineField(
199
258
  'datadogOrigin',
200
- 'x-datadog-origin',
201
- readLastText,
259
+ datadogOriginHeader,
260
+ readDatadogOrigin,
202
261
  FIELD_READ | FIELD_WRITE
203
262
  )
204
263
  const datadogSamplingPriority = defineField(
205
264
  'datadogSamplingPriority',
206
- 'x-datadog-sampling-priority',
207
- readLastText,
265
+ datadogSamplingPriorityHeader,
266
+ readDatadogSamplingPriority,
208
267
  FIELD_READ | FIELD_WRITE
209
268
  )
210
- defineField('datadogTags', 'x-datadog-tags', readListText, FIELD_READ | FIELD_WRITE)
211
- defineField('baggage', 'baggage', readListText, FIELD_READ | FIELD_WRITE)
212
- const b3TraceId = defineField('b3TraceId', 'x-b3-traceid', readLastText, FIELD_READ | FIELD_WRITE)
213
- const b3SpanId = defineField('b3SpanId', 'x-b3-spanid', readLastText, FIELD_READ | FIELD_WRITE)
214
- const b3ParentId = defineField('b3ParentId', 'x-b3-parentspanid', readLastText, FIELD_WRITE)
215
- const b3Sampled = defineField('b3Sampled', 'x-b3-sampled', readLastText, FIELD_READ | FIELD_WRITE)
216
- const b3Flags = defineField('b3Flags', 'x-b3-flags', readLastText, FIELD_READ | FIELD_WRITE)
217
- const b3 = defineField('b3', 'b3', readLastText, FIELD_READ | FIELD_WRITE)
218
- defineField('sqsd', 'x-aws-sqsd-attr-_datadog', readLastText, FIELD_READ)
219
- const traceparent = defineField('traceparent', 'traceparent', readUniqueText, FIELD_READ | FIELD_WRITE)
220
- const tracestate = defineField('tracestate', 'tracestate', readListText, FIELD_READ | FIELD_WRITE)
269
+ defineField('datadogTags', datadogTagsHeader, readDatadogTags, FIELD_READ | FIELD_WRITE)
270
+ defineField('baggage', baggageHeader, readBaggage, FIELD_READ | FIELD_WRITE)
271
+ const b3TraceId = defineField('b3TraceId', b3TraceIdHeader, readB3TraceId, FIELD_READ | FIELD_WRITE)
272
+ const b3SpanId = defineField('b3SpanId', b3SpanIdHeader, readB3SpanId, FIELD_READ | FIELD_WRITE)
273
+ const b3ParentId = defineField('b3ParentId', 'x-b3-parentspanid', undefined, FIELD_WRITE)
274
+ const b3Sampled = defineField('b3Sampled', b3SampledHeader, readB3Sampled, FIELD_READ | FIELD_WRITE)
275
+ const b3Flags = defineField('b3Flags', b3FlagsHeader, readB3Flags, FIELD_READ | FIELD_WRITE)
276
+ const b3 = defineField('b3', b3Header, readB3, FIELD_READ | FIELD_WRITE)
277
+ defineField('sqsd', sqsdHeader, readSqsd, FIELD_READ)
278
+ const traceparent = defineField('traceparent', traceparentHeader, readTraceparent, FIELD_READ | FIELD_WRITE)
279
+ const tracestate = defineField('tracestate', tracestateHeader, readTracestate, FIELD_READ | FIELD_WRITE)
221
280
  const dsmBase64 = defineField(
222
281
  'dsmBase64',
223
- 'dd-pathway-ctx-base64',
224
- readLastPathway,
282
+ dsmBase64Header,
283
+ readDsmBase64,
225
284
  FIELD_READ | FIELD_WRITE | FIELD_HAS | FIELD_NAME_LENGTH
226
285
  )
227
- const dsmBinary = defineField('dsmBinary', 'dd-pathway-ctx', readLastPathway, FIELD_READ | FIELD_HAS)
286
+ const dsmBinary = defineField('dsmBinary', dsmBinaryHeader, readDsmBinary, FIELD_READ | FIELD_HAS)
228
287
 
229
288
  const legacyBaggagePrefix = 'ot-baggage-'
230
289
  // RFC 7230 `token`, compatible with Node.js header-name validation.
@@ -314,23 +373,23 @@ function pickDsm (carrier) {
314
373
  * @property {typeof dsmBinary.has} hasDsmBinary
315
374
  * @property {typeof pickDsm} pickDsm
316
375
  * @property {typeof pickTextMap} pickTextMap
317
- * @property {(carrier: Carrier) => string | undefined} readBaggage
318
- * @property {typeof b3.read} readB3
319
- * @property {typeof b3Flags.read} readB3Flags
320
- * @property {typeof b3Sampled.read} readB3Sampled
321
- * @property {typeof b3SpanId.read} readB3SpanId
322
- * @property {typeof b3TraceId.read} readB3TraceId
323
- * @property {typeof datadogOrigin.read} readDatadogOrigin
324
- * @property {typeof datadogParentId.read} readDatadogParentId
325
- * @property {typeof datadogSamplingPriority.read} readDatadogSamplingPriority
326
- * @property {(carrier: Carrier) => string | undefined} readDatadogTags
327
- * @property {typeof datadogTraceId.read} readDatadogTraceId
328
- * @property {typeof dsmBase64.read} readDsmBase64
329
- * @property {typeof dsmBinary.read} readDsmBinary
376
+ * @property {typeof readBaggage} readBaggage
377
+ * @property {typeof readB3} readB3
378
+ * @property {typeof readB3Flags} readB3Flags
379
+ * @property {typeof readB3Sampled} readB3Sampled
380
+ * @property {typeof readB3SpanId} readB3SpanId
381
+ * @property {typeof readB3TraceId} readB3TraceId
382
+ * @property {typeof readDatadogOrigin} readDatadogOrigin
383
+ * @property {typeof readDatadogParentId} readDatadogParentId
384
+ * @property {typeof readDatadogSamplingPriority} readDatadogSamplingPriority
385
+ * @property {typeof readDatadogTags} readDatadogTags
386
+ * @property {typeof readDatadogTraceId} readDatadogTraceId
387
+ * @property {typeof readDsmBase64} readDsmBase64
388
+ * @property {typeof readDsmBinary} readDsmBinary
330
389
  * @property {(carrier: Carrier, target: Record<string, string>) => void} readLegacyBaggage
331
- * @property {(carrier: Carrier) => string | undefined} readSqsd
332
- * @property {typeof traceparent.read} readTraceparent
333
- * @property {typeof tracestate.read} readTracestate
390
+ * @property {typeof readSqsd} readSqsd
391
+ * @property {typeof readTraceparent} readTraceparent
392
+ * @property {typeof readTracestate} readTracestate
334
393
  * @property {(carrier: Carrier, value: string) => void} writeBaggage
335
394
  * @property {typeof b3.write} writeB3
336
395
  * @property {typeof b3Flags.write} writeB3Flags
@@ -92,6 +92,7 @@ function removeEmptyCaptureProperties (value) {
92
92
  if (current.fields === null || (
93
93
  current.fields &&
94
94
  typeof current.fields === 'object' &&
95
+ // eslint-disable-next-line no-restricted-syntax -- snapshot field names are user variables; no key to probe
95
96
  Object.keys(current.fields).length === 0
96
97
  )) {
97
98
  delete current.fields
@@ -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,8 +14,8 @@ const {
14
14
  TELEMETRY_KNOWN_TESTS_RESPONSE_TESTS,
15
15
  TELEMETRY_KNOWN_TESTS_RESPONSE_BYTES,
16
16
  } = require('../../ci-visibility/telemetry')
17
-
18
17
  const { getNumFromKnownTests } = require('../../plugins/util/test')
18
+ const request = require('../requests/request')
19
19
  const { buildCacheKey, writeToCache, withCache } = require('../requests/fs-cache')
20
20
  const { validateKnownTestsResponse } = require('../test-optimization-http-cache-schema')
21
21
 
@@ -162,8 +162,8 @@ function fetchFromApi ({
162
162
  }
163
163
 
164
164
  if (isEvpProxy) {
165
- options.path = `${evpProxyPrefix}/api/v2/ci/libraries/tests`
166
- options.headers['X-Datadog-EVP-Subdomain'] = 'api'
165
+ options.path = joinEVPProxyPath(evpProxyPrefix, '/api/v2/ci/libraries/tests')
166
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'api'
167
167
  } else {
168
168
  const { DD_API_KEY } = getConfig()
169
169
  if (!DD_API_KEY) {
@@ -4,9 +4,12 @@ const AgentWriter = require('../../../exporters/agent/writer')
4
4
  const AgentlessWriter = require('../agentless/writer')
5
5
  const CoverageWriter = require('../agentless/coverage-writer')
6
6
  const CiVisibilityExporter = require('../ci-visibility-exporter')
7
+ const request = require('../request')
7
8
  const { fetchAgentInfo } = require('../../../agent/info')
8
9
  const { DEBUGGER_INPUT_V1 } = require('../../../debugger/constants')
9
10
 
11
+ // Product-specific discovery: newest advertised version, skip v3 (citestcycle), gzip if >= v4.
12
+ // Shared `evp_proxy` discovery is an explicit path allowlist and does not cover this contract.
10
13
  const AGENT_EVP_PROXY_PATH_PREFIX = '/evp_proxy/v'
11
14
  const AGENT_EVP_PROXY_PATH_REGEX = /\/evp_proxy\/v(\d+)\/?/
12
15
 
@@ -43,9 +46,21 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
43
46
  testOptimization,
44
47
  } = config
45
48
 
49
+ const initializationController = new AbortController()
50
+ const initializationOptions = { signal: initializationController.signal }
51
+ this._initializationRequest = {
52
+ controller: initializationController,
53
+ options: initializationOptions,
54
+ }
55
+
46
56
  fetchAgentInfo(this._url, (err, agentInfo) => {
57
+ const initializationFinalFlush = this._initializationRequest?.finalFlush
58
+ this._initializationRequest = undefined
59
+ const initializationAborted = initializationController.signal.aborted
60
+ const agentInfoError = err || (initializationAborted ? initializationController.signal.reason : undefined)
61
+
47
62
  this._isInitialized = true
48
- let latestEvpProxyVersion = getLatestEvpProxyVersion(err, agentInfo)
63
+ let latestEvpProxyVersion = getLatestEvpProxyVersion(agentInfoError, agentInfo)
49
64
  const isEvpCompatible = latestEvpProxyVersion >= 2
50
65
  this._isGzipCompatible = latestEvpProxyVersion >= 4
51
66
 
@@ -72,7 +87,7 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
72
87
  // path with evpProxyPrefix and sets X-Datadog-EVP-Subdomain: api (see uploadTestScreenshot).
73
88
  this._testScreenshotUploadUrl = this._url
74
89
  if (testOptimization.DD_TEST_FAILED_TEST_REPLAY_ENABLED) {
75
- const canFowardLogs = getCanForwardDebuggerLogs(err, agentInfo)
90
+ const canFowardLogs = getCanForwardDebuggerLogs(agentInfoError, agentInfo)
76
91
  if (canFowardLogs) {
77
92
  const DynamicInstrumentationLogsWriter = require('../agentless/di-logs-writer')
78
93
  this._logsWriter = new DynamicInstrumentationLogsWriter({
@@ -89,14 +104,25 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
89
104
  lookup,
90
105
  protocolVersion,
91
106
  headers,
107
+ isTestOptimization: true,
92
108
  })
93
109
  // coverages will never be used, so we discard them
94
110
  this._coverageBuffer = []
95
111
  }
96
112
  this._resolveCanUseCiVisProtocol(isEvpCompatible)
113
+ if (initializationAborted) {
114
+ this.resetUncodedTraces()
115
+ this.resetDeferredTestSuiteSpans()
116
+ return
117
+ }
118
+ if (isEvpCompatible) {
119
+ if (initializationFinalFlush) this.exportDeferredTestSuiteSpans()
120
+ } else {
121
+ this.resetDeferredTestSuiteSpans()
122
+ }
97
123
  this.exportUncodedTraces()
98
124
  this.exportUncodedCoverages()
99
- })
125
+ }, initializationOptions, request)
100
126
  }
101
127
 
102
128
  setUrl (url, coverageUrl) {
@@ -1,11 +1,10 @@
1
1
  'use strict'
2
2
  const getConfig = require('../../../config')
3
- const request = require('../../../exporters/common/request')
4
- const log = require('../../../log')
3
+ const { EVP_SUBDOMAIN_HEADER_NAME } = require('../../../evp_proxy/constants')
4
+ const { joinEVPProxyPath } = require('../../../evp_proxy/path')
5
5
  const { safeJSONStringify } = require('../../../exporters/common/util')
6
+ const log = require('../../../log')
6
7
 
7
- const { CoverageCIVisibilityEncoder } = require('../../../encode/coverage-ci-visibility')
8
- const BaseWriter = require('../../../exporters/common/writer')
9
8
  const {
10
9
  incrementCountMetric,
11
10
  distributionMetric,
@@ -15,16 +14,34 @@ const {
15
14
  TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
16
15
  TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
17
16
  } = require('../../../ci-visibility/telemetry')
17
+ const { CoverageCIVisibilityEncoder } = require('../../../encode/coverage-ci-visibility')
18
+ const BaseWriter = require('../../../exporters/common/writer')
19
+ const request = require('../request')
20
+ const TestOptimizationRequestTracker = require('./request-tracker')
18
21
 
19
22
  class Writer extends BaseWriter {
23
+ #requestTracker
24
+
20
25
  constructor ({ url, evpProxyPrefix = '' }) {
21
26
  super(...arguments)
27
+ this.#requestTracker = new TestOptimizationRequestTracker(this)
22
28
  this._url = url
23
29
  this._encoder = new CoverageCIVisibilityEncoder(this)
24
30
  this._evpProxyPrefix = evpProxyPrefix
25
31
  }
26
32
 
27
- _sendPayload (form, _, done) {
33
+ /**
34
+ * Flushes buffered coverage, waiting for tracked requests during finalization.
35
+ *
36
+ * @param {(error?: Error) => void} [done]
37
+ * @param {{ deadline?: number }} [options]
38
+ * @returns {void}
39
+ */
40
+ flush (done, options) {
41
+ this.#requestTracker.flush(done, options)
42
+ }
43
+
44
+ _sendPayload (form, _, done, flushOptions) {
28
45
  const options = {
29
46
  path: '/api/v2/citestcov',
30
47
  method: 'POST',
@@ -34,12 +51,13 @@ class Writer extends BaseWriter {
34
51
  },
35
52
  timeout: 15_000,
36
53
  url: this._url,
54
+ deadline: flushOptions?.deadline,
37
55
  }
38
56
 
39
57
  if (this._evpProxyPrefix) {
40
- options.path = `${this._evpProxyPrefix}/api/v2/citestcov`
58
+ options.path = joinEVPProxyPath(this._evpProxyPrefix, '/api/v2/citestcov')
41
59
  delete options.headers['dd-api-key']
42
- options.headers['X-Datadog-EVP-Subdomain'] = 'citestcov-intake'
60
+ options.headers[EVP_SUBDOMAIN_HEADER_NAME] = 'citestcov-intake'
43
61
  }
44
62
 
45
63
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
@@ -50,7 +68,7 @@ class Writer extends BaseWriter {
50
68
  incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'code_coverage' })
51
69
  distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'code_coverage' }, form.size())
52
70
 
53
- request(form, options, (err, res, statusCode) => {
71
+ this.#requestTracker.send(request, form, options, (err, res, statusCode) => {
54
72
  distributionMetric(
55
73
  TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
56
74
  { endpoint: 'code_coverage' },
@@ -66,7 +84,7 @@ class Writer extends BaseWriter {
66
84
  { endpoint: 'code_coverage' }
67
85
  )
68
86
  log.error('Error sending CI coverage payload', err)
69
- done()
87
+ done(err)
70
88
  return
71
89
  }
72
90
  log.debug('Response from the intake:', res)
@@ -1,27 +1,42 @@
1
1
  'use strict'
2
2
  const getConfig = require('../../../config')
3
- const request = require('../../../exporters/common/request')
4
3
  const log = require('../../../log')
5
4
  const { safeJSONStringify } = require('../../../exporters/common/util')
6
5
  const { JSONEncoder } = require('../../encode/json-encoder')
7
6
  const { DEBUGGER_INPUT_V1 } = require('../../../debugger/constants')
8
-
9
7
  const BaseWriter = require('../../../exporters/common/writer')
10
8
 
9
+ const request = require('../request')
10
+ const TestOptimizationRequestTracker = require('./request-tracker')
11
+
11
12
  // Writer used by the integration between Dynamic Instrumentation and Test Visibility
12
13
  // It is used to encode and send logs to both the logs intake directly and the
13
14
  // `/debugger/v1/input` endpoint in the agent, which is a proxy to the logs intake.
14
15
  class DynamicInstrumentationLogsWriter extends BaseWriter {
16
+ #requestTracker
17
+
15
18
  // TODO: what's a good value for timeout for the logs intake?
16
19
  constructor ({ url, timeout = 15_000, isAgentProxy = false }) {
17
20
  super(...arguments)
21
+ this.#requestTracker = new TestOptimizationRequestTracker(this)
18
22
  this._url = url
19
23
  this._encoder = new JSONEncoder()
20
24
  this._isAgentProxy = isAgentProxy
21
25
  this.timeout = timeout
22
26
  }
23
27
 
24
- _sendPayload (data, _, done) {
28
+ /**
29
+ * Flushes buffered logs, waiting for tracked requests during finalization.
30
+ *
31
+ * @param {(error?: Error) => void} [done]
32
+ * @param {{ deadline?: number }} [options]
33
+ * @returns {void}
34
+ */
35
+ flush (done, options) {
36
+ this.#requestTracker.flush(done, options)
37
+ }
38
+
39
+ _sendPayload (data, _, done, flushOptions) {
25
40
  const options = {
26
41
  path: '/api/v2/logs',
27
42
  method: 'POST',
@@ -31,6 +46,7 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
31
46
  },
32
47
  timeout: this.timeout,
33
48
  url: this._url,
49
+ deadline: flushOptions?.deadline,
34
50
  }
35
51
 
36
52
  if (this._isAgentProxy) {
@@ -41,10 +57,10 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
41
57
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
42
58
  log.debug(() => `Request to the logs intake: ${safeJSONStringify(options)}`)
43
59
 
44
- request(data, options, (err, res) => {
60
+ this.#requestTracker.send(request, data, options, (err, res) => {
45
61
  if (err) {
46
62
  log.error('Error sending DI logs payload', err)
47
- done()
63
+ done(err)
48
64
  return
49
65
  }
50
66
  log.debug('Response from the logs intake:', res)
@@ -0,0 +1,164 @@
1
+ 'use strict'
2
+
3
+ const BaseWriter = require('../../../exporters/common/writer')
4
+
5
+ const FINAL_FLUSH_TIMEOUT_CODE = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
6
+
7
+ class TestOptimizationRequestTracker {
8
+ #writer
9
+ #pendingRequests = new Set()
10
+ #finalFlushes = new Set()
11
+ #activeFinalFlush
12
+
13
+ /**
14
+ * Creates request tracking for a Test Optimization writer.
15
+ *
16
+ * @param {BaseWriter} writer
17
+ */
18
+ constructor (writer) {
19
+ this.#writer = writer
20
+ }
21
+
22
+ /**
23
+ * Flushes queued payloads and, for a final flush, waits for requests that were
24
+ * already in flight. The absolute deadline prevents Test Optimization from
25
+ * keeping the test process alive indefinitely.
26
+ *
27
+ * @param {(error?: Error) => void} [done]
28
+ * @param {{ deadline?: number }} [options]
29
+ * @returns {void}
30
+ */
31
+ flush (done, options) {
32
+ if (options?.deadline === undefined) {
33
+ BaseWriter.prototype.flush.call(this.#writer, done, options)
34
+ return
35
+ }
36
+
37
+ const finalFlush = {
38
+ deadline: options.deadline,
39
+ done: done || (() => {}),
40
+ error: undefined,
41
+ requests: new Set(),
42
+ timeoutId: undefined,
43
+ writerDone: false,
44
+ }
45
+ this.#finalFlushes.add(finalFlush)
46
+
47
+ for (const pendingRequest of this.#pendingRequests) {
48
+ this.#attachRequest(finalFlush, pendingRequest)
49
+ }
50
+
51
+ const remaining = Math.max(0, options.deadline - Date.now())
52
+ finalFlush.timeoutId = setTimeout(() => {
53
+ const error = new Error('Timed out flushing Test Optimization data')
54
+ error.code = FINAL_FLUSH_TIMEOUT_CODE
55
+
56
+ finalFlush.error ||= error
57
+ finalFlush.writerDone = true
58
+
59
+ for (const pendingRequest of finalFlush.requests) {
60
+ pendingRequest.finalFlushes.delete(finalFlush)
61
+ if (pendingRequest.finalFlushes.size === 0) {
62
+ pendingRequest.controller.abort(error)
63
+ this.#pendingRequests.delete(pendingRequest)
64
+ } else {
65
+ this.#updateRequestDeadline(pendingRequest)
66
+ }
67
+ }
68
+ finalFlush.requests.clear()
69
+ this.#finishFinalFlush(finalFlush)
70
+ }, remaining)
71
+
72
+ const previousFinalFlush = this.#activeFinalFlush
73
+ this.#activeFinalFlush = finalFlush
74
+ try {
75
+ BaseWriter.prototype.flush.call(this.#writer, (error) => {
76
+ finalFlush.error ||= error
77
+ finalFlush.writerDone = true
78
+ this.#finishFinalFlush(finalFlush)
79
+ }, options)
80
+ } finally {
81
+ this.#activeFinalFlush = previousFinalFlush
82
+ }
83
+ this.#finishFinalFlush(finalFlush)
84
+ }
85
+
86
+ /**
87
+ * Sends and tracks a request so a later final flush can wait for or abort it.
88
+ *
89
+ * @param {Function} request
90
+ * @param {Buffer|string|object} data
91
+ * @param {object} options
92
+ * @param {(error: Error|null, result?: string|null, statusCode?: number,
93
+ * headers?: import('node:http').IncomingHttpHeaders) => void} callback
94
+ * @returns {void}
95
+ */
96
+ send (request, data, options, callback) {
97
+ const controller = new AbortController()
98
+ const requestOptions = { ...options, signal: controller.signal }
99
+ const pendingRequest = { controller, finalFlushes: new Set(), options: requestOptions }
100
+ this.#pendingRequests.add(pendingRequest)
101
+ if (this.#activeFinalFlush) this.#attachRequest(this.#activeFinalFlush, pendingRequest)
102
+
103
+ request(data, requestOptions, (error, result, statusCode, headers) => {
104
+ if (error) {
105
+ for (const finalFlush of pendingRequest.finalFlushes) finalFlush.error ||= error
106
+ }
107
+
108
+ try {
109
+ callback(error, result, statusCode, headers)
110
+ } finally {
111
+ this.#pendingRequests.delete(pendingRequest)
112
+ for (const finalFlush of pendingRequest.finalFlushes) {
113
+ finalFlush.requests.delete(pendingRequest)
114
+ this.#finishFinalFlush(finalFlush)
115
+ }
116
+ pendingRequest.finalFlushes.clear()
117
+ }
118
+ })
119
+ }
120
+
121
+ /**
122
+ * Associates a request with the final flush boundary that must wait for it.
123
+ *
124
+ * @param {object} finalFlush
125
+ * @param {object} pendingRequest
126
+ * @returns {void}
127
+ */
128
+ #attachRequest (finalFlush, pendingRequest) {
129
+ finalFlush.requests.add(pendingRequest)
130
+ pendingRequest.finalFlushes.add(finalFlush)
131
+ this.#updateRequestDeadline(pendingRequest)
132
+ }
133
+
134
+ /**
135
+ * Gives a shared request the latest deadline of the flushes waiting for it.
136
+ *
137
+ * @param {object} pendingRequest
138
+ * @returns {void}
139
+ */
140
+ #updateRequestDeadline (pendingRequest) {
141
+ let deadline = 0
142
+ for (const finalFlush of pendingRequest.finalFlushes) {
143
+ deadline = Math.max(deadline, finalFlush.deadline)
144
+ }
145
+ pendingRequest.options.deadline = deadline
146
+ }
147
+
148
+ /**
149
+ * Completes a final flush callback once its writer and associated requests
150
+ * have settled.
151
+ *
152
+ * @param {object} finalFlush
153
+ * @returns {void}
154
+ */
155
+ #finishFinalFlush (finalFlush) {
156
+ if (!this.#finalFlushes.has(finalFlush) || !finalFlush.writerDone || finalFlush.requests.size !== 0) return
157
+
158
+ this.#finalFlushes.delete(finalFlush)
159
+ clearTimeout(finalFlush.timeoutId)
160
+ finalFlush.done(finalFlush.error)
161
+ }
162
+ }
163
+
164
+ module.exports = TestOptimizationRequestTracker