dd-trace 6.12.0 → 6.14.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 (251) hide show
  1. package/LICENSE-3rdparty.csv +3 -1
  2. package/ci/init.js +8 -3
  3. package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
  4. package/ci/test-optimization-validation/ci-remediation.js +6 -6
  5. package/ci/test-optimization-validation/command-blocker.js +7 -5
  6. package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
  7. package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
  8. package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
  9. package/ci/test-optimization-validation/package-check.js +1 -1
  10. package/ci/test-optimization-validation/report-writer.js +7 -5
  11. package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
  12. package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
  13. package/ci/vitest-no-worker-init-setup.mjs +13 -13
  14. package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
  15. package/index.d.ts +127 -26
  16. package/package.json +22 -19
  17. package/packages/datadog-esbuild/index.js +3 -1
  18. package/packages/datadog-instrumentations/src/bunyan.js +4 -2
  19. package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
  20. package/packages/datadog-instrumentations/src/fs.js +24 -20
  21. package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
  22. package/packages/datadog-instrumentations/src/graphql.js +16 -16
  23. package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
  24. package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
  25. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  26. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
  32. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  33. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
  34. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
  35. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
  36. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
  37. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
  38. package/packages/datadog-instrumentations/src/jest.js +165 -38
  39. package/packages/datadog-instrumentations/src/knex.js +76 -0
  40. package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
  41. package/packages/datadog-instrumentations/src/mariadb.js +13 -0
  42. package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
  43. package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
  44. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
  45. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
  46. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
  47. package/packages/datadog-instrumentations/src/net.js +14 -17
  48. package/packages/datadog-instrumentations/src/next.js +212 -62
  49. package/packages/datadog-instrumentations/src/openai.js +42 -40
  50. package/packages/datadog-instrumentations/src/pg.js +15 -0
  51. package/packages/datadog-instrumentations/src/pino.js +15 -4
  52. package/packages/datadog-instrumentations/src/playwright.js +137 -27
  53. package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
  54. package/packages/datadog-instrumentations/src/sequelize.js +94 -2
  55. package/packages/datadog-instrumentations/src/undici.js +53 -4
  56. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
  57. package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
  58. package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
  59. package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
  60. package/packages/datadog-instrumentations/src/winston.js +6 -3
  61. package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
  62. package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
  63. package/packages/datadog-plugin-amqp10/src/util.js +2 -1
  64. package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
  65. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
  66. package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
  67. package/packages/datadog-plugin-cucumber/src/index.js +0 -2
  68. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
  69. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  70. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
  71. package/packages/datadog-plugin-graphql/src/execute.js +998 -196
  72. package/packages/datadog-plugin-graphql/src/index.js +27 -7
  73. package/packages/datadog-plugin-graphql/src/jit.js +136 -0
  74. package/packages/datadog-plugin-graphql/src/request.js +18 -27
  75. package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
  76. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
  77. package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
  78. package/packages/datadog-plugin-graphql/src/utils.js +208 -120
  79. package/packages/datadog-plugin-graphql/src/validate.js +10 -23
  80. package/packages/datadog-plugin-http/src/client.js +2 -15
  81. package/packages/datadog-plugin-http2/src/client.js +2 -15
  82. package/packages/datadog-plugin-jest/src/index.js +0 -6
  83. package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
  84. package/packages/datadog-plugin-mocha/src/index.js +403 -75
  85. package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
  86. package/packages/datadog-plugin-mysql/src/index.js +41 -15
  87. package/packages/datadog-plugin-net/src/tcp.js +3 -1
  88. package/packages/datadog-plugin-next/src/index.js +63 -29
  89. package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
  90. package/packages/datadog-plugin-playwright/src/index.js +176 -28
  91. package/packages/datadog-plugin-undici/src/index.js +29 -56
  92. package/packages/datadog-plugin-vitest/src/index.js +6 -12
  93. package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
  94. package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
  95. package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
  96. package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
  97. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
  98. package/packages/dd-trace/src/aiguard/noop.js +9 -1
  99. package/packages/dd-trace/src/aiguard/redaction.js +183 -0
  100. package/packages/dd-trace/src/aiguard/sdk.js +7 -2
  101. package/packages/dd-trace/src/aiguard/tags.js +2 -0
  102. package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
  103. package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
  104. package/packages/dd-trace/src/appsec/graphql.js +6 -2
  105. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
  106. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
  107. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
  108. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
  109. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
  110. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
  111. package/packages/dd-trace/src/appsec/lambda.js +44 -26
  112. package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
  113. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
  114. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
  115. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
  116. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
  117. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
  118. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
  119. package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
  120. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
  121. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
  122. package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
  123. package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
  124. package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
  125. package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
  126. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
  127. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
  128. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
  129. package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
  130. package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
  131. package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
  132. package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
  133. package/packages/dd-trace/src/config/index.js +75 -14
  134. package/packages/dd-trace/src/config/parsers.js +11 -1
  135. package/packages/dd-trace/src/config/stable.js +3 -3
  136. package/packages/dd-trace/src/config/supported-configurations.json +47 -2
  137. package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
  138. package/packages/dd-trace/src/debugger/config.js +12 -1
  139. package/packages/dd-trace/src/debugger/constants.js +1 -0
  140. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
  141. package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
  142. package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
  143. package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
  144. package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
  145. package/packages/dd-trace/src/debugger/index.js +23 -3
  146. package/packages/dd-trace/src/dogstatsd.js +532 -71
  147. package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
  148. package/packages/dd-trace/src/encode/0.4.js +5 -5
  149. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
  150. package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
  151. package/packages/dd-trace/src/exporters/agent/index.js +24 -3
  152. package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
  153. package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
  154. package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
  155. package/packages/dd-trace/src/exporters/common/agents.js +28 -6
  156. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
  157. package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
  158. package/packages/dd-trace/src/exporters/common/request.js +31 -11
  159. package/packages/dd-trace/src/exporters/common/url.js +40 -1
  160. package/packages/dd-trace/src/exporters/common/writer.js +31 -7
  161. package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
  162. package/packages/dd-trace/src/external-logger/src/index.js +4 -3
  163. package/packages/dd-trace/src/flush.js +105 -0
  164. package/packages/dd-trace/src/id.js +14 -0
  165. package/packages/dd-trace/src/index.js +5 -2
  166. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
  167. package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
  168. package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
  169. package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
  170. package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
  171. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  172. package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
  173. package/packages/dd-trace/src/llmobs/index.js +111 -48
  174. package/packages/dd-trace/src/llmobs/noop.js +2 -1
  175. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
  176. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
  177. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
  178. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
  179. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
  180. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
  181. package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
  182. package/packages/dd-trace/src/llmobs/util.js +0 -33
  183. package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
  184. package/packages/dd-trace/src/log/index.js +7 -1
  185. package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
  186. package/packages/dd-trace/src/noop/span.js +5 -0
  187. package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
  188. package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
  189. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  190. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
  191. package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
  192. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
  193. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
  194. package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
  195. package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
  196. package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
  197. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
  198. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
  199. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
  200. package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
  201. package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
  202. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  203. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
  204. package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
  205. package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
  206. package/packages/dd-trace/src/opentracing/span.js +28 -0
  207. package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
  208. package/packages/dd-trace/src/payload-tagging/index.js +2 -1
  209. package/packages/dd-trace/src/plugin_manager.js +2 -0
  210. package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
  211. package/packages/dd-trace/src/plugins/tracing.js +12 -10
  212. package/packages/dd-trace/src/plugins/util/ci.js +3 -1
  213. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
  214. package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
  215. package/packages/dd-trace/src/plugins/util/test.js +157 -41
  216. package/packages/dd-trace/src/plugins/util/url.js +2 -2
  217. package/packages/dd-trace/src/plugins/util/web.js +6 -74
  218. package/packages/dd-trace/src/profiling/config.js +1 -0
  219. package/packages/dd-trace/src/profiling/oom.js +11 -5
  220. package/packages/dd-trace/src/profiling/profiler.js +30 -13
  221. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  222. package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
  223. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
  224. package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
  225. package/packages/dd-trace/src/proxy.js +108 -10
  226. package/packages/dd-trace/src/remote_config/index.js +319 -101
  227. package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
  228. package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
  229. package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
  230. package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
  231. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
  232. package/packages/dd-trace/src/sampling_rule.js +36 -9
  233. package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
  234. package/packages/dd-trace/src/serverless/vercel.js +155 -0
  235. package/packages/dd-trace/src/serverless.js +45 -21
  236. package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
  237. package/packages/dd-trace/src/span_stats.js +64 -31
  238. package/packages/dd-trace/src/startup-log.js +3 -2
  239. package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
  240. package/packages/dd-trace/src/telemetry/send-data.js +18 -16
  241. package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
  242. package/packages/dd-trace/src/tracer.js +26 -5
  243. package/packages/dd-trace/src/tracer_metadata.js +11 -3
  244. package/packages/dd-trace/src/web-tags-cache.js +67 -13
  245. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
  246. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  247. package/vendor/dist/protobufjs/index.js +1 -1
  248. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  249. package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
  250. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
  251. package/packages/dd-trace/src/encode/agentless-json.js +0 -209
@@ -108,7 +108,7 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
108
108
  value = JSON.stringify(cloneObj, null, 2)
109
109
 
110
110
  if (counter > 0) {
111
- const segments = []
111
+ let formattedValue = ''
112
112
  let outputLength = 0
113
113
  let pos = 0
114
114
  let rangeKeyIndex = value.indexOf(STRINGIFY_RANGE_KEY)
@@ -133,13 +133,14 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
133
133
  start,
134
134
  end: start + originalValue.length,
135
135
  })
136
- segments.push(value.slice(pos, rangeKeyIndex), originalValue)
136
+ formattedValue += value.slice(pos, rangeKeyIndex)
137
+ formattedValue += originalValue
137
138
  outputLength += cleanLength + originalValue.length
138
139
  pos = rangeKeyIndex + matchValue.length
139
140
  } else {
140
141
  // can't happen, the only way to this to happen is
141
142
  // if the JSON has a value starting with the value of STRINGIFY_SENSITIVE_NOT_STRING_KEY
142
- segments.push(value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1))
143
+ formattedValue += value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1)
143
144
  outputLength += cleanLength + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1
144
145
  pos = rangeKeyIndex + STRINGIFY_SENSITIVE_NOT_STRING_KEY.length + 1
145
146
  }
@@ -152,13 +153,13 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
152
153
  start,
153
154
  end: start + Number.parseInt(regexRes[1], 10),
154
155
  })
155
- segments.push(value.slice(pos, rangeKeyIndex))
156
+ formattedValue += value.slice(pos, rangeKeyIndex)
156
157
  outputLength += cleanLength
157
158
  pos = rangeKeyIndex + regexRes[0].length
158
159
  } else {
159
160
  // can't happen, the only way to this to happen is
160
161
  // if the JSON has a value starting with the value of STRINGIFY_SENSITIVE_KEY
161
- segments.push(value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length))
162
+ formattedValue += value.slice(pos, rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length)
162
163
  outputLength += cleanLength + STRINGIFY_SENSITIVE_KEY.length
163
164
  pos = rangeKeyIndex + STRINGIFY_SENSITIVE_KEY.length
164
165
  }
@@ -175,13 +176,13 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
175
176
  }))
176
177
  ranges.push(...updatedRanges)
177
178
 
178
- segments.push(value.slice(pos, rangeKeyIndex))
179
+ formattedValue += value.slice(pos, rangeKeyIndex)
179
180
  outputLength += cleanLength
180
181
  pos = rangeKeyIndex + regexRes[0].length
181
182
  } else {
182
183
  // can't happen, the only way to this to happen is
183
184
  // if the JSON has a value starting with the value of STRINGIFY_RANGE_KEY
184
- segments.push(value.slice(pos, rangeKeyIndex + STRINGIFY_RANGE_KEY.length))
185
+ formattedValue += value.slice(pos, rangeKeyIndex + STRINGIFY_RANGE_KEY.length)
185
186
  outputLength += cleanLength + STRINGIFY_RANGE_KEY.length
186
187
  pos = rangeKeyIndex + STRINGIFY_RANGE_KEY.length
187
188
  }
@@ -190,8 +191,8 @@ function stringifyWithRanges (obj, objRanges, loadSensitiveRanges = false) {
190
191
  rangeKeyIndex = value.indexOf(STRINGIFY_RANGE_KEY, pos)
191
192
  }
192
193
 
193
- segments.push(value.slice(pos))
194
- value = segments.join('')
194
+ formattedValue += value.slice(pos)
195
+ value = formattedValue
195
196
  }
196
197
  } else {
197
198
  value = JSON.stringify(obj, null, 2)
@@ -3,7 +3,9 @@
3
3
  const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
4
4
 
5
5
  const log = require('../log')
6
+ const { isEmpty } = require('../util')
6
7
  const addresses = require('./addresses')
8
+ const apiSecurity = require('./api_security')
7
9
  const Reporter = require('./reporter')
8
10
  const waf = require('./waf')
9
11
 
@@ -22,7 +24,7 @@ const activeInvocations = new WeakMap()
22
24
  */
23
25
  function onLambdaStartInvocation (data) {
24
26
  try {
25
- const { span, headers, method, path, query, body, clientIp, pathParams, cookies } = data
27
+ const { span, headers, method, path, query, body, clientIp, pathParams, cookies, route } = data
26
28
 
27
29
  if (!span) {
28
30
  log.warn('[ASM] No span provided in Lambda start invocation')
@@ -30,7 +32,7 @@ function onLambdaStartInvocation (data) {
30
32
  }
31
33
 
32
34
  const req = { headers: headers ?? {} }
33
- activeInvocations.set(span, req)
35
+ activeInvocations.set(span, { req, method, route })
34
36
 
35
37
  span.addTags({
36
38
  '_dd.appsec.enabled': 1,
@@ -80,8 +82,8 @@ function onLambdaStartInvocation (data) {
80
82
  }
81
83
 
82
84
  /**
83
- * Maps response data to WAF addresses, runs a final WAF pass,
84
- * disposes the WAF context, and finishes the request report.
85
+ * Maps response data to WAF addresses, takes the API Security sampling decision, runs a final
86
+ * WAF pass, disposes the WAF context, and finishes the request report.
85
87
  *
86
88
  * @param {{ span: object, statusCode: string | undefined,
87
89
  * responseHeaders: Record<string, string> | undefined }} data
@@ -99,31 +101,47 @@ function onLambdaEndInvocation (data) {
99
101
  return
100
102
  }
101
103
 
102
- const req = activeInvocations.get(span)
104
+ const { req, method, route } = activeInvocations.get(span)
103
105
  activeInvocations.delete(span)
104
106
 
105
- let hasPersistentData = false
106
- const persistent = {}
107
-
108
- if (statusCode) {
109
- persistent[addresses.HTTP_INCOMING_RESPONSE_CODE] = String(statusCode)
110
- hasPersistentData = true
111
- }
112
-
113
- if (responseHeaders) {
114
- const filteredHeaders = { ...responseHeaders }
115
- delete filteredHeaders['set-cookie']
116
- persistent[addresses.HTTP_INCOMING_RESPONSE_HEADERS] = filteredHeaders
117
- hasPersistentData = true
107
+ try {
108
+ const persistent = {}
109
+
110
+ if (statusCode) {
111
+ persistent[addresses.HTTP_INCOMING_RESPONSE_CODE] = String(statusCode)
112
+ }
113
+
114
+ if (responseHeaders) {
115
+ const filteredHeaders = { ...responseHeaders }
116
+ delete filteredHeaders['set-cookie']
117
+ persistent[addresses.HTTP_INCOMING_RESPONSE_HEADERS] = filteredHeaders
118
+ }
119
+
120
+ const samplingDecision = apiSecurity.sampleRootSpanRequest(span, {
121
+ method,
122
+ statusCode,
123
+ route,
124
+ // The tracer does not block in Lambda yet, so a response is never a blocked one.
125
+ blocked: false,
126
+ }, true)
127
+
128
+ if (samplingDecision === apiSecurity.SamplingDecision.SAMPLE) {
129
+ persistent[addresses.WAF_CONTEXT_PROCESSOR] = { 'extract-schema': true }
130
+ }
131
+
132
+ let wafResult
133
+ if (!isEmpty(persistent)) {
134
+ wafResult = waf.run({ persistent }, req, undefined, span)
135
+ }
136
+
137
+ apiSecurity.reportRootSpanRequest(span, samplingDecision, wafResult)
138
+ } finally {
139
+ // The execution environment outlives the invocation, so the native WAF context and the
140
+ // module-level metrics queue must be released even if the work above threw.
141
+ waf.disposeContext(req)
142
+
143
+ Reporter.finishRequest(req, null, {}, undefined, span)
118
144
  }
119
-
120
- if (hasPersistentData) {
121
- waf.run({ persistent }, req, undefined, span)
122
- }
123
-
124
- waf.disposeContext(req)
125
-
126
- Reporter.finishRequest(req, null, {}, undefined, span)
127
145
  } catch (err) {
128
146
  log.error('[ASM] Error in Lambda end-invocation handler', err)
129
147
  }
@@ -82,6 +82,8 @@ function updateWafFromRC (transaction) {
82
82
  const asmFile = /** @type {AsmConfigFile} */ (item.file)
83
83
  if (asmFile?.actions?.length) {
84
84
  newActions.set(item.id, asmFile.actions)
85
+ } else {
86
+ newActions.delete(item.id)
85
87
  }
86
88
  }
87
89
  } catch (e) {
@@ -1,12 +1,29 @@
1
1
  'use strict'
2
2
 
3
3
  class JSONEncoder {
4
- constructor () {
4
+ #bytes = 2
5
+ #maxBytes
6
+
7
+ /**
8
+ * @param {number} [maxBytes]
9
+ */
10
+ constructor (maxBytes) {
11
+ this.#maxBytes = maxBytes
5
12
  this.payloads = []
6
13
  }
7
14
 
8
15
  encode (payload) {
16
+ if (this.#maxBytes !== undefined) {
17
+ const serializedPayload = JSON.stringify(payload) ?? 'null'
18
+ const payloadBytes = Buffer.byteLength(serializedPayload)
19
+ const separatorBytes = this.payloads.length === 0 ? 0 : 1
20
+ if (payloadBytes + separatorBytes > this.#maxBytes - this.#bytes) return false
21
+ this.#bytes += payloadBytes + separatorBytes
22
+ this.payloads.push(serializedPayload)
23
+ return true
24
+ }
9
25
  this.payloads.push(payload)
26
+ return true
10
27
  }
11
28
 
12
29
  count () {
@@ -14,11 +31,24 @@ class JSONEncoder {
14
31
  }
15
32
 
16
33
  reset () {
34
+ this.#bytes = 2
17
35
  this.payloads = []
18
36
  }
19
37
 
20
38
  makePayload () {
21
- const data = JSON.stringify(this.payloads)
39
+ let data
40
+ if (this.#maxBytes === undefined) {
41
+ data = JSON.stringify(this.payloads)
42
+ } else if (this.payloads.length === 0) {
43
+ data = ['[', ']']
44
+ } else {
45
+ data = new Array(this.payloads.length * 2 + 1)
46
+ data[0] = '['
47
+ for (let index = 0; index < this.payloads.length; index++) {
48
+ data[index * 2 + 1] = this.payloads[index]
49
+ data[index * 2 + 2] = index === this.payloads.length - 1 ? ']' : ','
50
+ }
51
+ }
22
52
  this.reset()
23
53
  return data
24
54
  }
@@ -10,6 +10,7 @@ const { DEBUGGER_INPUT_V1 } = require('../../../debugger/constants')
10
10
 
11
11
  // Product-specific discovery: newest advertised version, skip v3 (citestcycle), gzip if >= v4.
12
12
  // Shared `evp_proxy` discovery is an explicit path allowlist and does not cover this contract.
13
+ const AGENT_INFO_TIMEOUT_MS = 5000
13
14
  const AGENT_EVP_PROXY_PATH_PREFIX = '/evp_proxy/v'
14
15
  const AGENT_EVP_PROXY_PATH_REGEX = /\/evp_proxy\/v(\d+)\/?/
15
16
 
@@ -47,14 +48,19 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
47
48
  } = config
48
49
 
49
50
  const initializationController = new AbortController()
50
- const initializationOptions = { signal: initializationController.signal }
51
+ const initializationOptions = {
52
+ deadline: Date.now() + AGENT_INFO_TIMEOUT_MS,
53
+ signal: initializationController.signal,
54
+ // Test runners await agent discovery before starting. A detached retry can let Node exit
55
+ // while that promise is still pending because promises alone do not keep the event loop alive.
56
+ keepProcessAlive: true,
57
+ }
51
58
  this._initializationRequest = {
52
59
  controller: initializationController,
53
60
  options: initializationOptions,
54
61
  }
55
62
 
56
63
  fetchAgentInfo(this._url, (err, agentInfo) => {
57
- const initializationFinalFlush = this._initializationRequest?.finalFlush
58
64
  this._initializationRequest = undefined
59
65
  const initializationAborted = initializationController.signal.aborted
60
66
  const agentInfoError = err || (initializationAborted ? initializationController.signal.reason : undefined)
@@ -112,14 +118,8 @@ class AgentProxyCiVisibilityExporter extends CiVisibilityExporter {
112
118
  this._resolveCanUseCiVisProtocol(isEvpCompatible)
113
119
  if (initializationAborted) {
114
120
  this.resetUncodedTraces()
115
- this.resetDeferredTestSuiteSpans()
116
121
  return
117
122
  }
118
- if (isEvpCompatible) {
119
- if (initializationFinalFlush) this.exportDeferredTestSuiteSpans()
120
- } else {
121
- this.resetDeferredTestSuiteSpans()
122
- }
123
123
  this.exportUncodedTraces()
124
124
  this.exportUncodedCoverages()
125
125
  }, initializationOptions, request)
@@ -16,6 +16,7 @@ const {
16
16
  } = require('../../../ci-visibility/telemetry')
17
17
  const { CoverageCIVisibilityEncoder } = require('../../../encode/coverage-ci-visibility')
18
18
  const BaseWriter = require('../../../exporters/common/writer')
19
+ const { getAgent } = require('../agents')
19
20
  const request = require('../request')
20
21
  const TestOptimizationRequestTracker = require('./request-tracker')
21
22
 
@@ -23,7 +24,7 @@ class Writer extends BaseWriter {
23
24
  #requestTracker
24
25
 
25
26
  constructor ({ url, evpProxyPrefix = '' }) {
26
- super(...arguments)
27
+ super({ ...arguments[0], retainOnBackpressure: true })
27
28
  this.#requestTracker = new TestOptimizationRequestTracker(this)
28
29
  this._url = url
29
30
  this._encoder = new CoverageCIVisibilityEncoder(this)
@@ -51,6 +52,7 @@ class Writer extends BaseWriter {
51
52
  },
52
53
  timeout: 15_000,
53
54
  url: this._url,
55
+ agent: getAgent(this._url),
54
56
  deadline: flushOptions?.deadline,
55
57
  }
56
58
 
@@ -61,7 +63,7 @@ class Writer extends BaseWriter {
61
63
  }
62
64
 
63
65
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
64
- log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
66
+ log.debug(() => `Request to the intake: ${safeJSONStringify({ ...options, agent: undefined })}`)
65
67
 
66
68
  const startRequestTime = Date.now()
67
69
 
@@ -77,11 +79,11 @@ class Writer extends BaseWriter {
77
79
  if (err) {
78
80
  incrementCountMetric(
79
81
  TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
80
- { endpoint: 'code_coverage', statusCode }
82
+ { endpoint: 'code_coverage', statusCode, errorType: statusCode ? undefined : err.code }
81
83
  )
82
84
  incrementCountMetric(
83
85
  TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
84
- { endpoint: 'code_coverage' }
86
+ { endpoint: 'code_coverage', statusCode, errorType: statusCode ? undefined : err.code }
85
87
  )
86
88
  log.error('Error sending CI coverage payload', err)
87
89
  done(err)
@@ -5,7 +5,14 @@ const { safeJSONStringify } = require('../../../exporters/common/util')
5
5
  const { JSONEncoder } = require('../../encode/json-encoder')
6
6
  const { DEBUGGER_INPUT_V1 } = require('../../../debugger/constants')
7
7
  const BaseWriter = require('../../../exporters/common/writer')
8
+ const {
9
+ incrementCountMetric,
10
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
11
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
12
+ } = require('../../../ci-visibility/telemetry')
8
13
 
14
+ const { getAgent } = require('../agents')
15
+ const { MAX_DI_LOG_BUFFERED_BYTES } = require('../limits')
9
16
  const request = require('../request')
10
17
  const TestOptimizationRequestTracker = require('./request-tracker')
11
18
 
@@ -17,10 +24,10 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
17
24
 
18
25
  // TODO: what's a good value for timeout for the logs intake?
19
26
  constructor ({ url, timeout = 15_000, isAgentProxy = false }) {
20
- super(...arguments)
27
+ super({ ...arguments[0], retainOnBackpressure: true })
21
28
  this.#requestTracker = new TestOptimizationRequestTracker(this)
22
29
  this._url = url
23
- this._encoder = new JSONEncoder()
30
+ this._encoder = new JSONEncoder(MAX_DI_LOG_BUFFERED_BYTES)
24
31
  this._isAgentProxy = isAgentProxy
25
32
  this.timeout = timeout
26
33
  }
@@ -46,6 +53,7 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
46
53
  },
47
54
  timeout: this.timeout,
48
55
  url: this._url,
56
+ agent: getAgent(this._url),
49
57
  deadline: flushOptions?.deadline,
50
58
  }
51
59
 
@@ -55,10 +63,18 @@ class DynamicInstrumentationLogsWriter extends BaseWriter {
55
63
  }
56
64
 
57
65
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
58
- log.debug(() => `Request to the logs intake: ${safeJSONStringify(options)}`)
66
+ log.debug(() => `Request to the logs intake: ${safeJSONStringify({ ...options, agent: undefined })}`)
59
67
 
60
- this.#requestTracker.send(request, data, options, (err, res) => {
68
+ this.#requestTracker.send(request, data, options, (err, res, statusCode) => {
61
69
  if (err) {
70
+ incrementCountMetric(
71
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
72
+ { endpoint: 'di_logs', statusCode, errorType: statusCode ? undefined : err.code }
73
+ )
74
+ incrementCountMetric(
75
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
76
+ { endpoint: 'di_logs', statusCode, errorType: statusCode ? undefined : err.code }
77
+ )
62
78
  log.error('Error sending DI logs payload', err)
63
79
  done(err)
64
80
  return
@@ -1,163 +1,20 @@
1
1
  'use strict'
2
2
 
3
3
  const BaseWriter = require('../../../exporters/common/writer')
4
+ const FinalFlushRequestTracker = require('../../../exporters/common/final-flush-request-tracker')
5
+ const { createFinalFlushTimeoutError } = require('../../final-flush')
4
6
 
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
-
7
+ class TestOptimizationRequestTracker extends FinalFlushRequestTracker {
13
8
  /**
14
9
  * Creates request tracking for a Test Optimization writer.
15
10
  *
16
11
  * @param {BaseWriter} writer
17
12
  */
18
13
  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)
14
+ super(
15
+ (done, options) => BaseWriter.prototype.flushDirect.call(writer, done, options),
16
+ createFinalFlushTimeoutError
17
+ )
161
18
  }
162
19
  }
163
20
 
@@ -16,6 +16,7 @@ const {
16
16
  } = require('../../../ci-visibility/telemetry')
17
17
  const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
18
18
  const BaseWriter = require('../../../exporters/common/writer')
19
+ const { getAgent } = require('../agents')
19
20
  const request = require('../request')
20
21
  const TestOptimizationRequestTracker = require('./request-tracker')
21
22
 
@@ -23,11 +24,10 @@ class Writer extends BaseWriter {
23
24
  #requestTracker
24
25
 
25
26
  constructor ({ url, tags, evpProxyPrefix = '' }) {
26
- super(...arguments)
27
+ super({ ...arguments[0], retainOnBackpressure: true })
27
28
  this.#requestTracker = new TestOptimizationRequestTracker(this)
28
- const { 'runtime-id': runtimeId, env, service } = tags
29
29
  this._url = url
30
- this._encoder = new AgentlessCiVisibilityEncoder(this, { runtimeId, env, service })
30
+ this._encoder = new AgentlessCiVisibilityEncoder(this, { tags })
31
31
  this._evpProxyPrefix = evpProxyPrefix
32
32
  }
33
33
 
@@ -52,6 +52,7 @@ class Writer extends BaseWriter {
52
52
  },
53
53
  timeout: 15_000,
54
54
  url: this._url,
55
+ agent: getAgent(this._url),
55
56
  deadline: flushOptions?.deadline,
56
57
  }
57
58
 
@@ -62,7 +63,7 @@ class Writer extends BaseWriter {
62
63
  }
63
64
 
64
65
  // eslint-disable-next-line eslint-rules/eslint-log-printf-style
65
- log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
66
+ log.debug(() => `Request to the intake: ${safeJSONStringify({ ...options, agent: undefined })}`)
66
67
 
67
68
  const startRequestTime = Date.now()
68
69
 
@@ -78,11 +79,11 @@ class Writer extends BaseWriter {
78
79
  if (err) {
79
80
  incrementCountMetric(
80
81
  TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
81
- { endpoint: 'test_cycle', statusCode }
82
+ { endpoint: 'test_cycle', statusCode, errorType: statusCode ? undefined : err.code }
82
83
  )
83
84
  incrementCountMetric(
84
85
  TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
85
- { endpoint: 'test_cycle' }
86
+ { endpoint: 'test_cycle', statusCode, errorType: statusCode ? undefined : err.code }
86
87
  )
87
88
  log.error('Error sending CI agentless payload', err)
88
89
  done(err)
@@ -0,0 +1,54 @@
1
+ 'use strict'
2
+
3
+ const http = require('http')
4
+ const https = require('https')
5
+
6
+ const { createAgents } = require('../../exporters/common/agents')
7
+
8
+ // Finalization can flush several Test Optimization payload types together. Keep media on a separate
9
+ // bounded pool so long uploads cannot consume the sockets needed by trace, coverage, and log writers.
10
+ const maxSockets = 16
11
+
12
+ const payloadAgents = createAgents(maxSockets)
13
+ const mediaAgents = createAgents(maxSockets)
14
+
15
+ /**
16
+ * Selects an agent by intake URL protocol.
17
+ *
18
+ * @param {string|URL|object} url
19
+ * @param {{httpAgent: http.Agent, httpsAgent: https.Agent}} agents
20
+ * @returns {http.Agent|https.Agent}
21
+ */
22
+ function selectAgent (url, { httpAgent, httpsAgent }) {
23
+ const protocol = url?.protocol
24
+ if (protocol === 'https:' || protocol === 'https') return httpsAgent
25
+ if (protocol === 'http:' || protocol === 'http') return httpAgent
26
+
27
+ try {
28
+ return new URL(url).protocol === 'https:' ? httpsAgent : httpAgent
29
+ } catch {
30
+ return String(url).startsWith('https:') ? httpsAgent : httpAgent
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Selects the dedicated Test Optimization payload agent for an intake URL.
36
+ *
37
+ * @param {string|URL|object} url
38
+ * @returns {http.Agent|https.Agent}
39
+ */
40
+ function getAgent (url) {
41
+ return selectAgent(url, payloadAgents)
42
+ }
43
+
44
+ /**
45
+ * Selects the isolated Test Optimization media agent for an intake URL.
46
+ *
47
+ * @param {string|URL|object} url
48
+ * @returns {http.Agent|https.Agent}
49
+ */
50
+ function getMediaAgent (url) {
51
+ return selectAgent(url, mediaAgents)
52
+ }
53
+
54
+ module.exports = { getAgent, getMediaAgent }
@@ -11,9 +11,8 @@ class CiValidationWriter {
11
11
  * @param {object} options.tags tracer tags
12
12
  */
13
13
  constructor ({ sink, tags }) {
14
- const { 'runtime-id': runtimeId, env, service } = tags
15
14
  this._sink = sink
16
- this._encoder = new AgentlessCiVisibilityEncoder(this, { runtimeId, env, service })
15
+ this._encoder = new AgentlessCiVisibilityEncoder(this, { tags })
17
16
  }
18
17
 
19
18
  /**